From 866269d740b32cf209e5188c5269c4118f8be07b Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Sun, 6 Dec 2020 11:28:10 +0100 Subject: * Better checks for MPQ header v 4.0 --- StormLib_vs19.vcxproj | 4 +-- StormLib_vs19_dll.vcxproj | 10 +++--- StormLib_vs19_test.vcxproj | 10 +++--- src/SBaseCommon.cpp | 41 +++++++++++++-------- src/SBaseFileTable.cpp | 90 +++++++++++++++++++++++++++++++--------------- src/SFileOpenArchive.cpp | 46 ++++++++++++------------ src/StormCommon.h | 3 +- test/StormTest.cpp | 16 ++++++--- 8 files changed, 136 insertions(+), 84 deletions(-) diff --git a/StormLib_vs19.vcxproj b/StormLib_vs19.vcxproj index 273668a..529f014 100644 --- a/StormLib_vs19.vcxproj +++ b/StormLib_vs19.vcxproj @@ -71,9 +71,9 @@ {78424708-1F6E-4D4B-920C-FB6D26847055} StormLib false - 10.0.17763.0 + 10.0.17134.0 StaticLibrary - v142 + v141 false diff --git a/StormLib_vs19_dll.vcxproj b/StormLib_vs19_dll.vcxproj index 941a605..712b427 100644 --- a/StormLib_vs19_dll.vcxproj +++ b/StormLib_vs19_dll.vcxproj @@ -23,29 +23,29 @@ {CB385198-50B1-4CF4-883B-11F042DED6AA} StormLib_dll Win32Proj - 10.0.17763.0 + 10.0.17134.0 DynamicLibrary - v142 + v141 Unicode true DynamicLibrary - v142 + v141 Unicode DynamicLibrary - v142 + v141 Unicode true DynamicLibrary - v142 + v141 Unicode diff --git a/StormLib_vs19_test.vcxproj b/StormLib_vs19_test.vcxproj index 15f9a6b..3bd8248 100644 --- a/StormLib_vs19_test.vcxproj +++ b/StormLib_vs19_test.vcxproj @@ -23,29 +23,29 @@ {AA561A7B-26EA-49AF-90E8-C53C1FA2965D} StormLib_test Win32Proj - 10.0.17763.0 + 10.0.17134.0 Application - v142 + v141 Unicode true Application - v142 + v141 Unicode Application - v142 + v141 Unicode true Application - v142 + v141 Unicode diff --git a/src/SBaseCommon.cpp b/src/SBaseCommon.cpp index 28dc701..9003960 100644 --- a/src/SBaseCommon.cpp +++ b/src/SBaseCommon.cpp @@ -907,6 +907,7 @@ TMPQFile * CreateWritableHandle(TMPQArchive * ha, DWORD dwFileSize) void * LoadMpqTable( TMPQArchive * ha, ULONGLONG ByteOffset, + LPBYTE pbTableHash, DWORD dwCompressedSize, DWORD dwTableSize, DWORD dwKey, @@ -964,6 +965,19 @@ void * LoadMpqTable( // If everything succeeded, read the raw table from the MPQ if(FileStream_Read(ha->pStream, &ByteOffset, pbToRead, dwBytesToRead)) + { + // Verify the MD5 of the table, if present + if(!VerifyDataBlockHash(pbToRead, dwBytesToRead, pbTableHash)) + { + nError = ERROR_FILE_CORRUPT; + } + } + else + { + nError = GetLastError(); + } + + if(nError == ERROR_SUCCESS) { // First of all, decrypt the table if(dwKey != 0) @@ -986,10 +1000,6 @@ void * LoadMpqTable( // Make sure that the table is properly byte-swapped BSWAP_ARRAY32_UNSIGNED(pbMpqTable, dwTableSize); } - else - { - nError = GetLastError(); - } // If read failed, free the table and return if(nError != ERROR_SUCCESS) @@ -1341,7 +1351,7 @@ int AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile) RawFilePos = CalculateRawSectorOffset(hf, dwCrcOffset); // Now read the table from the MPQ - hf->SectorChksums = (DWORD *)LoadMpqTable(ha, RawFilePos, dwCompressedSize, dwCrcSize, 0, NULL); + hf->SectorChksums = (DWORD *)LoadMpqTable(ha, RawFilePos, NULL, dwCompressedSize, dwCrcSize, 0, NULL); if(hf->SectorChksums == NULL) return ERROR_NOT_ENOUGH_MEMORY; } @@ -1692,7 +1702,7 @@ bool IsValidMD5(LPBYTE pbMd5) { LPDWORD Md5 = (LPDWORD)pbMd5; - return (Md5[0] | Md5[1] | Md5[2] | Md5[3]) ? true : false; + return ((Md5 != NULL) && (Md5[0] | Md5[1] | Md5[2] | Md5[3])) ? true : false; } bool IsValidSignature(LPBYTE pbSignature) @@ -1711,18 +1721,21 @@ bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_ { hash_state md5_state; BYTE md5_digest[MD5_DIGEST_SIZE]; + bool bResult = true; // Don't verify the block if the MD5 is not valid. - if(!IsValidMD5(expected_md5)) - return true; + if(IsValidMD5(expected_md5)) + { + // Calculate the MD5 of the data block + md5_init(&md5_state); + md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock); + md5_done(&md5_state, md5_digest); - // Calculate the MD5 of the data block - md5_init(&md5_state); - md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock); - md5_done(&md5_state, md5_digest); + // Does the MD5's match? + bResult = (memcmp(md5_digest, expected_md5, MD5_DIGEST_SIZE) == 0); + } - // Does the MD5's match? - return (memcmp(md5_digest, expected_md5, MD5_DIGEST_SIZE) == 0); + return bResult; } void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash) diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index 98d8e44..f9a86a0 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -258,6 +258,18 @@ static bool VerifyTablePosition64( return true; } +static bool VerifyTableTandemPositions( + ULONGLONG MpqOffset, // Position of the MPQ header + ULONGLONG TableOffset1, // 1st table: Position, relative to MPQ header + ULONGLONG TableSize1, // 1st table: Size in bytes + ULONGLONG TableOffset2, // 2nd table: Position, relative to MPQ header + ULONGLONG TableSize2, // 2nd table: Size in bytes + ULONGLONG FileSize) // Size of the entire file, in bytes +{ + return VerifyTablePosition64(MpqOffset, TableOffset1, TableSize1, FileSize) && + VerifyTablePosition64(MpqOffset, TableOffset2, TableSize2, FileSize); +} + static ULONGLONG DetermineArchiveSize_V1( TMPQArchive * ha, TMPQHeader * pHeader, @@ -417,7 +429,7 @@ ULONGLONG CalculateRawSectorOffset( // This function converts the MPQ header so it always looks like version 4 int ConvertMpqHeaderToFormat4( TMPQArchive * ha, - ULONGLONG MpqOffset, + ULONGLONG ByteOffset, ULONGLONG FileSize, DWORD dwFlags, MTYPE MapType) @@ -426,8 +438,10 @@ int ConvertMpqHeaderToFormat4( ULONGLONG BlockTablePos64 = 0; ULONGLONG HashTablePos64 = 0; ULONGLONG BlockTableMask = (ULONGLONG)-1; - ULONGLONG ByteOffset; + ULONGLONG MaxOffset; USHORT wFormatVersion = BSWAP_INT16_UNSIGNED(pHeader->wFormatVersion); + bool bHashBlockOffsetOK = false; + bool bHetBetOffsetOK = false; int nError = ERROR_SUCCESS; // If version 1.0 is forced, then the format version is forced to be 1.0 @@ -479,14 +493,14 @@ int ConvertMpqHeaderToFormat4( // Block table position must be calculated as 32-bit value // Note: BOBA protector puts block table before the MPQ header, so it is negative - BlockTablePos64 = (ULONGLONG)((DWORD)MpqOffset + pHeader->dwBlockTablePos); + BlockTablePos64 = (ULONGLONG)((DWORD)ByteOffset + pHeader->dwBlockTablePos); BlockTableMask = 0xFFFFFFF0; // Determine the archive size on malformed MPQs if(ha->dwFlags & MPQ_FLAG_MALFORMED) { // Calculate the archive size - pHeader->ArchiveSize64 = DetermineArchiveSize_V1(ha, pHeader, MpqOffset, FileSize); + pHeader->ArchiveSize64 = DetermineArchiveSize_V1(ha, pHeader, ByteOffset, FileSize); pHeader->dwArchiveSize = (DWORD)pHeader->ArchiveSize64; } @@ -538,7 +552,7 @@ int ConvertMpqHeaderToFormat4( assert(pHeader->BlockTableSize64 <= (pHeader->dwBlockTableSize * sizeof(TMPQBlock))); // Determine real archive size - pHeader->ArchiveSize64 = DetermineArchiveSize_V2(pHeader, MpqOffset, FileSize); + pHeader->ArchiveSize64 = DetermineArchiveSize_V2(pHeader, ByteOffset, FileSize); // Calculate the size of the hi-block table pHeader->HiBlockTableSize64 = pHeader->ArchiveSize64 - pHeader->HiBlockTablePos64; @@ -547,7 +561,7 @@ int ConvertMpqHeaderToFormat4( else { // Determine real archive size - pHeader->ArchiveSize64 = DetermineArchiveSize_V2(pHeader, MpqOffset, FileSize); + pHeader->ArchiveSize64 = DetermineArchiveSize_V2(pHeader, ByteOffset, FileSize); // Calculate size of the block table pHeader->BlockTableSize64 = pHeader->ArchiveSize64 - BlockTablePos64; @@ -561,7 +575,7 @@ int ConvertMpqHeaderToFormat4( } // Add the MPQ Offset - BlockTablePos64 += MpqOffset; + BlockTablePos64 += ByteOffset; break; case MPQ_FORMAT_VERSION_3: @@ -590,45 +604,45 @@ int ConvertMpqHeaderToFormat4( memset((LPBYTE)pHeader + MPQ_HEADER_SIZE_V3, 0, sizeof(TMPQHeader) - MPQ_HEADER_SIZE_V3); BlockTablePos64 = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); HashTablePos64 = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); - ByteOffset = pHeader->ArchiveSize64; + MaxOffset = pHeader->ArchiveSize64; // Size of the hi-block table if(pHeader->HiBlockTablePos64) { - pHeader->HiBlockTableSize64 = ByteOffset - pHeader->HiBlockTablePos64; - ByteOffset = pHeader->HiBlockTablePos64; + pHeader->HiBlockTableSize64 = MaxOffset - pHeader->HiBlockTablePos64; + MaxOffset = pHeader->HiBlockTablePos64; } // Size of the block table if(BlockTablePos64) { - pHeader->BlockTableSize64 = ByteOffset - BlockTablePos64; - ByteOffset = BlockTablePos64; + pHeader->BlockTableSize64 = MaxOffset - BlockTablePos64; + MaxOffset = BlockTablePos64; } // Size of the hash table if(HashTablePos64) { - pHeader->HashTableSize64 = ByteOffset - HashTablePos64; - ByteOffset = HashTablePos64; + pHeader->HashTableSize64 = MaxOffset - HashTablePos64; + MaxOffset = HashTablePos64; } // Size of the BET table if(pHeader->BetTablePos64) { - pHeader->BetTableSize64 = ByteOffset - pHeader->BetTablePos64; - ByteOffset = pHeader->BetTablePos64; + pHeader->BetTableSize64 = MaxOffset - pHeader->BetTablePos64; + MaxOffset = pHeader->BetTablePos64; } // Size of the HET table if(pHeader->HetTablePos64) { - pHeader->HetTableSize64 = ByteOffset - pHeader->HetTablePos64; -// ByteOffset = pHeader->HetTablePos64; + pHeader->HetTableSize64 = MaxOffset - pHeader->HetTablePos64; +// MaxOffset = pHeader->HetTablePos64; } // Add the MPQ Offset - BlockTablePos64 += MpqOffset; + BlockTablePos64 += ByteOffset; break; case MPQ_FORMAT_VERSION_4: @@ -638,16 +652,34 @@ int ConvertMpqHeaderToFormat4( BSWAP_TMPQHEADER(pHeader, MPQ_FORMAT_VERSION_4); // Apparently, Starcraft II only accepts MPQ headers where the MPQ header hash matches - // If MD5 doesn't match, we ignore this offset + // If MD5 doesn't match, we ignore this offset. We also ignore it if there's no MD5 at all + if(!IsValidMD5(pHeader->MD5_MpqHeader)) + return ERROR_FAKE_MPQ_HEADER; if(!VerifyDataBlockHash(pHeader, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE, pHeader->MD5_MpqHeader)) return ERROR_FAKE_MPQ_HEADER; - if(!VerifyTablePosition64(MpqOffset, pHeader->HiBlockTablePos64, pHeader->HiBlockTableSize64, FileSize)) + + // HiBlockTable must be 0 for archives under 4GB + if((pHeader->ArchiveSize64 >> 0x20) == 0 && pHeader->HiBlockTablePos64 != 0) return ERROR_FAKE_MPQ_HEADER; - if(!VerifyTablePosition64(MpqOffset, pHeader->HetTablePos64, pHeader->HetTableSize64, FileSize)) + + // Is the "HET&BET" table tandem OK? + bHetBetOffsetOK = VerifyTableTandemPositions(ByteOffset, + pHeader->HetTablePos64, pHeader->HetTableSize64, + pHeader->BetTablePos64, pHeader->BetTableSize64, + FileSize); + + // Is the "Hash&Block" table tandem OK? + bHashBlockOffsetOK = VerifyTableTandemPositions(ByteOffset, + pHeader->dwHashTablePos, pHeader->HashTableSize64, + pHeader->dwBlockTablePos, pHeader->BlockTableSize64, + FileSize); + + // At least one pair must be OK + if(bHetBetOffsetOK == false && bHashBlockOffsetOK == false) return ERROR_FAKE_MPQ_HEADER; // Check for malformed MPQs - if(pHeader->wFormatVersion != MPQ_FORMAT_VERSION_4 || (MpqOffset + pHeader->ArchiveSize64) != FileSize || (MpqOffset + pHeader->HiBlockTablePos64) >= FileSize) + if(pHeader->wFormatVersion != MPQ_FORMAT_VERSION_4 || (ByteOffset + pHeader->ArchiveSize64) != FileSize || (ByteOffset + pHeader->HiBlockTablePos64) >= FileSize) { pHeader->wFormatVersion = MPQ_FORMAT_VERSION_4; pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V4; @@ -658,12 +690,12 @@ int ConvertMpqHeaderToFormat4( if(ha->dwFlags & MPQ_FLAG_MALFORMED) { // Calculate the archive size - pHeader->ArchiveSize64 = DetermineArchiveSize_V4(pHeader, MpqOffset, FileSize); + pHeader->ArchiveSize64 = DetermineArchiveSize_V4(pHeader, ByteOffset, FileSize); pHeader->dwArchiveSize = (DWORD)pHeader->ArchiveSize64; } // Calculate the block table position - BlockTablePos64 = MpqOffset + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + BlockTablePos64 = ByteOffset + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); break; default: @@ -679,13 +711,13 @@ int ConvertMpqHeaderToFormat4( } // Calculate the block table position - BlockTablePos64 = MpqOffset + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + BlockTablePos64 = ByteOffset + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); break; } // Handle case when block table is placed before the MPQ header // Used by BOBA protector - if(BlockTablePos64 < MpqOffset) + if(BlockTablePos64 < ByteOffset) ha->dwFlags |= MPQ_FLAG_MALFORMED; return nError; } @@ -2304,7 +2336,7 @@ static TMPQHash * LoadHashTable(TMPQArchive * ha) dwCmpSize = (DWORD)pHeader->HashTableSize64; // Read, decrypt and uncompress the hash table - pHashTable = (TMPQHash *)LoadMpqTable(ha, ByteOffset, dwCmpSize, dwTableSize, MPQ_KEY_HASH_TABLE, &bHashTableIsCut); + pHashTable = (TMPQHash *)LoadMpqTable(ha, ByteOffset, pHeader->MD5_HashTable, dwCmpSize, dwTableSize, MPQ_KEY_HASH_TABLE, &bHashTableIsCut); // DumpHashTable(pHashTable, pHeader->dwHashTableSize); // If the hash table was cut, we can/have to defragment it @@ -2365,7 +2397,7 @@ TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool /* bDontFixEntries */) dwCmpSize = (DWORD)pHeader->BlockTableSize64; // Read, decrypt and uncompress the block table - pBlockTable = (TMPQBlock * )LoadMpqTable(ha, ByteOffset, dwCmpSize, dwTableSize, MPQ_KEY_BLOCK_TABLE, &bBlockTableIsCut); + pBlockTable = (TMPQBlock * )LoadMpqTable(ha, ByteOffset, NULL, dwCmpSize, dwTableSize, MPQ_KEY_BLOCK_TABLE, &bBlockTableIsCut); // If the block table was cut, we need to remember it if(pBlockTable != NULL && bBlockTableIsCut) diff --git a/src/SFileOpenArchive.cpp b/src/SFileOpenArchive.cpp index 7dfb851..500e041 100644 --- a/src/SFileOpenArchive.cpp +++ b/src/SFileOpenArchive.cpp @@ -135,12 +135,12 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize) } // Check the begin of hi-block table - if(pHeader->HiBlockTablePos64 != 0) - { - ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64; - if(ByteOffset > FileSize) - return ERROR_BAD_FORMAT; - } + //if(pHeader->HiBlockTablePos64 != 0) + //{ + // ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64; + // if(ByteOffset > FileSize) + // return ERROR_BAD_FORMAT; + //} // All OK. return ERROR_SUCCESS; @@ -186,7 +186,7 @@ bool WINAPI SFileOpenArchive( ULONGLONG FileSize = 0; // Size of the file LPBYTE pbHeaderBuffer = NULL; // Buffer for searching MPQ header DWORD dwStreamFlags = (dwFlags & STREAM_FLAGS_MASK); - MTYPE MapType = MapTypeNotRecognized; + MTYPE MapType = MapTypeNotChecked; int nError = ERROR_SUCCESS; // Verify the parameters @@ -234,7 +234,7 @@ bool WINAPI SFileOpenArchive( // Find the position of MPQ header if(nError == ERROR_SUCCESS) { - ULONGLONG SearchOffset = 0; + ULONGLONG ByteOffset = 0; ULONGLONG EndOfSearch = FileSize; DWORD dwStrmFlags = 0; DWORD dwHeaderSize; @@ -261,26 +261,25 @@ bool WINAPI SFileOpenArchive( EndOfSearch = 0x08000000; // Find the offset of MPQ header within the file - while(bSearchComplete == false && SearchOffset < EndOfSearch) + while(bSearchComplete == false && ByteOffset < EndOfSearch) { // Always read at least 0x1000 bytes for performance. // This is what Storm.dll (2002) does. DWORD dwBytesAvailable = HEADER_SEARCH_BUFFER_SIZE; - DWORD dwInBufferOffset = 0; // Cut the bytes available, if needed - if((FileSize - SearchOffset) < HEADER_SEARCH_BUFFER_SIZE) - dwBytesAvailable = (DWORD)(FileSize - SearchOffset); + if((FileSize - ByteOffset) < HEADER_SEARCH_BUFFER_SIZE) + dwBytesAvailable = (DWORD)(FileSize - ByteOffset); // Read the eventual MPQ header - if(!FileStream_Read(ha->pStream, &SearchOffset, pbHeaderBuffer, dwBytesAvailable)) + if(!FileStream_Read(ha->pStream, &ByteOffset, pbHeaderBuffer, dwBytesAvailable)) { nError = GetLastError(); break; } // Check whether the file is AVI file or a Warcraft III/Starcraft II map - if(SearchOffset == 0) + if(MapType == MapTypeNotChecked) { // Do nothing if the file is an AVI file if((MapType = CheckMapType(szMpqName, pbHeaderBuffer, dwBytesAvailable)) == MapTypeAviFile) @@ -291,7 +290,7 @@ bool WINAPI SFileOpenArchive( } // Search the header buffer - while(dwInBufferOffset < dwBytesAvailable) + for(DWORD dwInBufferOffset = 0; dwInBufferOffset < dwBytesAvailable; dwInBufferOffset += 0x200) { // Copy the data from the potential header buffer to the MPQ header memcpy(ha->HeaderData, pbHeaderBuffer + dwInBufferOffset, sizeof(ha->HeaderData)); @@ -304,16 +303,16 @@ bool WINAPI SFileOpenArchive( if(ha->pUserData == NULL && dwHeaderID == ID_MPQ_USERDATA) { // Verify if this looks like a valid user data - pUserData = IsValidMpqUserData(SearchOffset, FileSize, ha->HeaderData); + pUserData = IsValidMpqUserData(ByteOffset, FileSize, ha->HeaderData); if(pUserData != NULL) { // Fill the user data header - ha->UserDataPos = SearchOffset; + ha->UserDataPos = ByteOffset; ha->pUserData = &ha->UserData; memcpy(ha->pUserData, pUserData, sizeof(TMPQUserData)); // Continue searching from that position - SearchOffset += ha->pUserData->dwHeaderOffs; + ByteOffset += ha->pUserData->dwHeaderOffs; break; } } @@ -327,7 +326,7 @@ bool WINAPI SFileOpenArchive( if(dwHeaderID == ID_MPQ && dwHeaderSize >= MPQ_HEADER_SIZE_V1) { // Now convert the header to version 4 - nError = ConvertMpqHeaderToFormat4(ha, SearchOffset, FileSize, dwFlags, MapType); + nError = ConvertMpqHeaderToFormat4(ha, ByteOffset, FileSize, dwFlags, MapType); if(nError != ERROR_FAKE_MPQ_HEADER) { bSearchComplete = true; @@ -353,8 +352,7 @@ bool WINAPI SFileOpenArchive( } // Move the pointers - SearchOffset += 0x200; - dwInBufferOffset += 0x200; + ByteOffset += 0x200; } } @@ -363,15 +361,15 @@ bool WINAPI SFileOpenArchive( { // Set the user data position to the MPQ header, if none if(ha->pUserData == NULL) - ha->UserDataPos = SearchOffset; + ha->UserDataPos = ByteOffset; // Set the position of the MPQ header ha->pHeader = (TMPQHeader *)ha->HeaderData; - ha->MpqPos = SearchOffset; + ha->MpqPos = ByteOffset; ha->FileSize = FileSize; // Sector size must be nonzero. - if(SearchOffset >= FileSize || ha->pHeader->wSectorSize == 0) + if(ByteOffset >= FileSize || ha->pHeader->wSectorSize == 0) nError = ERROR_BAD_FORMAT; } } diff --git a/src/StormCommon.h b/src/StormCommon.h index 1167d29..9f9715f 100644 --- a/src/StormCommon.h +++ b/src/StormCommon.h @@ -75,6 +75,7 @@ typedef enum _MTYPE { + MapTypeNotChecked, // The map type was not checked yet MapTypeNotRecognized, // The file does not seems to be a map MapTypeAviFile, // The file is actually an AVI file (Warcraft III cinematics) MapTypeWarcraft3, // The file is a Warcraft III map @@ -310,7 +311,7 @@ int SCompDecompressMpk(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer TMPQFile * CreateFileHandle(TMPQArchive * ha, TFileEntry * pFileEntry); TMPQFile * CreateWritableHandle(TMPQArchive * ha, DWORD dwFileSize); -void * LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey, bool * pbTableIsCut); +void * LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, LPBYTE pbTableHash, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey, bool * pbTableIsCut); int AllocateSectorBuffer(TMPQFile * hf); int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile); int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile); diff --git a/test/StormTest.cpp b/test/StormTest.cpp index 3d92ae6..497ecee 100644 --- a/test/StormTest.cpp +++ b/test/StormTest.cpp @@ -332,6 +332,8 @@ static bool IsMpqExtension(LPCTSTR szFileName) return true; if(!_tcsicmp(szExtension, _T(".SC2Map"))) return true; + if(!_tcsicmp(szExtension, _T(".SC2Mod"))) + return true; if(!_tcsicmp(szExtension, _T(".0"))) // .MPQ.0 return true; // if(!_tcsicmp(szExtension, ".link")) @@ -3111,6 +3113,12 @@ static int ForEachFile_OpenArchive(LPCTSTR szFullPath) nError = SearchArchive(&Logger, hMpq, 0, &dwFileCount); SFileCloseArchive(hMpq); } + + // Show warning if no files found + if(dwFileCount == 0) + { + Logger.PrintMessage("Warning: no files in the archive"); + } } // Correct some errors @@ -4237,10 +4245,10 @@ int _tmain(int argc, TCHAR * argv[]) // Tests on a local listfile // - if(dwErrCode == ERROR_SUCCESS) - { - dwErrCode = TestOnLocalListFile(_T("ListFile_Blizzard.txt")); - } + //if(dwErrCode == ERROR_SUCCESS) + //{ + // dwErrCode = TestOnLocalListFile(_T("ListFile_Blizzard.txt")); + //} // // Open all files from the command line -- cgit v1.2.3