diff options
Diffstat (limited to 'dep/CascLib/src/CascOpenFile.cpp')
-rw-r--r-- | dep/CascLib/src/CascOpenFile.cpp | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/dep/CascLib/src/CascOpenFile.cpp b/dep/CascLib/src/CascOpenFile.cpp new file mode 100644 index 00000000000..e7cdb9aa107 --- /dev/null +++ b/dep/CascLib/src/CascOpenFile.cpp @@ -0,0 +1,440 @@ +/*****************************************************************************/ +/* CascOpenFile.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* System-dependent directory functions for CascLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 01.05.14 1.00 Lad The first version of CascOpenFile.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" +#include "CascMndxRoot.h" + +//----------------------------------------------------------------------------- +// Local structures + +//----------------------------------------------------------------------------- +// Local functions + +TCascFile * IsValidFileHandle(HANDLE hFile) +{ + TCascFile * hf = (TCascFile *)hFile; + + return (hf != NULL && hf->hs != NULL && hf->szClassName != NULL && !strcmp(hf->szClassName, "TCascFile")) ? hf : NULL; +} + +PCASC_INDEX_ENTRY FindIndexEntry(TCascStorage * hs, PQUERY_KEY pIndexKey) +{ + PCASC_INDEX_ENTRY pIndexEntry = NULL; + + if(hs->pIndexEntryMap != NULL) + pIndexEntry = (PCASC_INDEX_ENTRY)Map_FindObject(hs->pIndexEntryMap, pIndexKey->pbData); + + return pIndexEntry; +} + +PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, size_t * PtrIndex) +{ + PCASC_ENCODING_ENTRY pEncodingEntry; + size_t StartEntry = 0; + size_t MidlEntry; + size_t EndEntry = hs->nEncodingEntries; + int nResult; + + // Perform binary search + while(StartEntry < EndEntry) + { + // Calculate the middle of the interval + MidlEntry = StartEntry + ((EndEntry - StartEntry) / 2); + pEncodingEntry = hs->ppEncodingEntries[MidlEntry]; + + // Did we find it? + nResult = memcmp(pEncodingKey->pbData, pEncodingEntry->EncodingKey, MD5_HASH_SIZE); + if(nResult == 0) + { + if(PtrIndex != NULL) + PtrIndex[0] = MidlEntry; + return pEncodingEntry; + } + + // Move the interval to the left or right + (nResult < 0) ? EndEntry = MidlEntry : StartEntry = MidlEntry + 1; + } + + // Not found, sorry + return NULL; +} + +// Also used in CascSearchFile +PCASC_ROOT_ENTRY FindFirstRootEntry(TCascStorage * hs, const char * szFileName, size_t * PtrIndex) +{ + PCASC_ROOT_ENTRY pFoundEntry = NULL; + PCASC_ROOT_ENTRY pRootEntry; + ULONGLONG FileNameHash; + uint32_t dwHashHigh = 0; + uint32_t dwHashLow = 0; + size_t StartEntry = 0; + size_t MidlEntry = 0; + size_t EndEntry = hs->nRootEntries; + + // Calculate the HASH value of the normalized file name + hashlittle2(szFileName, strlen(szFileName), &dwHashHigh, &dwHashLow); + FileNameHash = ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow; + + // Perform binary search + while(StartEntry < EndEntry) + { + // Calculate the middle of the interval + MidlEntry = StartEntry + ((EndEntry - StartEntry) / 2); + pRootEntry = hs->ppRootEntries[MidlEntry]; + + // Did we find it? + if(pRootEntry->FileNameHash == FileNameHash) + { + pFoundEntry = pRootEntry; + break; + } + + // Move the interval to the left or right + (FileNameHash < pRootEntry->FileNameHash) ? EndEntry = MidlEntry : StartEntry = MidlEntry + 1; + } + + // Move the pointer back to the first entry with that hash + if(pFoundEntry != NULL) + { + while(MidlEntry > 0 && hs->ppRootEntries[MidlEntry - 1]->FileNameHash == FileNameHash) + { + pFoundEntry = hs->ppRootEntries[MidlEntry - 1]; + MidlEntry--; + } + } + + // Return what we found + if(PtrIndex != NULL) + *PtrIndex = MidlEntry; + return pFoundEntry; +} + +// Check the root directory for that hash +PCASC_ROOT_ENTRY FindRootEntryLocale(TCascStorage * hs, char * szFileName, DWORD Locale) +{ + PCASC_ROOT_ENTRY pThatEntry = NULL; + PCASC_ROOT_ENTRY pENUSEntry = NULL; + PCASC_ROOT_ENTRY pENGBEntry = NULL; + PCASC_ROOT_ENTRY pAnyEntry = NULL; + PCASC_ROOT_ENTRY pEndEntry = NULL; + PCASC_ROOT_ENTRY pRootEntry = NULL; + ULONGLONG FileNameHash; + size_t EntryIndex = 0; + size_t EndEntry = hs->nRootEntries; + + // Find a root entry with the given name hash + pRootEntry = FindFirstRootEntry(hs, szFileName, &EntryIndex); + if(pRootEntry != NULL) + { + // Rememeber the file name hash + pEndEntry = hs->pRootEntries + hs->nRootEntries; + FileNameHash = pRootEntry->FileNameHash; + + // Find all suitable root entries + while(EntryIndex < EndEntry) + { + // Get the root entry + pRootEntry = hs->ppRootEntries[EntryIndex++]; + if(pRootEntry->FileNameHash != FileNameHash) + break; + + // If a locale has been given, check it + if(pThatEntry == NULL && Locale != 0 && (Locale & pRootEntry->Locales)) + pThatEntry = pRootEntry; + if(pENUSEntry == NULL && (pRootEntry->Locales & CASC_LOCALE_ENUS)) + pENUSEntry = pRootEntry; + if(pENGBEntry == NULL && (pRootEntry->Locales & CASC_LOCALE_ENGB)) + pENGBEntry = pRootEntry; + if(pAnyEntry == NULL) + pAnyEntry = pRootEntry; + + // Move to the next one + pRootEntry++; + } + + // Return the key by priority + if(pThatEntry != NULL) + return pThatEntry; + if(pENGBEntry != NULL) + return pENGBEntry; + if(pENUSEntry != NULL) + return pENUSEntry; + } + + // Return whatever we got + return pAnyEntry; +} + +static TCascFile * CreateFileHandle(TCascStorage * hs, PCASC_INDEX_ENTRY pIndexEntry) +{ + ULONGLONG FileOffsMask = ((ULONGLONG)1 << hs->KeyMapping[0].SegmentBits) - 1; + ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffset); + TCascFile * hf; + + // Allocate the CASC file structure + hf = (TCascFile *)CASC_ALLOC(TCascFile, 1); + if(hf != NULL) + { + // Initialize the structure + memset(hf, 0, sizeof(TCascFile)); + hf->ArchiveIndex = (DWORD)(FileOffset >> hs->KeyMapping[0].SegmentBits); + hf->HeaderOffset = (DWORD)(FileOffset & FileOffsMask); + hf->szClassName = "TCascFile"; + + // Copy the compressed file size + hf->CompressedSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSize) - 0x1E; + + // For now, we set the file size to be equal to compressed size + // This is used when loading the "encoding" file, which does not + // have entry in the encoding itself + hf->FileSize = hf->CompressedSize; + + // Increment the number of references to the archive + hs->dwRefCount++; + hf->hs = hs; + } + + return hf; +} + +static bool OpenFileByIndexKey(TCascStorage * hs, PQUERY_KEY pIndexKey, DWORD dwFlags, HANDLE * phFile) +{ + PCASC_INDEX_ENTRY pIndexEntry; + TCascFile * hf = NULL; + int nError = ERROR_SUCCESS; + + CASCLIB_UNUSED(dwFlags); + + // Find the key entry in the array of file keys + pIndexEntry = FindIndexEntry(hs, pIndexKey); + if(pIndexEntry == NULL) + nError = ERROR_FILE_NOT_FOUND; + + // Create the file handle structure + if(nError == ERROR_SUCCESS) + { + hf = CreateFileHandle(hs, pIndexEntry); + *phFile = (HANDLE)hf; + if(hf == NULL) + nError = ERROR_FILE_NOT_FOUND; + } + + if(nError != ERROR_SUCCESS) + SetLastError(nError); + return (nError == ERROR_SUCCESS); +} + +static bool OpenFileByEncodingKey(TCascStorage * hs, PQUERY_KEY pEncodingKey, DWORD dwFlags, HANDLE * phFile) +{ + PCASC_ENCODING_ENTRY pEncodingEntry; + QUERY_KEY IndexKey; + TCascFile * hf = NULL; + int nError = ERROR_SUCCESS; + + // Find the encoding entry + pEncodingEntry = FindEncodingEntry(hs, pEncodingKey, NULL); + if(pEncodingEntry == NULL) + nError = ERROR_FILE_NOT_FOUND; + + // Prepare the file index and open the file by index + // Note: We don't know what to do if there is more than just one index key + // We always take the first file present. Is that correct? +// IndexKey.pbData = pEncodingEntry->EncodingKey + (MD5_HASH_SIZE * pEncodingEntry->KeyCount); +// assert(pEncodingEntry->KeyCount == 1); + IndexKey.pbData = pEncodingEntry->EncodingKey + MD5_HASH_SIZE; + IndexKey.cbData = MD5_HASH_SIZE; + if(OpenFileByIndexKey(hs, &IndexKey, dwFlags, phFile)) + { + // Fix the file size from the encoding key + hf = IsValidFileHandle(*phFile); + if(hf != NULL) + { + hf->FileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBytes); + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Public functions + +bool WINAPI CascOpenFileByIndexKey(HANDLE hStorage, PQUERY_KEY pIndexKey, DWORD dwFlags, HANDLE * phFile) +{ + TCascStorage * hs; + + // Validate the storage handle + hs = IsValidStorageHandle(hStorage); + if(hs == NULL) + { + SetLastError(ERROR_INVALID_HANDLE); + return false; + } + + // Validate the other parameters + if(pIndexKey == NULL || pIndexKey->pbData == NULL || pIndexKey->cbData == 0 || phFile == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // Use the internal function to open the file + return OpenFileByIndexKey(hs, pIndexKey, dwFlags, phFile); +} + +bool WINAPI CascOpenFileByEncodingKey(HANDLE hStorage, PQUERY_KEY pEncodingKey, DWORD dwFlags, HANDLE * phFile) +{ + TCascStorage * hs; + + // Validate the storage handle + hs = IsValidStorageHandle(hStorage); + if(hs == NULL) + { + SetLastError(ERROR_INVALID_HANDLE); + return false; + } + + // Validate the other parameters + if(pEncodingKey == NULL || pEncodingKey->pbData == NULL || pEncodingKey->cbData == 0 || phFile == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // Use the internal function fo open the file + return OpenFileByEncodingKey(hs, pEncodingKey, dwFlags, phFile); +} + +bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocale, DWORD dwFlags, HANDLE * phFile) +{ + CASC_ROOT_KEY_INFO EncodingKeyInfo; + PCASC_ROOT_ENTRY pRootEntry; + PCASC_PACKAGE pPackage; + TCascStorage * hs; + QUERY_KEY EncodingKey; + char * szStrippedName; + char * szFileName2; + int nError = ERROR_SUCCESS; + + // Validate the storage handle + hs = IsValidStorageHandle(hStorage); + if(hs == NULL) + { + SetLastError(ERROR_INVALID_HANDLE); + return false; + } + + // Validate the other parameters + if(szFileName == NULL || szFileName[0] == 0 || phFile == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // Create the copy of the file name + szFileName2 = NewStr(szFileName, 0); + if(szFileName2 != NULL) + { + // If the storage has a MNDX root directory, use it to search the entry + if(hs->pMndxInfo != NULL) + { + // Convert the file name to lowercase + slashes + NormalizeFileName_LowerSlash(szFileName2); + + // Find the package number + pPackage = FindMndxPackage(hs, szFileName2); + if(pPackage != NULL) + { + // Cut the package name off the full path + szStrippedName = szFileName2 + pPackage->nLength; + while(szStrippedName[0] == '/') + szStrippedName++; + + nError = SearchMndxInfo(hs->pMndxInfo, szStrippedName, (DWORD)(pPackage - hs->pPackages->Packages), &EncodingKeyInfo); + if(nError == ERROR_SUCCESS) + { + // Prepare the encoding key + EncodingKey.pbData = EncodingKeyInfo.EncodingKey; + EncodingKey.cbData = MD5_HASH_SIZE; + } + } + else + { + nError = ERROR_FILE_NOT_FOUND; + } + } + else + { + // Convert the file name to lowercase + slashes + NormalizeFileName_UpperBkSlash(szFileName2); + + // Check the root directory for that hash + pRootEntry = FindRootEntryLocale(hs, szFileName2, dwLocale); + nError = (pRootEntry != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND; + if(pRootEntry != NULL) + { + // Prepare the root key + EncodingKey.pbData = pRootEntry->EncodingKey; + EncodingKey.cbData = MD5_HASH_SIZE; + } + } + + // Use the root key to find the file in the encoding table entry + if(nError == ERROR_SUCCESS) + { + if(!OpenFileByEncodingKey(hs, &EncodingKey, dwFlags, phFile)) + { + assert(GetLastError() != ERROR_SUCCESS); + nError = GetLastError(); + } + } + + // Delete the file name copy + delete [] szFileName2; + } + else + nError = ERROR_NOT_ENOUGH_MEMORY; + + if(nError != ERROR_SUCCESS) + SetLastError(nError); + return (nError == ERROR_SUCCESS); +} + +bool WINAPI CascCloseFile(HANDLE hFile) +{ + TCascFile * hf; + + hf = IsValidFileHandle(hFile); + if(hf != NULL) + { + // Close (dereference) the archive handle + if(hf->hs != NULL) + CascCloseStorage((HANDLE)hf->hs); + hf->hs = NULL; + + // Free the file cache and frame array + if(hf->pbFileCache != NULL) + CASC_FREE(hf->pbFileCache); + if(hf->pFrames != NULL) + CASC_FREE(hf->pFrames); + + // Free the structure itself + hf->szClassName = NULL; + CASC_FREE(hf); + return true; + } + + SetLastError(ERROR_INVALID_HANDLE); + return false; +} + |