From 47b6b6eb4addd82a89d6bcd8fa92f9c0a09a5781 Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Fri, 27 May 2016 16:50:50 +0200 Subject: + Support for MPQs that have invalid (and ignored) flags in the block table + Support for MPQs that have malformed block indexes (0x8000xxxx or 0x4000xxxx) --- src/SBaseFileTable.cpp | 22 ++++++++++++++++------ src/SFileOpenArchive.cpp | 4 ++-- src/SFileOpenFileEx.cpp | 6 ++++-- src/StormLib.h | 3 ++- test/StormTest.cpp | 17 ++++++++++++----- 5 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index b38dbc3..9d37ff1 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -581,23 +581,33 @@ int ConvertMpqHeaderToFormat4( // Hash entry verification when the file table does not exist yet bool IsValidHashEntry(TMPQArchive * ha, TMPQHash * pHash) { - TFileEntry * pFileEntry = ha->pFileTable + pHash->dwBlockIndex; - return ((pHash->dwBlockIndex < ha->dwFileTableSize) && (pFileEntry->dwFlags & MPQ_FILE_EXISTS)) ? true : false; + TFileEntry * pFileEntry = ha->pFileTable + MPQ_BLOCK_INDEX(pHash->dwBlockIndex); + DWORD dwBlockIndex = MPQ_BLOCK_INDEX(pHash->dwBlockIndex); + + pFileEntry = ha->pFileTable + dwBlockIndex; + return ((dwBlockIndex < ha->dwFileTableSize) && (pFileEntry->dwFlags & MPQ_FILE_EXISTS)) ? true : false; } // Hash entry verification when the file table does not exist yet static bool IsValidHashEntry1(TMPQArchive * ha, TMPQHash * pHash, TMPQBlock * pBlockTable) { ULONGLONG ByteOffset; - TMPQBlock * pBlock = pBlockTable + pHash->dwBlockIndex; + TMPQBlock * pBlock; + DWORD dwBlockIndex; // We need to mask out the upper 4 bits of the block table index. // This is because it gets shifted out when calculating block table offset // BlockTableOffset = pHash->dwBlockIndex * 0x10 // Malformed MPQ maps may contain invalid entries // Note that Storm.dll does not perfom this check - if((pHash->dwBlockIndex & 0x0FFFFFFF) < ha->pHeader->dwBlockTableSize) + dwBlockIndex = MPQ_BLOCK_INDEX(pHash->dwBlockIndex); + + // The block index is considered valid if it's less than block table size + if(dwBlockIndex < ha->pHeader->dwBlockTableSize) { + // Calculate the block table position + pBlock = pBlockTable + dwBlockIndex; + // Check whether this is an existing file // Also we do not allow to be file size greater than 2GB if((pBlock->dwFlags & MPQ_FILE_EXISTS) && (pBlock->dwFSize & 0x8000000) == 0) @@ -783,8 +793,8 @@ static int BuildFileTableFromBlockTable( if(IsValidHashEntry1(ha, pHash, pBlockTable)) { - DWORD dwBlockIndex = pHash->dwBlockIndex; - DWORD dwNewIndex = pHash->dwBlockIndex; + DWORD dwBlockIndex = MPQ_BLOCK_INDEX(pHash->dwBlockIndex); + DWORD dwNewIndex = MPQ_BLOCK_INDEX(pHash->dwBlockIndex); // Determine the new block index if(DefragmentTable != NULL) diff --git a/src/SFileOpenArchive.cpp b/src/SFileOpenArchive.cpp index 6a249f2..2cb6698 100644 --- a/src/SFileOpenArchive.cpp +++ b/src/SFileOpenArchive.cpp @@ -369,8 +369,8 @@ bool WINAPI SFileOpenArchive( // higher than 0x10000000, it would overflow in 32-bit version // Observed in the malformed Warcraft III maps // Example map: MPQ_2016_v1_ProtectedMap_TableSizeOverflow.w3x - ha->pHeader->dwHashTableSize &= 0x0FFFFFFF; - ha->pHeader->dwBlockTableSize &= 0x0FFFFFFF; + ha->pHeader->dwBlockTableSize = MPQ_BLOCK_INDEX(ha->pHeader->dwBlockTableSize); + ha->pHeader->dwHashTableSize = MPQ_BLOCK_INDEX(ha->pHeader->dwHashTableSize); // Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES)) diff --git a/src/SFileOpenFileEx.cpp b/src/SFileOpenFileEx.cpp index 3794680..afbfc78 100644 --- a/src/SFileOpenFileEx.cpp +++ b/src/SFileOpenFileEx.cpp @@ -309,8 +309,10 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch { if(pFileEntry == NULL || (pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) nError = ERROR_FILE_NOT_FOUND; - if(pFileEntry != NULL && pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) - nError = ERROR_NOT_SUPPORTED; + + // Ignore unknown loading flags (example: MPQ_2016_v1_WME4_4.w3x) +// if(pFileEntry != NULL && pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) +// nError = ERROR_NOT_SUPPORTED; } // Did the caller just wanted to know if the file exists? diff --git a/src/StormLib.h b/src/StormLib.h index 22658bd..c25a1a8 100644 --- a/src/StormLib.h +++ b/src/StormLib.h @@ -218,7 +218,6 @@ extern "C" { #define MPQ_FILE_REPLACEEXISTING 0x80000000 // Replace when the file exist (SFileAddFile) #define MPQ_FILE_COMPRESS_MASK 0x0000FF00 // Mask for a file being compressed -#define MPQ_FILE_EXISTS_MASK 0xF00000FF // These must be either zero or MPQ_FILE_EXISTS #define MPQ_FILE_VALID_FLAGS (MPQ_FILE_IMPLODE | \ MPQ_FILE_COMPRESS | \ @@ -231,6 +230,8 @@ extern "C" { MPQ_FILE_SIGNATURE | \ MPQ_FILE_EXISTS) +#define MPQ_BLOCK_INDEX(bi) ((bi) & 0x0FFFFFFF) // Index mask for block table index + // Compression types for multiple compressions #define MPQ_COMPRESSION_HUFFMANN 0x01 // Huffmann compression (used on WAVE files only) #define MPQ_COMPRESSION_ZLIB 0x02 // ZLIB compression diff --git a/test/StormTest.cpp b/test/StormTest.cpp index f0456e7..2d73bd9 100644 --- a/test/StormTest.cpp +++ b/test/StormTest.cpp @@ -4273,11 +4273,11 @@ int main(int argc, char * argv[]) // Not a test, but rather a tool for creating links to duplicated files // if(nError == ERROR_SUCCESS) // nError = FindFilePairs(ForEachFile_CreateArchiveLink, "2004 - WoW\\06080", "2004 - WoW\\06299"); - - // Search all testing archives and verify their SHA1 hash -// if(nError == ERROR_SUCCESS) -// nError = FindFiles(ForEachFile_VerifyFileChecksum, szMpqSubDir); /* + // Search all testing archives and verify their SHA1 hash + if(nError == ERROR_SUCCESS) + nError = FindFiles(ForEachFile_VerifyFileChecksum, szMpqSubDir); + // Test sparse compression if(nError == ERROR_SUCCESS) nError = TestSparseCompression(); @@ -4456,10 +4456,17 @@ int main(int argc, char * argv[]) // Open another protected map if(nError == ERROR_SUCCESS) nError = TestOpenArchive("MPQ_2016_v1_ProtectedMap_HashOffsIsZero.w3x"); -*/ + // Something like Somj 2.0 if(nError == ERROR_SUCCESS) nError = TestOpenArchive("MPQ_2016_v1_ProtectedMap_Somj2.w3x"); + + // Protector from China (2016-05-27) + if(nError == ERROR_SUCCESS) + nError = TestOpenArchive("MPQ_2016_v1_WME4_4.w3x"); +*/ + if(nError == ERROR_SUCCESS) + nError = TestOpenArchive("MPQ_2016_v1_AnotherProtectedMap.w3x"); /* // Open the multi-file archive with wrong prefix to see how StormLib deals with it if(nError == ERROR_SUCCESS) -- cgit v1.2.3