summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorLadislav Zezula <ladislav.zezula@avg.com>2013-01-11 14:55:08 +0100
committerLadislav Zezula <ladislav.zezula@avg.com>2013-01-11 14:55:08 +0100
commit3a926f0228c68d7d91cf3946624d7859976440ec (patch)
treec4e7d36dc8157576929988cdfcf5bfd8262cd09c /test
parentdf4b0c085478389c9a21a09521d46735a0109c8a (diff)
Initial creation
Diffstat (limited to 'test')
-rw-r--r--test/Test.cpp2263
1 files changed, 2263 insertions, 0 deletions
diff --git a/test/Test.cpp b/test/Test.cpp
new file mode 100644
index 0000000..3edde98
--- /dev/null
+++ b/test/Test.cpp
@@ -0,0 +1,2263 @@
+/*****************************************************************************/
+/* StormLibTest.cpp Copyright (c) Ladislav Zezula 2003 */
+/*---------------------------------------------------------------------------*/
+/* Test module for StormLib */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 25.03.03 1.00 Lad The first version of StormLibTest.cpp */
+/*****************************************************************************/
+
+#define _CRT_SECURE_NO_DEPRECATE
+#define __INCLUDE_CRYPTOGRAPHY__
+#define __STORMLIB_SELF__ // Don't use StormLib.lib
+#include <stdio.h>
+
+#ifdef _MSC_VER
+#include <crtdbg.h>
+#endif
+
+#include "../src/StormLib.h"
+#include "../src/StormCommon.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4505) // 'XXX' : unreferenced local function has been removed
+#pragma comment(lib, "winmm.lib")
+#endif
+
+#ifndef ERROR_BAD_LENGTH
+#define ERROR_INVALID_DATA 13L
+#define ERROR_BAD_LENGTH 24L
+#endif
+
+//------------------------------------------------------------------------------
+// Defines
+
+#ifdef PLATFORM_WINDOWS
+#define WORK_PATH_ROOT "E:\\Multimedia\\MPQs\\"
+#endif
+
+#ifdef PLATFORM_LINUX
+#define WORK_PATH_ROOT "/home/ladik/MPQs/"
+#endif
+
+#ifdef PLATFORM_MAC
+#define WORK_PATH_ROOT "/Users/sam/StormLib/test"
+#endif
+
+#ifndef LANG_CZECH
+#define LANG_CZECH 0x0405
+#endif
+
+#define MPQ_SECTOR_SIZE 0x1000
+
+#define MAKE_PATH(path) _T(WORK_PATH_ROOT) _T(path)
+#define MAKE_PATHA(path) (WORK_PATH_ROOT path)
+
+//-----------------------------------------------------------------------------
+// Unicode MPQ names
+
+/* Czech */ static const wchar_t szUnicodeName1[] = {0x010C, 0x0065, 0x0073, 0x006B, 0x00FD, _T('.'), _T('m'), _T('p'), _T('q'), 0};
+/* Russian */ static const wchar_t szUnicodeName2[] = {0x0420, 0x0443, 0x0441, 0x0441, 0x043A, 0x0438, 0x0439, _T('.'), _T('m'), _T('p'), _T('q'), 0};
+/* Greece */ static const wchar_t szUnicodeName3[] = {0x03B5, 0x03BB, 0x03BB, 0x03B7, 0x03BD, 0x03B9, 0x03BA, 0x03AC, _T('.'), _T('m'), _T('p'), _T('q'), 0};
+/* Chinese */ static const wchar_t szUnicodeName4[] = {0x65E5, 0x672C, 0x8A9E, _T('.'), _T('m'), _T('p'), _T('q'), 0};
+/* Japanese */ static const wchar_t szUnicodeName5[] = {0x7B80, 0x4F53, 0x4E2D, 0x6587, _T('.'), _T('m'), _T('p'), _T('q'), 0};
+/* Arabic */ static const wchar_t szUnicodeName6[] = {0x0627, 0x0644, 0x0639, 0x0639, 0x0631, 0x0628, 0x064A, 0x0629, _T('.'), _T('m'), _T('p'), _T('q'), 0};
+
+//-----------------------------------------------------------------------------
+// Constants
+
+static const TCHAR * szWorkDir = MAKE_PATH("Work");
+
+static unsigned int AddFlags[] =
+{
+// Compression Encryption Fixed key Single Unit Sector CRC
+ 0 | 0 | 0 | 0 | 0,
+ 0 | MPQ_FILE_ENCRYPTED | 0 | 0 | 0,
+ 0 | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | 0 | 0,
+ 0 | 0 | 0 | MPQ_FILE_SINGLE_UNIT | 0,
+ 0 | MPQ_FILE_ENCRYPTED | 0 | MPQ_FILE_SINGLE_UNIT | 0,
+ 0 | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | MPQ_FILE_SINGLE_UNIT | 0,
+ MPQ_FILE_IMPLODE | 0 | 0 | 0 | 0,
+ MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | 0 | 0 | 0,
+ MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | 0 | 0,
+ MPQ_FILE_IMPLODE | 0 | 0 | MPQ_FILE_SINGLE_UNIT | 0,
+ MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | 0 | MPQ_FILE_SINGLE_UNIT | 0,
+ MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | MPQ_FILE_SINGLE_UNIT | 0,
+ MPQ_FILE_IMPLODE | 0 | 0 | 0 | MPQ_FILE_SECTOR_CRC,
+ MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | 0 | 0 | MPQ_FILE_SECTOR_CRC,
+ MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | 0 | MPQ_FILE_SECTOR_CRC,
+ MPQ_FILE_COMPRESS | 0 | 0 | 0 | 0,
+ MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | 0 | 0 | 0,
+ MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | 0 | 0,
+ MPQ_FILE_COMPRESS | 0 | 0 | MPQ_FILE_SINGLE_UNIT | 0,
+ MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | 0 | MPQ_FILE_SINGLE_UNIT | 0,
+ MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | MPQ_FILE_SINGLE_UNIT | 0,
+ MPQ_FILE_COMPRESS | 0 | 0 | 0 | MPQ_FILE_SECTOR_CRC,
+ MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | 0 | 0 | MPQ_FILE_SECTOR_CRC,
+ MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | 0 | MPQ_FILE_SECTOR_CRC,
+ 0xFFFFFFFF
+};
+
+//-----------------------------------------------------------------------------
+// Local testing functions
+
+static void clreol()
+{
+#ifdef PLATFORM_WINDOWS
+ CONSOLE_SCREEN_BUFFER_INFO ScreenInfo;
+ HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+ LPTSTR szConsoleLine;
+ int nConsoleChars;
+ int i = 0;
+
+ GetConsoleScreenBufferInfo(hConsole, &ScreenInfo);
+ nConsoleChars = (ScreenInfo.srWindow.Right - ScreenInfo.srWindow.Left);
+ if(nConsoleChars > 0)
+ {
+ szConsoleLine = new TCHAR[nConsoleChars + 3];
+ if(szConsoleLine != NULL)
+ {
+ szConsoleLine[i++] = '\r';
+ for(; i < nConsoleChars; i++)
+ szConsoleLine[i] = ' ';
+ szConsoleLine[i++] = '\r';
+ szConsoleLine[i] = 0;
+
+ _tprintf(szConsoleLine);
+ delete [] szConsoleLine;
+ }
+ }
+#endif // PLATFORM_WINDOWS
+}
+
+static void PrintfTA(const TCHAR * szFormat, const TCHAR * szStrT, const char * szStrA, int lcLocale = 0)
+{
+ TCHAR * szTemp;
+ TCHAR szBuffer[MAX_PATH];
+
+ // Convert ANSI string to TCHAR
+ for(szTemp = szBuffer; *szStrA != 0; szTemp++, szStrA++)
+ szTemp[0] = szStrA[0];
+ szTemp[0] = 0;
+
+ _tprintf(szFormat, szStrT, szBuffer, lcLocale);
+}
+
+static void MergeLocalPath(TCHAR * szBuffer, const TCHAR * szPart1, const char * szPart2)
+{
+ // Copy directory name
+ while(*szPart1 != 0)
+ *szBuffer++ = *szPart1++;
+
+ // Add separator
+ *szBuffer++ = _T('/');
+
+ // Copy file name
+ while(*szPart2 != 0)
+ *szBuffer++ = *szPart2++;
+
+ // Terminate the string
+ *szBuffer = 0;
+}
+
+int GetFirstDiffer(void * ptr1, void * ptr2, int nSize)
+{
+ char * buff1 = (char *)ptr1;
+ char * buff2 = (char *)ptr2;
+ int nDiffer;
+
+ for(nDiffer = 0; nDiffer < nSize; nDiffer++)
+ {
+ if(*buff1++ != *buff2++)
+ return nDiffer;
+ }
+ return -1;
+}
+
+static void WINAPI CompactCB(void * /* lpParam */, DWORD dwWork, ULONGLONG BytesDone, ULONGLONG TotalBytes)
+{
+ clreol();
+
+ _tprintf(_T("%u of %u "), (DWORD)BytesDone, (DWORD)TotalBytes);
+ switch(dwWork)
+ {
+ case CCB_CHECKING_FILES:
+ _tprintf(_T("Checking files in archive ...\r"));
+ break;
+
+ case CCB_CHECKING_HASH_TABLE:
+ _tprintf(_T("Checking hash table ...\r"));
+ break;
+
+ case CCB_COPYING_NON_MPQ_DATA:
+ _tprintf(_T("Copying non-MPQ data ...\r"));
+ break;
+
+ case CCB_COMPACTING_FILES:
+ _tprintf(_T("Compacting archive ...\r"));
+ break;
+
+ case CCB_CLOSING_ARCHIVE:
+ _tprintf(_T("Closing archive ...\r"));
+ break;
+ }
+}
+
+static void GenerateRandomDataBlock(LPBYTE pbBuffer, DWORD cbBuffer)
+{
+ LPBYTE pbBufferEnd = pbBuffer + cbBuffer;
+ LPBYTE pbPtr = pbBuffer;
+ DWORD cbBytesToPut = 0;
+ BYTE ByteToPut = 0;
+ bool bRandomData = false;
+
+ while(pbPtr < pbBufferEnd)
+ {
+ // If there are no bytes to put, we will generate new byte and length
+ if(cbBytesToPut == 0)
+ {
+ bRandomData = false;
+ switch(rand() % 10)
+ {
+ case 0: // A short sequence of zeros
+ cbBytesToPut = rand() % 0x08;
+ ByteToPut = 0;
+ break;
+
+ case 1: // A long sequence of zeros
+ cbBytesToPut = rand() % 0x80;
+ ByteToPut = 0;
+ break;
+
+ case 2: // A short sequence of non-zeros
+ cbBytesToPut = rand() % 0x08;
+ ByteToPut = (BYTE)(rand() % 0x100);
+ break;
+
+ case 3: // A long sequence of non-zeros
+ cbBytesToPut = rand() % 0x80;
+ ByteToPut = (BYTE)(rand() % 0x100);
+ break;
+
+ case 4: // A short random data
+ cbBytesToPut = rand() % 0x08;
+ bRandomData = true;
+ break;
+
+ case 5: // A long random data
+ cbBytesToPut = rand() % 0x80;
+ bRandomData = true;
+ break;
+
+ default: // A single random byte
+ cbBytesToPut = 1;
+ ByteToPut = (BYTE)(rand() % 0x100);
+ break;
+ }
+ }
+
+ // Generate random byte, if needed
+ if(bRandomData)
+ ByteToPut = (BYTE)(rand() % 0x100);
+
+ // Put next byte to the output buffer
+ *pbPtr++ = ByteToPut;
+ cbBytesToPut--;
+ }
+}
+
+static bool CompareArchivedFiles(const char * szFileName, HANDLE hFile1, HANDLE hFile2, DWORD dwBlockSize)
+{
+ LPBYTE pbBuffer1 = NULL;
+ LPBYTE pbBuffer2 = NULL;
+ DWORD dwRead1; // Number of bytes read (Storm.dll)
+ DWORD dwRead2; // Number of bytes read (StormLib)
+ bool bResult1 = false; // Result from Storm.dll
+ bool bResult2 = false; // Result from StormLib
+ bool bResult = true;
+ int nDiff;
+
+ szFileName = szFileName;
+
+ // Allocate buffers
+ pbBuffer1 = new BYTE[dwBlockSize];
+ pbBuffer2 = new BYTE[dwBlockSize];
+
+ for(;;)
+ {
+ // Read the file's content by both methods and compare the result
+ memset(pbBuffer1, 0, dwBlockSize);
+ memset(pbBuffer2, 0, dwBlockSize);
+ bResult1 = SFileReadFile(hFile1, pbBuffer1, dwBlockSize, &dwRead1, NULL);
+ bResult2 = SFileReadFile(hFile2, pbBuffer2, dwBlockSize, &dwRead2, NULL);
+ if(bResult1 != bResult2)
+ {
+ _tprintf(_T("Different results from SFileReadFile, Mpq1 %u, Mpq2 %u\n"), bResult1, bResult2);
+ bResult = false;
+ break;
+ }
+
+ // Test the number of bytes read
+ if(dwRead1 != dwRead2)
+ {
+ _tprintf(_T("Different bytes read from SFileReadFile, Mpq1 %u, Mpq2 %u\n"), dwRead1, dwRead2);
+ bResult = false;
+ break;
+ }
+
+ // No more bytes ==> OK
+ if(dwRead1 == 0)
+ break;
+
+ // Test the content
+ if((nDiff = GetFirstDiffer(pbBuffer1, pbBuffer2, dwRead1)) != -1)
+ {
+ bResult = false;
+ break;
+ }
+ }
+
+ delete [] pbBuffer2;
+ delete [] pbBuffer1;
+ return bResult;
+}
+
+// Random read version
+static bool CompareArchivedFilesRR(const char * /* szFileName */, HANDLE hFile1, HANDLE hFile2, DWORD dwBlockSize)
+{
+ const char * szPositions[3] = {"FILE_BEGIN ", "FILE_CURRENT", "FILE_END "};
+ LPBYTE pbBuffer1 = NULL;
+ LPBYTE pbBuffer2 = NULL;
+ DWORD dwFileSize1; // File size (Storm.dll)
+ DWORD dwFileSize2; // File size (StormLib)
+ DWORD dwRead1; // Number of bytes read (Storm.dll)
+ DWORD dwRead2; // Number of bytes read (StormLib)
+ bool bResult1 = false; // Result from Storm.dll
+ bool bResult2 = false; // Result from StormLib
+ int nError = ERROR_SUCCESS;
+
+ // Test the file size
+ dwFileSize1 = SFileGetFileSize(hFile1, NULL);
+ dwFileSize2 = SFileGetFileSize(hFile2, NULL);
+ if(dwFileSize1 != dwFileSize2)
+ {
+ _tprintf(_T("Different size from SFileGetFileSize (file1: %u, file2: %u)\n"), dwFileSize1, dwFileSize2);
+ return false;
+ }
+
+ if(dwFileSize1 != 0)
+ {
+ for(int i = 0; i < 10000; i++)
+ {
+ DWORD dwRandom = rand() * rand();
+ DWORD dwMoveMethod = dwRandom % 3;
+ DWORD dwPosition = dwRandom % dwFileSize1;
+ DWORD dwToRead = dwRandom % dwBlockSize;
+
+ // Also test negative seek
+ if(rand() & 1)
+ {
+ int nPosition = (int)dwPosition;
+ dwPosition = (DWORD)(-nPosition);
+ }
+
+ // Allocate buffers
+ pbBuffer1 = new BYTE[dwToRead];
+ pbBuffer2 = new BYTE[dwToRead];
+
+ // Set the file pointer
+ _tprintf(_T("RndRead (%u): pos %8i from %s, size %u ...\r"), i, dwPosition, szPositions[dwMoveMethod], dwToRead);
+ dwRead1 = SFileSetFilePointer(hFile1, dwPosition, NULL, dwMoveMethod);
+ dwRead2 = SFileSetFilePointer(hFile2, dwPosition, NULL, dwMoveMethod);
+ if(dwRead1 != dwRead2)
+ {
+ _tprintf(_T("Difference returned by SFileSetFilePointer (file1: %u, file2: %u)\n"), dwRead1, dwRead2);
+ nError = ERROR_CAN_NOT_COMPLETE;
+ break;
+ }
+
+ // Read the file's content by both methods and compare the result
+ bResult1 = SFileReadFile(hFile1, pbBuffer1, dwToRead, &dwRead1, NULL);
+ bResult2 = SFileReadFile(hFile2, pbBuffer2, dwToRead, &dwRead2, NULL);
+ if(bResult1 != bResult2)
+ {
+ _tprintf(_T("Different results from SFileReadFile (file1: %u, file2: %u)\n\n"), bResult1, bResult2);
+ nError = ERROR_CAN_NOT_COMPLETE;
+ break;
+ }
+
+ // Test the number of bytes read
+ if(dwRead1 != dwRead2)
+ {
+ _tprintf(_T("Different bytes read from SFileReadFile (file1: %u, file2: %u)\n\n"), dwRead1, dwRead2);
+ nError = ERROR_CAN_NOT_COMPLETE;
+ break;
+ }
+
+ // Test the content
+ if(dwRead1 != 0 && memcmp(pbBuffer1, pbBuffer2, dwRead1))
+ {
+ _tprintf(_T("Different data content from SFileReadFile\n"));
+ nError = ERROR_CAN_NOT_COMPLETE;
+ break;
+ }
+
+ delete [] pbBuffer2;
+ delete [] pbBuffer1;
+ }
+ }
+ clreol();
+ return (nError == ERROR_SUCCESS) ? true : false;
+}
+
+//-----------------------------------------------------------------------------
+// Opening local file
+
+static int TestOpenLocalFile(const char * szFileName)
+{
+ HANDLE hFile;
+ char szRetrievedName[MAX_PATH];
+
+ if(SFileOpenFileEx(NULL, szFileName, SFILE_OPEN_LOCAL_FILE, &hFile))
+ {
+ SFileGetFileName(hFile, szRetrievedName);
+ SFileCloseFile(hFile);
+ }
+
+ return ERROR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// Partial file reading
+
+static int TestPartFileRead(const TCHAR * szFileName)
+{
+ ULONGLONG ByteOffset;
+ ULONGLONG FileSize = 0;
+ TFileStream * pStream;
+ BYTE BigBuffer[0x7000];
+ BYTE Buffer[0x100];
+ int nError = ERROR_SUCCESS;
+
+ // Open the partial file
+ pStream = FileStream_OpenFile(szFileName, false);
+ if(pStream == NULL)
+ nError = GetLastError();
+
+ // Get the size of the stream
+ if(nError == ERROR_SUCCESS)
+ {
+ if(!FileStream_GetSize(pStream, &FileSize))
+ nError = GetLastError();
+ }
+
+ // Read the last 0x7000 bytes
+ if(nError == ERROR_SUCCESS)
+ {
+ ByteOffset = FileSize - sizeof(BigBuffer);
+ if(!FileStream_Read(pStream, &ByteOffset, BigBuffer, sizeof(BigBuffer)))
+ nError = GetLastError();
+ }
+
+ // Read the last 0x100 bytes
+ if(nError == ERROR_SUCCESS)
+ {
+ ByteOffset = FileSize - sizeof(Buffer);
+ if(!FileStream_Read(pStream, &ByteOffset, Buffer, sizeof(Buffer)))
+ nError = GetLastError();
+ }
+
+ // Read 0x100 bytes from position (FileSize - 0xFF)
+ if(nError == ERROR_SUCCESS)
+ {
+ ByteOffset = FileSize - sizeof(Buffer) + 1;
+ if(!FileStream_Read(pStream, &ByteOffset, Buffer, sizeof(Buffer)))
+ nError = GetLastError();
+ }
+
+ FileStream_Close(pStream);
+ return nError;
+}
+
+//-----------------------------------------------------------------------------
+// Compare Huffmann decompression
+
+struct TFileData
+{
+ DWORD dwFileSize;
+ BYTE FileData[1];
+};
+
+static TFileData * ReadFileContent(const TCHAR * szMpqName, const char * szFileName)
+{
+ TFileData * pFileData = NULL;
+ HANDLE hMpq = NULL;
+ HANDLE hFile = NULL;
+ DWORD dwSearchScope = (szMpqName == NULL) ? SFILE_OPEN_LOCAL_FILE : SFILE_OPEN_FROM_MPQ;
+ DWORD dwBytesRead = 0;
+ DWORD dwFileSize = 0;
+
+ // Open the MPQ, if any
+ if(szMpqName != NULL)
+ {
+ if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq))
+ return NULL;
+ }
+
+ // Open the file
+ if(SFileOpenFileEx(hMpq, szFileName, dwSearchScope, &hFile))
+ {
+ dwFileSize = SFileGetFileSize(hFile, NULL);
+ pFileData = (TFileData *)(new BYTE[sizeof(TFileData) + dwFileSize]);
+ if(pFileData != NULL)
+ {
+ SFileReadFile(hFile, pFileData->FileData, dwFileSize, &dwBytesRead, NULL);
+ pFileData->dwFileSize = dwFileSize;
+ }
+ SFileCloseFile(hFile);
+ }
+
+ if(hMpq != NULL)
+ SFileCloseArchive(hMpq);
+ return pFileData;
+}
+
+static TFileData * CompressFileContent(TFileData * pDecompressed, BYTE Compression1, BYTE Compression2)
+{
+ TFileData * pRecompressed;
+ LPBYTE pbOutBuffer;
+ LPBYTE pbInBuffer;
+ DWORD dwBytesRemaining;
+ int cbOutBuffer;
+ int cbInBuffer;
+
+ // Allocate buffer for compressed data
+ pRecompressed = (TFileData *)(new BYTE[sizeof(TFileData) + pDecompressed->dwFileSize]);
+ if(pRecompressed != NULL)
+ {
+ // Set the source and target
+ dwBytesRemaining = pDecompressed->dwFileSize;
+ pbInBuffer = pDecompressed->FileData;
+ pbOutBuffer = pRecompressed->FileData;
+
+ while(dwBytesRemaining != 0)
+ {
+ // Perform the compression
+ cbOutBuffer = cbInBuffer = (int)STORMLIB_MIN(dwBytesRemaining, 0x1000);
+ SCompCompress((char *)pbOutBuffer, &cbOutBuffer, (char *)pbInBuffer, cbInBuffer, Compression1, 0x00, 0);
+ assert(cbOutBuffer < cbInBuffer);
+
+ // Move buffers
+ dwBytesRemaining -= cbInBuffer;
+ pbOutBuffer += cbOutBuffer;
+ pbInBuffer += cbInBuffer;
+ Compression1 = Compression2;
+ }
+
+ // Put the size of the decompressed part
+ pRecompressed->dwFileSize = (DWORD)(pbOutBuffer - pRecompressed->FileData);
+ }
+
+ return pRecompressed;
+}
+
+static int WriteFileContent(const TCHAR * szFileName, TFileData * pFileData)
+{
+ TFileStream * pLocalFile = NULL;
+ int nError = ERROR_SUCCESS;
+
+ // Create the local file
+ if(nError == ERROR_SUCCESS)
+ {
+ pLocalFile = FileStream_CreateFile(szFileName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
+ if(pLocalFile == NULL)
+ nError = GetLastError();
+ }
+
+ // Write the file data
+ if(nError == ERROR_SUCCESS)
+ {
+ if(!FileStream_Write(pLocalFile, NULL, pFileData->FileData, pFileData->dwFileSize))
+ nError = GetLastError();
+ }
+
+ // Close handles and return
+ if(pLocalFile != NULL)
+ FileStream_Close(pLocalFile);
+ return nError;
+}
+
+static int CompareHuffmanCompressions7()
+{
+ TFileData * pDecompressed1 = NULL;
+ TFileData * pDecompressed2 = NULL;
+ TFileData * pRecompressed1 = NULL;
+ TFileData * pRecompressed2 = NULL;
+ int nDifference;
+ int nError = ERROR_SUCCESS;
+
+ // Load the decompressed data
+ if(nError == ERROR_SUCCESS)
+ {
+ pDecompressed1 = ReadFileContent(MAKE_PATH("BroodWar.mpq"), "music\\prdyroom.wav");
+ pDecompressed2 = ReadFileContent(NULL, MAKE_PATHA("mpq_decompressed.wav"));
+ if(pDecompressed1 != NULL && pDecompressed2 != NULL)
+ {
+ // Compare decompressed size
+ if(pDecompressed1->dwFileSize == pDecompressed2->dwFileSize)
+ {
+ // Compare the data
+ nDifference = GetFirstDiffer(pDecompressed1->FileData, pDecompressed2->FileData, pDecompressed1->dwFileSize);
+ if(nDifference != -1)
+ {
+ _tprintf(_T("Different decompressed data at offset %08X\n\n"), nDifference);
+ nError = ERROR_BAD_FORMAT;
+ }
+ else
+ {
+ _tprintf(_T("Huffmann decompression OK\n"));
+ }
+ }
+ else
+ {
+ _tprintf(_T("Different decompressed size: %u <-> %u\n"), pDecompressed1->dwFileSize, pDecompressed2->dwFileSize);
+ nError = ERROR_BAD_LENGTH;
+ }
+ }
+ else
+ {
+ _tprintf(_T("Failed to real one of the compressed files.\n"));
+ nError = ERROR_CAN_NOT_COMPLETE;
+ }
+ }
+
+ // Get the recompressed data
+ if(nError == ERROR_SUCCESS)
+ {
+ pRecompressed1 = CompressFileContent(pDecompressed1, MPQ_COMPRESSION_PKWARE, MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_HUFFMANN);
+ pRecompressed2 = ReadFileContent(NULL, MAKE_PATHA("mpq_recompressed.bin"));
+ if(pRecompressed1 != NULL && pRecompressed2 != NULL)
+ {
+ // Comprare decompressed size
+ if(pRecompressed1->dwFileSize == pRecompressed2->dwFileSize)
+ {
+ // Compare the data
+ nDifference = GetFirstDiffer(pRecompressed1->FileData, pRecompressed2->FileData, pRecompressed1->dwFileSize);
+ if(nDifference != -1)
+ {
+ _tprintf(_T("Different recompressed data at offset %08X\n"), nDifference);
+ nError = ERROR_BAD_FORMAT;
+ }
+ else
+ {
+ _tprintf(_T("Huffmann recompression OK\n"));
+ }
+ }
+ else
+ {
+ _tprintf(_T("Different recompressed size: %u <-> %u\n"), pRecompressed1->dwFileSize, pRecompressed2->dwFileSize);
+ nError = ERROR_BAD_LENGTH;
+ }
+ }
+ else
+ {
+ _tprintf(_T("Failed to compress the file or read the compressed muster.\n"));
+ nError = ERROR_CAN_NOT_COMPLETE;
+ }
+ }
+
+ assert(nError == ERROR_SUCCESS);
+
+ if(pRecompressed1 != NULL)
+ delete [] pRecompressed1;
+ if(pRecompressed2 != NULL)
+ delete [] pRecompressed2;
+ if(pDecompressed1 != NULL)
+ delete [] pDecompressed1;
+ if(pDecompressed2 != NULL)
+ delete [] pDecompressed2;
+ return ERROR_SUCCESS;
+}
+
+// Just some binary data
+static unsigned char Decompressed[2048] =
+{
+ 0x2E, 0x56, 0xF8, 0x44, 0xB0, 0xE2, 0x58, 0x20, 0x44, 0xBC, 0xE2, 0x46, 0x00, 0x44, 0x4A, 0x1D,
+ 0x59, 0x44, 0xC4, 0xE3, 0x00, 0x00, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x34, 0xE2, 0x01, 0x01,
+ 0x01, 0x6C, 0x44, 0xB4, 0xE2, 0x58, 0x01, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x00, 0xB4, 0xE2, 0x58,
+ 0x44, 0xB4, 0x02, 0x02, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x00, 0x44, 0xB4, 0xE2, 0x40, 0x58,
+ 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x02, 0x02, 0xE2, 0x58, 0x44, 0x04, 0xB4, 0xE0, 0x58, 0x40,
+ 0x44, 0xC0, 0xC3, 0x58, 0x58, 0x08, 0x8B, 0x8A, 0x4B, 0xDE, 0xF6, 0x80, 0x92, 0x5B, 0xDC, 0x2F,
+ 0xA1, 0x78, 0x10, 0x10, 0x65, 0x1C, 0x32, 0x8A, 0xA2, 0xF4, 0x02, 0xBC, 0x02, 0x02, 0x9A, 0x7A,
+ 0x06, 0x9A, 0x9E, 0x01, 0xF4, 0x38, 0x20, 0xB2, 0xA2, 0x5C, 0xA2, 0x01, 0x9C, 0x8E, 0xF4, 0x06,
+ 0xB2, 0x98, 0x80, 0x80, 0xF4, 0x08, 0x84, 0x8C, 0x7E, 0x40, 0x06, 0x18, 0xEA, 0x66, 0x10, 0x3E,
+ 0x3E, 0x08, 0x20, 0xAE, 0xF6, 0x10, 0x2A, 0xB4, 0x10, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44,
+ 0x00, 0x00, 0xB4, 0xE2, 0x58, 0x44, 0x04, 0xB4, 0xE2, 0x58, 0x80, 0x44, 0xB4, 0xE2, 0x58, 0x44,
+ 0x80, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x10, 0x10, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x40, 0x58, 0x44,
+ 0x08, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x80, 0x80, 0x58, 0x44, 0xB4,
+ 0xE2, 0x58, 0x44, 0xB4, 0x02, 0x02, 0xE2, 0x58, 0x00, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2,
+ 0x00, 0x00, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x00, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58,
+ 0x44, 0xB4, 0x01, 0x01, 0x01, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x01, 0x58, 0x44, 0xB4, 0xE2, 0x58,
+ 0x44, 0xB4, 0xE2, 0x58, 0x02, 0x02, 0x44, 0xB4, 0xE2, 0x20, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44,
+ 0x01, 0x01, 0xB4, 0xE2, 0x58, 0x44, 0x40, 0xB4, 0xE2, 0x58, 0x44, 0x40, 0xB4, 0xE2, 0x58, 0x08,
+ 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x08, 0x08, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4,
+ 0x40, 0x40, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x00, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2,
+ 0x01, 0x01, 0x58, 0x44, 0x14, 0x68, 0x58, 0x44, 0x2C, 0xE0, 0x5C, 0x01, 0x01, 0x01, 0x44, 0x20,
+ 0x85, 0x3B, 0xD3, 0xB4, 0xE2, 0x02, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x20, 0x20, 0x44, 0x75, 0xE3,
+ 0x44, 0x08, 0x47, 0xA2, 0xE0, 0x5C, 0x76, 0xB4, 0x00, 0xBA, 0x7E, 0x44, 0x40, 0xB4, 0xB2, 0x4B,
+ 0x44, 0xB4, 0x08, 0x08, 0xE2, 0x58, 0x44, 0xB4, 0xC2, 0x58, 0x01, 0x44, 0xB4, 0xC2, 0x80, 0x58,
+ 0x44, 0xB4, 0x62, 0x10, 0x7E, 0x44, 0xB4, 0xE2, 0xD8, 0x10, 0x44, 0xB4, 0x20, 0xC2, 0x58, 0x44,
+ 0xB4, 0xE6, 0x58, 0x80, 0x80, 0x44, 0xBE, 0xE2, 0x58, 0x44, 0x04, 0xB4, 0xE2, 0x58, 0x44, 0xBE,
+ 0xE2, 0x58, 0x44, 0x10, 0x10, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x04, 0x82, 0x63, 0x44, 0xB4, 0xEA,
+ 0x58, 0x80, 0x80, 0x44, 0xAE, 0x6E, 0x42, 0x10, 0x44, 0xB0, 0xE2, 0x58, 0x44, 0x01, 0x01, 0xB4,
+ 0xE2, 0x78, 0x44, 0xB4, 0x62, 0x58, 0x04, 0x44, 0xB4, 0xE2, 0x78, 0x44, 0xB4, 0x01, 0x01, 0xC2,
+ 0x58, 0x44, 0xB4, 0xE2, 0x04, 0x58, 0x44, 0x94, 0x02, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44,
+ 0xB4, 0x80, 0x80, 0xE2, 0x58, 0x44, 0x2C, 0xDC, 0x63, 0x44, 0x02, 0x02, 0xDC, 0xE4, 0x58, 0x44,
+ 0xB4, 0x02, 0x02, 0xE2, 0x63, 0x44, 0x64, 0xD8, 0x58, 0x44, 0xB4, 0xE2, 0x04, 0x04, 0x58, 0x44,
+ 0xB4, 0xE2, 0x58, 0x00, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x00, 0x00, 0xE2, 0x58, 0x44, 0x02,
+ 0xB4, 0xE2, 0x58, 0x00, 0x44, 0xB4, 0xE2, 0x58, 0x10, 0x44, 0xB4, 0xE2, 0x58, 0x20, 0x44, 0xB4,
+ 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x00, 0x00, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x80,
+ 0x80, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x2C, 0x01, 0x01, 0xBC, 0x63, 0x44, 0x0C, 0xE2, 0x58,
+ 0x44, 0x20, 0x20, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x08, 0x08, 0x44, 0xB4, 0xE2, 0x58,
+ 0x44, 0xB4, 0x20, 0xE2, 0x58, 0x44, 0x01, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x01, 0xE2, 0x58, 0x44,
+ 0xB4, 0x00, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x00, 0x00, 0x58, 0x44, 0xB4, 0xE2, 0x01, 0x58, 0x44,
+ 0xB4, 0xE2, 0x80, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x08, 0x08, 0xE2, 0x58, 0x44, 0xE8,
+ 0x0A, 0x92, 0xB4, 0x04, 0x04, 0x5C, 0xE2, 0x58, 0x44, 0xB4, 0x02, 0x62, 0x44, 0xB4, 0xC2, 0x01,
+ 0x01, 0x58, 0x44, 0xB4, 0xBE, 0x08, 0x41, 0x44, 0xB4, 0xEA, 0x80, 0x80, 0x58, 0x44, 0x14, 0x68,
+ 0xDE, 0x20, 0x1C, 0x7C, 0x20, 0x20, 0x58, 0x44, 0xB4, 0xE2, 0x40, 0x58, 0x44, 0xF5, 0xE2, 0x01,
+ 0x58, 0x84, 0xE8, 0x06, 0xBE, 0xA0, 0x72, 0xE2, 0x02, 0x02, 0x58, 0x44, 0xB4, 0x82, 0x58, 0x44,
+ 0xB4, 0xE2, 0x63, 0x20, 0x20, 0x44, 0xB4, 0x82, 0x58, 0x44, 0xB4, 0x86, 0x02, 0x02, 0x41, 0x44,
+ 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x01, 0x01, 0x58, 0x40, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xF5,
+ 0xE2, 0x58, 0x08, 0x08, 0x84, 0xB4, 0xE2, 0x58, 0x08, 0x44, 0xB4, 0xE2, 0x04, 0x58, 0x44, 0xB4,
+ 0xE2, 0x58, 0x10, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x20, 0x20, 0x44, 0x80, 0xB4,
+ 0xE2, 0x58, 0x44, 0xB4, 0x40, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x80, 0x80, 0xE2,
+ 0x58, 0x44, 0xB4, 0x20, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x20, 0x20, 0xB4, 0xE2, 0x58,
+ 0x08, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x20, 0xB4, 0xE2, 0x20, 0x58, 0x44, 0xB4, 0x04, 0xE2, 0x58,
+ 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x80, 0xB4, 0xE2, 0x10, 0x58, 0x44, 0xB4, 0x00, 0xE2, 0x58, 0x44,
+ 0xB4, 0xE2, 0x58, 0x08, 0x44, 0xB4, 0x40, 0xE2, 0x58, 0x44, 0xB4, 0x08, 0xE2, 0x58, 0x80, 0x44,
+ 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x80, 0x80, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2,
+ 0x04, 0x04, 0x58, 0x44, 0xB4, 0xE2, 0x08, 0x58, 0x44, 0xB4, 0x04, 0xE2, 0x58, 0x44, 0xB4, 0xE2,
+ 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x02, 0x02, 0x02, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x04, 0xE2,
+ 0x58, 0x44, 0xB4, 0xE2, 0x10, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x10, 0x10, 0xB4, 0xE2, 0x58,
+ 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x00, 0x00, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x80, 0x80,
+ 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x40, 0x40, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4,
+ 0x80, 0x80, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x20, 0x44, 0xB4, 0xE2, 0x01, 0x58, 0x44, 0x40,
+ 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x08, 0x08, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x40, 0xB4,
+ 0xE2, 0x58, 0x44, 0x01, 0xB4, 0xE2, 0x58, 0x44, 0x80, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58,
+ 0x04, 0x04, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x04, 0x04, 0x58, 0x44, 0xB4, 0x02, 0xE2,
+ 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x04, 0x04, 0xB4, 0xE2, 0x58, 0x44, 0x20, 0xB4, 0xE2, 0x58,
+ 0x44, 0x20, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x20, 0x20, 0xB4, 0xE2, 0x58, 0x44,
+ 0x20, 0xB4, 0xE2, 0x58, 0x44, 0x40, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x08, 0x08,
+ 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x02, 0x02, 0x02, 0xE2, 0x58, 0x44, 0xB4,
+ 0xE2, 0x58, 0x44, 0x01, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x01, 0x01, 0x01, 0xB4,
+ 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x10, 0x10, 0xE2, 0x58, 0x44, 0xB4, 0x01, 0xE2,
+ 0x58, 0x44, 0x10, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x08, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44,
+ 0xB4, 0x01, 0x01, 0xE2, 0x04, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x01, 0x44, 0xB4, 0xE2, 0x58, 0x44,
+ 0xB4, 0xE2, 0x58, 0x08, 0x08, 0x44, 0x02, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4,
+ 0xE2, 0x20, 0x20, 0x58, 0x44, 0xB4, 0x08, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x40, 0x58, 0x44, 0x20,
+ 0xB4, 0xE2, 0x58, 0x20, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x08, 0x08, 0x58, 0x44, 0xB4,
+ 0xE2, 0x58, 0x01, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x00, 0x00, 0xE2, 0x58, 0x44, 0xB4, 0xE2,
+ 0x58, 0x40, 0x44, 0xB4, 0x20, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0x10, 0x10, 0xE2,
+ 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x10, 0x10, 0xB4, 0xE2, 0x58, 0x44, 0x01, 0xB4, 0xE2, 0x58,
+ 0x10, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x02, 0x02, 0x02, 0x58,
+ 0x44, 0xB4, 0xE2, 0x58, 0x02, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x00, 0xB4, 0xE2, 0x58, 0x44, 0xB4,
+ 0xE2, 0x08, 0x08, 0x58, 0x44, 0x80, 0xB4, 0xE2, 0x58, 0x44, 0xB4, 0xE2, 0x40, 0x58, 0x44, 0xB4,
+ 0xE2, 0x58, 0x44, 0xB4, 0x20, 0x20, 0xE2, 0x58, 0x04, 0x44, 0xB4, 0xE2, 0x58, 0x44, 0x20, 0xB4,
+ 0xE2, 0x58, 0x44, 0xC4, 0xEB, 0x05, 0x10, 0x10, 0xFF, 0xB5, 0x42, 0x90, 0xBA, 0xDE, 0x20, 0xE2,
+ 0x58, 0x44, 0x40, 0xB4, 0x2A, 0x40, 0x4A, 0x0F, 0xB4, 0x80, 0xE2, 0x58, 0x44, 0xD2, 0x40, 0x62,
+ 0x4B, 0x55, 0x14, 0x68, 0x02, 0xDE, 0x9A, 0x00, 0x6E, 0x02, 0x9A, 0x82, 0x5C, 0x86, 0x00, 0x58,
+ 0x62, 0x0B, 0xBA, 0x7F, 0xA9, 0x00, 0x00, 0x00, 0xFF, 0xCC, 0x38, 0x71, 0xD8, 0x90, 0x4C, 0x01,
+ 0x01, 0x27, 0xB2, 0x6B, 0x10, 0x30, 0x43, 0xC4, 0x9B, 0xB1, 0x1E, 0x45, 0x02, 0x02, 0x34, 0xDF,
+ 0x72, 0x36, 0x00, 0xF5, 0xDC, 0xCE, 0x7A, 0x01, 0x93, 0xEF, 0x4F, 0xB0, 0xDA, 0x22, 0xA4, 0xBB,
+ 0x01, 0x01, 0xC5, 0x47, 0x27, 0x50, 0x80, 0x23, 0x7B, 0xC1, 0x5F, 0x00, 0x13, 0x47, 0xC1, 0x52,
+ 0x46, 0xD9, 0xAB, 0xB6, 0x20, 0x20, 0x8F, 0x04, 0xEF, 0xD5, 0x9E, 0x2B, 0x73, 0x5F, 0x91, 0xF4,
+ 0x40, 0x40, 0x9C, 0xB2, 0x9B, 0x76, 0xDE, 0xDF, 0x90, 0x01, 0x01, 0xFC, 0x27, 0x9C, 0x66, 0x17,
+ 0x6B, 0xB1, 0x5E, 0x00, 0x00, 0xEC, 0x8E, 0x0A, 0xEF, 0x1B, 0xFA, 0xE3, 0xF8, 0xDF, 0x08, 0x08,
+ 0x8E, 0x38, 0x77, 0x24, 0x40, 0xCA, 0x10, 0x2B, 0x10, 0x9D, 0xF7, 0x8E, 0x67, 0xD1, 0x20, 0xEF,
+ 0xF2, 0x94, 0x8F, 0xFC, 0x70, 0xF8, 0x3F, 0x01, 0x01, 0x01, 0xF7, 0xBF, 0x6B, 0xD1, 0x50, 0xA5,
+ 0x10, 0x66, 0x6A, 0x6F, 0xB0, 0x69, 0x68, 0x21, 0xAA, 0x01, 0x01, 0x17, 0x00, 0x48, 0x85, 0x6A,
+ 0xBB, 0xAA, 0x51, 0x0F, 0x01, 0xCE, 0xBB, 0x08, 0x08, 0x1C, 0x2D, 0x84, 0x04, 0x4A, 0xBD, 0x01,
+ 0x7E, 0x88, 0x04, 0x04, 0xE4, 0x29, 0x3E, 0x6C, 0xD0, 0x10, 0x49, 0xA7, 0x93, 0x09, 0xA7, 0x52,
+ 0x48, 0x01, 0x01, 0x4A, 0x24, 0xA5, 0x5E, 0xBE, 0x04, 0x0C, 0x77, 0x01, 0x23, 0xE0, 0x5D, 0xFB,
+ 0x02, 0xD0, 0x1F, 0xD2, 0x3A, 0xFC, 0x5F, 0x08, 0x9A, 0xF2, 0x20, 0x77, 0x54, 0x95, 0x22, 0xD2,
+ 0x32, 0x17, 0x9D, 0x02, 0x02, 0x6B, 0xC0, 0xD6, 0x5B, 0xF4, 0xE5, 0x50, 0x00, 0x00, 0x9A, 0xF7,
+ 0xC6, 0xD6, 0x02, 0x5F, 0x8D, 0x71, 0x80, 0xD8, 0xD2, 0x87, 0x40, 0x7C, 0xF3, 0xB2, 0xBC, 0x84,
+ 0x4B, 0x83, 0xCE, 0x80, 0x80, 0xE3, 0x71, 0x89, 0xAF, 0xE2, 0x01, 0xF8, 0x1B, 0x86, 0x66, 0x4F,
+ 0x40, 0x0F, 0x73, 0x4C, 0x4E, 0xF3, 0x0B, 0x01, 0x01, 0x18, 0x7F, 0x04, 0xBD, 0x7D, 0x51, 0x3C,
+ 0xEB, 0x5B, 0x04, 0xC6, 0xFE, 0x7B, 0x9B, 0x2F, 0x86, 0x62, 0x02, 0x02, 0x56, 0x35, 0x20, 0x05,
+ 0xE6, 0x1C, 0x4C, 0x68, 0x01, 0x29, 0xEA, 0xB0, 0xD3, 0xB0, 0xB5, 0xFE, 0x40, 0x40, 0x9D, 0xE0,
+ 0xC4, 0x6B, 0xF8, 0x6C, 0x00, 0x00, 0xE4, 0x74, 0x6A, 0x95, 0x04, 0x7A, 0xDC, 0x8C, 0x1B, 0x02,
+ 0xBB, 0x8C, 0x29, 0x1F, 0x01, 0xC6, 0xA7, 0x9C, 0x9C, 0xC7, 0x02, 0xB8, 0xD1, 0x10, 0x73, 0xA5,
+ 0x95, 0x92, 0xCC, 0xE8, 0x10, 0x67, 0x14, 0x39, 0xB3, 0x22, 0xB2, 0xAB, 0x00, 0x00, 0xF1, 0x5E,
+ 0x56, 0x90, 0x5F, 0x9D, 0xC5, 0x00, 0x00, 0xA4, 0xA3, 0x36, 0x20, 0xAF, 0x4E, 0x5A, 0xB3, 0x96,
+ 0x41, 0xDE, 0x04, 0x04, 0x77, 0x92, 0x16, 0x8D, 0x02, 0x2D, 0xB9, 0xCA, 0xC2, 0x40, 0xEF, 0x88,
+ 0x5D, 0x5F, 0x81, 0x93, 0xF7, 0x5A, 0x08, 0x08, 0xE3, 0xA3, 0x37, 0xE4, 0x10, 0xE1, 0xEA, 0x8B,
+ 0xDE, 0x02, 0xAC, 0x2D, 0xFE, 0x9C, 0x09, 0x05, 0xBF, 0xA4, 0x40, 0x40, 0xF0, 0x02, 0x92, 0xBB,
+ 0x5D, 0x42, 0x9D, 0x83, 0xF5, 0x2C, 0x40, 0x40, 0x3A, 0xE4, 0x52, 0x62, 0x36, 0x53, 0x8E, 0x40,
+ 0x40, 0x71, 0x91, 0xB7, 0x3E, 0xC0, 0x55, 0x88, 0x1C, 0x40, 0x40, 0xEF, 0x3F, 0xB4, 0x70, 0xF8,
+ 0xE9, 0x2A, 0x7A, 0x4D, 0x02, 0x02, 0xC0, 0x9B, 0x1C, 0x05, 0x08, 0xE2, 0x6C, 0x5E, 0x80, 0xDC,
+ 0x2A, 0xC0, 0x91, 0x05, 0x10, 0x10, 0x1B, 0x85, 0x09, 0x47, 0xBC, 0x27, 0x38, 0x02, 0x04, 0x04,
+ 0xCC, 0x7F, 0x55, 0x1A, 0xF0, 0x06, 0x02, 0xDF, 0xC6, 0xF8, 0xB5, 0x18, 0xB5, 0xF1, 0x97, 0x08,
+ 0x08, 0x08, 0x20, 0xF5, 0x1F, 0xCD, 0x02, 0x20, 0x07, 0xC5, 0x98, 0x44, 0x57, 0x21, 0x00, 0x24,
+ 0xBC, 0x78, 0x10, 0xD6, 0xA9, 0xCC, 0xD1, 0xC4, 0x20, 0x20, 0x30, 0x61, 0x51, 0xEC, 0x5F, 0x80,
+ 0x06, 0xAE, 0x5F, 0x6E, 0x32, 0x2E, 0x8B, 0x01, 0x01, 0x8F, 0xC5, 0x9D, 0x4F, 0x5E, 0x00, 0x07,
+ 0x6A, 0x02, 0xF9, 0xF6, 0x9B, 0xA1, 0x20, 0xF6, 0x8B, 0x19, 0xCA, 0x6D, 0x7E, 0x01, 0xBE, 0x7D,
+ 0x40, 0x18, 0x2D, 0xC2, 0xD5, 0xF8, 0x14, 0x83, 0x49, 0x80, 0x80, 0x1A, 0x7E, 0xDC, 0x6F, 0x0F,
+ 0xB1, 0xA4, 0x04, 0x04, 0x13, 0x0A, 0x2E, 0xF0, 0x08, 0x38, 0x74, 0xAB, 0x40, 0x13, 0xBE, 0xC5,
+ 0x20, 0xFD, 0xFB, 0xC5, 0xDD, 0x37, 0xF3, 0x47, 0xB6, 0x10, 0x10, 0xE0, 0x01, 0x7C, 0xB3, 0xE8,
+ 0x80, 0x95, 0x91, 0xF7, 0x00, 0x63, 0x01, 0x01, 0xFE, 0xBA, 0x86, 0xBE, 0xF0, 0xA8, 0x20, 0xBE,
+ 0xA3, 0x20, 0x90, 0x5A, 0x84, 0xB4, 0x9F, 0x63, 0x20, 0xC2, 0x8E, 0x3A, 0x38, 0xFD, 0xB7, 0x1D,
+ 0x20, 0x20, 0x26, 0x02, 0xDA, 0xDA, 0xA5, 0xFC, 0xA3, 0x65, 0x08, 0x88, 0xF6, 0xCD, 0xAF, 0x81,
+ 0x98, 0x90, 0x10, 0x10, 0xED, 0x7D, 0x2B, 0x74, 0xF5, 0x0B, 0x08, 0x08, 0xE8, 0xB3, 0x92, 0x04,
+ 0x7D, 0xE5, 0x7E, 0x36, 0x48, 0x02, 0xC2, 0x0D, 0x93, 0x4C, 0x20, 0x02, 0xC8, 0x73, 0x81, 0xC5
+};
+
+static int CompareHuffmanCompressions0()
+{
+ unsigned char * pbDecompressed = Decompressed;
+ unsigned char * pbCompressed;
+ unsigned char * pbUncompressed;
+ int cbOutLength;
+ int cbInLength;
+ int nLength = sizeof(Decompressed);
+ int nError = ERROR_NOT_ENOUGH_MEMORY;
+
+ // Allocate buffers for compressed buffer
+ pbCompressed = new unsigned char[nLength];
+ if(pbCompressed != NULL)
+ {
+ // Perform the compression
+ cbInLength =
+ cbOutLength = nLength;
+ SCompCompress(pbCompressed, &cbOutLength, pbDecompressed, nLength, MPQ_COMPRESSION_HUFFMANN, 0x00, 0);
+ assert(cbOutLength < cbInLength);
+
+ // Allocate space for decompressed buffer
+ pbUncompressed = new unsigned char[nLength];
+ if(pbUncompressed != NULL)
+ {
+ // Perform the decompression
+ cbInLength = cbOutLength;
+ cbOutLength = nLength;
+ SCompDecompress(pbUncompressed, &cbOutLength, pbCompressed, cbInLength);
+
+ // Compare length
+ if(cbOutLength == nLength)
+ {
+ if(!memcmp(pbUncompressed, pbDecompressed, nLength))
+ {
+ _tprintf(_T("Huffmann compression OK\n"));
+ nError = ERROR_SUCCESS;
+ }
+ else
+ {
+ _tprintf(_T("Error: Decompressed data are not the same like the compressed ones\n"));
+ nError = ERROR_INVALID_DATA;
+ }
+ }
+ else
+ {
+ _tprintf(_T("Error: Length of the uncompressed data is different from original length\n"));
+ nError = ERROR_BAD_LENGTH;
+ }
+
+ delete [] pbUncompressed;
+ }
+
+ delete [] pbCompressed;
+ }
+
+ assert(nError == ERROR_SUCCESS);
+ return ERROR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// Compare PKLIB decompression
+
+BYTE pbCompressed1[] = {0x00, 0x04, 0x00, 0x00, 0x04, 0xF0, 0x1F, 0x7B, 0x01, 0xFF};
+BYTE pbCompressed2[] = {0x00, 0x04, 0x00, 0x00, 0x04, 0xF0, 0x1F, 0x00, 0x00, 0x04, 0xFC, 0x03};
+
+static int ComparePklibCompressions()
+{
+ char Decompressed[0x1000];
+ char Compressed[0x1000];
+ int cbDecompressed = 0x208;
+ int cbCompressed = sizeof(Compressed);
+
+ memset(Decompressed, 0, cbDecompressed);
+ SCompImplode(Compressed, &cbCompressed, Decompressed, cbDecompressed);
+
+ cbDecompressed = sizeof(Decompressed);
+ SCompExplode(Decompressed, &cbDecompressed, Compressed, cbCompressed);
+
+
+ return ERROR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// Compare LZMA decompression
+
+#ifdef PLATFORM_WINDOWS
+typedef void * (*ALLOC_MEMORY)(size_t);
+typedef void (*FREE_MEMORY)(void *);
+typedef int (GIVE_DATA)(void *);
+
+extern "C" int starcraft_decompress_lzma(char * pbInBuffer, int cbInBuffer, char * pbOutBuffer, int cbOutBuffer, int * pcbOutBuffer, ALLOC_MEMORY pfnAllocMemory, FREE_MEMORY pfnFreeMemory);
+extern "C" int starcraft_compress_lzma(char * pbInBuffer, int cbInBuffer, int dummy1, char * pbOutBuffer, int cbOutBuffer, int dummy2, int * pcbOutBuffer, ALLOC_MEMORY pfnAllocMemory, FREE_MEMORY pfnFreeMemory, GIVE_DATA pfnGiveData);
+void Compress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer, int *, int);
+int Decompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer);
+
+extern "C" void * operator_new(size_t sz)
+{
+ return malloc(sz);
+}
+
+void * Memory_Allocate(size_t byte_size)
+{
+ return malloc(byte_size);
+}
+
+void Memory_Free(void * address)
+{
+ if(address != NULL)
+ free(address);
+}
+
+int GiveData(void *)
+{
+ return 0;
+}
+
+static int StarcraftCompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer)
+{
+ return starcraft_compress_lzma(pbInBuffer,
+ cbInBuffer,
+ 0,
+ pbOutBuffer,
+ *pcbOutBuffer,
+ 0,
+ pcbOutBuffer,
+ Memory_Allocate,
+ Memory_Free,
+ GiveData);
+}
+
+static int StarcraftDecompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer)
+{
+ return starcraft_decompress_lzma(pbInBuffer,
+ cbInBuffer,
+ pbOutBuffer,
+ *pcbOutBuffer,
+ pcbOutBuffer,
+ Memory_Allocate,
+ Memory_Free);
+}
+
+static int CompareLzmaCompressions(int nSectorSize)
+{
+ LPBYTE pbCompressed1 = NULL; // Compressed by our code
+ LPBYTE pbCompressed2 = NULL; // Compressed by Blizzard's code
+ LPBYTE pbDecompressed1 = NULL; // Decompressed by our code
+ LPBYTE pbDecompressed2 = NULL; // Decompressed by Blizzard's code
+ LPBYTE pbOriginalData = NULL;
+ int nError = ERROR_SUCCESS;
+
+ // Allocate buffers
+ // Must allocate twice blocks due to probable bug in Storm.dll.
+ // Storm.dll corrupts stack when uncompresses data with PKWARE DCL
+ // and no compression occurs.
+ pbDecompressed1 = new BYTE [nSectorSize];
+ pbDecompressed2 = new BYTE [nSectorSize];
+ pbCompressed1 = new BYTE [nSectorSize];
+ pbCompressed2 = new BYTE [nSectorSize];
+ pbOriginalData = new BYTE[nSectorSize];
+ if(!pbDecompressed1 || !pbDecompressed2 || !pbCompressed1 || !pbCompressed2 || !pbOriginalData)
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+
+ if(nError == ERROR_SUCCESS)
+ {
+ for(int i = 0; i < 100000; i++)
+ {
+ int nDcmpLength1;
+ int nDcmpLength2;
+ int nCmpLength1;
+ int nCmpLength2;
+ int nDiff;
+
+ clreol();
+ _tprintf(_T("Testing compression of sector %u\r"), i + 1);
+
+ // Generate random data sector
+ GenerateRandomDataBlock(pbOriginalData, nSectorSize);
+
+ // Compress the sector by both methods
+ nCmpLength1 = nCmpLength2 = nSectorSize;
+// Compress_LZMA((char *)pbCompressed1, &nCmpLength1, (char *)pbOriginalData, nSectorSize, 0, 0);
+ StarcraftCompress_LZMA((char *)pbCompressed1, &nCmpLength2, (char *)pbOriginalData, nSectorSize);
+
+__TryToDecompress:
+
+ // Only test decompression when the compression actually succeeded
+ if(nCmpLength1 < nSectorSize)
+ {
+ // Decompress both data
+ nDcmpLength2 = nDcmpLength1 = nSectorSize;
+// Decompress_LZMA((char *)pbDecompressed1, &nDcmpLength1, (char *)pbCompressed1, nCmpLength1);
+ StarcraftDecompress_LZMA((char *)pbDecompressed2, &nDcmpLength2, (char *)pbCompressed1, nCmpLength1);
+
+ // Compare the length of the output data
+ if(nDcmpLength1 != nDcmpLength2)
+ {
+ _tprintf(_T("Difference in compressed blocks lengths (%u vs %u)\n"), nDcmpLength1, nDcmpLength2);
+ goto __TryToDecompress;
+ }
+
+ // Compare the output
+ if((nDiff = GetFirstDiffer(pbDecompressed1, pbDecompressed2, nDcmpLength1)) != -1)
+ {
+ _tprintf(_T("Difference in decompressed blocks (offset 0x%08X)\n"), nDiff);
+ goto __TryToDecompress;
+ }
+
+ // Check for data overflow
+ if(pbDecompressed1[nSectorSize] != 0xFD || pbDecompressed1[nSectorSize] != 0xFD)
+ {
+ _tprintf(_T("Damage after decompressed sector !!!\n"));
+ goto __TryToDecompress;
+ }
+
+ // Compare the decompressed data against original data
+ if((nDiff = GetFirstDiffer(pbDecompressed1, pbOriginalData, nDcmpLength1)) != -1)
+ {
+ _tprintf(_T("Difference between original data and decompressed data (offset 0x%08X)\n"), nDiff);
+ goto __TryToDecompress;
+ }
+ }
+ }
+ }
+
+ // Cleanup
+ if(pbOriginalData != NULL)
+ delete [] pbOriginalData;
+ if(pbCompressed2 != NULL)
+ delete [] pbCompressed2;
+ if(pbCompressed1 != NULL)
+ delete [] pbCompressed1;
+ if(pbDecompressed2 != NULL)
+ delete [] pbDecompressed2;
+ if(pbDecompressed1 != NULL)
+ delete [] pbDecompressed1;
+ clreol();
+ return nError;
+}
+#endif // PLATFORM_WINDOWS
+
+//-----------------------------------------------------------------------------
+// Compression method test
+
+static int TestSectorCompress(int nSectorSize)
+{
+ LPBYTE pbDecompressed = NULL;
+ LPBYTE pbCompressed = NULL;
+ LPBYTE pbOriginal = NULL;
+ int nError = ERROR_SUCCESS;
+
+ // Allocate buffers
+ pbDecompressed = new BYTE[nSectorSize];
+ pbCompressed = new BYTE[nSectorSize];
+ pbOriginal = new BYTE[nSectorSize];
+ if(!pbDecompressed || !pbCompressed || !pbOriginal)
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+
+ if(nError == ERROR_SUCCESS)
+ {
+ for(int i = 0; i < 100000; i++)
+ {
+ int nOriginalLength = nSectorSize % (rand() + 1);
+ int nCompressedLength;
+ int nDecompressedLength;
+ int nCmp = MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB | MPQ_COMPRESSION_BZIP2 | MPQ_COMPRESSION_PKWARE;
+ int nDiff;
+
+ clreol();
+ _tprintf(_T("Testing compression of sector %u\r"), i + 1);
+
+ // Generate random data sector
+ GenerateRandomDataBlock(pbOriginal, nOriginalLength);
+ if(nOriginalLength == 0x123)
+ nOriginalLength = 0;
+
+__TryAgain:
+
+ // Compress the sector
+ nCompressedLength = nOriginalLength;
+ SCompCompress((char *)pbCompressed, &nCompressedLength, (char *)pbOriginal, nOriginalLength, nCmp, 0, -1);
+// SCompImplode((char *)pbCompressed, &nCompressedLength, (char *)pbOriginal, nOriginalLength);
+
+ // When the method was unable to compress data,
+ // the compressed data must be identical to original data
+ if(nCompressedLength == nOriginalLength)
+ {
+ if((nDiff = GetFirstDiffer(pbCompressed, pbOriginal, nOriginalLength)) != -1)
+ {
+ _tprintf(_T("Compression error: Fail when unable to compress the data (Offset 0x%08X).\n"), nDiff);
+ goto __TryAgain;
+ }
+ }
+
+ // Uncompress the sector
+ nDecompressedLength = nOriginalLength;
+ SCompDecompress((char *)pbDecompressed, &nDecompressedLength, (char *)pbCompressed, nCompressedLength);
+// SCompExplode((char *)pbDecompressed, &nDecompressedLength, (char *)pbCompressed, nCompressedLength);
+
+ // Check the decompressed length against original length
+ if(nDecompressedLength != nOriginalLength)
+ {
+ _tprintf(_T("Length of uncompressed data does not agree with original data length !!!\n"));
+ goto __TryAgain;
+ }
+
+ // Check decompressed block against original block
+ if((nDiff = GetFirstDiffer(pbDecompressed, pbOriginal, nOriginalLength)) != -1)
+ {
+ _tprintf(_T("Decompressed sector does not agree with the original data !!! (Offset 0x%08X)\n"), nDiff);
+ goto __TryAgain;
+ }
+ }
+ }
+
+ // Cleanup
+ delete [] pbOriginal;
+ delete [] pbCompressed;
+ delete [] pbDecompressed;
+ clreol();
+ return nError;
+}
+
+static int TestArchiveOpenAndClose(const TCHAR * szMpqName)
+{
+ const char * szFileName1 = "Sound\\terran\\duran\\TDnPss01.wav";
+// const char * szFileName2 = "items\\map\\mapz_deleted.cel";
+ TMPQArchive * ha = NULL;
+ HANDLE hFile1 = NULL;
+// HANDLE hFile2 = NULL;
+ HANDLE hMpq = NULL;
+ int nError = ERROR_SUCCESS;
+
+ if(nError == ERROR_SUCCESS)
+ {
+ _tprintf(_T("Opening archive %s ...\n"), szMpqName);
+ if(!SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE, &hMpq))
+ nError = GetLastError();
+ ha = (TMPQArchive *)hMpq;
+ }
+
+ if(nError == ERROR_SUCCESS)
+ {
+ SFileAddListFile(hMpq, "c:\\Tools32\\Listfiles\\ListFile.txt");
+ }
+
+ // Verify the raw data in the archive
+ if(nError == ERROR_SUCCESS)
+ {
+ // Verify the archive
+ SFileVerifyRawData(hMpq, SFILE_VERIFY_FILE, szFileName1);
+
+ // Try to open a file
+ if(!SFileOpenFileEx(hMpq, szFileName1, SFILE_OPEN_FROM_MPQ, &hFile1))
+ {
+ nError = GetLastError();
+ printf("%s - file not found in the MPQ\n", szFileName1);
+ }
+ }
+
+ // Dummy read from the file
+ if(nError == ERROR_SUCCESS)
+ {
+ DWORD dwBytesRead = 0;
+ BYTE Buffer[0x1000];
+
+ SFileSetFilePointer(hFile1, 0x1000, NULL, FILE_BEGIN);
+ SFileReadFile(hFile1, Buffer, sizeof(Buffer), &dwBytesRead, NULL);
+ }
+
+ // Verify the MPQ listfile
+#ifdef _MSC_VER
+ if(nError == ERROR_SUCCESS)
+ {
+ SFileExtractFile(hMpq, szFileName1, _T("E:\\extracted.wav"), 0);
+ PlaySound(_T("E:\\extracted.wav"), NULL, SND_FILENAME);
+ }
+#endif
+
+ if(hFile1 != NULL)
+ SFileCloseFile(hFile1);
+ if(hMpq != NULL)
+ SFileCloseArchive(hMpq);
+ return nError;
+}
+
+static int TestFindFiles(const TCHAR * szMpqName)
+{
+ TMPQFile * hf;
+ HANDLE hFile;
+ HANDLE hMpq = NULL;
+ BYTE Buffer[100];
+ int nError = ERROR_SUCCESS;
+ int nFiles = 0;
+ int nFound = 0;
+
+ // Open the archive
+ if(nError == ERROR_SUCCESS)
+ {
+ _tprintf(_T("Opening \"%s\" for finding files ...\n"), szMpqName);
+ if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq))
+ nError = GetLastError();
+ }
+
+ // Compact the archive
+ if(nError == ERROR_SUCCESS)
+ {
+ SFILE_FIND_DATA sf;
+ HANDLE hFind;
+ DWORD dwExtraDataSize;
+ bool bFound = true;
+
+ hFind = SFileFindFirstFile(hMpq, "*", &sf, "c:\\Tools32\\ListFiles\\ListFile.txt");
+ while(hFind != NULL && bFound != false)
+ {
+ if(SFileOpenFileEx(hMpq, sf.cFileName, 0, &hFile))
+ {
+ hf = (TMPQFile *)hFile;
+ SFileReadFile(hFile, Buffer, sizeof(Buffer), NULL, NULL);
+ nFiles++;
+
+ if(sf.dwFileFlags & MPQ_FILE_SECTOR_CRC)
+ {
+ dwExtraDataSize = hf->SectorOffsets[hf->dwSectorCount + 1] - hf->SectorOffsets[hf->dwSectorCount];
+ if(dwExtraDataSize != 0)
+ nFound++;
+ }
+
+ SFileCloseFile(hFile);
+ }
+
+ bFound = SFileFindNextFile(hFind, &sf);
+ }
+ }
+
+ if(hMpq != NULL)
+ SFileCloseArchive(hMpq);
+ if(nError == ERROR_SUCCESS)
+ _tprintf(_T("Search complete\n"));
+ return nError;
+}
+
+static int TestMpqCompacting(const TCHAR * szMpqName)
+{
+ HANDLE hMpq = NULL;
+ int nError = ERROR_SUCCESS;
+
+ // Open the archive
+ if(nError == ERROR_SUCCESS)
+ {
+ _tprintf(_T("Opening \"%s\" for compacting ...\n"), szMpqName);
+ if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq))
+ nError = GetLastError();
+ }
+
+ if(nError == ERROR_SUCCESS)
+ {
+ const char * szFileName = "Shaders\\Effects\\shadowmap.wfx";
+
+ printf("Deleting file %s ...\r", szFileName);
+ if(!SFileRemoveFile(hMpq, szFileName, SFILE_OPEN_FROM_MPQ))
+ nError = GetLastError();
+ }
+/*
+ // Compact the archive
+ if(nError == ERROR_SUCCESS)
+ {
+ _tprintf(_T("Compacting archive ...\r"));
+ SFileSetCompactCallback(hMpq, CompactCB, NULL);
+ if(!SFileCompactArchive(hMpq, "c:\\Tools32\\ListFiles\\ListFile.txt"))
+ nError = GetLastError();
+ }
+*/
+ if(hMpq != NULL)
+ SFileCloseArchive(hMpq);
+ if(nError == ERROR_SUCCESS)
+ _tprintf(_T("Compacting complete (No errors)\n"));
+ return nError;
+}
+
+static int TestCreateArchive(const TCHAR * szMpqName)
+{
+ TFileStream * pStream;
+ const TCHAR * szFileName1 = MAKE_PATH("FileTest.exe");
+ const TCHAR * szFileName2 = MAKE_PATH("ZeroSize.txt");
+ HANDLE hMpq = NULL; // Handle of created archive
+ DWORD dwVerifyResult;
+ DWORD dwFileCount = 0;
+ LCID LocaleIDs[] = {0x000, 0x405, 0x406, 0x407, 0xFFFF};
+ char szMpqFileName[MAX_PATH];
+ int nError = ERROR_SUCCESS;
+ int i;
+
+ // Create the new file
+ _tprintf(_T("Creating %s ...\n"), szMpqName);
+ pStream = FileStream_CreateFile(szMpqName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
+ if(pStream == NULL)
+ nError = GetLastError();
+
+ // Write some data
+ if(nError == ERROR_SUCCESS)
+ {
+ ULONGLONG FileSize = 0x100000;
+
+ FileStream_SetSize(pStream, FileSize);
+ FileStream_Close(pStream);
+ }
+
+ // Well, now create the MPQ archive
+ if(nError == ERROR_SUCCESS)
+ {
+ if(!SFileCreateArchive(szMpqName,
+ MPQ_CREATE_ARCHIVE_V4 | MPQ_CREATE_ATTRIBUTES,
+ 17,
+ &hMpq))
+ {
+ nError = GetLastError();
+ }
+ }
+
+ // Add the same file multiple times
+ if(nError == ERROR_SUCCESS)
+ {
+ // Add FileTest.exe
+ for(i = 0; AddFlags[i] != 0xFFFFFFFF; i++)
+ {
+ sprintf(szMpqFileName, "FileTest_%02u.exe", i);
+ PrintfTA(_T("Adding %s as %s ...\n"), szFileName1, szMpqFileName);
+ if(SFileAddFileEx(hMpq, szFileName1, szMpqFileName, AddFlags[i], MPQ_COMPRESSION_ZLIB, MPQ_COMPRESSION_NEXT_SAME))
+ {
+ dwVerifyResult = SFileVerifyFile(hMpq, szMpqFileName, MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_MD5);
+ if(dwVerifyResult & (VERIFY_OPEN_ERROR | VERIFY_READ_ERROR | VERIFY_FILE_SECTOR_CRC_ERROR | VERIFY_FILE_CHECKSUM_ERROR | VERIFY_FILE_MD5_ERROR))
+ printf("CRC error on \"%s\"\n", szMpqFileName);
+ dwFileCount++;
+ }
+ else
+ {
+ printf("Failed to add the file \"%s\".\n", szMpqFileName);
+ }
+ }
+
+
+ // Delete a file in the middle of the file table
+ SFileRemoveFile(hMpq, "FileTest_10.exe", SFILE_OPEN_FROM_MPQ);
+ SFileAddFileEx(hMpq, szFileName1, "FileTest_xx.exe", MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED, MPQ_COMPRESSION_ZLIB, MPQ_COMPRESSION_NEXT_SAME);
+
+ // Try to decrement max file count
+ dwFileCount = SFileGetMaxFileCount(hMpq);
+ SFileSetMaxFileCount(hMpq, dwFileCount - 1);
+
+ // Add ZeroSize.txt (1)
+ sprintf(szMpqFileName, "ZeroSize_1.txt");
+ for(i = 0; LocaleIDs[i] != 0xFFFF; i++)
+ {
+ PrintfTA(_T("Adding %s as %s (locale %04x) ...\n"), szFileName2, szMpqFileName, LocaleIDs[i]);
+ SFileSetLocale(LocaleIDs[i]);
+ if(!SFileAddFileEx(hMpq, szFileName2, szMpqFileName, MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED, MPQ_COMPRESSION_ZLIB, MPQ_COMPRESSION_NEXT_SAME))
+ printf("Cannot add the file\n");
+ }
+
+ // Add ZeroSize.txt (1)
+ sprintf(szMpqFileName, "ZeroSize_2.txt");
+ for(int i = 0; LocaleIDs[i] != 0xFFFF; i++)
+ {
+ PrintfTA(_T("Adding %s as %s (locale %04x) ...\n"), szFileName2, szMpqFileName, LocaleIDs[i]);
+ SFileSetLocale(LocaleIDs[i]);
+ if(!SFileAddFileEx(hMpq, szFileName2, szMpqFileName, MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED, MPQ_COMPRESSION_ZLIB, MPQ_COMPRESSION_NEXT_SAME))
+ printf("Cannot add the file\n");
+ }
+ }
+
+ // Test rename function
+ if(nError == ERROR_SUCCESS)
+ {
+ _tprintf(_T("Testing rename files ...\n"));
+ SFileSetLocale(LANG_NEUTRAL);
+ if(!SFileRenameFile(hMpq, "FileTest_08.exe", "FileTest_08a.exe"))
+ {
+ nError = GetLastError();
+ _tprintf(_T("Failed to rename the file\n"));
+ }
+
+ if(!SFileRenameFile(hMpq, "FileTest_08a.exe", "FileTest_08.exe"))
+ {
+ nError = GetLastError();
+ _tprintf(_T("Failed to rename the file\n"));
+ }
+
+ if(!SFileRenameFile(hMpq, "FileTest_10.exe", "FileTest_10a.exe"))
+ {
+ nError = GetLastError();
+ _tprintf(_T("Failed to rename the file\n"));
+ }
+
+ if(!SFileRenameFile(hMpq, "FileTest_10a.exe", "FileTest_10.exe"))
+ {
+ nError = GetLastError();
+ _tprintf(_T("Failed to rename the file\n"));
+ }
+
+ if(nError == ERROR_SUCCESS)
+ _tprintf(_T("Rename test succeeded.\n\n"));
+ else
+ _tprintf(_T("Rename test failed.\n\n"));
+ }
+
+ // Compact the archive
+// if(nError == ERROR_SUCCESS)
+// SFileCompactArchive(hMpq);
+
+ // Test changing hash table size
+ if(nError == ERROR_SUCCESS)
+ SFileSetMaxFileCount(hMpq, 0x95);
+
+ if(hMpq != NULL)
+ SFileCloseArchive(hMpq);
+
+ // Try to reopen the archive
+ if(SFileOpenArchive(szMpqName, 0, 0, &hMpq))
+ SFileCloseArchive(hMpq);
+
+ _tprintf(_T("\n"));
+ return nError;
+}
+
+static int TestCreateArchive_PaliRoharBug(const TCHAR * szMpqName)
+{
+ const TCHAR * szFileName = MAKE_PATH("FileTest.exe");
+ HANDLE hMpq = NULL; // Handle of created archive
+ DWORD dwMaxFileCount = 0;
+ DWORD dwMpqFlags = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS;
+ char szMpqFileName[MAX_PATH];
+ int nError = ERROR_SUCCESS;
+ int i;
+
+ _tremove(szMpqName);
+ if(SFileCreateArchive(szMpqName,
+ MPQ_CREATE_ARCHIVE_V4 | MPQ_CREATE_ATTRIBUTES,
+ 1,
+ &hMpq))
+ {
+ // Add the file there
+ SFileAddFileEx(hMpq, szFileName, "FileTest_base.exe", dwMpqFlags, MPQ_COMPRESSION_ZLIB, MPQ_COMPRESSION_NEXT_SAME);
+ SFileFlushArchive(hMpq);
+ SFileCloseArchive(hMpq);
+
+ // Add the same file 10 times
+ for(i = 0; i < 10; i++)
+ {
+ if(SFileOpenArchive(szMpqName, 0, 0, &hMpq))
+ {
+ dwMaxFileCount = SFileGetMaxFileCount(hMpq) + 1;
+ _tprintf(_T("Increasing max file count to %u ...\n"), dwMaxFileCount);
+ SFileSetMaxFileCount(hMpq, dwMaxFileCount);
+
+ sprintf(szMpqFileName, "FileTest_%02u.exe", dwMaxFileCount);
+ PrintfTA(_T("Adding %s as %s\n"), szFileName, szMpqFileName);
+ if(!SFileAddFileEx(hMpq, szFileName, szMpqFileName, dwMpqFlags, MPQ_COMPRESSION_ZLIB, MPQ_COMPRESSION_NEXT_SAME))
+ {
+ printf("Failed to add the file \"%s\".\n", szMpqFileName);
+ break;
+ }
+
+ SFileFlushArchive(hMpq);
+ SFileCompactArchive(hMpq, NULL, false);
+ SFileCloseArchive(hMpq);
+ }
+ }
+ }
+
+ _tprintf(_T("\n"));
+ return nError;
+}
+
+
+static int TestAddFilesToMpq(
+ const TCHAR * szMpqName,
+ ...
+ )
+{
+ const TCHAR * szFileName;
+ const TCHAR * szSrc;
+ char * szTrg;
+ HANDLE hMpq;
+ va_list argList;
+ char szMpqFileName[MAX_PATH];
+ int nError = ERROR_SUCCESS;
+
+ if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq))
+ return GetLastError();
+
+ va_start(argList, szMpqName);
+ while((szFileName = va_arg(argList, const TCHAR *)) != NULL)
+ {
+ // Convert the plain name to ANSI
+ szSrc = GetPlainFileNameT(szFileName);
+ szTrg = szMpqFileName;
+ while(*szSrc != 0)
+ *szTrg++ = (char)*szSrc++;
+ *szTrg = 0;
+
+ // Add the file to MPQ
+ if(!SFileAddFileEx(hMpq, szFileName,
+ szMpqFileName,
+ MPQ_FILE_COMPRESS,
+ MPQ_COMPRESSION_ZLIB,
+ MPQ_COMPRESSION_NEXT_SAME))
+ {
+ nError = GetLastError();
+ printf("Failed to add the file \"%s\"\n", szFileName);
+ }
+ }
+
+ SFileCloseArchive(hMpq);
+ return nError;
+}
+
+static int TestCreateArchiveFromMemory(const TCHAR * szMpqName)
+{
+#define FILE_SIZE 65535
+
+ HANDLE hFile;
+ HANDLE hMPQ;
+ char* data = new char [FILE_SIZE]; // random memory data
+ char szFileName[100];
+ int i;
+
+ // Create an mpq file for testing
+ if(SFileCreateArchive(szMpqName, MPQ_CREATE_ARCHIVE_V2|MPQ_CREATE_ATTRIBUTES, 0x100000, &hMPQ))
+ {
+ for(i = 0; i < 1000; i++)
+ {
+ sprintf(szFileName, "File%03u.bin", i);
+ printf("Adding file %s\r", szFileName);
+
+ if(SFileCreateFile(hMPQ, szFileName, 0, FILE_SIZE, 0, MPQ_FILE_COMPRESS, &hFile))
+ {
+ SFileWriteFile(hFile, data, FILE_SIZE, MPQ_COMPRESSION_ZLIB);
+ SFileFinishFile(hFile);
+ }
+ }
+ }
+ SFileCloseArchive(hMPQ);
+ delete [] data;
+ return ERROR_SUCCESS;
+}
+
+static int TestFileReadAndWrite(
+ const TCHAR * szMpqName,
+ const char * szFileName)
+{
+ LPBYTE pvFile = NULL;
+ HANDLE hFile = NULL;
+ HANDLE hMpq = NULL;
+ DWORD dwBytesRead;
+ DWORD dwFileSize = 0;
+ int nError = ERROR_SUCCESS;
+
+ if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq))
+ {
+ nError = GetLastError();
+ _tprintf(_T("Failed to open the archive %s (%u).\n"), szMpqName, nError);
+ }
+
+ if(nError == ERROR_SUCCESS)
+ {
+ if(!SFileOpenFileEx(hMpq, szFileName, 0, &hFile))
+ {
+ nError = GetLastError();
+ printf("Failed to open the file %s (%u).\n", szFileName, nError);
+ }
+ }
+
+ if(nError == ERROR_SUCCESS)
+ {
+ if(!SFileGetFileInfo(hFile, SFILE_INFO_FILE_SIZE, &dwFileSize, sizeof(DWORD), NULL))
+ {
+ nError = GetLastError();
+ _tprintf(_T("Failed to get the file size (%u).\n"), nError);
+ }
+ }
+
+ if(nError == ERROR_SUCCESS)
+ {
+ pvFile = new BYTE[dwFileSize];
+ if(pvFile == NULL)
+ {
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ printf("Failed to allocate buffer for the file (%u).\n", nError);
+ }
+ }
+
+ if(nError == ERROR_SUCCESS)
+ {
+ if(!SFileReadFile(hFile, pvFile, dwFileSize, &dwBytesRead, NULL))
+ {
+ nError = GetLastError();
+ printf("Failed to read file (%u).\n", nError);
+ }
+ }
+
+ if(hFile != NULL)
+ {
+ SFileCloseFile(hFile);
+ hFile = NULL;
+ }
+
+ if(nError == ERROR_SUCCESS)
+ {
+ if(!SFileCreateFile(hMpq, szFileName, 0, dwFileSize, 0, MPQ_FILE_REPLACEEXISTING, &hFile))
+ {
+ nError = GetLastError();
+ printf("Failed to create %s in the archive (%u).\n", szFileName, nError);
+ }
+ }
+
+ if(nError == ERROR_SUCCESS)
+ {
+ if(!SFileWriteFile(hFile, pvFile, dwFileSize, 0))
+ {
+ nError = GetLastError();
+ printf("Failed to write the data to the MPQ (%u).\n", nError);
+ }
+ }
+
+ if(hFile != NULL)
+ {
+ if(!SFileFinishFile(hFile))
+ {
+ nError = GetLastError();
+ printf("Failed to finalize file creation (%u).\n", nError);
+ }
+ }
+
+ if(pvFile != NULL)
+ delete [] pvFile;
+ if(hMpq != NULL)
+ SFileCloseArchive(hMpq);
+ return nError;
+}
+
+static int TestSignatureVerify(const TCHAR * szMpqName)
+{
+ HANDLE hMpq;
+
+ if(SFileOpenArchive(szMpqName, 0, 0, &hMpq))
+ {
+ _tprintf(_T("Verifying digital signature in %s:\n"), szMpqName);
+ switch(SFileVerifyArchive(hMpq))
+ {
+ case ERROR_NO_SIGNATURE:
+ _tprintf(_T("No digital signature present.\n"));
+ break;
+
+ case ERROR_VERIFY_FAILED:
+ _tprintf(_T("Failed to verify signature.\n"));
+ break;
+
+ case ERROR_WEAK_SIGNATURE_OK:
+ _tprintf(_T("Weak signature is OK.\n"));
+ break;
+
+ case ERROR_WEAK_SIGNATURE_ERROR:
+ _tprintf(_T("Weak signature mismatch.\n"));
+ break;
+
+ case ERROR_STRONG_SIGNATURE_OK:
+ _tprintf(_T("Strong signature is OK.\n"));
+ break;
+
+ case ERROR_STRONG_SIGNATURE_ERROR:
+ _tprintf(_T("Strong signature mismatch.\n"));
+ break;
+ }
+
+ SFileCloseArchive(hMpq);
+ _tprintf(_T("\n"));
+ }
+
+ return 0;
+}
+
+
+static int TestCreateArchiveCopy(const TCHAR * szMpqName, const TCHAR * szMpqCopyName, const char * szListFile)
+{
+ TFileStream * pStream;
+ TCHAR szLocalFile[MAX_PATH];
+ HANDLE hMpq1 = NULL; // Handle of existing archive
+ HANDLE hMpq2 = NULL; // Handle of created archive
+ DWORD dwHashTableSize = 0;
+ int nError = ERROR_SUCCESS;
+
+ // If no listfile or an empty one, use NULL
+ if(szListFile == NULL || *szListFile == 0)
+ szListFile = NULL;
+
+ // Create the new file
+ pStream = FileStream_CreateFile(szMpqCopyName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
+ if(pStream == NULL)
+ nError = GetLastError();
+
+ // Write some data
+ if(nError == ERROR_SUCCESS)
+ {
+ ULONGLONG FileSize = 0x100000;
+
+ FileStream_SetSize(pStream, FileSize);
+ FileStream_Close(pStream);
+ }
+
+ // Open the existing MPQ archive
+ if(nError == ERROR_SUCCESS)
+ {
+ _tprintf(_T("Opening %s ...\n"), szMpqName);
+ if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq1))
+ nError = GetLastError();
+ }
+
+ // Well, now create the MPQ archive
+ if(nError == ERROR_SUCCESS)
+ {
+ _tprintf(_T("Creating %s ...\n"), szMpqCopyName);
+ SFileGetFileInfo(hMpq1, SFILE_INFO_HASH_TABLE_SIZE, &dwHashTableSize, 4, NULL);
+ if(!SFileCreateArchive(szMpqCopyName, 0, dwHashTableSize, &hMpq2))
+ nError = GetLastError();
+ }
+
+ // Copy all files from one archive to another
+ if(nError == ERROR_SUCCESS)
+ {
+ SFILE_FIND_DATA sf;
+ HANDLE hFind = SFileFindFirstFile(hMpq1, "*", &sf, szListFile);
+ bool bResult = true;
+
+ _tprintf(_T("Copying files ...\n"));
+
+ if(hFind != NULL)
+ {
+ while(bResult)
+ {
+ if(strcmp(sf.cFileName, LISTFILE_NAME) && strcmp(sf.cFileName, ATTRIBUTES_NAME))
+ {
+ SFileSetLocale(sf.lcLocale);
+
+ // Create the local file name
+ MergeLocalPath(szLocalFile, szWorkDir, sf.szPlainName);
+ if(SFileExtractFile(hMpq1, sf.cFileName, szLocalFile, SFILE_OPEN_FROM_MPQ))
+ {
+ printf("Extracting %s ... OK\n", sf.cFileName);
+ if(!SFileAddFile(hMpq2, szLocalFile, sf.cFileName, sf.dwFileFlags))
+ {
+ nError = GetLastError();
+ printf("Adding %s ... Failed\n\n", sf.cFileName);
+ _tremove(szLocalFile);
+ break;
+ }
+ else
+ {
+ printf("Adding %s ... OK\n", sf.cFileName);
+ }
+ }
+ else
+ {
+ printf("Extracting %s ... Failed\n", sf.cFileName);
+ }
+
+ // Delete the added file
+ _tremove(szLocalFile);
+ }
+
+ // Find the next file
+ bResult = SFileFindNextFile(hFind, &sf);
+ }
+
+ // Close the search handle
+ SFileFindClose(hFind);
+ printf("\n");
+ }
+ }
+
+ // Close both archives
+ if(hMpq2 != NULL)
+ SFileCloseArchive(hMpq2);
+ if(hMpq1 != NULL)
+ SFileCloseArchive(hMpq1);
+ return nError;
+}
+
+static int TestOpenPatchedArchive(const TCHAR * szMpqName, ...)
+{
+ TFileStream * pStream;
+ HANDLE hFile = NULL;
+ HANDLE hMpq = NULL;
+ va_list argList;
+ const char * szFileName = "Interface/Cinematics/MOP_BR.mp3";
+ TCHAR szLocFileName[MAX_PATH];
+ LPBYTE pbFullFile = NULL;
+ DWORD dwFileSize;
+ int nError = ERROR_SUCCESS;
+
+ // Open the primary MPQ
+ _tprintf(_T("Opening %s ...\n"), szMpqName);
+ if(!SFileOpenArchive(szMpqName, 0, MPQ_OPEN_READ_ONLY, &hMpq))
+ {
+ nError = GetLastError();
+ _tprintf(_T("Failed to open the archive %s ...\n"), szMpqName);
+ }
+
+ // Add all patches
+ if(nError == ERROR_SUCCESS)
+ {
+ va_start(argList, szMpqName);
+ while((szMpqName = va_arg(argList, const TCHAR *)) != NULL)
+ {
+ _tprintf(_T("Adding patch %s ...\n"), szMpqName);
+ if(!SFileOpenPatchArchive(hMpq, szMpqName, NULL, 0))
+ {
+ nError = GetLastError();
+ printf("Failed to add patch %s ...\n", szMpqName);
+ }
+ }
+ va_end(argList);
+ }
+/*
+ // Now search all files
+ if(nError == ERROR_SUCCESS)
+ {
+ SFILE_FIND_DATA sf;
+ HANDLE hFind;
+ bool bResult = true;
+
+ hFind = SFileFindFirstFile(hMpq, "World\\Minimaps\\Azeroth\\noLiquid_map20_44.*", &sf, NULL);
+ while(hFind && bResult)
+ {
+ printf("%s\n", sf.cFileName);
+ bResult = SFileFindNextFile(hFind, &sf);
+ }
+ }
+
+ // Now try to open patched version of a file
+ if(nError == ERROR_SUCCESS)
+ {
+ SFileExtractFile(hMpq, szFileName, _T("E:\\noLiquid_map20_44.blp"), SFILE_OPEN_FROM_MPQ);
+ }
+*/
+ // Now try to open patched version of "Achievement.dbc"
+ if(nError == ERROR_SUCCESS)
+ {
+ printf("Opening patched file \"%s\" ...\n", szFileName);
+ SFileHasFile(hMpq, szFileName);
+ SFileVerifyFile(hMpq, szFileName, SFILE_VERIFY_RAW_MD5);
+ if(!SFileOpenFileEx(hMpq, szFileName, 0, &hFile))
+ {
+ nError = GetLastError();
+ printf("Failed to open patched file \"%s\"\n", szFileName);
+ }
+ }
+
+ // Verify of the patched version is correct
+ if(nError == ERROR_SUCCESS)
+ {
+ TCHAR * szPatchChain = NULL;
+ DWORD cbPatchChain = 0;
+
+ // Get the patch chain
+ SFileGetFileInfo(hFile, SFILE_INFO_PATCH_CHAIN, szPatchChain, cbPatchChain, &cbPatchChain);
+ szPatchChain = (TCHAR *)(new BYTE[cbPatchChain]);
+ SFileGetFileInfo(hFile, SFILE_INFO_PATCH_CHAIN, szPatchChain, cbPatchChain, &cbPatchChain);
+ delete [] szPatchChain;
+
+ // Get the size of the full patched file
+ dwFileSize = SFileGetFileSize(hFile, NULL);
+ if(dwFileSize != 0)
+ {
+ DWORD dwBytesRead = 0;
+ BYTE TempData[0x100];
+
+ SFileReadFile(hFile, TempData, sizeof(TempData), &dwBytesRead, NULL);
+ SFileSetFilePointer(hFile, 0, NULL, FILE_BEGIN);
+
+ // Allocate space for the full file
+ pbFullFile = new BYTE[dwFileSize];
+ if(pbFullFile != NULL)
+ {
+ if(!SFileReadFile(hFile, pbFullFile, dwFileSize, NULL, NULL))
+ {
+ nError = GetLastError();
+ printf("Failed to read full patched file data \"%s\"\n", szFileName);
+ }
+
+ if(nError == ERROR_SUCCESS)
+ {
+ MergeLocalPath(szLocFileName, MAKE_PATH("Work//"), GetPlainFileNameA(szFileName));
+ pStream = FileStream_CreateFile(szLocFileName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
+ if(pStream != NULL)
+ {
+ FileStream_Write(pStream, NULL, pbFullFile, dwFileSize);
+ FileStream_Close(pStream);
+ }
+ }
+
+ delete [] pbFullFile;
+ }
+ }
+ }
+
+ // Close handles
+ if(hFile != NULL)
+ SFileCloseFile(hFile);
+ if(hMpq != NULL)
+ SFileCloseArchive(hMpq);
+ return nError;
+}
+
+static int TestCompareTwoArchives(
+ const TCHAR * szMpqName1,
+ const TCHAR * szMpqName2,
+ const char * szListFile,
+ DWORD dwBlockSize)
+{
+ TMPQArchive * ha1 = NULL;
+ TMPQArchive * ha2 = NULL;
+ HANDLE hMpq1 = NULL; // Handle of the first archive
+ HANDLE hMpq2 = NULL; // Handle of the second archive
+ HANDLE hFile1 = NULL;
+ HANDLE hFile2 = NULL;
+ int nError = ERROR_SUCCESS;
+
+ // If no listfile or an empty one, use NULL
+ if(szListFile == NULL || *szListFile == 0)
+ szListFile = NULL;
+
+ _tprintf(_T("=============== Comparing MPQ archives ===============\n"));
+
+ // Open the first MPQ archive
+ if(nError == ERROR_SUCCESS && szMpqName1 != NULL)
+ {
+ _tprintf(_T("Opening %s ...\n"), szMpqName1);
+ if(!SFileOpenArchive(szMpqName1, 0, 0, &hMpq1))
+ nError = GetLastError();
+ ha1 = (TMPQArchive *)hMpq1;
+ }
+
+ // Open the second MPQ archive
+ if(nError == ERROR_SUCCESS && szMpqName2 != NULL)
+ {
+ _tprintf(_T("Opening %s ...\n"), szMpqName2);
+ if(!SFileOpenArchive(szMpqName2, 0, 0, &hMpq2))
+ nError = GetLastError();
+ ha2 = (TMPQArchive *)hMpq2;
+ }
+
+ // Compare the header
+ if(nError == ERROR_SUCCESS && (ha1 != NULL && ha2 != NULL))
+ {
+ if(ha1->pHeader->dwHeaderSize != ha2->pHeader->dwHeaderSize)
+ printf(" - Header size is different\n");
+ if(ha1->pHeader->wFormatVersion != ha2->pHeader->wFormatVersion)
+ printf(" - Format version is different\n");
+ if(ha1->pHeader->wSectorSize != ha2->pHeader->wSectorSize)
+ printf(" - Sector size is different\n");
+ if(ha1->pHeader->HetTableSize64 != ha2->pHeader->HetTableSize64)
+ printf(" - HET table size is different\n");
+ if(ha1->pHeader->BetTableSize64 != ha2->pHeader->BetTableSize64)
+ printf(" - BET table size is different\n");
+ if(ha1->pHeader->dwHashTableSize != ha2->pHeader->dwHashTableSize)
+ printf(" - Hash table size is different\n");
+ if(ha1->pHeader->dwBlockTableSize != ha2->pHeader->dwBlockTableSize)
+ printf(" - Block table size is different\n");
+ }
+
+ // Find all files in the first archive and compare them
+ if(nError == ERROR_SUCCESS)
+ {
+ SFILE_FIND_DATA sf;
+ TMPQFile * hf1;
+ TMPQFile * hf2;
+ HANDLE hFind = SFileFindFirstFile(hMpq1, "*", &sf, szListFile);
+ bool bResult = true;
+
+ while(hFind != NULL && bResult == true)
+ {
+// printf("%s \r", sf.cFileName);
+ SFileSetLocale(sf.lcLocale);
+
+ // Open the first file
+ if(!SFileOpenFileEx(hMpq1, sf.cFileName, 0, &hFile1))
+ printf("Failed to open the file %s in the first archive\n", sf.cFileName);
+
+ if(!SFileOpenFileEx(hMpq2, sf.cFileName, 0, &hFile2))
+ printf("Failed to open the file %s in the second archive\n", sf.cFileName);
+
+ if(hFile1 != NULL && hFile2 != NULL)
+ {
+ // Get the TMPQFile pointers
+ hf1 = (TMPQFile *)hFile1;
+ hf2 = (TMPQFile *)hFile2;
+
+ // Compare the file sizes
+ if(hf1->pFileEntry->dwFileSize != hf2->pFileEntry->dwFileSize)
+ printf("Different file size: %s (%u x %u)\n", sf.cFileName, hf1->pFileEntry->dwFileSize, hf2->pFileEntry->dwFileSize);
+
+ if(hf1->pFileEntry->dwCmpSize != hf2->pFileEntry->dwCmpSize)
+ printf("Different cmpr size: %s (%u x %u)\n", sf.cFileName, hf1->pFileEntry->dwCmpSize, hf2->pFileEntry->dwCmpSize);
+
+ if(hf1->pFileEntry->dwFlags != hf2->pFileEntry->dwFlags)
+ printf("Different mpq flags: %s (%08X x %08X)\n", sf.cFileName, hf1->pFileEntry->dwFlags, hf2->pFileEntry->dwFlags);
+
+ if(!CompareArchivedFiles(sf.cFileName, hFile1, hFile2, dwBlockSize))
+ printf("Different file data: %s\n", sf.cFileName);
+
+// if(!CompareArchivedFilesRR(sf.cFileName, hFile1, hFile2, dwBlockSize))
+// printf("Different file data: %s\n", sf.cFileName);
+ }
+
+ // Close both files
+ if(hFile2 != NULL)
+ SFileCloseFile(hFile2);
+ if(hFile1 != NULL)
+ SFileCloseFile(hFile1);
+ hFile2 = hFile1 = NULL;
+
+ // Find the next file
+ bResult = SFileFindNextFile(hFind, &sf);
+ }
+
+ // Close the find handle
+ if(hFind != NULL)
+ SFileFindClose(hFind);
+ }
+
+ // Close both archives
+ clreol();
+ printf("================ MPQ compare complete ================\n");
+ if(hMpq2 != NULL)
+ SFileCloseArchive(hMpq2);
+ if(hMpq1 != NULL)
+ SFileCloseArchive(hMpq1);
+ return nError;
+}
+
+//-----------------------------------------------------------------------------
+// Searching all known MPQs
+
+#ifdef _WIN32
+static int TestSearchOneArchive(const TCHAR * szMpqName)
+{
+ SFILE_FIND_DATA sf;
+ HANDLE hFind;
+ HANDLE hMpq;
+ const TCHAR * szExtension;
+ bool bFound = true;
+
+ // Get the file extension
+ szExtension = _tcsrchr(szMpqName, _T('.'));
+ if(szExtension == NULL)
+ return ERROR_SUCCESS;
+
+ // Only search defined extensions
+ if(_tcsicmp(szExtension, _T(".mpq")) && _tcsnicmp(szExtension, _T(".SC2"), 4))
+ return ERROR_SUCCESS;
+
+ _tprintf(_T("Searching %s ...\n"), szMpqName);
+
+ // Try to open the MPQ
+ if(SFileOpenArchive(szMpqName, 0, MPQ_OPEN_READ_ONLY, &hMpq))
+ {
+ hFind = SFileFindFirstFile(hMpq, "*", &sf, NULL);
+ if(hFind != NULL)
+ {
+ while(bFound)
+ {
+ if(sf.dwFileFlags & MPQ_FILE_DELETE_MARKER)
+ _tprintf(_T("Delete marker: %s:%hs\n"), szMpqName, sf.cFileName);
+
+ bFound = SFileFindNextFile(hFind, &sf);
+ }
+ }
+
+ SFileCloseArchive(hMpq);
+ }
+
+ return ERROR_SUCCESS;
+}
+
+static int TestSearchAllArchives(const TCHAR * szSearchMask)
+{
+ WIN32_FIND_DATA wf;
+ LPTSTR szFilePart;
+ HANDLE hFind;
+ TCHAR szFullPath[MAX_PATH];
+ BOOL bFound = TRUE;
+
+ // Initiate search
+ _tcscpy(szFullPath, szSearchMask);
+ szFilePart = _tcschr(szFullPath, _T('*'));
+ assert(szFilePart != NULL);
+
+ // Begin search
+ hFind = FindFirstFile(szSearchMask, &wf);
+ if(hFind != INVALID_HANDLE_VALUE)
+ {
+ while(bFound)
+ {
+ // Eliminate "." and ".."
+ if(wf.cFileName[0] != _T('.'))
+ {
+ // Construct the full path
+ _tcscpy(szFilePart, wf.cFileName);
+
+ // If it a directory?
+ if(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ _tcscat(szFullPath, _T("\\*"));
+ TestSearchAllArchives(szFullPath);
+ }
+ else
+ {
+ TestSearchOneArchive(szFullPath);
+ }
+ }
+ bFound = FindNextFile(hFind, &wf);
+ }
+ FindClose(hFind);
+ }
+
+ return ERROR_SUCCESS;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Main
+//
+
+int main(void)
+{
+ int nError = ERROR_SUCCESS;
+
+#if defined(_MSC_VER) && defined(_DEBUG)
+ _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+#endif // defined(_MSC_VER) && defined(_DEBUG)
+
+ // Mix the random number generator
+// srand(GetTickCount());
+
+// FileStream_OpenEncrypted(_T("e:\\Multimedia\\MPQs\\2010 - Starcraft II\\Installer UI 2 deDE.MPQE"));
+
+ // Test structure sizes
+// if(nError == ERROR_SUCCESS)
+// nError = TestStructureSizes();
+
+// if(nError == ERROR_SUCCESS)
+// nError = TestOpenLocalFile("C:\\autoexec.bat");
+
+ // Test reading partial file
+// if(nError == ERROR_SUCCESS)
+// nError = TestPartFileRead(MAKE_PATH("2009 - PartialMPQs/patch.MPQ.part"));
+
+ if(nError == ERROR_SUCCESS)
+ {
+ CompareHuffmanCompressions7();
+ nError = CompareHuffmanCompressions0();
+ }
+
+// if(nError == ERROR_SUCCESS)
+// nError = ComparePklibCompressions();
+
+ // Test LZMA compression method against the code ripped from Starcraft II
+// if(nError == ERROR_SUCCESS)
+// nError = CompareLzmaCompressions(MPQ_SECTOR_SIZE);
+
+ // Test compression methods
+// if(nError == ERROR_SUCCESS)
+// nError = TestSectorCompress(MPQ_SECTOR_SIZE);
+
+ // Test the archive open and close
+// if(nError == ERROR_SUCCESS)
+// nError = TestArchiveOpenAndClose(MAKE_PATH("BrooDat.mpq"));
+
+// if(nError == ERROR_SUCCESS)
+// nError = TestFindFiles(MAKE_PATH("2002 - Warcraft III/HumanEd.mpq"));
+
+ // Create a big MPQ archive
+// if(nError == ERROR_SUCCESS)
+// nError = TestCreateArchive_PaliRoharBug(MAKE_PATH("Test.mpq"));
+// nError = TestCreateArchive(MAKE_PATH("Test.mpq"));
+// nError = TestCreateArchive((const TCHAR*)szUnicodeName1);
+// nError = TestCreateArchive((const TCHAR*)szUnicodeName2);
+// nError = TestCreateArchive((const TCHAR*)szUnicodeName3);
+// nError = TestCreateArchive((const TCHAR*)szUnicodeName4);
+// nError = TestCreateArchive((const TCHAR*)szUnicodeName5);
+// nError = TestCreateArchive((const TCHAR*)szUnicodeName6);
+
+// if(nError == ERROR_SUCCESS)
+// nError = TestAddFilesToMpq(MAKE_PATH("wow-update-13202.MPQ"),
+// "c:\\Tools32\\Arj32.exe",
+// "c:\\Tools32\\autoruns.chm",
+// "c:\\Tools32\\CPUEater.exe",
+// "c:\\Tools32\\dumpbin.exe",
+// "c:\\Tools32\\editbin.exe",
+// "c:\\Tools32\\fsg.ini",
+// "c:\\Tools32\\hiew8.ini",
+// "c:\\Tools32\\ida.bat",
+// "c:\\Tools32\\mp3.ini",
+// NULL);
+
+// if(nError == ERROR_SUCCESS)
+// nError = TestCreateArchiveFromMemory(MAKE_PATH("Test-leak.mpq"));
+
+// if(nError == ERROR_SUCCESS)
+// nError = TestFileReadAndWrite(MAKE_PATH("2002 - Warcraft III/(10)DustwallowKeys.w3m"), "war3map.j");
+
+ // Verify the archive signature
+// if(nError == ERROR_SUCCESS)
+// nError = TestSignatureVerify(MAKE_PATH("1998 - Starcraft/BW-1152.exe"));
+// nError = TestSignatureVerify(MAKE_PATH("2002 - Warcraft III/(10)DustwallowKeys.w3m"));
+// nError = TestSignatureVerify(MAKE_PATH("2002 - Warcraft III/War3TFT_121b_English.exe"));
+// nError = TestSignatureVerify(MAKE_PATH("2004 - World of Warcraft/WoW-2.3.3.7799-to-2.4.0.8089-enUS-patch.exe"));
+// nError = TestSignatureVerify(MAKE_PATH("2004 - World of Warcraft/standalone.MPQ"));
+
+ // Compact the archive
+// if(nError == ERROR_SUCCESS)
+// nError = TestMpqCompacting(MAKE_PATH("wow-update-base-14333.MPQ"));
+
+ // Create copy of the archive, appending some bytes before the MPQ header
+// if(nError == ERROR_SUCCESS)
+// nError = TestCreateArchiveCopy(MAKE_PATH("PartialMPQs/interface.MPQ.part"), MAKE_PATH("PartialMPQs/interface-copy.MPQ.part"), NULL);
+
+
+// if(nError == ERROR_SUCCESS)
+// {
+// nError = TestOpenPatchedArchive(MAKE_PATH("2012 - WoW\\16057\\enUS\\locale-enUS.MPQ"),
+// MAKE_PATH("2012 - WoW\\16057\\enUS\\wow-update-enUS-16016.MPQ"),
+// NULL);
+// }
+
+// if(nError == ERROR_SUCCESS)
+// {
+// nError = TestCompareTwoArchives(MAKE_PATH("Sound-copy.mpq"),
+// MAKE_PATH("Sound.mpq"),
+// NULL,
+// 0x1001);
+// }
+
+// if(nError == ERROR_SUCCESS)
+// nError = TestSearchAllArchives(MAKE_PATH("*"));
+
+ return nError;
+}