mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
396 lines
12 KiB
C++
396 lines
12 KiB
C++
/*****************************************************************************/
|
|
/* 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 FindRootEntry(TCascStorage * hs, const char * szFileName, DWORD * PtrTableIndex)
|
|
{
|
|
PCASC_ROOT_ENTRY pRootEntry;
|
|
ULONGLONG FileNameHash;
|
|
DWORD TableIndex;
|
|
uint32_t dwHashHigh = 0;
|
|
uint32_t dwHashLow = 0;
|
|
|
|
// Calculate the HASH value of the normalized file name
|
|
hashlittle2(szFileName, strlen(szFileName), &dwHashHigh, &dwHashLow);
|
|
FileNameHash = ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow;
|
|
|
|
// Get the first table index
|
|
TableIndex = (DWORD)(FileNameHash & (hs->RootTable.TableSize - 1));
|
|
assert(hs->RootTable.ItemCount < hs->RootTable.TableSize);
|
|
|
|
// Search the proper entry
|
|
for(;;)
|
|
{
|
|
// Does the has match?
|
|
pRootEntry = hs->RootTable.TablePtr + TableIndex;
|
|
if(pRootEntry->FileNameHash == FileNameHash)
|
|
{
|
|
if(PtrTableIndex != NULL)
|
|
PtrTableIndex[0] = TableIndex;
|
|
return pRootEntry;
|
|
}
|
|
|
|
// If the entry is free, the file is not there
|
|
if(pRootEntry->FileNameHash == 0 && pRootEntry->SumValue == 0)
|
|
return NULL;
|
|
|
|
// Move to the next entry
|
|
TableIndex = (DWORD)((TableIndex + 1) & (hs->RootTable.TableSize - 1));
|
|
}
|
|
}
|
|
|
|
static TCascFile * CreateFileHandle(TCascStorage * hs, PCASC_INDEX_ENTRY pIndexEntry)
|
|
{
|
|
ULONGLONG FileOffsMask = ((ULONGLONG)1 << hs->KeyMapping[0].SegmentBits) - 1;
|
|
ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffsetBE);
|
|
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 file size. Note that for all files except ENCODING,
|
|
// this is the compressed file size
|
|
hf->CompressedSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE);
|
|
|
|
// 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 table
|
|
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, TCascFile ** ppCascFile)
|
|
{
|
|
PCASC_INDEX_ENTRY pIndexEntry;
|
|
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)
|
|
{
|
|
ppCascFile[0] = CreateFileHandle(hs, pIndexEntry);
|
|
if(ppCascFile[0] == NULL)
|
|
nError = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
#ifdef CASCLIB_TEST
|
|
if(nError == ERROR_SUCCESS && ppCascFile[0] != NULL)
|
|
{
|
|
ppCascFile[0]->FileSize_IdxEntry = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE);
|
|
}
|
|
#endif
|
|
|
|
if(nError != ERROR_SUCCESS)
|
|
SetLastError(nError);
|
|
return (nError == ERROR_SUCCESS);
|
|
}
|
|
|
|
static bool OpenFileByEncodingKey(TCascStorage * hs, PQUERY_KEY pEncodingKey, DWORD dwFlags, TCascFile ** ppCascFile)
|
|
{
|
|
PCASC_ENCODING_ENTRY pEncodingEntry;
|
|
QUERY_KEY IndexKey;
|
|
|
|
// Find the encoding entry
|
|
pEncodingEntry = FindEncodingEntry(hs, pEncodingKey, NULL);
|
|
if(pEncodingEntry == NULL)
|
|
{
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
return false;
|
|
}
|
|
|
|
// 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;
|
|
IndexKey.cbData = MD5_HASH_SIZE;
|
|
if(OpenFileByIndexKey(hs, &IndexKey, dwFlags, ppCascFile))
|
|
{
|
|
// Check if the file handle was created
|
|
if(ppCascFile[0] != NULL)
|
|
{
|
|
// Fill-in the file size. For all files except ENCODING,
|
|
// this overrides the value stored in the index entry.
|
|
ppCascFile[0]->FileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE);
|
|
|
|
#ifdef CASCLIB_TEST
|
|
ppCascFile[0]->FileSize_EncEntry = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE);
|
|
#endif
|
|
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, (TCascFile **)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, (TCascFile **)phFile);
|
|
}
|
|
|
|
bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocale, DWORD dwFlags, HANDLE * phFile)
|
|
{
|
|
PCASC_ROOT_ENTRY_MNDX pRootEntryMndx = NULL;
|
|
PCASC_ROOT_ENTRY pRootEntry;
|
|
PCASC_PACKAGE pPackage;
|
|
TCascStorage * hs;
|
|
QUERY_KEY EncodingKey;
|
|
char * szStrippedName;
|
|
char * szFileName2;
|
|
int nError = ERROR_SUCCESS;
|
|
|
|
CASCLIB_UNUSED(dwLocale);
|
|
|
|
// 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), &pRootEntryMndx);
|
|
if(nError == ERROR_SUCCESS)
|
|
{
|
|
// Prepare the encoding key
|
|
EncodingKey.pbData = pRootEntryMndx->EncodingKey;
|
|
EncodingKey.cbData = MD5_HASH_SIZE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nError = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Convert the file name to lowercase + slashes
|
|
NormalizeFileName_UpperBkSlash(szFileName2, szFileName2);
|
|
|
|
// Check the root directory for that hash
|
|
pRootEntry = FindRootEntry(hs, szFileName2, NULL);
|
|
if(pRootEntry != NULL)
|
|
{
|
|
// Prepare the root key
|
|
EncodingKey.pbData = (LPBYTE)pRootEntry->EncodingKey;
|
|
EncodingKey.cbData = MD5_HASH_SIZE;
|
|
nError = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
nError = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
// Use the root key to find the file in the encoding table entry
|
|
if(nError == ERROR_SUCCESS)
|
|
{
|
|
if(!OpenFileByEncodingKey(hs, &EncodingKey, dwFlags, (TCascFile **)phFile))
|
|
{
|
|
assert(GetLastError() != ERROR_SUCCESS);
|
|
nError = GetLastError();
|
|
}
|
|
}
|
|
|
|
// Delete the file name copy
|
|
CASC_FREE(szFileName2);
|
|
}
|
|
else
|
|
nError = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
#ifdef CASCLIB_TEST
|
|
if(phFile[0] != NULL && pRootEntryMndx != NULL)
|
|
{
|
|
((TCascFile *)(phFile[0]))->FileSize_RootEntry = pRootEntryMndx->FileSize;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|