summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorLadislav Zezula <ladislav.zezula@avg.com>2013-12-19 11:23:48 +0100
committerLadislav Zezula <ladislav.zezula@avg.com>2013-12-19 11:23:48 +0100
commitebd502e0c220f7c2d3b9ce65bd83ed569d65f314 (patch)
treecb3c0d82de0432448db4a09e9e0ef6d4e71beacb /test
parent55f159cf69d8b4816876653d25239abf6c3ef5a4 (diff)
+ Bitmap support was moved from archive functions to FileStream functions, where are more appropriate
+ Added support for master-mirror streams (like Blizzard games do) + SFileGetArchiveBitmap was moved into SFileGetFileInfo + Fixed bug in SFileCompactArchive + Removed classes SFileMpqBitmapXXX from SFileGetFileInfo
Diffstat (limited to 'test')
-rw-r--r--test/Test.cpp267
1 files changed, 247 insertions, 20 deletions
diff --git a/test/Test.cpp b/test/Test.cpp
index a9cc5e6..e3e78f0 100644
--- a/test/Test.cpp
+++ b/test/Test.cpp
@@ -196,13 +196,27 @@ static bool IsMpqExtension(const char * szFileName)
return true;
if(!_stricmp(szExtension, ".SC2Map"))
return true;
- if(!_stricmp(szExtension, ".link"))
- return true;
+// if(!_stricmp(szExtension, ".link"))
+// return true;
}
return false;
}
+static bool CompareBlocks(LPBYTE pbBlock1, LPBYTE pbBlock2, DWORD dwLength, DWORD * pdwDifference)
+{
+ for(DWORD i = 0; i < dwLength; i++)
+ {
+ if(pbBlock1[i] != pbBlock2[i])
+ {
+ pdwDifference[0] = i;
+ return false;
+ }
+ }
+
+ return true;
+}
+
static size_t ConvertSha1ToText(const unsigned char * sha1_digest, char * szSha1Text)
{
const char * szTable = "0123456789abcdef";
@@ -609,11 +623,6 @@ static int InitializeMpqDirectory(char * argv[], int argc)
const char * szDirName;
char szFullPath[MAX_PATH];
-#ifdef _MSC_VER
- // Mix the random number generator
- srand(GetTickCount());
-#endif
-
// Retrieve the name of the MPQ directory
if(argc > 1 && argv[1] != NULL)
{
@@ -847,9 +856,6 @@ static int CopyFileData(
{
while(ByteOffset < EndOffset)
{
- // Notify the user
- pLogger->PrintProgress("Copying %I64u of %I64u ...", BytesCopied, ByteCount);
-
// Read source
BytesToRead = ((EndOffset - ByteOffset) > BlockLength) ? BlockLength : (DWORD)(EndOffset - ByteOffset);
if(!FileStream_Read(pStream1, &ByteOffset, pbCopyBuffer, BytesToRead))
@@ -865,8 +871,12 @@ static int CopyFileData(
break;
}
+ // Increment the byte counts
BytesCopied += BytesToRead;
ByteOffset += BytesToRead;
+
+ // Notify the user
+ pLogger->PrintProgress("Copying (%I64u of %I64u complete) ...", BytesCopied, ByteCount);
}
STORM_FREE(pbCopyBuffer);
@@ -876,7 +886,7 @@ static int CopyFileData(
}
// Support function for copying file
-static int CreateMpqCopy(
+static int CreateFileCopy(
TLogHelper * pLogger,
const char * szPlainName,
const char * szFileCopy,
@@ -953,6 +963,30 @@ static int CreateMpqCopy(
return nError;
}
+static int CreateMasterAndMirrorPaths(
+ TLogHelper * pLogger,
+ char * szMirrorPath,
+ char * szMasterPath,
+ const char * szMirrorName,
+ const char * szMasterName)
+{
+ char szCopyPath[MAX_PATH];
+ int nError;
+
+ // Copy the mirrored file from the source to the work directory
+ nError = CreateFileCopy(pLogger, szMirrorName, szMirrorName, szCopyPath);
+ if(nError == ERROR_SUCCESS)
+ {
+ // Create the full path name of the master file
+ CreateFullPathName(szMasterPath, szMpqSubDir, szMasterName);
+
+ // Create the full path name of the mirror file
+ sprintf(szMirrorPath, "%s*%s", szCopyPath, szMasterPath);
+ }
+
+ return nError;
+}
+
static void WINAPI AddFileCallback(void * pvUserData, DWORD dwBytesWritten, DWORD dwTotalBytes, bool bFinalCall)
{
TLogHelper * pLogger = (TLogHelper *)pvUserData;
@@ -1086,6 +1120,81 @@ static TFileData * LoadLocalFile(TLogHelper * pLogger, const char * szFileName,
return pFileData;
}
+static int CompareTwoLocalFilesRR(
+ TLogHelper * pLogger,
+ TFileStream * pStream1, // Master file
+ TFileStream * pStream2) // Mirror file
+{
+ ULONGLONG RandomNumber = 0x12345678; // We need pseudo-random number that will repeat each run of the program
+ ULONGLONG RandomSeed;
+ ULONGLONG ByteOffset;
+ ULONGLONG FileSize1 = 1;
+ ULONGLONG FileSize2 = 2;
+ DWORD BytesToRead;
+ DWORD Difference;
+ LPBYTE pbBuffer1;
+ LPBYTE pbBuffer2;
+ DWORD cbBuffer = 0x100000;
+ int nError = ERROR_SUCCESS;
+
+ // Compare file sizes
+ FileStream_GetSize(pStream1, &FileSize1);
+ FileStream_GetSize(pStream2, &FileSize2);
+ if(FileSize1 != FileSize2)
+ {
+ pLogger->PrintMessage("The files have different size");
+ return ERROR_CAN_NOT_COMPLETE;
+ }
+
+ // Allocate both buffers
+ pbBuffer1 = STORM_ALLOC(BYTE, cbBuffer);
+ pbBuffer2 = STORM_ALLOC(BYTE, cbBuffer);
+ if(pbBuffer1 && pbBuffer2)
+ {
+ // Perform many random reads
+ for(int i = 0; i < 0x10000; i++)
+ {
+ // Generate psudo-random offsrt and data size
+ ByteOffset = (RandomNumber % FileSize1);
+ BytesToRead = (DWORD)(RandomNumber % cbBuffer);
+
+ // Show the progress message
+ pLogger->PrintProgress("Comparing file: Offset: " I64u_a ", Length: %u", ByteOffset, BytesToRead);
+
+ // Only perform read if the byte offset is below
+ if(ByteOffset < FileSize1)
+ {
+ if((ByteOffset + BytesToRead) > FileSize1)
+ BytesToRead = (DWORD)(FileSize1 - ByteOffset);
+
+ memset(pbBuffer1, 0xEE, cbBuffer);
+ memset(pbBuffer2, 0xAA, cbBuffer);
+
+ FileStream_Read(pStream1, &ByteOffset, pbBuffer1, BytesToRead);
+ FileStream_Read(pStream2, &ByteOffset, pbBuffer2, BytesToRead);
+
+ if(!CompareBlocks(pbBuffer1, pbBuffer2, BytesToRead, &Difference))
+ {
+ pLogger->PrintMessage("Difference at %u (Offset " I64u_a ", Length %u)", Difference, ByteOffset, BytesToRead);
+ nError = ERROR_FILE_CORRUPT;
+ break;
+ }
+
+ // Shuffle the random number
+ memcpy(&RandomSeed, pbBuffer1, sizeof(RandomSeed));
+ RandomNumber = ((RandomNumber >> 0x11) | (RandomNumber << 0x29)) ^ (RandomNumber + RandomSeed);
+ }
+ }
+ }
+
+ // Free both buffers
+ if(pbBuffer2 != NULL)
+ STORM_FREE(pbBuffer2);
+ if(pbBuffer1 != NULL)
+ STORM_FREE(pbBuffer1);
+ return nError;
+}
+
static TFileData * LoadMpqFile(TLogHelper * pLogger, HANDLE hMpq, const char * szFileName)
{
TFileData * pFileData = NULL;
@@ -1390,7 +1499,7 @@ static int OpenExistingArchiveWithCopy(TLogHelper * pLogger, const char * szFile
// If both names entered, create a copy
if(szFileName != NULL && szCopyName != NULL)
{
- nError = CreateMpqCopy(pLogger, szFileName, szCopyName, szFullPath);
+ nError = CreateFileCopy(pLogger, szFileName, szCopyName, szFullPath);
if(nError != ERROR_SUCCESS)
return nError;
}
@@ -1654,8 +1763,37 @@ static int TestSearchListFile(const char * szPlainName)
return ERROR_SUCCESS;
}
+// Open a file stream with mirroring a master file
+static int TestReadFile_MasterMirror(const char * szMirrorName, const char * szMasterName)
+{
+ TFileStream * pStream1; // Master file
+ TFileStream * pStream2; // Mirror file
+ TLogHelper Logger("OpenMirrorFile", szMasterName);
+ char szMirrorPath[MAX_PATH + MAX_PATH];
+ char szMasterPath[MAX_PATH];
+ int nError;
+
+ // Create copy of the file to serve as mirror, keep master there
+ nError = CreateMasterAndMirrorPaths(&Logger, szMirrorPath, szMasterPath, szMirrorName, szMasterName);
+ if(nError == ERROR_SUCCESS)
+ {
+ // Open both master and mirror file
+ pStream1 = OpenLocalFile(szMasterPath, STREAM_FLAG_READ_ONLY | STREAM_FLAG_USE_BITMAP);
+ pStream2 = OpenLocalFile(szMirrorPath, STREAM_FLAG_READ_ONLY | STREAM_FLAG_USE_BITMAP);
+ if(pStream1 && pStream2)
+ nError = CompareTwoLocalFilesRR(&Logger, pStream1, pStream2);
+
+ if(pStream2 != NULL)
+ FileStream_Close(pStream2);
+ if(pStream1 != NULL)
+ FileStream_Close(pStream1);
+ }
+
+ return nError;
+}
+
//
-static int TestPartFileRead(const char * szPlainName)
+static int TestReadFile_Partial(const char * szPlainName)
{
TLogHelper Logger("PartFileRead", szPlainName);
TMPQHeader Header;
@@ -1810,6 +1948,27 @@ static int TestOpenArchive(const char * szPlainName, const char * szListFile = N
return nError;
}
+static int TestOpenArchive_Corrupt(const char * szPlainName)
+{
+ TLogHelper Logger("OpenCorruptMpqTest", szPlainName);
+ HANDLE hMpq = NULL;
+ TCHAR szFullPathT[MAX_PATH];
+ char szFullPath[MAX_PATH];
+
+ // Copy the archive so we won't fuck up the original one
+ CreateFullPathName(szFullPath, szMpqSubDir, szPlainName);
+ CopyFileName(szFullPathT, szFullPath, strlen(szFullPath));
+ if(SFileOpenArchive(szFullPathT, 0, STREAM_FLAG_READ_ONLY, &hMpq))
+ {
+ SFileCloseArchive(hMpq);
+ Logger.PrintMessage("Opening archive %s succeeded, but it shouldn't", szFullPath);
+ return ERROR_CAN_NOT_COMPLETE;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+
// Opens a patched MPQ archive
static int TestOpenArchive_Patched(const char * PatchList[], const char * szPatchedFile = NULL, int nExpectedPatchCount = 0)
{
@@ -1850,7 +2009,7 @@ static int TestOpenArchive_ReadOnly(const char * szPlainName, bool bReadOnly)
// Copy the fiel so we wont screw up something
szCopyName = bReadOnly ? "StormLibTest_ReadOnly.mpq" : "StormLibTest_ReadWrite.mpq";
- nError = CreateMpqCopy(&Logger, szPlainName, szCopyName, szFullPathName);
+ nError = CreateFileCopy(&Logger, szPlainName, szCopyName, szFullPathName);
// Now open the archive for read-only access
if(nError == ERROR_SUCCESS)
@@ -1966,6 +2125,62 @@ static int TestOpenArchive_GetFileInfo(const char * szPlainName1, const char * s
return ERROR_SUCCESS;
}
+static int TestOpenArchive_MasterMirror(const char * szMirrorName, const char * szMasterName, const char * szFileToExtract)
+{
+ TFileData * pFileData;
+ TLogHelper Logger("OpenServerMirror", szMirrorName);
+ HANDLE hFile = NULL;
+ HANDLE hMpq = NULL;
+ DWORD dwVerifyResult;
+ char szMirrorPath[MAX_PATH + MAX_PATH]; // Combined name
+ char szMasterPath[MAX_PATH]; // Original (server) name
+ int nError;
+
+ // Create both paths
+ nError = CreateMasterAndMirrorPaths(&Logger, szMirrorPath, szMasterPath, szMirrorName, szMasterName);
+
+ // Now open both archives as local-server pair
+ if(nError == ERROR_SUCCESS)
+ {
+ nError = OpenExistingArchive(&Logger, szMirrorPath, 0, &hMpq);
+ }
+
+ // The MPQ must be read-only. Writing to mirrored MPQ is not allowed
+ if(nError == ERROR_SUCCESS)
+ {
+ if(SFileCreateFile(hMpq, "AddedFile.bin", 0, 0x10, 0, MPQ_FILE_COMPRESS, &hFile))
+ {
+ SFileCloseFile(hFile);
+ Logger.PrintMessage("The archive is writable, although it should not be");
+ nError = ERROR_FILE_CORRUPT;
+ }
+ }
+
+ // Verify the file
+ if(nError == ERROR_SUCCESS && szFileToExtract)
+ {
+ dwVerifyResult = SFileVerifyFile(hMpq, szFileToExtract, SFILE_VERIFY_ALL);
+ if(dwVerifyResult & VERIFY_FILE_ERROR_MASK)
+ {
+ Logger.PrintMessage("File verification failed");
+ nError = ERROR_FILE_CORRUPT;
+ }
+ }
+
+ // Load the file to memory
+ if(nError == ERROR_SUCCESS && szFileToExtract)
+ {
+ pFileData = LoadMpqFile(&Logger, hMpq, szFileToExtract);
+ if(pFileData != NULL)
+ STORM_FREE(pFileData);
+ }
+
+ if(hMpq != NULL)
+ SFileCloseArchive(hMpq);
+ return nError;
+}
+
+
static int TestOpenArchive_VerifySignature(const char * szPlainName, const char * szOriginalName)
{
TLogHelper Logger("VerifySignatureTest", szPlainName);
@@ -2018,7 +2233,7 @@ static int TestOpenArchive_CraftedUserData(const char * szPlainName, const char
int nError;
// Create copy of the archive, with interleaving some user data
- nError = CreateMpqCopy(&Logger, szPlainName, szCopyName, szFullPath, 0x400, 0x531);
+ nError = CreateFileCopy(&Logger, szPlainName, szCopyName, szFullPath, 0x400, 0x531);
// Open the archive and load some files
if(nError == ERROR_SUCCESS)
@@ -2892,18 +3107,22 @@ int main(int argc, char * argv[])
// Search all testing archives and verify their SHA1 hash
if(nError == ERROR_SUCCESS)
nError = FindFiles(ForEachFile_VerifyFileChecksum, szMpqDirectory);
-
- // Test opening local file with SFileOpenFileEx
- if(nError == ERROR_SUCCESS)
- nError = TestOpenLocalFile("ListFile_Blizzard.txt");
// Search in listfile
if(nError == ERROR_SUCCESS)
nError = TestSearchListFile("ListFile_Blizzard.txt");
+ // Search in listfile
+ if(nError == ERROR_SUCCESS)
+ nError = TestReadFile_MasterMirror("MPQ_2013_v4_alternate-downloaded.MPQ", "MPQ_2013_v4_alternate-original.MPQ");
+
// Test reading partial file
if(nError == ERROR_SUCCESS)
- nError = TestPartFileRead("MPQ_2009_v2_WoW_patch.MPQ.part");
+ nError = TestReadFile_Partial("MPQ_2009_v2_WoW_patch.MPQ.part");
+
+ // Test opening local file with SFileOpenFileEx
+ if(nError == ERROR_SUCCESS)
+ nError = TestOpenLocalFile("ListFile_Blizzard.txt");
// Test working with an archive that has no listfile
if(nError == ERROR_SUCCESS)
@@ -2969,6 +3188,10 @@ int main(int argc, char * argv[])
if(nError == ERROR_SUCCESS)
nError = FindFiles(ForEachFile_OpenArchive, szMpqDirectory);
+ // Test on an archive that has been invalidated by extending an old valid MPQ
+ if(nError == ERROR_SUCCESS)
+ nError = TestOpenArchive_Corrupt("MPQ_2013_vX_Battle.net.MPQ");
+
// Open a patched archive
if(nError == ERROR_SUCCESS)
nError = TestOpenArchive_Patched(PatchList_WoW_OldWorld13286, "OldWorld\\World\\Model.blob", 2);
@@ -2993,6 +3216,10 @@ int main(int argc, char * argv[])
if(nError == ERROR_SUCCESS)
nError = TestOpenArchive_GetFileInfo("MPQ_2002_v1_StrongSignature.w3m", "MPQ_2013_v4_SC2_EmptyMap.SC2Map");
+ // Downloadable MPQ archive
+ if(nError == ERROR_SUCCESS)
+ nError = TestOpenArchive_MasterMirror("MPQ_2013_v4_alternate-downloaded.MPQ", "MPQ_2013_v4_alternate-original.MPQ", "alternate\\DUNGEONS\\TEXTURES\\ICECROWN\\GATE\\jlo_IceC_Floor_Thrown.blp");
+
// Check archive signature
if(nError == ERROR_SUCCESS)
nError = TestOpenArchive_VerifySignature("MPQ_1999_v1_WeakSignature.exe", "War2Patch_202.exe");