aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/SBaseFileTable.cpp11
-rw-r--r--src/SFilePatchArchives.cpp138
-rw-r--r--src/StormLib.h1
-rw-r--r--test/StormTest.cpp38
4 files changed, 102 insertions, 86 deletions
diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp
index b293574..ed0748d 100644
--- a/src/SBaseFileTable.cpp
+++ b/src/SBaseFileTable.cpp
@@ -374,10 +374,13 @@ int ConvertMpqHeaderToFormat4(
//
Label_ArchiveVersion1:
- if(pHeader->dwHashTablePos <= pHeader->dwHeaderSize || (pHeader->dwHashTablePos & 0x80000000))
- ha->dwFlags |= MPQ_FLAG_MALFORMED;
- if(pHeader->dwBlockTablePos <= pHeader->dwHeaderSize || (pHeader->dwBlockTablePos & 0x80000000))
- ha->dwFlags |= MPQ_FLAG_MALFORMED;
+ if(pHeader->dwBlockTableSize > 1) // Prevent empty MPQs being marked as malformed
+ {
+ if(pHeader->dwHashTablePos <= pHeader->dwHeaderSize || (pHeader->dwHashTablePos & 0x80000000))
+ ha->dwFlags |= MPQ_FLAG_MALFORMED;
+ if(pHeader->dwBlockTablePos <= pHeader->dwHeaderSize || (pHeader->dwBlockTablePos & 0x80000000))
+ ha->dwFlags |= MPQ_FLAG_MALFORMED;
+ }
// Only low byte of sector size is really used
if(pHeader->wSectorSize & 0xFF00)
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);
diff --git a/src/StormLib.h b/src/StormLib.h
index b6cb0c9..fb32b8b 100644
--- a/src/StormLib.h
+++ b/src/StormLib.h
@@ -149,6 +149,7 @@ extern "C" {
#define ERROR_MARKED_FOR_DELETE 10005 // The file was marked as "deleted" in the MPQ
#define ERROR_FILE_INCOMPLETE 10006 // The required file part is missing
#define ERROR_UNKNOWN_FILE_NAMES 10007 // A name of at least one file is unknown
+#define ERROR_CANT_FIND_PATCH_PREFIX 10008 // StormLib was unable to find patch prefix for the patches
// Values for SFileCreateArchive
#define HASH_TABLE_SIZE_MIN 0x00000004 // Verified: If there is 1 file, hash table size is 4
diff --git a/test/StormTest.cpp b/test/StormTest.cpp
index b67e851..685e7de 100644
--- a/test/StormTest.cpp
+++ b/test/StormTest.cpp
@@ -203,7 +203,7 @@ static const char * PatchList_SC2_34644_Maps[] =
static const char * PatchList_SC2_32283_enGB[] =
{
- "MPQ_2013_v4_enGB.SC2Data",
+ "MPQ_2013_v4_Mods#Core.SC2Mod#enGB.SC2Assets",
"s2-update-enGB-23258.MPQ",
"s2-update-enGB-24540.MPQ",
"s2-update-enGB-26147.MPQ",
@@ -213,6 +213,18 @@ static const char * PatchList_SC2_32283_enGB[] =
NULL
};
+static const char * PatchList_SC2_36281_enGB[] =
+{
+ "MPQ_2013_v4_Mods#Liberty.SC2Mod#enGB.SC2Data",
+ "s2-update-enGB-23258.MPQ",
+ "s2-update-enGB-24540.MPQ",
+ "s2-update-enGB-26147.MPQ",
+ "s2-update-enGB-28522.MPQ",
+ "s2-update-enGB-30384.MPQ",
+ "s2-update-enGB-32281.MPQ",
+ NULL
+};
+
static const char * PatchList_HS_6898_enGB[] =
{
"MPQ_2014_v4_base-Win.MPQ",
@@ -1924,10 +1936,15 @@ static int OpenPatchedArchive(TLogHelper * pLogger, HANDLE * phMpq, const char *
}
}
- // Store the archive handle or close the archive
- if(phMpq == NULL)
+ // If anything failed, close the MPQ handle
+ if(nError != ERROR_SUCCESS)
+ {
SFileCloseArchive(hMpq);
- else
+ hMpq = NULL;
+ }
+
+ // Give the archive handle to the caller
+ if(phMpq != NULL)
*phMpq = hMpq;
return nError;
}
@@ -2494,7 +2511,7 @@ static int TestOpenArchive_Corrupt(const char * szPlainName)
// Opens a patched MPQ archive
-static int TestOpenArchive_Patched(const char * PatchList[], const char * szPatchedFile, int nExpectedPatchCount)
+static int TestOpenArchive_Patched(const char * PatchList[], const char * szPatchedFile, int nExpectedPatchCount, bool bExpectedToFail = false)
{
TLogHelper Logger("OpenPatchedMpqTest", PatchList[0]);
HANDLE hMpq;
@@ -2530,6 +2547,9 @@ static int TestOpenArchive_Patched(const char * PatchList[], const char * szPatc
SFileCloseArchive(hMpq);
}
+ // Clear the error if patch prefix was not found
+ if(nError == ERROR_CANT_FIND_PATCH_PREFIX && bExpectedToFail)
+ nError = ERROR_SUCCESS;
return nError;
}
@@ -4283,10 +4303,14 @@ int main(int argc, char * argv[])
// Open a patched archive with new format of BSDIFF patch
if(nError == ERROR_SUCCESS)
nError = TestOpenArchive_Patched(PatchList_SC2_34644_Maps, "Maps\\Campaign\\THorner03.SC2Map\\BankList.xml", 3);
-
+*/
// Open a patched archive
if(nError == ERROR_SUCCESS)
- nError = TestOpenArchive_Patched(PatchList_SC2_32283_enGB, "LocalizedData\\GameHotkeys.txt", 6);
+ nError = TestOpenArchive_Patched(PatchList_SC2_32283_enGB, "LocalizedData\\GameHotkeys.txt", 0, true);
+/*
+ // Open a patched archive where the "StreamingBuckets.txt" is not a patch file
+ if(nError == ERROR_SUCCESS)
+ nError = TestOpenArchive_Patched(PatchList_SC2_36281_enGB, "LocalizedData\\GameHotkeys.txt", 6);
// Open a patched archive
if(nError == ERROR_SUCCESS)