diff options
author | Ladislav <Zezula> | 2014-03-27 17:21:38 +0100 |
---|---|---|
committer | Ladislav <Zezula> | 2014-03-27 17:21:38 +0100 |
commit | 27631209d90b8863db06b4c81c7b21efb32f2f2d (patch) | |
tree | a8680e8c5c4262c514b25d129eb1cd0f1ddf6178 | |
parent | c8db90a9c4dcf30b626861bfe6efeec818a78719 (diff) |
+ Fixed crash when adding file after an empty archive has been flushed
-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) |