#define __CASCLIB_SELF__
#include "../CascLib.h"
#include "../CascCommon.h"
static BYTE PathSeparators[256] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00
};
#define START_ITEM_COUNT 0x4000
inline DWORD GET_NODE_INT32(void * node, size_t offset)
{
PDWORD PtrValue = (PDWORD)((LPBYTE)node + offset);
return PtrValue[0];
}
inline void SET_NODE_INT32(void * node, size_t offset, DWORD value)
{
PDWORD PtrValue = (PDWORD)((LPBYTE)node + offset);
PtrValue[0] = value;
}
#ifdef CASCLIB_DEV
#endif
PCASC_FILE_NODE CASC_FILE_TREE::InsertNew(PCASC_CKEY_ENTRY pCKeyEntry)
{
PCASC_FILE_NODE pFileNode;
pFileNode = InsertNew();
if(pFileNode != NULL)
{
pFileNode->pCKeyEntry = pCKeyEntry;
}
return pFileNode;
}
PCASC_FILE_NODE CASC_FILE_TREE::InsertNew()
{
PCASC_FILE_NODE pFileNode;
void * SaveItemArray = NodeTable.ItemArray();
pFileNode = (PCASC_FILE_NODE)NodeTable.Insert(1);
if(pFileNode != NULL)
{
pFileNode->FileNameHash = 0;
pFileNode->pCKeyEntry = NULL;
pFileNode->Parent = 0;
pFileNode->NameIndex = 0;
pFileNode->NameLength = 0;
pFileNode->Flags = 0;
SetExtras(pFileNode, CASC_INVALID_ID, CASC_INVALID_ID, CASC_INVALID_ID);
if(NodeTable.ItemArray() != SaveItemArray || (NodeTable.ItemCount() * 3 / 2) > NameMap.HashTableSize())
{
if(!RebuildNameMaps())
{
pFileNode = NULL;
assert(false);
}
}
}
return pFileNode;
}
bool CASC_FILE_TREE::InsertToNameMap(PCASC_FILE_NODE pFileNode)
{
bool bResult = false;
if(pFileNode->FileNameHash != 0)
bResult = NameMap.InsertObject(pFileNode, &pFileNode->FileNameHash);
return bResult;
}
bool CASC_FILE_TREE::InsertToIdTable(PCASC_FILE_NODE pFileNode)
{
PCASC_FILE_NODE * RefElement;
DWORD FileDataId = CASC_INVALID_ID;
if(FileDataIds.IsInitialized())
{
GetExtras(pFileNode, &FileDataId, NULL, NULL);
if(FileDataId != CASC_INVALID_ID)
{
assert(FileDataId < CASC_INVALID_ID);
RefElement = (PCASC_FILE_NODE *)FileDataIds.InsertAt(FileDataId);
if(RefElement != NULL)
{
RefElement[0] = pFileNode;
return true;
}
}
}
return false;
}
bool CASC_FILE_TREE::SetNodePlainName(PCASC_FILE_NODE pFileNode, const char * szPlainName, const char * szPlainNameEnd)
{
char * szNodeName;
size_t nLength = (szPlainNameEnd - szPlainName);
szNodeName = (char *)NameTable.Insert(nLength);
if(szNodeName != NULL)
{
memcpy(szNodeName, szPlainName, nLength);
pFileNode->NameIndex = (DWORD)NameTable.IndexOf(szNodeName);
pFileNode->NameLength = (USHORT)nLength;
return true;
}
return false;
}
bool CASC_FILE_TREE::SetKeyLength(DWORD aKeyLength)
{
if(aKeyLength > MD5_HASH_SIZE)
return false;
KeyLength = aKeyLength;
return true;
}
DWORD CASC_FILE_TREE::GetNextFileDataId()
{
if(FileDataIds.IsInitialized())
return (DWORD)(FileDataIds.ItemCount() + 1);
return CASC_INVALID_ID;
}
bool CASC_FILE_TREE::RebuildNameMaps()
{
PCASC_FILE_NODE pFileNode;
size_t nMaxItems = NodeTable.ItemCountMax();
NameMap.Free();
if(NameMap.Create(nMaxItems, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_NODE, FileNameHash)) != ERROR_SUCCESS)
return false;
FileDataIds.Reset();
for(size_t i = 0; i < NodeTable.ItemCount(); i++)
{
pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(i);
if(pFileNode != NULL)
{
if(pFileNode->FileNameHash != 0)
InsertToNameMap(pFileNode);
if(FileDataIds.IsInitialized())
InsertToIdTable(pFileNode);
}
}
return true;
}
DWORD CASC_FILE_TREE::Create(DWORD Flags)
{
PCASC_FILE_NODE pRootNode;
size_t FileNodeSize = FIELD_OFFSET(CASC_FILE_NODE, ExtraValues);
DWORD dwErrCode;
memset(this, 0, sizeof(CASC_FILE_TREE));
KeyLength = MD5_HASH_SIZE;
if(Flags & FTREE_FLAG_USE_DATA_ID)
{
FileDataIdOffset = FileNodeSize;
FileNodeSize += sizeof(DWORD);
dwErrCode = FileDataIds.Create<PCASC_FILE_NODE>(START_ITEM_COUNT);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
}
if(Flags & FTREE_FLAG_USE_LOCALE_FLAGS)
{
LocaleFlagsOffset = FileNodeSize;
FileNodeSize += sizeof(DWORD);
}
if(Flags & FTREE_FLAG_USE_CONTENT_FLAGS)
{
ContentFlagsOffset = FileNodeSize;
FileNodeSize += sizeof(DWORD);
}
FileNodeSize = ALIGN_TO_SIZE(FileNodeSize, 8);
dwErrCode = NodeTable.Create(FileNodeSize, START_ITEM_COUNT);
if(dwErrCode == ERROR_SUCCESS)
{
dwErrCode = NameTable.Create<char>(START_ITEM_COUNT);
if(dwErrCode == ERROR_SUCCESS)
{
pRootNode = (PCASC_FILE_NODE)NodeTable.Insert(1);
if(pRootNode != NULL)
{
memset(pRootNode, 0, NodeTable.ItemSize());
pRootNode->Parent = CASC_INVALID_INDEX;
pRootNode->NameIndex = CASC_INVALID_INDEX;
pRootNode->Flags = CFN_FLAG_FOLDER;
SetExtras(pRootNode, CASC_INVALID_ID, CASC_INVALID_ID, CASC_INVALID_ID);
}
}
}
if(!RebuildNameMaps())
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
return dwErrCode;
}
void CASC_FILE_TREE::Free()
{
NodeTable.Free();
NameTable.Free();
FileDataIds.Free();
NameMap.Free();
memset(this, 0, sizeof(CASC_FILE_TREE));
}
PCASC_FILE_NODE CASC_FILE_TREE::InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const char * szFileName, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
{
PCASC_FILE_NODE pFileNode;
ULONGLONG FileNameHash;
assert(szFileName != NULL && szFileName[0] != 0);
assert(pCKeyEntry != NULL);
FileNameHash = CalcFileNameHash(szFileName);
pFileNode = (PCASC_FILE_NODE)NameMap.FindObject(&FileNameHash);
if(pFileNode == NULL)
{
pFileNode = InsertNew(pCKeyEntry);
if(pFileNode != NULL)
{
pFileNode->FileNameHash = FileNameHash;
SetExtras(pFileNode, FileDataId, LocaleFlags, ContentFlags);
InsertToNameMap(pFileNode);
InsertToIdTable(pFileNode);
SetNodeFileName(pFileNode, szFileName);
assert(pCKeyEntry->RefCount < 0xFFFFFFFF);
pCKeyEntry->RefCount++;
FileNodes++;
}
}
#ifdef CASCLIB_DEV
#endif
return pFileNode;
}
PCASC_FILE_NODE CASC_FILE_TREE::InsertByHash(PCASC_CKEY_ENTRY pCKeyEntry, ULONGLONG FileNameHash, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
{
PCASC_FILE_NODE pFileNode;
assert(FileDataIds.IsInitialized());
assert(FileDataId != CASC_INVALID_ID);
assert(FileNameHash != 0);
assert(pCKeyEntry != NULL);
pFileNode = InsertById(pCKeyEntry, FileDataId, LocaleFlags, ContentFlags);
if(pFileNode != NULL)
{
pFileNode->FileNameHash = FileNameHash;
InsertToNameMap(pFileNode);
}
return pFileNode;
}
PCASC_FILE_NODE CASC_FILE_TREE::InsertById(PCASC_CKEY_ENTRY pCKeyEntry, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
{
PCASC_FILE_NODE pFileNode;
assert(FileDataIds.IsInitialized());
assert(FileDataId != CASC_INVALID_ID);
assert(pCKeyEntry != NULL);
if((pFileNode = FindById(FileDataId)) == NULL)
{
pFileNode = InsertNew(pCKeyEntry);
if(pFileNode != NULL)
{
SetExtras(pFileNode, FileDataId, LocaleFlags, ContentFlags);
InsertToIdTable(pFileNode);
pCKeyEntry->RefCount++;
}
}
return pFileNode;
}
PCASC_FILE_NODE CASC_FILE_TREE::ItemAt(size_t nItemIndex)
{
return (PCASC_FILE_NODE)NodeTable.ItemAt(nItemIndex);
}
PCASC_FILE_NODE CASC_FILE_TREE::PathAt(char * szBuffer, size_t cchBuffer, size_t nItemIndex)
{
PCASC_FILE_NODE * RefFileNode;
PCASC_FILE_NODE pFileNode = NULL;
if(FileDataIds.IsInitialized())
{
RefFileNode = (PCASC_FILE_NODE *)FileDataIds.ItemAt(nItemIndex);
if(RefFileNode != NULL)
{
pFileNode = RefFileNode[0];
}
}
else
{
pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nItemIndex);
}
PathAt(szBuffer, cchBuffer, pFileNode);
return pFileNode;
}
size_t CASC_FILE_TREE::PathAt(char * szBuffer, size_t cchBuffer, PCASC_FILE_NODE pFileNode)
{
PCASC_FILE_NODE pParentNode;
const char * szNamePtr;
char * szSaveBuffer = szBuffer;
char * szBufferEnd = szBuffer + cchBuffer - 1;
if(pFileNode != NULL && pFileNode->Parent != CASC_INVALID_INDEX)
{
pParentNode = (PCASC_FILE_NODE)NodeTable.ItemAt(pFileNode->Parent);
if(pParentNode != NULL)
{
szBuffer = szBuffer + PathAt(szBuffer, cchBuffer, pParentNode);
}
szNamePtr = (const char *)NameTable.ItemAt(pFileNode->NameIndex);
if((szBuffer + pFileNode->NameLength) < szBufferEnd)
{
memcpy(szBuffer, szNamePtr, pFileNode->NameLength);
szBuffer += pFileNode->NameLength;
if((pFileNode->Flags & CFN_FLAG_FOLDER) && ((szBuffer + 1) < szBufferEnd))
{
*szBuffer++ = (pFileNode->Flags & CFN_FLAG_MOUNT_POINT) ? ':' : '\\';
}
}
}
szBuffer[0] = 0;
return (szBuffer - szSaveBuffer);
}
PCASC_FILE_NODE CASC_FILE_TREE::Find(const char * szFullPath, DWORD FileDataId, PCASC_FIND_DATA pFindData)
{
PCASC_FILE_NODE pFileNode = NULL;
ULONGLONG FileNameHash;
if(FileDataIds.IsInitialized() && (FileDataId != CASC_INVALID_ID || IsFileDataIdName(szFullPath, FileDataId)))
{
pFileNode = FindById(FileDataId);
}
else
{
if(szFullPath != NULL && szFullPath[0] != 0)
{
FileNameHash = CalcFileNameHash(szFullPath);
pFileNode = (PCASC_FILE_NODE)NameMap.FindObject(&FileNameHash);
}
}
if(pFileNode != NULL && pFindData != NULL)
{
GetExtras(pFileNode, &pFindData->dwFileDataId, &pFindData->dwLocaleFlags, &pFindData->dwContentFlags);
}
return pFileNode;
}
PCASC_FILE_NODE CASC_FILE_TREE::Find(PCASC_CKEY_ENTRY pCKeyEntry)
{
PCASC_FILE_NODE pFileNode;
for(size_t i = 0; i < NodeTable.ItemCount(); i++)
{
pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(i);
if((pFileNode->Flags & (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT)) == 0)
{
if(pFileNode->pCKeyEntry == pCKeyEntry)
return pFileNode;
}
}
return NULL;
}
PCASC_FILE_NODE CASC_FILE_TREE::Find(ULONGLONG FileNameHash)
{
return (PCASC_FILE_NODE)NameMap.FindObject(&FileNameHash);
}
PCASC_FILE_NODE CASC_FILE_TREE::FindById(DWORD FileDataId)
{
PCASC_FILE_NODE * RefElement;
PCASC_FILE_NODE pFileNode = NULL;
if(FileDataId != CASC_INVALID_ID && FileDataIds.IsInitialized())
{
RefElement = (PCASC_FILE_NODE *)FileDataIds.ItemAt(FileDataId);
if(RefElement != NULL)
{
pFileNode = RefElement[0];
}
}
return pFileNode;
}
bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szFileName)
{
ULONGLONG FileNameHash = 0;
PCASC_FILE_NODE pFolderNode = NULL;
CASC_PATH<char> PathBuffer;
LPCSTR szNodeBegin = szFileName;
size_t nFileNode = NodeTable.IndexOf(pFileNode);
size_t i;
DWORD Parent = 0;
assert(szFileName != NULL && szFileName[0] != 0);
for(i = 0; szFileName[i] != 0; i++)
{
char chOneChar = szFileName[i];
if(PathSeparators[chOneChar])
{
size_t nHashLength = i;
if(PathSeparators[chOneChar] == 0x02)
{
PathBuffer.AppendChar(chOneChar);
nHashLength++;
}
FileNameHash = CalcNormNameHash(PathBuffer, nHashLength);
if((pFolderNode = Find(FileNameHash)) == NULL)
{
pFolderNode = InsertNew();
if(pFolderNode == NULL)
return false;
pFolderNode->Flags |= (chOneChar == ':') ? (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT) : CFN_FLAG_FOLDER;
pFolderNode->FileNameHash = FileNameHash;
pFolderNode->Parent = Parent;
FolderNodes++;
SetNodePlainName(pFolderNode, szNodeBegin, szFileName + i);
InsertToNameMap(pFolderNode);
}
else if(pFolderNode == pFileNode)
{
assert(pFolderNode->FileNameHash == FileNameHash);
assert(szFileName[i + 1] == 0);
pFolderNode->Flags |= (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT);
pFolderNode->Parent = Parent;
FolderNodes++;
SetNodePlainName(pFolderNode, szNodeBegin, szFileName + i);
return true;
}
Parent = (DWORD)NodeTable.IndexOf(pFolderNode);
szNodeBegin = szFileName + i + 1;
if(PathSeparators[chOneChar] == 0x02)
{
continue;
}
}
PathBuffer.AppendChar(AsciiToUpperTable_BkSlash[chOneChar]);
}
if(szNodeBegin < szFileName + i)
{
pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nFileNode);
SetNodePlainName(pFileNode, szNodeBegin, szFileName + i);
pFileNode->Parent = Parent;
if(pFileNode->FileNameHash == 0)
{
pFileNode->FileNameHash = CalcNormNameHash(PathBuffer, i);
InsertToNameMap(pFileNode);
}
}
return true;
}
size_t CASC_FILE_TREE::GetMaxFileIndex()
{
if(FileDataIds.IsInitialized())
{
return FileDataIds.ItemCount();
}
else
{
return NodeTable.ItemCount();
}
}
size_t CASC_FILE_TREE::GetCount()
{
return NodeTable.ItemCount();
}
size_t CASC_FILE_TREE::IndexOf(PCASC_FILE_NODE pFileNode)
{
return NodeTable.IndexOf(pFileNode);
}
void CASC_FILE_TREE::GetExtras(PCASC_FILE_NODE pFileNode, PDWORD PtrFileDataId, PDWORD PtrLocaleFlags, PDWORD PtrContentFlags)
{
DWORD FileDataId = CASC_INVALID_ID;
DWORD LocaleFlags = CASC_INVALID_ID;
DWORD ContentFlags = CASC_INVALID_ID;
if(PtrFileDataId != NULL)
{
if(FileDataIdOffset != 0)
FileDataId = GET_NODE_INT32(pFileNode, FileDataIdOffset);
PtrFileDataId[0] = FileDataId;
}
if(PtrLocaleFlags != NULL)
{
if(LocaleFlagsOffset != 0)
LocaleFlags = GET_NODE_INT32(pFileNode, LocaleFlagsOffset);
PtrLocaleFlags[0] = LocaleFlags;
}
if(PtrContentFlags != NULL)
{
if(ContentFlagsOffset != 0)
ContentFlags = GET_NODE_INT32(pFileNode, ContentFlagsOffset);
PtrContentFlags[0] = ContentFlags;
}
}
void CASC_FILE_TREE::SetExtras(PCASC_FILE_NODE pFileNode, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
{
if(FileDataIdOffset != 0)
{
SET_NODE_INT32(pFileNode, FileDataIdOffset, FileDataId);
}
if(LocaleFlagsOffset != 0)
{
SET_NODE_INT32(pFileNode, LocaleFlagsOffset, LocaleFlags);
}
if(ContentFlagsOffset != 0)
{
SET_NODE_INT32(pFileNode, ContentFlagsOffset, ContentFlags);
}
}