From 9eef856125e84a00169f64a854241ff43f500592 Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Sun, 6 Feb 2022 18:05:34 +0100 Subject: Fixed file search order for Starcraft I --- src/SBaseFileTable.cpp | 25 +++++++++++++++++++------ src/SFileGetFileInfo.cpp | 4 +++- test/StormTest.cpp | 20 +++++++++++--------- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index 97fa3a2..90b7aa4 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -769,6 +769,7 @@ static TMPQHash * GetHashEntryLocale(TMPQArchive * ha, const char * szFileName, { TMPQHash * pFirstHash = GetFirstHashEntry(ha, szFileName); TMPQHash * pBestEntry = NULL; + TMPQHash * p1stEntry = NULL; TMPQHash * pHash = pFirstHash; // Parse the found hashes @@ -776,26 +777,38 @@ static TMPQHash * GetHashEntryLocale(TMPQArchive * ha, const char * szFileName, { // Storm_2016.dll: 150209CB // If the hash entry matches both locale and platform, return it immediately - // Note: We only succeed this check if the locale is non-neutral, because - // some Warcraft III maps have several items with neutral locale&platform, which leads - // to wrong item being returned + // Only do that for non-0 locale&platform, because for loc&plat=0, there's different + // processing in Warcraft III vs. Starcraft, which is abused by some protectors. if((lcLocale || Platform) && pHash->lcLocale == lcLocale && pHash->Platform == Platform) return pHash; // Storm_2016.dll: 150209D9 - // If (locale matches or is neutral) OR (platform matches or is neutral) - // remember this as the best entry + // If (locale matches or is neutral) OR (platform matches or is neutral), remember this as the best entry + // Also remember the first matching entry for Starcraft maps if(pHash->lcLocale == 0 || pHash->lcLocale == lcLocale) { if(pHash->Platform == 0 || pHash->Platform == Platform) + { + p1stEntry = (p1stEntry != NULL) ? p1stEntry : pHash; pBestEntry = pHash; + } } // Get the next hash entry for that file pHash = GetNextHashEntry(ha, pFirstHash, pHash); } - // At the end, return neutral hash (if found), otherwise NULL + // + // Different processing (Starcraft vs. Warcraft III), abused by some protectors + // + // * Starcraft I: for an entry with locale&platform = 0, then the first entry is returned + // Map: MPQ_2022_v1_Sniper.scx + // * Warcraft III: for an entry with locale&platform = 0, then the last entry is returned + // Map: MPQ_2015_v1_ProtectedMap_Spazy.w3x + // + + if(ha->dwValidFileFlags == MPQ_FILE_VALID_FLAGS_SCX) + return p1stEntry; return pBestEntry; } diff --git a/src/SFileGetFileInfo.cpp b/src/SFileGetFileInfo.cpp index 5bc3a3f..b980755 100644 --- a/src/SFileGetFileInfo.cpp +++ b/src/SFileGetFileInfo.cpp @@ -199,6 +199,7 @@ bool WINAPI SFileGetFileInfo( TFileEntry * pFileEntry = NULL; TMPQHeader * pHeader = NULL; ULONGLONG Int64Value = 0; + ULONGLONG ByteOffset; TMPQFile * hf = NULL; void * pvSrcFileInfo = NULL; DWORD cbSrcFileInfo = 0; @@ -317,7 +318,8 @@ bool WINAPI SFileGetFileInfo( return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwBlockTableSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqBlockTable: - if(MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) >= ha->FileSize) + ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos)); + if(ByteOffset >= ha->FileSize) return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); cbSrcFileInfo = pHeader->dwBlockTableSize * sizeof(TMPQBlock); pvSrcFileInfo = LoadBlockTable(ha, true); diff --git a/test/StormTest.cpp b/test/StormTest.cpp index 2700bf1..88b12d9 100644 --- a/test/StormTest.cpp +++ b/test/StormTest.cpp @@ -4214,8 +4214,8 @@ static DWORD TestModifyArchive_ReplaceFile(LPCTSTR szMpqPlainName, LPCTSTR szFil //----------------------------------------------------------------------------- // Tables -static LPCTSTR szBliz = _T("ListFile_Blizzard.txt"); -static LPCTSTR szWotI = _T("ListFile_WarOfTheImmortals.txt"); +static LPCTSTR Bliz = _T("ListFile_Blizzard.txt"); +static LPCTSTR WotI = _T("ListFile_WarOfTheImmortals.txt"); static const TEST_INFO TestList_StreamOps[] = { @@ -4246,7 +4246,7 @@ static const TEST_INFO Test_Mpqs[] = {_T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"), NULL, 0, "music\\dintro.wav", "File00000023.xxx"}, {_T("MPQ_2016_v1_D2XP_IX86_1xx_114a.mpq"), NULL, TEST_DATA("255d87a62f3c9518f72cf723a1818946", 221), "waitingroombkgd.dc6"}, // Update MPQ from Diablo II (patch 2016) {_T("MPQ_2018_v1_icon_error.w3m"), NULL, TEST_DATA("fcefa25fb50c391e8714f2562d1e10ff", 19), "file00000002.blp"}, - {_T("MPQ_1997_v1_Diablo1_STANDARD.SNP"), szBliz, TEST_DATA("5ef18ef9a26b5704d8d46a344d976c89", 2)}, // File whose archive's (signature) file has flags = 0x90000000 + {_T("MPQ_1997_v1_Diablo1_STANDARD.SNP"), Bliz, TEST_DATA("5ef18ef9a26b5704d8d46a344d976c89", 2)}, // File whose archive's (signature) file has flags = 0x90000000 {_T("MPQ_2012_v2_EmptyMpq.MPQ"), NULL, TEST_DATA("00000000000000000000000000000000", 0)}, // Empty archive (found in WoW cache - it's just a header) {_T("MPQ_2013_v4_EmptyMpq.MPQ"), NULL, TEST_DATA("00000000000000000000000000000000", 0)}, // Empty archive (created artificially - it's just a header) {_T("MPQ_2013_v4_patch-base-16357.MPQ"), NULL, TEST_DATA("d41d8cd98f00b204e9800998ecf8427e", 1)}, // Empty archive (found in WoW cache - it's just a header) @@ -4258,7 +4258,7 @@ static const TEST_INFO Test_Mpqs[] = {_T("MPQ_2010_v3_expansion-locale-frFR.MPQ"), NULL, TEST_DATA("0c8fc921466f07421a281a05fad08b01", 53)}, // MPQ archive v 3.0 (the only one I know) {_T("mpqe-file://MPQ_2011_v2_EncryptedMpq.MPQE"), NULL, TEST_DATA("10e4dcdbe95b7ad731c563ec6b71bc16", 82)}, // Encrypted archive from Starcraft II installer {_T("MPx_2013_v1_LongwuOnline.mpk"), NULL, TEST_DATA("548f7db88284097f7e94c95a08c5bc24", 469)}, // MPK archive from Longwu online - {_T("MPx_2013_v1_WarOfTheImmortals.sqp"), szWotI, TEST_DATA("a048f37f7c6162a96253d8081722b6d9", 9396)}, // SQP archive from War of the Immortals + {_T("MPx_2013_v1_WarOfTheImmortals.sqp"), WotI, TEST_DATA("a048f37f7c6162a96253d8081722b6d9", 9396)}, // SQP archive from War of the Immortals {_T("part-file://MPQ_2010_v2_HashTableCompressed.MPQ.part"),0, TEST_DATA("d41d8cd98f00b204e9800998ecf8427e", 14263)}, // Partial MPQ with compressed hash table {_T("blk4-file://streaming/model.MPQ.0"), NULL, TEST_DATA("e06b00efb2fc7e7469dd8b3b859ae15d", 39914)}, // Archive that is merged with multiple files @@ -4290,6 +4290,7 @@ static const TEST_INFO Test_Mpqs[] = {_T("MPQ_2015_v1_flem1.w3x"), NULL, TEST_DATA("1c4c13e627658c473e84d94371e31f37", 20)}, {_T("MPQ_2002_v1_ProtectedMap_HashTable_FakeValid.w3x"), NULL, TEST_DATA("5250975ed917375fc6540d7be436d4de", 114)}, {_T("MPQ_2021_v1_CantExtractCHK.scx"), NULL, TEST_DATA("055fd548a789c910d9dd37472ecc1e66", 28)}, + {_T("MPQ_2022_v1_Sniper.scx"), NULL, TEST_DATA("2e955271b70b79344ad85b698f6ce9d8", 63)}, // Multiple items in hash table for staredit\scenario.chk (locale=0, platform=0) }; static const TEST_INFO Patched_Mpqs[] = @@ -4323,13 +4324,14 @@ int _tmain(int argc, TCHAR * argv[]) dwErrCode = InitializeMpqDirectory(argv, argc); // - // Open all files from the command line + // Test-open MPQs from the command line. They must be plain name + // and must be plade in the Test-MPQs folder // - //for(int i = 1; i < argc; i++) - //{ - // TestArchive(_T("MPQ_2021_v1_CantExtractCHK.scx"), NULL, TFLG_FILE_LOCALE | 0x0409, "File00000014.xxx", NULL); - //} + for(int i = 2; i < argc; i++) + { + TestArchive(argv[i], NULL, TFLG_FILE_LOCALE | 0x0409, "File00000014.xxx", NULL); + } // // Tests on a local listfile -- cgit v1.2.3