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/SFileOpenFileEx.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/SFileOpenFileEx.cpp')
-rw-r--r-- | src/SFileOpenFileEx.cpp | 316 |
1 files changed, 130 insertions, 186 deletions
diff --git a/src/SFileOpenFileEx.cpp b/src/SFileOpenFileEx.cpp index 3a9976e..3794680 100644 --- a/src/SFileOpenFileEx.cpp +++ b/src/SFileOpenFileEx.cpp @@ -16,6 +16,33 @@ /* Local functions */ /*****************************************************************************/ +static DWORD FindHashIndex(TMPQArchive * ha, DWORD dwFileIndex) +{ + TMPQHash * pHashTableEnd; + TMPQHash * pHash; + DWORD dwFirstIndex = HASH_ENTRY_FREE; + + // Should only be called if the archive has hash table + assert(ha->pHashTable != NULL); + + // Multiple hash table entries can point to the file table entry. + // We need to search all of them + pHashTableEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + for(pHash = ha->pHashTable; pHash < pHashTableEnd; pHash++) + { + if(pHash->dwBlockIndex == dwFileIndex) + { + // Duplicate hash entry found + if(dwFirstIndex != HASH_ENTRY_FREE) + return HASH_ENTRY_FREE; + dwFirstIndex = (DWORD)(pHash - ha->pHashTable); + } + } + + // Return the hash table entry index + return dwFirstIndex; +} + static const char * GetPatchFileName(TMPQArchive * ha, const char * szFileName, char * szBuffer) { TMPQNamePrefix * pPrefix; @@ -41,7 +68,7 @@ static const char * GetPatchFileName(TMPQArchive * ha, const char * szFileName, return szFileName; } -static bool OpenLocalFile(const char * szFileName, HANDLE * phFile) +static bool OpenLocalFile(const char * szFileName, HANDLE * PtrFile) { TFileStream * pStream; TMPQFile * hf = NULL; @@ -59,7 +86,7 @@ static bool OpenLocalFile(const char * szFileName, HANDLE * phFile) if(hf != NULL) { hf->pStream = pStream; - *phFile = hf; + *PtrFile = hf; return true; } else @@ -68,11 +95,11 @@ static bool OpenLocalFile(const char * szFileName, HANDLE * phFile) SetLastError(ERROR_NOT_ENOUGH_MEMORY); } } - *phFile = NULL; + *PtrFile = NULL; return false; } -bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, HANDLE * phFile) +bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, HANDLE * PtrFile) { TMPQArchive * haBase = NULL; TMPQArchive * ha = (TMPQArchive *)hMpq; @@ -88,7 +115,7 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, HANDLE * phFile) while(ha != NULL) { // If the file is there, then we remember the archive - pFileEntry = GetFileEntryExact(ha, GetPatchFileName(ha, szFileName, szNameBuffer), 0); + pFileEntry = GetFileEntryExact(ha, GetPatchFileName(ha, szFileName, szNameBuffer), 0, NULL); if(pFileEntry != NULL && (pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) haBase = ha; @@ -130,8 +157,8 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, HANDLE * phFile) } // Give the updated base MPQ - if(phFile != NULL) - *phFile = (HANDLE)hfBase; + if(PtrFile != NULL) + *PtrFile = (HANDLE)hfBase; return (hfBase != NULL); } @@ -148,15 +175,15 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, HANDLE * phFile) int WINAPI SFileEnumLocales( HANDLE hMpq, const char * szFileName, - LCID * plcLocales, - LPDWORD pdwMaxLocales, + LCID * PtrLocales, + LPDWORD PtrMaxLocales, DWORD dwSearchScope) { TMPQArchive * ha = (TMPQArchive *)hMpq; - TFileEntry * pFileEntry; TMPQHash * pFirstHash; TMPQHash * pHash; DWORD dwFileIndex = 0; + DWORD dwMaxLocales; DWORD dwLocales = 0; // Test the parameters @@ -164,130 +191,63 @@ int WINAPI SFileEnumLocales( return ERROR_INVALID_HANDLE; if(szFileName == NULL || *szFileName == 0) return ERROR_INVALID_PARAMETER; - if(pdwMaxLocales == NULL) + if(ha->pHashTable == NULL) + return ERROR_NOT_SUPPORTED; + if(PtrMaxLocales == NULL) + return ERROR_INVALID_PARAMETER; + if(IsPseudoFileName(szFileName, &dwFileIndex)) return ERROR_INVALID_PARAMETER; // Keep compiler happy + dwMaxLocales = PtrMaxLocales[0]; dwSearchScope = dwSearchScope; - // Parse hash table entries for all locales - if(!IsPseudoFileName(szFileName, &dwFileIndex)) - { - // Calculate the number of locales - pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); - while(pHash != NULL) - { - dwLocales++; - pHash = GetNextHashEntry(ha, pFirstHash, pHash); - } - - // Test if there is enough space to copy the locales - if(*pdwMaxLocales < dwLocales) - { - *pdwMaxLocales = dwLocales; - return ERROR_INSUFFICIENT_BUFFER; - } - - // Enum the locales - pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); - while(pHash != NULL) - { - *plcLocales++ = pHash->lcLocale; - pHash = GetNextHashEntry(ha, pFirstHash, pHash); - } - } - else - { - // There must be space for 1 locale - if(*pdwMaxLocales < 1) - { - *pdwMaxLocales = 1; - return ERROR_INSUFFICIENT_BUFFER; - } - - // For nameless access, always return 1 locale - pFileEntry = GetFileEntryByIndex(ha, dwFileIndex); - pHash = ha->pHashTable + pFileEntry->dwHashIndex; - *plcLocales = pHash->lcLocale; - dwLocales = 1; - } - - // Give the caller the total number of found locales - *pdwMaxLocales = dwLocales; - return ERROR_SUCCESS; -} - -//----------------------------------------------------------------------------- -// SFileHasFile -// -// hMpq - Handle of opened MPQ archive -// szFileName - Name of file to look for - -bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName) -{ - TMPQArchive * ha = (TMPQArchive *)hMpq; - TFileEntry * pFileEntry; - DWORD dwFlagsToCheck = MPQ_FILE_EXISTS; - DWORD dwFileIndex = 0; - char szPrefixBuffer[MAX_PATH]; - bool bIsPseudoName; - int nError = ERROR_SUCCESS; - - if(!IsValidMpqHandle(hMpq)) - nError = ERROR_INVALID_HANDLE; - if(szFileName == NULL || *szFileName == 0) - nError = ERROR_INVALID_PARAMETER; - - // Prepare the file opening - if(nError == ERROR_SUCCESS) + // Parse all files with that name + pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); + while(pHash != NULL) { - // Different processing for pseudo-names - bIsPseudoName = IsPseudoFileName(szFileName, &dwFileIndex); - - // Walk through the MPQ and all patches - while(ha != NULL) - { - // Verify presence of the file - pFileEntry = (bIsPseudoName == false) ? GetFileEntryLocale(ha, GetPatchFileName(ha, szFileName, szPrefixBuffer), lcFileLocale) - : GetFileEntryByIndex(ha, dwFileIndex); - // Verify the file flags - if(pFileEntry != NULL && (pFileEntry->dwFlags & dwFlagsToCheck) == MPQ_FILE_EXISTS) - return true; - - // If this is patched archive, go to the patch - dwFlagsToCheck = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE; - ha = ha->haPatch; - } + // Put the locales to the buffer + if(PtrLocales != NULL && dwLocales < dwMaxLocales) + *PtrLocales++ = pHash->lcLocale; + dwLocales++; - // Not found, sorry - nError = ERROR_FILE_NOT_FOUND; + // Get the next locale + pHash = GetNextHashEntry(ha, pFirstHash, pHash); } - // Cleanup - SetLastError(nError); - return false; + // Give the caller the number of locales and return + PtrMaxLocales[0] = dwLocales; + return (dwLocales <= dwMaxLocales) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER; } - //----------------------------------------------------------------------------- // SFileOpenFileEx // // hMpq - Handle of opened MPQ archive // szFileName - Name of file to open // dwSearchScope - Where to search -// phFile - Pointer to store opened file handle +// PtrFile - Pointer to store opened file handle -bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile) +bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * PtrFile) { - TMPQArchive * ha = (TMPQArchive *)hMpq; + TMPQArchive * ha = IsValidMpqHandle(hMpq); TFileEntry * pFileEntry = NULL; TMPQFile * hf = NULL; + DWORD dwHashIndex = HASH_ENTRY_FREE; DWORD dwFileIndex = 0; bool bOpenByIndex = false; int nError = ERROR_SUCCESS; // Don't accept NULL pointer to file handle - if(phFile == NULL) + if(szFileName == NULL || *szFileName == 0) + nError = ERROR_INVALID_PARAMETER; + + // When opening a file from MPQ, the handle must be valid + if(dwSearchScope != SFILE_OPEN_LOCAL_FILE && ha == NULL) + nError = ERROR_INVALID_HANDLE; + + // When not checking for existence, the pointer to file handle must be valid + if(dwSearchScope != SFILE_OPEN_CHECK_EXISTS && PtrFile == NULL) nError = ERROR_INVALID_PARAMETER; // Prepare the file opening @@ -297,40 +257,28 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch { case SFILE_OPEN_FROM_MPQ: case SFILE_OPEN_BASE_FILE: + case SFILE_OPEN_CHECK_EXISTS: - if(!IsValidMpqHandle(hMpq)) - { - nError = ERROR_INVALID_HANDLE; - break; - } - - if(szFileName == NULL || *szFileName == 0) - { - nError = ERROR_INVALID_PARAMETER; - break; - } - // Check the pseudo-file name - if(IsPseudoFileName(szFileName, &dwFileIndex)) + if((bOpenByIndex = IsPseudoFileName(szFileName, &dwFileIndex)) == true) { - pFileEntry = GetFileEntryByIndex(ha, dwFileIndex); - bOpenByIndex = true; - if(pFileEntry == NULL) - nError = ERROR_FILE_NOT_FOUND; + // Get the file entry for the file + if(dwFileIndex > ha->dwFileTableSize) + break; + pFileEntry = ha->pFileTable + dwFileIndex; } else { - // If this MPQ is a patched archive, open the file as patched + // If this MPQ has no patches, open the file from this MPQ directly if(ha->haPatch == NULL || dwSearchScope == SFILE_OPEN_BASE_FILE) { - // Otherwise, open the file from *this* MPQ - pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); - if(pFileEntry == NULL) - nError = ERROR_FILE_NOT_FOUND; + pFileEntry = GetFileEntryLocale2(ha, szFileName, lcFileLocale, &dwHashIndex); } + + // If this MPQ is a patched archive, open the file as patched else { - return OpenPatchedFile(hMpq, szFileName, phFile); + return OpenPatchedFile(hMpq, szFileName, PtrFile); } } break; @@ -340,20 +288,13 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch // This open option is reserved for opening MPQ internal listfile. // No argument validation. Tries to open file with neutral locale first, // then any other available. - pFileEntry = GetFileEntryAny(ha, szFileName); - if(pFileEntry == NULL) - nError = ERROR_FILE_NOT_FOUND; + pFileEntry = GetFileEntryLocale2(ha, szFileName, 0, &dwHashIndex); break; case SFILE_OPEN_LOCAL_FILE: - if(szFileName == NULL || *szFileName == 0) - { - nError = ERROR_INVALID_PARAMETER; - break; - } - - return OpenLocalFile(szFileName, phFile); + // Open a local file + return OpenLocalFile(szFileName, PtrFile); default: @@ -361,73 +302,76 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch nError = ERROR_INVALID_PARAMETER; break; } - - // Quick return if something failed - if(nError != ERROR_SUCCESS) - { - SetLastError(nError); - *phFile = NULL; - return false; - } } - // Test if the file was not already deleted. + // Check whether the file really exists in the MPQ if(nError == ERROR_SUCCESS) { - if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) + if(pFileEntry == NULL || (pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) nError = ERROR_FILE_NOT_FOUND; - if(pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) + if(pFileEntry != NULL && pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) nError = ERROR_NOT_SUPPORTED; } - // Allocate file handle - if(nError == ERROR_SUCCESS) + // Did the caller just wanted to know if the file exists? + if(nError == ERROR_SUCCESS && dwSearchScope != SFILE_OPEN_CHECK_EXISTS) { + // Allocate file handle hf = CreateFileHandle(ha, pFileEntry); - if(hf == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // Initialize file handle - if(nError == ERROR_SUCCESS) - { - // If the MPQ has sector CRC enabled, enable if for the file - if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC) - hf->bCheckSectorCRCs = true; - - // If we know the real file name, copy it to the file entry - if(bOpenByIndex == false) + if(hf != NULL) { - // If there is no file name yet, allocate it - AllocateFileName(ha, pFileEntry, szFileName); - - // If the file is encrypted, we should detect the file key - if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) + // Get the hash index for the file + if(ha->pHashTable != NULL && dwHashIndex == HASH_ENTRY_FREE) + dwHashIndex = FindHashIndex(ha, dwFileIndex); + if(dwHashIndex != HASH_ENTRY_FREE) + hf->pHashEntry = ha->pHashTable + dwHashIndex; + hf->dwHashIndex = dwHashIndex; + + // If the MPQ has sector CRC enabled, enable if for the file + if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC) + hf->bCheckSectorCRCs = true; + + // If we know the real file name, copy it to the file entry + if(bOpenByIndex == false) { - hf->dwFileKey = DecryptFileKey(szFileName, - pFileEntry->ByteOffset, - pFileEntry->dwFileSize, - pFileEntry->dwFlags); + // If there is no file name yet, allocate it + AllocateFileName(ha, pFileEntry, szFileName); + + // If the file is encrypted, we should detect the file key + if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) + { + hf->dwFileKey = DecryptFileKey(szFileName, + pFileEntry->ByteOffset, + pFileEntry->dwFileSize, + pFileEntry->dwFlags); + } } } else { - // Try to auto-detect the file name - if(!SFileGetFileName(hf, NULL)) - nError = GetLastError(); + nError = ERROR_NOT_ENOUGH_MEMORY; } } - // Cleanup and exit + // Give the file entry + if(PtrFile != NULL) + PtrFile[0] = hf; + + // Return error code if(nError != ERROR_SUCCESS) - { SetLastError(nError); - FreeFileHandle(hf); - return false; - } + return (nError == ERROR_SUCCESS); +} - *phFile = hf; - return true; +//----------------------------------------------------------------------------- +// SFileHasFile +// +// hMpq - Handle of opened MPQ archive +// szFileName - Name of file to look for + +bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName) +{ + return SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_CHECK_EXISTS, NULL); } //----------------------------------------------------------------------------- |