Files
TrinityCore/dep/CascLib/src/CascFindFile.cpp

272 lines
8.2 KiB
C++

/*****************************************************************************/
/* 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"
//-----------------------------------------------------------------------------
// Local functions
// Reset the search structure. Called before each search
static void ResetFindData(PCASC_FIND_DATA pFindData)
{
// Reset the variables
ZeroMemory16(pFindData->CKey);
ZeroMemory16(pFindData->EKey);
pFindData->szFileName[0] = 0;
pFindData->szPlainName = pFindData->szFileName;
pFindData->TagBitMask = 0;
pFindData->FileSize = CASC_INVALID_SIZE64;
pFindData->dwFileDataId = CASC_INVALID_ID;
pFindData->dwLocaleFlags = CASC_INVALID_ID;
pFindData->dwContentFlags = CASC_INVALID_ID;
pFindData->dwSpanCount = 1;
pFindData->NameType = CascNameFull;
pFindData->bFileAvailable = false;
}
static void SupplyFakeFileName(PCASC_FIND_DATA pFindData, PCASC_CKEY_ENTRY pCKeyEntry)
{
// If there is a file data ID, create fake file name
if(pFindData->dwFileDataId != CASC_INVALID_ID)
{
CascStrPrintf(pFindData->szFileName, _countof(pFindData->szFileName), CASC_FILEID_FORMAT, pFindData->dwFileDataId);
pFindData->NameType = CascNameDataId;
return;
}
// If there is a CKey, convert the CKey to file name
if(pCKeyEntry->Flags & CASC_CE_HAS_CKEY)
{
StringFromBinary(pFindData->CKey, MD5_HASH_SIZE, pFindData->szFileName);
pFindData->NameType = CascNameCKey;
return;
}
// EKey should be always present
if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY)
{
StringFromBinary(pFindData->EKey, MD5_HASH_SIZE, pFindData->szFileName);
pFindData->NameType = CascNameEKey;
return;
}
assert(false);
}
static bool CopyCKeyEntryToFindData(PCASC_FIND_DATA pFindData, PCASC_CKEY_ENTRY pCKeyEntry)
{
ULONGLONG ContentSize = 0;
ULONGLONG EncodedSize = 0;
// Supply both keys
CopyMemory16(pFindData->CKey, pCKeyEntry->CKey);
CopyMemory16(pFindData->EKey, pCKeyEntry->EKey);
// Supply the tag mask
pFindData->TagBitMask = pCKeyEntry->TagBitMask;
// Supply the plain name. Only do that if the found name is not a CKey/EKey
if(pFindData->szFileName[0] != 0)
pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName);
// If we retrieved the file size directly from the root provider, use it
// Otherwise, supply EncodedSize or ContentSize, whichever is available (but ContentSize > EncodedSize)
pFindData->dwSpanCount = GetFileSpanInfo(pCKeyEntry, &ContentSize, &EncodedSize);
if(pFindData->FileSize == CASC_INVALID_SIZE64)
{
if(ContentSize != CASC_INVALID_SIZE64)
pFindData->FileSize = ContentSize;
else
pFindData->FileSize = EncodedSize;
}
// Set flag indicating that the file is locally available
pFindData->bFileAvailable = (pCKeyEntry->Flags & CASC_CE_FILE_IS_LOCAL);
// Supply a fake file name, if there is none supplied by the root handler
if(pFindData->szFileName[0] == 0)
SupplyFakeFileName(pFindData, pCKeyEntry);
return true;
}
// Perform searching using root-specific provider.
// The provider may need the listfile
static bool DoStorageSearch_RootFile(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
{
PCASC_CKEY_ENTRY pCKeyEntry;
TCascStorage * hs = pSearch->hs;
// Reset the search structure
ResetFindData(pFindData);
// Enumerate over all files
for(;;)
{
// Attempt to find (the next) file from the root handler
pCKeyEntry = hs->pRootHandler->Search(pSearch, pFindData);
if(pCKeyEntry == NULL)
return false;
// The entry is expected to be referenced by the root directory
assert(pCKeyEntry->RefCount != 0);
// Copy the CKey entry to the find data and return it
return CopyCKeyEntryToFindData(pFindData, pCKeyEntry);
}
}
static bool DoStorageSearch_CKey(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
{
PCASC_CKEY_ENTRY pCKeyEntry;
TCascStorage * hs = pSearch->hs;
size_t nTotalItems = hs->CKeyArray.ItemCount();
// Reset the find data structure
ResetFindData(pFindData);
// Check for CKeys that haven't been found yet
while(pSearch->nFileIndex < nTotalItems)
{
// Locate the n-th CKey entry.
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.ItemAt(pSearch->nFileIndex++);
//BREAK_ON_XKEY3(pCKeyEntry->CKey, 0x2B, 0xfc, 0xe4);
// Only report files that are unreferenced by the ROOT handler
if(pCKeyEntry->IsFile() && pCKeyEntry->RefCount == 0)
{
return CopyCKeyEntryToFindData(pFindData, pCKeyEntry);
}
}
// Nameless search ended
return false;
}
static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
{
// State 0: No search done yet
if(pSearch->nSearchState == 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->nSearchState = 1;
pSearch->nFileIndex = 0;
}
// State 1: Searching the list file
if(pSearch->nSearchState == 1)
{
if(DoStorageSearch_RootFile(pSearch, pFindData))
return true;
// Move to the nameless search state
pSearch->nSearchState = 2;
pSearch->nFileIndex = 0;
}
// State 2: Searching the remaining entries by CKey
if(pSearch->nSearchState == 2 && (pSearch->szMask == NULL || !strcmp(pSearch->szMask, "*")))
{
if(DoStorageSearch_CKey(pSearch, pFindData))
return true;
// Move to the final search state
pSearch->nSearchState++;
pSearch->nFileIndex = 0;
}
return false;
}
//-----------------------------------------------------------------------------
// Public functions
HANDLE WINAPI CascFindFirstFile(
HANDLE hStorage,
LPCSTR szMask,
PCASC_FIND_DATA pFindData,
LPCTSTR szListFile)
{
TCascStorage * hs;
TCascSearch * pSearch = NULL;
DWORD dwErrCode = ERROR_SUCCESS;
// Check parameters
if((hs = TCascStorage::IsValid(hStorage)) == NULL)
dwErrCode = ERROR_INVALID_HANDLE;
if(pFindData == NULL)
dwErrCode = ERROR_INVALID_PARAMETER;
// Supply default mask, if needed
if(szMask == NULL || szMask[0] == 0)
szMask = "*";
// Init the search structure and search handle
if(dwErrCode == ERROR_SUCCESS)
{
// Allocate the search handle
pSearch = new TCascSearch(hs, szListFile, szMask);
if(pSearch == NULL)
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
// Perform search
if(dwErrCode == ERROR_SUCCESS)
{
if(!DoStorageSearch(pSearch, pFindData))
dwErrCode = ERROR_NO_MORE_FILES;
}
if(dwErrCode != ERROR_SUCCESS)
{
delete pSearch;
pSearch = (TCascSearch *)INVALID_HANDLE_VALUE;
}
return (HANDLE)pSearch;
}
bool WINAPI CascFindNextFile(
HANDLE hFind,
PCASC_FIND_DATA pFindData)
{
TCascSearch * pSearch;
pSearch = TCascSearch::IsValid(hFind);
if(pSearch == NULL || pFindData == NULL)
{
SetCascError(ERROR_INVALID_PARAMETER);
return false;
}
// Perform search
return DoStorageSearch(pSearch, pFindData);
}
bool WINAPI CascFindClose(HANDLE hFind)
{
TCascSearch * pSearch;
pSearch = TCascSearch::IsValid(hFind);
if(pSearch == NULL)
{
SetCascError(ERROR_INVALID_PARAMETER);
return false;
}
delete pSearch;
return true;
}