From 999ee276cfd7420abaf7566932b591677aff184f Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Thu, 2 Feb 2023 00:22:24 +0100 Subject: Fixed bugs --- src/SBaseFileTable.cpp | 2 +- src/SFileFindFile.cpp | 7 +- src/SFileGetFileInfo.cpp | 42 +- src/SFileListFile.cpp | 2 +- src/SFileVerify.cpp | 17 +- src/StormCommon.h | 3 + test/StormTest.cpp | 952 ++++++++++++++++++++++++--------------------- test/stormlib-test-001.txt | 46 ++- 8 files changed, 583 insertions(+), 488 deletions(-) diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index 2fd717b..43202d0 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -683,7 +683,7 @@ DWORD ConvertMpqHeaderToFormat4( return ERROR_FAKE_MPQ_HEADER; // Check for malformed MPQs - if(pHeader->wFormatVersion != MPQ_FORMAT_VERSION_4 || (ByteOffset + pHeader->ArchiveSize64) != FileSize || (ByteOffset + pHeader->HiBlockTablePos64) >= FileSize) + if(pHeader->wFormatVersion != MPQ_FORMAT_VERSION_4 || (ByteOffset + pHeader->ArchiveSize64) > FileSize || (ByteOffset + pHeader->HiBlockTablePos64) >= FileSize) { pHeader->wFormatVersion = MPQ_FORMAT_VERSION_4; pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V4; diff --git a/src/SFileFindFile.cpp b/src/SFileFindFile.cpp index 4de1022..b741e5f 100644 --- a/src/SFileFindFile.cpp +++ b/src/SFileFindFile.cpp @@ -213,7 +213,7 @@ static bool DoMPQSearch_FileEntry( TFileEntry * pPatchEntry; HANDLE hFile = NULL; const char * szFileName; - size_t nPrefixLength = (ha->pPatchPrefix != NULL) ? ha->pPatchPrefix->nLength : 0; + size_t nGlobalPrefixLength = (ha->pPatchPrefix != NULL) ? ha->pPatchPrefix->nLength : 0; DWORD dwBlockIndex; char szNameBuff[MAX_PATH]; @@ -227,6 +227,8 @@ static bool DoMPQSearch_FileEntry( // Now we have to check if this file was not enumerated before if(!FileWasFoundBefore(ha, hs, pFileEntry)) { + size_t nPrefixLength = nGlobalPrefixLength; + // if(pFileEntry != NULL && !_stricmp(pFileEntry->szFileName, "TriggerLibs\\NativeLib.galaxy")) // DebugBreak(); @@ -246,8 +248,9 @@ static bool DoMPQSearch_FileEntry( if(SFileOpenFileEx((HANDLE)hs->ha, szNameBuff, SFILE_OPEN_BASE_FILE, &hFile)) { SFileGetFileName(hFile, szNameBuff); - szFileName = szNameBuff; SFileCloseFile(hFile); + szFileName = szNameBuff; + nPrefixLength = 0; } } diff --git a/src/SFileGetFileInfo.cpp b/src/SFileGetFileInfo.cpp index 77c86fd..f9b9a76 100644 --- a/src/SFileGetFileInfo.cpp +++ b/src/SFileGetFileInfo.cpp @@ -42,7 +42,7 @@ static DWORD GetMpqFileCount(TMPQArchive * ha) return dwFileCount; } -static bool GetInfo_ReturdwErrCode(DWORD dwErrCode) +static bool GetInfo_ReturnError(DWORD dwErrCode) { SetLastError(dwErrCode); return false; @@ -56,11 +56,11 @@ static bool GetInfo_BufferCheck(void * pvFileInfo, DWORD cbFileInfo, DWORD cbDat // Check for sufficient buffer if(cbData > cbFileInfo) - return GetInfo_ReturdwErrCode(ERROR_INSUFFICIENT_BUFFER); + return GetInfo_ReturnError(ERROR_INSUFFICIENT_BUFFER); // If the buffer size is sufficient, check for valid user buffer if(pvFileInfo == NULL) - return GetInfo_ReturdwErrCode(ERROR_INVALID_PARAMETER); + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); // Buffers and sizes are OK, we are ready to proceed file copying return true; @@ -150,7 +150,7 @@ static bool GetInfo_PatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInf // Patch chain is only supported on MPQ files. Local files are not supported. if(hf->pStream != NULL) - return GetInfo_ReturdwErrCode(ERROR_INVALID_PARAMETER); + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); // Calculate the necessary length of the multi-string for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) @@ -209,13 +209,13 @@ bool WINAPI SFileGetFileInfo( if((int)InfoClass <= (int)SFileMpqFlags) { if((ha = IsValidMpqHandle(hMpqOrFile)) == NULL) - return GetInfo_ReturdwErrCode(ERROR_INVALID_HANDLE); + return GetInfo_ReturnError(ERROR_INVALID_HANDLE); pHeader = ha->pHeader; } else { if((hf = IsValidFileHandle(hMpqOrFile)) == NULL) - return GetInfo_ReturdwErrCode(ERROR_INVALID_HANDLE); + return GetInfo_ReturnError(ERROR_INVALID_HANDLE); pFileEntry = hf->pFileEntry; } @@ -235,12 +235,12 @@ bool WINAPI SFileGetFileInfo( case SFileMpqUserDataHeader: if(ha->pUserData == NULL) - return GetInfo_ReturdwErrCode(ERROR_INVALID_PARAMETER); + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); return GetInfo_ReadFromFile(pvFileInfo, cbFileInfo, ha->pStream, ha->UserDataPos, sizeof(TMPQUserData), pcbLengthNeeded); case SFileMpqUserData: if(ha->pUserData == NULL) - return GetInfo_ReturdwErrCode(ERROR_INVALID_PARAMETER); + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); return GetInfo_ReadFromFile(pvFileInfo, cbFileInfo, ha->pStream, ha->UserDataPos + sizeof(TMPQUserData), ha->pUserData->dwHeaderOffs - sizeof(TMPQUserData), pcbLengthNeeded); case SFileMpqHeaderOffset: @@ -261,12 +261,12 @@ bool WINAPI SFileGetFileInfo( case SFileMpqHetHeader: pvSrcFileInfo = LoadExtTable(ha, pHeader->HetTablePos64, (size_t)pHeader->HetTableSize64, HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE); if(pvSrcFileInfo == NULL) - return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); return GetInfo_Allocated(pvFileInfo, cbFileInfo, pvSrcFileInfo, sizeof(TMPQHetHeader), pcbLengthNeeded); case SFileMpqHetTable: if((pvSrcFileInfo = LoadHetTable(ha)) == NULL) - return GetInfo_ReturdwErrCode(ERROR_NOT_ENOUGH_MEMORY); + return GetInfo_ReturnError(ERROR_NOT_ENOUGH_MEMORY); return GetInfo_TablePointer(pvFileInfo, cbFileInfo, pvSrcFileInfo, InfoClass, pcbLengthNeeded); case SFileMpqBetTableOffset: @@ -280,7 +280,7 @@ bool WINAPI SFileGetFileInfo( // Retrieve the table and its size pvSrcFileInfo = LoadExtTable(ha, pHeader->BetTablePos64, (size_t)pHeader->BetTableSize64, BET_TABLE_SIGNATURE, MPQ_KEY_BLOCK_TABLE); if(pvSrcFileInfo == NULL) - return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); cbSrcFileInfo = sizeof(TMPQBetHeader) + ((TMPQBetHeader *)pvSrcFileInfo)->dwFlagCount * sizeof(DWORD); // It is allowed for the caller to only require BET header @@ -290,7 +290,7 @@ bool WINAPI SFileGetFileInfo( case SFileMpqBetTable: if((pvSrcFileInfo = LoadBetTable(ha)) == NULL) - return GetInfo_ReturdwErrCode(ERROR_NOT_ENOUGH_MEMORY); + return GetInfo_ReturnError(ERROR_NOT_ENOUGH_MEMORY); return GetInfo_TablePointer(pvFileInfo, cbFileInfo, pvSrcFileInfo, InfoClass, pcbLengthNeeded); case SFileMpqHashTableOffset: @@ -320,7 +320,7 @@ bool WINAPI SFileGetFileInfo( case SFileMpqBlockTable: ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos)); if(ByteOffset >= ha->FileSize) - return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); cbSrcFileInfo = pHeader->dwBlockTableSize * sizeof(TMPQBlock); pvSrcFileInfo = LoadBlockTable(ha, true); return GetInfo_Allocated(pvFileInfo, cbFileInfo, pvSrcFileInfo, cbSrcFileInfo, pcbLengthNeeded); @@ -332,27 +332,27 @@ bool WINAPI SFileGetFileInfo( return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HiBlockTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHiBlockTable: - return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); case SFileMpqSignatures: if(!QueryMpqSignatureInfo(ha, &SignatureInfo)) - return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); return GetInfo(pvFileInfo, cbFileInfo, &SignatureInfo.SignatureTypes, sizeof(DWORD), pcbLengthNeeded); case SFileMpqStrongSignatureOffset: if(QueryMpqSignatureInfo(ha, &SignatureInfo) == false || (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) == 0) - return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); return GetInfo(pvFileInfo, cbFileInfo, &SignatureInfo.EndMpqData, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqStrongSignatureSize: if(QueryMpqSignatureInfo(ha, &SignatureInfo) == false || (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) == 0) - return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); dwInt32Value = MPQ_STRONG_SIGNATURE_SIZE + 4; return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); case SFileMpqStrongSignature: if(QueryMpqSignatureInfo(ha, &SignatureInfo) == false || (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) == 0) - return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); return GetInfo(pvFileInfo, cbFileInfo, SignatureInfo.Signature, MPQ_STRONG_SIGNATURE_SIZE + 4, pcbLengthNeeded); case SFileMpqArchiveSize64: @@ -376,7 +376,7 @@ bool WINAPI SFileGetFileInfo( case SFileMpqRawChunkSize: if(pHeader->dwRawChunkSize == 0) - return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwRawChunkSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqStreamFlags: @@ -391,7 +391,7 @@ bool WINAPI SFileGetFileInfo( case SFileInfoFileEntry: if(pFileEntry == NULL) - return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); return GetInfo_FileEntry(pvFileInfo, cbFileInfo, pFileEntry, pcbLengthNeeded); case SFileInfoHashEntry: @@ -445,7 +445,7 @@ bool WINAPI SFileGetFileInfo( return GetInfo(pvFileInfo, cbFileInfo, &hf->pFileEntry->dwCrc32, sizeof(DWORD), pcbLengthNeeded); default: // Invalid info class - return GetInfo_ReturdwErrCode(ERROR_INVALID_PARAMETER); + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); } } diff --git a/src/SFileListFile.cpp b/src/SFileListFile.cpp index 6adf1ab..2a3f02a 100644 --- a/src/SFileListFile.cpp +++ b/src/SFileListFile.cpp @@ -573,7 +573,7 @@ static DWORD SFileAddInternalListFile( if(ha->pHashTable != NULL) { // If the archive is a malformed map, ignore too large listfiles - if(ha->dwFlags & MPQ_FLAG_MALFORMED) + if(STORMLIB_TEST_FLAGS(ha->dwFlags, MPQ_FLAG_MALFORMED | MPQ_FLAG_PATCH, MPQ_FLAG_MALFORMED)) dwMaxSize = 0x40000; pFirstHash = pHash = GetFirstHashEntry(ha, LISTFILE_NAME); diff --git a/src/SFileVerify.cpp b/src/SFileVerify.cpp index 4c8c181..3748918 100644 --- a/src/SFileVerify.cpp +++ b/src/SFileVerify.cpp @@ -801,11 +801,11 @@ DWORD SSignFileCreate(TMPQArchive * ha) // Create the (signature) file file in the MPQ // Note that the file must not be compressed or encrypted dwErrCode = SFileAddFile_Init(ha, SIGNATURE_NAME, - 0, - sizeof(EmptySignature), - LANG_NEUTRAL, - ha->dwFileFlags3 | MPQ_FILE_REPLACEEXISTING, - &hf); + 0, + sizeof(EmptySignature), + LANG_NEUTRAL, + ha->dwFileFlags3 | MPQ_FILE_REPLACEEXISTING, + &hf); // Write the empty signature file to the archive if(dwErrCode == ERROR_SUCCESS) @@ -813,6 +813,13 @@ DWORD SSignFileCreate(TMPQArchive * ha) // Write the empty zeroed file to the MPQ memset(EmptySignature, 0, sizeof(EmptySignature)); dwErrCode = SFileAddFile_Write(hf, EmptySignature, (DWORD)sizeof(EmptySignature), 0); + } + + // Finalize the signature + if(dwErrCode == ERROR_SUCCESS) + { + // Clear the CRC as it will not be valid + hf->pFileEntry->dwCrc32 = hf->dwCrc32 = 0; SFileAddFile_Finish(hf); // Clear the invalid mark diff --git a/src/StormCommon.h b/src/StormCommon.h index 39f7524..74b687e 100644 --- a/src/StormCommon.h +++ b/src/StormCommon.h @@ -70,6 +70,9 @@ // Checks for data pointers aligned to 4-byte boundary #define STORMLIB_DWORD_ALIGNED(ptr) (((size_t)(ptr) & 0x03) == 0) +// Check for masked flags +#define STORMLIB_TEST_FLAGS(dwFlags, dwMask, dwValue) ((dwFlags & (dwMask)) == (dwValue)) + // Macro for building 64-bit file offset from two 32-bit #define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | (ULONGLONG)lo) diff --git a/test/StormTest.cpp b/test/StormTest.cpp index 46f8637..ca409f1 100644 --- a/test/StormTest.cpp +++ b/test/StormTest.cpp @@ -36,16 +36,18 @@ //------------------------------------------------------------------------------ // Local structures -#define TFLG_WILL_FAIL 0x04000000 // The process is expected to fail -#define TFLG_VALUE_MASK 0x00FFFFFF // Mask for integer value - +// Artificial error code for situations where we don't know the result #define ERROR_UNDETERMINED_RESULT 0xC000FFFF +// Artificial flag for not reporting open failure +#define MPQ_OPEN_DONT_REPORT_FAILURE 0x80000000 + typedef DWORD(*FIND_FILE_CALLBACK)(LPCTSTR szFullPath); typedef enum _EXTRA_TYPE { NoExtraType = 0, + ListFile, TwoFiles, PatchList, } EXTRA_TYPE, *PEXTRA_TYPE; @@ -59,6 +61,12 @@ typedef struct _FILE_DATA BYTE FileData[1]; } FILE_DATA, *PFILE_DATA; +typedef struct _TEST_EXTRA_ONEFILE +{ + EXTRA_TYPE Type; // Must be ListFile + LPCTSTR szFile; // The name of the (list)file +} TEST_EXTRA_ONEFILE, *PTEST_EXTRA_ONEFILE; + typedef struct _TEST_EXTRA_TWOFILES { EXTRA_TYPE Type; // Must be TwoFiles @@ -75,22 +83,13 @@ typedef struct _TEST_EXTRA_PATCHES } TEST_EXTRA_PATCHES, *PTEST_EXTRA_PATCHES; typedef struct _TEST_INFO -{ - LPCTSTR szMpqName1; - LPCTSTR szMpqName2; - DWORD dwFlags; - const void * param1; - const void * param2; -} TEST_INFO, *PTEST_INFO; - -typedef struct _TEST_INFO2 { LPCTSTR szName1; // MPQ name LPCTSTR szName2; // ListFile name or NULL if none LPCSTR szDataHash; // Compound name+data hash DWORD dwFlags; // Flags for testing the file. Low 16 bits contains number of files const void * pExtra; -} TEST_INFO2, *PTEST_INFO2; +} TEST_INFO, *PTEST_INFO; typedef struct _LINE_INFO { @@ -122,6 +121,15 @@ static const TCHAR szListFileDir[] = { '1', '9', '9', '5', ' ', '-', ' ', 'T', ' static const TCHAR szListFileDir[] = { '1', '9', '9', '5', ' ', '-', ' ', 'T', 'e', 's', 't', ' ', 'M', 'P', 'Q', 's', '\\', 'l', 'i', 's', 't', 'f', 'i', 'l', 'e', 's', '-', (TCHAR)0xe6, (TCHAR)0x96, (TCHAR)0xB0, (TCHAR)0xE5, (TCHAR)0xBB, (TCHAR)0xBA, (TCHAR)0xE6, (TCHAR)0x96, (TCHAR)0x87, (TCHAR)0xE4, (TCHAR)0xBB, (TCHAR)0xB6, (TCHAR)0xE5, (TCHAR)0xA4, (TCHAR)0xB9, 0 }; #endif +// Definition of the path separator +#ifdef STORMLIB_WINDOWS +static LPCTSTR g_szPathSeparator = _T("\\"); +static const TCHAR PATH_SEPARATOR = _T('\\'); // Path separator for Windows platforms +#else +static LPCSTR g_szPathSeparator = "/"; +static const TCHAR PATH_SEPARATOR = '/'; // Path separator for Non-Windows platforms +#endif + // Global for the work MPQ static LPCTSTR szMpqSubDir = _T("1995 - Test MPQs"); static LPCTSTR szMpqPatchDir = _T("1995 - Test MPQs\\patches"); @@ -130,10 +138,21 @@ static LPCSTR IntToHexChar = "0123456789abcdef"; //----------------------------------------------------------------------------- // Testing data -// Artificial flag for not reporting open failure -#define MPQ_OPEN_DONT_REPORT_FAILURE 0x80000000 - -// Flags for TestOpenArchive_SignatureTest() +// Flags for TestOpenArchive +#define TFLG_VALUE_MASK 0x000FFFFF // Mask for file count +#define TFLG_READ_ONLY 0x00100000 // Open the archive read-only +#define TFLG_SIGCHECK_BEFORE 0x00200000 // Verify signature before modification +#define TFLG_MODIFY 0x00400000 // Modify the archive +#define TFLG_SIGN_ARCHIVE 0x00800000 // Sign an archive that is not signed +#define TFLG_SIGCHECK_AFTER 0x01000000 // Verify signature after modification +#define TFLG_GET_FILE_INFO 0x02000000 // Test the GetFileInfo function +#define TFLG_ADD_USER_DATA 0x04000000 // Add user data during MPQ copying +#define TFLG_HAS_LISTFILE 0x08000000 // The MPQ must have (listfile) +#define TFLG_HAS_ATTRIBUTES 0x10000000 // The MPQ must have (attributes) +#define TFLG_COMPACT 0x20000000 // Perform archive compacting between two opens +#define TFLG_WILL_FAIL 0x80000000 // The process is expected to fail + +// Flags for TestOpenArchive_SignatureTest #define SFLAG_VERIFY_BEFORE 0x00000001 // Verify signature before modification #define SFLAG_CREATE_ARCHIVE 0x00000002 // Create new archive #define SFLAG_MODIFY_ARCHIVE 0x00000004 // Modify the archive before signing @@ -141,13 +160,7 @@ static LPCSTR IntToHexChar = "0123456789abcdef"; #define SFLAG_SIGN_ARCHIVE 0x00000010 // Sign the archive #define SFLAG_VERIFY_AFTER 0x00000020 // Verify the signature after modification -// Flags for TestOpenArchive_MiscTest() -#define MFLAG_WILL_FAIL 0x00000001 // The file will fail to open -#define MFLAG_OPEN_READ_ONLY 0x00000002 // Open the file as read only -#define MFLAG_OPEN_READ_WRITE 0x00000004 // Open the file as read-write -#define MFLAG_GET_FILE_INFO 0x00000008 // Get the file info - -// Flags for TestOpenArchive_AddFile() +// Flags for TestOpenArchive_AddFile #define TFLAG_REOPEN 0x00000001 // Reopen the archive #define TFLAG_HAS_LISTFILE 0x00000002 // The archive must have (listfile) after reopen #define TFLAG_HAS_ATTRIBUTES 0x00000004 // The archive must have (attributes) after reopen @@ -224,15 +237,6 @@ static SFILE_MARKERS MpqMarkers[] = //----------------------------------------------------------------------------- // Local file functions -// Definition of the path separator -#ifdef STORMLIB_WINDOWS -static LPCTSTR g_szPathSeparator = _T("\\"); -static const TCHAR PATH_SEPARATOR = _T('\\'); // Path separator for Windows platforms -#else -static LPCSTR g_szPathSeparator = "/"; -static const TCHAR PATH_SEPARATOR = '/'; // Path separator for Non-Windows platforms -#endif - // This must be the directory where our test MPQs are stored. // We also expect a subdirectory named static TCHAR szMpqDirectory[MAX_PATH+1]; @@ -1181,43 +1185,6 @@ static void WINAPI AddFileCallback(void * pvUserData, DWORD dwBytesWritten, DWOR dwTotalBytes); } -static void WINAPI CompactCallback(void * pvUserData, DWORD dwWork, ULONGLONG BytesDone, ULONGLONG TotalBytes) -{ - TLogHelper * pLogger = (TLogHelper *)pvUserData; - LPCSTR szWork = NULL; - - switch(dwWork) - { - case CCB_CHECKING_FILES: - szWork = "Checking files in archive"; - break; - - case CCB_CHECKING_HASH_TABLE: - szWork = "Checking hash table"; - break; - - case CCB_COPYING_NON_MPQ_DATA: - szWork = "Copying non-MPQ data"; - break; - - case CCB_COMPACTING_FILES: - szWork = "Compacting files"; - break; - - case CCB_CLOSING_ARCHIVE: - szWork = "Closing archive"; - break; - } - - if(szWork != NULL) - { - if(pLogger != NULL) - pLogger->PrintProgress("%s (%I64u of %I64u) ...", szWork, BytesDone, TotalBytes); - else - printf("%s (" fmt_I64u_a " of " fmt_I64u_a ") ... \r", szWork, BytesDone, TotalBytes); - } -} - //----------------------------------------------------------------------------- // MPQ file utilities @@ -1321,8 +1288,8 @@ static DWORD LoadMpqFile(TLogHelper & Logger, HANDLE hMpq, LPCSTR szFileName, LC Logger.PrintProgress("Loading file %s ...", GetShortPlainName(szFileName)); #if defined(_MSC_VER) && defined(_DEBUG) - // if(!_stricmp(szFileName, "File00000687.xxx")) - // __debugbreak(); + //if(!_stricmp(szFileName, "(signature)")) + // __debugbreak(); #endif // Make sure that we open the proper locale file @@ -1518,6 +1485,7 @@ static DWORD SearchArchive( SFILE_FIND_DATA sf; PFILE_DATA pFileData; HANDLE hFind; + FILE * fp = NULL; DWORD dwFileCount = 0; hash_state md5state; TCHAR szListFile[MAX_PATH] = _T(""); @@ -1527,6 +1495,7 @@ static DWORD SearchArchive( // Construct the full name of the listfile CreateFullPathName(szListFile, _countof(szListFile), szListFileDir, _T("ListFile_Blizzard.txt")); + //fp = fopen("E:\\mpq-listing.txt", "wt"); // Prepare hashing md5_init(&md5state); @@ -1568,14 +1537,18 @@ static DWORD SearchArchive( } // Debug: Show CRC32 of each file in order to debug differences - //pFileData->dwCrc32 = crc32(0, pFileData->FileData, pFileData->dwFileSize); - //printf("%08x: %s \n", pFileData->dwCrc32, sf.cFileName); + if(fp != NULL) + { + pFileData->dwCrc32 = crc32(0, pFileData->FileData, pFileData->dwFileSize); + fprintf(fp, "%08x: %s \n", pFileData->dwCrc32, sf.cFileName); + } // Free the loaded file data STORM_FREE(pFileData); } } + // The last file that was OK bFound = SFileFindNextFile(hFind, &sf); } SFileFindClose(hFind); @@ -1588,6 +1561,9 @@ static DWORD SearchArchive( if(pbFileHash != NULL && (dwSearchFlags & SEARCH_FLAG_HASH_FILES)) md5_done(&md5state, pbFileHash); + // Close the log file, if any + if(fp != NULL) + fclose(fp); return dwErrCode; } @@ -1599,7 +1575,7 @@ static DWORD VerifyDataChecksum(TLogHelper & Logger, HANDLE hMpq, DWORD dwSearch char szNameHash[0x40]; // Do nothing if no name hash and no known file count - if(IS_VALID_STRING(szNameHash) || (dwExpectedFileCount != 0)) + if(IS_VALID_STRING(szExpectedHash) || (dwExpectedFileCount != 0)) { // Search the archive, obtain file count and name hash if((dwErrCode = SearchArchive(&Logger, hMpq, dwSearchFlags, &dwFileCount, NameHash)) != ERROR_SUCCESS) @@ -1724,7 +1700,7 @@ static DWORD CreateNewArchiveU(TLogHelper * pLogger, const wchar_t * szPlainName return ERROR_SUCCESS; } -static DWORD OpenExistingArchive(TLogHelper * pLogger, LPCTSTR szFullPath, DWORD dwFlags, HANDLE * phMpq) +static DWORD OpenExistingArchive(TLogHelper * pLogger, LPCTSTR szFullPath, DWORD dwOpenFlags, HANDLE * phMpq) { HANDLE hMpq = NULL; size_t nMarkerIndex; @@ -1734,13 +1710,13 @@ static DWORD OpenExistingArchive(TLogHelper * pLogger, LPCTSTR szFullPath, DWORD if(_tcsnicmp(szFullPath, _T("flat-file://"), 11)) { if(_tcsstr(szFullPath, _T(".MPQE")) != NULL) - dwFlags |= STREAM_PROVIDER_MPQE; + dwOpenFlags |= STREAM_PROVIDER_MPQE; if(_tcsstr(szFullPath, _T(".MPQ.part")) != NULL) - dwFlags |= STREAM_PROVIDER_PARTIAL; + dwOpenFlags |= STREAM_PROVIDER_PARTIAL; if(_tcsstr(szFullPath, _T(".mpq.part")) != NULL) - dwFlags |= STREAM_PROVIDER_PARTIAL; + dwOpenFlags |= STREAM_PROVIDER_PARTIAL; if(_tcsstr(szFullPath, _T(".MPQ.0")) != NULL) - dwFlags |= STREAM_PROVIDER_BLOCK4; + dwOpenFlags |= STREAM_PROVIDER_BLOCK4; } // Handle ASI files properly @@ -1749,7 +1725,7 @@ static DWORD OpenExistingArchive(TLogHelper * pLogger, LPCTSTR szFullPath, DWORD // Open the copied archive pLogger->PrintProgress(_T("Opening archive %s ..."), GetShortPlainName(szFullPath)); - if(!SFileOpenArchive(szFullPath, 0, dwFlags, &hMpq)) + if(!SFileOpenArchive(szFullPath, 0, dwOpenFlags, &hMpq)) { switch(dwErrCode = GetLastError()) { @@ -1764,7 +1740,7 @@ static DWORD OpenExistingArchive(TLogHelper * pLogger, LPCTSTR szFullPath, DWORD } // Show the open error to the user - if((dwFlags & MPQ_OPEN_DONT_REPORT_FAILURE) == 0) + if((dwOpenFlags & MPQ_OPEN_DONT_REPORT_FAILURE) == 0) dwErrCode = pLogger->PrintError(_T("Failed to open archive %s"), szFullPath); return dwErrCode; } @@ -1777,7 +1753,7 @@ static DWORD OpenExistingArchive(TLogHelper * pLogger, LPCTSTR szFullPath, DWORD return dwErrCode; } -static DWORD OpenExistingArchiveWithCopy(TLogHelper * pLogger, LPCTSTR szFileName, LPCTSTR szCopyName, HANDLE * phMpq, DWORD dwFlags = 0) +static DWORD OpenExistingArchiveWithCopy(TLogHelper * pLogger, LPCTSTR szFileName, LPCTSTR szCopyName, HANDLE * phMpq, DWORD dwOpenFlags = 0) { DWORD dwErrCode = ERROR_SUCCESS; TCHAR szFullPath[MAX_PATH]; @@ -1800,7 +1776,7 @@ static DWORD OpenExistingArchiveWithCopy(TLogHelper * pLogger, LPCTSTR szFileNam else if(szFileName != NULL && szCopyName == NULL) { CreateFullPathName(szFullPath, _countof(szFullPath), szMpqSubDir, szFileName); - dwFlags |= MPQ_OPEN_READ_ONLY; + dwOpenFlags |= MPQ_OPEN_READ_ONLY; } // If only target name entered, open it directly @@ -1810,7 +1786,7 @@ static DWORD OpenExistingArchiveWithCopy(TLogHelper * pLogger, LPCTSTR szFileNam } // Open the archive - return OpenExistingArchive(pLogger, szFullPath, dwFlags, phMpq); + return OpenExistingArchive(pLogger, szFullPath, dwOpenFlags, phMpq); } static DWORD AddFileToMpq( @@ -1849,10 +1825,17 @@ static DWORD AddFileToMpq( } // Check the expected error code - if(dwExpectedError != ERROR_UNDETERMINED_RESULT && dwErrCode != dwExpectedError) + if(dwExpectedError != ERROR_UNDETERMINED_RESULT) { - pLogger->PrintError("Unexpected result from SFileCreateFile(%s)", szFileName); - dwErrCode = ERROR_CAN_NOT_COMPLETE; + if(dwErrCode != dwExpectedError) + { + pLogger->PrintError("Unexpected result from SFileCreateFile(%s)", szFileName); + dwErrCode = ERROR_CAN_NOT_COMPLETE; + } + else + { + dwErrCode = ERROR_SUCCESS; + } } return dwErrCode; } @@ -2280,22 +2263,62 @@ static DWORD TestArchive_LoadFiles(TLogHelper * pLogger, HANDLE hMpq, DWORD bIgn //----------------------------------------------------------------------------- // Testing archive operations: Single archive -static DWORD TestArchive_AddListFile(TLogHelper & Logger, HANDLE hMpq, LPCTSTR szListFile) +static DWORD TestOpenArchive_VerifySignature(TLogHelper & Logger, HANDLE hMpq, DWORD dwDoItIfNonZero) +{ + DWORD dwSignatures = 0; + DWORD dwVerifyError; + + // Only do it if we asked to + if(dwDoItIfNonZero) + { + // Query the signature types + Logger.PrintProgress("Retrieving signatures ..."); + TestGetFileInfo(&Logger, hMpq, SFileMpqSignatures, &dwSignatures, sizeof(DWORD), NULL, true, ERROR_SUCCESS); + + // Are there some signatures at all? + if(dwSignatures == 0) + { + Logger.PrintMessage("No signatures present in the file"); + return ERROR_FILE_CORRUPT; + } + + // Verify any of the present signatures + Logger.PrintProgress("Verifying archive signature ..."); + dwVerifyError = SFileVerifyArchive(hMpq); + + // Verify the result + if((dwSignatures & SIGNATURE_TYPE_STRONG) && (dwVerifyError != ERROR_STRONG_SIGNATURE_OK)) + { + Logger.PrintMessage("Strong signature verification error"); + return ERROR_FILE_CORRUPT; + } + + // Verify the result + if((dwSignatures & SIGNATURE_TYPE_WEAK) && (dwVerifyError != ERROR_WEAK_SIGNATURE_OK)) + { + Logger.PrintMessage("Weak signature verification error"); + return ERROR_FILE_CORRUPT; + } + } + return ERROR_SUCCESS; +} + +static DWORD TestOpenArchive_Extra_ListFile(TLogHelper & Logger, HANDLE hMpq, PTEST_EXTRA_ONEFILE pExtra) { DWORD dwErrCode = ERROR_SUCCESS; TCHAR szFullName[MAX_PATH]; - if(IS_VALID_STRING(szListFile)) + if(IS_VALID_STRING(pExtra->szFile)) { - Logger.PrintProgress(_T("Adding listfile %s ..."), szListFile); - CreateFullPathName(szFullName, _countof(szFullName), szListFileDir, szListFile); + Logger.PrintProgress(_T("Adding listfile %s ..."), pExtra->szFile); + CreateFullPathName(szFullName, _countof(szFullName), szListFileDir, pExtra->szFile); if((dwErrCode = SFileAddListFile(hMpq, szFullName)) != ERROR_SUCCESS) Logger.PrintMessage("Failed to add the listfile to the MPQ"); } return dwErrCode; } -static DWORD TestArchive_Extra_TwoFiles(TLogHelper & Logger, HANDLE hMpq, DWORD dwSearchFlags, PTEST_EXTRA_TWOFILES pExtra) +static DWORD TestOpenArchive_Extra_TwoFiles(TLogHelper & Logger, HANDLE hMpq, DWORD dwSearchFlags, PTEST_EXTRA_TWOFILES pExtra) { PFILE_DATA pFileData1 = NULL; PFILE_DATA pFileData2 = NULL; @@ -2334,7 +2357,7 @@ static DWORD TestArchive_Extra_TwoFiles(TLogHelper & Logger, HANDLE hMpq, DWORD return dwErrCode; } -static DWORD TestArchive_Extra_Patches(TLogHelper & Logger, HANDLE hMpq, PTEST_EXTRA_PATCHES pExtra) +static DWORD TestOpenArchive_Extra_Patches(TLogHelper & Logger, HANDLE hMpq, PTEST_EXTRA_PATCHES pExtra) { LPCTSTR szPatch; TCHAR szFullPath[MAX_PATH]; @@ -2358,37 +2381,133 @@ static DWORD TestArchive_Extra_Patches(TLogHelper & Logger, HANDLE hMpq, PTEST_E return dwErrCode; } -static DWORD TestArchive_ExtraType(TLogHelper & Logger, HANDLE hMpq, DWORD dwSearchFlags, const void * pExtra) +static DWORD TestOpenArchive_ExtraType(TLogHelper & Logger, HANDLE hMpq, DWORD dwSearchFlags, const void * pExtra) { switch(GetExtraType(pExtra)) { + case ListFile: + return TestOpenArchive_Extra_ListFile(Logger, hMpq, (PTEST_EXTRA_ONEFILE)(pExtra)); + case TwoFiles: - return TestArchive_Extra_TwoFiles(Logger, hMpq, dwSearchFlags, (PTEST_EXTRA_TWOFILES)(pExtra)); + return TestOpenArchive_Extra_TwoFiles(Logger, hMpq, dwSearchFlags, (PTEST_EXTRA_TWOFILES)(pExtra)); case PatchList: - return TestArchive_Extra_Patches(Logger, hMpq, (PTEST_EXTRA_PATCHES)(pExtra)); + return TestOpenArchive_Extra_Patches(Logger, hMpq, (PTEST_EXTRA_PATCHES)(pExtra)); default: return ERROR_SUCCESS; } } -static DWORD TestArchive( - LPCTSTR szMpqName, // Name of the MPQ - LPCTSTR szListFile, // Name of the listfile or NULL +static DWORD TestOpenArchive_ModifyArchive(TLogHelper & Logger, HANDLE hMpq, DWORD dwFlags) +{ + DWORD dwExpectedError = (dwFlags & TFLG_READ_ONLY) ? ERROR_ACCESS_DENIED : ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; + + // Modify the archive, if required + if(dwFlags & TFLG_MODIFY) + { + Logger.PrintProgress("Modifying archive ..."); + dwErrCode = AddFileToMpq(&Logger, hMpq, "AddedFile01.txt", "This is a file added to signed MPQ", MPQ_FILE_COMPRESS, 0, dwExpectedError); + } + return dwErrCode; +} + +static DWORD TestOpenArchive_SignArchive(TLogHelper & Logger, HANDLE hMpq, DWORD dwDoItIfNonZero) +{ + // Sign the MPQ archive, if required + if(dwDoItIfNonZero) + { + Logger.PrintProgress("Signing the MPQ ..."); + if(!SFileSignArchive(hMpq, SIGNATURE_TYPE_WEAK)) + { + Logger.PrintMessage("Failed to create archive signature"); + return ERROR_FILE_CORRUPT; + } + } + return ERROR_SUCCESS; +} + +static DWORD TestOpenArchive_GetFileInfo(TLogHelper & Logger, HANDLE hMpq, DWORD dwFlags) +{ + if(dwFlags & TFLG_GET_FILE_INFO) + { + TMPQHeader Header = {0}; + HANDLE hFile = NULL; + DWORD dwExpectedError; + DWORD cbLength; + BYTE DataBuff[0x400]; + + // Retrieve the version of the MPQ + Logger.PrintProgress("Checking SFileGetFileInfo"); + SFileGetFileInfo(hMpq, SFileMpqHeader, &Header, sizeof(TMPQHeader), NULL); + + // Test on invalid archive/file handle + TestGetFileInfo(&Logger, NULL, SFileMpqBetHeader, NULL, 0, NULL, false, ERROR_INVALID_HANDLE); + TestGetFileInfo(&Logger, NULL, (SFileInfoClass)0xFFF, NULL, 0, NULL, false, ERROR_INVALID_HANDLE); + TestGetFileInfo(&Logger, NULL, SFileInfoNameHash1, NULL, 0, NULL, false, ERROR_INVALID_HANDLE); + + // Valid handle but all parameters NULL + dwExpectedError = (Header.wFormatVersion == MPQ_FORMAT_VERSION_4) ? ERROR_INSUFFICIENT_BUFFER : ERROR_FILE_NOT_FOUND; + TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, NULL, 0, NULL, false, dwExpectedError); + TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, NULL, 0, &cbLength, false, dwExpectedError); + + // When we call SFileInfo with buffer = NULL and nonzero buffer size, it is ignored + TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, NULL, 3, &cbLength, false, dwExpectedError); + + // When we call SFileInfo with buffer != NULL and nonzero buffer size, it should return error + TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, DataBuff, 3, &cbLength, false, dwExpectedError); + + // Request for bet table header should also succeed if we want header only + dwExpectedError = (Header.wFormatVersion == MPQ_FORMAT_VERSION_4) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND; + TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, DataBuff, sizeof(TMPQBetHeader), &cbLength, (dwExpectedError == ERROR_SUCCESS), dwExpectedError); + TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, DataBuff, sizeof(DataBuff), &cbLength, (dwExpectedError == ERROR_SUCCESS), dwExpectedError); + + // Try to retrieve strong signature from the MPQ + dwExpectedError = (Header.wFormatVersion == MPQ_FORMAT_VERSION_4) ? ERROR_FILE_NOT_FOUND : ERROR_INSUFFICIENT_BUFFER; + TestGetFileInfo(&Logger, hMpq, SFileMpqStrongSignature, NULL, 0, NULL, false, dwExpectedError); + TestGetFileInfo(&Logger, hMpq, SFileMpqStrongSignature, NULL, 0, &cbLength, false, dwExpectedError); + if(Header.wFormatVersion == MPQ_FORMAT_VERSION_1) + assert(cbLength == MPQ_STRONG_SIGNATURE_SIZE + 4); + + // Retrieve the signature + dwExpectedError = (Header.wFormatVersion == MPQ_FORMAT_VERSION_4) ? ERROR_FILE_NOT_FOUND : ERROR_SUCCESS; + TestGetFileInfo(&Logger, hMpq, SFileMpqStrongSignature, DataBuff, sizeof(DataBuff), &cbLength, (dwExpectedError == ERROR_SUCCESS), dwExpectedError); + if(Header.wFormatVersion == MPQ_FORMAT_VERSION_1) + assert(memcmp(DataBuff, "NGIS", 4) == 0); + + // Check SFileGetFileInfo on a listfile + if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &hFile)) + { + TestGetFileInfo(&Logger, hMpq, SFileInfoFileTime, DataBuff, sizeof(DataBuff), &cbLength, false, ERROR_INVALID_HANDLE); + TestGetFileInfo(&Logger, hFile, SFileInfoFileTime, DataBuff, sizeof(DataBuff), &cbLength, true, ERROR_SUCCESS); + SFileCloseFile(hFile); + } + } + return ERROR_SUCCESS; +} + +static DWORD TestOpenArchive( + LPCTSTR szMpqName1, // Name of the MPQ + LPCTSTR szMpqName2, // Name of the MPQ (original name or name of a copy) LPCSTR szExpectedHash, // Expected name+data hash - DWORD dwFlags, // Test flags. Lower 16 bits contains the number of files + DWORD dwFlags, // Test flags. Lower bits contains the number of files const void * pExtra) // Extra parameter { - TLogHelper Logger("TestMpq", szMpqName); + TLogHelper Logger("TestMpq", szMpqName1); HANDLE hMpq = NULL; DWORD dwExpectedFileCount = 0; DWORD dwSearchFlags = 0; + DWORD dwOpenFlags = 0; DWORD dwMpqFlags = 0; DWORD dwErrCode; + // Propagate the open MPQ flags from the input + dwOpenFlags |= (dwFlags & TFLG_WILL_FAIL) ? MPQ_OPEN_DONT_REPORT_FAILURE : 0; + dwOpenFlags |= (dwFlags & TFLG_READ_ONLY) ? STREAM_FLAG_READ_ONLY : 0; + // If the file is a partial MPQ, don't load all files - if(_tcsstr(szMpqName, _T(".MPQ.part")) == NULL) + if(_tcsstr(szMpqName1, _T(".MPQ.part")) == NULL) dwSearchFlags |= SEARCH_FLAG_LOAD_FILES; // If we shall hash the files, do it @@ -2399,7 +2518,7 @@ static DWORD TestArchive( } // Copy the archive so we won't fuck up the original one - dwErrCode = OpenExistingArchiveWithCopy(&Logger, szMpqName, NULL, &hMpq); + dwErrCode = OpenExistingArchiveWithCopy(&Logger, szMpqName1, szMpqName2, &hMpq, dwOpenFlags); while(dwErrCode == ERROR_SUCCESS) { // Check for malformed MPQs @@ -2407,12 +2526,28 @@ static DWORD TestArchive( dwSearchFlags |= (dwMpqFlags & MPQ_FLAG_MALFORMED) ? SEARCH_FLAG_IGNORE_ERRORS : 0; dwSearchFlags |= (GetExtraType(pExtra) == PatchList) ? SEARCH_FLAG_IGNORE_ERRORS : 0; + // Verify signature before any changes + if((dwErrCode = TestOpenArchive_VerifySignature(Logger, hMpq, (dwFlags & TFLG_SIGCHECK_BEFORE))) != ERROR_SUCCESS) + break; + // Perform extra action, dependent on the data passed - if((dwErrCode = TestArchive_ExtraType(Logger, hMpq, dwSearchFlags, pExtra)) != ERROR_SUCCESS) + if((dwErrCode = TestOpenArchive_ExtraType(Logger, hMpq, dwSearchFlags, pExtra)) != ERROR_SUCCESS) break; - // If the listfile was given, add it to the MPQ - if((dwErrCode = TestArchive_AddListFile(Logger, hMpq, szListFile)) != ERROR_SUCCESS) + // Modify the archive, if required + if((dwErrCode = TestOpenArchive_ModifyArchive(Logger, hMpq, dwFlags)) != ERROR_SUCCESS) + break; + + // Sign the archive, if needed + if((dwErrCode = TestOpenArchive_SignArchive(Logger, hMpq, (dwFlags & TFLG_SIGN_ARCHIVE))) != ERROR_SUCCESS) + break; + + // Test the SFileGetFileInfo, if required + if((dwErrCode = TestOpenArchive_GetFileInfo(Logger, hMpq, dwFlags)) != ERROR_SUCCESS) + break; + + // Verify signature after any changes + if((dwErrCode = TestOpenArchive_VerifySignature(Logger, hMpq, (dwFlags & TFLG_SIGCHECK_AFTER))) != ERROR_SUCCESS) break; // Attempt to open the (listfile), (attributes), (signature) @@ -2426,177 +2561,152 @@ static DWORD TestArchive( break; } + // Reset error code, if the failure is expected + if((dwErrCode != ERROR_SUCCESS) && (hMpq == NULL) && (dwFlags & TFLG_WILL_FAIL)) + SetLastError(dwErrCode = ERROR_SUCCESS); + // Cleanup and exit if(hMpq != NULL) SFileCloseArchive(hMpq); return Logger.PrintVerdict(dwErrCode); } -static DWORD TestArchive(const TEST_INFO2 & TestInfo) +static DWORD TestOpenArchive(const TEST_INFO & TestInfo) { - return TestArchive(TestInfo.szName1, // Name of the MPQ - TestInfo.szName2, // Name of the listfile or NULL - TestInfo.szDataHash, // Compound name+data hash - TestInfo.dwFlags, // Test flags - TestInfo.pExtra); // Extra parameter + return TestOpenArchive(TestInfo.szName1, // Name of the MPQ + TestInfo.szName2, // Name of the listfile or NULL + TestInfo.szDataHash, // Compound name+data hash + TestInfo.dwFlags, // Test flags + TestInfo.pExtra); // Extra parameter } -// Open an archive for read-only access -static DWORD TestOpenArchive_MiscTests(LPCTSTR szPlainName, LPCTSTR szCopyName, DWORD dwFlags) -{ - TLogHelper Logger("MiscTest", szPlainName); - HANDLE hFile = NULL; - HANDLE hMpq = NULL; - DWORD dwOpenFlags = 0; - DWORD dwErrCode; - - // Configure the open flags - dwOpenFlags |= (dwFlags & MFLAG_OPEN_READ_ONLY) ? MPQ_OPEN_READ_ONLY : 0; - dwOpenFlags |= (dwFlags & MFLAG_WILL_FAIL) ? MPQ_OPEN_DONT_REPORT_FAILURE: 0; +//----------------------------------------------------------------------------- +// Reopening archives - // Open the file - szCopyName = (szCopyName != NULL) ? szCopyName : szPlainName; - Logger.PrintProgress(_T("Opening archive %s"), szPlainName); - dwErrCode = OpenExistingArchiveWithCopy(&Logger, szPlainName, szCopyName, &hMpq, dwOpenFlags); +static void WINAPI CompactCallback(void * pvUserData, DWORD dwWork, ULONGLONG BytesDone, ULONGLONG TotalBytes) +{ + TLogHelper * pLogger = (TLogHelper *)pvUserData; + LPCSTR szWork = NULL; - // Did it succeed? - if(dwErrCode == ERROR_SUCCESS) + switch(dwWork) { - if((dwFlags & MFLAG_WILL_FAIL) == 0) - { - // Test the GetFileInfo operations - if(dwFlags & MFLAG_GET_FILE_INFO) - { - TMPQHeader Header = {0}; - DWORD dwExpectedError; - DWORD cbLength; - BYTE DataBuff[0x400]; - - // Retrieve the version of the MPQ - Logger.PrintProgress("Checking SFileGetFileInfo"); - SFileGetFileInfo(hMpq, SFileMpqHeader, &Header, sizeof(TMPQHeader), NULL); - - // Test on invalid archive/file handle - TestGetFileInfo(&Logger, NULL, SFileMpqBetHeader, NULL, 0, NULL, false, ERROR_INVALID_HANDLE); - TestGetFileInfo(&Logger, NULL, (SFileInfoClass)0xFFF, NULL, 0, NULL, false, ERROR_INVALID_HANDLE); - TestGetFileInfo(&Logger, NULL, SFileInfoNameHash1, NULL, 0, NULL, false, ERROR_INVALID_HANDLE); - - // Valid handle but all parameters NULL - dwExpectedError = (Header.wFormatVersion == MPQ_FORMAT_VERSION_4) ? ERROR_INSUFFICIENT_BUFFER : ERROR_FILE_NOT_FOUND; - TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, NULL, 0, NULL, false, dwExpectedError); - TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, NULL, 0, &cbLength, false, dwExpectedError); - - // When we call SFileInfo with buffer = NULL and nonzero buffer size, it is ignored - TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, NULL, 3, &cbLength, false, dwExpectedError); - - // When we call SFileInfo with buffer != NULL and nonzero buffer size, it should return error - TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, DataBuff, 3, &cbLength, false, dwExpectedError); - - // Request for bet table header should also succeed if we want header only - dwExpectedError = (Header.wFormatVersion == MPQ_FORMAT_VERSION_4) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND; - TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, DataBuff, sizeof(TMPQBetHeader), &cbLength, (dwExpectedError == ERROR_SUCCESS), dwExpectedError); - TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, DataBuff, sizeof(DataBuff), &cbLength, (dwExpectedError == ERROR_SUCCESS), dwExpectedError); - - // Try to retrieve strong signature from the MPQ - dwExpectedError = (Header.wFormatVersion == MPQ_FORMAT_VERSION_4) ? ERROR_FILE_NOT_FOUND : ERROR_INSUFFICIENT_BUFFER; - TestGetFileInfo(&Logger, hMpq, SFileMpqStrongSignature, NULL, 0, NULL, false, dwExpectedError); - TestGetFileInfo(&Logger, hMpq, SFileMpqStrongSignature, NULL, 0, &cbLength, false, dwExpectedError); - if(Header.wFormatVersion == MPQ_FORMAT_VERSION_1) - assert(cbLength == MPQ_STRONG_SIGNATURE_SIZE + 4); - - // Retrieve the signature - dwExpectedError = (Header.wFormatVersion == MPQ_FORMAT_VERSION_4) ? ERROR_FILE_NOT_FOUND : ERROR_SUCCESS; - TestGetFileInfo(&Logger, hMpq, SFileMpqStrongSignature, DataBuff, sizeof(DataBuff), &cbLength, (dwExpectedError == ERROR_SUCCESS), dwExpectedError); - if(Header.wFormatVersion == MPQ_FORMAT_VERSION_1) - assert(memcmp(DataBuff, "NGIS", 4) == 0); - - // Check SFileGetFileInfo on a listfile - if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &hFile)) - { - TestGetFileInfo(&Logger, hMpq, SFileInfoFileTime, DataBuff, sizeof(DataBuff), &cbLength, false, ERROR_INVALID_HANDLE); - TestGetFileInfo(&Logger, hFile, SFileInfoFileTime, DataBuff, sizeof(DataBuff), &cbLength, true, ERROR_SUCCESS); - SFileCloseFile(hFile); - } - } + case CCB_CHECKING_FILES: + szWork = "Checking files in archive"; + break; - // Test read-only vs read-write operations - if(dwFlags & (MFLAG_OPEN_READ_ONLY | MFLAG_OPEN_READ_WRITE)) - { - DWORD dwExpectedError = (dwFlags & MFLAG_OPEN_READ_ONLY) ? ERROR_ACCESS_DENIED : ERROR_SUCCESS; + case CCB_CHECKING_HASH_TABLE: + szWork = "Checking hash table"; + break; - AddFileToMpq (&Logger, hMpq, "AddedFile.txt", "This is an added file.", MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED, 0, dwExpectedError); - RenameMpqFile(&Logger, hMpq, "spawn.mpq", "spawn-renamed.mpq", dwExpectedError); - RemoveMpqFile(&Logger, hMpq, "spawn-renamed.mpq", dwExpectedError); - } - } - else - { - Logger.PrintError(_T("Archive %s succeeded to open, but it should fail."), szPlainName); - dwErrCode = ERROR_CAN_NOT_COMPLETE; - } + case CCB_COPYING_NON_MPQ_DATA: + szWork = "Copying non-MPQ data"; + break; - SFileCloseArchive(hMpq); + case CCB_COMPACTING_FILES: + szWork = "Compacting files"; + break; + + case CCB_CLOSING_ARCHIVE: + szWork = "Closing archive"; + break; } - else + + if(szWork != NULL) { - if((dwFlags & MFLAG_WILL_FAIL) == 0) - { - Logger.PrintError(_T("Archive %s failed to open, but it should succeed."), szPlainName); - } + if(pLogger != NULL) + pLogger->PrintProgress("%s (%I64u of %I64u) ...", szWork, BytesDone, TotalBytes); else - { - dwErrCode = ERROR_SUCCESS; - } + printf("%s (" fmt_I64u_a " of " fmt_I64u_a ") ... \r", szWork, BytesDone, TotalBytes); } - return dwErrCode; } -static DWORD TestOpenArchive_SignatureTest_Verify(TLogHelper & Logger, HANDLE hMpq) +static DWORD TestReopenArchive_CompactArchive(TLogHelper & Logger, HANDLE hMpq, DWORD dwFlags) { - DWORD dwSignatures = 0; - DWORD dwVerifyError; + if(dwFlags & TFLG_COMPACT) + { + // Set the compact callback + Logger.PrintProgress("Compacting archive ..."); + if(!SFileSetCompactCallback(hMpq, CompactCallback, &Logger)) + return Logger.PrintError("Failed to set the compact callback"); - // Query the signature types - Logger.PrintProgress("Retrieving signatures ..."); - TestGetFileInfo(&Logger, hMpq, SFileMpqSignatures, &dwSignatures, sizeof(DWORD), NULL, true, ERROR_SUCCESS); + // Compact the archive + if(!SFileCompactArchive(hMpq, NULL, false)) + return Logger.PrintError("Failed to compact archive"); + } + return ERROR_SUCCESS; +} + +static DWORD TestReopenArchive( + LPCTSTR szMpqName1, // Name of the MPQ + LPCSTR szExpectedHash, // Expected name+data hash + DWORD dwFlags) // Test flags. Lower bits contains the number of files +{ + TLogHelper Logger("ReopenMpqTest", szMpqName1); + ULONGLONG PreMpqDataSize = (dwFlags & TFLG_ADD_USER_DATA) ? 0x400 : 0; + ULONGLONG UserDataSize = (dwFlags & TFLG_ADD_USER_DATA) ? 0x531 : 0; + LPCTSTR szCopyName = _T("StormLibTest_Reopened.mpq"); + HANDLE hMpq; + DWORD dwExpectedFileCount = 0; + DWORD dwSearchFlags = SEARCH_FLAG_LOAD_FILES; + TCHAR szFullPath[MAX_PATH]; + DWORD dwErrCode; - // Are there some signatures at all? - if(dwSignatures == 0) + // If we shall hash the files, do it + if(IS_VALID_STRING(szExpectedHash)) { - Logger.PrintMessage("No signatures present in the file"); - return ERROR_FILE_CORRUPT; + dwExpectedFileCount = (dwFlags & TFLG_VALUE_MASK); + dwSearchFlags |= SEARCH_FLAG_HASH_FILES; } - // Verify any of the present signatures - Logger.PrintProgress("Verifying archive signature ..."); - dwVerifyError = SFileVerifyArchive(hMpq); + // Create copy of the archive, with interleaving some user data + dwErrCode = CreateFileCopy(&Logger, szMpqName1, szCopyName, szFullPath, _countof(szFullPath), PreMpqDataSize, UserDataSize); - // Verify the result - if((dwSignatures & SIGNATURE_TYPE_STRONG) && (dwVerifyError != ERROR_STRONG_SIGNATURE_OK)) + // Open the archive and read the hash of the files + if(dwErrCode == ERROR_SUCCESS) { - Logger.PrintMessage("Strong signature verification error"); - return ERROR_FILE_CORRUPT; + if((dwErrCode = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq)) == ERROR_SUCCESS) + { + // Verify presence of (listfile) and (attributes) + CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, (dwFlags & TFLG_HAS_LISTFILE)); + CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, (dwFlags & TFLG_HAS_ATTRIBUTES)); + + // If required, we search the archive and compare file cound and name hash + dwErrCode = VerifyDataChecksum(Logger, hMpq, dwSearchFlags, szExpectedHash, dwExpectedFileCount); + SFileCloseArchive(hMpq); + } } - // Verify the result - if((dwSignatures & SIGNATURE_TYPE_WEAK) && (dwVerifyError != ERROR_WEAK_SIGNATURE_OK)) + // Try to modify and/or compact the MPQ + if(dwErrCode == ERROR_SUCCESS) { - Logger.PrintMessage("Weak signature verification error"); - return ERROR_FILE_CORRUPT; + // Open the archive again + if((dwErrCode = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq)) == ERROR_SUCCESS) + { + // Modify the archive, if required + if((dwErrCode = TestOpenArchive_ModifyArchive(Logger, hMpq, dwFlags)) == ERROR_SUCCESS) + { + dwErrCode = TestReopenArchive_CompactArchive(Logger, hMpq, dwFlags); + } + SFileCloseArchive(hMpq); + } } - return ERROR_SUCCESS; -} -static DWORD TestOpenArchive_SignatureTest_Sign(TLogHelper & Logger, HANDLE hMpq) -{ - // Sign the MPQ archive - Logger.PrintProgress("Signing the MPQ ..."); - if(!SFileSignArchive(hMpq, SIGNATURE_TYPE_WEAK)) + // Open the archive and load some files + if((dwErrCode == ERROR_SUCCESS) && STORMLIB_TEST_FLAGS(dwFlags, TFLG_COMPACT | TFLG_MODIFY, TFLG_COMPACT)) { - Logger.PrintMessage("Failed to create archive signature"); - return ERROR_FILE_CORRUPT; + // Open the archive + if((dwErrCode = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq)) == ERROR_SUCCESS) + { + // Verify presence of (listfile) and (attributes) + CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, (dwFlags & TFLG_HAS_LISTFILE)); + CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, (dwFlags & TFLG_HAS_ATTRIBUTES)); + + // Search the archive and load every file + dwErrCode = VerifyDataChecksum(Logger, hMpq, dwSearchFlags, szExpectedHash, dwExpectedFileCount); + } + SFileCloseArchive(hMpq); } - return ERROR_SUCCESS; + return dwErrCode; } static DWORD TestOpenArchive_SignatureTest(LPCTSTR szPlainName, LPCTSTR szOriginalName, DWORD dwFlags) @@ -2622,28 +2732,27 @@ static DWORD TestOpenArchive_SignatureTest(LPCTSTR szPlainName, LPCTSTR szOrigin if(dwErrCode == ERROR_SUCCESS) { // Shall we check the signatures before modifications? - if((dwErrCode == ERROR_SUCCESS) && (dwFlags & SFLAG_VERIFY_BEFORE)) + if(dwErrCode == ERROR_SUCCESS) { - dwErrCode = TestOpenArchive_SignatureTest_Verify(Logger, hMpq); + dwErrCode = TestOpenArchive_VerifySignature(Logger, hMpq, (dwFlags & SFLAG_VERIFY_BEFORE)); } // Shall we modify the archive? - if((dwErrCode == ERROR_SUCCESS) && (dwFlags & SFLAG_MODIFY_ARCHIVE)) + if(dwErrCode == ERROR_SUCCESS) { - Logger.PrintProgress("Modifying archive ..."); - dwErrCode = AddFileToMpq(&Logger, hMpq, "AddedFile01.txt", "This is a file added to signed MPQ", MPQ_FILE_COMPRESS, 0, ERROR_SUCCESS); + dwErrCode = TestOpenArchive_ModifyArchive(Logger, hMpq, (dwFlags & SFLAG_MODIFY_ARCHIVE)); } // Shall we sign the archive? - if((dwErrCode == ERROR_SUCCESS) && (dwFlags & SFLAG_SIGN_ARCHIVE)) + if(dwErrCode == ERROR_SUCCESS) { - dwErrCode = TestOpenArchive_SignatureTest_Sign(Logger, hMpq); + dwErrCode = TestOpenArchive_SignArchive(Logger, hMpq, (dwFlags & SFLAG_SIGN_ARCHIVE)); } // Shall we check the signatures after modifications? - if((dwErrCode == ERROR_SUCCESS) && (dwFlags & SFLAG_VERIFY_AFTER)) + if(dwErrCode == ERROR_SUCCESS) { - dwErrCode = TestOpenArchive_SignatureTest_Verify(Logger, hMpq); + dwErrCode = TestOpenArchive_VerifySignature(Logger, hMpq, (dwFlags & SFLAG_VERIFY_AFTER)); } SFileCloseArchive(hMpq); @@ -2731,11 +2840,75 @@ static DWORD TestOpenArchive_CompactArchive(LPCTSTR szPlainName, LPCTSTR szCopyN return dwErrCode; } +static DWORD TestOpenArchive_AddFile(LPCTSTR szMpqName, DWORD dwFlags) +{ + TLogHelper Logger("AddFileToMpqTest", szMpqName); + PFILE_DATA pFileData = NULL; + LPCTSTR szBackupMpq = (dwFlags & TFLAG_REOPEN) ? _T("StormLibTest_Reopened.mpq") : szMpqName; + LPCSTR szFileName = "AddedFile001.txt"; + LPCSTR szFileData = "0123456789ABCDEF"; + HANDLE hMpq = NULL; + DWORD dwFileSize = (DWORD)strlen(szFileData); + DWORD dwErrCode = ERROR_SUCCESS; + + // Copy the archive so we won't fuck up the original one + dwErrCode = OpenExistingArchiveWithCopy(&Logger, szMpqName, szBackupMpq, &hMpq); + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = AddFileToMpq(&Logger, hMpq, szFileName, szFileData, MPQ_FILE_IMPLODE, MPQ_COMPRESSION_PKWARE, ERROR_SUCCESS); + SFileCloseArchive(hMpq); + } + + // Now the file has been written and the MPQ has been saved. + // We reopen the MPQ and check the state of (listfile) and (attributes) + if((dwErrCode == ERROR_SUCCESS) && (dwFlags & TFLAG_REOPEN)) + { + if((dwErrCode = OpenExistingArchiveWithCopy(&Logger, NULL, szBackupMpq, &hMpq)) == ERROR_SUCCESS) + { + // Verify presence of (listfile) and (attributes) + CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, (dwFlags & TFLAG_HAS_LISTFILE)); + CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, (dwFlags & TFLAG_HAS_ATTRIBUTES)); + + // Try to open the file that we recently added + dwErrCode = LoadMpqFile(Logger, hMpq, szFileName, 0, 0, &pFileData); + if(dwErrCode == ERROR_SUCCESS) + { + // Verify if the file size matches + if(pFileData->dwFileSize == dwFileSize) + { + // Verify if the file data match + if(memcmp(pFileData->FileData, szFileData, dwFileSize)) + { + Logger.PrintError("The data of the added file does not match"); + dwErrCode = ERROR_FILE_CORRUPT; + } + } + else + { + Logger.PrintError("The size of the added file does not match"); + dwErrCode = ERROR_FILE_CORRUPT; + } + + // Delete the file data + STORM_FREE(pFileData); + } + else + { + Logger.PrintError("Failed to open the file previously added"); + } + + SFileCloseArchive(hMpq); + } + } + return dwErrCode; +} + + static DWORD ForEachFile_VerifyFileChecksum(LPCTSTR szFullPath) { PFILE_DATA pFileData; TCHAR * szExtension; - TCHAR szShaFileName[MAX_PATH+1]; + TCHAR szShaFileName[MAX_PATH + 1]; TCHAR szSha1Text[0x40]; char szSha1TextA[0x40]; DWORD dwErrCode = ERROR_SUCCESS; @@ -2817,69 +2990,6 @@ static DWORD ForEachFile_OpenArchive(LPCTSTR szFullPath) return dwErrCode; } -static DWORD TestOpenArchive_AddFile(LPCTSTR szMpqName, DWORD dwFlags) -{ - TLogHelper Logger("AddFileToMpqTest", szMpqName); - PFILE_DATA pFileData = NULL; - LPCTSTR szBackupMpq = (dwFlags & TFLAG_REOPEN) ? _T("StormLibTest_Reopened.mpq") : szMpqName; - LPCSTR szFileName = "AddedFile001.txt"; - LPCSTR szFileData = "0123456789ABCDEF"; - HANDLE hMpq = NULL; - DWORD dwFileSize = (DWORD)strlen(szFileData); - DWORD dwErrCode = ERROR_SUCCESS; - - // Copy the archive so we won't fuck up the original one - dwErrCode = OpenExistingArchiveWithCopy(&Logger, szMpqName, szBackupMpq, &hMpq); - if(dwErrCode == ERROR_SUCCESS) - { - dwErrCode = AddFileToMpq(&Logger, hMpq, szFileName, szFileData, MPQ_FILE_IMPLODE, MPQ_COMPRESSION_PKWARE, ERROR_SUCCESS); - SFileCloseArchive(hMpq); - } - - // Now the file has been written and the MPQ has been saved. - // We reopen the MPQ and check the state of (listfile) and (attributes) - if((dwErrCode == ERROR_SUCCESS) && (dwFlags & TFLAG_REOPEN)) - { - if((dwErrCode = OpenExistingArchiveWithCopy(&Logger, NULL, szBackupMpq, &hMpq)) == ERROR_SUCCESS) - { - // Verify presence of (listfile) and (attributes) - CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, (dwFlags & TFLAG_HAS_LISTFILE)); - CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, (dwFlags & TFLAG_HAS_ATTRIBUTES)); - - // Try to open the file that we recently added - dwErrCode = LoadMpqFile(Logger, hMpq, szFileName, 0, 0, &pFileData); - if(dwErrCode == ERROR_SUCCESS) - { - // Verify if the file size matches - if(pFileData->dwFileSize == dwFileSize) - { - // Verify if the file data match - if(memcmp(pFileData->FileData, szFileData, dwFileSize)) - { - Logger.PrintError("The data of the added file does not match"); - dwErrCode = ERROR_FILE_CORRUPT; - } - } - else - { - Logger.PrintError("The size of the added file does not match"); - dwErrCode = ERROR_FILE_CORRUPT; - } - - // Delete the file data - STORM_FREE(pFileData); - } - else - { - Logger.PrintError("Failed to open the file previously added"); - } - - SFileCloseArchive(hMpq); - } - } - return dwErrCode; -} - static DWORD TestCreateArchive_EmptyMpq(LPCTSTR szPlainName, DWORD dwCreateFlags) { TLogHelper Logger("CreateEmptyMpq", szPlainName); @@ -3737,34 +3847,18 @@ static DWORD TestModifyArchive_ReplaceFile(LPCTSTR szMpqPlainName, LPCTSTR szFil //----------------------------------------------------------------------------- // Tables -static LPCTSTR Bliz = _T("ListFile_Blizzard.txt"); -static LPCTSTR WotI = _T("ListFile_WarOfTheImmortals.txt"); +static LPCTSTR szSigned1 = _T("STANDARD.SNP"); +static LPCTSTR szSigned2 = _T("War2Patch_202.exe"); +static LPCTSTR szSigned3 = _T("WoW-1.2.3.4211-enUS-patch.exe"); +static LPCTSTR szSigned4 = _T("(10)DustwallowKeys.w3m"); -static const TEST_INFO TestList_StreamOps[] = -{ - {_T("MPQ_2013_v4_alternate-original.MPQ"), NULL, 0}, - {_T("MPQ_2013_v4_alternate-original.MPQ"), NULL, STREAM_FLAG_READ_ONLY}, - {_T("MPQ_2013_v4_alternate-complete.MPQ"), NULL, STREAM_FLAG_USE_BITMAP}, - {_T("part-file://MPQ_2009_v2_WoW_patch.MPQ.part"), NULL, 0}, - {_T("blk4-file://streaming/model.MPQ.0"), NULL, STREAM_PROVIDER_BLOCK4}, - {_T("mpqe-file://MPQ_2011_v2_EncryptedMpq.MPQE"), NULL, STREAM_PROVIDER_MPQE} -}; +static LPCTSTR szDiabdatMPQ = _T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"); -static const TEST_INFO TestList_MasterMirror[] = -{ - {_T("part-file://MPQ_2009_v1_patch-created.MPQ.part"), _T("MPQ_2009_v1_patch-original.MPQ"), 0}, - {_T("part-file://MPQ_2009_v1_patch-partial.MPQ.part"), _T("MPQ_2009_v1_patch-original.MPQ"), 1}, - {_T("part-file://MPQ_2009_v1_patch-complete.MPQ.part"), _T("MPQ_2009_v1_patch-original.MPQ"), 1}, - {_T("MPQ_2013_v4_alternate-created.MPQ"), _T("MPQ_2013_v4_alternate-original.MPQ"), 0}, - {_T("MPQ_2013_v4_alternate-incomplete.MPQ"), _T("MPQ_2013_v4_alternate-incomplete.MPQ"), 1}, - {_T("MPQ_2013_v4_alternate-complete.MPQ"), _T("MPQ_2013_v4_alternate-original.MPQ"), 1}, +static const TEST_EXTRA_ONEFILE LfBliz = {ListFile, _T("ListFile_Blizzard.txt")}; +static const TEST_EXTRA_ONEFILE LfWotI = {ListFile, _T("ListFile_WarOfTheImmortals.txt")}; - // Takes hell a lot of time!!! -// {_T("MPQ_2013_v4_alternate-downloaded.MPQ"), _T("http://www.zezula.net\\mpqs\\alternate.zip"), 0} -}; - -static const TEST_EXTRA_TWOFILES TwoFilesD1 = {TwoFiles, "music\\dintro.wav", "File00000023.xxx" }; -static const TEST_EXTRA_TWOFILES TwoFilesD2 = {TwoFiles, "waitingroombkgd.dc6"}; +static const TEST_EXTRA_TWOFILES TwoFilesD1 = {TwoFiles, "music\\dintro.wav", "File00000023.xxx"}; +static const TEST_EXTRA_TWOFILES TwoFilesD2 = {TwoFiles, "waitingroombkgd.dc6"}; static const TEST_EXTRA_TWOFILES TwoFilesW3M = {TwoFiles, "file00000002.blp"}; static const TEST_EXTRA_TWOFILES TwoFilesW3X = {TwoFiles, "BlueCrystal.mdx"}; @@ -3913,7 +4007,30 @@ static const TEST_EXTRA_PATCHES PatchH6898 = 10 }; -static const TEST_INFO2 Test_Mpqs[] = +static const TEST_INFO TestList_StreamOps[] = +{ + {_T("MPQ_2013_v4_alternate-original.MPQ"), NULL, NULL, 0}, + {_T("MPQ_2013_v4_alternate-original.MPQ"), NULL, NULL, STREAM_FLAG_READ_ONLY}, + {_T("MPQ_2013_v4_alternate-complete.MPQ"), NULL, NULL, STREAM_FLAG_USE_BITMAP}, + {_T("part-file://MPQ_2009_v2_WoW_patch.MPQ.part"), NULL, NULL, 0}, + {_T("blk4-file://streaming/model.MPQ.0"), NULL, NULL, STREAM_PROVIDER_BLOCK4}, + {_T("mpqe-file://MPQ_2011_v2_EncryptedMpq.MPQE"), NULL, NULL, STREAM_PROVIDER_MPQE} +}; + +static const TEST_INFO TestList_MasterMirror[] = +{ + {_T("part-file://MPQ_2009_v1_patch-created.MPQ.part"), _T("MPQ_2009_v1_patch-original.MPQ"), NULL, 0}, + {_T("part-file://MPQ_2009_v1_patch-partial.MPQ.part"), _T("MPQ_2009_v1_patch-original.MPQ"), NULL, 1}, + {_T("part-file://MPQ_2009_v1_patch-complete.MPQ.part"), _T("MPQ_2009_v1_patch-original.MPQ"), NULL, 1}, + {_T("MPQ_2013_v4_alternate-created.MPQ"), _T("MPQ_2013_v4_alternate-original.MPQ"), NULL, 0}, + {_T("MPQ_2013_v4_alternate-incomplete.MPQ"), _T("MPQ_2013_v4_alternate-incomplete.MPQ"), NULL, 1}, + {_T("MPQ_2013_v4_alternate-complete.MPQ"), _T("MPQ_2013_v4_alternate-original.MPQ"), NULL, 1}, + + // Takes hell a lot of time!!! +// {_T("MPQ_2013_v4_alternate-downloaded.MPQ"), _T("http://www.zezula.net\\mpqs\\alternate.zip"), NULL, 0} +}; + +static const TEST_INFO Test_OpenMpqs[] = { // Correct or damaged archives @@ -3923,7 +4040,7 @@ static const TEST_INFO2 Test_Mpqs[] = {_T("MPQ_1997_v1_INSTALL_SC1B.EXE_"), NULL, "3248460c89bb6f8e3b8fc3e08de7ffbb", 79}, // From Starcraft 1 BETA {_T("MPQ_2016_v1_D2XP_IX86_1xx_114a.mpq"), NULL, "255d87a62f3c9518f72cf723a1818946", 221, &TwoFilesD2}, // Update MPQ from Diablo II (patch 2016) {_T("MPQ_2018_v1_icon_error.w3m"), NULL, "fcefa25fb50c391e8714f2562d1e10ff", 19, &TwoFilesW3M}, - {_T("MPQ_1997_v1_Diablo1_STANDARD.SNP"), Bliz, "5ef18ef9a26b5704d8d46a344d976c89", 2}, // File whose archive's (signature) file has flags = 0x90000000 + {_T("MPQ_1997_v1_Diablo1_STANDARD.SNP"), NULL, "5ef18ef9a26b5704d8d46a344d976c89", 2, &LfBliz}, // File whose archive's (signature) file has flags = 0x90000000 {_T("MPQ_2012_v2_EmptyMpq.MPQ"), NULL, "00000000000000000000000000000000", 0}, // Empty archive (found in WoW cache - it's just a header) {_T("MPQ_2013_v4_EmptyMpq.MPQ"), NULL, "00000000000000000000000000000000", 0}, // Empty archive (created artificially - it's just a header) {_T("MPQ_2013_v4_patch-base-16357.MPQ"), NULL, "d41d8cd98f00b204e9800998ecf8427e", 1}, // Empty archive (found in WoW cache - it's just a header) @@ -3975,7 +4092,7 @@ static const TEST_INFO2 Test_Mpqs[] = // MPQ modifications from Chinese games {_T("MPx_2013_v1_LongwuOnline.mpk"), NULL, "548f7db88284097f7e94c95a08c5bc24", 469}, // MPK archive from Longwu online - {_T("MPx_2013_v1_WarOfTheImmortals.sqp"), WotI, "a048f37f7c6162a96253d8081722b6d9", 9396}, // SQP archive from War of the Immortals + {_T("MPx_2013_v1_WarOfTheImmortals.sqp"), NULL, "a048f37f7c6162a96253d8081722b6d9", 9396, &LfWotI}, // SQP archive from War of the Immortals {_T("MPx_2022_v1_Music.mpk"), NULL, "fc369cff4ff4b573dd024de963e4cdd5", 650}, // MPK archive from Warriors of the Ghost Valley {_T("MPx_2022_v1_Scp.mpk"), NULL, "9cb453dc159f2e667c14f48957fd9e77", 113}, // MPK archive from Warriors of the Ghost Valley {_T("MPx_2022_v1_UI.mpk"), NULL, "677a36b458d528a3158ced3dfb711e49", 3086}, // MPK archive from Warriors of the Ghost Valley @@ -3983,7 +4100,7 @@ static const TEST_INFO2 Test_Mpqs[] = // Patched MPQs {_T("MPQ_1998_v1_StarCraft.mpq"), NULL, "5ecef2f41c5fd44c264e269416de9495", 1943, &PatchSC1}, // Patched MPQ from StarCraft I {_T("MPQ_2012_v4_OldWorld.MPQ"), NULL, "07643ec62864b4dd4fc8f8a6a16ce006", 71439, &Patch13286}, // WoW 13286: Patched "OldWorld.MPQ" - {_T("MPQ_2013_v4_world.MPQ"), NULL, "246b46105b85a3a80a23ff68a402c01e", 52286, &Patch15050}, // WoW 15050: Patched "world.MPQ" + {_T("MPQ_2013_v4_world.MPQ"), NULL, "af9baeceab20139bbf94d03f99170ae0", 48930, &Patch15050}, // WoW 15050: Patched "world.MPQ" {_T("MPQ_2013_v4_locale-enGB.MPQ"), NULL, "d39e743aaf6dad51d643d65e6e564804", 14349, &Patch16965}, // WoW 16965: Patched "locale-enGB.MPQ" {_T("MPQ_2013_v4_Base1.SC2Data"), NULL, "28a0f3cff1f400feb268ddae0efb2985", 1459, &Patch32283}, // SC2 32283: Patched "Base1.SC2Data" {_T("MPQ_2013_v4_Mods#Core.SC2Mod#enGB.SC2Assets"), NULL, "89df7ddac15700721b3f4c37d2673c1f", 11, &Patch32283a},// SC2 32283: Patched "Mods#Core.SC2Mod#enGB.SC2Assets" @@ -3992,56 +4109,54 @@ static const TEST_INFO2 Test_Mpqs[] = {_T("MPQ_2013_v4_Mods#Liberty.SC2Mod#enGB.SC2Data"), NULL, "fde3842552c1a9cd5ceee0e571227d18", 17, &Patch36281}, // SC2 36281: Patched "Mods#Liberty.SC2Mod#enGB.SC2Data" {_T("MPQ_2014_v4_base-Win.MPQ"), NULL, "337b609b2469a6732f2837eae8f730a4", 207, &PatchH3604}, // HSTN 3604: Patched "base-Win.MPQ" {_T("MPQ_2014_v4_base-Win.MPQ"), NULL, "28c3447bdc6b221b5cb346123ea03a94", 234, &PatchH6898}, // HSTN 6898: Patched "base-Win.MPQ" -}; -static const TEST_INFO Test_Signature[] = -{ - {_T("MPQ_1997_v1_Diablo1_STANDARD.SNP"), _T("STANDARD.SNP"), SFLAG_VERIFY_BEFORE}, - {_T("MPQ_1997_v1_Diablo1_STANDARD.SNP"), _T("STANDARD.SNP"), SFLAG_VERIFY_BEFORE}, - {_T("MPQ_1999_v1_WeakSignature.exe"), _T("War2Patch_202.exe"), SFLAG_VERIFY_BEFORE}, - {_T("MPQ_2003_v1_WeakSignatureEmpty.exe"), _T("WoW-1.2.3.4211-enUS-patch.exe"), SFLAG_VERIFY_BEFORE}, - {_T("MPQ_2002_v1_StrongSignature.w3m"), _T("(10)DustwallowKeys.w3m"), SFLAG_VERIFY_BEFORE}, - {_T("MPQ_1998_v1_StarDat.mpq"), NULL, SFLAG_SIGN_ARCHIVE | SFLAG_VERIFY_AFTER}, - {_T("MPQ_1999_v1_WeakSignature.exe"), _T("War2Patch_202.exe"), SFLAG_VERIFY_BEFORE | SFLAG_MODIFY_ARCHIVE | SFLAG_SIGN_ARCHIVE | SFLAG_VERIFY_AFTER}, - {_T("MPQ_1999_v1_WeakSigned1.mpq"), NULL, SFLAG_CREATE_ARCHIVE | SFLAG_SIGN_AT_CREATE | SFLAG_MODIFY_ARCHIVE | SFLAG_SIGN_ARCHIVE | SFLAG_VERIFY_AFTER}, - {_T("MPQ_1999_v1_WeakSigned1.mpq"), NULL, SFLAG_CREATE_ARCHIVE | SFLAG_MODIFY_ARCHIVE | SFLAG_SIGN_ARCHIVE | SFLAG_VERIFY_AFTER}, -}; + // Signed archives + {_T("MPQ_1997_v1_Diablo1_STANDARD.SNP"), szSigned1, "5ef18ef9a26b5704d8d46a344d976c89", 2 | TFLG_SIGCHECK_BEFORE}, + {_T("MPQ_1999_v1_WeakSignature.exe"), szSigned2, "c1084033d0bd5f7e2b9b78b600c0bba8", 24 | TFLG_SIGCHECK_BEFORE}, + {_T("MPQ_2003_v1_WeakSignatureEmpty.exe"), szSigned3, "97580f9f6d98ffc50191c2f07773e818", 12259 | TFLG_SIGCHECK_BEFORE}, + {_T("MPQ_2002_v1_StrongSignature.w3m"), szSigned4, "7b725d87e07a2173c42fe2314b95fa6c", 17 | TFLG_SIGCHECK_BEFORE}, + {_T("MPQ_1998_v1_StarDat.mpq"), _T("MPQ_1998_v1_StarDat.mpq"), "2530cb937565fd41b1dc0443697096a2", 2925 | TFLG_SIGN_ARCHIVE | TFLG_SIGCHECK_AFTER}, + {_T("MPQ_1999_v1_WeakSignature.exe"), szSigned2, "807fe2e4d38eccf5ee6bc88f5ee5940d", 25 | TFLG_SIGCHECK_BEFORE | TFLG_MODIFY | TFLG_SIGCHECK_AFTER}, -static const TEST_INFO Test_MiscTests[] = -{ // Multi-file archive with wrong prefix to see how StormLib deals with it - {_T("flat-file://streaming/model.MPQ.0"), _T("flat-file://model.MPQ.0"), MFLAG_WILL_FAIL}, - + {_T("flat-file://streaming/model.MPQ.0"), _T("flat-file://model.MPQ.0"), NULL, 0 | TFLG_WILL_FAIL}, + // An archive that has been invalidated by extending an old valid MPQ - {_T("MPQ_2013_vX_Battle.net.MPQ"), NULL, MFLAG_WILL_FAIL}, + {_T("MPQ_2013_vX_Battle.net.MPQ"), NULL, NULL, 0 | TFLG_WILL_FAIL}, - // Check for a read-only archive - {_T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"), NULL, MFLAG_OPEN_READ_ONLY}, - {_T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"), NULL, 0}, + // Check whether read-only archive fails with update + {_T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"), NULL, "554b538541e42170ed41cb236483489e", 2910 | TFLG_READ_ONLY | TFLG_MODIFY | TFLG_WILL_FAIL}, + {_T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"), szDiabdatMPQ, "84fcb4aefbd0deac5b5159ec11922dbf", 2911 | TFLG_MODIFY}, // Check the GetFileInfo operations - {_T("MPQ_2002_v1_StrongSignature.w3m"), NULL, MFLAG_GET_FILE_INFO}, - {_T("MPQ_2013_v4_SC2_EmptyMap.SC2Map"), NULL, MFLAG_GET_FILE_INFO} + {_T("MPQ_2002_v1_StrongSignature.w3m"), NULL, "7b725d87e07a2173c42fe2314b95fa6c", 17 | TFLG_GET_FILE_INFO}, + {_T("MPQ_2013_v4_SC2_EmptyMap.SC2Map"), NULL, "88e1b9a88d56688c9c24037782b7bb68", 33 | TFLG_GET_FILE_INFO}, }; -static const TEST_INFO Test_CompactArchive[] = +static const TEST_INFO Test_ReopenMpqs[] = { - {_T("MPQ_2010_v3_expansion-locale-frFR.MPQ"), _T("StormLibTest_CraftedMpq1_v3.mpq"), TRUE}, - {_T("MPQ_2016_v1_00000.pak"), _T("MPQ_2016_v1_00000.pak"), FALSE}, - {_T("MPQ_2013_v4_SC2_EmptyMap.SC2Map"), _T("StormLibTest_CraftedMpq2_v4.mpq"), TRUE}, - {_T("MPQ_2013_v4_expansion1.MPQ"), _T("StormLibTest_CraftedMpq3_v4.mpq"), TRUE} -}; + // Test the archive compacting feature + {_T("MPQ_2010_v3_expansion-locale-frFR.MPQ"), NULL, "0c8fc921466f07421a281a05fad08b01", 53 | TFLG_COMPACT | TFLG_ADD_USER_DATA | TFLG_HAS_LISTFILE | TFLG_HAS_ATTRIBUTES}, + {_T("MPQ_2016_v1_00000.pak"), NULL, "76c5c4dffee8a9e3568e22216b5f0b94", 2072 | TFLG_COMPACT | TFLG_HAS_LISTFILE}, + {_T("MPQ_2013_v4_SC2_EmptyMap.SC2Map"), NULL, "88e1b9a88d56688c9c24037782b7bb68", 33 | TFLG_COMPACT | TFLG_ADD_USER_DATA | TFLG_HAS_LISTFILE | TFLG_HAS_ATTRIBUTES}, + {_T("MPQ_2013_v4_expansion1.MPQ"), NULL, "c97d2b4e2561d3eb3a728d72a74d86c2", 15633 | TFLG_COMPACT | TFLG_ADD_USER_DATA | TFLG_HAS_LISTFILE | TFLG_HAS_ATTRIBUTES}, -static const TEST_INFO Test_AddFile[] = -{ // Adding a file to MPQ that had size of the file table equal // or greater than hash table, but has free entries - {_T("MPQ_2014_v1_out1.w3x"), NULL, 0}, - {_T("MPQ_2014_v1_out2.w3x"), NULL, 0}, + {_T("MPQ_2014_v1_out1.w3x"), NULL, "222e685bd76e1af6d267ea1e0c27371f", 39 | TFLG_MODIFY | TFLG_HAS_LISTFILE}, + {_T("MPQ_2014_v1_out2.w3x"), NULL, "514ab40dc72fc29965cc30b2e0d63674", 34 | TFLG_MODIFY | TFLG_HAS_LISTFILE}, // Adding a file to MPQ and testing that (listfile) and (attributes) has the same state like before - {_T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"), NULL, TFLAG_REOPEN}, - {_T("MPQ_2013_v4_SC2_EmptyMap.SC2Map"), NULL, TFLAG_REOPEN | TFLAG_HAS_LISTFILE | TFLAG_HAS_ATTRIBUTES}, + {_T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"), NULL, "554b538541e42170ed41cb236483489e", 2910 | TFLG_MODIFY}, + {_T("MPQ_2013_v4_SC2_EmptyMap.SC2Map"), NULL, "88e1b9a88d56688c9c24037782b7bb68", 33 | TFLG_MODIFY | TFLG_HAS_LISTFILE | TFLG_HAS_ATTRIBUTES} + +}; + +// TODO: Include this in the CreateArchive tests +static const TEST_INFO Test_Signature[] = +{ + {_T("MPQ_1999_v1_WeakSigned1.mpq"), NULL, NULL, SFLAG_CREATE_ARCHIVE | SFLAG_SIGN_AT_CREATE | SFLAG_MODIFY_ARCHIVE | SFLAG_SIGN_ARCHIVE | SFLAG_VERIFY_AFTER}, + {_T("MPQ_1999_v1_WeakSigned1.mpq"), NULL, NULL, SFLAG_CREATE_ARCHIVE | SFLAG_MODIFY_ARCHIVE | SFLAG_SIGN_ARCHIVE | SFLAG_VERIFY_AFTER}, }; //----------------------------------------------------------------------------- @@ -4051,11 +4166,9 @@ static const TEST_INFO Test_AddFile[] = #define TEST_LOCAL_LISTFILE #define TEST_STREAM_OPERATIONS #define TEST_MASTER_MIRROR -#define TEST_SINGLE_MPQS +#define TEST_OPEN_MPQ +#define TEST_REOPEN_MPQ #define TEST_VERIFY_SIGNATURE -#define TEST_MISC_TESTS -#define TEST_COMPACT_ARCHIVES -#define TEST_ADD_NEW_FILE int _tmain(int argc, TCHAR * argv[]) { @@ -4074,7 +4187,7 @@ int _tmain(int argc, TCHAR * argv[]) // and must be placed in the Test-MPQs folder for(int i = 2; i < argc; i++) { - TestArchive(argv[i], Bliz, 0, "Scripts\\War3map.j", NULL); + TestOpenArchive(argv[i], Bliz, 0, "Scripts\\War3map.j", NULL); } #endif // TEST_COMMAND_LINE @@ -4091,7 +4204,7 @@ int _tmain(int argc, TCHAR * argv[]) { for(size_t i = 0; i < _countof(TestList_StreamOps); i++) { - dwErrCode = TestFileStreamOperations(TestList_StreamOps[i].szMpqName1, TestList_StreamOps[i].dwFlags); + dwErrCode = TestFileStreamOperations(TestList_StreamOps[i].szName1, TestList_StreamOps[i].dwFlags); if(dwErrCode != ERROR_SUCCESS) break; } @@ -4103,8 +4216,8 @@ int _tmain(int argc, TCHAR * argv[]) { for(size_t i = 0; i < _countof(TestList_MasterMirror); i++) { - dwErrCode = TestReadFile_MasterMirror(TestList_MasterMirror[i].szMpqName1, - TestList_MasterMirror[i].szMpqName2, + dwErrCode = TestReadFile_MasterMirror(TestList_MasterMirror[i].szName1, + TestList_MasterMirror[i].szName2, TestList_MasterMirror[i].dwFlags != 0); if(dwErrCode != ERROR_SUCCESS) break; @@ -4112,77 +4225,48 @@ int _tmain(int argc, TCHAR * argv[]) } #endif // TEST_MASTER_MIRROR -#ifdef TEST_SINGLE_MPQS // Test opening various archives - correct, damaged, protected +#ifdef TEST_OPEN_MPQ // Test opening various archives - correct, damaged, protected if(dwErrCode == ERROR_SUCCESS) { - for(size_t i = 0; i < _countof(Test_Mpqs); i++) + for(size_t i = 0; i < _countof(Test_OpenMpqs); i++) { - dwErrCode = TestArchive(Test_Mpqs[i]); - if(dwErrCode != ERROR_SUCCESS) - break; + dwErrCode = TestOpenArchive(Test_OpenMpqs[i]); + //if(dwErrCode != ERROR_SUCCESS) + // break; } } -#endif // TEST_LOCAL_MPQs +#endif // TEST_OPEN_MPQ -#ifdef TEST_VERIFY_SIGNATURE // Verify digital signatures of the archives +#ifdef TEST_REOPEN_MPQ // Test operations involving reopening the archive if(dwErrCode == ERROR_SUCCESS) { - for(size_t i = 0; i < _countof(Test_Signature); i++) + for(size_t i = 0; i < _countof(Test_ReopenMpqs); i++) { // Ignore the error code here; we want to see results of all opens - dwErrCode = TestOpenArchive_SignatureTest(Test_Signature[i].szMpqName1, - Test_Signature[i].szMpqName2, - Test_Signature[i].dwFlags); - if(dwErrCode != ERROR_SUCCESS) - break; - } - } -#endif - -#ifdef TEST_MISC_TESTS // Miscelanneous tests - if(dwErrCode == ERROR_SUCCESS) - { - for(size_t i = 0; i < _countof(Test_MiscTests); i++) - { - // Ignore the error code here; we want to see results of all opens - dwErrCode = TestOpenArchive_MiscTests(Test_MiscTests[i].szMpqName1, - Test_MiscTests[i].szMpqName2, - Test_MiscTests[i].dwFlags); - if(dwErrCode != ERROR_SUCCESS) - break; + dwErrCode = TestReopenArchive(Test_ReopenMpqs[i].szName1, + Test_ReopenMpqs[i].szDataHash, + Test_ReopenMpqs[i].dwFlags); + //if(dwErrCode != ERROR_SUCCESS) + // break; } } #endif -#ifdef TEST_COMPACT_ARCHIVES // Compact archives and verify them after +#ifdef TEST_VERIFY_SIGNATURE // Verify digital signatures of the archives if(dwErrCode == ERROR_SUCCESS) { - for(size_t i = 0; i < _countof(Test_CompactArchive); i++) + for(size_t i = 0; i < _countof(Test_Signature); i++) { // Ignore the error code here; we want to see results of all opens - dwErrCode = TestOpenArchive_CompactArchive(Test_CompactArchive[i].szMpqName1, - Test_CompactArchive[i].szMpqName2, - Test_CompactArchive[i].dwFlags); + dwErrCode = TestOpenArchive_SignatureTest(Test_Signature[i].szName1, + Test_Signature[i].szName1, + Test_Signature[i].dwFlags); if(dwErrCode != ERROR_SUCCESS) break; } } #endif -#ifdef TEST_ADD_NEW_FILE // Test adding new file to existing archive - if(dwErrCode == ERROR_SUCCESS) - { - for(size_t i = 0; i < _countof(Test_AddFile); i++) - { - // Ignore the error code here; we want to see results of all opens - dwErrCode = TestOpenArchive_AddFile(Test_AddFile[i].szMpqName1, - Test_AddFile[i].dwFlags); - if(dwErrCode != ERROR_SUCCESS) - break; - } - } -#endif // TEST_ADD_NEW_FILE - // Verify SHA1 of each MPQ that we have in the list if(dwErrCode == ERROR_SUCCESS) dwErrCode = FindFiles(ForEachFile_VerifyFileChecksum, szMpqSubDir); diff --git a/test/stormlib-test-001.txt b/test/stormlib-test-001.txt index 8165ba8..70d5325 100644 --- a/test/stormlib-test-001.txt +++ b/test/stormlib-test-001.txt @@ -1,7 +1,7 @@ Microsoft Windows [Version 10.0.19045.2486] (c) Microsoft Corporation. All rights reserved. -E:\Ladik\AppDir\StormLib\bin\StormLib_test\x64\Release>StormLib_test.exe +E:\Ladik\Appdir\StormLib\bin\StormLib_test\x64\Release>StormLib_test.exe ==== Test Suite for StormLib version 9.24 ==== InitWorkDir: Work directory \Multimedia\MPQs (default) LocalListFile (FLAT-MAP:ListFile_Blizzard.txt) succeeded. @@ -90,30 +90,28 @@ TestMpq (MPQ_2013_v4_Base3.SC2Maps) succeeded. TestMpq (MPQ_2013_v4_Mods#Liberty.SC2Mod#enGB.SC2Data) succeeded. TestMpq (MPQ_2014_v4_base-Win.MPQ) succeeded. TestMpq (MPQ_2014_v4_base-Win.MPQ) succeeded. -SignatureTest (MPQ_1997_v1_Diablo1_STANDARD.SNP) succeeded. -SignatureTest (MPQ_1997_v1_Diablo1_STANDARD.SNP) succeeded. -SignatureTest (MPQ_1999_v1_WeakSignature.exe) succeeded. -SignatureTest (MPQ_2003_v1_WeakSignatureEmpty.exe) succeeded. -SignatureTest (MPQ_2002_v1_StrongSignature.w3m) succeeded. -SignatureTest (MPQ_1998_v1_StarDat.mpq) succeeded. -SignatureTest (MPQ_1999_v1_WeakSignature.exe) succeeded. +TestMpq (MPQ_1997_v1_Diablo1_STANDARD.SNP) succeeded. +TestMpq (MPQ_1999_v1_WeakSignature.exe) succeeded. +TestMpq (MPQ_2003_v1_WeakSignatureEmpty.exe) succeeded. +TestMpq (MPQ_2002_v1_StrongSignature.w3m) succeeded. +TestMpq (MPQ_1998_v1_StarDat.mpq) succeeded. +TestMpq (MPQ_1999_v1_WeakSignature.exe) succeeded. +TestMpq (flat-file://streaming/model.MPQ.0) succeeded. +TestMpq (MPQ_2013_vX_Battle.net.MPQ) succeeded. +TestMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. +TestMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. +TestMpq (MPQ_2002_v1_StrongSignature.w3m) succeeded. +TestMpq (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded. +ReopenMpqTest (MPQ_2010_v3_expansion-locale-frFR.MPQ) succeeded. +ReopenMpqTest (MPQ_2016_v1_00000.pak) succeeded. +ReopenMpqTest (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded. +ReopenMpqTest (MPQ_2013_v4_expansion1.MPQ) succeeded. +ReopenMpqTest (MPQ_2014_v1_out1.w3x) succeeded. +ReopenMpqTest (MPQ_2014_v1_out2.w3x) succeeded. +ReopenMpqTest (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. +ReopenMpqTest (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded. SignatureTest (MPQ_1999_v1_WeakSigned1.mpq) succeeded. SignatureTest (MPQ_1999_v1_WeakSigned1.mpq) succeeded. -MiscTest (flat-file://streaming/model.MPQ.0) succeeded. -MiscTest (MPQ_2013_vX_Battle.net.MPQ) succeeded. -MiscTest (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. -MiscTest (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. -MiscTest (MPQ_2002_v1_StrongSignature.w3m) succeeded. -MiscTest (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded. -CompactMpqTest (MPQ_2010_v3_expansion-locale-frFR.MPQ) succeeded. -CompactMpqTest: The file "(attributes)" is not present, but it should be -CompactMpqTest: The file "(attributes)" is not present, but it should be -CompactMpqTest (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded. -CompactMpqTest (MPQ_2013_v4_expansion1.MPQ) succeeded. -AddFileToMpqTest (MPQ_2014_v1_out1.w3x) succeeded. -AddFileToMpqTest (MPQ_2014_v1_out2.w3x) succeeded. -AddFileToMpqTest (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. -AddFileToMpqTest (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded. VerifyFileHash succeeded. VerifyFileHash succeeded. VerifyFileHash succeeded. @@ -2369,4 +2367,4 @@ BigMpqTest (StormLibTest_BigArchive_v4.mpq) succeeded. ModifyTest (MPQ_2014_v4_Base.StormReplay) succeeded. ModifyTest (MPQ_2022_v1_v4.329.w3x) succeeded. -E:\Ladik\AppDir\StormLib\bin\StormLib_test\x64\Release> \ No newline at end of file +E:\Ladik\Appdir\StormLib\bin\StormLib_test\x64\Release> \ No newline at end of file -- cgit v1.2.3