From 922d3a82b897f3cbe45b45d4e072361c681a8959 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 3 Aug 2015 17:41:31 +0200 Subject: + Fixed patch archives + Empty MPQs are no longer marked as malformed --- src/SFilePatchArchives.cpp | 138 +++++++++++++++++++++------------------------ 1 file changed, 63 insertions(+), 75 deletions(-) (limited to 'src/SFilePatchArchives.cpp') diff --git a/src/SFilePatchArchives.cpp b/src/SFilePatchArchives.cpp index 7c6515e..d01aaf2 100644 --- a/src/SFilePatchArchives.cpp +++ b/src/SFilePatchArchives.cpp @@ -437,24 +437,37 @@ static bool CreatePatchPrefix(TMPQArchive * ha, const char * szFileName, size_t static bool IsMatchingPatchFile( TMPQArchive * ha, const char * szFileName, - LPBYTE pbFileMd5) + LPBYTE pbBaseFileMd5) { MPQ_PATCH_HEADER PatchHeader = {0}; HANDLE hFile = NULL; DWORD dwTransferred = 0; + DWORD dwFlags = 0; bool bResult = false; // Open the file and load the patch header if(SFileOpenFileEx((HANDLE)ha, szFileName, SFILE_OPEN_BASE_FILE, &hFile)) { - // Load the patch header - SFileReadFile(hFile, &PatchHeader, sizeof(MPQ_PATCH_HEADER), &dwTransferred, NULL); - BSWAP_ARRAY32_UNSIGNED(pPatchHeader, sizeof(DWORD) * 6); - - // If the file contains an incremental patch, - // compare the "MD5 before patching" with the base file MD5 - if(dwTransferred == sizeof(MPQ_PATCH_HEADER) && PatchHeader.dwSignature == PATCH_SIGNATURE_HEADER) - bResult = (!memcmp(PatchHeader.md5_before_patch, pbFileMd5, MD5_DIGEST_SIZE)); + // Retrieve the flags. We need to know whether the file is a patch or not + SFileGetFileInfo(hFile, SFileInfoFlags, &dwFlags, sizeof(DWORD), &dwTransferred); + if(dwFlags & MPQ_FILE_PATCH_FILE) + { + // Load the patch header + SFileReadFile(hFile, &PatchHeader, sizeof(MPQ_PATCH_HEADER), &dwTransferred, NULL); + BSWAP_ARRAY32_UNSIGNED(pPatchHeader, sizeof(DWORD) * 6); + + // If the file contains an incremental patch, + // compare the "MD5 before patching" with the base file MD5 + if(dwTransferred == sizeof(MPQ_PATCH_HEADER) && PatchHeader.dwSignature == PATCH_SIGNATURE_HEADER) + bResult = (!memcmp(PatchHeader.md5_before_patch, pbBaseFileMd5, MD5_DIGEST_SIZE)); + } + else + { + // TODO: How to match it if it's not an incremental patch? + // Example: StarCraft II\Updates\enGB\s2-update-enGB-23258.MPQ: + // Mods\Core.SC2Mod\enGB.SC2Assets\StreamingBuckets.txt" + bResult = false; + } // Close the file SFileCloseFile(hFile); @@ -499,33 +512,6 @@ static const char * FindArchiveLanguage(TMPQArchive * ha, PLOCALIZED_MPQ_INFO pM return NULL; } -static TFileEntry * FindBaseLstFile(TMPQArchive * ha) -{ - TFileEntry * pFileEntry; - const char * szLanguage; - char szFileName[0x40]; - - // Prepare the file name tenplate - memcpy(szFileName, "####-md5.lst", 13); - - // Try all languages - for(szLanguage = LanguageList; szLanguage[0] != 0; szLanguage++) - { - // Copy the language name - szFileName[0] = szLanguage[0]; - szFileName[1] = szLanguage[1]; - szFileName[2] = szLanguage[2]; - szFileName[3] = szLanguage[3]; - - // Check whether this file exists - pFileEntry = GetFileEntryLocale(ha, szFileName, 0); - if(pFileEntry != NULL) - return pFileEntry; - } - - return NULL; -} - static bool FindPatchPrefix_WoW_13164_13623(TMPQArchive * haBase, TMPQArchive * haPatch) { const char * szPatchPrefix; @@ -565,12 +551,11 @@ static bool FindPatchPrefix_WoW_13164_13623(TMPQArchive * haBase, TMPQArchive * // We need to match the file by its MD5 // - -static bool FindPatchPrefix_SC2(TMPQArchive * haBase, TMPQArchive * haPatch) +// Note: pBaseEntry is the file entry of the base version of "StreamingBuckets.txt" +static bool FindPatchPrefix_SC2(TMPQArchive * haBase, TMPQArchive * haPatch, TFileEntry * pBaseEntry) { TMPQNamePrefix * pPatchPrefix; - TFileEntry * pBaseEntry; - char * szLstFileName; + char * szPatchFileName; char * szPlainName; size_t cchWorkBuffer = 0x400; bool bResult = false; @@ -583,17 +568,9 @@ static bool FindPatchPrefix_SC2(TMPQArchive * haBase, TMPQArchive * haPatch) TFileEntry * pFileEntry; // Allocate working buffer for merging LST file - szLstFileName = STORM_ALLOC(char, cchWorkBuffer); - if(szLstFileName != NULL) + szPatchFileName = STORM_ALLOC(char, cchWorkBuffer); + if(szPatchFileName != NULL) { - // Find a *-md5.lst file in the base archive - pBaseEntry = FindBaseLstFile(haBase); - if(pBaseEntry == NULL) - { - STORM_FREE(szLstFileName); - return false; - } - // Parse the entire file table for(pFileEntry = haPatch->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { @@ -601,21 +578,21 @@ static bool FindPatchPrefix_SC2(TMPQArchive * haBase, TMPQArchive * haPatch) if(IsPatchMetadataFile(pFileEntry)) { // Construct the name of the MD5 file - strcpy(szLstFileName, pFileEntry->szFileName); - szPlainName = (char *)GetPlainFileName(szLstFileName); + strcpy(szPatchFileName, pFileEntry->szFileName); + szPlainName = (char *)GetPlainFileName(szPatchFileName); strcpy(szPlainName, pBaseEntry->szFileName); // Check for matching MD5 file - if(IsMatchingPatchFile(haPatch, szLstFileName, pBaseEntry->md5)) + if(IsMatchingPatchFile(haPatch, szPatchFileName, pBaseEntry->md5)) { - bResult = CreatePatchPrefix(haPatch, szLstFileName, (size_t)(szPlainName - szLstFileName)); + bResult = CreatePatchPrefix(haPatch, szPatchFileName, (size_t)(szPlainName - szPatchFileName)); break; } } } // Delete the merge buffer - STORM_FREE(szLstFileName); + STORM_FREE(szPatchFileName); } } @@ -637,6 +614,8 @@ static bool FindPatchPrefix_SC2(TMPQArchive * haBase, TMPQArchive * haPatch) static bool FindPatchPrefix(TMPQArchive * haBase, TMPQArchive * haPatch, const char * szPatchPathPrefix) { + TFileEntry * pFileEntry; + // If the patch prefix was explicitly entered, we use that one if(szPatchPathPrefix != NULL) return CreatePatchPrefix(haPatch, szPatchPathPrefix, 0); @@ -649,8 +628,9 @@ static bool FindPatchPrefix(TMPQArchive * haBase, TMPQArchive * haPatch, const c // Updates for Starcraft II // Match: LocalizedData\GameHotkeys.txt <==> Campaigns\Liberty.SC2Campaign\enGB.SC2Data\LocalizedData\GameHotkeys.txt // All Starcraft II base archives seem to have the file "StreamingBuckets.txt" present - if(GetFileEntryLocale(haBase, "StreamingBuckets.txt", 0)) - return FindPatchPrefix_SC2(haBase, haPatch); + pFileEntry = GetFileEntryLocale(haBase, "StreamingBuckets.txt", 0); + if(pFileEntry != NULL) + return FindPatchPrefix_SC2(haBase, haPatch, pFileEntry); // Diablo III patch MPQs don't use patch prefix // Hearthstone MPQs don't use patch prefix @@ -896,29 +876,37 @@ bool WINAPI SFileOpenPatchArchive( // Open the archive like it is normal archive if(nError == ERROR_SUCCESS) { - if(!SFileOpenArchive(szPatchMpqName, 0, MPQ_OPEN_READ_ONLY | MPQ_OPEN_PATCH, &hPatchMpq)) - return false; - haPatch = (TMPQArchive *)hPatchMpq; - - // We need to remember the proper patch prefix to match names of patched files - FindPatchPrefix(ha, (TMPQArchive *)hPatchMpq, szPatchPathPrefix); - - // Now add the patch archive to the list of patches to the original MPQ - while(ha != NULL) + if(SFileOpenArchive(szPatchMpqName, 0, MPQ_OPEN_READ_ONLY | MPQ_OPEN_PATCH, &hPatchMpq)) { - if(ha->haPatch == NULL) + // Cast the archive handle to structure pointer + haPatch = (TMPQArchive *)hPatchMpq; + + // We need to remember the proper patch prefix to match names of patched files + if(FindPatchPrefix(ha, (TMPQArchive *)hPatchMpq, szPatchPathPrefix)) { - haPatch->haBase = ha; - ha->haPatch = haPatch; - return true; + // Now add the patch archive to the list of patches to the original MPQ + while(ha != NULL) + { + if(ha->haPatch == NULL) + { + haPatch->haBase = ha; + ha->haPatch = haPatch; + return true; + } + + // Move to the next archive + ha = ha->haPatch; + } } - // Move to the next archive - ha = ha->haPatch; + // Close the archive + SFileCloseArchive(hPatchMpq); + nError = ERROR_CANT_FIND_PATCH_PREFIX; + } + else + { + nError = GetLastError(); } - - // Should never happen - nError = ERROR_CAN_NOT_COMPLETE; } SetLastError(nError); -- cgit v1.2.3