diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index 6dd4fd9e..a4c340b9 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -4560,20 +4560,20 @@ void CMinecraftApp::loadMediaArchive() wstring mediapath = L""; #ifdef __PS3__ - mediapath = L"Common\\Media\\MediaPS3.arc"; + mediapath = L"Common\\Media\\MediaPS3"; #elif _WINDOWS64 - mediapath = L"Common\\Media\\MediaWindows64.arc"; + mediapath = L"Common\\Media\\MediaWindows64"; #elif __ORBIS__ - mediapath = L"Common\\Media\\MediaOrbis.arc"; + mediapath = L"Common\\Media\\MediaOrbis"; #elif _DURANGO - mediapath = L"Common\\Media\\MediaDurango.arc"; + mediapath = L"Common\\Media\\MediaDurango"; #elif __PSVITA__ - mediapath = L"Common\\Media\\MediaPSVita.arc"; + mediapath = L"Common\\Media\\MediaPSVita"; #endif if (!mediapath.empty()) { - m_mediaArchive = new ArchiveFile( File(mediapath) ); + m_mediaArchive = new FolderFile(mediapath); } #if 0 string path = "Common\\media.arc"; diff --git a/Minecraft.Client/Common/Consoles_App.h b/Minecraft.Client/Common/Consoles_App.h index 24daa174..83264732 100644 --- a/Minecraft.Client/Common/Consoles_App.h +++ b/Minecraft.Client/Common/Consoles_App.h @@ -22,6 +22,7 @@ using namespace std; #include "./GameRules/GameRuleManager.h" #include "../SkinBox.h" #include "../ArchiveFile.h" +#include "../FolderFile.h" typedef struct _JoinFromInviteData { @@ -433,7 +434,7 @@ public: void loadStringTable(); protected: - ArchiveFile *m_mediaArchive; + FolderFile *m_mediaArchive; StringTable *m_stringTable; public: diff --git a/Minecraft.Client/FolderFile.cpp b/Minecraft.Client/FolderFile.cpp new file mode 100644 index 00000000..88a89887 --- /dev/null +++ b/Minecraft.Client/FolderFile.cpp @@ -0,0 +1,181 @@ +#include "stdafx.h" + +#include "../Minecraft.World/StringHelpers.h" +#include "../Minecraft.World/compression.h" + +#include "FolderFile.h" + +void FolderFile::_buildFileIndex() +{ + wstring searchPath = m_folderPath + L"\\*"; + WIN32_FIND_DATAW findData; + HANDLE hFind = FindFirstFileW(searchPath.c_str(), &findData); + + if (hFind == INVALID_HANDLE_VALUE) + { + app.DebugPrintf("Failed to open folder: %ls\n", m_folderPath.c_str()); + return; + } + + do + { + if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + wstring filename = findData.cFileName; + wstring fullPath = m_folderPath + L"\\" + filename; + + // Store filename without path for compatibility with existing code + m_filePaths[filename] = fullPath; + } + } while (FindNextFileW(hFind, &findData)); + + FindClose(hFind); + + app.DebugPrintf("Indexed %d files from folder\n", (int)m_filePaths.size()); +} + +FolderFile::FolderFile(wstring folderPath) +{ + m_folderPath = folderPath; + app.DebugPrintf("Loading folder file...\n"); + +#ifndef _CONTENT_PACKAGE + char buf[256]; + wcstombs(buf, folderPath.c_str(), 256); + app.DebugPrintf("folder path - %s\n", buf); +#endif + + // Check if folder exists + DWORD attrib = GetFileAttributesW(folderPath.c_str()); + if (attrib == INVALID_FILE_ATTRIBUTES || !(attrib & FILE_ATTRIBUTE_DIRECTORY)) + { + app.DebugPrintf("Failed to load folder - directory doesn't exist!\n"); + app.FatalLoadError(); + } + + _buildFileIndex(); + app.DebugPrintf("Finished loading folder file\n"); +} + +FolderFile::~FolderFile() +{ + +} + +vector* FolderFile::getFileList() +{ + vector* out = new vector(); + + for (const auto& it : m_filePaths) + { + out->push_back(it.first); + } + + return out; +} + +bool FolderFile::hasFile(const wstring &filename) +{ + return m_filePaths.find(filename) != m_filePaths.end(); +} + +int FolderFile::getFileSize(const wstring &filename) +{ + auto it = m_filePaths.find(filename); + if (it == m_filePaths.end()) + { + return -1; + } + + WIN32_FILE_ATTRIBUTE_DATA fileData; + if (GetFileAttributesExW(it->second.c_str(), GetFileExInfoStandard, &fileData)) + { + return (int)fileData.nFileSizeLow; + } + + return -1; +} + +byteArray FolderFile::getFile(const wstring &filename) +{ + byteArray out; + auto it = m_filePaths.find(filename); + + if (it == m_filePaths.end()) + { + app.DebugPrintf("Couldn't find file in folder\n"); + app.DebugPrintf("Failed to find file '%ls' in folder\n", filename.c_str()); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + app.FatalLoadError(); + return out; + } + + HANDLE hFile = CreateFileW( + it->second.c_str(), + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr + ); + + if (hFile == INVALID_HANDLE_VALUE) + { + app.DebugPrintf("Failed to open file: %ls\n", it->second.c_str()); + app.FatalLoadError(); + return out; + } + + DWORD fileSize = GetFileSize(hFile, nullptr); + if (fileSize == INVALID_FILE_SIZE) + { + app.DebugPrintf("Failed to get file size: %ls\n", it->second.c_str()); + CloseHandle(hFile); + app.FatalLoadError(); + return out; + } + + PBYTE pData = new BYTE[fileSize]; + DWORD bytesRead = 0; + + BOOL success = ReadFile(hFile, pData, fileSize, &bytesRead, nullptr); + CloseHandle(hFile); + + if (!success || bytesRead != fileSize) + { + app.DebugPrintf("Failed to read file: %ls\n", it->second.c_str()); + delete[] pData; + app.FatalLoadError(); + return out; + } + + out = byteArray(pData, fileSize); + + // Compressed filenames are preceeded with an asterisk. + if (filename[0] == L'*' && out.data != nullptr) + { + /* 4J-JEV: + * If a compressed file is accessed before compression object is + * initialized it will crash here (Compression::getCompression). + */ + ///4 279 553 556 + + ByteArrayInputStream bais(out); + DataInputStream dis(&bais); + unsigned int decompressedSize = dis.readInt(); + dis.close(); + + PBYTE uncompressedBuffer = new BYTE[decompressedSize]; + Compression::getCompression()->Decompress(uncompressedBuffer, &decompressedSize, out.data + 4, out.length - 4); + + delete[] out.data; + + out.data = uncompressedBuffer; + out.length = decompressedSize; + } + + return out; +} diff --git a/Minecraft.Client/FolderFile.h b/Minecraft.Client/FolderFile.h new file mode 100644 index 00000000..7c4cdecb --- /dev/null +++ b/Minecraft.Client/FolderFile.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +#include "../Minecraft.World/ArrayWithLength.h" + +using namespace std; + +class FolderFile +{ +private: + wstring m_folderPath; + unordered_map m_filePaths; // filename -> full path + + void _buildFileIndex(); + +public: + FolderFile(wstring folderPath); + ~FolderFile(); + + vector* getFileList(); + bool hasFile(const wstring &filename); + int getFileSize(const wstring &filename); + byteArray getFile(const wstring &filename); +}; diff --git a/Minecraft.Client/cmake/sources/Common.cmake b/Minecraft.Client/cmake/sources/Common.cmake index ba5260f2..b8d26ac9 100644 --- a/Minecraft.Client/cmake/sources/Common.cmake +++ b/Minecraft.Client/cmake/sources/Common.cmake @@ -277,7 +277,9 @@ source_group("Common/UI" FILES ${_MINECRAFT_CLIENT_COMMON_COMMON_UI}) set(_MINECRAFT_CLIENT_COMMON_COMMON_UI_ALL_PLATFORMS "${CMAKE_CURRENT_SOURCE_DIR}/ArchiveFile.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/ArchiveFile.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ArchiveFile.h" + "${CMAKE_CURRENT_SOURCE_DIR}/FolderFile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/FolderFile.h" "${BASE_DIR}/UI/IUIController.h" "${BASE_DIR}/UI/IUIScene_AbstractContainerMenu.cpp" "${BASE_DIR}/UI/IUIScene_AbstractContainerMenu.h" diff --git a/cmake/CopyAssets.cmake b/cmake/CopyAssets.cmake index a78c9170..a3366b9e 100644 --- a/cmake/CopyAssets.cmake +++ b/cmake/CopyAssets.cmake @@ -7,8 +7,8 @@ function(setup_asset_folder_copy TARGET_NAME ASSET_FOLDER_PAIRS) "*.xml" "*.lang" "*.bat" "*.cmd" "*.msscmp" "*.binka" # Old audio formats - "*.swf" # These are built into the .arc - "*.resx" "*.loc" + #"*.swf" # These are built into the .arc + "*.resx" #"*.loc" "*.wav" # Unsupported audio format "*.xui" ) @@ -18,20 +18,20 @@ function(setup_asset_folder_copy TARGET_NAME ASSET_FOLDER_PAIRS) "Graphics" ) - # Exclude platform-specific arc media files - set(PLATFORM_MEDIA_FILES - "MediaWindows64.arc" - "MediaDurango.arc" - "MediaOrbis.arc" - "MediaPS3.arc" - "MediaPSVita.arc" - "MediaXbox.arc" # Seems to be missing? + # Exclude platform-specific media folders + set(PLATFORM_MEDIA_FOLDERS + "MediaWindows64" + "MediaDurango" + "MediaOrbis" + "MediaPS3" + "MediaPSVita" + "MediaXbox" # Seems to be missing? ) - # Exclude all platform media files except the one for the current platform - foreach(media_file IN LISTS PLATFORM_MEDIA_FILES) - if(NOT media_file MATCHES "Media${PLATFORM_NAME}\\.arc") - list(APPEND ASSET_EXCLUDE_FILES "${media_file}") + # Exclude all platform media folders except the one for the current platform + foreach(media_folder IN LISTS PLATFORM_MEDIA_FOLDERS) + if(NOT media_folder MATCHES "Media${PLATFORM_NAME}") + list(APPEND ASSET_EXCLUDE_FOLDERS "${media_folder}") endif() endforeach()