diff options
author | unknown <E:\Ladik\Mail> | 2015-05-01 07:06:29 +0200 |
---|---|---|
committer | unknown <E:\Ladik\Mail> | 2015-05-01 07:06:29 +0200 |
commit | 46930855f500c1b494e3b16bb7a3323c07d4d5fb (patch) | |
tree | 6220cc643761137a930841d8ec828db9f3db53cf /src/SFileAddFile.cpp | |
parent | a205159d004871efbedd7cbfb686b8fe82bfb532 (diff) |
+ Removed back reference of FileTable -> HashTable, as it is logically incorrect
+ Optimized patching process so it consimes less memory
+ Added hash table and block table defragmenting for malformed War3 maps
Diffstat (limited to 'src/SFileAddFile.cpp')
-rw-r--r-- | src/SFileAddFile.cpp | 245 |
1 files changed, 102 insertions, 143 deletions
diff --git a/src/SFileAddFile.cpp b/src/SFileAddFile.cpp index aa47aa7..70a857b 100644 --- a/src/SFileAddFile.cpp +++ b/src/SFileAddFile.cpp @@ -354,7 +354,7 @@ static int RecryptFileData( } //----------------------------------------------------------------------------- -// Support functions for adding files to the MPQ +// Internal support for MPQ modifications int SFileAddFile_Init( TMPQArchive * ha, @@ -368,6 +368,7 @@ int SFileAddFile_Init( TFileEntry * pFileEntry = NULL; ULONGLONG TempPos; // For various file offset calculations TMPQFile * hf = NULL; // File structure for newly added file + DWORD dwHashIndex = HASH_ENTRY_FREE; int nError = ERROR_SUCCESS; // @@ -410,7 +411,7 @@ int SFileAddFile_Init( if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) { TempPos = hf->MpqFilePos + dwFileSize; - TempPos += ha->dwHashTableSize * sizeof(TMPQHash); + TempPos += ha->pHeader->dwHashTableSize * sizeof(TMPQHash); TempPos += ha->dwFileTableSize * sizeof(TMPQBlock); if((TempPos >> 32) != 0) nError = ERROR_DISK_FULL; @@ -421,25 +422,24 @@ int SFileAddFile_Init( if(nError == ERROR_SUCCESS) { // Check if the file already exists in the archive - pFileEntry = GetFileEntryExact(ha, szFileName, lcLocale); - if(pFileEntry == NULL) - { - // First, free the internal files - InvalidateInternalFiles(ha); - - // Find a free entry in the file table - pFileEntry = AllocateFileEntry(ha, szFileName, lcLocale); - if(pFileEntry == NULL) - nError = ERROR_DISK_FULL; - } - else + pFileEntry = GetFileEntryExact(ha, szFileName, lcLocale, &dwHashIndex); + if(pFileEntry != NULL) { - // If the caller didn't set MPQ_FILE_REPLACEEXISTING, fail it if(dwFlags & MPQ_FILE_REPLACEEXISTING) InvalidateInternalFiles(ha); else nError = ERROR_ALREADY_EXISTS; } + else + { + // Free all internal files - (listfile), (attributes), (signature) + InvalidateInternalFiles(ha); + + // Attempt to allocate new file entry + pFileEntry = AllocateFileEntry(ha, szFileName, lcLocale, &dwHashIndex); + if(pFileEntry == NULL) + nError = ERROR_DISK_FULL; + } } // Fill the file entry and TMPQFile structure @@ -452,7 +452,14 @@ int SFileAddFile_Init( // Initialize the hash entry for the file hf->pFileEntry = pFileEntry; hf->dwDataSize = dwFileSize; - + + // Set the hash table entry + if(ha->pHashTable != NULL && dwHashIndex < ha->pHeader->dwHashTableSize) + { + hf->pHashEntry = ha->pHashTable + dwHashIndex; + hf->pHashEntry->lcLocale = (USHORT)lcLocale; + } + // Decrypt the file key if(dwFlags & MPQ_FILE_ENCRYPTED) hf->dwFileKey = DecryptFileKey(szFileName, hf->MpqFilePos, dwFileSize, dwFlags); @@ -462,7 +469,6 @@ int SFileAddFile_Init( pFileEntry->dwFileSize = dwFileSize; pFileEntry->dwCmpSize = 0; pFileEntry->dwFlags = dwFlags | MPQ_FILE_EXISTS; - pFileEntry->lcLocale = (USHORT)lcLocale; // Initialize the file time, CRC32 and MD5 assert(sizeof(hf->hctx) >= sizeof(hash_state)); @@ -668,7 +674,7 @@ int SFileAddFile_Finish(TMPQFile * hf) { // Free the file entry in MPQ tables if(pFileEntry != NULL) - DeleteFileEntry(ha, pFileEntry); + DeleteFileEntry(ha, hf); } // Clear the add file callback @@ -1007,72 +1013,59 @@ bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szA //----------------------------------------------------------------------------- // bool SFileRemoveFile(HANDLE hMpq, char * szFileName) // -// This function removes a file from the archive. The file content -// remains there, only the entries in the hash table and in the block -// table are updated. +// This function removes a file from the archive. +// bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope) { - TMPQArchive * ha = (TMPQArchive *)hMpq; - TFileEntry * pFileEntry = NULL; // File entry of the file to be deleted - DWORD dwFileIndex = 0; + TMPQArchive * ha = IsValidMpqHandle(hMpq); + TMPQFile * hf = NULL; int nError = ERROR_SUCCESS; // Keep compiler happy dwSearchScope = dwSearchScope; // Check the parameters - if(nError == ERROR_SUCCESS) - { - if(!IsValidMpqHandle(hMpq)) - nError = ERROR_INVALID_HANDLE; - if(szFileName == NULL || *szFileName == 0) - nError = ERROR_INVALID_PARAMETER; - if(IsInternalMpqFileName(szFileName)) - nError = ERROR_INTERNAL_FILE; - } + if(ha == NULL) + nError = ERROR_INVALID_HANDLE; + if(szFileName == NULL || *szFileName == 0) + nError = ERROR_INVALID_PARAMETER; + if(IsInternalMpqFileName(szFileName)) + nError = ERROR_INTERNAL_FILE; + // Do not allow to remove files from read-only or patched MPQs if(nError == ERROR_SUCCESS) { - // Do not allow to remove files from MPQ open for read only - if(ha->dwFlags & MPQ_FLAG_READ_ONLY) + if((ha->dwFlags & MPQ_FLAG_READ_ONLY) || (ha->haPatch != NULL)) nError = ERROR_ACCESS_DENIED; } - // Get hash entry belonging to this file + // If all checks have passed, we can delete the file from the MPQ if(nError == ERROR_SUCCESS) { - if(!IsPseudoFileName(szFileName, &dwFileIndex)) + // Open the file from the MPQ + if(SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_BASE_FILE, (HANDLE *)&hf)) { - if((pFileEntry = GetFileEntryExact(ha, (char *)szFileName, lcFileLocale)) == NULL) - nError = ERROR_FILE_NOT_FOUND; + // Delete the file entry + nError = DeleteFileEntry(ha, hf); + FreeFileHandle(hf); } else - { - if((pFileEntry = GetFileEntryByIndex(ha, dwFileIndex)) == NULL) - nError = ERROR_FILE_NOT_FOUND; - } - } - - // Test if the file is not already deleted - if(nError == ERROR_SUCCESS) - { - if(!(pFileEntry->dwFlags & MPQ_FILE_EXISTS)) - nError = ERROR_FILE_NOT_FOUND; + nError = GetLastError(); } + // If the file has been deleted, we need to invalidate + // the internal files and recreate HET table if(nError == ERROR_SUCCESS) { - // Invalidate the entries for (listfile) and (attributes) + // Invalidate the entries for internal files // After we are done with MPQ changes, we need to re-create them anyway InvalidateInternalFiles(ha); - // Delete the file entry in the file table and defragment the file table - DeleteFileEntry(ha, pFileEntry); - - // We also need to rebuild the HET table, if present - if(ha->pHetTable != NULL) - nError = RebuildHetTable(ha); + // + // Don't rebuild HET table now; the file's flags indicate + // that it's been deleted, which is enough + // } // Resolve error and exit @@ -1084,81 +1077,56 @@ bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearch // Renames the file within the archive. bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * szNewFileName) { - TMPQArchive * ha = (TMPQArchive *)hMpq; - TFileEntry * pFileEntry = NULL; - ULONGLONG RawDataOffs; + TMPQArchive * ha = IsValidMpqHandle(hMpq); TMPQFile * hf; int nError = ERROR_SUCCESS; // Test the valid parameters - if(nError == ERROR_SUCCESS) - { - if(!IsValidMpqHandle(hMpq)) - nError = ERROR_INVALID_HANDLE; - if(szFileName == NULL || *szFileName == 0 || szNewFileName == NULL || *szNewFileName == 0) - nError = ERROR_INVALID_PARAMETER; - } + if(ha == NULL) + nError = ERROR_INVALID_HANDLE; + if(szFileName == NULL || *szFileName == 0 || szNewFileName == NULL || *szNewFileName == 0) + nError = ERROR_INVALID_PARAMETER; + if(IsInternalMpqFileName(szFileName) || IsInternalMpqFileName(szNewFileName)) + nError = ERROR_INTERNAL_FILE; + // Do not allow to rename files in MPQ open for read only if(nError == ERROR_SUCCESS) { - // Do not allow to rename files in MPQ open for read only if(ha->dwFlags & MPQ_FLAG_READ_ONLY) nError = ERROR_ACCESS_DENIED; - - // Do not allow renaming anything to a pseudo-file name - if(IsPseudoFileName(szFileName, NULL) || IsPseudoFileName(szNewFileName, NULL)) - nError = ERROR_INVALID_PARAMETER; - - // Do not allow to rename any of the internal files - // Also do not allow to rename any of files to an internal file - if(IsInternalMpqFileName(szFileName) || IsInternalMpqFileName(szNewFileName)) - nError = ERROR_INTERNAL_FILE; } - // Find the current file entry. + // Open the new file. If exists, we don't allow rename operation if(nError == ERROR_SUCCESS) { - // Get the file entry - pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); - if(pFileEntry == NULL) - nError = ERROR_FILE_NOT_FOUND; - } - - // Also try to find file entry for the new file. - // This verifies if we are not overwriting an existing file - // (whose name we perhaps don't know) - if(nError == ERROR_SUCCESS) - { - if(GetFileEntryLocale(ha, szNewFileName, pFileEntry->lcLocale) != NULL) + if(GetFileEntryLocale(ha, szNewFileName, lcFileLocale) != NULL) nError = ERROR_ALREADY_EXISTS; } - // Now we rename the existing file entry. + // Open the file from the MPQ if(nError == ERROR_SUCCESS) { - // Rename the file entry - nError = RenameFileEntry(ha, pFileEntry, szNewFileName); - } + // Attempt to open the file + if(SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_BASE_FILE, (HANDLE *)&hf)) + { + ULONGLONG RawDataOffs; + TFileEntry * pFileEntry = hf->pFileEntry; - // Now we need to recreate the HET table, if we have one - if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) - nError = RebuildHetTable(ha); + // Invalidate the entries for internal files + InvalidateInternalFiles(ha); - // Now we copy the existing file entry to the new one - if(nError == ERROR_SUCCESS) - { - // If the file is encrypted, we have to re-crypt the file content - // with the new decryption key - if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) - { - hf = CreateFileHandle(ha, pFileEntry); - if(hf != NULL) + // Rename the file entry in the table + nError = RenameFileEntry(ha, hf, szNewFileName); + + // If the file is encrypted, we have to re-crypt the file content + // with the new decryption key + if((nError == ERROR_SUCCESS) && (pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)) { // Recrypt the file data in the MPQ nError = RecryptFileData(ha, hf, szFileName, szNewFileName); - // Update the MD5 - if(ha->pHeader->dwRawChunkSize != 0) + // Update the MD5 of the raw block + if(nError == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0) { RawDataOffs = ha->MpqPos + pFileEntry->ByteOffset; WriteMpqDataMD5(ha->pStream, @@ -1166,20 +1134,22 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s pFileEntry->dwCmpSize, ha->pHeader->dwRawChunkSize); } - - FreeFileHandle(hf); - } - else - { - nError = ERROR_NOT_ENOUGH_MEMORY; } + + // Free the file handle + FreeFileHandle(hf); + } + else + { + nError = GetLastError(); } } - // Note: MPQ_FLAG_CHANGED is set by RenameFileEntry - // assert((ha->dwFlags & MPQ_FLAG_CHANGED) != 0); + // We also need to rebuild the HET table, if present + if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) + nError = RebuildHetTable(ha); - // Resolve error and return + // Resolve error and exit if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); @@ -1209,15 +1179,23 @@ bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale) { TMPQArchive * ha; TFileEntry * pFileEntry; - TMPQFile * hf = (TMPQFile *)hFile; + TMPQFile * hf = IsValidFileHandle(hFile); // Invalid handle => do nothing - if(!IsValidFileHandle(hFile)) + if(hf == NULL) { SetLastError(ERROR_INVALID_HANDLE); return false; } + // Do not allow to rename files in MPQ open for read only + ha = hf->ha; + if(ha->dwFlags & MPQ_FLAG_READ_ONLY) + { + SetLastError(ERROR_ACCESS_DENIED); + return false; + } + // Do not allow unnamed access if(hf->pFileEntry->szFileName == NULL) { @@ -1232,42 +1210,23 @@ bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale) return false; } - // Do not allow changing file locales in MPQs version 3 or higher - ha = hf->ha; - if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_3) + // Do not allow changing file locales if there is no hash table + if(hf->pHashEntry == NULL) { SetLastError(ERROR_NOT_SUPPORTED); return false; } - // Do not allow to rename files in MPQ open for read only - if(ha->dwFlags & MPQ_FLAG_READ_ONLY) - { - SetLastError(ERROR_ACCESS_DENIED); - return false; - } - - // If the file already has that locale, return OK - if(hf->pFileEntry->lcLocale == lcNewLocale) - return true; - // We have to check if the file+locale is not already there - pFileEntry = GetFileEntryExact(ha, hf->pFileEntry->szFileName, lcNewLocale); + pFileEntry = GetFileEntryExact(ha, hf->pFileEntry->szFileName, lcNewLocale, NULL); if(pFileEntry != NULL) { SetLastError(ERROR_ALREADY_EXISTS); return false; } - // Set the locale and return success - pFileEntry = hf->pFileEntry; - pFileEntry->lcLocale = (USHORT)lcNewLocale; - - // Save the new locale to the hash table, if any - if(ha->pHashTable != NULL) - ha->pHashTable[pFileEntry->dwHashIndex].lcLocale = (USHORT)lcNewLocale; - - // Remember that the MPQ tables have been changed + // Update the locale in the hash table entry + hf->pHashEntry->lcLocale = (USHORT)lcNewLocale; ha->dwFlags |= MPQ_FLAG_CHANGED; return true; } |