diff options
| -rw-r--r-- | .vscode/launch.json | 2 | ||||
| -rw-r--r-- | .vscode/tasks.json | 4 | ||||
| -rw-r--r-- | CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/SBaseCommon.cpp | 36 | ||||
| -rw-r--r-- | src/StormCommon.h | 1 | ||||
| -rwxr-xr-x | test/StormTest.cpp | 627 | ||||
| -rw-r--r-- | test/TLogHelper.cpp | 63 | ||||
| -rw-r--r-- | test/stormlib-test-001.txt | 274 | 
8 files changed, 465 insertions, 546 deletions
| diff --git a/.vscode/launch.json b/.vscode/launch.json index 9da39f0..e9457ca 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -3,7 +3,7 @@      // Hover to view descriptions of existing attributes.      // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387      "version": "0.2.0", -    "program": "./build/StormLib_test", +    "program": "${workspaceFolder}/build/StormLib_test",      "configurations": [          {              "name": "(gdb) Launch", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 4217112..05054c5 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,8 +2,8 @@      "tasks": [          {              "type": "cppbuild", -            "label": "C/C++: cpp build active file", -            "command": "/usr/bin/cpp", +            "label": "C/C++: g++ build active file", +            "command": "/usr/bin/g++",              "args": [                  "-fdiagnostics-color=always",                  "-g", diff --git a/CMakeLists.txt b/CMakeLists.txt index c480d40..d99ba20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,7 @@ set(TOMCRYPT_FILES             src/libtomcrypt/src/hashes/hash_memory.c             src/libtomcrypt/src/hashes/md5.c             src/libtomcrypt/src/hashes/sha1.c +           src/libtomcrypt/src/hashes/sha256.c             src/libtomcrypt/src/math/ltm_desc.c             src/libtomcrypt/src/math/multi.c             src/libtomcrypt/src/math/rand_prime.c @@ -393,8 +394,9 @@ if (NOT STORM_SKIP_INSTALL)  endif()  if(STORM_BUILD_TESTS) +    find_package(ALSA REQUIRED)      add_executable(StormLib_test ${TEST_SRC_FILES}) -    target_link_libraries(StormLib_test ${LIBRARY_NAME}) +    target_link_libraries(StormLib_test ${LIBRARY_NAME} ${ALSA_LIBRARIES})      install(TARGETS StormLib_test RUNTIME DESTINATION bin)  endif() diff --git a/src/SBaseCommon.cpp b/src/SBaseCommon.cpp index 7415583..587efe4 100644 --- a/src/SBaseCommon.cpp +++ b/src/SBaseCommon.cpp @@ -154,30 +154,18 @@ void StringCreatePseudoFileName(char * szBuffer, size_t cchMaxChars, unsigned in  #ifdef _UNICODE
  void StringCopy(TCHAR * szTarget, size_t cchTarget, const char * szSource)
  {
 -    if(cchTarget > 0)
 -    {
 -        size_t cchSource = strlen(szSource);
 +    int ccResult;
 -        if(cchSource >= cchTarget)
 -            cchSource = cchTarget - 1;
 -
 -        mbstowcs(szTarget, szSource, cchSource);
 -        szTarget[cchSource] = 0;
 -    }
 +    ccResult = MultiByteToWideChar(CP_UTF8, 0, szSource, -1, szTarget, (int)(cchTarget));
 +    szTarget[ccResult] = 0;
  }
  void StringCopy(char * szTarget, size_t cchTarget, const TCHAR * szSource)
  {
 -    if(cchTarget > 0)
 -    {
 -        size_t cchSource = _tcslen(szSource);
 -
 -        if(cchSource >= cchTarget)
 -            cchSource = cchTarget - 1;
 +    int ccResult;
 -        wcstombs(szTarget, szSource, cchSource);
 -        szTarget[cchSource] = 0;
 -    }
 +    ccResult = WideCharToMultiByte(CP_UTF8, 0, szSource, -1, szTarget, (int)(cchTarget), NULL, NULL);
 +    szTarget[ccResult] = 0;
  }
  void StringCopy(TCHAR * szTarget, size_t cchTarget, const TCHAR * szSource)
 @@ -205,6 +193,18 @@ void StringCat(TCHAR * szTarget, size_t cchTargetMax, const TCHAR * szSource)          StringCopy(szTarget + cchTarget, (cchTargetMax - cchTarget), szSource);
      }
  }
 +
 +void StringCat(TCHAR * szTarget, size_t cchTargetMax, const char * szSource)
 +{
 +    // Get the current length of the target
 +    size_t cchTarget = _tcslen(szTarget);
 +
 +    // Copy the string to the target
 +    if(cchTarget < cchTargetMax)
 +    {
 +        StringCopy(szTarget + cchTarget, (cchTargetMax - cchTarget), szSource);
 +    }
 +}
  #endif
  //-----------------------------------------------------------------------------
 diff --git a/src/StormCommon.h b/src/StormCommon.h index c73543d..c050093 100644 --- a/src/StormCommon.h +++ b/src/StormCommon.h @@ -206,6 +206,7 @@ void StringCopy(TCHAR * szTarget, size_t cchTarget, const char * szSource);  void StringCopy(char * szTarget, size_t cchTarget, const TCHAR * szSource);
  void StringCopy(TCHAR * szTarget, size_t cchTarget, const TCHAR * szSource);
  void StringCat(TCHAR * szTarget, size_t cchTargetMax, const TCHAR * szSource);
 +void StringCat(TCHAR * szTarget, size_t cchTargetMax, const char * szSource);
  #endif
  //-----------------------------------------------------------------------------
 diff --git a/test/StormTest.cpp b/test/StormTest.cpp index 596ebae..582529c 100755 --- a/test/StormTest.cpp +++ b/test/StormTest.cpp @@ -31,6 +31,7 @@  #ifndef STORMLIB_WINDOWS
  #include <dirent.h>
 +#include <alsa/asoundlib.h>                 // sudo apt-get install libasound2-dev
  #endif
  //------------------------------------------------------------------------------
 @@ -53,6 +54,7 @@ typedef enum _EXTRA_TYPE  {
      NoExtraType = 0,
      ListFile,
 +    Utf8File,
      TwoFiles,
      PatchList,
      HashValues,
 @@ -73,6 +75,13 @@ typedef struct _TEST_EXTRA_ONEFILE      LPCTSTR szFile;                         // The name of the (list)file
  } TEST_EXTRA_ONEFILE, *PTEST_EXTRA_ONEFILE;
 +typedef struct _TEST_EXTRA_UTF8
 +{
 +    EXTRA_TYPE Type;                        // Must be Utf8File
 +    const BYTE * szMpqFile;                 // (UTF-8) The name of the MPQ file
 +    const BYTE * szListFile;                // (UTF-8) The name of the listfile
 +} TEST_EXTRA_UTF8, *PTEST_EXTRA_UTF8;
 +
  typedef struct _TEST_EXTRA_TWOFILES
  {
      EXTRA_TYPE Type;                        // Must be TwoFiles
 @@ -101,14 +110,23 @@ typedef struct _TEST_EXTRA_HASHVALS      TEST_EXTRA_HASHVAL Items[2];
  } TEST_EXTRA_HASHVALS, *PTEST_EXTRA_HASHVALS;
 -typedef struct _TEST_INFO
 +typedef struct _TEST_INFO1
  {
      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
 +    LPCSTR szDataHash;                      // Compound name+data hash
 +    DWORD  dwFlags;                         // Flags for testing the file. Low 16 bits contains number of files
 +    const void * pExtra;
 +} TEST_INFO1, *PTEST_INFO1;
 +
 +typedef struct _TEST_INFO2
 +{
 +    LPCSTR szName1;                         // (UTF-8) MPQ name
 +    LPCSTR szName2;                         // (UTF-8) Added file 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_INFO, *PTEST_INFO;
 +} TEST_INFO2, *PTEST_INFO2;
  typedef struct _LINE_INFO
  {
 @@ -117,6 +135,23 @@ typedef struct _LINE_INFO      const char * szLine;
  } LINE_INFO, *PLINE_INFO;
 +typedef struct _WAVE_FILE_HEADER
 +{
 +    DWORD dwRiffSignature;                  // 'RIFF'
 +    DWORD dwFileSize;
 +    DWORD dwTypeSignature;                  // 'WAVE'
 +    DWORD dwChunkMarker;                    // 'fmt\0'
 +    DWORD dwLength;                         // 16
 +    USHORT wFormat;                         // 1 = PCM
 +    USHORT wChannels;                       // Number of channels
 +    DWORD dwSamplesPerSec;                  // Bitrate
 +    DWORD dwAvgSamplesPerSec;
 +    USHORT wBlockAlign;
 +    USHORT wBitsPerSample;
 +    DWORD dwDataSignature;                  // 'data'
 +    DWORD dwDataSize;                       // 'data'
 +} WAVE_FILE_HEADER, *PWAVE_FILE_HEADER;
 +
  //------------------------------------------------------------------------------
  // Local variables
 @@ -161,9 +196,17 @@ static TCHAR szMpqPatchDir[MAX_PATH] = {0};  #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_BIGFILE            0x20000000              // Add a big file to the MPQ
 +#define TFLG_COMPACT            0x40000000              // Perform archive compacting between two opens
  #define TFLG_WILL_FAIL          0x80000000              // The process is expected to fail
 +// Flags for TestCreateArchive
 +#define CFLG_V2                 0x00010000              // Create archive version 2
 +#define CFLG_V4                 0x00020000              // Create archive version 4
 +#define CFLG_EMPTY              0x00040000              // The archive will be empty
 +#define CFLG_NONSTD_NAMES       0x00080000              // Create archive containing non-standard names
 +#define CFLG_MPQEDITOR          0x00100000              // Create archive like MPQEditor would do
 +
  // Flags for TestOpenArchive_SignatureTest
  #define SFLAG_VERIFY_BEFORE     0x00000001              // Verify signature before modification
  #define SFLAG_CREATE_ARCHIVE    0x00000002              // Create new archive
 @@ -211,29 +254,12 @@ static DWORD WaveCompressions[] =      MPQ_COMPRESSION_BZIP2
  };
 -static const wchar_t szUnicodeName1[] = {   // Czech
 -    0x010C, 0x0065, 0x0073, 0x006B, 0x00FD, _T('.'), _T('m'), _T('p'), _T('q'), 0
 -};
 -
 -static const wchar_t szUnicodeName2[] = {   // Russian
 -    0x0420, 0x0443, 0x0441, 0x0441, 0x043A, 0x0438, 0x0439, _T('.'), _T('m'), _T('p'), _T('q'), 0
 -};
 -
 -static const wchar_t szUnicodeName3[] = {   // Greek
 -    0x03B5, 0x03BB, 0x03BB, 0x03B7, 0x03BD, 0x03B9, 0x03BA, 0x03AC, _T('.'), _T('m'), _T('p'), _T('q'), 0
 -};
 -
 -static const wchar_t szUnicodeName4[] = {   // Chinese
 -    0x65E5, 0x672C, 0x8A9E, _T('.'), _T('m'), _T('p'), _T('q'), 0
 -};
 -
 -static const wchar_t szUnicodeName5[] = {   // Japanese
 -    0x7B80, 0x4F53, 0x4E2D, 0x6587, _T('.'), _T('m'), _T('p'), _T('q'), 0
 -};
 -
 -static const wchar_t szUnicodeName6[] = {   // Arabic
 -    0x0627, 0x0644, 0x0639, 0x0639, 0x0631, 0x0628, 0x064A, 0x0629, _T('.'), _T('m'), _T('p'), _T('q'), 0
 -};
 +static const BYTE szPlainName_CZE[] = {                                                      0xc4, 0x8c, 0x65, 0x73, 0x6b, 0xc3, 0xbd, 0x2e, 0x6d, 0x70, 0x71, 0x00};   // (UTF-8) Czech
 +static const BYTE szPlainName_RUS[] = {            0xd0, 0xa0, 0xd1, 0x83, 0xd1, 0x81, 0xd1, 0x81, 0xd0, 0xba, 0xd0, 0xb8, 0xd0, 0xb9, 0x2e, 0x6d, 0x70, 0x71, 0x00};   // (UTF-8) Russian
 +static const BYTE szPlainName_GRC[] = {0xce, 0xb5, 0xce, 0xbb, 0xce, 0xbb, 0xce, 0xb7, 0xce, 0xbd, 0xce, 0xb9, 0xce, 0xba, 0xce, 0xac, 0x2e, 0x6d, 0x70, 0x71, 0x00};   // (UTF-8) Greek
 +static const BYTE szPlainName_CHN[] = {                                          0xe6, 0x97, 0xa5, 0xe6, 0x9c, 0xac, 0xe8, 0xaa, 0x9e, 0x2e, 0x6d, 0x70, 0x71, 0x00};   // (UTF-8) Chinese
 +static const BYTE szPlainName_JPN[] = {                        0xe7, 0xae, 0x80, 0xe4, 0xbd, 0x93, 0xe4, 0xb8, 0xad, 0xe6, 0x96, 0x87, 0x2e, 0x6d, 0x70, 0x71, 0x00};   // (UTF-8) Japanese
 +static const BYTE szPlainName_SAU[] = {0xd8, 0xa7, 0xd9, 0x84, 0xd8, 0xb9, 0xd8, 0xb9, 0xd8, 0xb1, 0xd8, 0xa8, 0xd9, 0x8a, 0xd8, 0xa9, 0x2e, 0x6d, 0x70, 0x71, 0x00};   // (UTF-8) Arabic
  static SFILE_MARKERS MpqMarkers[] =
  {
 @@ -261,6 +287,19 @@ static EXTRA_TYPE GetExtraType(const void * pExtra)      }
  }
 +LPCTSTR SwapMpqName(LPTSTR szBuffer, size_t ccBuffer, LPCTSTR szMpqName, PTEST_EXTRA_UTF8 pExtra)
 +{
 +    if((pExtra != NULL) && (pExtra->Type == Utf8File))
 +    {
 +        StringCopy(szBuffer, ccBuffer, (LPCSTR)(pExtra->szMpqFile));
 +        return szBuffer;
 +    }
 +    else
 +    {
 +        return szMpqName;
 +    }
 +}
 +
  template <typename XCHAR>
  static bool IsFullPath(const XCHAR * szFileName)
  {
 @@ -313,6 +352,48 @@ xchar * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, xchar * szBuffer)      return szSaveBuffer;
  }
 +const char * GetFileText(PFILE_DATA pFileData)
 +{
 +    const char * szFileText = (const char *)(pFileData->FileData);
 +
 +    // Skip UTF-8 marker
 +    if(pFileData->dwFileSize > 3 && !memcmp(szFileText, "\xEF\xBB\xBF", 3))
 +        szFileText += 3;
 +    return szFileText;
 +}
 +
 +static void PlayWaveSound(PFILE_DATA pFileData)
 +{
 +#ifdef STORMLIB_WINDOWS
 +    PlaySound((LPCTSTR)pFileData->FileData, NULL, SND_MEMORY);
 +#endif    
 +
 +#ifdef STORMLIB_LINUX
 +    PWAVE_FILE_HEADER pHeader = (PWAVE_FILE_HEADER)(pFileData->FileData);
 +    snd_pcm_hw_params_t *params;
 +    snd_pcm_t *pcm_handle;
 +    unsigned int bitrate = pHeader->dwSamplesPerSec;
 +
 +    if(snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0) >= 0)
 +    {
 +        snd_pcm_format_t format = (pHeader->wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_S8;
 +        unsigned int divider = (pHeader->wBitsPerSample == 16) ? 4 : 2;
 +        void * wave_buffer = &pFileData->FileData[sizeof(WAVE_FILE_HEADER)];
 +
 +        snd_pcm_hw_params_alloca(¶ms);
 +        snd_pcm_hw_params_any(pcm_handle, params);
 +        snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
 +        snd_pcm_hw_params_set_format(pcm_handle, params, format);
 +        snd_pcm_hw_params_set_channels(pcm_handle, params, pHeader->wChannels);
 +        snd_pcm_hw_params_set_rate_near(pcm_handle, params, &bitrate, 0);
 +        snd_pcm_hw_params(pcm_handle, params);
 +
 +        snd_pcm_writei(pcm_handle, wave_buffer, pHeader->dwDataSize / divider);
 +        snd_pcm_close(pcm_handle);
 +    }
 +#endif    
 +}                    
 +
  static bool CompareBlocks(LPBYTE pbBlock1, LPBYTE pbBlock2, DWORD dwLength, DWORD * pdwDifference)
  {
      for(DWORD i = 0; i < dwLength; i++)
 @@ -764,8 +845,8 @@ static DWORD ForEachFile_VerifyFileHash(LPCTSTR szFullPath, void * lpContext)      if(szExtension == NULL)
          return ERROR_SUCCESS;
 -    // Skip .SHA256
 -    if(!_tcsicmp(szExtension, szHashExtension))
 +    // Skip .sha and .sha256 files
 +    if(!_tcsicmp(szExtension, _T(".txt")) || !_tcsicmp(szExtension, _T(".sha")) || !_tcsicmp(szExtension, szHashExtension))
          return ERROR_SUCCESS;
      // Load the local file to memory
 @@ -782,7 +863,7 @@ static DWORD ForEachFile_VerifyFileHash(LPCTSTR szFullPath, void * lpContext)              {
                  // Compare the SHA256
                  StringCopy(szHashTextA, _countof(szHashTextA), szHashText);
 -                if(_strnicmp(szHashTextA, (char *)pFileData->FileData, (SHA256_DIGEST_SIZE * 2)))
 +                if(_strnicmp(szHashTextA, GetFileText(pFileData), (SHA256_DIGEST_SIZE * 2)))
                  {
                      SetLastError(dwErrCode = ERROR_FILE_CORRUPT);
                      pLogger->PrintError(_T("File hash check failed: %s"), szFullPath);
 @@ -798,6 +879,7 @@ static DWORD ForEachFile_VerifyFileHash(LPCTSTR szFullPath, void * lpContext)      return dwErrCode;
  }
 +// Verify SHA256 of each MPQ that we have in the collection
  static DWORD VerifyFileHashes(LPCTSTR szSubDirectory)
  {
      TLogHelper Logger("TestVerifyHash");
 @@ -1570,7 +1652,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");
 +    // fp = fopen("E:\\mpq-listing.txt", "wt");
      // Prepare hashing
      md5_init(&md5state);
 @@ -1605,10 +1687,8 @@ static DWORD SearchArchive(                  // Play sound files, if required
                  if((dwSearchFlags & SEARCH_FLAG_PLAY_WAVES) && strstr(sf.cFileName, ".wav") != NULL)
                  {
 -#ifdef _MSC_VER
                      pLogger->PrintProgress("Playing sound %s", sf.cFileName);
 -                    PlaySound((LPCTSTR)pFileData->FileData, NULL, SND_MEMORY);
 -#endif
 +                    PlayWaveSound(pFileData);
                  }
                  // Debug: Show CRC32 of each file in order to debug differences
 @@ -1744,37 +1824,6 @@ static DWORD CreateNewArchive_V2(TLogHelper * pLogger, LPCTSTR szPlainName, DWOR      return ERROR_SUCCESS;
  }
 -// Creates new archive with UNICODE name. Adds prefix to the name
 -static DWORD CreateNewArchiveU(TLogHelper * pLogger, const wchar_t * szPlainName, DWORD dwCreateFlags, DWORD dwMaxFileCount)
 -{
 -#ifdef _UNICODE
 -    HANDLE hMpq = NULL;
 -    TCHAR szMpqName[MAX_PATH+1];
 -    TCHAR szFullPath[MAX_PATH];
 -
 -    // Construct the full UNICODE name
 -    CreateFullPathName(szFullPath, _countof(szFullPath), NULL, _T("StormLibTest_"));
 -    StringCopy(szMpqName, _countof(szMpqName), szFullPath);
 -    StringCat(szMpqName, _countof(szMpqName), szPlainName);
 -
 -    // Make sure that the MPQ is deleted
 -    _tremove(szMpqName);
 -
 -    // Create the archive
 -    pLogger->PrintProgress("Creating new archive with UNICODE name ...");
 -    if(!SFileCreateArchive(szMpqName, dwCreateFlags, dwMaxFileCount, &hMpq))
 -        return pLogger->PrintError(_T("Failed to create archive %s"), szMpqName);
 -
 -    SFileCloseArchive(hMpq);
 -#else
 -    pLogger = pLogger;
 -    szPlainName = szPlainName;
 -    dwCreateFlags = dwCreateFlags;
 -    dwMaxFileCount = dwMaxFileCount;
 -#endif
 -    return ERROR_SUCCESS;
 -}
 -
  static DWORD OpenExistingArchive(TLogHelper * pLogger, LPCTSTR szFullPath, DWORD dwOpenFlags, HANDLE * phMpq)
  {
      HANDLE hMpq = NULL;
 @@ -2373,6 +2422,23 @@ static DWORD TestOpenArchive_Extra_ListFile(TLogHelper & Logger, HANDLE hMpq, PT      return dwErrCode;
  }
 +static DWORD TestOpenArchive_Extra_Utf8File(TLogHelper & Logger, HANDLE hMpq, PTEST_EXTRA_UTF8 pExtra)
 +{
 +    DWORD dwErrCode = ERROR_SUCCESS;
 +    TCHAR szFullName[MAX_PATH];
 +    TCHAR szListName[MAX_PATH];
 +
 +    if(IS_VALID_STRING(pExtra->szListFile))
 +    {
 +        StringCopy(szListName, _countof(szListName), (const char *)pExtra->szListFile);
 +        Logger.PrintProgress(_T("Adding listfile %s ..."), szListName);
 +        CreateFullPathName(szFullName, _countof(szFullName), szListFileDir, szListName);
 +        if((dwErrCode = SFileAddListFile(hMpq, szFullName)) != ERROR_SUCCESS)
 +            Logger.PrintMessage("Failed to add the listfile to the MPQ");
 +    }
 +    return dwErrCode;
 +}
 +
  static DWORD TestOpenArchive_Extra_TwoFiles(TLogHelper & Logger, HANDLE hMpq, DWORD dwSearchFlags, PTEST_EXTRA_TWOFILES pExtra)
  {
      PFILE_DATA pFileData1 = NULL;
 @@ -2478,6 +2544,9 @@ static DWORD TestOpenArchive_ExtraType(TLogHelper & Logger, HANDLE hMpq, DWORD d          case ListFile:
              return TestOpenArchive_Extra_ListFile(Logger, hMpq, (PTEST_EXTRA_ONEFILE)(pExtra));
 +        case Utf8File:
 +            return TestOpenArchive_Extra_Utf8File(Logger, hMpq, (PTEST_EXTRA_UTF8)(pExtra));
 +
          case TwoFiles:
              return TestOpenArchive_Extra_TwoFiles(Logger, hMpq, dwSearchFlags, (PTEST_EXTRA_TWOFILES)(pExtra));
 @@ -2494,14 +2563,28 @@ static DWORD TestOpenArchive_ExtraType(TLogHelper & Logger, HANDLE hMpq, DWORD d  static DWORD TestOpenArchive_ModifyArchive(TLogHelper & Logger, HANDLE hMpq, DWORD dwFlags)
  {
 -    DWORD dwExpectedError = (dwFlags & TFLG_READ_ONLY) ? ERROR_ACCESS_DENIED : ERROR_SUCCESS;
 +    DWORD dwExpectedError;
      DWORD dwErrCode = ERROR_SUCCESS;
 +    TCHAR szFullPath[MAX_PATH];
      // 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);
 +
 +        if(dwFlags & TFLG_BIGFILE)
 +        {
 +            dwExpectedError = (dwFlags & TFLG_WILL_FAIL) ? ERROR_DISK_FULL : ERROR_SUCCESS;
 +
 +            CreateFullPathName(szFullPath, _countof(szFullPath), szDataFileDir, _T("new-file-big.mp4"));
 +            dwErrCode = AddLocalFileToMpq(&Logger, hMpq, "added-extra-file.mp4", szFullPath);
 +            dwErrCode = (dwErrCode == dwExpectedError) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
 +        }
 +        else
 +        {
 +            dwExpectedError = (dwFlags & TFLG_READ_ONLY) ? ERROR_ACCESS_DENIED : ERROR_SUCCESS;
 +            dwErrCode = AddFileToMpq(&Logger, hMpq, "AddedFile01.txt", "This is a file added to signed MPQ", MPQ_FILE_COMPRESS, 0, dwExpectedError);
 +        }
      }
      return dwErrCode;
  }
 @@ -2581,8 +2664,8 @@ static DWORD TestOpenArchive_GetFileInfo(TLogHelper & Logger, HANDLE hMpq, DWORD  }
  static DWORD TestOpenArchive(
 -    LPCTSTR szMpqName1,                             // Name of the MPQ
 -    LPCTSTR szMpqName2,                             // Name of the MPQ (original name or name of a copy)
 +    LPCTSTR szMpqName1,                             // (UTF-8) Name of the MPQ
 +    LPCTSTR szMpqName2,                             // (UTF-8) Name of the MPQ (original name or name of a copy)
      LPCSTR szExpectedHash,                          // Expected name+data hash
      DWORD dwFlags,                                  // Test flags. Lower bits contains the number of files
      const void * pExtra)                            // Extra parameter
 @@ -2594,11 +2677,15 @@ static DWORD TestOpenArchive(      DWORD dwOpenFlags = 0;
      DWORD dwMpqFlags = 0;
      DWORD dwErrCode;
 +    TCHAR szMpqNameBuff[MAX_PATH];
      // 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;
 +    // Shall we switch the name of the MPQ?
 +    szMpqName1 = SwapMpqName(szMpqNameBuff, _countof(szMpqNameBuff), szMpqName1, (PTEST_EXTRA_UTF8)(pExtra));
 +
      // If the file is a partial MPQ, don't load all files
      if(_tcsstr(szMpqName1, _T(".MPQ.part")) == NULL)
          dwSearchFlags |= SEARCH_FLAG_LOAD_FILES;
 @@ -2664,7 +2751,7 @@ static DWORD TestOpenArchive(      return Logger.PrintVerdict(dwErrCode);
  }
 -static DWORD TestOpenArchive(const TEST_INFO & TestInfo)
 +static DWORD TestOpenArchive(const TEST_INFO1 & TestInfo)
  {
      return TestOpenArchive(TestInfo.szName1,            // Name of the MPQ
                             TestInfo.szName2,            // Name of the listfile or NULL
 @@ -2785,7 +2872,7 @@ static DWORD TestReopenArchive(      }
      // Open the archive and load some files
 -    if((dwErrCode == ERROR_SUCCESS) && STORMLIB_TEST_FLAGS(dwFlags, TFLG_COMPACT | TFLG_MODIFY, TFLG_COMPACT))
 +    if((dwErrCode == ERROR_SUCCESS) && STORMLIB_TEST_FLAGS(dwFlags, TFLG_COMPACT | TFLG_MODIFY | TFLG_BIGFILE, TFLG_COMPACT))
      {
          // Open the archive
          if((dwErrCode = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq)) == ERROR_SUCCESS)
 @@ -2853,144 +2940,60 @@ static DWORD TestOpenArchive_SignatureTest(LPCTSTR szPlainName, LPCTSTR szOrigin      return dwErrCode;
  }
 -static DWORD TestOpenArchive_CompactArchive(LPCTSTR szPlainName, LPCTSTR szCopyName, DWORD bAddUserData)
 +static DWORD TestCreateArchive(LPCTSTR szPlainName, LPCSTR szFileName, DWORD dwFlags)
  {
 -    TLogHelper Logger("TestCompactMpq", szPlainName);
 -    ULONGLONG PreMpqDataSize = (bAddUserData) ? 0x400 : 0;
 -    ULONGLONG UserDataSize = (bAddUserData) ? 0x531 : 0;
 -    HANDLE hMpq;
 -    DWORD dwFileCount1 = 0;
 -    DWORD dwFileCount2 = 0;
 -    TCHAR szFullPath[MAX_PATH];
 -    BYTE FileHash1[MD5_DIGEST_SIZE];
 -    BYTE FileHash2[MD5_DIGEST_SIZE];
 +    TLogHelper Logger("CreateNewMpq", szPlainName);
 +    HANDLE hMpq = NULL;
 +    DWORD dwMaxFileCount = dwFlags & 0x0000FFFF;
 +    DWORD dwCreateFlags = 0;
 +    DWORD dwFileCount = 0;
      DWORD dwErrCode;
 -    // Create copy of the archive, with interleaving some user data
 -    dwErrCode = CreateFileCopy(&Logger, szPlainName, szCopyName, szFullPath, _countof(szFullPath), PreMpqDataSize, UserDataSize);
 -
 -    // Open the archive and load some files
 -    if(dwErrCode == ERROR_SUCCESS)
 -    {
 -        // Open the archive
 -        dwErrCode = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq);
 -        if(dwErrCode != ERROR_SUCCESS)
 -            return dwErrCode;
 -
 -        // Verify presence of (listfile) and (attributes)
 -        CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, true);
 -        CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, true);
 -
 -        // Search the archive and load every file
 -        dwErrCode = SearchArchive(&Logger, hMpq, SEARCH_FLAG_LOAD_FILES | SEARCH_FLAG_HASH_FILES, &dwFileCount1, FileHash1);
 -        SFileCloseArchive(hMpq);
 -    }
 -
 -    // Try to compact the MPQ
 -    if(dwErrCode == ERROR_SUCCESS)
 -    {
 -        // Open the archive again
 -        dwErrCode = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq);
 -        if(dwErrCode != ERROR_SUCCESS)
 -            return dwErrCode;
 -
 -        // Compact the archive
 -        Logger.PrintProgress("Compacting archive %s ...", GetShortPlainName(szFullPath));
 -        if(!SFileSetCompactCallback(hMpq, CompactCallback, &Logger))
 -            dwErrCode = Logger.PrintError(_T("Failed to compact archive %s"), szFullPath);
 -
 -        SFileCompactArchive(hMpq, NULL, false);
 -        SFileCloseArchive(hMpq);
 -    }
 -
 -    // Open the archive and load some files
 -    if(dwErrCode == ERROR_SUCCESS)
 -    {
 -        // Open the archive
 -        dwErrCode = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq);
 -        if(dwErrCode != ERROR_SUCCESS)
 -            return dwErrCode;
 -
 -        // Verify presence of (listfile) and (attributes)
 -        CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, true);
 -        CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, true);
 -
 -        // Search the archive and load every file
 -        dwErrCode = SearchArchive(&Logger, hMpq, SEARCH_FLAG_LOAD_FILES | SEARCH_FLAG_HASH_FILES, &dwFileCount2, FileHash2);
 -        SFileCloseArchive(hMpq);
 -    }
 -
 -    // Compare the file counts and their hashes
 -    if(dwErrCode == ERROR_SUCCESS)
 -    {
 -        if(dwFileCount2 != dwFileCount1)
 -            Logger.PrintMessage("Different file count after compacting archive: %u vs %u", dwFileCount2, dwFileCount1);
 -
 -        if(memcmp(FileHash2, FileHash1, MD5_DIGEST_SIZE))
 -            Logger.PrintMessage("Different file hash after compacting archive");
 -    }
 -
 -    return dwErrCode;
 -}
 -
 -static DWORD TestOpenArchive_AddFiles2GB(LPCTSTR szMpqName, DWORD /* dwFlags */)
 -{
 -    TLogHelper Logger("TestAddFileOver2GB", szMpqName);
 -    LPCTSTR szCopyName = _T("size-overflow.w3m");
 -    HANDLE hMpq = NULL;
 -    DWORD dwErrCode = ERROR_SUCCESS;
 -    TCHAR szFullPath[MAX_PATH];
 -    bool bSucceeded = true;
 +    // Fixup the MPQ format
 +    dwCreateFlags |= (dwFlags & CFLG_EMPTY) ? 0 : MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES;
 +    dwCreateFlags |= (dwFlags & CFLG_V2) ? MPQ_CREATE_ARCHIVE_V2 : 0;
 +    dwCreateFlags |= (dwFlags & CFLG_V4) ? MPQ_CREATE_ARCHIVE_V4 : 0;
 -    // Copy the archive so we won't fuck up the original one
 -    dwErrCode = OpenExistingArchiveWithCopy(&Logger, szMpqName, szCopyName, &hMpq);
 +    // Create the full path name
 +    dwErrCode = CreateNewArchive(&Logger, szPlainName, dwCreateFlags, dwMaxFileCount, &hMpq);
      if(dwErrCode == ERROR_SUCCESS)
      {
 -        // Create both file names
 -        CreateFullPathName(szFullPath, _countof(szFullPath), szDataFileDir, _T("new-file-big.mp4"));
 -
 -        // Add the file to MPQ. This must fail, because the map size
 -        // will go over 2GB and StormLib marks the file malformed on subsequent open
 -        dwErrCode = AddLocalFileToMpq(&Logger, hMpq, "added-extra-file.mp4", szFullPath);
 -        if(dwErrCode != ERROR_DISK_FULL)
 -            bSucceeded = false;
 -
 -        SFileCloseArchive(hMpq);
 -    }
 -
 -    // Open the MPQ again
 -    if(dwErrCode == ERROR_SUCCESS || dwErrCode == ERROR_DISK_FULL)
 -    {
 -        CreateFullPathName(szFullPath, _countof(szFullPath), NULL, szCopyName);
 -        if((dwErrCode = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq)) == ERROR_SUCCESS)
 +        // Add non-standard names, if needed
 +        if(dwFlags & CFLG_NONSTD_NAMES)
          {
 -            DWORD dwFlags = 0;
 -
 -            SFileGetFileInfo(hMpq, SFileMpqFlags, &dwFlags, sizeof(dwFlags), NULL);
 -            if(dwFlags & MPQ_FLAG_MALFORMED)
 -                bSucceeded = false;
 -
 -            SFileCloseArchive(hMpq);
 +            // Add few files and close the archive
 +            AddFileToMpq(&Logger, hMpq, "AddedFile000.txt", "This is the file data 000.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "\\/\\/\\/\\AddedFile001.txt", "This is the file data 001.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "\\\\\\\\\\\\\\\\", "This is the file data 002.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "////////////////", "This is the file data 003.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "//\\//\\//\\//\\", "This is the file data 004.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "................", "This is the file data 005.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "//****//****//****//****.***", "This is the file data 006.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "//*??*//*??*//*??*//?**?.?*?", "This is the file data 007.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "\\/\\/File.txt", "This is the file data 008.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "\\/\\/File.txt..", "This is the file data 009.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "Dir1\\Dir2\\Dir3\\File.txt..", "This is the file data 010.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "\\Dir1\\Dir2\\Dir3\\File.txt..", "This is the file data 011.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "\\\\Dir1\\\\Dir2\\\\Dir3\\\\File.txt..", "This is the file data 012.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "/Dir1/Dir2/Dir3/File.txt..", "This is the file data 013.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "////Dir1////Dir2////Dir3////File.txt..", "This is the file data 014.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "\\//\\Dir1\\//\\Dir2\\//\\File.txt..", "This is the file data 015.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "\x10\x11\x12\x13\\\x14\x15\x16\x17\\\x18\x19\x1a\x1b\\\x1c\x1D\x1E\x1F.txt", "This is the file data 016.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "\x09\x20\x09\x20\\\x20\x09\x20\x09\\\x09\x20\x09\x20\\\x20\x09\x20\x09.txt", "This is the file data 017.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "\x80\x91\xA2\xB3\\\xC4\xD5\xE6\xF7\\\x80\x91\xA2\xB3.txt", "This is the file data 018.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "Dir1\x20\x09\x20\\Dir2\x20\x09\x20\\File.txt\x09\x09\x20\x2e", "This is the file data 019.", MPQ_FILE_COMPRESS);
 +            AddFileToMpq(&Logger, hMpq, "Dir1\x20\x09\x20\\Dir2\x20\x09\x20\\\x09\x20\x2e\x09\x20\x2e", "This is the file data 020.", MPQ_FILE_COMPRESS);
          }
 -    }
 -    // Both tests must have succeeded
 -    if(bSucceeded == false)
 -        dwErrCode = ERROR_CAN_NOT_COMPLETE;
 -    return dwErrCode;
 -}
 -
 -static DWORD TestCreateArchive_EmptyMpq(LPCTSTR szPlainName, DWORD dwCreateFlags)
 -{
 -    TLogHelper Logger("CreateEmptyMpq", szPlainName);
 -    HANDLE hMpq = NULL;
 -    DWORD dwFileCount = 0;
 -    DWORD dwErrCode;
 +        // Like MPQEditor: Flush archive, add one file, flush again
 +        if(dwFlags & CFLG_MPQEDITOR)
 +        {
 +            SFileFlushArchive(hMpq);
 +            dwErrCode = AddFileToMpq(&Logger, hMpq, szFileName, "This is the file data.", MPQ_FILE_COMPRESS);
 +            SFileFlushArchive(hMpq);
 +        }
 -    // Create the full path name
 -    dwErrCode = CreateNewArchive(&Logger, szPlainName, dwCreateFlags, 0, &hMpq);
 -    if(dwErrCode == ERROR_SUCCESS)
 -    {
 +        // Search the archive
          SearchArchive(&Logger, hMpq);
          SFileCloseArchive(hMpq);
      }
 @@ -3001,11 +3004,25 @@ static DWORD TestCreateArchive_EmptyMpq(LPCTSTR szPlainName, DWORD dwCreateFlags          dwErrCode = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq);
          if(dwErrCode == ERROR_SUCCESS)
          {
 +            // Retrieve the number of files
              SFileGetFileInfo(hMpq, SFileMpqNumberOfFiles, &dwFileCount, sizeof(dwFileCount), NULL);
 -            CheckIfFileIsPresent(&Logger, hMpq, "File00000000.xxx", false);
 -            CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, false);
 -            SearchArchive(&Logger, hMpq);
 +            // Special check for empty MPQs
 +            if(dwFlags & CFLG_EMPTY)
 +            {
 +                if(dwFileCount != 0)
 +                    dwErrCode = ERROR_FILE_CORRUPT;
 +                CheckIfFileIsPresent(&Logger, hMpq, "File00000000.xxx", false);
 +                CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, false);
 +                SearchArchive(&Logger, hMpq);
 +            }
 +            else
 +            {
 +                if(dwFileCount == 0)
 +                    dwErrCode = ERROR_FILE_CORRUPT;
 +            }
 +
 +            // Close the MPQ
              SFileCloseArchive(hMpq);
          }
      }
 @@ -3013,6 +3030,18 @@ static DWORD TestCreateArchive_EmptyMpq(LPCTSTR szPlainName, DWORD dwCreateFlags      return dwErrCode;
  }
 +static DWORD TestCreateArchive(const TEST_INFO2 & TestInfo)
 +{
 +    TCHAR szPlainNameT[MAX_PATH];
 +
 +    // Always prefix the archive name with "StormLibTest_"
 +    StringCopy(szPlainNameT, _countof(szPlainNameT), "StormLibTest_");
 +    StringCat(szPlainNameT, _countof(szPlainNameT), TestInfo.szName1);
 +
 +    // Perform creation of the archive
 +    return TestCreateArchive(szPlainNameT, TestInfo.szName2, TestInfo.dwFlags);
 +}
 +
  static DWORD TestCreateArchive_TestGaps(LPCTSTR szPlainName)
  {
      TLogHelper Logger("TestCreateGaps", szPlainName);
 @@ -3086,73 +3115,6 @@ static DWORD TestCreateArchive_TestGaps(LPCTSTR szPlainName)      return dwErrCode;
  }
 -static DWORD TestCreateArchive_NonStdNames(LPCTSTR szPlainName)
 -{
 -    TLogHelper Logger("TestNonStdNames", szPlainName);
 -    HANDLE hMpq = NULL;
 -    DWORD dwErrCode = ERROR_SUCCESS;
 -
 -    // Create new MPQ
 -    dwErrCode = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES | MPQ_FORMAT_VERSION_1, 4000, &hMpq);
 -    if(dwErrCode == ERROR_SUCCESS)
 -    {
 -        // Add few files and close the archive
 -        AddFileToMpq(&Logger, hMpq, "AddedFile000.txt", "This is the file data 000.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "\\/\\/\\/\\AddedFile001.txt", "This is the file data 001.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "\\\\\\\\\\\\\\\\", "This is the file data 002.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "////////////////", "This is the file data 003.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "//\\//\\//\\//\\", "This is the file data 004.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "................", "This is the file data 005.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "//****//****//****//****.***", "This is the file data 006.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "//*??*//*??*//*??*//?**?.?*?", "This is the file data 007.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "\\/\\/File.txt", "This is the file data 008.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "\\/\\/File.txt..", "This is the file data 009.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "Dir1\\Dir2\\Dir3\\File.txt..", "This is the file data 010.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "\\Dir1\\Dir2\\Dir3\\File.txt..", "This is the file data 011.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "\\\\Dir1\\\\Dir2\\\\Dir3\\\\File.txt..", "This is the file data 012.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "/Dir1/Dir2/Dir3/File.txt..", "This is the file data 013.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "////Dir1////Dir2////Dir3////File.txt..", "This is the file data 014.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "\\//\\Dir1\\//\\Dir2\\//\\File.txt..", "This is the file data 015.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "\x10\x11\x12\x13\\\x14\x15\x16\x17\\\x18\x19\x1a\x1b\\\x1c\x1D\x1E\x1F.txt", "This is the file data 016.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "\x09\x20\x09\x20\\\x20\x09\x20\x09\\\x09\x20\x09\x20\\\x20\x09\x20\x09.txt", "This is the file data 017.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "\x80\x91\xA2\xB3\\\xC4\xD5\xE6\xF7\\\x80\x91\xA2\xB3.txt", "This is the file data 018.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "Dir1\x20\x09\x20\\Dir2\x20\x09\x20\\File.txt\x09\x09\x20\x2e", "This is the file data 019.", MPQ_FILE_COMPRESS);
 -        AddFileToMpq(&Logger, hMpq, "Dir1\x20\x09\x20\\Dir2\x20\x09\x20\\\x09\x20\x2e\x09\x20\x2e", "This is the file data 020.", MPQ_FILE_COMPRESS);
 -
 -        SFileCloseArchive(hMpq);
 -    }
 -
 -    return ERROR_SUCCESS;
 -}
 -
 -static DWORD TestCreateArchive_MpqEditor(LPCTSTR szPlainName, LPCSTR szFileName)
 -{
 -    TLogHelper Logger("CreateMpqEditor", szPlainName);
 -    HANDLE hMpq = NULL;
 -    DWORD dwErrCode = ERROR_SUCCESS;
 -
 -    // Create new MPQ
 -    dwErrCode = CreateNewArchive_V2(&Logger, szPlainName, MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, 4000, &hMpq);
 -    if(dwErrCode == ERROR_SUCCESS)
 -    {
 -        // Flush the archive first
 -        SFileFlushArchive(hMpq);
 -
 -        // Add one file
 -        dwErrCode = AddFileToMpq(&Logger, hMpq, szFileName, "This is the file data.", MPQ_FILE_COMPRESS);
 -
 -        // Flush the archive again
 -        SFileFlushArchive(hMpq);
 -        SFileCloseArchive(hMpq);
 -    }
 -    else
 -    {
 -        dwErrCode = GetLastError();
 -    }
 -
 -    return dwErrCode;
 -}
 -
  static DWORD TestCreateArchive_FillArchive(LPCTSTR szPlainName, DWORD dwCreateFlags)
  {
      TLogHelper Logger("TestCreateFull", szPlainName);
 @@ -3328,39 +3290,6 @@ static DWORD TestCreateArchive_IncMaxFileCount(LPCTSTR szPlainName)      return dwErrCode;
  }
 -static DWORD TestCreateArchive_UnicodeNames()
 -{
 -    TLogHelper Logger("MpqUnicodeName");
 -    DWORD dwCreateFlags = MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES;
 -    DWORD dwErrCode = ERROR_SUCCESS;
 -
 -    dwErrCode = CreateNewArchiveU(&Logger, szUnicodeName1, dwCreateFlags | MPQ_CREATE_ARCHIVE_V1, 15);
 -    if(dwErrCode != ERROR_SUCCESS)
 -        return dwErrCode;
 -
 -    dwErrCode = CreateNewArchiveU(&Logger, szUnicodeName2, dwCreateFlags | MPQ_CREATE_ARCHIVE_V2, 58);
 -    if(dwErrCode != ERROR_SUCCESS)
 -        return dwErrCode;
 -
 -    dwErrCode = CreateNewArchiveU(&Logger, szUnicodeName3, dwCreateFlags | MPQ_CREATE_ARCHIVE_V3, 15874);
 -    if(dwErrCode != ERROR_SUCCESS)
 -        return dwErrCode;
 -
 -    dwErrCode = CreateNewArchiveU(&Logger, szUnicodeName4, dwCreateFlags | MPQ_CREATE_ARCHIVE_V4, 87541);
 -    if(dwErrCode != ERROR_SUCCESS)
 -        return dwErrCode;
 -
 -    dwErrCode = CreateNewArchiveU(&Logger, szUnicodeName5, dwCreateFlags | MPQ_CREATE_ARCHIVE_V3, 87541);
 -    if(dwErrCode != ERROR_SUCCESS)
 -        return dwErrCode;
 -
 -    dwErrCode = CreateNewArchiveU(&Logger, szUnicodeName5, dwCreateFlags | MPQ_CREATE_ARCHIVE_V2, 87541);
 -    if(dwErrCode != ERROR_SUCCESS)
 -        return dwErrCode;
 -
 -    return dwErrCode;
 -}
 -
  static DWORD TestCreateArchive_FileFlagTest(LPCTSTR szPlainName)
  {
      TLogHelper Logger("TestFileFlag", szPlainName);
 @@ -3889,6 +3818,10 @@ static LPCTSTR szDiabdatMPQ = _T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ");  static const TEST_EXTRA_ONEFILE  LfBliz = {ListFile, _T("ListFile_Blizzard.txt")};
  static const TEST_EXTRA_ONEFILE  LfWotI = {ListFile, _T("ListFile_WarOfTheImmortals.txt")};
 +static const BYTE szMpqFileNameUTF8[] = {0x4D, 0x50, 0x51, 0x5F, 0x32, 0x30, 0x32, 0x34, 0x5F, 0x76, 0x31, 0x5F, 0xE6, 0x9D, 0x82, 0xE9, 0xB1, 0xBC, 0xE5, 0x9C, 0xB0, 0xE7, 0x89, 0xA2, 0x5F, 0x30, 0x2E, 0x30, 0x38, 0x34, 0x62, 0x65, 0x74, 0x61, 0x34, 0x36, 0x2E, 0x77, 0x33, 0x78, 0x00};
 +static const BYTE szLstFileNameUTF8[] = {0x4C, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6C, 0x65, 0x5F, 0xE6, 0x9D, 0x82, 0xE9, 0xB1, 0xBC, 0xE5, 0x9C, 0xB0, 0xE7, 0x89, 0xA2, 0x5F, 0x30, 0x2E, 0x30, 0x38, 0x34, 0x62, 0x65, 0x74, 0x61, 0x34, 0x36, 0x2E, 0x74, 0x78, 0x74, 0x00};
 +static const TEST_EXTRA_UTF8     MpqUtf8 = {Utf8File, szMpqFileNameUTF8, szLstFileNameUTF8};
 +
  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"};
 @@ -4048,7 +3981,7 @@ static const TEST_EXTRA_PATCHES PatchH6898 =      10
  };
 -static const TEST_INFO TestList_StreamOps[] =
 +static const TEST_INFO1 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},
 @@ -4058,7 +3991,7 @@ static const TEST_INFO TestList_StreamOps[] =      {_T("mpqe-file://MPQ_2011_v2_EncryptedMpq.MPQE"),           NULL, NULL, STREAM_PROVIDER_MPQE}
  };
 -static const TEST_INFO TestList_MasterMirror[] =
 +static const TEST_INFO1 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},
 @@ -4071,7 +4004,7 @@ static const TEST_INFO TestList_MasterMirror[] =  //  {_T("MPQ_2013_v4_alternate-downloaded.MPQ"),                _T("http://www.zezula.net\\mpqs\\alternate.zip"), NULL, 0}
  };
 -static const TEST_INFO Test_OpenMpqs[] =
 +static const TEST_INFO1 Test_OpenMpqs[] =
  {
      // Correct or damaged archives
      {_T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"),                     NULL, "554b538541e42170ed41cb236483489e",  2910, &TwoFilesD1},  // Base MPQ from Diablo 1
 @@ -4100,6 +4033,7 @@ static const TEST_INFO Test_OpenMpqs[] =      {_T("MPQ_2023_v4_UTF8.s2ma"),                               NULL, "97b7a686650f3307d135e1d1b017a36a",    67},               // Map contaning files with Chinese names (UTF8-encoded)
      {_T("MPQ_2023_v1_GreenTD.w3x"),                             NULL, "477af4ddf11eead1412d7c87cb81b530",  2004},               // Corrupt sector checksum table in file #A0
      {_T("MPQ_2023_v4_1F644C5A.SC2Replay"),                      NULL, "b225828ffbf5037553e6a1290187caab",    17},               // Corrupt patch info of the "(attributes)" file
 +    {_T("<Chinese MPQ name>"),                                  NULL, "67faeffd0c0aece205ac8b7282d8ad8e",  4697, &MpqUtf8},     // Chinese name of the MPQ
      // Protected archives
      {_T("MPQ_2002_v1_ProtectedMap_InvalidUserData.w3x"),        NULL, "b900364cc134a51ddeca21a13697c3ca",    79},
 @@ -4181,7 +4115,7 @@ static const TEST_INFO Test_OpenMpqs[] =      {_T("MPQ_2013_v4_SC2_EmptyMap.SC2Map"),                 NULL,     "88e1b9a88d56688c9c24037782b7bb68",    33 | TFLG_GET_FILE_INFO},
  };
 -static const TEST_INFO Test_ReopenMpqs[] =
 +static const TEST_INFO1 Test_ReopenMpqs[] =
  {
      // 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},
 @@ -4196,28 +4130,61 @@ static const TEST_INFO Test_ReopenMpqs[] =      // 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,     "554b538541e42170ed41cb236483489e",  2910 | TFLG_MODIFY},
 -    {_T("MPQ_2013_v4_SC2_EmptyMap.SC2Map"),                 NULL,     "88e1b9a88d56688c9c24037782b7bb68",    33 | TFLG_MODIFY | TFLG_HAS_LISTFILE | TFLG_HAS_ATTRIBUTES}
 +    {_T("MPQ_2013_v4_SC2_EmptyMap.SC2Map"),                 NULL,     "88e1b9a88d56688c9c24037782b7bb68",    33 | TFLG_MODIFY | TFLG_HAS_LISTFILE | TFLG_HAS_ATTRIBUTES},
 +    // Adding a large file to archive version 1. This must fail
 +    {_T("MPQ_2002_v1_LargeMapFile.w3m"),                    NULL,     "2f302705280f0cf9da50ac10e3d71522",    22 | TFLG_MODIFY | TFLG_HAS_LISTFILE | TFLG_HAS_ATTRIBUTES | TFLG_BIGFILE | TFLG_WILL_FAIL}
  };
  // Tests for signature file
 -static const TEST_INFO Test_Signature[] =
 +static const TEST_INFO1 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},
  };
 -static const TEST_INFO Test_ReplaceFile[] =
 +static const TEST_INFO1 Test_ReplaceFile[] =
  {
      {_T("MPQ_2014_v4_Base.StormReplay"), _T("replay.message.events"), (LPCSTR)(MPQ_FILE_SINGLE_UNIT), MPQ_COMPRESSION_ZLIB},
      {_T("MPQ_2022_v1_v4.329.w3x"),       _T("war3map.j"),             (LPCSTR)(MPQ_FILE_SINGLE_UNIT), MPQ_COMPRESSION_ZLIB},
      {_T("MPQ_2023_v1_StarcraftMap.scm"), _T("staredit#scenario.chk"), NULL,                           MPQ_COMPRESSION_ZLIB | MPQ_COMPRESSION_HUFFMANN},
  };
 +static const TEST_INFO2 Test_CreateMpqs[] =
 +{
 +    // Create empty MPQs containing nothing
 +    {"EmptyMpq_v2.mpq",                 NULL, NULL, CFLG_V2 | CFLG_EMPTY},
 +    {"EmptyMpq_v4.mpq",                 NULL, NULL, CFLG_V4 | CFLG_EMPTY},
 +
 +    // Create empty MPQs with localized names
 +    {(LPCSTR)szPlainName_CZE,           NULL, NULL, CFLG_V2 | 15},       // (UTF-8) Czech
 +    {(LPCSTR)szPlainName_RUS,           NULL, NULL, CFLG_V2 | 58},       // (UTF-8) Russian
 +    {(LPCSTR)szPlainName_GRC,           NULL, NULL, CFLG_V2 | 15874},    // (UTF-8) Greek
 +    {(LPCSTR)szPlainName_CHN,           NULL, NULL, CFLG_V4 | 87541},    // (UTF-8) Chinese
 +    {(LPCSTR)szPlainName_JPN,           NULL, NULL, CFLG_V4 | 87541},    // (UTF-8) Japanese
 +    {(LPCSTR)szPlainName_SAU,           NULL, NULL, CFLG_V4 | 87541},    // (UTF-8) Arabic
 +
 +    // Create archive containing files with non-std names
 +    {"NonStdNames.mpq",                 NULL, NULL, CFLG_NONSTD_NAMES | 4000},
 +
 +    // Test creating of an archive the same way like MPQ Editor does
 +    {"MpqEditorTest.mpq",    "AddedFile.exe", NULL, CFLG_V2 | CFLG_MPQEDITOR | 4000},
 +};
 +
 +static const LPCSTR Test_CreateMpq_Localized[] =
 +{
 +    (LPCSTR)szPlainName_CZE,    // (UTF-8) Czech
 +    (LPCSTR)szPlainName_RUS,    // (UTF-8) Russian
 +    (LPCSTR)szPlainName_GRC,    // (UTF-8) Greek
 +    (LPCSTR)szPlainName_CHN,    // (UTF-8) Chinese
 +    (LPCSTR)szPlainName_JPN,    // (UTF-8) Japanese
 +    (LPCSTR)szPlainName_SAU     // (UTF-8) Arabic
 +};
 +
  //-----------------------------------------------------------------------------
  // Main
 -//#define TEST_COMMAND_LINE
 +#define TEST_COMMAND_LINE
  #define TEST_LOCAL_LISTFILE
  #define TEST_STREAM_OPERATIONS
  #define TEST_MASTER_MIRROR
 @@ -4225,6 +4192,8 @@ static const TEST_INFO Test_ReplaceFile[] =  #define TEST_REOPEN_MPQ
  #define TEST_VERIFY_SIGNATURE
  #define TEST_REPLACE_FILE
 +#define TEST_VERIFY_HASHES
 +#define TEST_CREATE_MPQS
  int _tmain(int argc, TCHAR * argv[])
  {
 @@ -4340,34 +4309,28 @@ int _tmain(int argc, TCHAR * argv[])      }
  #endif
 -    // Add files so the archive will go over 2GB
 -    if(dwErrCode == ERROR_SUCCESS)
 -        dwErrCode = TestOpenArchive_AddFiles2GB(_T("MPQ_2002_v1_LargeMapFile.w3m"), 0);
 -
 -    // Verify SHA256 of each MPQ that we have in the list
 +#ifdef TEST_VERIFY_HASHES
      if(dwErrCode == ERROR_SUCCESS)
          dwErrCode = VerifyFileHashes(szMpqSubDir);
 +#endif
 -    // Create an empty archive v2
 -    if(dwErrCode == ERROR_SUCCESS)
 -        dwErrCode = TestCreateArchive_EmptyMpq(_T("StormLibTest_EmptyMpq_v2.mpq"), MPQ_CREATE_ARCHIVE_V2 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES);
 -
 -    // Create an empty archive v4
 +#ifdef TEST_CREATE_MPQS
      if(dwErrCode == ERROR_SUCCESS)
 -        dwErrCode = TestCreateArchive_EmptyMpq(_T("StormLibTest_EmptyMpq_v4.mpq"), MPQ_CREATE_ARCHIVE_V4 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES);
 +    {
 +        for(size_t i = 0; i < _countof(Test_CreateMpqs); i++)
 +        {
 +            // Ignore the error code here; we want to see results of all opens
 +            dwErrCode = TestCreateArchive(Test_CreateMpqs[i]);
 +            if(dwErrCode != ERROR_SUCCESS)
 +                break;
 +        }
 +    }
 +#endif
      // Test creating of an archive the same way like MPQ Editor does
      if(dwErrCode == ERROR_SUCCESS)
          dwErrCode = TestCreateArchive_TestGaps(_T("StormLibTest_GapsTest.mpq"));
 -    // Test creating of an archive with non standard file names
 -    if(dwErrCode == ERROR_SUCCESS)
 -        dwErrCode = TestCreateArchive_NonStdNames(_T("StormLibTest_NonStdNames.mpq"));
 -
 -    // Test creating of an archive the same way like MPQ Editor does
 -    if(dwErrCode == ERROR_SUCCESS)
 -        dwErrCode = TestCreateArchive_MpqEditor(_T("StormLibTest_MpqEditorTest.mpq"), "AddedFile.exe");
 -
      // Create an archive and fill it with files up to the max file count
      if(dwErrCode == ERROR_SUCCESS)
          dwErrCode = TestCreateArchive_FillArchive(_T("StormLibTest_FileTableFull.mpq"), 0);
 @@ -4388,10 +4351,6 @@ int _tmain(int argc, TCHAR * argv[])      if(dwErrCode == ERROR_SUCCESS)
          dwErrCode = TestCreateArchive_IncMaxFileCount(_T("StormLibTest_IncMaxFileCount.mpq"));
 -    // Create a MPQ archive with UNICODE names
 -    if(dwErrCode == ERROR_SUCCESS)
 -        dwErrCode = TestCreateArchive_UnicodeNames();
 -
      // Create a MPQ file, add files with various flags
      if(dwErrCode == ERROR_SUCCESS)
          dwErrCode = TestCreateArchive_FileFlagTest(_T("StormLibTest_FileFlagTest.mpq"));
 diff --git a/test/TLogHelper.cpp b/test/TLogHelper.cpp index d96108f..c0e5e14 100644 --- a/test/TLogHelper.cpp +++ b/test/TLogHelper.cpp @@ -61,55 +61,6 @@ inline DWORD Test_GetLastError()  #endif
  }
 -void TestStrCopy(char * szTarget, size_t cchTarget, const char * szSource, size_t cchSource = -1)
 -{
 -    size_t cchToCopy;
 -
 -    if(cchTarget > 0)
 -    {
 -        // Make sure we know the length
 -        if(cchSource == -1)
 -            cchSource = strlen(szSource);
 -        cchToCopy = TEST_MIN((cchTarget - 1), cchSource);
 -
 -        // Copy the string
 -        memcpy(szTarget, szSource, cchToCopy);
 -        szTarget[cchToCopy] = 0;
 -    }
 -}
 -
 -void TestStrCopy(char * szTarget, size_t cchTarget, const wchar_t * szSource, size_t cchSource = -1)
 -{
 -    size_t cchToCopy;
 -
 -    if(cchTarget > 0)
 -    {
 -        // Make sure we know the length
 -        if(cchSource == -1)
 -            cchSource = wcslen(szSource);
 -        cchToCopy = TEST_MIN((cchTarget - 1), cchSource);
 -
 -        wcstombs(szTarget, szSource, cchToCopy);
 -        szTarget[cchToCopy] = 0;
 -    }
 -}
 -
 -void TestStrCopy(wchar_t * szTarget, size_t cchTarget, const char * szSource, size_t cchSource = -1)
 -{
 -    size_t cchToCopy;
 -
 -    if(cchTarget > 0)
 -    {
 -        // Make sure we know the length
 -        if(cchSource == -1)
 -            cchSource = strlen(szSource);
 -        cchToCopy = TEST_MIN((cchTarget - 1), cchSource);
 -
 -        mbstowcs(szTarget, szSource, cchToCopy);
 -        szTarget[cchToCopy] = 0;
 -    }
 -}
 -
  #ifdef STORMLIB_WINDOWS
  wchar_t * CopyFormatCharacter(wchar_t * szBuffer, const wchar_t *& szFormat)
  {
 @@ -151,7 +102,7 @@ char * CopyFormatCharacter(char * szBuffer, const char *& szFormat)      {
          if(szFormat[1] == 's')
          {
 -            TestStrCopy(szBuffer, 32, szStringFormat);
 +            StringCopy(szBuffer, 32, szStringFormat);
              szFormat += 2;
              return szBuffer + strlen(szStringFormat);
          }
 @@ -159,7 +110,7 @@ char * CopyFormatCharacter(char * szBuffer, const char *& szFormat)          // Replace %I64u with the proper platform-dependent suffix
          if(szFormat[1] == 'I' && szFormat[2] == '6' && szFormat[3] == '4' && szFormat[4] == 'u')
          {
 -            TestStrCopy(szBuffer, 32, szUint64Format);
 +            StringCopy(szBuffer, 32, szUint64Format);
              szFormat += 5;
              return szBuffer + strlen(szUint64Format);
          }
 @@ -263,7 +214,7 @@ class TLogHelper              TCHAR szMainTitleT[0x100] = {0};
              // Copy the UNICODE main title
 -            TestStrCopy(szMainTitleT, _countof(szMainTitleT), szMainTitle);
 +            StringCopy(szMainTitleT, _countof(szMainTitleT), szMainTitle);
              if(szSubTitle1 != NULL && szSubTitle2 != NULL)
                  nPrevPrinted = _tprintf(_T("\rRunning %s (%s+%s) ..."), szMainTitleT, szSubTitle1, szSubTitle2);
 @@ -346,9 +297,9 @@ class TLogHelper          }
          // Construct the message
 -        nLength = TestStrPrintfV(szMessage, _countof(szMessage), szFormat, argList);
 -        TestStrCopy(szBufferPtr, (szBufferEnd - szBufferPtr), szMessage);
 -        szBufferPtr += nLength;
 +        TestStrPrintfV(szMessage, _countof(szMessage), szFormat, argList);
 +        StringCopy(szBufferPtr, (szBufferEnd - szBufferPtr), szMessage);
 +        szBufferPtr = szBufferPtr + strlen(szBufferPtr);
          // Append the last error
          if(bPrintLastError)
 @@ -457,7 +408,7 @@ class TLogHelper          TCHAR szSaveMainTitle[0x80];
          // Set both to NULL so they won't be printed
 -        TestStrCopy(szSaveMainTitle, _countof(szSaveMainTitle), szMainTitle);
 +        StringCopy(szSaveMainTitle, _countof(szSaveMainTitle), szMainTitle);
          szMainTitle = NULL;
          szSubTitle1 = NULL;
          szSubTitle2 = NULL;
 diff --git a/test/stormlib-test-001.txt b/test/stormlib-test-001.txt index 7b780cf..2979f56 100644 --- a/test/stormlib-test-001.txt +++ b/test/stormlib-test-001.txt @@ -1,143 +1,149 @@  ==== Test Suite for StormLib version 9.25 ==== -InitWorkFolder: Work directory \Multimedia\MPQs (default) -TestLiFiSearch (FLAT-MAP:listfile-test.txt) succeeded. -TestLiFiSearch (listfile-test.txt) succeeded. -TestFileStream (MPQ_2013_v4_alternate-original.MPQ) succeeded. -TestFileStream (MPQ_2013_v4_alternate-original.MPQ) succeeded. -TestFileStream (MPQ_2013_v4_alternate-complete.MPQ) succeeded. -TestFileStream (part-file://MPQ_2009_v2_WoW_patch.MPQ.part) succeeded. -TestFileStream (blk4-file://streaming/model.MPQ.0) succeeded. -TestFileStream (mpqe-file://MPQ_2011_v2_EncryptedMpq.MPQE) succeeded. -TestFileMirror (part-file://MPQ_2009_v1_patch-created.MPQ.part) succeeded. -TestFileMirror (part-file://MPQ_2009_v1_patch-partial.MPQ.part) succeeded. -TestFileMirror (part-file://MPQ_2009_v1_patch-complete.MPQ.part) succeeded. -TestFileMirror (MPQ_2013_v4_alternate-created.MPQ) succeeded. -TestFileMirror (MPQ_2013_v4_alternate-incomplete.MPQ) succeeded. -TestFileMirror (MPQ_2013_v4_alternate-complete.MPQ) succeeded. -TestReadingMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. -TestReadingMpq (MPQ_1997_v1_patch_rt_SC1B.mpq) succeeded. -TestReadingMpq (MPQ_1997_v1_StarDat_SC1B.mpq) succeeded. -TestReadingMpq (MPQ_1997_v1_INSTALL_SC1B.EXE_) succeeded. -TestReadingMpq: Warning: CRC32 error on (signature) +InitWorkFolder: Work directory /media/ladik/MPQs (default) +TestLiFiSearch (FLAT-MAP:listfile-test.txt) succeeded.   +TestLiFiSearch (listfile-test.txt) succeeded.          +TestFileStream (MPQ_2013_v4_alternate-original.MPQ) succeeded.           +TestFileStream (MPQ_2013_v4_alternate-original.MPQ) succeeded.           +TestFileStream (MPQ_2013_v4_alternate-complete.MPQ) succeeded.           +TestFileStream (part-file://MPQ_2009_v2_WoW_patch.MPQ.part) succeeded.   +TestFileStream (blk4-file://streaming/model.MPQ.0) succeeded.   +TestFileStream (mpqe-file://MPQ_2011_v2_EncryptedMpq.MPQE) succeeded.   +TestFileMirror (part-file://MPQ_2009_v1_patch-created.MPQ.part) succeeded.   +TestFileMirror (part-file://MPQ_2009_v1_patch-partial.MPQ.part) succeeded.   +TestFileMirror (part-file://MPQ_2009_v1_patch-complete.MPQ.part) succeeded.   +TestFileMirror (MPQ_2013_v4_alternate-created.MPQ) succeeded.      +TestFileMirror (MPQ_2013_v4_alternate-incomplete.MPQ) succeeded.           +TestFileMirror (MPQ_2013_v4_alternate-complete.MPQ) succeeded.           +TestReadingMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded.          +TestReadingMpq (MPQ_1997_v1_patch_rt_SC1B.mpq) succeeded.          +TestReadingMpq (MPQ_1997_v1_StarDat_SC1B.mpq) succeeded.          +TestReadingMpq (MPQ_1997_v1_INSTALL_SC1B.EXE_) succeeded.          +TestReadingMpq: Warning: CRC32 error on (signature)                      TestReadingMpq: Warning: CRC32 error on (signature)  TestReadingMpq (MPQ_2016_v1_D2XP_IX86_1xx_114a.mpq) succeeded. -TestReadingMpq (MPQ_2018_v1_icon_error.w3m) succeeded. -TestReadingMpq (MPQ_1997_v1_Diablo1_STANDARD.SNP) succeeded. -TestReadingMpq (MPQ_2012_v2_EmptyMpq.MPQ) succeeded. -TestReadingMpq (MPQ_2013_v4_EmptyMpq.MPQ) succeeded. -TestReadingMpq (MPQ_2013_v4_patch-base-16357.MPQ) succeeded. -TestReadingMpq (MPQ_2011_v4_InvalidHetEntryCount.MPQ) succeeded. -TestReadingMpq (MPQ_2002_v1_BlockTableCut.MPQ) succeeded. -TestReadingMpq (MPQ_2010_v2_HasUserData.s2ma) succeeded. -TestReadingMpq: Warning: CRC32 error on (listfile) +TestReadingMpq (MPQ_2018_v1_icon_error.w3m) succeeded.          +TestReadingMpq (MPQ_1997_v1_Diablo1_STANDARD.SNP) succeeded.          +TestReadingMpq (MPQ_2012_v2_EmptyMpq.MPQ) succeeded.          +TestReadingMpq (MPQ_2013_v4_EmptyMpq.MPQ) succeeded.          +TestReadingMpq (MPQ_2013_v4_patch-base-16357.MPQ) succeeded.          +TestReadingMpq (MPQ_2011_v4_InvalidHetEntryCount.MPQ) succeeded.          +TestReadingMpq (MPQ_2002_v1_BlockTableCut.MPQ) succeeded.          +TestReadingMpq (MPQ_2010_v2_HasUserData.s2ma) succeeded.          +TestReadingMpq: Warning: CRC32 error on (listfile)                           TestReadingMpq: Warning: CRC32 error on (listfile)  TestReadingMpq: Warning: CRC32 error on File00000003.xxx  TestReadingMpq (MPQ_2014_v1_AttributesOneEntryLess.w3x) succeeded. -TestReadingMpq (MPQ_2020_v1_AHF04patch.mix) succeeded. -TestReadingMpq (MPQ_2010_v3_expansion-locale-frFR.MPQ) succeeded. -TestReadingMpq (mpqe-file://MPQ_2011_v2_EncryptedMpq.MPQE) succeeded. -TestReadingMpq (part-file://MPQ_2010_v2_HashTableCompressed.MPQ.part) succeeded. -TestReadingMpq (blk4-file://streaming/model.MPQ.0) succeeded. -TestReadingMpq: Warning: CRC32 error on replay.message.events +TestReadingMpq (MPQ_2020_v1_AHF04patch.mix) succeeded.          +TestReadingMpq (MPQ_2010_v3_expansion-locale-frFR.MPQ) succeeded.          +TestReadingMpq (mpqe-file://MPQ_2011_v2_EncryptedMpq.MPQE) succeeded.   +TestReadingMpq (part-file://MPQ_2010_v2_HashTableCompressed.MPQ.part) succeeded.   +TestReadingMpq (blk4-file://streaming/model.MPQ.0) succeeded.   +TestReadingMpq: Warning: CRC32 error on replay.message.events                TestReadingMpq (MPQ_2023_v2_MemoryCorruption.SC2Replay) succeeded. -TestReadingMpq (MPQ_2023_v1_StarcraftMap.scm) succeeded. -TestReadingMpq (MPQ_2023_v1_BroodWarMap.scx) succeeded. -TestReadingMpq (MPQ_2023_v1_Volcanis.scm) succeeded. -TestReadingMpq (MPQ_2023_v4_UTF8.s2ma) succeeded. -TestReadingMpq (MPQ_2023_v1_GreenTD.w3x) succeeded. -TestReadingMpq (MPQ_2023_v4_1F644C5A.SC2Replay) succeeded. -TestReadingMpq (MPQ_2002_v1_ProtectedMap_InvalidUserData.w3x) succeeded. -TestReadingMpq (MPQ_2002_v1_ProtectedMap_InvalidMpqFormat.w3x) succeeded. -TestReadingMpq (MPQ_2002_v1_ProtectedMap_Spazzler.w3x) succeeded. -TestReadingMpq (MPQ_2014_v1_ProtectedMap_Spazzler2.w3x) succeeded. -TestReadingMpq (MPQ_2014_v1_ProtectedMap_Spazzler3.w3x) succeeded. -TestReadingMpq (MPQ_2002_v1_ProtectedMap_BOBA.w3m) succeeded. -TestReadingMpq (MPQ_2015_v1_ProtectedMap_KangTooJee.w3x) succeeded. -TestReadingMpq (MPQ_2015_v1_ProtectedMap_Somj2hM16.w3x) succeeded. -TestReadingMpq (MPQ_2015_v1_ProtectedMap_Spazy.w3x) succeeded. -TestReadingMpq (MPQ_2015_v1_MessListFile.mpq) succeeded. -TestReadingMpq (MPQ_2016_v1_ProtectedMap_TableSizeOverflow.w3x) succeeded. -TestReadingMpq (MPQ_2016_v1_ProtectedMap_HashOffsIsZero.w3x) succeeded. -TestReadingMpq (MPQ_2016_v1_ProtectedMap_Somj2.w3x) succeeded. -TestReadingMpq (MPQ_2016_v1_WME4_4.w3x) succeeded. -TestReadingMpq (MPQ_2016_v1_SP_(4)Adrenaline.w3x) succeeded. -TestReadingMpq (MPQ_2016_v1_ProtectedMap_1.4.w3x) succeeded. -TestReadingMpq (MPQ_2016_v1_KoreanFile.w3m) succeeded. -TestReadingMpq (MPQ_2017_v1_Eden_RPG_S2_2.5J.w3x) succeeded. -TestReadingMpq (MPQ_2017_v1_BigDummyFiles.w3x) succeeded. -TestReadingMpq (MPQ_2017_v1_TildeInFileName.mpq) succeeded. -TestReadingMpq (MPQ_2018_v1_EWIX_v8_7.w3x) succeeded. -TestReadingMpq (MPQ_2020_v4_FakeMpqHeaders.SC2Mod) succeeded. -TestReadingMpq (MPQ_2020_v4_NP_Protect_1.s2ma) succeeded. -TestReadingMpq (MPQ_2020_v4_NP_Protect_2.s2ma) succeeded. -TestReadingMpq (MPQ_2015_v1_flem1.w3x) succeeded. -TestReadingMpq (MPQ_2002_v1_ProtectedMap_HashTable_FakeValid.w3x) succeeded. -TestReadingMpq (MPQ_2021_v1_CantExtractCHK.scx) succeeded. -TestReadingMpq (MPQ_2022_v1_Sniper.scx) succeeded. -TestReadingMpq (MPQ_2022_v1_OcOc_Bound_2.scx) succeeded. -TestReadingMpq (MPQ_2023_v1_Lusin2Rpg1.28.w3x) succeeded. -TestReadingMpq (MPQ_2020_v1_HS0.1.asi) succeeded. -TestReadingMpq (MPQ_2022_v1_hs0.8.asi) succeeded. -TestReadingMpq (MPQ_2022_v1_MoeMoeMod.asi) succeeded. -TestReadingMpq (MPx_2013_v1_LongwuOnline.mpk) succeeded. -TestReadingMpq (MPx_2013_v1_WarOfTheImmortals.sqp) succeeded. -TestReadingMpq (MPx_2022_v1_Music.mpk) succeeded. -TestReadingMpq (MPx_2022_v1_Scp.mpk) succeeded. -TestReadingMpq (MPx_2022_v1_UI.mpk) succeeded. -TestReadingMpq (MPQ_1998_v1_StarCraft.mpq) succeeded. -TestReadingMpq (MPQ_2012_v4_OldWorld.MPQ) succeeded. -TestReadingMpq (MPQ_2013_v4_world.MPQ) succeeded. -TestReadingMpq (MPQ_2013_v4_locale-enGB.MPQ) succeeded. -TestReadingMpq (MPQ_2013_v4_Base1.SC2Data) succeeded. -TestReadingMpq (MPQ_2013_v4_Mods#Core.SC2Mod#enGB.SC2Assets) succeeded. -TestReadingMpq (MPQ_2013_v4_Base1.SC2Data) succeeded. -TestReadingMpq (MPQ_2013_v4_Base3.SC2Maps) succeeded. -TestReadingMpq (MPQ_2013_v4_Mods#Liberty.SC2Mod#enGB.SC2Data) succeeded. -TestReadingMpq (MPQ_2014_v4_base-Win.MPQ) succeeded. -TestReadingMpq (MPQ_2014_v4_base-Win.MPQ) succeeded. -TestReadingMpq (MPQ_1997_v1_Diablo1_STANDARD.SNP) succeeded. -TestReadingMpq (MPQ_1999_v1_WeakSignature.exe) succeeded. -TestReadingMpq (MPQ_2003_v1_WeakSignatureEmpty.exe) succeeded. -TestReadingMpq (MPQ_2002_v1_StrongSignature.w3m) succeeded. -TestReadingMpq (MPQ_1998_v1_StarDat.mpq) succeeded. -TestReadingMpq (MPQ_1999_v1_WeakSignature.exe) succeeded. -TestReadingMpq (flat-file://streaming/model.MPQ.0) succeeded. -TestReadingMpq (MPQ_2013_vX_Battle.net.MPQ) succeeded. -TestReadingMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. -TestReadingMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. -TestReadingMpq (MPQ_2002_v1_StrongSignature.w3m) succeeded. -TestReadingMpq (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded. -Test_ReopenMpq (MPQ_2010_v3_expansion-locale-frFR.MPQ) succeeded. -Test_ReopenMpq (MPQ_2016_v1_00000.pak) succeeded. -Test_ReopenMpq (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded. -Test_ReopenMpq (MPQ_2013_v4_expansion1.MPQ) succeeded. -Test_ReopenMpq (MPQ_2014_v1_out1.w3x) succeeded. -Test_ReopenMpq (MPQ_2014_v1_out2.w3x) succeeded. -Test_ReopenMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. -Test_ReopenMpq (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded. -Test_Signature (MPQ_1999_v1_WeakSigned1.mpq) succeeded. -Test_Signature (MPQ_1999_v1_WeakSigned1.mpq) succeeded. -TestModifyMpq (MPQ_2014_v4_Base.StormReplay) succeeded. -TestModifyMpq (MPQ_2022_v1_v4.329.w3x) succeeded. -TestModifyMpq (MPQ_2023_v1_StarcraftMap.scm) succeeded. -TestAddFileOver2GB (MPQ_2002_v1_LargeMapFile.w3m) succeeded. -TestVerifyHash succeeded. -CreateEmptyMpq (StormLibTest_EmptyMpq_v2.mpq) succeeded. -CreateEmptyMpq (StormLibTest_EmptyMpq_v4.mpq) succeeded. -TestCreateGaps (StormLibTest_GapsTest.mpq) succeeded. -TestNonStdNames (StormLibTest_NonStdNames.mpq) succeeded. -CreateMpqEditor (StormLibTest_MpqEditorTest.mpq) succeeded. -TestCreateFull (StormLibTest_FileTableFull.mpq) succeeded. -TestCreateFull (StormLibTest_FileTableFull.mpq) succeeded. -TestCreateFull (StormLibTest_FileTableFull.mpq) succeeded. -TestCreateFull (StormLibTest_FileTableFull.mpq) succeeded. -IncMaxFileCount (StormLibTest_IncMaxFileCount.mpq) succeeded. -MpqUnicodeName succeeded. -TestFileFlag (StormLibTest_FileFlagTest.mpq) succeeded. -TestCompressions: Warning: CRC32 error on WaveFile_01.wav +TestReadingMpq (MPQ_2023_v1_StarcraftMap.scm) succeeded.          +TestReadingMpq (MPQ_2023_v1_BroodWarMap.scx) succeeded.          +TestReadingMpq (MPQ_2023_v1_Volcanis.scm) succeeded.          +TestReadingMpq (MPQ_2023_v4_UTF8.s2ma) succeeded.          +TestReadingMpq (MPQ_2023_v1_GreenTD.w3x) succeeded.          +TestReadingMpq (MPQ_2023_v4_1F644C5A.SC2Replay) succeeded.          +TestReadingMpq (<Chinese MPQ name>) succeeded.                                +TestReadingMpq (MPQ_2002_v1_ProtectedMap_InvalidUserData.w3x) succeeded.          +TestReadingMpq (MPQ_2002_v1_ProtectedMap_InvalidMpqFormat.w3x) succeeded.          +TestReadingMpq (MPQ_2002_v1_ProtectedMap_Spazzler.w3x) succeeded.          +TestReadingMpq (MPQ_2014_v1_ProtectedMap_Spazzler2.w3x) succeeded.          +TestReadingMpq (MPQ_2014_v1_ProtectedMap_Spazzler3.w3x) succeeded.          +TestReadingMpq (MPQ_2002_v1_ProtectedMap_BOBA.w3m) succeeded.          +TestReadingMpq (MPQ_2015_v1_ProtectedMap_KangTooJee.w3x) succeeded.          +TestReadingMpq (MPQ_2015_v1_ProtectedMap_Somj2hM16.w3x) succeeded.          +TestReadingMpq (MPQ_2015_v1_ProtectedMap_Spazy.w3x) succeeded.          +TestReadingMpq (MPQ_2015_v1_MessListFile.mpq) succeeded.          +TestReadingMpq (MPQ_2016_v1_ProtectedMap_TableSizeOverflow.w3x) succeeded.          +TestReadingMpq (MPQ_2016_v1_ProtectedMap_HashOffsIsZero.w3x) succeeded.          +TestReadingMpq (MPQ_2016_v1_ProtectedMap_Somj2.w3x) succeeded.          +TestReadingMpq (MPQ_2016_v1_WME4_4.w3x) succeeded.          +TestReadingMpq (MPQ_2016_v1_SP_(4)Adrenaline.w3x) succeeded.          +TestReadingMpq (MPQ_2016_v1_ProtectedMap_1.4.w3x) succeeded.          +TestReadingMpq (MPQ_2016_v1_KoreanFile.w3m) succeeded.          +TestReadingMpq (MPQ_2017_v1_Eden_RPG_S2_2.5J.w3x) succeeded.          +TestReadingMpq (MPQ_2017_v1_BigDummyFiles.w3x) succeeded.          +TestReadingMpq (MPQ_2017_v1_TildeInFileName.mpq) succeeded.          +TestReadingMpq (MPQ_2018_v1_EWIX_v8_7.w3x) succeeded.          +TestReadingMpq (MPQ_2020_v4_FakeMpqHeaders.SC2Mod) succeeded.          +TestReadingMpq (MPQ_2020_v4_NP_Protect_1.s2ma) succeeded.          +TestReadingMpq (MPQ_2020_v4_NP_Protect_2.s2ma) succeeded.          +TestReadingMpq (MPQ_2015_v1_flem1.w3x) succeeded.          +TestReadingMpq (MPQ_2002_v1_ProtectedMap_HashTable_FakeValid.w3x) succeeded.          +TestReadingMpq (MPQ_2021_v1_CantExtractCHK.scx) succeeded.          +TestReadingMpq (MPQ_2022_v1_Sniper.scx) succeeded.          +TestReadingMpq (MPQ_2022_v1_OcOc_Bound_2.scx) succeeded.          +TestReadingMpq (MPQ_2023_v1_Lusin2Rpg1.28.w3x) succeeded.          +TestReadingMpq (MPQ_2020_v1_HS0.1.asi) succeeded.          +TestReadingMpq (MPQ_2022_v1_hs0.8.asi) succeeded.          +TestReadingMpq (MPQ_2022_v1_MoeMoeMod.asi) succeeded.          +TestReadingMpq (MPx_2013_v1_LongwuOnline.mpk) succeeded.          +TestReadingMpq (MPx_2013_v1_WarOfTheImmortals.sqp) succeeded.          +TestReadingMpq (MPx_2022_v1_Music.mpk) succeeded.          +TestReadingMpq (MPx_2022_v1_Scp.mpk) succeeded.          +TestReadingMpq (MPx_2022_v1_UI.mpk) succeeded.                    +TestReadingMpq (MPQ_1998_v1_StarCraft.mpq) succeeded.          +TestReadingMpq (MPQ_2012_v4_OldWorld.MPQ) succeeded.                        +TestReadingMpq (MPQ_2013_v4_world.MPQ) succeeded.                              +TestReadingMpq (MPQ_2013_v4_locale-enGB.MPQ) succeeded.          +TestReadingMpq (MPQ_2013_v4_Base1.SC2Data) succeeded.          +TestReadingMpq (MPQ_2013_v4_Mods#Core.SC2Mod#enGB.SC2Assets) succeeded.          +TestReadingMpq (MPQ_2013_v4_Base1.SC2Data) succeeded.          +TestReadingMpq (MPQ_2013_v4_Base3.SC2Maps) succeeded.          +TestReadingMpq (MPQ_2013_v4_Mods#Liberty.SC2Mod#enGB.SC2Data) succeeded.          +TestReadingMpq (MPQ_2014_v4_base-Win.MPQ) succeeded.          +TestReadingMpq (MPQ_2014_v4_base-Win.MPQ) succeeded.          +TestReadingMpq (MPQ_1997_v1_Diablo1_STANDARD.SNP) succeeded.           +TestReadingMpq (MPQ_1999_v1_WeakSignature.exe) succeeded.           +TestReadingMpq (MPQ_2003_v1_WeakSignatureEmpty.exe) succeeded.           +TestReadingMpq (MPQ_2002_v1_StrongSignature.w3m) succeeded.           +TestReadingMpq (MPQ_1998_v1_StarDat.mpq) succeeded.           +TestReadingMpq (MPQ_1999_v1_WeakSignature.exe) succeeded.           +TestReadingMpq (flat-file://streaming/model.MPQ.0) succeeded.   +TestReadingMpq (MPQ_2013_vX_Battle.net.MPQ) succeeded.          +TestReadingMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded.          +TestReadingMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded.           +TestReadingMpq (MPQ_2002_v1_StrongSignature.w3m) succeeded.          +TestReadingMpq (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded.          +Test_ReopenMpq (MPQ_2010_v3_expansion-locale-frFR.MPQ) succeeded.           +Test_ReopenMpq (MPQ_2016_v1_00000.pak) succeeded.                +Test_ReopenMpq (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded.           +Test_ReopenMpq (MPQ_2013_v4_expansion1.MPQ) succeeded.                     +Test_ReopenMpq (MPQ_2014_v1_out1.w3x) succeeded.           +Test_ReopenMpq (MPQ_2014_v1_out2.w3x) succeeded.           +Test_ReopenMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded.           +Test_ReopenMpq (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded.           +Test_ReopenMpq (MPQ_2002_v1_LargeMapFile.w3m) succeeded.                                 +Test_Signature (MPQ_1999_v1_WeakSigned1.mpq) succeeded.   +Test_Signature (MPQ_1999_v1_WeakSigned1.mpq) succeeded.   +TestModifyMpq (MPQ_2014_v4_Base.StormReplay) succeeded.           +TestModifyMpq (MPQ_2022_v1_v4.329.w3x) succeeded.           +TestModifyMpq (MPQ_2023_v1_StarcraftMap.scm) succeeded.           +TestVerifyHash succeeded.                                                                  +CreateNewMpq (StormLibTest_EmptyMpq_v2.mpq) succeeded.   +CreateNewMpq (StormLibTest_EmptyMpq_v4.mpq) succeeded.   +CreateNewMpq (StormLibTest_Český.mpq) succeeded.     . +CreateNewMpq (StormLibTest_Русский.mpq) succeeded. ...      +CreateNewMpq (StormLibTest_ελληνικά.mpq) succeeded.           +CreateNewMpq (StormLibTest_日本語.mpq) succeeded.   .. +CreateNewMpq (StormLibTest_简体中文.mpq) succeeded....    +CreateNewMpq (StormLibTest_الععربية.mpq) succeeded....        +CreateNewMpq (StormLibTest_NonStdNames.mpq) succeeded.   +CreateNewMpq (StormLibTest_MpqEditorTest.mpq) succeeded.   +TestCreateGaps (StormLibTest_GapsTest.mpq) succeeded.   +TestCreateFull (StormLibTest_FileTableFull.mpq) succeeded.   +TestCreateFull (StormLibTest_FileTableFull.mpq) succeeded.   +TestCreateFull (StormLibTest_FileTableFull.mpq) succeeded.   +TestCreateFull (StormLibTest_FileTableFull.mpq) succeeded.   +IncMaxFileCount (StormLibTest_IncMaxFileCount.mpq) succeeded.   +TestFileFlag (StormLibTest_FileFlagTest.mpq) succeeded.                      +TestCompressions: Warning: CRC32 error on WaveFile_01.wav         TestCompressions: Warning: CRC32 error on WaveFile_02.wav -TestCompressions (StormLibTest_AddWaveMonoBadTest.mpq) succeeded. -TestCompressions: Warning: CRC32 error on WaveFile_01.wav +TestCompressions (StormLibTest_AddWaveMonoBadTest.mpq) succeeded.          +TestCompressions: Warning: CRC32 error on WaveFile_01.wav           TestCompressions: Warning: CRC32 error on WaveFile_02.wav -ListFilePos (StormLibTest_ListFilePos.mpq) succeeded. -TestBigArchive (StormLibTest_BigArchive_v4.mpq) succeeded. +ListFilePos (StormLibTest_ListFilePos.mpq) succeeded.   +TestBigArchive (StormLibTest_BigArchive_v4.mpq) succeeded.                             
\ No newline at end of file | 
