#include "../../Minecraft.World/Platform/stdafx.h" #include #include "DLCManager.h" #include "DLCPack.h" #include "DLCFile.h" #include "../../Minecraft.World/Util/StringHelpers.h" #include "../../Minecraft.Client/Minecraft.h" #include "../../Minecraft.Client/Textures/Packs/TexturePackRepository.h" // 4jcraft, this is the size of wchar_t on disk // the DLC was created on windows, with wchar_t beeing 2 bytes and UTF-16 static const size_t DLC_WCHAR_BIN_SIZE = 2; #if WCHAR_MAX > 0xFFFF // than sizeof(WCHAR) != DLC_WCHAR_BIN_SIZE // e.g. Linux and all Posix/Unix systems with wchar_t beeing 4B/32bit static_assert( sizeof(wchar_t) == 4, "wchar_t is not 4bytes but larger than 2bytes ???"); static inline std::wstring dlc_read_wstring(const void *data) { const uint16_t* p = (const uint16_t *)data; // find the end (nullterminated) const uint16_t* end = p; while(*end) { ++end; } size_t len = end - p; // allocate wstring with length len // it will be nullterminated internally, do not worry. std::wstring out(len, 0); // and copy them into thje string for(size_t i = 0; i < len; ++i) { out[i] = (wchar_t) p[i]; } return out; } #define DLC_WSTRING(ptr) dlc_read_wstring(ptr) #else // just in case. static_assert( sizeof(wchar_t) == 2, "How did we get here? wide char smaller than 2 bytes"); // perfectly fine scince wchar_t will be 2 bytes (UCS-2/UTF-16) #define DLC_WSTRING(ptr) std::wstring((wchar_t*)(ptr)) #endif #define DLC_PARAM_ADV(n) (sizeof(C4JStorage::DLC_FILE_PARAM) + (n) * DLC_WCHAR_BIN_SIZE) #define DLC_DETAIL_ADV(n) (sizeof(C4JStorage::DLC_FILE_DETAILS) + (n) * DLC_WCHAR_BIN_SIZE) const WCHAR *DLCManager::wchTypeNamesA[]= { L"DISPLAYNAME", L"THEMENAME", L"FREE", L"CREDIT", L"CAPEPATH", L"BOX", L"ANIM", L"PACKID", L"NETHERPARTICLECOLOUR", L"ENCHANTTEXTCOLOUR", L"ENCHANTTEXTFOCUSCOLOUR", L"DATAPATH", L"PACKVERSION", }; DLCManager::DLCManager() { //m_bNeedsUpdated = true; m_bNeedsCorruptCheck = true; } DLCManager::~DLCManager() { for(AUTO_VAR(it, m_packs.begin()); it != m_packs.end(); ++it) { DLCPack *pack = *it; delete pack; } } DLCManager::EDLCParameterType DLCManager::getParameterType(const std::wstring ¶mName) { EDLCParameterType type = e_DLCParamType_Invalid; for(DWORD i = 0; i < e_DLCParamType_Max; ++i) { if(paramName.compare(wchTypeNamesA[i]) == 0) { type = (EDLCParameterType)i; break; } } return type; } DWORD DLCManager::getPackCount(EDLCType type /*= e_DLCType_All*/) { DWORD packCount = 0; if( type != e_DLCType_All ) { for(AUTO_VAR(it, m_packs.begin()); it != m_packs.end(); ++it) { DLCPack *pack = *it; if( pack->getDLCItemsCount(type) > 0 ) { ++packCount; } } } else { packCount = (DWORD)m_packs.size(); } return packCount; } void DLCManager::addPack(DLCPack *pack) { m_packs.push_back(pack); } void DLCManager::removePack(DLCPack *pack) { if(pack != NULL) { AUTO_VAR(it, find(m_packs.begin(),m_packs.end(),pack)); if(it != m_packs.end() ) m_packs.erase(it); delete pack; } } DLCPack *DLCManager::getPack(const std::wstring &name) { DLCPack *pack = NULL; //DWORD currentIndex = 0; DLCPack *currentPack = NULL; for(AUTO_VAR(it, m_packs.begin()); it != m_packs.end(); ++it) { currentPack = *it; std::wstring wsName=currentPack->getName(); if(wsName.compare(name) == 0) { pack = currentPack; break; } } return pack; } #ifdef _XBOX_ONE DLCPack *DLCManager::getPackFromProductID(const std::wstring &productID) { DLCPack *pack = NULL; //DWORD currentIndex = 0; DLCPack *currentPack = NULL; for(AUTO_VAR(it, m_packs.begin()); it != m_packs.end(); ++it) { currentPack = *it; std::wstring wsName=currentPack->getPurchaseOfferId(); if(wsName.compare(productID) == 0) { pack = currentPack; break; } } return pack; } #endif DLCPack *DLCManager::getPack(DWORD index, EDLCType type /*= e_DLCType_All*/) { DLCPack *pack = NULL; if( type != e_DLCType_All ) { DWORD currentIndex = 0; DLCPack *currentPack = NULL; for(AUTO_VAR(it, m_packs.begin()); it != m_packs.end(); ++it) { currentPack = *it; if(currentPack->getDLCItemsCount(type)>0) { if(currentIndex == index) { pack = currentPack; break; } ++currentIndex; } } } else { if(index >= m_packs.size()) { app.DebugPrintf("DLCManager: Trying to access a DLC pack beyond the range of valid packs\n"); __debugbreak(); } pack = m_packs[index]; } return pack; } DWORD DLCManager::getPackIndex(DLCPack *pack, bool &found, EDLCType type /*= e_DLCType_All*/) { DWORD foundIndex = 0; found = false; if(pack == NULL) { app.DebugPrintf("DLCManager: Attempting to find the index for a NULL pack\n"); //__debugbreak(); return foundIndex; } if( type != e_DLCType_All ) { DWORD index = 0; for(AUTO_VAR(it, m_packs.begin()); it != m_packs.end(); ++it) { DLCPack *thisPack = *it; if(thisPack->getDLCItemsCount(type)>0) { if(thisPack == pack) { found = true; foundIndex = index; break; } ++index; } } } else { DWORD index = 0; for(AUTO_VAR(it, m_packs.begin()); it != m_packs.end(); ++it) { DLCPack *thisPack = *it; if(thisPack == pack) { found = true; foundIndex = index; break; } ++index; } } return foundIndex; } DWORD DLCManager::getPackIndexContainingSkin(const std::wstring &path, bool &found) { DWORD foundIndex = 0; found = false; DWORD index = 0; for(AUTO_VAR(it, m_packs.begin()); it != m_packs.end(); ++it) { DLCPack *pack = *it; if(pack->getDLCItemsCount(e_DLCType_Skin)>0) { if(pack->doesPackContainSkin(path)) { foundIndex = index; found = true; break; } ++index; } } return foundIndex; } DLCPack *DLCManager::getPackContainingSkin(const std::wstring &path) { DLCPack *foundPack = NULL; for(AUTO_VAR(it, m_packs.begin()); it != m_packs.end(); ++it) { DLCPack *pack = *it; if(pack->getDLCItemsCount(e_DLCType_Skin)>0) { if(pack->doesPackContainSkin(path)) { foundPack = pack; break; } } } return foundPack; } DLCSkinFile *DLCManager::getSkinFile(const std::wstring &path) { DLCSkinFile *foundSkinfile = NULL; for(AUTO_VAR(it, m_packs.begin()); it != m_packs.end(); ++it) { DLCPack *pack = *it; foundSkinfile=pack->getSkinFile(path); if(foundSkinfile!=NULL) { break; } } return foundSkinfile; } DWORD DLCManager::checkForCorruptDLCAndAlert(bool showMessage /*= true*/) { DWORD corruptDLCCount = m_dwUnnamedCorruptDLCCount; DLCPack *pack = NULL; DLCPack *firstCorruptPack = NULL; for(AUTO_VAR(it, m_packs.begin()); it != m_packs.end(); ++it) { pack = *it; if( pack->IsCorrupt() ) { ++corruptDLCCount; if(firstCorruptPack == NULL) firstCorruptPack = pack; } } // gotta fix this someday if(corruptDLCCount > 0 && showMessage) { UINT uiIDA[1]; uiIDA[0]=IDS_CONFIRM_OK; if(corruptDLCCount == 1 && firstCorruptPack != NULL) { // pass in the pack format string WCHAR wchFormat[132]; swprintf(wchFormat, 132, L"%ls\n\n%%ls", firstCorruptPack->getName().c_str()); C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_CORRUPT_DLC_TITLE, IDS_CORRUPT_DLC, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(),wchFormat); } else { C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_CORRUPT_DLC_TITLE, IDS_CORRUPT_DLC_MULTIPLE, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); } } SetNeedsCorruptCheck(false); return corruptDLCCount; } bool DLCManager::readDLCDataFile(DWORD &dwFilesProcessed, const std::wstring &path, DLCPack *pack, bool fromArchive) { return readDLCDataFile( dwFilesProcessed, wstringtofilename(path), pack, fromArchive); } bool DLCManager::readDLCDataFile(DWORD &dwFilesProcessed, const std::string &path, DLCPack *pack, bool fromArchive) { std::wstring wPath = convStringToWstring(path); if (fromArchive && app.getArchiveFileSize(wPath) >= 0) { byteArray bytes = app.getArchiveFile(wPath); return processDLCDataFile(dwFilesProcessed, bytes.data, bytes.length, pack); } else if (fromArchive) return false; #ifdef _WINDOWS64 std::string finalPath = StorageManager.GetMountedPath(path.c_str()); if(finalPath.size() == 0) finalPath = path; HANDLE file = CreateFile(finalPath.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); #elif defined(_DURANGO) std::wstring finalPath = StorageManager.GetMountedPath(wPath.c_str()); if(finalPath.size() == 0) finalPath = wPath; HANDLE file = CreateFile(finalPath.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); #else HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); #endif if( file == INVALID_HANDLE_VALUE ) { DWORD error = GetLastError(); app.DebugPrintf("Failed to open DLC data file with error code %d (%x)\n", error, error); if( dwFilesProcessed == 0 ) removePack(pack); assert(false); return false; } DWORD bytesRead,dwFileSize = GetFileSize(file,NULL); PBYTE pbData = (PBYTE) new BYTE[dwFileSize]; BOOL bSuccess = ReadFile(file,pbData,dwFileSize,&bytesRead,NULL); if(bSuccess==FALSE) { // need to treat the file as corrupt, and flag it, so can't call fatal error //app.FatalLoadError(); } else { CloseHandle(file); } if(bSuccess==FALSE) { // Corrupt or some other error. In any case treat as corrupt app.DebugPrintf("Failed to read %s from DLC content package\n", path.c_str()); pack->SetIsCorrupt( true ); SetNeedsCorruptCheck(true); return false; } return processDLCDataFile(dwFilesProcessed, pbData, bytesRead, pack); } // a bunch of makros to reduce memcpy and offset boilerplate #define DLC_READ_UINT(out, buf, off) memcpy((out), (buf) + (off), sizeof(unsigned int)) #define DLC_READ_PARAM(out, buf, off) memcpy((out), (buf) + (off), sizeof(C4JStorage::DLC_FILE_PARAM)) #define DLC_READ_DETAIL(out, buf, off) memcpy((out), (buf) + (off), sizeof(C4JStorage::DLC_FILE_DETAILS)) // for details, read in the function below #define DLC_PARAM_WSTR(buf, off) DLC_WSTRING((buf) + (off) + offsetof(C4JStorage::DLC_FILE_PARAM, wchData)) #define DLC_DETAIL_WSTR(buf, off) DLC_WSTRING((buf) + (off) + offsetof(C4JStorage::DLC_FILE_DETAILS, wchFile)) bool DLCManager::processDLCDataFile(DWORD &dwFilesProcessed, PBYTE pbData, DWORD dwLength, DLCPack *pack) { std::unordered_map parameterMapping; unsigned int uiCurrentByte=0; // File format defined in the DLC_Creator // File format: Version 2 // unsigned long, version number // unsigned long, t = number of parameter types // t * DLC_FILE_PARAM structs mapping strings to id's // unsigned long, n = number of files // n * DLC_FILE_DETAILS describing each file in the pack // n * files of the form // // unsigned long, p = number of parameters // // p * DLC_FILE_PARAM describing each parameter for this file // // ulFileSize bytes of data blob of the file added // 4jcraft, some parts of this code changed, specifically: // instead of casting a goddamn raw byte pointer and dereferencing it // use memcpy, and access WSTRING with propper offset // (scince bufferoffset after advancing by variable string length is not // guaranteed to be properly aligned, so casting to a scalar/struct is UB) // those casts coult be dangerous on e.g. ARM, because it doesnt handle // missaligned loads, like x86/x64, so it would crash // WHO TF USES HUNGARIAN NOTATION unsigned int uiVersion; DLC_READ_UINT(&uiVersion, pbData, uiCurrentByte); uiCurrentByte+=sizeof(int); if(uiVersion < CURRENT_DLC_VERSION_NUM) { if(pbData!=NULL) delete [] pbData; app.DebugPrintf("DLC version of %d is too old to be read\n", uiVersion); return false; } pack->SetDataPointer(pbData); // safe, offset 4, aligned unsigned int uiParameterCount; DLC_READ_UINT(&uiParameterCount, pbData, uiCurrentByte); uiCurrentByte+=sizeof(int); C4JStorage::DLC_FILE_PARAM parBuf; DLC_READ_PARAM(&parBuf, pbData, uiCurrentByte); //DWORD dwwchCount=0; for(unsigned int i=0;igetName(), pack->getLicenseMask()); } else if(type != e_DLCType_PackConfig) { dlcFile = pack->addFile(type, DLC_DETAIL_WSTR(pbData, uiCurrentByte)); } // Params unsigned int uiParamCount; DLC_READ_UINT(&uiParamCount, pbTemp, 0); pbTemp+=sizeof(int); DLC_READ_PARAM(&parBuf, pbTemp, 0); for(unsigned int j=0;jaddParameter(it->second, DLC_PARAM_WSTR(pbTemp, 0)); } else { if(dlcFile != NULL) dlcFile->addParameter(it->second, DLC_PARAM_WSTR(pbTemp, 0)); else if(dlcTexturePack != NULL) dlcTexturePack->addParameter(it->second, DLC_PARAM_WSTR(pbTemp, 0)); } } pbTemp+=DLC_PARAM_ADV(parBuf.dwWchCount); DLC_READ_PARAM(&parBuf, pbTemp, 0); } //pbTemp+=ulParameterCount * sizeof(C4JStorage::DLC_FILE_PARAM); if(dlcTexturePack != NULL) { DWORD texturePackFilesProcessed = 0; bool validPack = processDLCDataFile(texturePackFilesProcessed,pbTemp,fileBuf.uiFileSize,dlcTexturePack); pack->SetDataPointer(NULL); // If it's a child pack, it doesn't own the data if(!validPack || texturePackFilesProcessed == 0) { delete dlcTexturePack; dlcTexturePack = NULL; } else { pack->addChildPack(dlcTexturePack); if(dlcTexturePack->getDLCItemsCount(DLCManager::e_DLCType_Texture) > 0) { Minecraft::GetInstance()->skins->addTexturePackFromDLC(dlcTexturePack, dlcTexturePack->GetPackId() ); } } ++dwFilesProcessed; } else if(dlcFile != NULL) { // Data dlcFile->addData(pbTemp,fileBuf.uiFileSize); // TODO - 4J Stu Remove the need for this vSkinNames vector, or manage it differently switch(fileBuf.dwType) { case DLCManager::e_DLCType_Skin: app.vSkinNames.push_back(DLC_DETAIL_WSTR(pbData, uiCurrentByte)); break; } ++dwFilesProcessed; } // Move the pointer to the start of the next files data; pbTemp+=fileBuf.uiFileSize; uiCurrentByte+=DLC_DETAIL_ADV(fileBuf.dwWchCount); DLC_READ_DETAIL(&fileBuf, pbData, uiCurrentByte); } if( pack->getDLCItemsCount(DLCManager::e_DLCType_GameRules) > 0 || pack->getDLCItemsCount(DLCManager::e_DLCType_GameRulesHeader) > 0) { app.m_gameRules.loadGameRules(pack); } if(pack->getDLCItemsCount(DLCManager::e_DLCType_Audio) > 0) { //app.m_Audio.loadAudioDetails(pack); } // TODO Should be able to delete this data, but we can't yet due to how it is added to the Memory textures (MEM_file) return true; } DWORD DLCManager::retrievePackIDFromDLCDataFile(const std::string &path, DLCPack *pack) { DWORD packId = 0; std::wstring wPath = convStringToWstring(path); #ifdef _WINDOWS64 std::string finalPath = StorageManager.GetMountedPath(path.c_str()); if(finalPath.size() == 0) finalPath = path; HANDLE file = CreateFile(finalPath.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); #elif defined(_DURANGO) std::wstring finalPath = StorageManager.GetMountedPath(wPath.c_str()); if(finalPath.size() == 0) finalPath = wPath; HANDLE file = CreateFile(finalPath.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); #else HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); #endif if( file == INVALID_HANDLE_VALUE ) { return 0; } DWORD bytesRead; DWORD dwFileSize = GetFileSize(file,NULL); PBYTE pbData = (PBYTE) new BYTE[dwFileSize]; BOOL bSuccess = ReadFile(file,pbData,dwFileSize,&bytesRead,NULL); if(bSuccess==FALSE) { // need to treat the file as corrupt, and flag it, so can't call fatal error //app.FatalLoadError(); } else { CloseHandle(file); } if(bSuccess==FALSE) { // Corrupt or some other error. In any case treat as corrupt app.DebugPrintf("Failed to read %s from DLC content package\n", path.c_str()); delete [] pbData; return 0; } packId=retrievePackID(pbData, bytesRead, pack); delete [] pbData; return packId; } DWORD DLCManager::retrievePackID(PBYTE pbData, DWORD dwLength, DLCPack *pack) { DWORD packId=0; bool bPackIDSet=false; std::unordered_map parameterMapping; unsigned int uiCurrentByte=0; // File format defined in the DLC_Creator // File format: Version 2 // unsigned long, version number // unsigned long, t = number of parameter types // t * DLC_FILE_PARAM structs mapping strings to id's // unsigned long, n = number of files // n * DLC_FILE_DETAILS describing each file in the pack // n * files of the form // // unsigned long, p = number of parameters // // p * DLC_FILE_PARAM describing each parameter for this file // // ulFileSize bytes of data blob of the file added unsigned int uiVersion=*(unsigned int *)pbData; uiCurrentByte+=sizeof(int); if(uiVersion < CURRENT_DLC_VERSION_NUM) { app.DebugPrintf("DLC version of %d is too old to be read\n", uiVersion); return 0; } pack->SetDataPointer(pbData); unsigned int uiParameterCount=*(unsigned int *)&pbData[uiCurrentByte]; uiCurrentByte+=sizeof(int); C4JStorage::DLC_FILE_PARAM *pParams = (C4JStorage::DLC_FILE_PARAM *)&pbData[uiCurrentByte]; for(unsigned int i=0;iwchData); DLCManager::EDLCParameterType type = DLCManager::getParameterType(parameterName); if( type != DLCManager::e_DLCParamType_Invalid ) { parameterMapping[pParams->dwType] = type; } uiCurrentByte+= DLC_PARAM_ADV(pParams->dwWchCount); pParams = (C4JStorage::DLC_FILE_PARAM *)&pbData[uiCurrentByte]; } unsigned int uiFileCount=*(unsigned int *)&pbData[uiCurrentByte]; uiCurrentByte+=sizeof(int); C4JStorage::DLC_FILE_DETAILS *pFile = (C4JStorage::DLC_FILE_DETAILS *)&pbData[uiCurrentByte]; DWORD dwTemp=uiCurrentByte; for(unsigned int i=0;idwWchCount); pFile = (C4JStorage::DLC_FILE_DETAILS *)&pbData[dwTemp]; } PBYTE pbTemp=((PBYTE )pFile); pFile = (C4JStorage::DLC_FILE_DETAILS *)&pbData[uiCurrentByte]; for(unsigned int i=0;idwType; // Params uiParameterCount=*(unsigned int *)pbTemp; pbTemp+=sizeof(int); pParams = (C4JStorage::DLC_FILE_PARAM *)pbTemp; for(unsigned int j=0;jdwType )); if(it != parameterMapping.end() ) { if(type==e_DLCType_PackConfig) { if(it->second==e_DLCParamType_PackId) { std::wstring wsTemp = DLC_WSTRING(pParams->wchData); std::wstringstream ss; // 4J Stu - numbered using decimal to make it easier for artists/people to number manually ss << std::dec << wsTemp.c_str(); ss >> packId; bPackIDSet=true; break; } } } pbTemp+=DLC_PARAM_ADV(pParams->dwWchCount); pParams = (C4JStorage::DLC_FILE_PARAM *)pbTemp; } if(bPackIDSet) break; // Move the pointer to the start of the next files data; pbTemp+=pFile->uiFileSize; uiCurrentByte+=DLC_DETAIL_ADV(pFile->dwWchCount); pFile=(C4JStorage::DLC_FILE_DETAILS *)&pbData[uiCurrentByte]; } parameterMapping.clear(); return packId; }