summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLadislav Zezula <ladislav.zezula@avg.com>2014-03-03 14:56:59 +0100
committerLadislav Zezula <ladislav.zezula@avg.com>2014-03-03 14:56:59 +0100
commit2abb68a7c5f894b9fb826dea9887a7d26a22888f (patch)
tree5b72c822085d018974693827a21423c3dbb2d52f
parent11f1d89cee7d09b8ffa2350cd2ee5a1b929260bf (diff)
+ Fixed bug in checking size of (attributes) file
-rw-r--r--src/SFileAttributes.cpp150
-rw-r--r--test/Test.cpp14
2 files changed, 101 insertions, 63 deletions
diff --git a/src/SFileAttributes.cpp b/src/SFileAttributes.cpp
index a74b54f..2589fc3 100644
--- a/src/SFileAttributes.cpp
+++ b/src/SFileAttributes.cpp
@@ -51,65 +51,111 @@ static DWORD GetSizeOfAttributesFile(DWORD dwAttrFlags, DWORD dwFileTableSize)
return cbAttrFile;
}
-static bool CheckSizeOfAttributesFile(DWORD cbSizeOfAttr, DWORD dwAttrFlags, DWORD dwFileTableSize)
+static DWORD CheckSizeOfAttributesFile(DWORD cbAttrFile, DWORD dwAttrFlags, DWORD dwFileTableSize)
{
- DWORD cbAttrFile = sizeof(MPQ_ATTRIBUTES_HEADER);
+ DWORD cbHeaderSize = sizeof(MPQ_ATTRIBUTES_HEADER);
+ DWORD cbChecksumSize1 = 0;
+ DWORD cbChecksumSize2 = 0;
+ DWORD cbFileTimeSize1 = 0;
+ DWORD cbFileTimeSize2 = 0;
+ DWORD cbFileHashSize1 = 0;
+ DWORD cbFileHashSize2 = 0;
+ DWORD cbPatchBitSize1 = 0;
+ DWORD cbPatchBitSize2 = 0;
+ DWORD cbPatchBitSize3 = 0;
- // Calculate size of the (attributes) file
+ //
+ // Various variants with the patch bit
+ //
+ // interface.MPQ.part from WoW build 10958 has
+ // the MPQ_ATTRIBUTE_PATCH_BIT set, but there's an array of DWORDs instead.
+ // The array is filled with zeros, so we don't know what it should contain
+ //
+ // Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing
+ //
+ // Elimination Tournament 2.w3x's (attributes) have one entry less
+ //
+ // There may be two variants: Either the (attributes) file has full
+ // number of entries, or has one entry less
+ //
+
+ // Get the expected size of CRC32 array
if(dwAttrFlags & MPQ_ATTRIBUTE_CRC32)
- cbAttrFile += dwFileTableSize * sizeof(DWORD);
+ {
+ cbChecksumSize1 += dwFileTableSize * sizeof(DWORD);
+ cbChecksumSize2 += cbChecksumSize1 - sizeof(DWORD);
+ }
+
+ // Get the expected size of FILETIME array
if(dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
- cbAttrFile += dwFileTableSize * sizeof(ULONGLONG);
+ {
+ cbFileTimeSize1 += dwFileTableSize * sizeof(ULONGLONG);
+ cbFileTimeSize2 += cbFileTimeSize1 - sizeof(ULONGLONG);
+ }
+
+ // Get the expected size of MD5 array
if(dwAttrFlags & MPQ_ATTRIBUTE_MD5)
- cbAttrFile += dwFileTableSize * MD5_DIGEST_SIZE;
+ {
+ cbFileHashSize1 += dwFileTableSize * MD5_DIGEST_SIZE;
+ cbFileHashSize2 += cbFileHashSize1 - MD5_DIGEST_SIZE;
+ }
- // Various variants with the patch bit
+ // Get the expected size of patch bit array
if(dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
{
- // Check for expected size
- if(cbSizeOfAttr == (cbAttrFile + (dwFileTableSize + 6) / 8))
- return true;
-
- // Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing
- if(cbSizeOfAttr == cbSizeOfAttr)
- return false;
-
- // interface.MPQ.part from WoW build 10958 has
- // the MPQ_ATTRIBUTE_PATCH_BIT set, but there's an array of DWORDs instead.
- // The array is filled with zeros, so we don't know what it should contain
- if(cbSizeOfAttr == (cbAttrFile + dwFileTableSize * sizeof(DWORD)))
- return false;
-
- // Size mismatch
- assert(false);
- return false;
+ cbPatchBitSize1 =
+ cbPatchBitSize2 = ((dwFileTableSize + 6) / 8);
+ cbPatchBitSize3 = dwFileTableSize * sizeof(DWORD);
}
- assert(cbSizeOfAttr == cbAttrFile);
- return false;
+ // Check if the (attributes) file entry count is equal to our file table size
+ if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1 + cbPatchBitSize1))
+ return dwFileTableSize;
+
+ // 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;
+
+ // Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing
+ if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1))
+ return dwFileTableSize;
+
+ // 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;
+
+ // Invalid size of the (attributes) file
+ assert(false);
+ return 0;
}
static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrFile)
{
LPBYTE pbAttrFileEnd = pbAttrFile + cbAttrFile;
LPBYTE pbAttrPtr = pbAttrFile;
- DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize;
+ DWORD dwAttributesEntries = 0;
DWORD i;
- bool bPatchBitsValid = false;
// Load and verify the header
if((pbAttrPtr + sizeof(MPQ_ATTRIBUTES_HEADER)) <= pbAttrFileEnd)
{
PMPQ_ATTRIBUTES_HEADER pAttrHeader = (PMPQ_ATTRIBUTES_HEADER)pbAttrPtr;
+ // Verify the header version
BSWAP_ARRAY32_UNSIGNED(pAttrHeader, sizeof(MPQ_ATTRIBUTES_HEADER));
if(pAttrHeader->dwVersion != MPQ_ATTRIBUTES_V1)
return ERROR_BAD_FORMAT;
- // Verify the flags and of the file
- assert((pAttrHeader->dwFlags & ~MPQ_ATTRIBUTE_ALL) == 0);
+ // Verify the flags
+ if(pAttrHeader->dwFlags & ~MPQ_ATTRIBUTE_ALL)
+ return ERROR_BAD_FORMAT;
+
+ // Verify whether file size of (attributes) is expected
+ dwAttributesEntries = CheckSizeOfAttributesFile(cbAttrFile, pAttrHeader->dwFlags, ha->pHeader->dwBlockTableSize);
+ if(dwAttributesEntries == 0)
+ return ERROR_BAD_FORMAT;
- bPatchBitsValid = CheckSizeOfAttributesFile(cbAttrFile, pAttrHeader->dwFlags, dwBlockTableSize);
ha->dwAttrFlags = pAttrHeader->dwFlags;
pbAttrPtr = (LPBYTE)(pAttrHeader + 1);
}
@@ -118,14 +164,14 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32)
{
LPDWORD ArrayCRC32 = (LPDWORD)pbAttrPtr;
- DWORD cbArraySize = dwBlockTableSize * sizeof(DWORD);
+ DWORD cbArraySize = dwAttributesEntries * sizeof(DWORD);
// Verify if there's enough data
if((pbAttrPtr + cbArraySize) > pbAttrFileEnd)
return ERROR_FILE_CORRUPT;
BSWAP_ARRAY32_UNSIGNED(ArrayCRC32, cbCRC32Size);
- for(i = 0; i < dwBlockTableSize; i++)
+ for(i = 0; i < dwAttributesEntries; i++)
ha->pFileTable[i].dwCrc32 = ArrayCRC32[i];
pbAttrPtr += cbArraySize;
}
@@ -134,14 +180,14 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
{
ULONGLONG * ArrayFileTime = (ULONGLONG *)pbAttrPtr;
- DWORD cbArraySize = dwBlockTableSize * sizeof(ULONGLONG);
+ DWORD cbArraySize = dwAttributesEntries * sizeof(ULONGLONG);
// Verify if there's enough data
if((pbAttrPtr + cbArraySize) > pbAttrFileEnd)
return ERROR_FILE_CORRUPT;
BSWAP_ARRAY64_UNSIGNED(ArrayFileTime, cbFileTimeSize);
- for(i = 0; i < dwBlockTableSize; i++)
+ for(i = 0; i < dwAttributesEntries; i++)
ha->pFileTable[i].FileTime = ArrayFileTime[i];
pbAttrPtr += cbArraySize;
}
@@ -150,13 +196,13 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5)
{
LPBYTE ArrayMd5 = pbAttrPtr;
- DWORD cbArraySize = dwBlockTableSize * MD5_DIGEST_SIZE;
+ DWORD cbArraySize = dwAttributesEntries * MD5_DIGEST_SIZE;
// Verify if there's enough data
if((pbAttrPtr + cbArraySize) > pbAttrFileEnd)
return ERROR_FILE_CORRUPT;
- for(i = 0; i < dwBlockTableSize; i++)
+ for(i = 0; i < dwAttributesEntries; i++)
{
memcpy(ha->pFileTable[i].md5, ArrayMd5, MD5_DIGEST_SIZE);
ArrayMd5 += MD5_DIGEST_SIZE;
@@ -165,36 +211,25 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF
}
// Read the patch bit for each file (if present)
- if((ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) && bPatchBitsValid)
+ if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
{
LPBYTE pbBitArray = pbAttrPtr;
- DWORD cbArraySize = (dwBlockTableSize + 7) / 8;
+ DWORD cbArraySize = (dwAttributesEntries + 7) / 8;
DWORD dwByteIndex = 0;
DWORD dwBitMask = 0x80;
// Verify if there's enough data
- if((pbAttrPtr + cbArraySize) > pbAttrFileEnd)
- return ERROR_FILE_CORRUPT;
-
- for(i = 0; i < dwBlockTableSize; i++)
+ if((pbAttrPtr + cbArraySize) == pbAttrFileEnd)
{
- ha->pFileTable[i].dwFlags |= (pbBitArray[dwByteIndex] & dwBitMask) ? MPQ_FILE_PATCH_FILE : 0;
- dwByteIndex += (dwBitMask & 0x01);
- dwBitMask = (dwBitMask << 0x07) | (dwBitMask >> 0x01);
+ for(i = 0; i < dwAttributesEntries; i++)
+ {
+ ha->pFileTable[i].dwFlags |= (pbBitArray[dwByteIndex] & dwBitMask) ? MPQ_FILE_PATCH_FILE : 0;
+ dwByteIndex += (dwBitMask & 0x01);
+ dwBitMask = (dwBitMask << 0x07) | (dwBitMask >> 0x01);
+ }
}
-
- pbAttrPtr += cbArraySize;
}
- //
- // Note: Version 7.00 of StormLib saved the (attributes) incorrectly.
- // Sometimes, number of entries in the (attributes) was 1 item less
- // than block table size.
- // If we encounter such table, we will zero all three arrays
- //
-
- if(pbAttrPtr != pbAttrFileEnd)
- ha->dwAttrFlags = 0;
return ERROR_SUCCESS;
}
@@ -333,7 +368,6 @@ int SAttrLoadAttributes(TMPQArchive * ha)
return ERROR_FILE_CORRUPT;
// Attempt to open the "(attributes)" file.
- // If it's not there, then the archive doesn't support attributes
if(SFileOpenFileEx((HANDLE)ha, ATTRIBUTES_NAME, SFILE_OPEN_ANY_LOCALE, &hFile))
{
// Retrieve and check size of the (attributes) file
diff --git a/test/Test.cpp b/test/Test.cpp
index 940a013..150d335 100644
--- a/test/Test.cpp
+++ b/test/Test.cpp
@@ -3511,8 +3511,8 @@ int main(int argc, char * argv[])
nError = TestReadFile_MasterMirror("MPQ_2013_v4_alternate-complete.MPQ", "MPQ_2013_v4_alternate-original.MPQ", true);
// 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);
+// 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)
@@ -3566,10 +3566,14 @@ int main(int argc, char * argv[])
if(nError == ERROR_SUCCESS)
nError = TestOpenArchive("MPQ_2002_v1_ProtectedMap_Spazzler.w3x");
- // Open an Warcraft III map locked by the Spazzler protector
+ // Open an Warcraft III map locked by the BOBA protector
if(nError == ERROR_SUCCESS)
nError = TestOpenArchive("MPQ_2002_v1_ProtectedMap_BOBA.w3m");
+ // Open an Warcraft III map whose "(attributes)" file has (BlockTableSize-1) entries
+ if(nError == ERROR_SUCCESS)
+ nError = TestOpenArchive("MPQ_2014_v1_AttributesOneEntryLess.w3x");
+
// Open a MPQ archive v 3.0
if(nError == ERROR_SUCCESS)
nError = TestOpenArchive("MPQ_2010_v3_expansion-locale-frFR.MPQ");
@@ -3705,7 +3709,7 @@ int main(int argc, char * argv[])
// Create a MPQ file, add a stereo-WAVE file with various compressions
if(nError == ERROR_SUCCESS)
nError = TestCreateArchive_WaveCompressionsTest("StormLibTest_AddWaveStereoTest.mpq", "AddFile-Stereo.wav");
-/*
+
// Check if the listfile is always created at the end of the file table in the archive
if(nError == ERROR_SUCCESS)
nError = TestCreateArchive_ListFilePos("StormLibTest_ListFilePos.mpq");
@@ -3713,6 +3717,6 @@ int main(int argc, char * argv[])
// Open a MPQ (add custom user data to it)
if(nError == ERROR_SUCCESS)
nError = TestCreateArchive_BigArchive("StormLibTest_BigArchive_v4.mpq");
-*/
+
return nError;
}