summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLadislav Zezula <ladislav.zezula@avg.com>2013-12-09 14:51:40 +0100
committerLadislav Zezula <ladislav.zezula@avg.com>2013-12-09 14:51:40 +0100
commit5106d34fdaaf6378abf0fa8bb0b30d54a487e398 (patch)
treec5a25c22fdb67a0c0f0021c49380d081ff2679eb
parentcc0ed30d33eb020c4fda8b7ceeb7fde7a0af9b41 (diff)
+ Bugfixes
-rw-r--r--StormLib_test.vcproj2
-rw-r--r--src/SBaseCommon.cpp3
-rw-r--r--src/SBaseFileTable.cpp161
-rw-r--r--src/SFileAttributes.cpp36
-rw-r--r--src/SFileCompactArchive.cpp3
-rw-r--r--src/SFileCreateArchive.cpp6
-rw-r--r--src/SFileOpenArchive.cpp30
-rw-r--r--src/StormCommon.h21
-rw-r--r--src/StormLib.h3
-rw-r--r--test/Readme.txt4
-rw-r--r--test/Test.cpp543
11 files changed, 481 insertions, 331 deletions
diff --git a/StormLib_test.vcproj b/StormLib_test.vcproj
index 9d6ac24..3276b53 100644
--- a/StormLib_test.vcproj
+++ b/StormLib_test.vcproj
@@ -106,7 +106,7 @@
OutputDirectory="./bin/$(ProjectName)/$(PlatformName)/$(ConfigurationName)"
IntermediateDirectory="./bin/$(ProjectName)/$(PlatformName)/$(ConfigurationName)"
ConfigurationType="1"
- CharacterSet="2"
+ CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
diff --git a/src/SBaseCommon.cpp b/src/SBaseCommon.cpp
index 3bb10de..a0e789b 100644
--- a/src/SBaseCommon.cpp
+++ b/src/SBaseCommon.cpp
@@ -710,8 +710,7 @@ void * LoadMpqTable(
pbMpqTable = pbToRead = STORM_ALLOC(BYTE, dwTableSize);
if(pbMpqTable != NULL)
{
- // "interface.MPQ.part" in trial version of World of Warcraft
- // has block table and hash table compressed.
+ // Check if the MPQ table is encrypted
if(dwCompressedSize < dwTableSize)
{
// Allocate temporary buffer for holding compressed data
diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp
index 6b4eada..bf6be59 100644
--- a/src/SBaseFileTable.cpp
+++ b/src/SBaseFileTable.cpp
@@ -229,47 +229,53 @@ void SetBits(
//-----------------------------------------------------------------------------
// Support for MPQ header
-static DWORD GetArchiveSize32(TMPQArchive * ha, TMPQBlock * pBlockTable, DWORD dwBlockTableSize)
+static DWORD GetMaxFileOffset32(TMPQArchive * ha, TMPQBlock * pBlockTable, DWORD dwBlockTableSize)
{
TMPQHeader * pHeader = ha->pHeader;
- ULONGLONG FileSize = 0;
- DWORD dwArchiveSize = pHeader->dwHeaderSize;
+ DWORD dwMaxFileOffset = ha->pHeader->dwArchiveSize;
DWORD dwByteOffset;
DWORD dwBlockIndex;
- // Increment by hash table size
- dwByteOffset = pHeader->dwHashTablePos + (pHeader->dwHashTableSize * sizeof(TMPQHash));
- if(dwByteOffset > dwArchiveSize)
- dwArchiveSize = dwByteOffset;
+ // 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;
+}
- // Increment by block table size
- dwByteOffset = pHeader->dwBlockTablePos + (pHeader->dwBlockTableSize * sizeof(TMPQBlock));
- if(dwByteOffset > dwArchiveSize)
- dwArchiveSize = dwByteOffset;
+static ULONGLONG DetermineEndOfArchive_V1_V2(
+ TMPQArchive * ha,
+ ULONGLONG MpqOffset,
+ ULONGLONG FileSize)
+{
+ ULONGLONG ByteOffset;
+ ULONGLONG EndOfMpq = FileSize;
+ DWORD SignatureHeader = 0;
- // If any of the MPQ files is beyond the hash table/block table, set the end to the file size
- for(dwBlockIndex = 0; dwBlockIndex < dwBlockTableSize; dwBlockIndex++)
+ // Check if there is a signature header
+ if((EndOfMpq - MpqOffset) > (MPQ_STRONG_SIGNATURE_SIZE + 4))
{
- // Only count files that exists
- if(pBlockTable[dwBlockIndex].dwFlags & MPQ_FILE_EXISTS)
- {
- // If this file begins past the end of tables,
- // assume that the hash/block table is not at the end of the archive
- if(pBlockTable[dwBlockIndex].dwFilePos > dwArchiveSize)
- {
- FileStream_GetSize(ha->pStream, &FileSize);
- dwArchiveSize = (DWORD)(FileSize - ha->MpqPos);
- break;
- }
- }
+ ByteOffset = EndOfMpq - MPQ_STRONG_SIGNATURE_SIZE - 4;
+ FileStream_Read(ha->pStream, &ByteOffset, &SignatureHeader, sizeof(DWORD));
+ if(BSWAP_INT32_UNSIGNED(SignatureHeader) == MPQ_STRONG_SIGNATURE_ID)
+ EndOfMpq = EndOfMpq - MPQ_STRONG_SIGNATURE_SIZE - 4;
}
- // Return what we found
- return dwArchiveSize;
+ // Return the returned archive size
+ return (EndOfMpq - MpqOffset);
}
// This function converts the MPQ header so it always looks like version 4
-static ULONGLONG GetArchiveSize64(TMPQHeader * pHeader)
+/*static ULONGLONG GetArchiveSize64(TMPQHeader * pHeader)
{
ULONGLONG ArchiveSize = pHeader->dwHeaderSize;
ULONGLONG ByteOffset = pHeader->dwHeaderSize;
@@ -316,13 +322,16 @@ static ULONGLONG GetArchiveSize64(TMPQHeader * pHeader)
return ArchiveSize;
}
-
+*/
int ConvertMpqHeaderToFormat4(
TMPQArchive * ha,
+ ULONGLONG MpqOffset,
ULONGLONG FileSize,
DWORD dwFlags)
{
TMPQHeader * pHeader = (TMPQHeader *)ha->HeaderData;
+ ULONGLONG BlockTablePos64 = 0;
+ ULONGLONG HashTablePos64 = 0;
ULONGLONG ByteOffset;
USHORT wFormatVersion = BSWAP_INT16_UNSIGNED(pHeader->wFormatVersion);
int nError = ERROR_SUCCESS;
@@ -342,7 +351,7 @@ int ConvertMpqHeaderToFormat4(
if(pHeader->dwHeaderSize != MPQ_HEADER_SIZE_V1)
{
pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1;
- ha->dwFlags |= MPQ_FLAG_PROTECTED;
+ ha->dwFlags |= MPQ_FLAG_MALFORMED;
}
//
@@ -351,35 +360,87 @@ int ConvertMpqHeaderToFormat4(
// ("w3xmaster" protector).
//
- // Fill the rest of the header with zeros
+ Label_ArchiveVersion1:
+ if(pHeader->dwHashTablePos <= pHeader->dwHeaderSize)
+ ha->dwFlags |= MPQ_FLAG_MALFORMED;
+ if(pHeader->dwBlockTablePos <= pHeader->dwHeaderSize)
+ ha->dwFlags |= MPQ_FLAG_MALFORMED;
+ if(pHeader->dwArchiveSize != pHeader->dwBlockTablePos + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)))
+ ha->dwFlags |= MPQ_FLAG_MALFORMED;
+
+ // Fill the rest of the header
memset((LPBYTE)pHeader + MPQ_HEADER_SIZE_V1, 0, sizeof(TMPQHeader) - MPQ_HEADER_SIZE_V1);
pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
pHeader->HashTableSize64 = pHeader->dwHashTableSize * sizeof(TMPQHash);
pHeader->ArchiveSize64 = pHeader->dwArchiveSize;
+
+ // Determine the archive size on malformed MPQs
+ if(ha->dwFlags & MPQ_FLAG_MALFORMED)
+ {
+ pHeader->ArchiveSize64 = DetermineEndOfArchive_V1_V2(ha, MpqOffset, FileSize);
+ pHeader->dwArchiveSize = (DWORD)pHeader->ArchiveSize64;
+ }
break;
case MPQ_FORMAT_VERSION_2:
- // Check for malformed MPQ header version 2.0
+ // Check for malformed MPQ header version 1.0
BSWAP_TMPQHEADER(pHeader, MPQ_FORMAT_VERSION_2);
if(pHeader->dwHeaderSize != MPQ_HEADER_SIZE_V2)
{
pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1;
- pHeader->HiBlockTablePos64 = 0;
- pHeader->wHashTablePosHi = 0;
- pHeader->wBlockTablePosHi = 0;
- ha->dwFlags |= MPQ_FLAG_PROTECTED;
+ ha->dwFlags |= MPQ_FLAG_MALFORMED;
+ goto Label_ArchiveVersion1;
}
// Fill the rest of the header with zeros
memset((LPBYTE)pHeader + MPQ_HEADER_SIZE_V2, 0, sizeof(TMPQHeader) - MPQ_HEADER_SIZE_V2);
- if(pHeader->wHashTablePosHi || pHeader->dwHashTablePos)
- pHeader->HashTableSize64 = pHeader->dwHashTableSize * sizeof(TMPQHash);
- if(pHeader->wBlockTablePosHi || pHeader->dwBlockTablePos)
- pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
- if(pHeader->HiBlockTablePos64)
- pHeader->HiBlockTableSize64 = pHeader->dwBlockTableSize * sizeof(USHORT);
- pHeader->ArchiveSize64 = GetArchiveSize64(pHeader);
+
+ // Calculate the expected hash table size
+ pHeader->HashTableSize64 = (pHeader->dwHashTableSize * sizeof(TMPQHash));
+ HashTablePos64 = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos);
+
+ // Calculate the expected block table size
+ pHeader->BlockTableSize64 = (pHeader->dwBlockTableSize * sizeof(TMPQBlock));
+ BlockTablePos64 = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
+
+ // We require the block table to follow hash table
+ if(BlockTablePos64 > HashTablePos64)
+ {
+ // HashTableSize64 may be less than TblSize * sizeof(TMPQHash).
+ // That means that the hash table is compressed.
+ pHeader->HashTableSize64 = BlockTablePos64 - HashTablePos64;
+
+ // Calculate the compressed block table size
+ if(pHeader->HiBlockTablePos64 != 0)
+ {
+ // BlockTableSize64 may be less than TblSize * sizeof(TMPQBlock).
+ // That means that the hash table is compressed.
+ pHeader->BlockTableSize64 = pHeader->HiBlockTablePos64 - BlockTablePos64;
+ assert(pHeader->BlockTableSize64 <= (pHeader->dwBlockTableSize * sizeof(TMPQBlock)));
+
+ // Determine the size of the hi-block table
+ pHeader->HiBlockTableSize64 = DetermineEndOfArchive_V1_V2(ha, MpqOffset, FileSize) - pHeader->HiBlockTablePos64;
+ assert(pHeader->HiBlockTableSize64 == (pHeader->dwBlockTableSize * sizeof(USHORT)));
+
+ // Recalculate the archive size
+ pHeader->ArchiveSize64 = pHeader->HiBlockTablePos64 + pHeader->HiBlockTableSize64;
+ }
+ else
+ {
+ // Block table size is the end of the archive minus the block table position
+ pHeader->BlockTableSize64 = DetermineEndOfArchive_V1_V2(ha, MpqOffset, FileSize) - BlockTablePos64;
+ assert(pHeader->BlockTableSize64 <= (pHeader->dwBlockTableSize * sizeof(TMPQBlock)));
+
+ // dwArchiveSize in the header v 2.0 is 32-bit only
+ pHeader->ArchiveSize64 = BlockTablePos64 + pHeader->BlockTableSize64;
+ }
+ }
+ else
+ {
+ pHeader->ArchiveSize64 = pHeader->dwArchiveSize;
+ ha->dwFlags |= MPQ_FLAG_MALFORMED;
+ }
break;
case MPQ_FORMAT_VERSION_3:
@@ -2123,7 +2184,7 @@ static void FixBlockTableSize(
{
dwFixedTableSize = (DWORD)((FileDataStart - BlockTableStart) / sizeof(TMPQBlock));
BlockTableEnd = FileDataStart;
- ha->dwFlags |= MPQ_FLAG_PROTECTED;
+ ha->dwFlags |= MPQ_FLAG_MALFORMED;
}
}
@@ -2159,7 +2220,7 @@ static void FixCompressedFileSize(
TMPQBlock * pBlock;
size_t nElements = 0;
size_t nIndex;
- DWORD dwArchiveSize;
+ DWORD dwMaxFileOffs;
// Only perform this check on MPQs version 1.0
assert(pHeader->dwHeaderSize == MPQ_HEADER_SIZE_V1);
@@ -2169,7 +2230,7 @@ static void FixCompressedFileSize(
if(SortTable != NULL)
{
// Calculate the end of the archive
- dwArchiveSize = GetArchiveSize32(ha, pBlockTable, pHeader->dwBlockTableSize);
+ dwMaxFileOffs = GetMaxFileOffset32(ha, pBlockTable, pHeader->dwBlockTableSize);
// Put all blocks to a sort table
for(pBlock = pBlockTable; pBlock < pBlockTableEnd; pBlock++)
@@ -2191,12 +2252,12 @@ static void FixCompressedFileSize(
TMPQBlock * pBlock1 = SortTable[nIndex + 1];
TMPQBlock * pBlock0 = SortTable[nIndex];
- pBlock0->dwCSize = (pBlock->dwFlags & MPQ_FILE_COMPRESS_MASK) ? (pBlock1->dwFilePos - pBlock0->dwFilePos) : pBlock0->dwFSize;
+ pBlock0->dwCSize = (pBlock0->dwFlags & MPQ_FILE_COMPRESS_MASK) ? (pBlock1->dwFilePos - pBlock0->dwFilePos) : pBlock0->dwFSize;
}
// Fix the last entry
pBlock = SortTable[nElements - 1];
- pBlock->dwCSize = dwArchiveSize - pBlock->dwFilePos;
+ pBlock->dwCSize = (pBlock->dwFlags & MPQ_FILE_COMPRESS_MASK) ? (dwMaxFileOffs - pBlock->dwFilePos) : pBlock->dwFSize;
}
STORM_FREE(SortTable);
@@ -2239,7 +2300,7 @@ TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool bDontFixEntries)
pHeader->BlockTableSize64 = FileSize - ByteOffset;
pHeader->dwBlockTableSize = (DWORD)(pHeader->BlockTableSize64 / sizeof(TMPQBlock));
dwTableSize = dwCmpSize = (DWORD)pHeader->BlockTableSize64;
- ha->dwFlags |= MPQ_FLAG_PROTECTED;
+ ha->dwFlags |= MPQ_FLAG_MALFORMED;
}
//
@@ -2259,7 +2320,7 @@ TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool bDontFixEntries)
FixBlockTableSize(ha, pBlockTable);
// Defense against protectors that set invalid compressed size
- if((ha->dwFlags & MPQ_FLAG_PROTECTED) && (bDontFixEntries == false))
+ if((ha->dwFlags & MPQ_FLAG_MALFORMED) && (bDontFixEntries == false))
FixCompressedFileSize(ha, pBlockTable);
}
break;
diff --git a/src/SFileAttributes.cpp b/src/SFileAttributes.cpp
index 7f12028..a0ba774 100644
--- a/src/SFileAttributes.cpp
+++ b/src/SFileAttributes.cpp
@@ -40,16 +40,39 @@ static DWORD GetSizeOfAttributesFile(DWORD dwAttrFlags, DWORD dwFileTableSize)
cbAttrFile += dwFileTableSize * sizeof(ULONGLONG);
if(dwAttrFlags & MPQ_ATTRIBUTE_MD5)
cbAttrFile += dwFileTableSize * MD5_DIGEST_SIZE;
-
- // The bit array has been create without the last bit belonging to (attributes)
+
+ // 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;
+
+ return cbAttrFile;
+}
+
+#ifdef _DEBUG
+static DWORD GetSizeOfAttributesFile_v2(DWORD dwAttrFlags, DWORD dwFileTableSize)
+{
+ DWORD cbAttrFile = sizeof(MPQ_ATTRIBUTES_HEADER);
+
+ // Calculate size of the (attributes) file
+ if(dwAttrFlags & MPQ_ATTRIBUTE_CRC32)
+ cbAttrFile += dwFileTableSize * sizeof(DWORD);
+ if(dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
+ cbAttrFile += dwFileTableSize * sizeof(ULONGLONG);
+ if(dwAttrFlags & MPQ_ATTRIBUTE_MD5)
+ cbAttrFile += dwFileTableSize * MD5_DIGEST_SIZE;
+
+ // 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(dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
+ cbAttrFile += dwFileTableSize * sizeof(DWORD);
return cbAttrFile;
}
+#endif
static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrFile)
{
@@ -67,9 +90,14 @@ static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrF
if(pAttrHeader->dwVersion != MPQ_ATTRIBUTES_V1)
return ERROR_BAD_FORMAT;
- // Verify the flags and size of the file
+ // Verify the size of the file
assert((pAttrHeader->dwFlags & ~MPQ_ATTRIBUTE_ALL) == 0);
- assert(GetSizeOfAttributesFile(pAttrHeader->dwFlags, dwBlockTableSize) == cbAttrFile);
+
+ // Verify the size of the file
+#ifdef _DEBUG
+ assert(cbAttrFile == GetSizeOfAttributesFile(pAttrHeader->dwFlags, dwBlockTableSize) ||
+ cbAttrFile == GetSizeOfAttributesFile_v2(pAttrHeader->dwFlags, dwBlockTableSize));
+#endif
ha->dwAttrFlags = pAttrHeader->dwFlags;
pbAttrPtr = (LPBYTE)(pAttrHeader + 1);
diff --git a/src/SFileCompactArchive.cpp b/src/SFileCompactArchive.cpp
index 64c599d..ad9801b 100644
--- a/src/SFileCompactArchive.cpp
+++ b/src/SFileCompactArchive.cpp
@@ -332,14 +332,13 @@ static int CopyMpqFileSectors(
{
// At this point, number of bytes written should be exactly
// the same like the compressed file size. If it isn't,
- // there's something wrong (an unknown archive version, MPQ protection, ...)
+ // there's something wrong (an unknown archive version, MPQ malformation, ...)
//
// Note: Diablo savegames have very weird layout, and the file "hero"
// seems to have improper compressed size. Instead of real compressed size,
// the "dwCmpSize" member of the block table entry contains
// uncompressed size of file data + size of the sector table.
// If we compact the archive, Diablo will refuse to load the game
- // Seems like some sort of protection to me.
//
// Note: Some patch files in WOW patches don't count the patch header
// into compressed size
diff --git a/src/SFileCreateArchive.cpp b/src/SFileCreateArchive.cpp
index 2b51efa..4ae71e9 100644
--- a/src/SFileCreateArchive.cpp
+++ b/src/SFileCreateArchive.cpp
@@ -80,11 +80,15 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwCreateFlags, DWO
CreateInfo.dwStreamFlags = STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE;
CreateInfo.dwFileFlags1 = (dwCreateFlags & MPQ_CREATE_LISTFILE) ? MPQ_FILE_EXISTS : 0;
CreateInfo.dwFileFlags2 = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_FILE_EXISTS : 0;
- CreateInfo.dwAttrFlags = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_ATTRIBUTE_ALL : 0;
+ CreateInfo.dwAttrFlags = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? (MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_FILETIME | MPQ_ATTRIBUTE_MD5) : 0;
CreateInfo.dwSectorSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000;
CreateInfo.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0;
CreateInfo.dwMaxFileCount = dwMaxFileCount;
+ // Set the proper attribute parts
+ if((CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) && (dwCreateFlags & MPQ_CREATE_ATTRIBUTES))
+ CreateInfo.dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT;
+
// Backward compatibility: SFileCreateArchive always used to add (listfile)
// We would break loads of applications if we change that
CreateInfo.dwFileFlags1 = MPQ_FILE_EXISTS;
diff --git a/src/SFileOpenArchive.cpp b/src/SFileOpenArchive.cpp
index b05cff3..417b43d 100644
--- a/src/SFileOpenArchive.cpp
+++ b/src/SFileOpenArchive.cpp
@@ -213,7 +213,7 @@ bool WINAPI SFileOpenArchive(
// Initialize handle structure and allocate structure for MPQ header
if(nError == ERROR_SUCCESS)
{
- ULONGLONG SearchPos = 0;
+ ULONGLONG SearchOffset = 0;
DWORD dwHeaderID;
memset(ha, 0, sizeof(TMPQArchive));
@@ -230,23 +230,23 @@ bool WINAPI SFileOpenArchive(
ha->dwFlags |= MPQ_FLAG_CHECK_SECTOR_CRC;
// Find the offset of MPQ header within the file
- while(SearchPos < FileSize)
+ while(SearchOffset < FileSize)
{
DWORD dwBytesAvailable = MPQ_HEADER_SIZE_V4;
// Cut the bytes available, if needed
- if((FileSize - SearchPos) < MPQ_HEADER_SIZE_V4)
- dwBytesAvailable = (DWORD)(FileSize - SearchPos);
+ if((FileSize - SearchOffset) < MPQ_HEADER_SIZE_V4)
+ dwBytesAvailable = (DWORD)(FileSize - SearchOffset);
// Read the eventual MPQ header
- if(!FileStream_Read(ha->pStream, &SearchPos, ha->HeaderData, dwBytesAvailable))
+ if(!FileStream_Read(ha->pStream, &SearchOffset, ha->HeaderData, dwBytesAvailable))
{
nError = GetLastError();
break;
}
// There are AVI files from Warcraft III with 'MPQ' extension.
- if(SearchPos == 0 && IsAviFile(ha->HeaderData))
+ if(SearchOffset == 0 && IsAviFile(ha->HeaderData))
{
nError = ERROR_AVI_FILE;
break;
@@ -257,16 +257,16 @@ bool WINAPI SFileOpenArchive(
if(dwHeaderID == ID_MPQ_USERDATA && ha->pUserData == NULL && (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0)
{
// Verify if this looks like a valid user data
- pUserData = IsValidMpqUserData(SearchPos, FileSize, ha->HeaderData);
+ pUserData = IsValidMpqUserData(SearchOffset, FileSize, ha->HeaderData);
if(pUserData != NULL)
{
// Fill the user data header
- ha->UserDataPos = SearchPos;
+ ha->UserDataPos = SearchOffset;
ha->pUserData = &ha->UserData;
memcpy(ha->pUserData, pUserData, sizeof(TMPQUserData));
// Continue searching from that position
- SearchPos += ha->pUserData->dwHeaderOffs;
+ SearchOffset += ha->pUserData->dwHeaderOffs;
continue;
}
}
@@ -275,7 +275,7 @@ bool WINAPI SFileOpenArchive(
if(dwHeaderID == ID_MPQ)
{
// Now convert the header to version 4
- nError = ConvertMpqHeaderToFormat4(ha, FileSize, dwFlags);
+ nError = ConvertMpqHeaderToFormat4(ha, SearchOffset, FileSize, dwFlags);
break;
}
@@ -295,7 +295,7 @@ bool WINAPI SFileOpenArchive(
}
// Move to the next possible offset
- SearchPos += 0x200;
+ SearchOffset += 0x200;
}
// Did we identify one of the supported headers?
@@ -303,11 +303,11 @@ bool WINAPI SFileOpenArchive(
{
// Set the user data position to the MPQ header, if none
if(ha->pUserData == NULL)
- ha->UserDataPos = SearchPos;
+ ha->UserDataPos = SearchOffset;
// Set the position of the MPQ header
ha->pHeader = (TMPQHeader *)ha->HeaderData;
- ha->MpqPos = SearchPos;
+ ha->MpqPos = SearchOffset;
// Sector size must be nonzero.
if(ha->pHeader->wSectorSize == 0)
@@ -376,8 +376,8 @@ bool WINAPI SFileOpenArchive(
nError = BuildFileTable(ha);
}
- // Verify the file table, if no kind of protection was detected
- if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_PROTECTED) == 0)
+ // Verify the file table, if no kind of malformation was detected
+ if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_MALFORMED) == 0)
{
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
ULONGLONG RawFilePos;
diff --git a/src/StormCommon.h b/src/StormCommon.h
index bfe99f7..aeedbca 100644
--- a/src/StormCommon.h
+++ b/src/StormCommon.h
@@ -74,20 +74,21 @@
// MPQ signature information
// Size of each signature type
-#define MPQ_WEAK_SIGNATURE_SIZE 64
-#define MPQ_STRONG_SIGNATURE_SIZE 256
+#define MPQ_WEAK_SIGNATURE_SIZE 64
+#define MPQ_STRONG_SIGNATURE_SIZE 256
+#define MPQ_STRONG_SIGNATURE_ID 0x5349474E // ID of the strong signature ("NGIS")
// MPQ signature info
typedef struct _MPQ_SIGNATURE_INFO
{
- ULONGLONG BeginMpqData; // File offset where the hashing starts
- ULONGLONG BeginExclude; // Begin of the excluded area (used for (signature) file)
- ULONGLONG EndExclude; // End of the excluded area (used for (signature) file)
- ULONGLONG EndMpqData; // File offset where the hashing ends
- ULONGLONG EndOfFile; // Size of the entire file
+ ULONGLONG BeginMpqData; // File offset where the hashing starts
+ ULONGLONG BeginExclude; // Begin of the excluded area (used for (signature) file)
+ ULONGLONG EndExclude; // End of the excluded area (used for (signature) file)
+ ULONGLONG EndMpqData; // File offset where the hashing ends
+ ULONGLONG EndOfFile; // Size of the entire file
BYTE Signature[MPQ_STRONG_SIGNATURE_SIZE + 0x10];
- DWORD cbSignatureSize; // Length of the signature
- DWORD SignatureTypes; // See SIGNATURE_TYPE_XXX
+ DWORD cbSignatureSize; // Length of the signature
+ DWORD SignatureTypes; // See SIGNATURE_TYPE_XXX
} MPQ_SIGNATURE_INFO, *PMPQ_SIGNATURE_INFO;
@@ -167,7 +168,7 @@ TMPQFile * IsValidFileHandle(HANDLE hFile);
//-----------------------------------------------------------------------------
// Support for MPQ file tables
-int ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags);
+int ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG MpqOffset, ULONGLONG FileSize, DWORD dwFlags);
TMPQHash * FindFreeHashEntry(TMPQArchive * ha, DWORD dwStartIndex, DWORD dwName1, DWORD dwName2, LCID lcLocale);
TMPQHash * GetFirstHashEntry(TMPQArchive * ha, const char * szFileName);
diff --git a/src/StormLib.h b/src/StormLib.h
index 3659c23..9cdb28b 100644
--- a/src/StormLib.h
+++ b/src/StormLib.h
@@ -174,7 +174,7 @@ extern "C" {
// Flags for TMPQArchive::dwFlags
#define MPQ_FLAG_READ_ONLY 0x00000001 // If set, the MPQ has been open for read-only access
#define MPQ_FLAG_CHANGED 0x00000002 // If set, the MPQ tables have been changed
-#define MPQ_FLAG_PROTECTED 0x00000004 // Some kind of protector detected (W3M maps)
+#define MPQ_FLAG_MALFORMED 0x00000004 // Malformed data structure detected (W3M map protectors)
#define MPQ_FLAG_CHECK_SECTOR_CRC 0x00000008 // Checking sector CRC when reading files
#define MPQ_FLAG_LISTFILE_INVALID 0x00000020 // If set, it means that the (listfile) has been invalidated
#define MPQ_FLAG_ATTRIBUTES_INVALID 0x00000040 // If set, it means that the (attributes) has been invalidated
@@ -286,6 +286,7 @@ extern "C" {
// Deprecated
#define MPQ_OPEN_READ_ONLY STREAM_FLAG_READ_ONLY
#define MPQ_OPEN_ENCRYPTED STREAM_PROVIDER_ENCRYPTED
+#define MPQ_OPEN_PARTIAL STREAM_PROVIDER_PARTIAL
// Flags for SFileCreateArchive
#define MPQ_CREATE_LISTFILE 0x00100000 // Also add the (listfile) file
diff --git a/test/Readme.txt b/test/Readme.txt
index 8ef0c67..19bdf53 100644
--- a/test/Readme.txt
+++ b/test/Readme.txt
@@ -46,7 +46,7 @@ MPQ_1997_v1_Diablo1_single_0.sha 201 // Single player
MPQ_1997_v1_Diablo1_single_0.sv 98 980
MPQ_1999_v1_WeakSignature.exe 1 031 826 // War2Patch_202.exe from Warcraft II Bnet Edition
MPQ_1999_v1_WeakSignature.sha 260
-MPQ_2002_v1_BlockTableCut.MPQ 27 765 301 // Cut file War3Patch.mpq from Warcraft III
+MPQ_2002_v1_BlockTableCut.MPQ 27 498 436 // Cut file War3Patch.mpq from Warcraft III
MPQ_2002_v1_BlockTableCut.sha 250
MPQ_2002_v1_ProtectedMap_HashTable_FakeValid.sha 1 067
MPQ_2002_v1_ProtectedMap_HashTable_FakeValid.w3x 1 089 638 // Protected Warcraft III map
@@ -58,6 +58,8 @@ MPQ_2002_v1_StrongSignature.sha 250
MPQ_2002_v1_StrongSignature.w3m 306 818 // (10)DustwallowKeys.w3m from Warcraft III
MPQ_2009_v2_WoW_patch.MPQ.part 31 396 380 // patch.MPQ.part from trial WoW build 10958
MPQ_2009_v2_WoW_patch.MPQ.sha 226
+MPQ_2010_v2_HashTableCompressed.MPQ.part 14 546 972 // interface.MPQ.part from WoW build 10958
+MPQ_2010_v2_HashTableCompressed.MPQ.sha 277
MPQ_2010_v2_HasUserData.s2ma 1 972 177 // (4) - AI - Kulas Ravine (1x).s2ma from Starcraft II Beta
MPQ_2010_v2_HasUserData.sha 261
MPQ_2010_v3_expansion-locale-frFR.MPQ 2 980 489 // expansion-locale-frFR.MPQ from WoW 12911
diff --git a/test/Test.cpp b/test/Test.cpp
index 9dcea46..eee67d5 100644
--- a/test/Test.cpp
+++ b/test/Test.cpp
@@ -173,116 +173,56 @@ static const char * PatchList_WoW16965[] =
static char szMpqDirectory[MAX_PATH];
size_t cchMpqDirectory = 0;
-static size_t ConvertSha1ToText(const unsigned char * sha1_digest, char * szSha1Text)
-{
- const char * szTable = "0123456789abcdef";
-
- for(size_t i = 0; i < SHA1_DIGEST_SIZE; i++)
- {
- *szSha1Text++ = szTable[(sha1_digest[0] >> 0x04)];
- *szSha1Text++ = szTable[(sha1_digest[0] & 0x0F)];
- sha1_digest++;
- }
-
- *szSha1Text = 0;
- return (SHA1_DIGEST_SIZE * 2);
-}
-
-#ifdef _UNICODE
-static const TCHAR * GetShortPlainName(const TCHAR * szFileName)
+static bool IsMpqExtension(const char * szFileName)
{
- const TCHAR * szPlainName = szFileName;
- const TCHAR * szPlainEnd = szFileName + _tcslen(szFileName);
-
- // If there is terminating slash or backslash, move to it
- while(szFileName < szPlainEnd)
- {
- if(szFileName[0] == _T('\\') || szFileName[0] == _T('/'))
- szPlainName = szFileName + 1;
- szFileName++;
+ const char * szExtension = strrchr(szFileName, '.');
+
+ if(szExtension != NULL)
+ {
+ if(!_stricmp(szExtension, ".mpq"))
+ return true;
+ if(!_stricmp(szExtension, ".w3m"))
+ return true;
+ if(!_stricmp(szExtension, ".w3x"))
+ return true;
+ if(!_stricmp(szExtension, ".mpqe"))
+ return true;
+ if(!_stricmp(szExtension, ".part"))
+ return true;
+ if(!_stricmp(szExtension, ".sv"))
+ return true;
+ if(!_stricmp(szExtension, ".s2ma"))
+ return true;
+ if(!_stricmp(szExtension, ".SC2Map"))
+ return true;
}
-
- // If the name is still too long, cut it
- if((szPlainEnd - szPlainName) > 50)
- szPlainName = szPlainEnd - 50;
- return szPlainName;
+
+ return false;
}
-static void CreateFullPathName(TCHAR * szBuffer, const char * szSubDir, const char * szNamePart1, const char * szNamePart2 = NULL)
+static bool IsUnicodeNameConvertableToAnsi(const TCHAR * szFileNameT, const char * szFileNameA)
{
- size_t nLength;
-
- // Copy the master MPQ directory
- mbstowcs(szBuffer, szMpqDirectory, cchMpqDirectory);
- szBuffer += cchMpqDirectory;
-
- // Append the subdirectory, if any
- if(szSubDir != NULL && (nLength = strlen(szSubDir)) != 0)
- {
- // No leading or trailing separators allowed
- assert(szSubDir[0] != '/' && szSubDir[0] != '\\');
- assert(szSubDir[nLength - 1] != '/' && szSubDir[nLength - 1] != '\\');
-
- // Append file path separator
- *szBuffer++ = PATH_SEPARATOR;
-
- // Copy the subdirectory
- mbstowcs(szBuffer, szSubDir, nLength);
-
- // Fix the path separators
- for(size_t i = 0; i < nLength; i++)
- szBuffer[i] = (szBuffer[i] != '\\' && szBuffer[i] != '/') ? szBuffer[i] : PATH_SEPARATOR;
-
- // Move the buffer pointer
- szBuffer += nLength;
- }
-
- // Copy the file name, if any
- if(szNamePart1 != NULL && (nLength = strlen(szNamePart1)) != 0)
- {
- // No path separator can be there
- assert(strchr(szNamePart1, '\\') == NULL);
- assert(strchr(szNamePart1, '/') == NULL);
-
- // Append file path separator
- *szBuffer++ = PATH_SEPARATOR;
-
- // Copy the file name
- mbstowcs(szBuffer, szNamePart1, nLength);
- szBuffer += nLength;
- }
+ TCHAR szUnicodeName[MAX_PATH];
- // Append the second part of the name
- if(szNamePart2 != NULL && (nLength = strlen(szNamePart2)) != 0)
- {
- // Copy the file name
- mbstowcs(szBuffer, szNamePart2, nLength);
- szBuffer += nLength;
- }
-
- // Terminate the buffer with zero
- *szBuffer = 0;
+ // Convert the ANSI to UNICODE and compare them
+ CopyFileName(szUnicodeName, szFileNameA, strlen(szFileNameA));
+ return (_tcsicmp(szUnicodeName, szFileNameT) == 0);
}
-TFileStream * FileStream_OpenFile(const char * szFileName, DWORD dwStreamFlags)
+static size_t ConvertSha1ToText(const unsigned char * sha1_digest, char * szSha1Text)
{
- TFileStream * pStream = NULL;
- TCHAR * szFileNameT;
- size_t nLength = strlen(szFileName);
+ const char * szTable = "0123456789abcdef";
- // Allocate buffer for the UNICODE file name
- szFileNameT = STORM_ALLOC(TCHAR, nLength + 1);
- if(szFileNameT != NULL)
+ for(size_t i = 0; i < SHA1_DIGEST_SIZE; i++)
{
- CopyFileName(szFileNameT, szFileName, nLength);
- pStream = FileStream_OpenFile(szFileNameT, dwStreamFlags);
- STORM_FREE(szFileNameT);
+ *szSha1Text++ = szTable[(sha1_digest[0] >> 0x04)];
+ *szSha1Text++ = szTable[(sha1_digest[0] & 0x0F)];
+ sha1_digest++;
}
- // Return what we got
- return pStream;
+ *szSha1Text = 0;
+ return (SHA1_DIGEST_SIZE * 2);
}
-#endif
static const char * GetShortPlainName(const char * szFileName)
{
@@ -359,13 +299,29 @@ static void CreateFullPathName(char * szBuffer, const char * szSubDir, const cha
*szBuffer = 0;
}
+TFileStream * OpenLocalFile(const char * szFileName, DWORD dwStreamFlags)
+{
+ TCHAR szFileNameT[MAX_PATH];
+
+ CopyFileName(szFileNameT, szFileName, strlen(szFileName));
+ return FileStream_OpenFile(szFileNameT, dwStreamFlags);
+}
+
+TFileStream * CreateLocalFile(const char * szFileName, DWORD dwStreamFlags)
+{
+ TCHAR szFileNameT[MAX_PATH];
+
+ CopyFileName(szFileNameT, szFileName, strlen(szFileName));
+ return FileStream_CreateFile(szFileNameT, dwStreamFlags);
+}
+
static int InitializeMpqDirectory(char * argv[], int argc)
{
TLogHelper Logger("InitWorkDir");
TFileStream * pStream;
const char * szWhereFrom = NULL;
const char * szDirName;
- TCHAR szFileName[MAX_PATH];
+ char szFullPath[MAX_PATH];
#ifdef _MSC_VER
// Mix the random number generator
@@ -397,8 +353,8 @@ static int InitializeMpqDirectory(char * argv[], int argc)
Logger.PrintMessage("Work directory %s (%s)", szMpqDirectory, szWhereFrom);
// Verify if the work MPQ directory is writable
- CreateFullPathName(szFileName, NULL, "TestFile.bin");
- pStream = FileStream_CreateFile(szFileName, 0);
+ CreateFullPathName(szFullPath, NULL, "TestFile.bin");
+ pStream = CreateLocalFile(szFullPath, 0);
if(pStream == NULL)
return Logger.PrintError("MPQ subdirectory is not writable");
@@ -406,10 +362,10 @@ static int InitializeMpqDirectory(char * argv[], int argc)
FileStream_Close(pStream);
// Verify if the working directory exists and if there is a subdirectory with the file name
- CreateFullPathName(szFileName, szMpqSubDir, "ListFile_Blizzard.txt");
- pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY);
+ CreateFullPathName(szFullPath, szMpqSubDir, "ListFile_Blizzard.txt");
+ pStream = OpenLocalFile(szFullPath, STREAM_FLAG_READ_ONLY);
if(pStream == NULL)
- return Logger.PrintError(_T("The main listfile (%s) was not found. Check your paths"), szFileName);
+ return Logger.PrintError("The main listfile (%s) was not found. Check your paths", GetShortPlainName(szFullPath));
// Close the stream
FileStream_Close(pStream);
@@ -495,22 +451,27 @@ static int VerifyFilePatchCount(TLogHelper * pLogger, HANDLE hMpq, const char *
return ERROR_SUCCESS;
}
-static int CreateEmptyFile(TLogHelper * pLogger, const char * szPlainName, ULONGLONG FileSize, TCHAR * szBuffer)
+static int CreateEmptyFile(TLogHelper * pLogger, const char * szPlainName, ULONGLONG FileSize, char * szBuffer)
{
TFileStream * pStream;
+ char szFullPath[MAX_PATH];
// Notify the user
pLogger->PrintProgress("Creating empty file %s ...", szPlainName);
// Construct the full path and crete the file
- CreateFullPathName(szBuffer, NULL, szPlainName);
- pStream = FileStream_CreateFile(szBuffer, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
+ CreateFullPathName(szFullPath, NULL, szPlainName);
+ pStream = CreateLocalFile(szFullPath, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
if(pStream == NULL)
- return pLogger->PrintError(_T("Failed to create file %s"), szBuffer);
+ return pLogger->PrintError("Failed to create file %s", szBuffer);
// Write the required size
FileStream_SetSize(pStream, FileSize);
FileStream_Close(pStream);
+
+ // Give the caller the full file name
+ if(szBuffer != NULL)
+ strcpy(szBuffer, szFullPath);
return ERROR_SUCCESS;
}
@@ -633,7 +594,7 @@ static int CreateMpqCopy(
TLogHelper * pLogger,
const char * szPlainName,
const char * szFileCopy,
- TCHAR * szBuffer,
+ char * szBuffer,
ULONGLONG PreMpqDataSize = 0,
ULONGLONG UserDataSize = 0)
{
@@ -641,8 +602,8 @@ static int CreateMpqCopy(
TFileStream * pStream2; // Target file
ULONGLONG ByteOffset = 0;
ULONGLONG FileSize = 0;
- TCHAR szFileName1[MAX_PATH];
- TCHAR szFileName2[MAX_PATH];
+ char szFileName1[MAX_PATH];
+ char szFileName2[MAX_PATH];
int nError = ERROR_SUCCESS;
// Notify the user
@@ -651,22 +612,22 @@ static int CreateMpqCopy(
// Construct both file names. Check if they are not the same
CreateFullPathName(szFileName1, szMpqSubDir, szPlainName);
CreateFullPathName(szFileName2, NULL, szFileCopy);
- if(!_tcsicmp(szFileName1, szFileName2))
+ if(!_stricmp(szFileName1, szFileName2))
{
pLogger->PrintError("Failed to create copy of MPQ (the copy name is the same like the original name)");
return ERROR_CAN_NOT_COMPLETE;
}
// Open the source file
- pStream1 = FileStream_OpenFile(szFileName1, STREAM_FLAG_READ_ONLY);
+ pStream1 = OpenLocalFile(szFileName1, STREAM_FLAG_READ_ONLY);
if(pStream1 == NULL)
{
- pLogger->PrintError(_T("Failed to open the source file %s"), szFileName1);
+ pLogger->PrintError("Failed to open the source file %s", szFileName1);
return ERROR_CAN_NOT_COMPLETE;
}
// Create the destination file
- pStream2 = FileStream_CreateFile(szFileName2, 0);
+ pStream2 = CreateLocalFile(szFileName2, 0);
if(pStream2 != NULL)
{
// If we should write some pre-MPQ data to the target file, do it
@@ -700,7 +661,7 @@ static int CreateMpqCopy(
FileStream_Close(pStream1);
if(szBuffer != NULL)
- _tcscpy(szBuffer, szFileName2);
+ strcpy(szBuffer, szFileName2);
if(nError != ERROR_SUCCESS)
pLogger->PrintError("Failed to create copy of MPQ");
return nError;
@@ -805,7 +766,7 @@ static TFileData * LoadLocalFile(TLogHelper * pLogger, const char * szFileName,
pLogger->PrintProgress("Loading local file ...");
// Attempt to open the file
- pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY);
+ pStream = OpenLocalFile(szFileName, STREAM_FLAG_READ_ONLY);
if(pStream == NULL)
{
if(pLogger != NULL && bMustSucceed == true)
@@ -1026,17 +987,21 @@ static int SearchArchive(
return nError;
}
-static int CreateNewArchive_FullPath(TLogHelper * pLogger, const TCHAR * szMpqName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq)
+static int CreateNewArchive(TLogHelper * pLogger, const char * szPlainName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq)
{
HANDLE hMpq = NULL;
+ TCHAR szMpqName[MAX_PATH];
+ char szFullPath[MAX_PATH];
// Make sure that the MPQ is deleted
- _tremove(szMpqName);
+ CreateFullPathName(szFullPath, NULL, szPlainName);
+ remove(szFullPath);
// Fix the flags
dwCreateFlags |= (MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES);
// Create the new MPQ
+ CopyFileName(szMpqName, szFullPath, strlen(szFullPath));
if(!SFileCreateArchive(szMpqName, dwCreateFlags, dwMaxFileCount, &hMpq))
return pLogger->PrintError(_T("Failed to create archive %s"), szMpqName);
@@ -1049,37 +1014,85 @@ static int CreateNewArchive_FullPath(TLogHelper * pLogger, const TCHAR * szMpqNa
return ERROR_SUCCESS;
}
-static int CreateNewArchive_AddPrefix(TLogHelper * pLogger, const wchar_t * szPlainName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq)
+// Creates new archive with UNICODE name. Adds prefix to the name
+static int CreateNewArchiveU(TLogHelper * pLogger, const wchar_t * szPlainName, DWORD dwCreateFlags, DWORD dwMaxFileCount)
{
#ifdef _UNICODE
- wchar_t szMpqName[MAX_PATH];
+ HANDLE hMpq = NULL;
+ TCHAR szMpqName[MAX_PATH];
+ char szFullPath[MAX_PATH];
- CreateFullPathName(szMpqName, NULL, "StormLibTest_");
- _tcscat(szMpqName, szPlainName);
- return CreateNewArchive_FullPath(pLogger, szMpqName, dwCreateFlags, dwMaxFileCount, phMpq);
+ // Construct the full UNICODE name
+ CreateFullPathName(szFullPath, NULL, "StormLibTest_");
+ CopyFileName(szMpqName, szFullPath, strlen(szFullPath));
+ wcscat(szMpqName, szPlainName);
+
+ // Make sure that the MPQ is deleted
+ _tremove(szMpqName);
+
+ // Create the archive
+ pLogger->PrintProgress("Creating new archive with UNICODE name ...");
+ if(!SFileCreateArchive(szMpqName, dwCreateFlags, dwMaxFileCount, &hMpq))
+ return pLogger->PrintError(_T("Failed to create archive %s"), szMpqName);
+
+ SFileCloseArchive(hMpq);
#else
pLogger = pLogger;
szPlainName = szPlainName;
dwCreateFlags = dwCreateFlags;
dwMaxFileCount = dwMaxFileCount;
- phMpq = phMpq;
- return ERROR_SUCCESS;
#endif
+ return ERROR_SUCCESS;
}
-static int CreateNewArchive(TLogHelper * pLogger, const char * szPlainName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq)
+static int OpenExistingArchive(TLogHelper * pLogger, const char * szFullPath, DWORD dwFlags, HANDLE * phMpq)
{
+ HANDLE hMpq = NULL;
TCHAR szMpqName[MAX_PATH];
+ int nError = ERROR_SUCCESS;
+
+ // Is it an encrypted MPQ ?
+ if(strstr(szFullPath, ".MPQE") != NULL)
+ dwFlags |= STREAM_PROVIDER_ENCRYPTED;
+ if(strstr(szFullPath, ".MPQ.part") != NULL)
+ dwFlags |= STREAM_PROVIDER_PARTIAL;
+
+ // Open the copied archive
+ pLogger->PrintProgress("Opening archive %s ...", GetShortPlainName(szFullPath));
+ CopyFileName(szMpqName, szFullPath, strlen(szFullPath));
+ if(!SFileOpenArchive(szMpqName, 0, dwFlags, &hMpq))
+ {
+ // Ignore the error if it's an AVI file
+ if(GetLastError() == ERROR_AVI_FILE)
+ return ERROR_AVI_FILE;
+ return pLogger->PrintError("Failed to open archive %s", szFullPath);
+ }
- CreateFullPathName(szMpqName, NULL, szPlainName);
- return CreateNewArchive_FullPath(pLogger, szMpqName, dwCreateFlags, dwMaxFileCount, phMpq);
+ // Store the archive handle or close the archive
+ if(phMpq == NULL)
+ SFileCloseArchive(hMpq);
+ else
+ *phMpq = hMpq;
+ return nError;
}
-static int OpenExistingArchive(TLogHelper * pLogger, const char * szFileName, const char * szCopyName, HANDLE * phMpq)
+static int OpenPatchArchive(TLogHelper * pLogger, HANDLE hMpq, const char * szFullPath)
+{
+ TCHAR szPatchName[MAX_PATH];
+ int nError = ERROR_SUCCESS;
+
+ pLogger->PrintProgress("Adding patch %s ...", GetShortPlainName(szFullPath));
+ CopyFileName(szPatchName, szFullPath, strlen(szFullPath));
+ if(!SFileOpenPatchArchive(hMpq, szPatchName, NULL, 0))
+ nError = pLogger->PrintError("Failed to add patch %s ...", szFullPath);
+
+ return nError;
+}
+
+static int OpenExistingArchiveWithCopy(TLogHelper * pLogger, const char * szFileName, const char * szCopyName, HANDLE * phMpq)
{
- TCHAR szMpqName[MAX_PATH];
- HANDLE hMpq = NULL;
DWORD dwFlags = 0;
+ char szFullPath[MAX_PATH];
int nError = ERROR_SUCCESS;
// We expect MPQ directory to be already prepared by InitializeMpqDirectory
@@ -1091,7 +1104,7 @@ static int OpenExistingArchive(TLogHelper * pLogger, const char * szFileName, co
// If both names entered, create a copy
if(szFileName != NULL && szCopyName != NULL)
{
- nError = CreateMpqCopy(pLogger, szFileName, szCopyName, szMpqName);
+ nError = CreateMpqCopy(pLogger, szFileName, szCopyName, szFullPath);
if(nError != ERROR_SUCCESS)
return nError;
}
@@ -1099,60 +1112,42 @@ static int OpenExistingArchive(TLogHelper * pLogger, const char * szFileName, co
// If only source name entered, open it for read-only access
else if(szFileName != NULL && szCopyName == NULL)
{
- CreateFullPathName(szMpqName, szMpqSubDir, szFileName);
+ CreateFullPathName(szFullPath, szMpqSubDir, szFileName);
dwFlags |= MPQ_OPEN_READ_ONLY;
}
// If only target name entered, open it directly
else if(szFileName == NULL && szCopyName != NULL)
{
- CreateFullPathName(szMpqName, NULL, szCopyName);
+ CreateFullPathName(szFullPath, NULL, szCopyName);
}
- // Is it an encrypted MPQ ?
- if(_tcsstr(szMpqName, _T(".MPQE")) != NULL)
- dwFlags |= MPQ_OPEN_ENCRYPTED;
-
- // Open the copied archive
- pLogger->PrintProgress("Opening archive %s ...", (szCopyName != NULL) ? szCopyName : szFileName);
- if(!SFileOpenArchive(szMpqName, 0, dwFlags, &hMpq))
- return pLogger->PrintError(_T("Failed to open archive %s"), szMpqName);
-
- // Store the archive handle or close the archive
- if(phMpq == NULL)
- SFileCloseArchive(hMpq);
- else
- *phMpq = hMpq;
- return nError;
+ // Open the archive
+ return OpenExistingArchive(pLogger, szFullPath, dwFlags, phMpq);
}
static int OpenPatchedArchive(TLogHelper * pLogger, HANDLE * phMpq, const char * PatchList[])
{
- TCHAR szMpqName[MAX_PATH];
HANDLE hMpq = NULL;
+ char szFullPath[MAX_PATH];
int nError = ERROR_SUCCESS;
// The first file is expected to be valid
assert(PatchList[0] != NULL);
// Open the primary MPQ
- CreateFullPathName(szMpqName, szMpqSubDir, PatchList[0]);
- pLogger->PrintProgress("Opening base MPQ %s ...", PatchList[0]);
- if(!SFileOpenArchive(szMpqName, 0, MPQ_OPEN_READ_ONLY, &hMpq))
- nError = pLogger->PrintError(_T("Failed to open the archive %s"), szMpqName);
+ CreateFullPathName(szFullPath, szMpqSubDir, PatchList[0]);
+ nError = OpenExistingArchive(pLogger, szFullPath, MPQ_OPEN_READ_ONLY, &hMpq);
// Add all patches
if(nError == ERROR_SUCCESS)
{
for(size_t i = 1; PatchList[i] != NULL; i++)
{
- CreateFullPathName(szMpqName, szMpqPatchDir, PatchList[i]);
- pLogger->PrintProgress("Adding patch %s ...", PatchList[i]);
- if(!SFileOpenPatchArchive(hMpq, szMpqName, NULL, 0))
- {
- nError = pLogger->PrintError(_T("Failed to add patch %s ..."), szMpqName);
+ CreateFullPathName(szFullPath, szMpqPatchDir, PatchList[i]);
+ nError = OpenPatchArchive(pLogger, hMpq, szFullPath);
+ if(nError != ERROR_SUCCESS)
break;
- }
}
}
@@ -1208,15 +1203,16 @@ static int AddLocalFileToMpq(
TLogHelper * pLogger,
HANDLE hMpq,
const char * szArchivedName,
- const TCHAR * szFileName,
+ const char * szLocalFileName,
DWORD dwFlags = 0,
DWORD dwCompression = 0,
bool bMustSucceed = false)
{
+ TCHAR szFileName[MAX_PATH];
DWORD dwVerifyResult;
// Notify the user
- pLogger->PrintProgress("Adding file %s (%u of %u)...", szArchivedName, pLogger->UserCount, pLogger->UserTotal);
+ pLogger->PrintProgress("Adding file %s (%u of %u)...", GetShortPlainName(szLocalFileName), pLogger->UserCount, pLogger->UserTotal);
pLogger->UserString = szArchivedName;
// Get the default flags
@@ -1229,6 +1225,7 @@ static int AddLocalFileToMpq(
SFileSetAddFileCallback(hMpq, AddFileCallback, pLogger);
// Add the file to the MPQ
+ CopyFileName(szFileName, szLocalFileName, strlen(szLocalFileName));
if(!SFileAddFileEx(hMpq, szFileName, szArchivedName, dwFlags, dwCompression, MPQ_COMPRESSION_NEXT_SAME))
{
if(bMustSucceed)
@@ -1346,7 +1343,7 @@ static int TestVerifyFileChecksum(const char * szFullPath)
TLogHelper Logger("VerifyFileHash", szShortPlainName);
// Open the file to be verified
- pStream = FileStream_OpenFile(szFullPath, STREAM_FLAG_READ_ONLY);
+ pStream = OpenLocalFile(szFullPath, STREAM_FLAG_READ_ONLY);
if(pStream != NULL)
{
// Notify the user
@@ -1453,15 +1450,15 @@ static int TestPartFileRead(const char * szPlainName)
ULONGLONG ByteOffset;
ULONGLONG FileSize = 0;
TFileStream * pStream;
- TCHAR szFileName[MAX_PATH];
+ char szFileName[MAX_PATH];
BYTE Buffer[0x100];
int nError = ERROR_SUCCESS;
// Open the partial file
CreateFullPathName(szFileName, szMpqSubDir, szPlainName);
- pStream = FileStream_OpenFile(szFileName, STREAM_PROVIDER_PARTIAL | BASE_PROVIDER_FILE | STREAM_FLAG_READ_ONLY);
+ pStream = OpenLocalFile(szFileName, STREAM_PROVIDER_PARTIAL | BASE_PROVIDER_FILE | STREAM_FLAG_READ_ONLY);
if(pStream == NULL)
- nError = Logger.PrintError(_T("Failed to open %s"), szFileName);
+ nError = Logger.PrintError("Failed to open %s", szFileName);
// Get the size of the stream
if(nError == ERROR_SUCCESS)
@@ -1510,7 +1507,7 @@ static int TestOpenFile_OpenById(const char * szPlainName)
int nError;
// Copy the archive so we won't fuck up the original one
- nError = OpenExistingArchive(&Logger, szPlainName, NULL, &hMpq);
+ nError = OpenExistingArchiveWithCopy(&Logger, szPlainName, NULL, &hMpq);
// Now try to open a file without knowing the file name
if(nError == ERROR_SUCCESS)
@@ -1554,11 +1551,16 @@ static int TestOpenArchive(const char * szPlainName, const char * szListFile = N
TFileData * pFileData;
HANDLE hMpq;
DWORD dwFileCount = 0;
+ DWORD dwTestFlags;
char szListFileBuff[MAX_PATH];
+ bool bIsPartialMpq = false;
int nError;
+ // If the file is a partial MPQ, don;t load all files
+ bIsPartialMpq = (strstr(szPlainName, ".MPQ.part") != NULL);
+
// Copy the archive so we won't fuck up the original one
- nError = OpenExistingArchive(&Logger, szPlainName, NULL, &hMpq);
+ nError = OpenExistingArchiveWithCopy(&Logger, szPlainName, NULL, &hMpq);
if(nError == ERROR_SUCCESS)
{
// If the listfile was given, add it to the MPQ
@@ -1588,7 +1590,8 @@ static int TestOpenArchive(const char * szPlainName, const char * szListFile = N
}
// Search the archive and load every file
- nError = SearchArchive(&Logger, hMpq, TEST_FLAG_LOAD_FILES, &dwFileCount);
+ dwTestFlags = bIsPartialMpq ? 0 : TEST_FLAG_LOAD_FILES;
+ nError = SearchArchive(&Logger, hMpq, dwTestFlags, &dwFileCount);
SFileCloseArchive(hMpq);
}
@@ -1628,24 +1631,18 @@ static int TestOpenArchive_ReadOnly(const char * szPlainName, bool bReadOnly)
const char * szCopyName;
TLogHelper Logger("ReadOnlyTest", szPlainName);
HANDLE hMpq;
- TCHAR szMpqName[MAX_PATH];
- DWORD dwFlags = 0;
+ char szFullPathName[MAX_PATH];
+ DWORD dwFlags = bReadOnly ? MPQ_OPEN_READ_ONLY : 0;;
bool bMustSucceed;
int nError;
// Copy the fiel so we wont screw up something
szCopyName = bReadOnly ? "StormLibTest_ReadOnly.mpq" : "StormLibTest_ReadWrite.mpq";
- nError = CreateMpqCopy(&Logger, szPlainName, szCopyName, szMpqName);
+ nError = CreateMpqCopy(&Logger, szPlainName, szCopyName, szFullPathName);
// Now open the archive for read-only access
if(nError == ERROR_SUCCESS)
- {
- Logger.PrintProgress("Opening archive %s ...", szCopyName);
-
- dwFlags = bReadOnly ? MPQ_OPEN_READ_ONLY : 0;
- if(!SFileOpenArchive(szMpqName, 0, dwFlags, &hMpq))
- nError = Logger.PrintError("Failed to open the archive %s", szCopyName);
- }
+ nError = OpenExistingArchive(&Logger, szFullPathName, dwFlags, &hMpq);
// Now try to add a file. This must fail if the MPQ is read only
if(nError == ERROR_SUCCESS)
@@ -1692,8 +1689,8 @@ static int TestOpenArchive_GetFileInfo(const char * szPlainName1, const char * s
int nError4;
// Copy the archive so we won't fuck up the original one
- nError1 = OpenExistingArchive(&Logger, szPlainName1, NULL, &hMpq1);
- nError4 = OpenExistingArchive(&Logger, szPlainName4, NULL, &hMpq4);
+ nError1 = OpenExistingArchiveWithCopy(&Logger, szPlainName1, NULL, &hMpq1);
+ nError4 = OpenExistingArchiveWithCopy(&Logger, szPlainName4, NULL, &hMpq4);
if(nError1 == ERROR_SUCCESS && nError4 == ERROR_SUCCESS)
{
// Invalid handle - expected (false, ERROR_INVALID_HANDLE)
@@ -1766,7 +1763,7 @@ static int TestOpenArchive_VerifySignature(const char * szPlainName, const char
int nError = ERROR_SUCCESS;
// We need original name for the signature check
- nError = OpenExistingArchive(&Logger, szPlainName, szOriginalName, &hMpq);
+ nError = OpenExistingArchiveWithCopy(&Logger, szPlainName, szOriginalName, &hMpq);
if(nError == ERROR_SUCCESS)
{
// Query the signature types
@@ -1803,20 +1800,21 @@ static int TestOpenArchive_CraftedUserData(const char * szPlainName, const char
HANDLE hMpq;
DWORD dwFileCount1 = 0;
DWORD dwFileCount2 = 0;
- TCHAR szMpqName[MAX_PATH];
BYTE FileHash1[MD5_DIGEST_SIZE];
BYTE FileHash2[MD5_DIGEST_SIZE];
+ char szFullPath[MAX_PATH];
int nError;
// Create copy of the archive, with interleaving some user data
- nError = CreateMpqCopy(&Logger, szPlainName, szCopyName, szMpqName, 0x400, 0x531);
+ nError = CreateMpqCopy(&Logger, szPlainName, szCopyName, szFullPath, 0x400, 0x531);
// Open the archive and load some files
if(nError == ERROR_SUCCESS)
{
- Logger.PrintProgress("Opening archive %s ...", szCopyName);
- if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq))
- return Logger.PrintError(_T("Failed to open archive %s"), szMpqName);
+ // Open the archive
+ nError = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq);
+ if(nError != ERROR_SUCCESS)
+ return nError;
// Verify presence of (listfile) and (attributes)
CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, true);
@@ -1831,14 +1829,14 @@ static int TestOpenArchive_CraftedUserData(const char * szPlainName, const char
if(nError == ERROR_SUCCESS)
{
// Open the archive again
- Logger.PrintProgress("Reopening archive %s ...", szCopyName);
- if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq))
- return Logger.PrintError(_T("Failed to open archive %s"), szMpqName);
+ nError = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq);
+ if(nError != ERROR_SUCCESS)
+ return nError;
// Compact the archive
- Logger.PrintProgress("Compacting archive %s ...", szMpqName);
+ Logger.PrintProgress("Compacting archive %s ...", GetShortPlainName(szFullPath));
if(!SFileSetCompactCallback(hMpq, CompactCallback, &Logger))
- nError = Logger.PrintError(_T("Failed to compact archive %s"), szMpqName);
+ nError = Logger.PrintError("Failed to compact archive %s", szFullPath);
SFileCompactArchive(hMpq, NULL, false);
SFileCloseArchive(hMpq);
@@ -1847,9 +1845,10 @@ static int TestOpenArchive_CraftedUserData(const char * szPlainName, const char
// Open the archive and load some files
if(nError == ERROR_SUCCESS)
{
- Logger.PrintProgress("Reopening archive %s ...", szCopyName);
- if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq))
- return Logger.PrintError(_T("Failed to open archive %s"), szMpqName);
+ // Open the archive
+ nError = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq);
+ if(nError != ERROR_SUCCESS)
+ return nError;
// Verify presence of (listfile) and (attributes)
CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, true);
@@ -1873,6 +1872,35 @@ static int TestOpenArchive_CraftedUserData(const char * szPlainName, const char
return nError;
}
+
+// Searches a direcroty
+static int TestOpenEachArchive_EachFile(const char * szFullPath)
+{
+ HANDLE hMpq = NULL;
+ DWORD dwFileCount = 0;
+ int nError = ERROR_SUCCESS;
+
+ // Check if it's a MPQ file type
+ if(IsMpqExtension(szFullPath))
+ {
+ TLogHelper Logger("OpenEachMpqTest", GetShortPlainName(szFullPath));
+
+ // Open the MPQ name
+ nError = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq);
+ if(nError == ERROR_AVI_FILE)
+ return ERROR_SUCCESS;
+
+ // Search the archive and load every file
+ if(nError == ERROR_SUCCESS)
+ {
+ nError = SearchArchive(&Logger, hMpq, 0, &dwFileCount);
+ SFileCloseArchive(hMpq);
+ }
+ }
+
+ return nError;
+}
+
// Adding a file to MPQ that had no (listfile) and no (attributes).
// We expect that neither of these will be present after the archive is closed
static int TestAddFile_ListFileTest(const char * szSourceMpq, bool bShouldHaveListFile, bool bShouldHaveAttributes)
@@ -1887,7 +1915,7 @@ static int TestAddFile_ListFileTest(const char * szSourceMpq, bool bShouldHaveLi
int nError = ERROR_SUCCESS;
// Copy the archive so we won't fuck up the original one
- nError = OpenExistingArchive(&Logger, szSourceMpq, szBackupMpq, &hMpq);
+ nError = OpenExistingArchiveWithCopy(&Logger, szSourceMpq, szBackupMpq, &hMpq);
// Add a file
if(nError == ERROR_SUCCESS)
@@ -1899,7 +1927,7 @@ static int TestAddFile_ListFileTest(const char * szSourceMpq, bool bShouldHaveLi
// Now reopen the archive
if(nError == ERROR_SUCCESS)
- nError = OpenExistingArchive(&Logger, NULL, szBackupMpq, &hMpq);
+ nError = OpenExistingArchiveWithCopy(&Logger, NULL, szBackupMpq, &hMpq);
// Now the file has been written and the MPQ has been saved.
// We Reopen the MPQ and check if there is no (listfile) nor (attributes).
@@ -1962,7 +1990,7 @@ static int TestCreateArchive_EmptyMpq(const char * szPlainName, DWORD dwCreateFl
// Reopen the empty MPQ
if(nError == ERROR_SUCCESS)
{
- nError = OpenExistingArchive(&Logger, NULL, szPlainName, &hMpq);
+ nError = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq);
if(nError == ERROR_SUCCESS)
{
SFileGetFileInfo(hMpq, SFileMpqNumberOfFiles, &dwFileCount, sizeof(dwFileCount), NULL);
@@ -2018,7 +2046,7 @@ static int TestCreateArchive_FillArchive(const char * szPlainName)
// Reopen the archive again
if(nError == ERROR_SUCCESS)
- nError = OpenExistingArchive(&Logger, NULL, szPlainName, &hMpq);
+ nError = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq);
// The archive should still be full
if(nError == ERROR_SUCCESS)
@@ -2060,7 +2088,7 @@ static int TestCreateArchive_FillArchive(const char * szPlainName)
// Reopen the archive for the third time to verify that both internal files are there
if(nError == ERROR_SUCCESS)
{
- nError = OpenExistingArchive(&Logger, NULL, szPlainName, &hMpq);
+ nError = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq);
if(nError == ERROR_SUCCESS)
{
CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, true);
@@ -2099,7 +2127,7 @@ static int TestCreateArchive_IncMaxFileCount(const char * szPlainName)
for(DWORD i = 0; i < 10; i++)
{
// Open the archive again
- nError = OpenExistingArchive(&Logger, NULL, szPlainName, &hMpq);
+ nError = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq);
if(nError != ERROR_SUCCESS)
break;
@@ -2134,27 +2162,27 @@ static int TestCreateArchive_UnicodeNames()
TLogHelper Logger("MpqUnicodeName");
int nError = ERROR_SUCCESS;
- nError = CreateNewArchive_AddPrefix(&Logger, szUnicodeName1, MPQ_CREATE_ARCHIVE_V1, 15, NULL);
+ nError = CreateNewArchiveU(&Logger, szUnicodeName1, MPQ_CREATE_ARCHIVE_V1, 15);
if(nError != ERROR_SUCCESS)
return nError;
- nError = CreateNewArchive_AddPrefix(&Logger, szUnicodeName2, MPQ_CREATE_ARCHIVE_V2, 58, NULL);
+ nError = CreateNewArchiveU(&Logger, szUnicodeName2, MPQ_CREATE_ARCHIVE_V2, 58);
if(nError != ERROR_SUCCESS)
return nError;
- nError = CreateNewArchive_AddPrefix(&Logger, szUnicodeName3, MPQ_CREATE_ARCHIVE_V3, 15874, NULL);
+ nError = CreateNewArchiveU(&Logger, szUnicodeName3, MPQ_CREATE_ARCHIVE_V3, 15874);
if(nError != ERROR_SUCCESS)
return nError;
- nError = CreateNewArchive_AddPrefix(&Logger, szUnicodeName4, MPQ_CREATE_ARCHIVE_V4, 87541, NULL);
+ nError = CreateNewArchiveU(&Logger, szUnicodeName4, MPQ_CREATE_ARCHIVE_V4, 87541);
if(nError != ERROR_SUCCESS)
return nError;
- nError = CreateNewArchive_AddPrefix(&Logger, szUnicodeName5, MPQ_CREATE_ARCHIVE_V3, 87541, NULL);
+ nError = CreateNewArchiveU(&Logger, szUnicodeName5, MPQ_CREATE_ARCHIVE_V3, 87541);
if(nError != ERROR_SUCCESS)
return nError;
- nError = CreateNewArchive_AddPrefix(&Logger, szUnicodeName5, MPQ_CREATE_ARCHIVE_V2, 87541, NULL);
+ nError = CreateNewArchiveU(&Logger, szUnicodeName5, MPQ_CREATE_ARCHIVE_V2, 87541);
if(nError != ERROR_SUCCESS)
return nError;
@@ -2165,9 +2193,9 @@ static int TestCreateArchive_FileFlagTest(const char * szPlainName)
{
TLogHelper Logger("FileFlagTest", szPlainName);
HANDLE hMpq = NULL; // Handle of created archive
- TCHAR szFileName1[MAX_PATH];
- TCHAR szFileName2[MAX_PATH];
- TCHAR szMpqName[MAX_PATH];
+ char szFileName1[MAX_PATH];
+ char szFileName2[MAX_PATH];
+ char szFullPath[MAX_PATH];
const char * szMiddleFile = "FileTest_10.exe";
LCID LocaleIDs[] = {0x000, 0x405, 0x406, 0x407, 0xFFFF};
char szArchivedName[MAX_PATH];
@@ -2181,11 +2209,11 @@ static int TestCreateArchive_FileFlagTest(const char * szPlainName)
CreateFullPathName(szFileName2, szMpqSubDir, "AddFile.bin");
// Create an empty file that will serve as holder for the MPQ
- nError = CreateEmptyFile(&Logger, szPlainName, 0x100000, szMpqName);
+ nError = CreateEmptyFile(&Logger, szPlainName, 0x100000, szFullPath);
// Create new MPQ archive over that file
if(nError == ERROR_SUCCESS)
- nError = CreateNewArchive_FullPath(&Logger, szMpqName, MPQ_CREATE_ARCHIVE_V1, 17, &hMpq);
+ nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V1, 17, &hMpq);
// Add the same file multiple times
if(nError == ERROR_SUCCESS)
@@ -2314,7 +2342,7 @@ static int TestCreateArchive_FileFlagTest(const char * szPlainName)
hMpq = NULL;
// Try to reopen the archive
- nError = OpenExistingArchive(&Logger, NULL, szPlainName, NULL);
+ nError = OpenExistingArchive(&Logger, szFullPath, 0, NULL);
return nError;
}
@@ -2322,8 +2350,7 @@ static int TestCreateArchive_CompressionsTest(const char * szPlainName)
{
TLogHelper Logger("CompressionsTest", szPlainName);
HANDLE hMpq = NULL; // Handle of created archive
- TCHAR szFileName[MAX_PATH]; // Source file to be added
- TCHAR szMpqName[MAX_PATH];
+ char szFileName[MAX_PATH]; // Source file to be added
char szArchivedName[MAX_PATH];
DWORD dwCmprCount = sizeof(Compressions) / sizeof(DWORD);
DWORD dwAddedFiles = 0;
@@ -2333,10 +2360,9 @@ static int TestCreateArchive_CompressionsTest(const char * szPlainName)
// Create paths for local file to be added
CreateFullPathName(szFileName, szMpqSubDir, "AddFile.wav");
- CreateFullPathName(szMpqName, NULL, szPlainName);
// Create new archive
- nError = CreateNewArchive_FullPath(&Logger, szMpqName, MPQ_CREATE_ARCHIVE_V4, 0x40, &hMpq);
+ nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V4, 0x40, &hMpq);
// Add the same file multiple times
if(nError == ERROR_SUCCESS)
@@ -2359,7 +2385,7 @@ static int TestCreateArchive_CompressionsTest(const char * szPlainName)
// Reopen the archive extract each WAVE file and try to play it
if(nError == ERROR_SUCCESS)
{
- nError = OpenExistingArchive(&Logger, NULL, szPlainName, &hMpq);
+ nError = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq);
if(nError == ERROR_SUCCESS)
{
SearchArchive(&Logger, hMpq, TEST_FLAG_LOAD_FILES | TEST_FLAG_PLAY_WAVES, &dwFoundFiles, NULL);
@@ -2427,7 +2453,7 @@ static int TestCreateArchive_ListFilePos(const char * szPlainName)
// Reopen the archive to catch any asserts
if(nError == ERROR_SUCCESS)
- nError = OpenExistingArchive(&Logger, NULL, szPlainName, &hMpq);
+ nError = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq);
// Check that (listfile) is at the end
if(nError == ERROR_SUCCESS)
@@ -2473,7 +2499,7 @@ static int TestCreateArchive_BigArchive(const char * szPlainName)
const char * szFileMask = "AddedFile_%02u.txt";
TLogHelper Logger("BigMpqTest");
HANDLE hMpq = NULL; // Handle of created archive
- TCHAR szFileName[MAX_PATH];
+ char szLocalFileName[MAX_PATH];
char szArchivedName[MAX_PATH];
DWORD dwMaxFileCount = 0x20;
DWORD dwAddedCount = 0;
@@ -2485,13 +2511,13 @@ static int TestCreateArchive_BigArchive(const char * szPlainName)
if(nError == ERROR_SUCCESS)
{
// Now add few really big files
- CreateFullPathName(szFileName, szMpqSubDir, "MPQ_1997_v1_Diablo1_DIABDAT.MPQ");
+ CreateFullPathName(szLocalFileName, szMpqSubDir, "MPQ_1997_v1_Diablo1_DIABDAT.MPQ");
Logger.UserTotal = (dwMaxFileCount / 2);
for(i = 0; i < dwMaxFileCount / 2; i++)
{
sprintf(szArchivedName, szFileMask, i + 1);
- nError = AddLocalFileToMpq(&Logger, hMpq, szArchivedName, szFileName, 0, 0, true);
+ nError = AddLocalFileToMpq(&Logger, hMpq, szArchivedName, szLocalFileName, 0, 0, true);
if(nError != ERROR_SUCCESS)
break;
@@ -2507,7 +2533,7 @@ static int TestCreateArchive_BigArchive(const char * szPlainName)
// Reopen the archive to catch any asserts
if(nError == ERROR_SUCCESS)
- nError = OpenExistingArchive(&Logger, NULL, szPlainName, &hMpq);
+ nError = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq);
// Check that (listfile) is at the end
if(nError == ERROR_SUCCESS)
@@ -2521,21 +2547,27 @@ static int TestCreateArchive_BigArchive(const char * szPlainName)
return nError;
}
-static int TestForEachArchive(ARCHIVE_TEST pfnTest, char * szSearchMask, char * szPlainName)
+static int TestForEachArchive(ARCHIVE_TEST pfnTest, TCHAR * szSearchMask, TCHAR * szPlainName)
{
- char * szPathBuff = NULL;
+ TCHAR szPathBuffT[MAX_PATH];
+ char szPathBuffA[MAX_PATH];
+ char szFullPath[MAX_PATH];
int nError = ERROR_SUCCESS;
- // If the name was not entered, use new one
+ // If the name was not entered, construct new one
if(szSearchMask == NULL)
{
- szPathBuff = STORM_ALLOC(char, MAX_PATH);
- if(szPathBuff != NULL)
- {
- CreateFullPathName(szPathBuff, szMpqSubDir, "*");
- szSearchMask = szPathBuff;
- szPlainName = strrchr(szSearchMask, '*');
- }
+ CreateFullPathName(szPathBuffA, szMpqSubDir, "*");
+ CopyFileName(szPathBuffT, szPathBuffA, strlen(szPathBuffA));
+ szSearchMask = szPathBuffT;
+ }
+
+ // Get the position of the plain name
+ if(szPlainName == NULL)
+ {
+ szPlainName = _tcsrchr(szSearchMask, _T('*'));
+ if(szPlainName == NULL)
+ return ERROR_SUCCESS;
}
// At this point, both pointers must be valid
@@ -2545,31 +2577,36 @@ static int TestForEachArchive(ARCHIVE_TEST pfnTest, char * szSearchMask, char *
if(szSearchMask != NULL && szPlainName != NULL)
{
#ifdef PLATFORM_WINDOWS
- WIN32_FIND_DATAA wf;
+ WIN32_FIND_DATA wf;
HANDLE hFind;
// Initiate search. Use ANSI function only
- hFind = FindFirstFileA(szSearchMask, &wf);
+ hFind = FindFirstFile(szSearchMask, &wf);
if(hFind != INVALID_HANDLE_VALUE)
{
// Skip the first entry, since it's always "." or ".."
- while(FindNextFileA(hFind, &wf) && nError == ERROR_SUCCESS)
+ while(FindNextFile(hFind, &wf) && nError == ERROR_SUCCESS)
{
// Found a directory?
if(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if(wf.cFileName[0] != '.')
{
- sprintf(szPlainName, "%s\\*", wf.cFileName);
- nError = TestForEachArchive(pfnTest, szSearchMask, strrchr(szSearchMask, '*'));
+ _stprintf(szPlainName, _T("%s\\*"), wf.cFileName);
+ nError = TestForEachArchive(pfnTest, szSearchMask, NULL);
}
}
else
{
if(pfnTest != NULL)
{
- strcpy(szPlainName, wf.cFileName);
- nError = pfnTest(szSearchMask);
+ // Create the full path as TCHAR
+ _tcscpy(szPlainName, wf.cFileName);
+ CopyFileName(szFullPath, szSearchMask, _tcslen(szSearchMask));
+
+ // Check for UNICODE names
+ if(IsUnicodeNameConvertableToAnsi(szSearchMask, szFullPath))
+ nError = pfnTest(szFullPath);
}
}
}
@@ -2580,12 +2617,22 @@ static int TestForEachArchive(ARCHIVE_TEST pfnTest, char * szSearchMask, char *
}
// Free the path buffer, if any
- if(szPathBuff != NULL)
- STORM_FREE(szPathBuff);
- szPathBuff = NULL;
return nError;
}
+static int TestOpenArchive_EachArchive()
+{
+ TCHAR szSearchMaskT[MAX_PATH];
+ char szSearchMaskA[MAX_PATH];
+
+ // Create the TCHAR name of search mask
+ CreateFullPathName(szSearchMaskA, NULL, "*");
+ CopyFileName(szSearchMaskT, szSearchMaskA, strlen(szSearchMaskA));
+
+ // Invoke the searching function
+ return TestForEachArchive(TestOpenEachArchive_EachFile, szSearchMaskT, NULL);
+}
+
//-----------------------------------------------------------------------------
// Main
@@ -2600,7 +2647,7 @@ int main(int argc, char * argv[])
// Initialize storage and mix the random number generator
printf("==== Test Suite for StormLib version %s ====\n", STORMLIB_VERSION_STRING);
nError = InitializeMpqDirectory(argv, argc);
-/*
+
// Search all testing archives and verify their SHA1 hash
if(nError == ERROR_SUCCESS)
nError = TestForEachArchive(TestVerifyFileChecksum, NULL, NULL);
@@ -2669,6 +2716,10 @@ int main(int argc, char * argv[])
if(nError == ERROR_SUCCESS)
nError = TestOpenArchive("MPx_2013_v1_WarOfTheImmortals.sqp", "ListFile_WarOfTheImmortals.txt");
+ // Open a partial MPQ with compressed hash table
+ if(nError == ERROR_SUCCESS)
+ nError = TestOpenArchive("MPQ_2010_v2_HashTableCompressed.MPQ.part");
+
// Open a patched archive
if(nError == ERROR_SUCCESS)
nError = TestOpenArchive_Patched(PatchList_WoW_OldWorld13286, "OldWorld\\World\\Model.blob", 2);
@@ -2713,6 +2764,10 @@ int main(int argc, char * argv[])
if(nError == ERROR_SUCCESS)
nError = TestOpenArchive_CraftedUserData("MPQ_2013_v4_expansion1.MPQ", "StormLibTest_CraftedMpq3_v4.mpq");
+ // Open every MPQ that we have in the storage
+ if(nError == ERROR_SUCCESS)
+ nError = TestOpenArchive_EachArchive();
+
// Test modifying file with no (listfile) and no (attributes)
if(nError == ERROR_SUCCESS)
nError = TestAddFile_ListFileTest("MPQ_1997_v1_Diablo1_DIABDAT.MPQ", false, false);
@@ -2752,7 +2807,7 @@ int main(int argc, char * argv[])
// 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");
-*/
+
// Open a MPQ (add custom user data to it)
if(nError == ERROR_SUCCESS)
nError = TestCreateArchive_BigArchive("StormLibTest_BigArchive_v4.mpq");