aboutsummaryrefslogtreecommitdiff
path: root/src/SBaseFileTable.cpp
diff options
context:
space:
mode:
authorLadislav Zezula <ladislav.zezula@avg.com>2014-08-25 12:59:01 +0200
committerLadislav Zezula <ladislav.zezula@avg.com>2014-08-25 12:59:01 +0200
commitd7044aecaeb2bbb33e0f5cc2080d5b2995bd79d5 (patch)
treee260ef9d1415bef05b0071af573512d90f93ae3b /src/SBaseFileTable.cpp
parent2b5b7e977145c16f6ec23933c41678c382d22de3 (diff)
+ Added support for newest Spazzler protector
Diffstat (limited to 'src/SBaseFileTable.cpp')
-rw-r--r--src/SBaseFileTable.cpp259
1 files changed, 102 insertions, 157 deletions
diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp
index a2eb5a8..33e1472 100644
--- a/src/SBaseFileTable.cpp
+++ b/src/SBaseFileTable.cpp
@@ -56,30 +56,6 @@ static DWORD GetNecessaryBitCount(ULONGLONG MaxValue)
return dwBitCount;
}
-static int CompareFilePositions(const void * p1, const void * p2)
-{
- TMPQBlock * pBlock1 = *(TMPQBlock **)p1;
- TMPQBlock * pBlock2 = *(TMPQBlock **)p2;
- DWORD dwFileEnd1;
- DWORD dwFileEnd2;
-
- // Compare file begins
- if(pBlock1->dwFilePos < pBlock2->dwFilePos)
- return -1;
- if(pBlock1->dwFilePos > pBlock2->dwFilePos)
- return +1;
-
- // If the files begin at the same position, compare their ends
- dwFileEnd1 = pBlock1->dwFilePos + pBlock1->dwCSize;
- dwFileEnd2 = pBlock2->dwFilePos + pBlock2->dwCSize;
- if(dwFileEnd1 < dwFileEnd2)
- return -1;
- if(dwFileEnd1 > dwFileEnd2)
- return +1;
-
- return 0;
-}
-
//-----------------------------------------------------------------------------
// Support functions for BIT_ARRAY
@@ -240,27 +216,6 @@ void SetBits(
//-----------------------------------------------------------------------------
// Support for MPQ header
-static DWORD GetMaxFileOffset32(TMPQArchive * ha)
-{
- TMPQHeader * pHeader = ha->pHeader;
- DWORD dwMaxFileOffset = ha->pHeader->dwArchiveSize;
-
- // We can call this only for malformed archives v 1.0
- assert(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1);
- assert((ha->dwFlags & MPQ_FLAG_MALFORMED) != 0);
-
- // If the block table is at the end, decrement the limit by the block table
- if(pHeader->dwBlockTablePos + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)) >= dwMaxFileOffset)
- dwMaxFileOffset = pHeader->dwBlockTablePos;
-
- // If the hash table is at the end, decrement the limit by the hash table
- if(pHeader->dwHashTablePos + (pHeader->dwHashTableSize * sizeof(TMPQBlock)) >= dwMaxFileOffset)
- dwMaxFileOffset = pHeader->dwHashTablePos;
-
- // Return what we found
- return dwMaxFileOffset;
-}
-
static ULONGLONG DetermineArchiveSize_V1(
TMPQArchive * ha,
TMPQHeader * pHeader,
@@ -2049,78 +2004,40 @@ void InvalidateInternalFiles(TMPQArchive * ha)
//-----------------------------------------------------------------------------
// Support for file tables - hash table, block table, hi-block table
-int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize)
-{
- TMPQHash * pHashTable;
-
- // Sanity checks
- assert((dwHashTableSize & (dwHashTableSize - 1)) == 0);
- assert(ha->pHashTable == NULL);
-
- // If the required hash table size is zero, don't create anything
- if(dwHashTableSize == 0)
- dwHashTableSize = HASH_TABLE_SIZE_DEFAULT;
-
- // Create the hash table
- pHashTable = STORM_ALLOC(TMPQHash, dwHashTableSize);
- if(pHashTable == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Fill it
- memset(pHashTable, 0xFF, dwHashTableSize * sizeof(TMPQHash));
- ha->dwMaxFileCount = dwHashTableSize;
- ha->pHashTable = pHashTable;
- return ERROR_SUCCESS;
-}
-
-TMPQHash * LoadHashTable(TMPQArchive * ha)
+// Fixes the case when hash table size is set to arbitrary long value
+static void FixHashTableSize(TMPQArchive * ha, ULONGLONG ByteOffset)
{
- TMPQHeader * pHeader = ha->pHeader;
- ULONGLONG ByteOffset;
- TMPQHash * pHashTable = NULL;
- DWORD dwTableSize;
- DWORD dwCmpSize;
-
- // If the MPQ has no hash table, do nothing
- if(pHeader->dwHashTablePos == 0 && pHeader->wHashTablePosHi == 0)
- return NULL;
+ ULONGLONG FileSize = 0;
+ DWORD dwFixedTableSize;
+ DWORD dwHashTableSize = ha->pHeader->dwHashTableSize;
- // If the hash table size is zero, do nothing
- if(pHeader->dwHashTableSize == 0)
- return NULL;
+ // Spazzler protector abuses the fact that hash table size does not matter
+ // if the hash table is entirely filled with values different than 0xFFFFFFFF.
+ // It sets the hash table size to 0x00100000, which slows down file searching
+ // and adding a listfile.
- // Load the hash table for MPQ variations
- switch(ha->dwSubType)
+ // Only if the hash table size is correct
+ if((dwHashTableSize & (dwHashTableSize - 1)) == 0)
{
- case MPQ_SUBTYPE_MPQ:
-
- // Get the compressed and uncompressed hash table size
- dwTableSize = pHeader->dwHashTableSize * sizeof(TMPQHash);
- dwCmpSize = (DWORD)pHeader->HashTableSize64;
-
- //
- // Load the table from the MPQ, with decompression
- //
- // Note: We will NOT check if the hash table is properly decrypted.
- // Some MPQ protectors corrupt the hash table by rewriting part of it.
- // Hash table, the way how it works, allows arbitrary values for unused entries.
- //
+ // Retrieve the file size
+ FileStream_GetSize(ha->pStream, &FileSize);
- ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos);
- pHashTable = (TMPQHash *)LoadMpqTable(ha, ByteOffset, dwCmpSize, dwTableSize, MPQ_KEY_HASH_TABLE);
- break;
+ // Work as long as the size greater than file size
+ for(;;)
+ {
+ // Try the size of one half of the current
+ dwFixedTableSize = dwHashTableSize >> 1;
+ if(ByteOffset + (dwFixedTableSize * sizeof(TMPQHash)) <= FileSize)
+ break;
- case MPQ_SUBTYPE_SQP:
- pHashTable = LoadSqpHashTable(ha);
- break;
+ // Cut the hash table size to half
+ dwHashTableSize = dwFixedTableSize;
+ }
- case MPQ_SUBTYPE_MPK:
- pHashTable = LoadMpkHashTable(ha);
- break;
+ // Fix the hash table size
+ ha->pHeader->dwHashTableSize = dwHashTableSize;
+ ha->pHeader->HashTableSize64 = dwHashTableSize * sizeof(TMPQHash);
}
-
- // Return the hash table
- return pHashTable;
}
// This function fixes the scenario then dwBlockTableSize
@@ -2174,64 +2091,90 @@ static void FixBlockTableSize(
}
}
-// This function fixes the scenario then dwBlockTableSize
-// is greater and goes into a MPQ file
-static void FixCompressedFileSize(
- TMPQArchive * ha,
- TMPQBlock * pBlockTable)
+int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize)
{
- TMPQHeader * pHeader = ha->pHeader;
- TMPQBlock ** SortTable;
- TMPQBlock * pBlockTableEnd = pBlockTable + pHeader->dwBlockTableSize;
- TMPQBlock * pBlock;
- size_t nElements = 0;
- size_t nIndex;
- DWORD dwMaxFileOffs;
+ TMPQHash * pHashTable;
- // Only perform this check on MPQs version 1.0
- assert(pHeader->dwHeaderSize == MPQ_HEADER_SIZE_V1);
+ // Sanity checks
+ assert((dwHashTableSize & (dwHashTableSize - 1)) == 0);
+ assert(ha->pHashTable == NULL);
- // Allocate sort table for all entries
- SortTable = STORM_ALLOC(TMPQBlock*, pHeader->dwBlockTableSize);
- if(SortTable != NULL)
- {
- // Calculate the end of the archive
- dwMaxFileOffs = GetMaxFileOffset32(ha);
+ // If the required hash table size is zero, don't create anything
+ if(dwHashTableSize == 0)
+ dwHashTableSize = HASH_TABLE_SIZE_DEFAULT;
- // Put all blocks to a sort table
- for(pBlock = pBlockTable; pBlock < pBlockTableEnd; pBlock++)
- {
- if(pBlock->dwFlags & MPQ_FILE_EXISTS)
- SortTable[nElements++] = pBlock;
- }
+ // Create the hash table
+ pHashTable = STORM_ALLOC(TMPQHash, dwHashTableSize);
+ if(pHashTable == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
- // Have we found at least one block?
- if(nElements > 0)
- {
- // Sort the table
- qsort(SortTable, nElements, sizeof(TMPQBlock *), CompareFilePositions);
+ // Fill it
+ memset(pHashTable, 0xFF, dwHashTableSize * sizeof(TMPQHash));
+ ha->dwMaxFileCount = dwHashTableSize;
+ ha->pHashTable = pHashTable;
+ return ERROR_SUCCESS;
+}
+
+TMPQHash * LoadHashTable(TMPQArchive * ha)
+{
+ TMPQHeader * pHeader = ha->pHeader;
+ ULONGLONG ByteOffset;
+ TMPQHash * pHashTable = NULL;
+ DWORD dwTableSize;
+ DWORD dwCmpSize;
+
+ // If the MPQ has no hash table, do nothing
+ if(pHeader->dwHashTablePos == 0 && pHeader->wHashTablePosHi == 0)
+ return NULL;
+
+ // If the hash table size is zero, do nothing
+ if(pHeader->dwHashTableSize == 0)
+ return NULL;
+
+ // Load the hash table for MPQ variations
+ switch(ha->dwSubType)
+ {
+ case MPQ_SUBTYPE_MPQ:
- // Walk the table and set all compressed sizes to they
- // match the difference (dwFilePos1 - dwFilePos0)
- for(nIndex = 0; nIndex < nElements - 1; nIndex++)
+ ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos);
+ if((pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) && (ha->dwFlags & MPQ_FLAG_MALFORMED))
{
- TMPQBlock * pBlock1 = SortTable[nIndex + 1];
- TMPQBlock * pBlock0 = SortTable[nIndex];
-
- pBlock0->dwCSize = (pBlock0->dwFlags & MPQ_FILE_COMPRESS_MASK) ? (pBlock1->dwFilePos - pBlock0->dwFilePos) : pBlock0->dwFSize;
+ // Calculate the hash table offset as 32-bit value
+ ByteOffset = (DWORD)ha->MpqPos + pHeader->dwHashTablePos;
+
+ // Defense against map protectors that set the hash table size too big
+ FixHashTableSize(ha, ByteOffset);
}
- // Fix the last entry
- pBlock = SortTable[nElements - 1];
- pBlock->dwCSize = (pBlock->dwFlags & MPQ_FILE_COMPRESS_MASK) ? (dwMaxFileOffs - pBlock->dwFilePos) : pBlock->dwFSize;
- }
+ // Get the compressed and uncompressed hash table size
+ dwTableSize = pHeader->dwHashTableSize * sizeof(TMPQHash);
+ dwCmpSize = (DWORD)pHeader->HashTableSize64;
+
+ //
+ // Load the table from the MPQ, with decompression
+ //
+ // Note: We will NOT check if the hash table is properly decrypted.
+ // Some MPQ protectors corrupt the hash table by rewriting part of it.
+ // Hash table, the way how it works, allows arbitrary values for unused entries.
+ //
+
+ pHashTable = (TMPQHash *)LoadMpqTable(ha, ByteOffset, dwCmpSize, dwTableSize, MPQ_KEY_HASH_TABLE);
+ break;
+
+ case MPQ_SUBTYPE_SQP:
+ pHashTable = LoadSqpHashTable(ha);
+ break;
- STORM_FREE(SortTable);
+ case MPQ_SUBTYPE_MPK:
+ pHashTable = LoadMpkHashTable(ha);
+ break;
}
-}
+ // Return the hash table
+ return pHashTable;
+}
-TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool bDontFixEntries)
+TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool /* bDontFixEntries */)
{
TMPQHeader * pHeader = ha->pHeader;
TMPQBlock * pBlockTable = NULL;
@@ -2278,9 +2221,11 @@ TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool bDontFixEntries)
if(pBlockTable != NULL)
FixBlockTableSize(ha, pBlockTable);
- // Defense against protectors that set invalid compressed size
- if((ha->dwFlags & MPQ_FLAG_MALFORMED) && (bDontFixEntries == false))
- FixCompressedFileSize(ha, pBlockTable);
+ //
+ // Note: TMPQBlock::dwCSize file size can be completely invalid
+ // for compressed files and the file can still be read.
+ // Abused by some MPQ protectors
+ //
}
break;