mirror of
https://github.com/ladislav-zezula/StormLib.git
synced 2026-01-19 06:25:19 +01:00
+ Removed back reference of FileTable -> HashTable, as it is logically incorrect
+ Optimized patching process so it consimes less memory + Added hash table and block table defragmenting for malformed War3 maps
This commit is contained in:
@@ -34,29 +34,29 @@ typedef struct _MPQ_ATTRIBUTES_HEADER
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static DWORD GetSizeOfAttributesFile(DWORD dwAttrFlags, DWORD dwFileTableSize)
|
||||
static DWORD GetSizeOfAttributesFile(DWORD dwAttrFlags, DWORD dwBlockTableSize)
|
||||
{
|
||||
DWORD cbAttrFile = sizeof(MPQ_ATTRIBUTES_HEADER);
|
||||
|
||||
// Calculate size of the (attributes) file
|
||||
if(dwAttrFlags & MPQ_ATTRIBUTE_CRC32)
|
||||
cbAttrFile += dwFileTableSize * sizeof(DWORD);
|
||||
cbAttrFile += dwBlockTableSize * sizeof(DWORD);
|
||||
if(dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
|
||||
cbAttrFile += dwFileTableSize * sizeof(ULONGLONG);
|
||||
cbAttrFile += dwBlockTableSize * sizeof(ULONGLONG);
|
||||
if(dwAttrFlags & MPQ_ATTRIBUTE_MD5)
|
||||
cbAttrFile += dwFileTableSize * MD5_DIGEST_SIZE;
|
||||
cbAttrFile += dwBlockTableSize * MD5_DIGEST_SIZE;
|
||||
|
||||
// The bit array has been created without the last bit belonging to (attributes)
|
||||
// When the number of files is a multiplier of 8 plus one, then the size of (attributes)
|
||||
// if 1 byte less than expected.
|
||||
// Example: wow-update-13164.MPQ: BlockTableSize = 0x62E1, but there's only 0xC5C bytes
|
||||
if(dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
|
||||
cbAttrFile += (dwFileTableSize + 6) / 8;
|
||||
cbAttrFile += (dwBlockTableSize + 6) / 8;
|
||||
|
||||
return cbAttrFile;
|
||||
}
|
||||
|
||||
static DWORD CheckSizeOfAttributesFile(DWORD cbAttrFile, DWORD dwAttrFlags, DWORD dwFileTableSize)
|
||||
static DWORD CheckSizeOfAttributesFile(DWORD cbAttrFile, DWORD dwAttrFlags, DWORD dwBlockTableSize)
|
||||
{
|
||||
DWORD cbHeaderSize = sizeof(MPQ_ATTRIBUTES_HEADER);
|
||||
DWORD cbChecksumSize1 = 0;
|
||||
@@ -87,21 +87,21 @@ static DWORD CheckSizeOfAttributesFile(DWORD cbAttrFile, DWORD dwAttrFlags, DWOR
|
||||
// Get the expected size of CRC32 array
|
||||
if(dwAttrFlags & MPQ_ATTRIBUTE_CRC32)
|
||||
{
|
||||
cbChecksumSize1 += dwFileTableSize * sizeof(DWORD);
|
||||
cbChecksumSize1 += dwBlockTableSize * sizeof(DWORD);
|
||||
cbChecksumSize2 += cbChecksumSize1 - sizeof(DWORD);
|
||||
}
|
||||
|
||||
// Get the expected size of FILETIME array
|
||||
if(dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
|
||||
{
|
||||
cbFileTimeSize1 += dwFileTableSize * sizeof(ULONGLONG);
|
||||
cbFileTimeSize1 += dwBlockTableSize * sizeof(ULONGLONG);
|
||||
cbFileTimeSize2 += cbFileTimeSize1 - sizeof(ULONGLONG);
|
||||
}
|
||||
|
||||
// Get the expected size of MD5 array
|
||||
if(dwAttrFlags & MPQ_ATTRIBUTE_MD5)
|
||||
{
|
||||
cbFileHashSize1 += dwFileTableSize * MD5_DIGEST_SIZE;
|
||||
cbFileHashSize1 += dwBlockTableSize * MD5_DIGEST_SIZE;
|
||||
cbFileHashSize2 += cbFileHashSize1 - MD5_DIGEST_SIZE;
|
||||
}
|
||||
|
||||
@@ -109,26 +109,26 @@ static DWORD CheckSizeOfAttributesFile(DWORD cbAttrFile, DWORD dwAttrFlags, DWOR
|
||||
if(dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
|
||||
{
|
||||
cbPatchBitSize1 =
|
||||
cbPatchBitSize2 = ((dwFileTableSize + 6) / 8);
|
||||
cbPatchBitSize3 = dwFileTableSize * sizeof(DWORD);
|
||||
cbPatchBitSize2 = ((dwBlockTableSize + 6) / 8);
|
||||
cbPatchBitSize3 = dwBlockTableSize * sizeof(DWORD);
|
||||
}
|
||||
|
||||
// Check if the (attributes) file entry count is equal to our file table size
|
||||
if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1 + cbPatchBitSize1))
|
||||
return dwFileTableSize;
|
||||
return dwBlockTableSize;
|
||||
|
||||
// Check if the (attributes) file entry count is equal to our file table size minus one
|
||||
if(cbAttrFile == (cbHeaderSize + cbChecksumSize2 + cbFileTimeSize2 + cbFileHashSize2 + cbPatchBitSize2))
|
||||
return dwFileTableSize - 1;
|
||||
return dwBlockTableSize - 1;
|
||||
|
||||
// Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing
|
||||
if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1))
|
||||
return dwFileTableSize;
|
||||
return dwBlockTableSize;
|
||||
|
||||
// interface.MPQ.part (WoW build 10958) has the MPQ_ATTRIBUTE_PATCH_BIT set
|
||||
// but there's an array of DWORDs (filled with zeros) instead of array of bits
|
||||
if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1 + cbPatchBitSize3))
|
||||
return dwFileTableSize;
|
||||
return dwBlockTableSize;
|
||||
|
||||
#ifdef __STORMLIB_TEST__
|
||||
// Invalid size of the (attributes) file
|
||||
@@ -246,12 +246,11 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF
|
||||
static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile)
|
||||
{
|
||||
PMPQ_ATTRIBUTES_HEADER pAttrHeader;
|
||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->pHeader->dwBlockTableSize;
|
||||
TFileEntry * pFileEntry;
|
||||
LPBYTE pbAttrFile;
|
||||
LPBYTE pbAttrPtr;
|
||||
size_t cbAttrFile;
|
||||
DWORD dwFinalEntries = ha->dwFileTableSize + ha->dwReservedFiles;
|
||||
|
||||
// Check if we need patch bits in the (attributes) file
|
||||
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||
@@ -265,7 +264,7 @@ static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile)
|
||||
|
||||
// Allocate the buffer for holding the entire (attributes)
|
||||
// Allocate 1 byte more (See GetSizeOfAttributesFile for more info)
|
||||
cbAttrFile = GetSizeOfAttributesFile(ha->dwAttrFlags, dwFinalEntries);
|
||||
cbAttrFile = GetSizeOfAttributesFile(ha->dwAttrFlags, ha->pHeader->dwBlockTableSize);
|
||||
pbAttrFile = pbAttrPtr = STORM_ALLOC(BYTE, cbAttrFile + 1);
|
||||
if(pbAttrFile != NULL)
|
||||
{
|
||||
@@ -287,8 +286,8 @@ static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile)
|
||||
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||
*pArrayCRC32++ = BSWAP_INT32_UNSIGNED(pFileEntry->dwCrc32);
|
||||
|
||||
// Skip the reserved entries
|
||||
pbAttrPtr = (LPBYTE)(pArrayCRC32 + ha->dwReservedFiles);
|
||||
// Update pointer
|
||||
pbAttrPtr = (LPBYTE)pArrayCRC32;
|
||||
}
|
||||
|
||||
// Write the array of file time
|
||||
@@ -300,8 +299,8 @@ static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile)
|
||||
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||
*pArrayFileTime++ = BSWAP_INT64_UNSIGNED(pFileEntry->FileTime);
|
||||
|
||||
// Skip the reserved entries
|
||||
pbAttrPtr = (LPBYTE)(pArrayFileTime + ha->dwReservedFiles);
|
||||
// Update pointer
|
||||
pbAttrPtr = (LPBYTE)pArrayFileTime;
|
||||
}
|
||||
|
||||
// Write the array of MD5s
|
||||
@@ -316,8 +315,8 @@ static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile)
|
||||
pbArrayMD5 += MD5_DIGEST_SIZE;
|
||||
}
|
||||
|
||||
// Skip the reserved items
|
||||
pbAttrPtr = pbArrayMD5 + (ha->dwReservedFiles * MD5_DIGEST_SIZE);
|
||||
// Update pointer
|
||||
pbAttrPtr = pbArrayMD5;
|
||||
}
|
||||
|
||||
// Write the array of patch bits
|
||||
@@ -339,12 +338,8 @@ static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile)
|
||||
dwBitMask = (dwBitMask << 0x07) | (dwBitMask >> 0x01);
|
||||
}
|
||||
|
||||
// Having incremented the bit array just by the number of items in the file table
|
||||
// will create the bit array one byte less of the number of files is a multiplier of 8).
|
||||
// Blizzard MPQs have the same feature.
|
||||
|
||||
// Move past the bit array
|
||||
pbAttrPtr = (pbBitArray + dwByteIndex) + ((dwBitMask & 0x7F) ? 1 : 0);
|
||||
pbAttrPtr += (ha->pHeader->dwBlockTableSize + 6) / 8;
|
||||
}
|
||||
|
||||
// Now we expect that current position matches the estimated size
|
||||
@@ -419,7 +414,7 @@ int SAttrFileSaveToMpq(TMPQArchive * ha)
|
||||
if(ha->dwFileFlags2 != 0)
|
||||
{
|
||||
// At this point, we expect to have at least one reserved entry in the file table
|
||||
assert(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_INVALID);
|
||||
assert(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_NEW);
|
||||
assert(ha->dwReservedFiles > 0);
|
||||
|
||||
// Create the raw data that is to be written to (attributes)
|
||||
@@ -451,6 +446,10 @@ int SAttrFileSaveToMpq(TMPQArchive * ha)
|
||||
SFileAddFile_Finish(hf);
|
||||
}
|
||||
|
||||
// Clear the number of reserved files
|
||||
ha->dwFlags &= ~(MPQ_FLAG_ATTRIBUTES_NEW | MPQ_FLAG_ATTRIBUTES_NONE);
|
||||
ha->dwReservedFiles--;
|
||||
|
||||
// Free the attributes buffer
|
||||
STORM_FREE(pbAttrFile);
|
||||
}
|
||||
@@ -459,10 +458,6 @@ int SAttrFileSaveToMpq(TMPQArchive * ha)
|
||||
// If the (attributes) file would be empty, its OK
|
||||
nError = (cbAttrFile == 0) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Clear the number of reserved files
|
||||
ha->dwFlags &= ~MPQ_FLAG_ATTRIBUTES_INVALID;
|
||||
ha->dwReservedFiles--;
|
||||
}
|
||||
|
||||
return nError;
|
||||
|
||||
Reference in New Issue
Block a user