aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src/CascFindFile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dep/CascLib/src/CascFindFile.cpp')
-rw-r--r--dep/CascLib/src/CascFindFile.cpp364
1 files changed, 364 insertions, 0 deletions
diff --git a/dep/CascLib/src/CascFindFile.cpp b/dep/CascLib/src/CascFindFile.cpp
new file mode 100644
index 00000000000..f1b14560bed
--- /dev/null
+++ b/dep/CascLib/src/CascFindFile.cpp
@@ -0,0 +1,364 @@
+/*****************************************************************************/
+/* CascFindFile.cpp Copyright (c) Ladislav Zezula 2014 */
+/*---------------------------------------------------------------------------*/
+/* System-dependent directory functions for CascLib */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 10.05.14 1.00 Lad The first version of CascFindFile.cpp */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "CascLib.h"
+#include "CascCommon.h"
+#include "CascMndxRoot.h"
+
+//-----------------------------------------------------------------------------
+// Local functions
+
+static TCascSearch * IsValidSearchHandle(HANDLE hFind)
+{
+ TCascSearch * pSearch = (TCascSearch *)hFind;
+
+ return (pSearch != NULL && pSearch->szClassName != NULL && !strcmp(pSearch->szClassName, "TCascSearch") && pSearch->szMask != NULL) ? pSearch : NULL;
+}
+
+static void FreeSearchHandle(TCascSearch * pSearch)
+{
+ // Only if the storage handle is valid
+ assert(pSearch != NULL);
+
+ // Close (dereference) the archive handle
+ if(pSearch->hs != NULL)
+ CascCloseStorage((HANDLE)pSearch->hs);
+ pSearch->hs = NULL;
+
+ // Free the file cache and frame array
+ if(pSearch->szMask != NULL)
+ CASC_FREE(pSearch->szMask);
+ if(pSearch->szListFile != NULL)
+ CASC_FREE(pSearch->szListFile);
+ if(pSearch->pStruct1C != NULL)
+ delete pSearch->pStruct1C;
+ if(pSearch->pCache != NULL)
+ ListFile_Free(pSearch->pCache);
+
+ // Free the structure itself
+ pSearch->szClassName = NULL;
+ CASC_FREE(pSearch);
+}
+/*
+DWORD dwRootEntries = 0;
+DWORD dwEncoEntries = 0;
+DWORD dwIndexEntries = 0;
+*/
+static bool VerifyRootEntry(TCascSearch * pSearch, PCASC_ROOT_ENTRY pRootEntry, PCASC_FIND_DATA pFindData, size_t nRootIndex)
+{
+ PCASC_ENCODING_ENTRY pEncodingEntry;
+ PCASC_INDEX_ENTRY pIndexEntry;
+ TCascStorage * hs = pSearch->hs;
+ QUERY_KEY QueryKey;
+ DWORD dwByteIndex = (DWORD)(nRootIndex / 0x08);
+ DWORD dwBitIndex = (DWORD)(nRootIndex & 0x07);
+
+ // First of all, check if that entry has been reported before
+ // If the bit is set, then the file has already been reported
+ // by a previous search iteration
+ if(pSearch->BitArray[dwByteIndex] & (1 << dwBitIndex))
+ return false;
+ pSearch->BitArray[dwByteIndex] |= (1 << dwBitIndex);
+
+ // Increment the number of root entries
+// dwRootEntries++;
+
+ // Now try to find that encoding key in the array of encoding keys
+ QueryKey.pbData = pRootEntry->EncodingKey;
+ QueryKey.cbData = MD5_HASH_SIZE;
+ pEncodingEntry = FindEncodingEntry(hs, &QueryKey, NULL);
+ if(pEncodingEntry == NULL)
+ return false;
+
+// dwEncoEntries++;
+
+ // Now try to find the index entry. Note that we take the first key
+ QueryKey.pbData = pEncodingEntry->EncodingKey + MD5_HASH_SIZE;
+ QueryKey.cbData = MD5_HASH_SIZE;
+ pIndexEntry = FindIndexEntry(hs, &QueryKey);
+ if(pIndexEntry == NULL)
+ return false;
+
+// dwIndexEntries++;
+
+ // Fill the name hash and the MD5
+ memcpy(pFindData->EncodingKey, pRootEntry->EncodingKey, MD5_HASH_SIZE);
+ pFindData->FileNameHash = pRootEntry->FileNameHash;
+ pFindData->dwPackageIndex = 0;
+ pFindData->dwLocaleFlags = pRootEntry->Locales;
+ pFindData->dwFileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBytes);
+ return true;
+}
+
+static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szListFile, const char * szMask)
+{
+ TCascSearch * pSearch;
+ size_t cbToAllocate;
+
+ // When using the MNDX info, do not allocate the extra bit array
+ cbToAllocate = sizeof(TCascSearch) + ((hs->pMndxInfo == NULL) ? (hs->nRootEntries / 8) : 0);
+ pSearch = (TCascSearch *)CASC_ALLOC(BYTE, cbToAllocate);
+ if(pSearch != NULL)
+ {
+ // Initialize the structure
+ memset(pSearch, 0, cbToAllocate);
+ pSearch->szClassName = "TCascSearch";
+
+ // Save the search handle
+ pSearch->hs = hs;
+ hs->dwRefCount++;
+
+ // If the mask was not given, use default
+ if(szMask == NULL)
+ szMask = "*";
+
+ // Save the other variables
+ if(szListFile != NULL)
+ {
+ pSearch->szListFile = NewStr(szListFile, 0);
+ if(pSearch->szListFile == NULL)
+ {
+ FreeSearchHandle(pSearch);
+ return NULL;
+ }
+ }
+
+ // Allocate the search mask
+ pSearch->szMask = NewStr(szMask, 0);
+ if(pSearch->szMask == NULL)
+ {
+ FreeSearchHandle(pSearch);
+ return NULL;
+ }
+ }
+
+ return pSearch;
+}
+
+static bool DoStorageSearch_ListFile(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
+{
+ PCASC_ROOT_ENTRY pRootEntry;
+ TCascStorage * hs = pSearch->hs;
+ size_t RootIndex;
+
+ for(;;)
+ {
+ // Shall we get a new file name from the listfile?
+ if(pSearch->FileNameHash == 0)
+ {
+ // Try to get next file from the listfile
+ if(!ListFile_GetNext(pSearch->pCache, pSearch->szMask, pSearch->szFileName, MAX_PATH))
+ break;
+
+// if(!_stricmp(pSearch->szFileName, "Interface\\Glues\\MODELS\\UI_MainMenu_Warlords\\Caustic_MedFreq.blp"))
+// DebugBreak();
+
+ // Normalize the file name
+ strcpy(pSearch->szNormName, pSearch->szFileName);
+ NormalizeFileName_UpperBkSlash(pSearch->szNormName);
+
+ // Find the first root entry belonging to this file name
+ pRootEntry = FindFirstRootEntry(pSearch->hs, pSearch->szNormName, &RootIndex);
+ if(pRootEntry == NULL)
+ continue;
+
+ // We have the name now, search all locales
+ pSearch->FileNameHash = pRootEntry->FileNameHash;
+ pSearch->RootIndex = RootIndex;
+ }
+
+ // Is the root index in range?
+ while(pSearch->RootIndex < hs->nRootEntries)
+ {
+ // Get the next root entry. If name mismatches, stop searching
+ pRootEntry = hs->ppRootEntries[pSearch->RootIndex];
+ if(pRootEntry->FileNameHash != pSearch->FileNameHash)
+ break;
+
+ // Verify whether the file exists in the storage
+ if(VerifyRootEntry(pSearch, pRootEntry, pFindData, pSearch->RootIndex++))
+ {
+ strcpy(pFindData->szFileName, pSearch->szFileName);
+ pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName);
+ return true;
+ }
+ }
+
+ // Reset the name hash and root index and retry search
+ pSearch->FileNameHash = 0;
+ }
+
+ // Listfile search ended
+ return false;
+}
+
+static bool DoStorageSearch_Hash(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
+{
+ PCASC_ROOT_ENTRY pRootEntry;
+ TCascStorage * hs = pSearch->hs;
+
+ // Check if there is more files with the same name hash
+ while(pSearch->RootIndex < hs->nRootEntries)
+ {
+ // Get the pointer to the root entry
+ pRootEntry = hs->ppRootEntries[pSearch->RootIndex];
+
+ // Verify if that root entry exists in the CASC storage
+ // Note that the file name will be there from the previous search
+ if(VerifyRootEntry(pSearch, pRootEntry, pFindData, pSearch->RootIndex))
+ {
+ pFindData->szFileName[0] = 0;
+ pFindData->szPlainName = NULL;
+ return true;
+ }
+
+ // Move to the next entry
+ pSearch->RootIndex++;
+ }
+
+ // Nameless search ended
+ return false;
+}
+
+static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
+{
+ // Are we searching using the MNDX ?
+ if(pSearch->hs->pMndxInfo != NULL)
+ return DoStorageSearch_MNDX(pSearch, pFindData);
+
+ // State 0: No search done yet
+ if(pSearch->dwState == 0)
+ {
+ // Does the search specify listfile?
+ if(pSearch->szListFile != NULL)
+ pSearch->pCache = ListFile_OpenExternal(pSearch->szListFile);
+
+ // Move the search phase to the listfile searching
+ pSearch->FileNameHash = 0;
+ pSearch->RootIndex = 0;
+ pSearch->dwState++;
+
+ // If either file stream or listfile cache are invalid,
+ // move to the next phase
+ if(pSearch->pCache == NULL)
+ pSearch->dwState++;
+ }
+
+ // State 1: Searching the list file
+ if(pSearch->dwState == 1)
+ {
+ if(DoStorageSearch_ListFile(pSearch, pFindData))
+ return true;
+
+ // Move to the nameless search state
+ pSearch->RootIndex = 0;
+ pSearch->dwState++;
+ }
+
+ // State 2: Searching the remaining entries
+ if(pSearch->dwState == 2)
+ {
+ if(DoStorageSearch_Hash(pSearch, pFindData))
+ return true;
+
+ // Move to the final search state
+ pSearch->dwState++;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+HANDLE WINAPI CascFindFirstFile(
+ HANDLE hStorage,
+ const char * szMask,
+ PCASC_FIND_DATA pFindData,
+ const TCHAR * szListFile)
+{
+ TCascStorage * hs;
+ TCascSearch * pSearch = NULL;
+ int nError = ERROR_SUCCESS;
+
+ // Check parameters
+ if((hs = IsValidStorageHandle(hStorage)) == NULL)
+ nError = ERROR_INVALID_HANDLE;
+ if(szMask == NULL || pFindData == NULL)
+ nError = ERROR_INVALID_PARAMETER;
+
+ // Allocate the structure for archive search
+ if(nError == ERROR_SUCCESS)
+ {
+ // Clear the entire search structure
+ memset(pFindData, 0, sizeof(CASC_FIND_DATA));
+
+ // We must have listfile for non-MNDX storages
+ if(hs->pMndxInfo == NULL && szListFile == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return NULL;
+ }
+
+ // Allocate the search handle
+ pSearch = AllocateSearchHandle(hs, szListFile, szMask);
+ if(pSearch == NULL)
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ // Perform search
+ if(nError == ERROR_SUCCESS)
+ {
+ if(!DoStorageSearch(pSearch, pFindData))
+ nError = ERROR_NO_MORE_FILES;
+ }
+
+ if(nError != ERROR_SUCCESS)
+ {
+ if(pSearch != NULL)
+ FreeSearchHandle(pSearch);
+ pSearch = NULL;
+ }
+
+ return (HANDLE)pSearch;
+}
+
+bool WINAPI CascFindNextFile(
+ HANDLE hFind,
+ PCASC_FIND_DATA pFindData)
+{
+ TCascSearch * pSearch;
+
+ pSearch = IsValidSearchHandle(hFind);
+ if(pSearch == NULL || pFindData == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ // Perform search
+ return DoStorageSearch(pSearch, pFindData);
+}
+
+bool WINAPI CascFindClose(HANDLE hFind)
+{
+ TCascSearch * pSearch;
+
+ pSearch = IsValidSearchHandle(hFind);
+ if(pSearch == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ FreeSearchHandle(pSearch);
+ return true;
+}