diff options
| -rw-r--r-- | src/SBaseFileTable.cpp | 66 | ||||
| -rw-r--r-- | src/SFileAddFile.cpp | 5 | ||||
| -rw-r--r-- | src/SFileAttributes.cpp | 93 | ||||
| -rw-r--r-- | src/SFileCreateArchive.cpp | 16 | ||||
| -rw-r--r-- | src/SFileListFile.cpp | 83 | ||||
| -rw-r--r-- | test/Test.cpp | 166 | 
6 files changed, 271 insertions, 158 deletions
| diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index 9e95d3e..a2eb5a8 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -1847,8 +1847,8 @@ TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID l              if((ha->dwFileTableSize + ha->dwReservedFiles) >= ha->dwMaxFileCount)                  return NULL; -            // Invalidate the internal files. Then we need to search for a deleted entry again, -            // because the previous call to InvalidateInternalFiles might have created some +            // Invalidate the internal files so we free +            // their file entries.              InvalidateInternalFiles(ha);              // Re-check for deleted entries @@ -1856,7 +1856,7 @@ TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID l              if(pFileEntry == NULL)              {                  // If there is still no deleted entry, we allocate an entry at the end of the file table -                assert((ha->dwFileTableSize + ha->dwReservedFiles) < ha->dwMaxFileCount); +                assert((ha->dwFileTableSize + ha->dwReservedFiles) <= ha->dwMaxFileCount);                  pFileEntry = ha->pFileTable + ha->dwFileTableSize++;              }          } @@ -1869,7 +1869,7 @@ TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID l      else      {          // There should be at least one entry for that internal file -        assert((ha->dwFileTableSize + ha->dwReservedFiles) < ha->dwMaxFileCount); +        assert((ha->dwFileTableSize + ha->dwReservedFiles) <= ha->dwMaxFileCount);          pFileEntry = ha->pFileTable + ha->dwFileTableSize++;      } @@ -1985,8 +1985,9 @@ void DeleteFileEntry(  void InvalidateInternalFiles(TMPQArchive * ha)  { -    TFileEntry * pFileEntry; -    DWORD dwReservedFiles = 0; +    TFileEntry * pFileTableEnd; +    TFileEntry * pFileEntry1 = NULL; +    TFileEntry * pFileEntry2 = NULL;      // Do nothing if we are in the middle of saving internal files      if(!(ha->dwFlags & MPQ_FLAG_SAVING_TABLES)) @@ -1998,54 +1999,49 @@ void InvalidateInternalFiles(TMPQArchive * ha)          //          // Invalidate the (listfile), if not done yet -        if(!(ha->dwFlags & MPQ_FLAG_LISTFILE_INVALID)) +        if(ha->dwFileFlags1 != 0 && (ha->dwFlags & MPQ_FLAG_LISTFILE_INVALID) == 0)          { -            pFileEntry = GetFileEntryExact(ha, LISTFILE_NAME, LANG_NEUTRAL); -            if(pFileEntry != NULL) -            { -                DeleteFileEntry(ha, pFileEntry); -                dwReservedFiles++; -            } +            // Delete the existing entry for (listfile) +            pFileEntry1 = GetFileEntryExact(ha, LISTFILE_NAME, LANG_NEUTRAL); +            if(pFileEntry1 != NULL) +                DeleteFileEntry(ha, pFileEntry1); +            // Reserve one entry for (listfile)              ha->dwFlags |= MPQ_FLAG_LISTFILE_INVALID; +            ha->dwReservedFiles++;          }          // Invalidate the (attributes), if not done yet -        if(!(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_INVALID)) +        if(ha->dwFileFlags2 != 0 && (ha->dwFlags & MPQ_FLAG_ATTRIBUTES_INVALID) == 0)          { -            pFileEntry = GetFileEntryExact(ha, ATTRIBUTES_NAME, LANG_NEUTRAL); -            if(pFileEntry != NULL) -            { -                DeleteFileEntry(ha, pFileEntry); -                dwReservedFiles++; -            } +            // Delete the existing entry for (attributes) +            pFileEntry2 = GetFileEntryExact(ha, ATTRIBUTES_NAME, LANG_NEUTRAL); +            if(pFileEntry2 != NULL) +                DeleteFileEntry(ha, pFileEntry2); +            // Reserve one entry for (attributes)              ha->dwFlags |= MPQ_FLAG_ATTRIBUTES_INVALID; +            ha->dwReservedFiles++;          }          // If the internal files are at the end of the file table (they usually are),          // we want to free these 2 entries, so when new files are added, they get          // added to the freed entries and the internal files get added after that -        if(ha->dwFileTableSize > 0 && dwReservedFiles != 0) +        if(ha->dwFileTableSize > 0)          { -            // Go backwards while there are free entries -            for(pFileEntry = ha->pFileTable + ha->dwFileTableSize - 1; pFileEntry >= ha->pFileTable; pFileEntry--) -            { -                // Stop searching if a file is present -                if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) -                { -                    pFileEntry++; -                    break; -                } -            } +            pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; + +            // Is one of the entries the last one? +            if(pFileEntry1 == pFileTableEnd - 1 || pFileEntry2 == pFileTableEnd - 1) +                pFileTableEnd--; +            if(pFileEntry1 == pFileTableEnd - 1 || pFileEntry2 == pFileTableEnd - 1) +                pFileTableEnd--;              // Calculate the new file table size -            ha->dwFileTableSize = (DWORD)(pFileEntry - ha->pFileTable); -            ha->dwReservedFiles = dwReservedFiles; +            ha->dwFileTableSize = (DWORD)(pFileTableEnd - ha->pFileTable);          } -        // Remember that the MPQ has been changed and it will be necessary -        // to update the tables +        // Remember that the MPQ has been changed          ha->dwFlags |= MPQ_FLAG_CHANGED;      }  } diff --git a/src/SFileAddFile.cpp b/src/SFileAddFile.cpp index 040ed57..f941141 100644 --- a/src/SFileAddFile.cpp +++ b/src/SFileAddFile.cpp @@ -434,6 +434,11 @@ int SFileAddFile_Init(              // If the caller didn't set MPQ_FILE_REPLACEEXISTING, fail it              if((dwFlags & MPQ_FILE_REPLACEEXISTING) == 0)                  nError = ERROR_ALREADY_EXISTS; + +            // When replacing an existing file, +            // we still need to invalidate the (attributes) file +            if(nError == ERROR_SUCCESS) +                InvalidateInternalFiles(ha);          }      } diff --git a/src/SFileAttributes.cpp b/src/SFileAttributes.cpp index aafd4c3..ca865a0 100644 --- a/src/SFileAttributes.cpp +++ b/src/SFileAttributes.cpp @@ -246,7 +246,7 @@ static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile)      LPBYTE pbAttrFile;      LPBYTE pbAttrPtr;      size_t cbAttrFile; -    DWORD dwFinalEntries = ha->dwFileTableSize + ha->dwReservedFiles + 1; +    DWORD dwFinalEntries = ha->dwFileTableSize + ha->dwReservedFiles;      // Check if we need patch bits in the (attributes) file      for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) @@ -283,7 +283,7 @@ static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile)                  *pArrayCRC32++ = BSWAP_INT32_UNSIGNED(pFileEntry->dwCrc32);              // Skip the reserved entries -            pbAttrPtr = (LPBYTE)(pArrayCRC32 + ha->dwReservedFiles + 1); +            pbAttrPtr = (LPBYTE)(pArrayCRC32 + ha->dwReservedFiles);          }          // Write the array of file time @@ -296,7 +296,7 @@ static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile)                  *pArrayFileTime++ = BSWAP_INT64_UNSIGNED(pFileEntry->FileTime);              // Skip the reserved entries -            pbAttrPtr = (LPBYTE)(pArrayFileTime + ha->dwReservedFiles + 1); +            pbAttrPtr = (LPBYTE)(pArrayFileTime + ha->dwReservedFiles);          }          // Write the array of MD5s @@ -312,7 +312,7 @@ static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile)              }              // Skip the reserved items -            pbAttrPtr = pbArrayMD5 + ((ha->dwReservedFiles + 1) * MD5_DIGEST_SIZE); +            pbAttrPtr = pbArrayMD5 + (ha->dwReservedFiles * MD5_DIGEST_SIZE);          }          // Write the array of patch bits @@ -334,8 +334,8 @@ static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile)                  dwBitMask = (dwBitMask << 0x07) | (dwBitMask >> 0x01);              } -            // Note: Do not increment the array by the last bit that belongs to (attributes). -            // This might create the array one byte less (if the number of files a multiplier of 8). +            // Having incremented the bit array just by the number of items in the file table +            // will create the bit array one byte less of the number of files is a multiplier of 8).              // Blizzard MPQs have the same feature.              // Move past the bit array @@ -410,48 +410,57 @@ int SAttrFileSaveToMpq(TMPQArchive * ha)      DWORD cbAttrFile = 0;      int nError = ERROR_SUCCESS; -    // If there are no file flags for (attributes) file, do nothing -    if(ha->dwFileFlags2 == 0 || ha->dwMaxFileCount == 0) -        return ERROR_SUCCESS; +    // Only save the attributes if we should do so +    if(ha->dwFileFlags2 != 0) +    { +        // At this point, we expect to have at least one reserved entry in the file table +        assert(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_INVALID); +        assert(ha->dwReservedFiles >= 1); + +        // Create the raw data that is to be written to (attributes) +        // Note: Blizzard MPQs have entries for (listfile) and (attributes), +        // but they are filled empty +        pbAttrFile = CreateAttributesFile(ha, &cbAttrFile); +        if(pbAttrFile != NULL) +        { +            // We expect it to be nonzero size +            assert(cbAttrFile != 0); + +            // Determine the real flags for (attributes) +            if(ha->dwFileFlags2 == MPQ_FILE_EXISTS) +                ha->dwFileFlags2 = GetDefaultSpecialFileFlags(cbAttrFile, ha->pHeader->wFormatVersion); + +            // Create the attributes file in the MPQ +            nError = SFileAddFile_Init(ha, ATTRIBUTES_NAME, +                                           0, +                                           cbAttrFile, +                                           LANG_NEUTRAL, +                                           ha->dwFileFlags2 | MPQ_FILE_REPLACEEXISTING, +                                          &hf); + +            // Write the attributes file raw data to it +            if(nError == ERROR_SUCCESS) +            { +                // Write the content of the attributes file to the MPQ +                nError = SFileAddFile_Write(hf, pbAttrFile, cbAttrFile, MPQ_COMPRESSION_ZLIB); +                SFileAddFile_Finish(hf); +            } -    // We expect at least one reserved entry to be there -    assert(ha->dwReservedFiles >= 1); -    ha->dwReservedFiles--; +            // Free the attributes buffer +            STORM_FREE(pbAttrFile); +        } +        else +        { +            // If the list file is empty, we assume ERROR_SUCCESS +            nError = (cbAttrFile == 0) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY; +        } -    // Create the raw data that is to be written to (attributes) -    // Note: Blizzard MPQs have entries for (listfile) and (attributes), -    // but they are filled empty -    pbAttrFile = CreateAttributesFile(ha, &cbAttrFile); -    if(pbAttrFile != NULL) -    { -        // We expect it to be nonzero size -        assert(cbAttrFile != 0); - -        // Determine the real flags for (attributes) -        if(ha->dwFileFlags2 == MPQ_FILE_EXISTS) -            ha->dwFileFlags2 = GetDefaultSpecialFileFlags(cbAttrFile, ha->pHeader->wFormatVersion); - -        // Create the attributes file in the MPQ -        nError = SFileAddFile_Init(ha, ATTRIBUTES_NAME, -                                       0, -                                       cbAttrFile, -                                       LANG_NEUTRAL, -                                       ha->dwFileFlags2 | MPQ_FILE_REPLACEEXISTING, -                                      &hf); - -        // Write the attributes file raw data to it +        // If the save process succeeded, we clear the MPQ_FLAG_ATTRIBUTE_INVALID flag          if(nError == ERROR_SUCCESS)          { -            // Write the content of the attributes file to the MPQ -            nError = SFileAddFile_Write(hf, pbAttrFile, cbAttrFile, MPQ_COMPRESSION_ZLIB); -            SFileAddFile_Finish(hf); - -            // Clear the invalidate flag              ha->dwFlags &= ~MPQ_FLAG_ATTRIBUTES_INVALID; +            ha->dwReservedFiles--;          } - -        // Free the attributes buffer -        STORM_FREE(pbAttrFile);      }      return nError; diff --git a/src/SFileCreateArchive.cpp b/src/SFileCreateArchive.cpp index a922e90..569b953 100644 --- a/src/SFileCreateArchive.cpp +++ b/src/SFileCreateArchive.cpp @@ -107,6 +107,7 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea      DWORD dwBlockTableSize = 0;             // Initial block table size      DWORD dwHashTableSize = 0;      DWORD dwReservedFiles = 0;              // Number of reserved file entries +    DWORD dwMpqFlags = 0;      int nError = ERROR_SUCCESS;      // Check the parameters, if they are valid @@ -154,11 +155,19 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea              return false;      } -    // Increment the maximum amount of files to have space for (listfile) and (attributes) +    // Increment the maximum amount of files to have space for (listfile)      if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags1) +    { +        dwMpqFlags |= MPQ_FLAG_LISTFILE_INVALID;          dwReservedFiles++; +    } + +    // Increment the maximum amount of files to have space for (attributes)      if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags2 && pCreateInfo->dwAttrFlags) +    { +        dwMpqFlags |= MPQ_FLAG_ATTRIBUTES_INVALID;          dwReservedFiles++; +    }      // If file count is not zero, initialize the hash table size      dwHashTableSize = GetHashTableSizeForFileCount(pCreateInfo->dwMaxFileCount + dwReservedFiles); @@ -197,7 +206,7 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea          ha->dwFileFlags1    = pCreateInfo->dwFileFlags1;          ha->dwFileFlags2    = pCreateInfo->dwFileFlags2;          ha->dwAttrFlags     = pCreateInfo->dwAttrFlags; -        ha->dwFlags         = 0; +        ha->dwFlags         = dwMpqFlags | MPQ_FLAG_CHANGED;          pStream = NULL;          // Fill the MPQ header @@ -219,9 +228,6 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea          // Write the naked MPQ header          nError = WriteNakedMPQHeader(ha); - -        // Remember that the (listfile) and (attributes) need to be saved -        ha->dwFlags |= MPQ_FLAG_CHANGED | MPQ_FLAG_LISTFILE_INVALID | MPQ_FLAG_ATTRIBUTES_INVALID;      }      // Create initial HET table, if the caller required an MPQ format 3.0 or newer diff --git a/src/SFileListFile.cpp b/src/SFileListFile.cpp index b2db0c5..ed549d1 100644 --- a/src/SFileListFile.cpp +++ b/src/SFileListFile.cpp @@ -363,49 +363,58 @@ int SListFileSaveToMpq(TMPQArchive * ha)      DWORD cbListFile = 0;      int nError = ERROR_SUCCESS; -    // Only save (listfile) if we should do so -    if(ha->dwFileFlags1 == 0 || ha->dwMaxFileCount == 0) -        return ERROR_SUCCESS; +    // Only save the listfile if we should do so +    if(ha->dwFileFlags1 != 0) +    { +        // At this point, we expect to have at least one reserved entry in the file table +        assert(ha->dwFlags & MPQ_FLAG_LISTFILE_INVALID); +        assert(ha->dwReservedFiles >= 1); + +        // Create the raw data that is to be written to (listfile) +        // Note: Creating the raw data before the (listfile) has been created in the MPQ +        // causes that the name of the listfile will not be included in the listfile itself. +        // That is OK, because (listfile) in Blizzard MPQs does not contain it either. +        pbListFile = CreateListFile(ha, &cbListFile); +        if(pbListFile != NULL) +        { +            // We expect it to be nonzero size +            assert(cbListFile != 0); + +            // Determine the real flags for (listfile) +            if(ha->dwFileFlags1 == MPQ_FILE_EXISTS) +                ha->dwFileFlags1 = GetDefaultSpecialFileFlags(cbListFile, ha->pHeader->wFormatVersion); + +            // Create the listfile in the MPQ +            nError = SFileAddFile_Init(ha, LISTFILE_NAME, +                                           0, +                                           cbListFile, +                                           LANG_NEUTRAL, +                                           ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING, +                                          &hf); + +            // Write the listfile raw data to it +            if(nError == ERROR_SUCCESS) +            { +                // Write the content of the listfile to the MPQ +                nError = SFileAddFile_Write(hf, pbListFile, cbListFile, MPQ_COMPRESSION_ZLIB); +                SFileAddFile_Finish(hf); +            } -    // At this point, we expect to have at least one reserved entry in the file table -    assert(ha->dwReservedFiles >= 1); -    ha->dwReservedFiles--; +            // Free the listfile buffer +            STORM_FREE(pbListFile); +        } +        else +        { +            // If the list file is empty, we assume ERROR_SUCCESS +            nError = (cbListFile == 0) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY; +        } -    // Create the raw data that is to be written to (listfile) -    // Note: Creating the raw data before the (listfile) has been created in the MPQ -    // causes that the name of the listfile will not be included in the listfile itself. -    // That is OK, because (listfile) in Blizzard MPQs does not contain it either. -    pbListFile = CreateListFile(ha, &cbListFile); -    if(pbListFile != NULL) -    { -        // We expect it to be nonzero size -        assert(cbListFile != 0); - -        // Determine the real flags for (listfile) -        if(ha->dwFileFlags1 == MPQ_FILE_EXISTS) -            ha->dwFileFlags1 = GetDefaultSpecialFileFlags(cbListFile, ha->pHeader->wFormatVersion); - -        // Create the listfile in the MPQ -        nError = SFileAddFile_Init(ha, LISTFILE_NAME, -                                       0, -                                       cbListFile, -                                       LANG_NEUTRAL, -                                       ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING, -                                      &hf); - -        // Write the listfile raw data to it +        // If the save process succeeded, we clear the MPQ_FLAG_LISTFILE_INVALID flag          if(nError == ERROR_SUCCESS)          { -            // Write the content of the listfile to the MPQ -            nError = SFileAddFile_Write(hf, pbListFile, cbListFile, MPQ_COMPRESSION_ZLIB); -            SFileAddFile_Finish(hf); - -            // Clear the invalidate flag              ha->dwFlags &= ~MPQ_FLAG_LISTFILE_INVALID; +            ha->dwReservedFiles--;          } - -        // Free the listfile buffer -        STORM_FREE(pbListFile);      }      return nError; diff --git a/test/Test.cpp b/test/Test.cpp index ed685a8..0b5e679 100644 --- a/test/Test.cpp +++ b/test/Test.cpp @@ -1615,9 +1615,6 @@ static int CreateNewArchive(TLogHelper * pLogger, const char * szPlainName, DWOR      CreateFullPathName(szFullPath, NULL, szPlainName);      remove(szFullPath); -    // Fix the flags -    dwCreateFlags |= (MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES); -      // Create the new MPQ      CopyFileName(szMpqName, szFullPath, strlen(szFullPath));      if(!SFileCreateArchive(szMpqName, dwCreateFlags, dwMaxFileCount, &hMpq)) @@ -1632,6 +1629,44 @@ static int CreateNewArchive(TLogHelper * pLogger, const char * szPlainName, DWOR      return ERROR_SUCCESS;  } +static int CreateNewArchive_V2(TLogHelper * pLogger, const char * szPlainName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq) +{ +    SFILE_CREATE_MPQ CreateInfo; +    HANDLE hMpq = NULL; +    TCHAR szMpqName[MAX_PATH]; +    char szFullPath[MAX_PATH]; + +    // Make sure that the MPQ is deleted +    CreateFullPathName(szFullPath, NULL, szPlainName); +    CopyFileName(szMpqName, szFullPath, strlen(szFullPath)); +    remove(szFullPath); + +    // Fill the create structure +    memset(&CreateInfo, 0, sizeof(SFILE_CREATE_MPQ)); +    CreateInfo.cbSize         = sizeof(SFILE_CREATE_MPQ); +    CreateInfo.dwMpqVersion   = (dwCreateFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT; +    CreateInfo.dwStreamFlags  = STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE; +    CreateInfo.dwFileFlags1   = (dwCreateFlags & MPQ_CREATE_LISTFILE)   ? MPQ_FILE_EXISTS : 0; +    CreateInfo.dwFileFlags2   = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_FILE_EXISTS : 0; +    CreateInfo.dwAttrFlags    = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? (MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_FILETIME | MPQ_ATTRIBUTE_MD5) : 0; +    CreateInfo.dwSectorSize   = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000; +    CreateInfo.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0; +    CreateInfo.dwMaxFileCount = dwMaxFileCount; + +    // Create the new MPQ +    if(!SFileCreateArchive2(szMpqName, &CreateInfo, &hMpq)) +        return pLogger->PrintError(_T("Failed to create archive %s"), szMpqName); + +    // Shall we close it right away? +    if(phMpq == NULL) +        SFileCloseArchive(hMpq); +    else +        *phMpq = hMpq; + +    return ERROR_SUCCESS; +} + +  // Creates new archive with UNICODE name. Adds prefix to the name  static int CreateNewArchiveU(TLogHelper * pLogger, const wchar_t * szPlainName, DWORD dwCreateFlags, DWORD dwMaxFileCount)  { @@ -2824,7 +2859,7 @@ static int TestCreateArchive_EmptyMpq(const char * szPlainName, DWORD dwCreateFl      TLogHelper Logger("CreateEmptyMpq", szPlainName);      HANDLE hMpq = NULL;      DWORD dwFileCount = 0; -    int nError; +    int nError;                                                           // Create the full path name      nError = CreateNewArchive(&Logger, szPlainName, dwCreateFlags, 0, &hMpq); @@ -2852,7 +2887,35 @@ static int TestCreateArchive_EmptyMpq(const char * szPlainName, DWORD dwCreateFl      return nError;  } -static int TestCreateArchive_FillArchive(const char * szPlainName) +static int TestCreateArchive_MpqEditor(const char * szPlainName, const char * szFileName) +{ +    TLogHelper Logger("CreateMpqEditor", szPlainName); +    HANDLE hMpq = NULL; +    int nError = ERROR_SUCCESS; + +    // Create new MPQ +    nError = CreateNewArchive_V2(&Logger, szPlainName, MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, 4000, &hMpq); +    if(nError == ERROR_SUCCESS) +    { +        // Flush the archive first +        SFileFlushArchive(hMpq); + +        // Add one file +        nError = AddFileToMpq(&Logger, hMpq, szFileName, "This is the file data.", MPQ_FILE_COMPRESS); + +        // Flush the archive again +        SFileFlushArchive(hMpq); +        SFileCloseArchive(hMpq); +    } +    else +    { +        nError = GetLastError(); +    } + +    return nError; +} + +static int TestCreateArchive_FillArchive(const char * szPlainName, DWORD dwCreateFlags)  {      TLogHelper Logger("CreateFullMpq", szPlainName);      const char * szFileData = "TestCreateArchive_FillArchive: Testing file data"; @@ -2863,12 +2926,21 @@ static int TestCreateArchive_FillArchive(const char * szPlainName)      DWORD dwFlags = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS;      int nError; -    // Create the new MPQ -    nError = CreateNewArchive(&Logger, szPlainName, 0, dwMaxFileCount, &hMpq); +    // Note that StormLib will round the maxfile count +    // up to hash table size (nearest power of two) +    if((dwCreateFlags & MPQ_CREATE_LISTFILE) == 0) +        dwMaxFileCount++; +    if((dwCreateFlags & MPQ_CREATE_ATTRIBUTES) == 0) +        dwMaxFileCount++; -    // Now we should be able to add 6 files +    // Create the new MPQ archive +    nError = CreateNewArchive_V2(&Logger, szPlainName, dwCreateFlags, dwMaxFileCount, &hMpq);      if(nError == ERROR_SUCCESS)      { +        // Flush the archive first +        SFileFlushArchive(hMpq); + +        // Add all files          for(unsigned int i = 0; i < dwMaxFileCount; i++)          {              sprintf(szFileName, "AddedFile%03u.txt", i); @@ -2876,6 +2948,9 @@ static int TestCreateArchive_FillArchive(const char * szPlainName)              if(nError != ERROR_SUCCESS)                  break;          } + +        // Flush the archive again +        SFileFlushArchive(hMpq);      }      // Now the MPQ should be full. It must not be possible to add another file @@ -2898,33 +2973,34 @@ static int TestCreateArchive_FillArchive(const char * szPlainName)      // The archive should still be full      if(nError == ERROR_SUCCESS)      { -        CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, true); -        CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, true); -        AddFileToMpq(&Logger, hMpq, "ShouldNotBeHere.txt", szFileData, MPQ_FILE_COMPRESS, MPQ_COMPRESSION_ZLIB, false); +        CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, (dwCreateFlags & MPQ_CREATE_LISTFILE) ? true : false); +        CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? true : false); +        nError = AddFileToMpq(&Logger, hMpq, "ShouldNotBeHere.txt", szFileData, MPQ_FILE_COMPRESS, MPQ_COMPRESSION_ZLIB, false); +        assert(nError != ERROR_SUCCESS); +        nError = ERROR_SUCCESS;      }      // The (listfile) must be present      if(nError == ERROR_SUCCESS)      { -        CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, true); -        CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, true); +        CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, (dwCreateFlags & MPQ_CREATE_LISTFILE) ? true : false); +        CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? true : false);          nError = RemoveMpqFile(&Logger, hMpq, szFileName, true);      }      // Now add the file again. This time, it should be possible OK      if(nError == ERROR_SUCCESS)      { -        CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, false); -        CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, false);          nError = AddFileToMpq(&Logger, hMpq, szFileName, szFileData, dwFlags, dwCompression, true); +        assert(nError == ERROR_SUCCESS);      }      // Now add the file again. This time, it should be fail      if(nError == ERROR_SUCCESS)      { -        CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, false); -        CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, false); -        AddFileToMpq(&Logger, hMpq, szFileName, szFileData, dwFlags, dwCompression, false); +        nError = AddFileToMpq(&Logger, hMpq, szFileName, szFileData, dwFlags, dwCompression, false); +        assert(nError != ERROR_SUCCESS); +        nError = ERROR_SUCCESS;      }      // Close the archive and return @@ -2938,8 +3014,8 @@ static int TestCreateArchive_FillArchive(const char * szPlainName)          nError = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq);          if(nError == ERROR_SUCCESS)          { -            CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, true); -            CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, true); +            CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, (dwCreateFlags & MPQ_CREATE_LISTFILE) ? true : false); +            CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? true : false);              SFileCloseArchive(hMpq);          }      } @@ -2957,7 +3033,7 @@ static int TestCreateArchive_IncMaxFileCount(const char * szPlainName)      int nError;      // Create the new MPQ -    nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V4, dwMaxFileCount, &hMpq); +    nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V4 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, dwMaxFileCount, &hMpq);      // Now add exactly one file      if(nError == ERROR_SUCCESS) @@ -3007,29 +3083,30 @@ static int TestCreateArchive_IncMaxFileCount(const char * szPlainName)  static int TestCreateArchive_UnicodeNames()  {      TLogHelper Logger("MpqUnicodeName"); +    DWORD dwCreateFlags = MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES;      int nError = ERROR_SUCCESS; -    nError = CreateNewArchiveU(&Logger, szUnicodeName1, MPQ_CREATE_ARCHIVE_V1, 15); +    nError = CreateNewArchiveU(&Logger, szUnicodeName1, dwCreateFlags | MPQ_CREATE_ARCHIVE_V1, 15);      if(nError != ERROR_SUCCESS)          return nError; -    nError = CreateNewArchiveU(&Logger, szUnicodeName2, MPQ_CREATE_ARCHIVE_V2, 58); +    nError = CreateNewArchiveU(&Logger, szUnicodeName2, dwCreateFlags | MPQ_CREATE_ARCHIVE_V2, 58);      if(nError != ERROR_SUCCESS)          return nError; -    nError = CreateNewArchiveU(&Logger, szUnicodeName3, MPQ_CREATE_ARCHIVE_V3, 15874); +    nError = CreateNewArchiveU(&Logger, szUnicodeName3, dwCreateFlags | MPQ_CREATE_ARCHIVE_V3, 15874);      if(nError != ERROR_SUCCESS)          return nError; -    nError = CreateNewArchiveU(&Logger, szUnicodeName4, MPQ_CREATE_ARCHIVE_V4, 87541); +    nError = CreateNewArchiveU(&Logger, szUnicodeName4, dwCreateFlags | MPQ_CREATE_ARCHIVE_V4, 87541);      if(nError != ERROR_SUCCESS)          return nError; -    nError = CreateNewArchiveU(&Logger, szUnicodeName5, MPQ_CREATE_ARCHIVE_V3, 87541); +    nError = CreateNewArchiveU(&Logger, szUnicodeName5, dwCreateFlags | MPQ_CREATE_ARCHIVE_V3, 87541);      if(nError != ERROR_SUCCESS)          return nError; -    nError = CreateNewArchiveU(&Logger, szUnicodeName5, MPQ_CREATE_ARCHIVE_V2, 87541); +    nError = CreateNewArchiveU(&Logger, szUnicodeName5, dwCreateFlags | MPQ_CREATE_ARCHIVE_V2, 87541);      if(nError != ERROR_SUCCESS)          return nError; @@ -3060,7 +3137,7 @@ static int TestCreateArchive_FileFlagTest(const char * szPlainName)      // Create new MPQ archive over that file      if(nError == ERROR_SUCCESS) -        nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V1, 17, &hMpq); +        nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V1 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, 17, &hMpq);      // Add the same file multiple times      if(nError == ERROR_SUCCESS) @@ -3209,7 +3286,7 @@ static int TestCreateArchive_WaveCompressionsTest(const char * szPlainName, cons      CreateFullPathName(szFileName, szMpqSubDir, szWaveFile);      // Create new archive -    nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V1, 0x40, &hMpq);  +    nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V1 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, 0x40, &hMpq);       // Add the same file multiple times      if(nError == ERROR_SUCCESS) @@ -3265,7 +3342,7 @@ static int TestCreateArchive_ListFilePos(const char * szPlainName)      int nError;      // Create a new archive with the limit of 0x20 files -    nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V4, dwMaxFileCount, &hMpq); +    nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V4 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, dwMaxFileCount, &hMpq);      // Add 0x1E files      if(nError == ERROR_SUCCESS) @@ -3354,7 +3431,7 @@ static int TestCreateArchive_BigArchive(const char * szPlainName)      int nError;      // Create a new archive with the limit of 0x20 files -    nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V3, dwMaxFileCount, &hMpq); +    nError = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V3 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, dwMaxFileCount, &hMpq);      if(nError == ERROR_SUCCESS)      {          // Now add few really big files @@ -3472,11 +3549,6 @@ int main(int argc, char * argv[])      printf("==== Test Suite for StormLib version %s ====\n", STORMLIB_VERSION_STRING);      nError = InitializeMpqDirectory(argv, argc); -    HANDLE hMpq = NULL; -    if(SFileOpenArchive(_T("e:\\Ladik\\Incoming\\Castle Defense v7.3 NEWest.w3x"), 0, 0, &hMpq)) -        SFileCloseArchive(hMpq); - -      // Not a test, but rather a tool for creating links to duplicated files  //  if(nError == ERROR_SUCCESS)  //      nError = FindFilePairs(ForEachFile_CreateArchiveLink, "2004 - WoW\\06080", "2004 - WoW\\06299"); @@ -3702,15 +3774,31 @@ int main(int argc, char * argv[])      // Test archive compacting      // Create an empty archive v2      if(nError == ERROR_SUCCESS) -        nError = TestCreateArchive_EmptyMpq("StormLibTest_EmptyMpq_v2.mpq", MPQ_CREATE_ARCHIVE_V2); +        nError = TestCreateArchive_EmptyMpq("StormLibTest_EmptyMpq_v2.mpq", MPQ_CREATE_ARCHIVE_V2 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES);      // Create an empty archive v4      if(nError == ERROR_SUCCESS) -        nError = TestCreateArchive_EmptyMpq("StormLibTest_EmptyMpq_v4.mpq", MPQ_CREATE_ARCHIVE_V4); +        nError = TestCreateArchive_EmptyMpq("StormLibTest_EmptyMpq_v4.mpq", MPQ_CREATE_ARCHIVE_V4 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES); + +    // Test creating of an archive the same way like MPQ Editor does +    if(nError == ERROR_SUCCESS) +        nError = TestCreateArchive_MpqEditor("StormLibTest_MpqEditorTest.mpq", "AddedFile.exe"); + +    // Create an archive and fill it with files up to the max file count +    if(nError == ERROR_SUCCESS) +        nError = TestCreateArchive_FillArchive("StormLibTest_FileTableFull.mpq", 0); + +    // Create an archive and fill it with files up to the max file count +    if(nError == ERROR_SUCCESS) +        nError = TestCreateArchive_FillArchive("StormLibTest_FileTableFull.mpq", MPQ_CREATE_LISTFILE); + +    // Create an archive and fill it with files up to the max file count +    if(nError == ERROR_SUCCESS) +        nError = TestCreateArchive_FillArchive("StormLibTest_FileTableFull.mpq", MPQ_CREATE_ATTRIBUTES);      // Create an archive and fill it with files up to the max file count      if(nError == ERROR_SUCCESS) -        nError = TestCreateArchive_FillArchive("StormLibTest_FileTableFull.mpq"); +        nError = TestCreateArchive_FillArchive("StormLibTest_FileTableFull.mpq", MPQ_CREATE_ATTRIBUTES | MPQ_CREATE_LISTFILE);      // Create an archive, and increment max file count several times      if(nError == ERROR_SUCCESS) | 
