aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/SBaseFileTable.cpp90
-rw-r--r--src/SFileGetFileInfo.cpp13
-rw-r--r--src/StormCommon.h1
-rw-r--r--test/StormTest.cpp6
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)