summaryrefslogtreecommitdiff
path: root/StormLib/src/SFileFindFile.cpp
diff options
context:
space:
mode:
authorNicholas Estelami <NEstelami@users.noreply.github.com>2022-06-01 16:05:05 -0400
committerNicholas Estelami <NEstelami@users.noreply.github.com>2022-06-16 20:35:52 -0400
commitb3e299dbdee829c12ada556a1c3aa1f109a0e05b (patch)
treea6e647da673ba7df9389cb1f382a11fc8350bc23 /StormLib/src/SFileFindFile.cpp
parent941c19f2c33a0d1af75a10a64fe0905387d1e440 (diff)
Replaced StormLib .lib files with source code.
Some modifications to handle backslashes and forward slashes, along with some optimizations to speed up OTR generation.
Diffstat (limited to 'StormLib/src/SFileFindFile.cpp')
-rw-r--r--StormLib/src/SFileFindFile.cpp483
1 files changed, 483 insertions, 0 deletions
diff --git a/StormLib/src/SFileFindFile.cpp b/StormLib/src/SFileFindFile.cpp
new file mode 100644
index 000000000..856de443e
--- /dev/null
+++ b/StormLib/src/SFileFindFile.cpp
@@ -0,0 +1,483 @@
+/*****************************************************************************/
+/* SFileFindFile.cpp Copyright (c) Ladislav Zezula 2003 */
+/*---------------------------------------------------------------------------*/
+/* A module for file searching within MPQs */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 25.03.03 1.00 Lad The first version of SFileFindFile.cpp */
+/*****************************************************************************/
+
+#define __STORMLIB_SELF__
+#include "StormLib.h"
+#include "StormCommon.h"
+
+//-----------------------------------------------------------------------------
+// Private structure used for file search (search handle)
+
+// Used by searching in MPQ archives
+struct TMPQSearch
+{
+ TMPQArchive * ha; // Handle to MPQ, where the search runs
+ TFileEntry ** pSearchTable; // Table for files that have been already found
+ DWORD dwSearchTableItems; // Number of items in the search table
+ DWORD dwNextIndex; // Next file index to be checked
+ DWORD dwFlagMask; // For checking flag mask
+ char szSearchMask[1]; // Search mask (variable length)
+};
+
+//-----------------------------------------------------------------------------
+// Local functions
+
+static TMPQSearch * IsValidSearchHandle(HANDLE hFind)
+{
+ TMPQSearch * hs = (TMPQSearch *)hFind;
+
+ if(hs != NULL && IsValidMpqHandle(hs->ha))
+ return hs;
+
+ return NULL;
+}
+
+bool SFileCheckWildCard(const char * szString, const char * szWildCard)
+{
+ const char * szWildCardPtr;
+
+ for(;;)
+ {
+ // If there is '?' in the wildcard, we skip one char
+ while(szWildCard[0] == '?')
+ {
+ if(szString[0] == 0)
+ return false;
+
+ szWildCard++;
+ szString++;
+ }
+
+ // Handle '*'
+ szWildCardPtr = szWildCard;
+ if(szWildCardPtr[0] != 0)
+ {
+ if(szWildCardPtr[0] == '*')
+ {
+ while(szWildCardPtr[0] == '*')
+ szWildCardPtr++;
+
+ if(szWildCardPtr[0] == 0)
+ return true;
+
+ if(AsciiToUpperTable[szWildCardPtr[0]] == AsciiToUpperTable[szString[0]])
+ {
+ if(SFileCheckWildCard(szString, szWildCardPtr))
+ return true;
+ }
+ }
+ else
+ {
+ if(AsciiToUpperTable[szWildCardPtr[0]] != AsciiToUpperTable[szString[0]])
+ return false;
+
+ szWildCard = szWildCardPtr + 1;
+ }
+
+ if(szString[0] == 0)
+ return false;
+ szString++;
+ }
+ else
+ {
+ return (szString[0] == 0) ? true : false;
+ }
+ }
+}
+
+static DWORD GetSearchTableItems(TMPQArchive * ha)
+{
+ DWORD dwMergeItems = 0;
+
+ // Loop over all patches
+ while(ha != NULL)
+ {
+ // Append the number of files
+ dwMergeItems += (ha->pHetTable != NULL) ? ha->pHetTable->dwEntryCount
+ : ha->pHeader->dwBlockTableSize;
+ // Move to the patched archive
+ ha = ha->haPatch;
+ }
+
+ // Return the double size of number of items
+ return (dwMergeItems | 1);
+}
+
+static bool FileWasFoundBefore(
+ TMPQArchive * ha,
+ TMPQSearch * hs,
+ TFileEntry * pFileEntry)
+{
+ TFileEntry * pEntry;
+ char * szRealFileName = pFileEntry->szFileName;
+ DWORD dwStartIndex;
+ DWORD dwNameHash;
+ DWORD dwIndex;
+
+ if(hs->pSearchTable != NULL && szRealFileName != NULL)
+ {
+ // If we are in patch MPQ, we check if patch prefix matches
+ // and then trim the patch prefix
+ if(ha->pPatchPrefix != NULL)
+ {
+ // If the patch prefix doesn't fit, we pretend that the file
+ // was there before and it will be skipped
+ if(_strnicmp(szRealFileName, ha->pPatchPrefix->szPatchPrefix, ha->pPatchPrefix->nLength))
+ return true;
+
+ szRealFileName += ha->pPatchPrefix->nLength;
+ }
+
+ // Calculate the hash to the table
+ dwNameHash = ha->pfnHashString(szRealFileName, MPQ_HASH_NAME_A);
+ dwStartIndex = dwIndex = (dwNameHash % hs->dwSearchTableItems);
+
+ // The file might have been found before
+ // only if this is not the first MPQ being searched
+ if(ha->haBase != NULL)
+ {
+ // Enumerate all entries in the search table
+ for(;;)
+ {
+ // Get the file entry at that position
+ pEntry = hs->pSearchTable[dwIndex];
+ if(pEntry == NULL)
+ break;
+
+ if(pEntry->szFileName != NULL)
+ {
+ // Does the name match?
+ if(!_stricmp(pEntry->szFileName, szRealFileName))
+ return true;
+ }
+
+ // Move to the next entry
+ dwIndex = (dwIndex + 1) % hs->dwSearchTableItems;
+ if(dwIndex == dwStartIndex)
+ break;
+ }
+ }
+
+ // Put the entry to the table for later use
+ hs->pSearchTable[dwIndex] = pFileEntry;
+ }
+ return false;
+}
+
+static TFileEntry * FindPatchEntry(TMPQArchive * ha, TFileEntry * pFileEntry)
+{
+ TFileEntry * pPatchEntry = pFileEntry;
+ TFileEntry * pTempEntry;
+ char szFileName[MAX_PATH+1];
+
+ // Can't find patch entry for a file that doesn't have name
+ if(pFileEntry->szFileName != NULL && pFileEntry->szFileName[0] != 0)
+ {
+ // Go while there are patches
+ while(ha->haPatch != NULL)
+ {
+ // Move to the patch archive
+ ha = ha->haPatch;
+ szFileName[0] = 0;
+
+ // Prepare the prefix for the file name
+ if(ha->pPatchPrefix && ha->pPatchPrefix->nLength)
+ StringCopy(szFileName, _countof(szFileName), ha->pPatchPrefix->szPatchPrefix);
+ StringCat(szFileName, _countof(szFileName), pFileEntry->szFileName);
+
+ // Try to find the file there
+ pTempEntry = GetFileEntryExact(ha, szFileName, 0, NULL);
+ if(pTempEntry != NULL)
+ pPatchEntry = pTempEntry;
+ }
+ }
+
+ // Return the found patch entry
+ return pPatchEntry;
+}
+
+static bool DoMPQSearch_FileEntry(
+ TMPQSearch * hs,
+ SFILE_FIND_DATA * lpFindFileData,
+ TMPQArchive * ha,
+ TMPQHash * pHashEntry,
+ TFileEntry * pFileEntry)
+{
+ TFileEntry * pPatchEntry;
+ HANDLE hFile = NULL;
+ const char * szFileName;
+ size_t nPrefixLength = (ha->pPatchPrefix != NULL) ? ha->pPatchPrefix->nLength : 0;
+ DWORD dwBlockIndex;
+ char szNameBuff[MAX_PATH];
+
+ // Is it a file but not a patch file?
+ if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS)
+ {
+ // Ignore fake files which are not compressed but have size higher than the archive
+ if ((pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) == 0 && (pFileEntry->dwFileSize > ha->FileSize))
+ return false;
+
+ // Now we have to check if this file was not enumerated before
+ if(!FileWasFoundBefore(ha, hs, pFileEntry))
+ {
+// if(pFileEntry != NULL && !_stricmp(pFileEntry->szFileName, "TriggerLibs\\NativeLib.galaxy"))
+// DebugBreak();
+
+ // Find a patch to this file
+ // Note: This either succeeds or returns pFileEntry
+ pPatchEntry = FindPatchEntry(ha, pFileEntry);
+
+ // Prepare the block index
+ dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable);
+ if(dwBlockIndex == 569)
+ szNameBuff[0] = 'F';
+
+ // Get the file name. If it's not known, we will create pseudo-name
+ szFileName = pFileEntry->szFileName;
+ if(szFileName == NULL)
+ {
+ // Open the file by its pseudo-name.
+ StringCreatePseudoFileName(szNameBuff, _countof(szNameBuff), dwBlockIndex, "xxx");
+ if(SFileOpenFileEx((HANDLE)hs->ha, szNameBuff, SFILE_OPEN_BASE_FILE, &hFile))
+ {
+ SFileGetFileName(hFile, szNameBuff);
+ szFileName = szNameBuff;
+ SFileCloseFile(hFile);
+ }
+ }
+
+ // If the file name is still NULL, we cannot include the file to search results
+ if(szFileName != NULL)
+ {
+ // Check the file name against the wildcard
+ if(SFileCheckWildCard(szFileName + nPrefixLength, hs->szSearchMask))
+ {
+ // Fill the found entry. hash entry and block index are taken from the base MPQ
+ lpFindFileData->dwHashIndex = HASH_ENTRY_FREE;
+ lpFindFileData->dwBlockIndex = dwBlockIndex;
+ lpFindFileData->dwFileSize = pPatchEntry->dwFileSize;
+ lpFindFileData->dwFileFlags = pPatchEntry->dwFlags;
+ lpFindFileData->dwCompSize = pPatchEntry->dwCmpSize;
+ lpFindFileData->lcLocale = 0; // pPatchEntry->lcLocale;
+
+ // Fill the filetime
+ lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32);
+ lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime);
+
+ // Fill-in the entries from hash table entry, if given
+ if(pHashEntry != NULL)
+ {
+ lpFindFileData->dwHashIndex = (DWORD)(pHashEntry - ha->pHashTable);
+ lpFindFileData->lcLocale = pHashEntry->lcLocale;
+ }
+
+ // Fill the file name and plain file name
+ StringCopy(lpFindFileData->cFileName, _countof(lpFindFileData->cFileName), szFileName + nPrefixLength);
+ lpFindFileData->szPlainName = (char *)GetPlainFileName(lpFindFileData->cFileName);
+ return true;
+ }
+ }
+ }
+ }
+
+ // Either not a valid item or was found before
+ return false;
+}
+
+static DWORD DoMPQSearch_HashTable(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData, TMPQArchive * ha)
+{
+ TMPQHash * pHashTableEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
+ TMPQHash * pHash;
+
+ // Parse the file table
+ for(pHash = ha->pHashTable + hs->dwNextIndex; pHash < pHashTableEnd; pHash++)
+ {
+ // Increment the next index for subsequent search
+ hs->dwNextIndex++;
+
+ // Does this hash table entry point to a proper block table entry?
+ if(IsValidHashEntry(ha, pHash))
+ {
+ // Check if this file entry should be included in the search result
+ if(DoMPQSearch_FileEntry(hs, lpFindFileData, ha, pHash, ha->pFileTable + MPQ_BLOCK_INDEX(pHash)))
+ return ERROR_SUCCESS;
+ }
+ }
+
+ // No more files
+ return ERROR_NO_MORE_FILES;
+}
+
+static DWORD DoMPQSearch_FileTable(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData, TMPQArchive * ha)
+{
+ TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
+ TFileEntry * pFileEntry;
+
+ // Parse the file table
+ for(pFileEntry = ha->pFileTable + hs->dwNextIndex; pFileEntry < pFileTableEnd; pFileEntry++)
+ {
+ // Increment the next index for subsequent search
+ hs->dwNextIndex++;
+
+ // Check if this file entry should be included in the search result
+ if(DoMPQSearch_FileEntry(hs, lpFindFileData, ha, NULL, pFileEntry))
+ return ERROR_SUCCESS;
+ }
+
+ // No more files
+ return ERROR_NO_MORE_FILES;
+}
+
+// Performs one MPQ search
+static DWORD DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
+{
+ TMPQArchive * ha = hs->ha;
+ DWORD dwErrCode;
+
+ // Start searching with base MPQ
+ while(ha != NULL)
+ {
+ // If the archive has hash table, we need to use hash table
+ // in order to catch hash table index and file locale.
+ // Note: If multiple hash table entries, point to the same block entry,
+ // we need, to report them all
+ dwErrCode = (ha->pHashTable != NULL) ? DoMPQSearch_HashTable(hs, lpFindFileData, ha)
+ : DoMPQSearch_FileTable(hs, lpFindFileData, ha);
+ if(dwErrCode == ERROR_SUCCESS)
+ return dwErrCode;
+
+ // If there is no more patches in the chain, stop it.
+ // This also keeps hs->ha non-NULL, which is required
+ // for freeing the handle later
+ if(ha->haPatch == NULL)
+ break;
+
+ // Move to the next patch in the patch chain
+ hs->ha = ha = ha->haPatch;
+ hs->dwNextIndex = 0;
+ }
+
+ // No more files found, return error
+ return ERROR_NO_MORE_FILES;
+}
+
+static void FreeMPQSearch(TMPQSearch *& hs)
+{
+ if(hs != NULL)
+ {
+ if(hs->pSearchTable != NULL)
+ STORM_FREE(hs->pSearchTable);
+ STORM_FREE(hs);
+ hs = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const TCHAR * szListFile)
+{
+ TMPQArchive * ha = (TMPQArchive *)hMpq;
+ TMPQSearch * hs = NULL;
+ size_t nSize = 0;
+ DWORD dwErrCode = ERROR_SUCCESS;
+
+ // Check for the valid parameters
+ if(!IsValidMpqHandle(hMpq))
+ dwErrCode = ERROR_INVALID_HANDLE;
+ if(szMask == NULL || lpFindFileData == NULL)
+ dwErrCode = ERROR_INVALID_PARAMETER;
+
+ // Include the listfile into the MPQ's internal listfile
+ // Note that if the listfile name is NULL, do nothing because the
+ // internal listfile is always included.
+ if(dwErrCode == ERROR_SUCCESS && szListFile != NULL && *szListFile != 0)
+ dwErrCode = SFileAddListFile((HANDLE)ha, szListFile);
+
+ // Allocate the structure for MPQ search
+ if(dwErrCode == ERROR_SUCCESS)
+ {
+ nSize = sizeof(TMPQSearch) + strlen(szMask) + 1;
+ if((hs = (TMPQSearch *)STORM_ALLOC(char, nSize)) == NULL)
+ dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ // Perform the first search
+ if(dwErrCode == ERROR_SUCCESS)
+ {
+ memset(hs, 0, sizeof(TMPQSearch));
+ strcpy(hs->szSearchMask, szMask);
+ hs->dwFlagMask = MPQ_FILE_EXISTS;
+ hs->ha = ha;
+
+ // If the archive is patched archive, we have to create a merge table
+ // to prevent files being repeated
+ if(ha->haPatch != NULL)
+ {
+ hs->dwSearchTableItems = GetSearchTableItems(ha);
+ hs->pSearchTable = STORM_ALLOC(TFileEntry *, hs->dwSearchTableItems);
+ hs->dwFlagMask = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE;
+ if(hs->pSearchTable != NULL)
+ memset(hs->pSearchTable, 0, hs->dwSearchTableItems * sizeof(TFileEntry *));
+ else
+ dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ // Perform first item searching
+ if(dwErrCode == ERROR_SUCCESS)
+ {
+ dwErrCode = DoMPQSearch(hs, lpFindFileData);
+ }
+
+ // Cleanup
+ if(dwErrCode != ERROR_SUCCESS)
+ {
+ FreeMPQSearch(hs);
+ SetLastError(dwErrCode);
+ }
+
+ // Return the result value
+ return (HANDLE)hs;
+}
+
+bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
+{
+ TMPQSearch * hs = IsValidSearchHandle(hFind);
+ DWORD dwErrCode = ERROR_SUCCESS;
+
+ // Check the parameters
+ if(hs == NULL)
+ dwErrCode = ERROR_INVALID_HANDLE;
+ if(lpFindFileData == NULL)
+ dwErrCode = ERROR_INVALID_PARAMETER;
+
+ if(dwErrCode == ERROR_SUCCESS)
+ dwErrCode = DoMPQSearch(hs, lpFindFileData);
+
+ if(dwErrCode != ERROR_SUCCESS)
+ SetLastError(dwErrCode);
+ return (dwErrCode == ERROR_SUCCESS);
+}
+
+bool WINAPI SFileFindClose(HANDLE hFind)
+{
+ TMPQSearch * hs = IsValidSearchHandle(hFind);
+
+ // Check the parameters
+ if(hs == NULL)
+ {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ FreeMPQSearch(hs);
+ return true;
+}