diff options
-rw-r--r-- | src/SBaseFileTable.cpp | 90 | ||||
-rw-r--r-- | src/SFileGetFileInfo.cpp | 13 | ||||
-rw-r--r-- | src/StormCommon.h | 1 | ||||
-rw-r--r-- | test/StormTest.cpp | 6 |
4 files changed, 77 insertions, 33 deletions
diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index 635db08..7209de2 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -568,6 +568,20 @@ int ConvertMpqHeaderToFormat4( //----------------------------------------------------------------------------- // Support for hash table +static DWORD IsValidHashEntry(TMPQArchive * ha, TMPQHash * pHash) +{ + // The block table index must be smaller than file table size + if(pHash->dwBlockIndex < ha->dwFileTableSize) + { + // The block table entry must contain a file + if(ha->pFileTable[pHash->dwBlockIndex].dwFlags & MPQ_FILE_EXISTS) + return pHash->dwBlockIndex; + } + + // Return an invalid index + return HASH_ENTRY_FREE; +} + // Returns a hash table entry in the following order: // 1) A hash table entry with the neutral locale // 2) A hash table entry with any other locale @@ -2034,7 +2048,7 @@ int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize) return ERROR_SUCCESS; } -TMPQHash * LoadHashTable(TMPQArchive * ha) +static TMPQHash * LoadHashTable(TMPQArchive * ha) { TMPQHeader * pHeader = ha->pHeader; ULONGLONG ByteOffset; @@ -2455,35 +2469,67 @@ int BuildFileTable(TMPQArchive * ha) // Defragment the file table so it does not contain any gaps int DefragmentFileTable(TMPQArchive * ha) { - TFileEntry * pFileTable = ha->pFileTable; - TFileEntry * pSrcEntry = pFileTable; - TFileEntry * pTrgEntry = pFileTable; + TFileEntry * pNewTable; + TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + TMPQHash * pHash; + PDWORD NewIndexes; + DWORD dwNewTableSize = 0; + DWORD dwNewBlockIndex; + DWORD dwBlockIndex = 0; - // Parse the file entries - for(DWORD i = 0; i < ha->dwFileTableSize; i++, pSrcEntry++) + // Allocate brand new file table + NewIndexes = STORM_ALLOC(DWORD, ha->dwFileTableSize); + if(NewIndexes != NULL) { - // Is the source file entry does not contain anything valid, we skip it - if(pSrcEntry->dwFlags & MPQ_FILE_EXISTS) + // Clear the file table + memset(NewIndexes, 0xFF, sizeof(DWORD) * ha->dwFileTableSize); + + // Create map of OldBlockIndex => NewBlockIndex + for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) { - // Fix the block table index and move the entry - if(pTrgEntry != pSrcEntry) + // If the entry is valid, move it + if((dwBlockIndex = IsValidHashEntry(ha, pHash)) != HASH_ENTRY_FREE) { - if(ha->pHashTable != NULL) - ha->pHashTable[pSrcEntry->dwHashIndex].dwBlockIndex = (DWORD)(pTrgEntry - pFileTable); - memmove(pTrgEntry, pSrcEntry, sizeof(TFileEntry)); + // Was the new index already resolved? + // Note: More hash entries can point to the same file + if(NewIndexes[dwBlockIndex] == 0xFFFFFFFF) + NewIndexes[dwBlockIndex] = dwNewTableSize++; } + } + + // Allocate new file table + pNewTable = STORM_ALLOC(TFileEntry, dwNewTableSize); + if(pNewTable != NULL) + { + // Fill the new table with zeros + memset(pNewTable, 0, sizeof(TFileEntry) * dwNewTableSize); + + // Create map of OldBlockIndex => NewBlockIndex + for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) + { + // If the entry is valid, move it + if((dwBlockIndex = IsValidHashEntry(ha, pHash)) != HASH_ENTRY_FREE) + { + // The new index should be resolved by now + assert(NewIndexes[dwBlockIndex] != 0xFFFFFFFF); + dwNewBlockIndex = NewIndexes[dwBlockIndex]; - // Move the target pointer by one entry - pTrgEntry++; + // Remap the old entry to the new entry + pNewTable[dwNewBlockIndex] = ha->pFileTable[dwBlockIndex]; + pHash->dwBlockIndex = dwNewBlockIndex; + } + } + + // Replace the old file table with the new table + STORM_FREE(ha->pFileTable); + ha->pFileTable = pNewTable; + ha->dwFileTableSize = dwNewTableSize; } - } - // Store the new size of the file table - ha->dwFileTableSize = (DWORD)(pTrgEntry - pFileTable); + // Free the conversion table + STORM_FREE(NewIndexes); + } - // Fill the rest with zeros - if(ha->dwFileTableSize < ha->dwMaxFileCount) - memset(pFileTable + ha->dwFileTableSize, 0, (ha->dwMaxFileCount - ha->dwFileTableSize) * sizeof(TFileEntry)); return ERROR_SUCCESS; } @@ -2502,7 +2548,7 @@ int ShrinkMalformedMpqTables(TMPQArchive * ha) // // Note: Unhandled possible case where multiple hash table entries - // point to the same block tabl entry. + // point to the same block table entry. // // Now perform the defragmentation of the block table diff --git a/src/SFileGetFileInfo.cpp b/src/SFileGetFileInfo.cpp index 753d2fd..d6d6da3 100644 --- a/src/SFileGetFileInfo.cpp +++ b/src/SFileGetFileInfo.cpp @@ -373,16 +373,11 @@ bool WINAPI SFileGetFileInfo( case SFileMpqHashTable: ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) + if(ha != NULL && ha->pHashTable != NULL) { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - if(MAKE_OFFSET64(ha->pHeader->wHashTablePosHi, ha->pHeader->dwHashTablePos) != 0) - { - cbSrcFileInfo = ha->pHeader->dwHashTableSize * sizeof(TMPQHash); - if(cbFileInfo >= cbSrcFileInfo) - pvSrcFileInfo = LoadHashTable(ha); - nInfoType = SFILE_INFO_TYPE_ALLOCATED; - } + pvSrcFileInfo = ha->pHashTable; + cbSrcFileInfo = ha->pHeader->dwHashTableSize * sizeof(TMPQHash); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; diff --git a/src/StormCommon.h b/src/StormCommon.h index 00ea3d8..c806ce0 100644 --- a/src/StormCommon.h +++ b/src/StormCommon.h @@ -185,7 +185,6 @@ TMPQExtHeader * LoadExtTable(TMPQArchive * ha, ULONGLONG ByteOffset, size_t Size TMPQHetTable * LoadHetTable(TMPQArchive * ha); TMPQBetTable * LoadBetTable(TMPQArchive * ha); -TMPQHash * LoadHashTable(TMPQArchive * ha); TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool bDontFixEntries = false); TMPQBlock * TranslateBlockTable(TMPQArchive * ha, ULONGLONG * pcbTableSize, bool * pbNeedHiBlockTable); diff --git a/test/StormTest.cpp b/test/StormTest.cpp index d0a7f83..8a3d631 100644 --- a/test/StormTest.cpp +++ b/test/StormTest.cpp @@ -4052,7 +4052,7 @@ int main(int argc, char * argv[]) // Open a stream, paired with remote master (takes hell lot of time!) // if(nError == ERROR_SUCCESS) // nError = TestReadFile_MasterMirror("MPQ_2013_v4_alternate-downloaded.MPQ", "http://www.zezula.net\\mpqs\\alternate.zip", false); -*/ + // Search in listfile if(nError == ERROR_SUCCESS) nError = TestSearchListFile("ListFile_Blizzard.txt"); @@ -4126,6 +4126,10 @@ int main(int argc, char * argv[]) // Open an Warcraft III map locked by Spazy protector if(nError == ERROR_SUCCESS) nError = TestOpenArchive("MPQ_2015_v1_ProtectedMap_Spazy.w3x"); +*/ + // Open an empty archive (found in WoW cache - it's just a header) + if(nError == ERROR_SUCCESS) + nError = TestOpenArchive("flem1.w3x"); // Open an Warcraft III map whose "(attributes)" file has (BlockTableSize-1) entries if(nError == ERROR_SUCCESS) |