aboutsummaryrefslogtreecommitdiff
path: root/dep/StormLib/src/SBaseFileTable.cpp
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2012-07-05 14:16:44 +0200
committerShauren <shauren.trinity@gmail.com>2012-07-05 14:16:44 +0200
commit32ba32c4ebe56ba931c8638460c24cd57ae29a75 (patch)
tree2f757648b85ec8989a7dfc573b954b9b4d718650 /dep/StormLib/src/SBaseFileTable.cpp
parentc95905ddbb22e2b5b5362b790aa851ef10d4e27e (diff)
parented6f3e2deff55f913f9646db5f540b7704088478 (diff)
Merge branch '4.x' of github.com:TrinityCore/TrinityCore into 4.3.4
Diffstat (limited to 'dep/StormLib/src/SBaseFileTable.cpp')
-rw-r--r--dep/StormLib/src/SBaseFileTable.cpp611
1 files changed, 342 insertions, 269 deletions
diff --git a/dep/StormLib/src/SBaseFileTable.cpp b/dep/StormLib/src/SBaseFileTable.cpp
index 83d31bbac94..09379ca5da7 100644
--- a/dep/StormLib/src/SBaseFileTable.cpp
+++ b/dep/StormLib/src/SBaseFileTable.cpp
@@ -15,7 +15,8 @@
//-----------------------------------------------------------------------------
// Local defines
-#define MAX_FLAG_INDEX 256
+#define INVALID_FLAG_VALUE 0xCCCCCCCC
+#define MAX_FLAG_INDEX 512
//-----------------------------------------------------------------------------
// Local structures
@@ -62,63 +63,10 @@ typedef struct _BET_TABLE_HEADER
//-----------------------------------------------------------------------------
// Support for calculating bit sizes
-static DWORD GetNecessaryBitCount(ULONGLONG MaxValue)
-{
- DWORD dwBitCount = 0;
-
- while(MaxValue > 0)
- {
- MaxValue >>= 1;
- dwBitCount++;
- }
-
- return dwBitCount;
-}
-
-static BYTE GetFlagsComboIndex(DWORD dwFlags)
+static void InitFileFlagArray(LPDWORD FlagArray)
{
- BYTE FlagComboIndex = 0;
-
- //
- // We only use 8 different bits. This allows us to
- // construct 256 unique values for every possible combination of MPQ file flags.
- //
-
- if(dwFlags & MPQ_FILE_IMPLODE)
- FlagComboIndex |= 0x01;
- dwFlags &= ~MPQ_FILE_IMPLODE;
-
- if(dwFlags & MPQ_FILE_COMPRESS)
- FlagComboIndex |= 0x02;
- dwFlags &= ~MPQ_FILE_COMPRESS;
-
- if(dwFlags & MPQ_FILE_ENCRYPTED)
- FlagComboIndex |= 0x04;
- dwFlags &= ~MPQ_FILE_ENCRYPTED;
-
- if(dwFlags & MPQ_FILE_FIX_KEY)
- FlagComboIndex |= 0x08;
- dwFlags &= ~MPQ_FILE_FIX_KEY;
-
- if(dwFlags & MPQ_FILE_PATCH_FILE)
- FlagComboIndex |= 0x10;
- dwFlags &= ~MPQ_FILE_PATCH_FILE;
-
- if(dwFlags & MPQ_FILE_SINGLE_UNIT)
- FlagComboIndex |= 0x20;
- dwFlags &= ~MPQ_FILE_SINGLE_UNIT;
-
- if(dwFlags & MPQ_FILE_DELETE_MARKER)
- FlagComboIndex |= 0x40;
- dwFlags &= ~MPQ_FILE_DELETE_MARKER;
-
- if(dwFlags & MPQ_FILE_SECTOR_CRC)
- FlagComboIndex |= 0x80;
- dwFlags &= ~MPQ_FILE_SECTOR_CRC;
-
- // Sanity check - the flags must now be zero
- assert((dwFlags & 0x7FFFFFFF) == 0);
- return FlagComboIndex;
+ for(DWORD dwFlagIndex = 0; dwFlagIndex < MAX_FLAG_INDEX; dwFlagIndex++)
+ FlagArray[dwFlagIndex] = INVALID_FLAG_VALUE;
}
static DWORD GetFileFlagIndex(LPDWORD FlagArray, DWORD dwFlags)
@@ -126,7 +74,7 @@ static DWORD GetFileFlagIndex(LPDWORD FlagArray, DWORD dwFlags)
// Find free or equal entry in the flag array
for(DWORD dwFlagIndex = 0; dwFlagIndex < MAX_FLAG_INDEX; dwFlagIndex++)
{
- if(FlagArray[dwFlagIndex] == 0 || FlagArray[dwFlagIndex] == dwFlags)
+ if(FlagArray[dwFlagIndex] == INVALID_FLAG_VALUE || FlagArray[dwFlagIndex] == dwFlags)
{
FlagArray[dwFlagIndex] = dwFlags;
return dwFlagIndex;
@@ -138,6 +86,19 @@ static DWORD GetFileFlagIndex(LPDWORD FlagArray, DWORD dwFlags)
return 0xFFFFFFFF;
}
+static DWORD GetNecessaryBitCount(ULONGLONG MaxValue)
+{
+ DWORD dwBitCount = 0;
+
+ while(MaxValue > 0)
+ {
+ MaxValue >>= 1;
+ dwBitCount++;
+ }
+
+ return dwBitCount;
+}
+
//-----------------------------------------------------------------------------
// Support functions for BIT_ARRAY
@@ -420,8 +381,7 @@ static TMPQBlock * TranslateBlockTable(
BlockTableSize = sizeof(TMPQBlock) * ha->dwFileTableSize;
for(DWORD i = 0; i < ha->dwFileTableSize; i++)
{
- if(pFileEntry->ByteOffset >> 32)
- bNeedHiBlockTable = true;
+ bNeedHiBlockTable = (pFileEntry->ByteOffset >> 32) ? true : false;
pBlock->dwFilePos = (DWORD)pFileEntry->ByteOffset;
pBlock->dwFSize = pFileEntry->dwFileSize;
pBlock->dwCSize = pFileEntry->dwCmpSize;
@@ -519,11 +479,15 @@ TMPQExtTable * LoadExtTable(
int cbOutBuffer = (int)pCompressed->dwDataSize;
int cbInBuffer = (int)Size;
- // Decompress the XXX block
+ // Decompress the extended table
pExtTable->dwSignature = pCompressed->dwSignature;
pExtTable->dwVersion = pCompressed->dwVersion;
pExtTable->dwDataSize = pCompressed->dwDataSize;
- SCompDecompress((char *)(pExtTable + 1), &cbOutBuffer, (char *)(pCompressed + 1), cbInBuffer);
+ if(!SCompDecompress2((char *)(pExtTable + 1), &cbOutBuffer, (char *)(pCompressed + 1), cbInBuffer))
+ {
+ STORM_FREE(pExtTable);
+ pExtTable = NULL;
+ }
}
// Free the compressed block
@@ -537,9 +501,9 @@ TMPQExtTable * LoadExtTable(
}
// Used in MPQ Editor
-void FreeExtTable(TMPQExtTable * pExtTable)
+void FreeMpqBuffer(void * pvBuffer)
{
- STORM_FREE(pExtTable);
+ STORM_FREE(pvBuffer);
}
static int SaveMpqTable(
@@ -1018,26 +982,18 @@ static void CreateBetHeader(
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
TFileEntry * pFileEntry;
ULONGLONG MaxByteOffset = 0;
+ DWORD FlagArray[MAX_FLAG_INDEX];
+ DWORD dwMaxFlagIndex = 0;
DWORD dwMaxFileSize = 0;
DWORD dwMaxCmpSize = 0;
- DWORD dwFlagCount = 0;
- BYTE FlagComboArray[MAX_FLAG_INDEX];
- BYTE FlagComboIndex;
+ DWORD dwFlagIndex;
// Initialize array of flag combinations
- memset(FlagComboArray, 0, sizeof(FlagComboArray));
+ InitFileFlagArray(FlagArray);
// Get the maximum values for the BET table
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
{
- // We don't allow the file table to have free entries in the middle
- // This must be a bug in the library somewhere.
- if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
- {
- pFileEntry->dwFlags = MPQ_FILE_EXISTS | MPQ_FILE_DELETE_MARKER;
- assert(false);
- }
-
// Highest file position in the MPQ
if(pFileEntry->ByteOffset > MaxByteOffset)
MaxByteOffset = pFileEntry->ByteOffset;
@@ -1051,10 +1007,9 @@ static void CreateBetHeader(
dwMaxCmpSize = pFileEntry->dwCmpSize;
// Check if this flag was there before
- FlagComboIndex = GetFlagsComboIndex(pFileEntry->dwFlags);
- if(FlagComboArray[FlagComboIndex] == 0)
- dwFlagCount++;
- FlagComboArray[FlagComboIndex] = 1;
+ dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags);
+ if(dwFlagIndex > dwMaxFlagIndex)
+ dwMaxFlagIndex = dwFlagIndex;
}
// Now save bit count for every piece of file information
@@ -1068,7 +1023,7 @@ static void CreateBetHeader(
pBetHeader->dwBitCount_CmpSize = GetNecessaryBitCount(dwMaxCmpSize);
pBetHeader->dwBitIndex_FlagIndex = pBetHeader->dwBitIndex_CmpSize + pBetHeader->dwBitCount_CmpSize;
- pBetHeader->dwBitCount_FlagIndex = GetNecessaryBitCount(dwFlagCount);
+ pBetHeader->dwBitCount_FlagIndex = GetNecessaryBitCount(dwMaxFlagIndex + 1);
pBetHeader->dwBitIndex_Unknown = pBetHeader->dwBitIndex_FlagIndex + pBetHeader->dwBitCount_FlagIndex;
pBetHeader->dwBitCount_Unknown = 0;
@@ -1082,7 +1037,7 @@ static void CreateBetHeader(
// Save the file count and flag count
pBetHeader->dwFileCount = ha->dwFileTableSize;
- pBetHeader->dwFlagCount = dwFlagCount;
+ pBetHeader->dwFlagCount = dwMaxFlagIndex + 1;
pBetHeader->dwUnknown08 = 0x10;
// Save the total size of the BET hash
@@ -1224,8 +1179,8 @@ TMPQExtTable * TranslateBetTable(
DWORD i;
// Calculate the bit sizes of various entries
+ InitFileFlagArray(FlagArray);
CreateBetHeader(ha, &BetHeader);
- memset(FlagArray, 0, sizeof(FlagArray));
// Calculate the size of the BET table
BetTableSize = sizeof(BET_TABLE_HEADER) +
@@ -1260,30 +1215,32 @@ TMPQExtTable * TranslateBetTable(
// Construct the array of flag values and bit-based file table
for(i = 0; i < BetHeader.dwFileCount; i++, pFileEntry++)
{
- // Only count files that exist
- if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
- {
- // Save the byte offset
- pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FilePos,
- BetHeader.dwBitCount_FilePos,
- &pFileEntry->ByteOffset,
- 8);
- pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FileSize,
- BetHeader.dwBitCount_FileSize,
- &pFileEntry->dwFileSize,
- 4);
- pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_CmpSize,
- BetHeader.dwBitCount_CmpSize,
- &pFileEntry->dwCmpSize,
- 4);
-
- // Get the flag array for the file
- dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags);
- pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FlagIndex,
- BetHeader.dwBitCount_FlagIndex,
- &dwFlagIndex,
- 4);
- }
+ //
+ // Note: Blizzard MPQs contain valid values even for non-existant files
+ // (FilePos, FileSize, CmpSize and FlagIndex)
+ // Note: If flags is zero, it must be in the flag table too !!!
+ //
+
+ // Save the byte offset
+ pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FilePos,
+ BetHeader.dwBitCount_FilePos,
+ &pFileEntry->ByteOffset,
+ 8);
+ pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FileSize,
+ BetHeader.dwBitCount_FileSize,
+ &pFileEntry->dwFileSize,
+ 4);
+ pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_CmpSize,
+ BetHeader.dwBitCount_CmpSize,
+ &pFileEntry->dwCmpSize,
+ 4);
+
+ // Save the flag index
+ dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags);
+ pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FlagIndex,
+ BetHeader.dwBitCount_FlagIndex,
+ &dwFlagIndex,
+ 4);
// Move the bit offset
nBitOffset += BetHeader.dwTableEntrySize;
@@ -1480,7 +1437,7 @@ void AllocateFileName(TFileEntry * pFileEntry, const char * szFileName)
TFileEntry * FindFreeFileEntry(TMPQArchive * ha)
{
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
- TFileEntry * pDeletedEntry = NULL;
+ TFileEntry * pFreeEntry = NULL;
TFileEntry * pFileEntry;
// Try to find a free entry
@@ -1488,18 +1445,22 @@ TFileEntry * FindFreeFileEntry(TMPQArchive * ha)
{
// If that entry is free, we reuse it
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
- return pFileEntry;
+ {
+ pFreeEntry = pFileEntry;
+ break;
+ }
- // If that entry is deleted, remember it
- if(pFileEntry->dwFlags & MPQ_FILE_DELETE_MARKER)
- pDeletedEntry = pFileEntry;
+ //
+ // Note: Files with "delete marker" are not deleted.
+ // Don't consider them free entries
+ //
}
// Do we have a deleted entry?
- if(pDeletedEntry != NULL)
+ if(pFreeEntry != NULL)
{
- ClearFileEntry(ha, pDeletedEntry);
- return pDeletedEntry;
+ ClearFileEntry(ha, pFreeEntry);
+ return pFreeEntry;
}
// If no file entry within the existing file table is free,
@@ -1702,9 +1663,11 @@ void ClearFileEntry(
4);
}
- // Free the file name, and zero the entire entry
+ // Free the file name, and set the file entry as deleted
if(pFileEntry->szFileName != NULL)
STORM_FREE(pFileEntry->szFileName);
+
+ // Invalidate the file entry
memset(pFileEntry, 0, sizeof(TFileEntry));
}
@@ -1718,7 +1681,7 @@ int FreeFileEntry(
//
// If we have HET table, we cannot just get rid of the file
- // Doing so would lead to empty gaps in the HET and BET tables
+ // Doing so would lead to empty gaps in the HET table
// We have to keep BET hash, hash index, HET index, locale, platform and file name
//
@@ -1750,14 +1713,10 @@ int FreeFileEntry(
}
else
{
- memset(pFileEntry->md5, 0, MD5_DIGEST_SIZE);
- pFileEntry->ByteOffset = 0;
- pFileEntry->FileTime = 0;
- pFileEntry->dwFileSize = 0;
- pFileEntry->dwCmpSize = 0;
- pFileEntry->dwFlags = MPQ_FILE_EXISTS | MPQ_FILE_DELETE_MARKER;
- pFileEntry->dwCrc32 = 0;
- nError = ERROR_MARKED_FOR_DELETE;
+ // Note: Deleted entries in Blizzard MPQs version 4.0
+ // normally contain valid byte offset and length
+ pFileEntry->dwFlags &= ~MPQ_FILE_EXISTS;
+ nError = ERROR_SUCCESS;
}
return nError;
@@ -1785,49 +1744,104 @@ void InvalidateInternalFiles(TMPQArchive * ha)
ha->dwFlags |= MPQ_FLAG_INV_ATTRIBUTES;
}
- // Remember that the MPQ has been changed and it will ne necessary
+ // Remember that the MPQ has been changed and it will be necessary
// to update the tables
ha->dwFlags |= MPQ_FLAG_CHANGED;
}
//-----------------------------------------------------------------------------
-// Support for file tables - hash table, block table, hi-block table,
-// (attributes) and (listfile)
+// Functions that loads and verify MPQ data bitmap
-static void FixBlockTableSize(
- TMPQArchive * ha,
- TMPQBlock * pBlockTable,
- DWORD dwClaimedSize)
+int LoadMpqDataBitmap(TMPQArchive * ha, ULONGLONG FileSize, bool * pbFileIsComplete)
{
- TMPQHeader * pHeader = ha->pHeader;
- ULONGLONG BlockTableStart;
- ULONGLONG BlockTableEnd;
- ULONGLONG FileDataStart;
+ TMPQBitmap * pBitmap = NULL;
+ TMPQBitmap DataBitmap;
+ ULONGLONG BitmapOffset;
+ ULONGLONG EndOfMpq;
+ DWORD DataBlockCount = 0;
+ DWORD BitmapByteSize;
+ DWORD WholeByteCount;
+ DWORD ExtraBitsCount;
+
+ // Is there enough space for a MPQ bitmap?
+ EndOfMpq = ha->MpqPos + ha->pHeader->ArchiveSize64;
+ FileSize = FileSize - sizeof(TMPQBitmap);
+ if(FileSize > EndOfMpq)
+ {
+ // Try to load the data bitmap from the end of the file
+ if(FileStream_Read(ha->pStream, &FileSize, &DataBitmap, sizeof(TMPQBitmap)))
+ {
+ // Is it a valid data bitmap?
+ BSWAP_ARRAY32_UNSIGNED((LPDWORD)(&DataBitmap), sizeof(TMPQBitmap));
+ if(DataBitmap.dwSignature == MPQ_DATA_BITMAP_SIGNATURE)
+ {
+ // We assume that MPQs with data bitmap begin at position 0
+ assert(ha->MpqPos == 0);
- // Only perform this check on MPQs version 1.0
- if(pHeader->dwHeaderSize == MPQ_HEADER_SIZE_V1)
+ // Calculate the number of extra bytes for data bitmap
+ DataBlockCount = (DWORD)(((ha->pHeader->ArchiveSize64 - 1) / DataBitmap.dwBlockSize) + 1);
+ BitmapByteSize = ((DataBlockCount - 1) / 8) + 1;
+
+ // Verify the data block size
+ BitmapOffset = ((ULONGLONG)DataBitmap.dwMapOffsetHi << 32) | DataBitmap.dwMapOffsetLo;
+ assert((DWORD)(FileSize - BitmapOffset) == BitmapByteSize);
+
+ // Allocate space for the data bitmap
+ pBitmap = (TMPQBitmap *)STORM_ALLOC(BYTE, sizeof(TMPQBitmap) + BitmapByteSize);
+ if(pBitmap != NULL)
+ {
+ // Copy the bitmap header
+ memcpy(pBitmap, &DataBitmap, sizeof(TMPQBitmap));
+
+ // Read the remaining part
+ if(!FileStream_Read(ha->pStream, &BitmapOffset, (pBitmap + 1), BitmapByteSize))
+ {
+ STORM_FREE(pBitmap);
+ pBitmap = NULL;
+ }
+ }
+ }
+ }
+ }
+
+ // If the caller asks for file completeness, check it
+ if(pBitmap != NULL && pbFileIsComplete != NULL)
{
- // Calculate claimed block table begin and end
- BlockTableStart = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
- BlockTableEnd = BlockTableStart + (pHeader->dwBlockTableSize * sizeof(TMPQBlock));
+ LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1);
+ DWORD i;
+ bool bFileIsComplete = true;
- for(DWORD i = 0; i < dwClaimedSize; i++)
+ // Calculate the number of whole bytes and extra bits of the bitmap
+ WholeByteCount = (DataBlockCount / 8);
+ ExtraBitsCount = (DataBlockCount & 7);
+
+ // Verify the whole bytes - their value must be 0xFF
+ for(i = 0; i < WholeByteCount; i++)
{
- // If the block table end goes into that file, fix the block table end
- FileDataStart = ha->MpqPos + pBlockTable[i].dwFilePos;
- if(BlockTableStart < FileDataStart && BlockTableEnd > FileDataStart)
- {
- dwClaimedSize = (DWORD)((FileDataStart - BlockTableStart) / sizeof(TMPQBlock));
- BlockTableEnd = FileDataStart;
- }
+ if(pbBitmap[i] != 0xFF)
+ bFileIsComplete = false;
+ }
+
+ // If there are extra bits, calculate the mask
+ if(ExtraBitsCount != 0)
+ {
+ BYTE ExpectedValue = (BYTE)((1 << ExtraBitsCount) - 1);
+
+ if(pbBitmap[i] != ExpectedValue)
+ bFileIsComplete = false;
}
+
+ // Give the result to the caller
+ *pbFileIsComplete = bFileIsComplete;
}
- // Fix the block table size
- pHeader->BlockTableSize64 = dwClaimedSize * sizeof(TMPQBlock);
- pHeader->dwBlockTableSize = dwClaimedSize;
+ ha->pBitmap = pBitmap;
+ return ERROR_SUCCESS;
}
+//-----------------------------------------------------------------------------
+// Support for file tables - hash table, block table, hi-block table
+
int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize)
{
TMPQHash * pHashTable;
@@ -1851,7 +1865,7 @@ int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize)
return ERROR_SUCCESS;
}
-int LoadHashTable(TMPQArchive * ha)
+TMPQHash * LoadHashTable(TMPQArchive * ha)
{
TMPQHeader * pHeader = ha->pHeader;
ULONGLONG ByteOffset;
@@ -1862,17 +1876,17 @@ int LoadHashTable(TMPQArchive * ha)
// If the MPQ has no hash table, do nothing
if(pHeader->dwHashTablePos == 0 && pHeader->wHashTablePosHi == 0)
- return ERROR_SUCCESS;
+ return NULL;
// If the hash table size is zero, do nothing
if(pHeader->dwHashTableSize == 0)
- return ERROR_SUCCESS;
+ return NULL;
// Allocate buffer for the hash table
dwTableSize = pHeader->dwHashTableSize * sizeof(TMPQHash);
pHashTable = STORM_ALLOC(TMPQHash, pHeader->dwHashTableSize);
if(pHashTable == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
+ return NULL;
// Compressed size of the hash table
dwCmpSize = (DWORD)pHeader->HashTableSize64;
@@ -1891,17 +1905,108 @@ int LoadHashTable(TMPQArchive * ha)
{
STORM_FREE(pHashTable);
pHashTable = NULL;
- return nError;
}
- // Set the maximum file count to the size of the hash table
- // In case there is HET table, we have to keep the file limit
- if(ha->pHetTable == NULL)
- ha->dwMaxFileCount = pHeader->dwHashTableSize;
+ // Return the hash table
+ return pHashTable;
+}
- // Store the hash table to the MPQ
- ha->pHashTable = pHashTable;
- return ERROR_SUCCESS;
+static void FixBlockTableSize(
+ TMPQArchive * ha,
+ TMPQBlock * pBlockTable,
+ DWORD dwClaimedSize)
+{
+ TMPQHeader * pHeader = ha->pHeader;
+ ULONGLONG BlockTableStart;
+ ULONGLONG BlockTableEnd;
+ ULONGLONG FileDataStart;
+
+ // Only perform this check on MPQs version 1.0
+ if(pHeader->dwHeaderSize == MPQ_HEADER_SIZE_V1)
+ {
+ // Calculate claimed block table begin and end
+ BlockTableStart = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
+ BlockTableEnd = BlockTableStart + (pHeader->dwBlockTableSize * sizeof(TMPQBlock));
+
+ for(DWORD i = 0; i < dwClaimedSize; i++)
+ {
+ // If the block table end goes into that file, fix the block table end
+ FileDataStart = ha->MpqPos + pBlockTable[i].dwFilePos;
+ if(BlockTableStart < FileDataStart && BlockTableEnd > FileDataStart)
+ {
+ dwClaimedSize = (DWORD)((FileDataStart - BlockTableStart) / sizeof(TMPQBlock));
+ BlockTableEnd = FileDataStart;
+ }
+ }
+ }
+
+ // Fix the block table size
+ pHeader->BlockTableSize64 = dwClaimedSize * sizeof(TMPQBlock);
+ pHeader->dwBlockTableSize = dwClaimedSize;
+}
+
+TMPQBlock * LoadBlockTable(TMPQArchive * ha, ULONGLONG FileSize)
+{
+ TMPQHeader * pHeader = ha->pHeader;
+ TMPQBlock * pBlockTable;
+ ULONGLONG ByteOffset;
+ DWORD dwTableSize;
+ DWORD dwCmpSize;
+ int nError;
+
+ // Do nothing if the block table position is zero
+ if(pHeader->dwBlockTablePos == 0 && pHeader->wBlockTablePosHi == 0)
+ return NULL;
+
+ // Do nothing if the block table size is zero
+ if(pHeader->dwBlockTableSize == 0)
+ return NULL;
+
+ // Sanity check, enforced by LoadAnyHashTable
+ assert(ha->dwMaxFileCount >= pHeader->dwBlockTableSize);
+
+ // Calculate sizes of both tables
+ ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
+ dwTableSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
+ dwCmpSize = (DWORD)pHeader->BlockTableSize64;
+
+ // Allocate space for the block table
+ // Note: pHeader->dwBlockTableSize can be zero !!!
+ pBlockTable = STORM_ALLOC(TMPQBlock, ha->dwMaxFileCount);
+ if(pBlockTable == NULL)
+ return NULL;
+
+ // Fill the block table with zeros
+ memset(pBlockTable, 0, dwTableSize);
+
+ // I found a MPQ which claimed 0x200 entries in the block table,
+ // but the file was cut and there was only 0x1A0 entries.
+ // We will handle this case properly.
+ if(dwTableSize == dwCmpSize && (ByteOffset + dwTableSize) > FileSize)
+ {
+ pHeader->dwBlockTableSize = (DWORD)((FileSize - ByteOffset) / sizeof(TMPQBlock));
+ pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
+ dwTableSize = dwCmpSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
+ }
+
+ //
+ // One of the first cracked versions of Diablo I had block table unencrypted
+ // StormLib does NOT support such MPQs anymore, as they are incompatible
+ // with compressed block table feature
+ //
+
+ // Load the block table
+ nError = LoadMpqTable(ha, ByteOffset, pBlockTable, dwCmpSize, dwTableSize, MPQ_KEY_BLOCK_TABLE);
+ if(nError != ERROR_SUCCESS)
+ {
+ // Failed, sorry
+ STORM_FREE(pBlockTable);
+ return NULL;
+ }
+
+ // Defense against MPQs that claim block table to be bigger than it really is
+ FixBlockTableSize(ha, pBlockTable, pHeader->dwBlockTableSize);
+ return pBlockTable;
}
int LoadHetTable(TMPQArchive * ha)
@@ -1963,19 +2068,25 @@ TMPQBetTable * LoadBetTable(TMPQArchive * ha)
int LoadAnyHashTable(TMPQArchive * ha)
{
TMPQHeader * pHeader = ha->pHeader;
- bool bHashTableLoaded = false;
// If the MPQ archive is empty, don't bother trying to load anything
if(pHeader->dwHashTableSize == 0 && pHeader->HetTableSize64 == 0)
return CreateHashTable(ha, HASH_TABLE_SIZE_DEFAULT);
- // Try to load HET table
- if(LoadHetTable(ha) == ERROR_SUCCESS)
- bHashTableLoaded = true;
+ // Try to load HET and/or classic hash table
+ LoadHetTable(ha);
- // Try to load the classic hash table
- if(LoadHashTable(ha) == ERROR_SUCCESS)
- bHashTableLoaded = true;
+ // Load the HASH table
+ ha->pHashTable = LoadHashTable(ha);
+
+ // Set the maximum file count to the size of the hash table
+ // In case there is HET table, we have to keep the file limit
+ if(ha->pHetTable == NULL)
+ ha->dwMaxFileCount = pHeader->dwHashTableSize;
+
+ // Did at least one succeed?
+ if(ha->pHetTable == NULL && ha->pHashTable == NULL)
+ return ERROR_FILE_CORRUPT;
// In theory, a MPQ could have bigger block table than hash table
if(ha->pHeader->dwBlockTableSize > ha->dwMaxFileCount)
@@ -1984,7 +2095,7 @@ int LoadAnyHashTable(TMPQArchive * ha)
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
}
- return bHashTableLoaded ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
+ return ERROR_SUCCESS;
}
int BuildFileTable_Classic(
@@ -2001,118 +2112,80 @@ int BuildFileTable_Classic(
// Sanity checks
assert(ha->pHashTable != NULL);
- // Do nothing if the size of the block table is zero
- if(pHeader->dwBlockTablePos != 0 && pHeader->dwBlockTableSize != 0)
+ // Load the block table
+ pBlockTable = LoadBlockTable(ha, FileSize);
+ if(pBlockTable != NULL)
{
- // Sanity check, enforced by LoadAnyHashTable
- assert(ha->dwMaxFileCount >= pHeader->dwBlockTableSize);
+ TMPQHash * pHashEnd = ha->pHashTable + pHeader->dwHashTableSize;
+ TMPQHash * pHash;
- // Allocate space for the block table
- // Note: pHeader->dwBlockTableSize can be zero !!!
- pBlockTable = STORM_ALLOC(TMPQBlock, ha->dwMaxFileCount);
- if(pBlockTable != NULL)
+ // If we don't have HET table, we build the file entries from the hash&block tables
+ if(ha->pHetTable == NULL)
{
- ULONGLONG ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
- TMPQHash * pHashEnd = ha->pHashTable + pHeader->dwHashTableSize;
- TMPQHash * pHash;
- DWORD dwTableSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
- DWORD dwCmpSize = (DWORD)pHeader->BlockTableSize64;
-
- // Fill the block table with zeros
- memset(pBlockTable, 0, dwTableSize);
-
- // I have found a MPQ which claimed 0x200 entries in the block table,
- // but the file was cut and there was only 0x1A0 entries.
- // We will handle this case properly.
- if(dwTableSize == dwCmpSize && (ByteOffset + dwTableSize) > FileSize)
+ for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
{
- pHeader->dwBlockTableSize = (DWORD)((FileSize - ByteOffset) / sizeof(TMPQBlock));
- pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
- dwTableSize = dwCmpSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
- }
-
- //
- // One of the first cracked versions of Diablo I had block table unencrypted
- // StormLib does NOT support such MPQs anymore, as they are incompatible
- // with compressed block table feature
- //
-
- // Load the block table
- nError = LoadMpqTable(ha, ByteOffset, pBlockTable, dwCmpSize, dwTableSize, MPQ_KEY_BLOCK_TABLE);
- if(nError == ERROR_SUCCESS)
- {
- // Defense against MPQs that claim block table to be bigger than it really is
- FixBlockTableSize(ha, pBlockTable, pHeader->dwBlockTableSize);
-
- // If we don't have HET table, we build the file entries from the hash&block tables
- if(ha->pHetTable == NULL)
+ if(pHash->dwBlockIndex < pHeader->dwBlockTableSize)
{
- for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
+ 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))
{
- 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;
- }
- }
+ // 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
- {
- for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
+ else
{
- if(pHash->dwBlockIndex < ha->dwFileTableSize)
- {
- pFileEntry = pFileTable + pHash->dwBlockIndex;
- if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
- {
- pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable);
- pFileEntry->lcLocale = pHash->lcLocale;
- pFileEntry->wPlatform = pHash->wPlatform;
- }
- }
+ // 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;
}
}
}
-
- // Free the block table
- STORM_FREE(pBlockTable);
}
else
{
- nError = ERROR_NOT_ENOUGH_MEMORY;
+ for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
+ {
+ if(pHash->dwBlockIndex < ha->dwFileTableSize)
+ {
+ pFileEntry = pFileTable + pHash->dwBlockIndex;
+ if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
+ {
+ pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable);
+ pFileEntry->lcLocale = pHash->lcLocale;
+ pFileEntry->wPlatform = pHash->wPlatform;
+ }
+ }
+ }
}
+
+ // Free the block table
+ STORM_FREE(pBlockTable);
+ }
+ else
+ {
+ nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Load the hi-block table