From 16f2797b8cadde9446d8c9bd694c265f8a058cda Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Fri, 11 Oct 2013 15:51:18 +0200 Subject: + Support for SQP files + Several checks added (thanks Dmitry "Vortex" Koteroff) + Fixed zlib + StormLib no longer changes "/" to "\". Hopefully this won't be problem for Linux and Mac people --- src/SBaseFileTable.cpp | 222 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 170 insertions(+), 52 deletions(-) (limited to 'src/SBaseFileTable.cpp') diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index b362857..7b6d2a6 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -339,6 +339,107 @@ static TMPQHash * GetHashEntryExact(TMPQArchive * ha, const char * szFileName, L return NULL; } +static int ConvertMpqBlockTable( + TMPQArchive * ha, + TFileEntry * pFileTable, + TMPQBlock * pBlockTable) +{ + TFileEntry * pFileEntry; + TMPQHeader * pHeader = ha->pHeader; + TMPQBlock * pMpqBlock; + TMPQHash * pHashEnd = ha->pHashTable + pHeader->dwHashTableSize; + TMPQHash * pHash; + + for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) + { + if(pHash->dwBlockIndex < pHeader->dwBlockTableSize) + { + pFileEntry = pFileTable + pHash->dwBlockIndex; + pMpqBlock = pBlockTable + pHash->dwBlockIndex; + + // + // Yet another silly map protector: For each valid file, + // there are 4 items in the hash table, that appears to be valid: + // + // a6d79af0 e61a0932 001e0000 0000770b <== Fake valid + // a6d79af0 e61a0932 0000d761 0000dacb <== Fake valid + // a6d79af0 e61a0932 00000000 0000002f <== Real file entry + // a6d79af0 e61a0932 00005a4f 000093bc <== Fake valid + // + + if(!(pMpqBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS) && (pMpqBlock->dwFlags & MPQ_FILE_EXISTS)) + { + // Fill the entry + pFileEntry->ByteOffset = pMpqBlock->dwFilePos; + pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable); + pFileEntry->dwFileSize = pMpqBlock->dwFSize; + pFileEntry->dwCmpSize = pMpqBlock->dwCSize; + pFileEntry->dwFlags = pMpqBlock->dwFlags; + pFileEntry->lcLocale = pHash->lcLocale; + pFileEntry->wPlatform = pHash->wPlatform; + } + else + { + // If the hash table entry doesn't point to the valid file item, + // we invalidate the hash table entry + pHash->dwName1 = 0xFFFFFFFF; + pHash->dwName2 = 0xFFFFFFFF; + pHash->lcLocale = 0xFFFF; + pHash->wPlatform = 0xFFFF; + pHash->dwBlockIndex = HASH_ENTRY_DELETED; + } + } + } + + return ERROR_SUCCESS; +} + +static int ConvertSqpBlockTable( + TMPQArchive * ha, + TFileEntry * pFileTable, + TSQPBlock * pBlockTable) +{ + TFileEntry * pFileEntry; + TMPQHeader * pHeader = ha->pHeader; + TSQPBlock * pSqpBlock; + TMPQHash * pHashEnd = ha->pHashTable + pHeader->dwHashTableSize; + TMPQHash * pHash; + + for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) + { + if(pHash->dwBlockIndex < pHeader->dwBlockTableSize) + { + pFileEntry = pFileTable + pHash->dwBlockIndex; + pSqpBlock = pBlockTable + pHash->dwBlockIndex; + + if(!(pSqpBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS) && (pSqpBlock->dwFlags & MPQ_FILE_EXISTS)) + { + // Convert SQP block table entry to the file entry + pFileEntry->ByteOffset = pSqpBlock->dwFilePos; + pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable); + pFileEntry->dwFileSize = pSqpBlock->dwFSize; + pFileEntry->dwCmpSize = pSqpBlock->dwCSize; + pFileEntry->dwFlags = pSqpBlock->dwFlags; + pFileEntry->lcLocale = pHash->lcLocale; + pFileEntry->wPlatform = pHash->wPlatform; + } + else + { + // If the hash table entry doesn't point to the valid file item, + // we invalidate the hash table entry + pHash->dwName1 = 0xFFFFFFFF; + pHash->dwName2 = 0xFFFFFFFF; + pHash->lcLocale = 0xFFFF; + pHash->wPlatform = 0xFFFF; + pHash->dwBlockIndex = HASH_ENTRY_DELETED; + } + } + } + + return ERROR_SUCCESS; +} + + static TMPQHash * TranslateHashTable( TMPQArchive * ha, ULONGLONG * pcbTableSize) @@ -702,19 +803,28 @@ TMPQHetTable * CreateHetTable(DWORD dwMaxFileCount, DWORD dwHashBitSize, bool bC // Allocate hash table pHetTable->pHetHashes = STORM_ALLOC(BYTE, pHetTable->dwHashTableSize); - memset(pHetTable->pHetHashes, 0, pHetTable->dwHashTableSize); + if(pHetTable->pHetHashes != NULL) + { + // Make sure that the HET hashes are initialized + memset(pHetTable->pHetHashes, 0, pHetTable->dwHashTableSize); - // If we shall create empty HET table, we have to allocate empty block index table as well - if(bCreateEmpty) - pHetTable->pBetIndexes = CreateBitArray(pHetTable->dwHashTableSize * pHetTable->dwIndexSizeTotal, 0xFF); + // If we shall create empty HET table, we have to allocate empty block index table as well + if(bCreateEmpty) + pHetTable->pBetIndexes = CreateBitArray(pHetTable->dwHashTableSize * pHetTable->dwIndexSizeTotal, 0xFF); - // Calculate masks - pHetTable->AndMask64 = 0; - if(dwHashBitSize != 0x40) - pHetTable->AndMask64 = (ULONGLONG)1 << dwHashBitSize; - pHetTable->AndMask64--; + // Calculate masks + pHetTable->AndMask64 = 0; + if(dwHashBitSize != 0x40) + pHetTable->AndMask64 = (ULONGLONG)1 << dwHashBitSize; + pHetTable->AndMask64--; - pHetTable->OrMask64 = (ULONGLONG)1 << (dwHashBitSize - 1); + pHetTable->OrMask64 = (ULONGLONG)1 << (dwHashBitSize - 1); + } + else + { + STORM_FREE(pHetTable); + pHetTable = NULL; + } } return pHetTable; @@ -1866,6 +1976,44 @@ int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize) return ERROR_SUCCESS; } +int VerifyAndConvertSqpHashTable(TMPQArchive * ha, TMPQHash * pHashTable, DWORD dwTableSize) +{ + TMPQHash * pMpqHashEnd = pHashTable + dwTableSize; + TSQPHash * pSqpHash = (TSQPHash *)pHashTable; + TMPQHash * pMpqHash; + DWORD dwBlockIndex; + + // Parse all SQP hash table entries + for(pMpqHash = pHashTable; pMpqHash < pMpqHashEnd; pMpqHash++, pSqpHash++) + { + // Ignore free entries + if(pSqpHash->dwBlockIndex != HASH_ENTRY_FREE) + { + // Check block index against the size of the block table + dwBlockIndex = pSqpHash->dwBlockIndex; + if(ha->pHeader->dwBlockTableSize <= dwBlockIndex && dwBlockIndex < HASH_ENTRY_DELETED) + return ERROR_FILE_CORRUPT; + + // We do not support nonzero locale and platform ID + if(pSqpHash->dwAlwaysZero != 0 && pSqpHash->dwAlwaysZero != HASH_ENTRY_FREE) + return ERROR_FILE_CORRUPT; + + // Store the file name hash + pMpqHash->dwName1 = pSqpHash->dwName1; + pMpqHash->dwName2 = pSqpHash->dwName2; + + // Store the rest. Note that this must be done last, + // because pSqpHash->dwBlockIndex corresponds to pMpqHash->dwName2 + pMpqHash->dwBlockIndex = dwBlockIndex; + pMpqHash->wPlatform = 0; + pMpqHash->lcLocale = 0; + } + } + + // The conversion went OK + return ERROR_SUCCESS; +} + TMPQHash * LoadHashTable(TMPQArchive * ha) { TMPQHeader * pHeader = ha->pHeader; @@ -1902,6 +2050,12 @@ TMPQHash * LoadHashTable(TMPQArchive * ha) ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); nError = LoadMpqTable(ha, ByteOffset, pHashTable, dwCmpSize, dwTableSize, MPQ_KEY_HASH_TABLE); + + // If the file is a SQP file, we need to verify the SQP hash table and convert it to MPQ hash table + if(ha->dwSubType == MPQ_SUBTYPE_SQP) + nError = VerifyAndConvertSqpHashTable(ha, pHashTable, pHeader->dwHashTableSize); + + // If anything failed, free the hash table if(nError != ERROR_SUCCESS) { STORM_FREE(pHashTable); @@ -2106,8 +2260,7 @@ int BuildFileTable_Classic( { TFileEntry * pFileEntry; TMPQHeader * pHeader = ha->pHeader; - TMPQBlock * pBlockTable; - TMPQBlock * pBlock; + void * pBlockTable; int nError = ERROR_SUCCESS; // Sanity checks @@ -2123,46 +2276,11 @@ int BuildFileTable_Classic( // If we don't have HET table, we build the file entries from the hash&block tables if(ha->pHetTable == NULL) { - for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) - { - if(pHash->dwBlockIndex < pHeader->dwBlockTableSize) - { - pFileEntry = pFileTable + pHash->dwBlockIndex; - pBlock = pBlockTable + pHash->dwBlockIndex; - - // - // Yet another silly map protector: For each valid file, - // there are 4 items in the hash table, that appears to be valid: - // - // a6d79af0 e61a0932 001e0000 0000770b <== Fake valid - // a6d79af0 e61a0932 0000d761 0000dacb <== Fake valid - // a6d79af0 e61a0932 00000000 0000002f <== Real file entry - // a6d79af0 e61a0932 00005a4f 000093bc <== Fake valid - // - - if(!(pBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS) && (pBlock->dwFlags & MPQ_FILE_EXISTS)) - { - // Fill the entry - pFileEntry->ByteOffset = pBlock->dwFilePos; - pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable); - pFileEntry->dwFileSize = pBlock->dwFSize; - pFileEntry->dwCmpSize = pBlock->dwCSize; - pFileEntry->dwFlags = pBlock->dwFlags; - pFileEntry->lcLocale = pHash->lcLocale; - pFileEntry->wPlatform = pHash->wPlatform; - } - else - { - // If the hash table entry doesn't point to the valid file item, - // we invalidate the entire hash table entry - pHash->dwName1 = 0xFFFFFFFF; - pHash->dwName2 = 0xFFFFFFFF; - pHash->lcLocale = 0xFFFF; - pHash->wPlatform = 0xFFFF; - pHash->dwBlockIndex = HASH_ENTRY_DELETED; - } - } - } + // For MPQ files, treat the block table as MPQ Block Table + if(ha->dwSubType == MPQ_SUBTYPE_MPQ) + nError = ConvertMpqBlockTable(ha, pFileTable, (TMPQBlock *)pBlockTable); + else + nError = ConvertSqpBlockTable(ha, pFileTable, (TSQPBlock *)pBlockTable); } else { -- cgit v1.2.3