#define __CASCLIB_SELF__
#include "CascLib.h"
#include "CascCommon.h"
static LPCTSTR szAllowedHexChars = _T("0123456789aAbBcCdDeEfF");
static LPCTSTR szIndexFormat_V1 = _T("data.i%x%x");
static LPCTSTR szIndexFormat_V2 = _T("%02x%08x.idx");
#define CASC_MAX_ORPHANED_ITEMS 0x100
typedef bool (*EKEY_ENTRY_CALLBACK)(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbEKeyEntry);
static bool IsFileNameMask(LPCTSTR szFileName, LPCTSTR szMask)
{
size_t i;
for(i = 0; szFileName[i] != 0 && szMask[i] != 0; i++)
{
if(szMask[i] == _T('#'))
{
if(!IsHexadecimalDigit(szFileName[i]))
return false;
continue;
}
if(AsciiToUpperTable_BkSlash[(BYTE)(szMask[i])] != AsciiToUpperTable_BkSlash[(BYTE)(szFileName[i])])
return false;
}
return (szFileName[i] == 0 && szMask[i] == 0);
}
static bool IsIndexFileName_V1(LPCTSTR szFileName)
{
return IsFileNameMask(szFileName, _T("data.i##"));
}
static bool IsIndexFileName_V2(LPCTSTR szFileName)
{
return IsFileNameMask(szFileName, _T("##########.idx"));
}
static bool IndexDirectory_OnFileFound(
LPCTSTR szFileName,
void * pvContext)
{
TCascStorage * hs = (TCascStorage *)pvContext;
PCASC_INDEX pIndexFile;
DWORD IndexVersion = 0;
DWORD IndexValue = 0;
if(hs->szIndexFormat == NULL)
{
if(IsIndexFileName_V2(szFileName))
hs->szIndexFormat = szIndexFormat_V2;
else if(IsIndexFileName_V1(szFileName))
hs->szIndexFormat = szIndexFormat_V1;
else
return true;
}
if(hs->szIndexFormat == szIndexFormat_V2)
{
if(!IsIndexFileName_V2(szFileName))
return true;
if(ConvertStringToInt(szFileName + 0, 2, IndexValue) != ERROR_SUCCESS)
return true;
if(ConvertStringToInt(szFileName + 2, 8, IndexVersion) != ERROR_SUCCESS)
return true;
}
else if(hs->szIndexFormat == szIndexFormat_V1)
{
if(!IsIndexFileName_V1(szFileName))
return true;
if(ConvertStringToInt(szFileName + 6, 1, IndexValue) != ERROR_SUCCESS)
return true;
if(ConvertStringToInt(szFileName + 7, 1, IndexVersion) != ERROR_SUCCESS)
return true;
}
else
{
assert(false);
return false;
}
if(IndexValue >= CASC_INDEX_COUNT)
return false;
pIndexFile = &hs->IndexFiles[IndexValue];
if(IndexVersion > pIndexFile->NewSubIndex)
{
pIndexFile->OldSubIndex = pIndexFile->NewSubIndex;
pIndexFile->NewSubIndex = IndexVersion;
}
else if(IndexVersion > pIndexFile->OldSubIndex)
{
pIndexFile->OldSubIndex = IndexVersion;
}
return true;
}
static LPTSTR CreateIndexFileName(TCascStorage * hs, DWORD IndexValue, DWORD IndexVersion)
{
TCHAR szPlainName[0x40];
assert(hs->szIndexFormat != NULL);
assert(hs->szIndexPath != NULL);
assert(IndexValue <= 0x0F);
CascStrPrintf(szPlainName, _countof(szPlainName), hs->szIndexFormat, IndexValue, IndexVersion);
return CASC_PATH<TCHAR>(hs->szIndexPath, szPlainName, NULL).New();
}
static void SaveFileOffsetBitsAndEKeyLength(TCascStorage * hs, BYTE FileOffsetBits, BYTE EKeyLength)
{
if(hs->FileOffsetBits == 0)
hs->FileOffsetBits = FileOffsetBits;
assert(hs->FileOffsetBits == FileOffsetBits);
if(hs->EKeyLength == 0)
hs->EKeyLength = EKeyLength;
assert(hs->EKeyLength == EKeyLength);
}
static LPBYTE CaptureGuardedBlock1(LPBYTE pbFileData, LPBYTE pbFileEnd)
{
PFILE_INDEX_GUARDED_BLOCK pBlock = (PFILE_INDEX_GUARDED_BLOCK)pbFileData;
if((pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK)) >= pbFileEnd)
return NULL;
if((pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK) + pBlock->BlockSize) > pbFileEnd)
return NULL;
if(hashlittle(pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK), pBlock->BlockSize, 0) != pBlock->BlockHash)
return NULL;
return (LPBYTE)(pBlock + 1);
}
static LPBYTE CaptureGuardedBlock2(LPBYTE pbFileData, LPBYTE pbFileEnd, size_t EntryLength, PDWORD PtrBlockSize = NULL)
{
PFILE_INDEX_GUARDED_BLOCK pBlock = (PFILE_INDEX_GUARDED_BLOCK)pbFileData;
LPBYTE pbEntryPtr;
size_t EntryCount;
unsigned int HashBlizzGet = 0;
unsigned int HashHigh = 0;
unsigned int HashLow = 0;
if((pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK)) >= pbFileEnd)
return NULL;
if(pBlock->BlockSize == 0 || (pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK) + pBlock->BlockSize) > pbFileEnd)
return NULL;
pbEntryPtr = pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK);
EntryCount = pBlock->BlockSize / EntryLength;
for(size_t i = 0; i < EntryCount; i++)
{
hashlittle2(pbEntryPtr, EntryLength, &HashHigh, &HashLow);
pbEntryPtr += EntryLength;
}
if(HashHigh == pBlock->BlockHash)
{
if(PtrBlockSize != NULL)
PtrBlockSize[0] = pBlock->BlockSize;
return (LPBYTE)(pBlock + 1);
}
pbEntryPtr = pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK);
EntryCount = pBlock->BlockSize / EntryLength;
for(size_t i = 0; i < EntryCount; i++)
{
HashBlizzGet = hashlittle(pbEntryPtr, EntryLength, HashBlizzGet);
pbEntryPtr += EntryLength;
}
if(HashBlizzGet == pBlock->BlockHash)
{
if(PtrBlockSize != NULL)
PtrBlockSize[0] = pBlock->BlockSize;
return (LPBYTE)(pBlock + 1);
}
return NULL;
}
static LPBYTE CaptureGuardedBlock3(LPBYTE pbFileData, LPBYTE pbFileEnd, size_t EntryLength)
{
PDWORD PtrEntryHash = (PDWORD)pbFileData;
DWORD EntryHash;
if((pbFileData + sizeof(DWORD) + EntryLength) >= pbFileEnd)
return NULL;
if(PtrEntryHash[0] == 0)
return NULL;
EntryHash = hashlittle(pbFileData + sizeof(DWORD), EntryLength+1, 0) | 0x80000000;
if(EntryHash != PtrEntryHash[0])
return NULL;
return (LPBYTE)(PtrEntryHash + 1);
}
static DWORD LoadIndexItems(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, EKEY_ENTRY_CALLBACK PfnEKeyEntry, LPBYTE pbEKeyEntry, LPBYTE pbEKeyEnd)
{
size_t EntryLength = InHeader.EntryLength;
while((pbEKeyEntry + EntryLength) <= pbEKeyEnd)
{
if(!PfnEKeyEntry(hs, InHeader, pbEKeyEntry))
return ERROR_INDEX_PARSING_DONE;
pbEKeyEntry += EntryLength;
}
return ERROR_SUCCESS;
}
static DWORD CaptureIndexHeader_V1(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, size_t cbFileData, DWORD BucketIndex)
{
PFILE_INDEX_HEADER_V1 pIndexHeader = (PFILE_INDEX_HEADER_V1)pbFileData;
LPBYTE pbKeyEntries;
LPBYTE pbFileEnd = pbFileData + cbFileData;
size_t cbKeyEntries;
DWORD HeaderHash;
if((pbFileData + sizeof(FILE_INDEX_HEADER_V1)) > pbFileEnd)
return ERROR_BAD_FORMAT;
if(pIndexHeader->IndexVersion != 0x05 || pIndexHeader->BucketIndex != (BYTE)BucketIndex || pIndexHeader->field_8 == 0)
return ERROR_BAD_FORMAT;
if(pIndexHeader->EncodedSizeLength != 0x04 || pIndexHeader->StorageOffsetLength != 0x05 || pIndexHeader->EKeyLength != 0x09)
return ERROR_NOT_SUPPORTED;
HeaderHash = pIndexHeader->HeaderHash;
pIndexHeader->HeaderHash = 0;
if(hashlittle(pbFileData, sizeof(FILE_INDEX_HEADER_V1), 0) != HeaderHash)
return ERROR_BAD_FORMAT;
pIndexHeader->HeaderHash = HeaderHash;
InHeader.IndexVersion = pIndexHeader->IndexVersion;
InHeader.BucketIndex = pIndexHeader->BucketIndex;
InHeader.StorageOffsetLength = pIndexHeader->StorageOffsetLength;
InHeader.EncodedSizeLength = pIndexHeader->EncodedSizeLength;
InHeader.EKeyLength = pIndexHeader->EKeyLength;
InHeader.FileOffsetBits = pIndexHeader->FileOffsetBits;
InHeader.Alignment = 0;
InHeader.SegmentSize = pIndexHeader->SegmentSize;
InHeader.HeaderLength = sizeof(FILE_INDEX_HEADER_V1);
InHeader.HeaderPadding = 0;
InHeader.EntryLength = pIndexHeader->EKeyLength + pIndexHeader->StorageOffsetLength + pIndexHeader->EncodedSizeLength;
InHeader.EKeyCount = pIndexHeader->EKeyCount1 + pIndexHeader->EKeyCount2;
pbKeyEntries = pbFileData + InHeader.HeaderLength;
cbKeyEntries = pIndexHeader->EKeyCount1 * InHeader.EntryLength;
if((pbKeyEntries + cbKeyEntries) > pbFileEnd)
return ERROR_FILE_CORRUPT;
if(hashlittle(pbKeyEntries, cbKeyEntries, 0) != pIndexHeader->KeysHash1)
return ERROR_FILE_CORRUPT;
pbKeyEntries = pbKeyEntries + cbKeyEntries;
cbKeyEntries = pIndexHeader->EKeyCount2 * InHeader.EntryLength;
if((pbKeyEntries + cbKeyEntries) > pbFileEnd)
return ERROR_FILE_CORRUPT;
if(hashlittle(pbKeyEntries, cbKeyEntries, 0) != pIndexHeader->KeysHash2)
return ERROR_FILE_CORRUPT;
return ERROR_SUCCESS;
}
static DWORD CaptureIndexHeader_V2(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, size_t cbFileData, DWORD BucketIndex)
{
PFILE_INDEX_HEADER_V2 pIndexHeader;
LPBYTE pbFileEnd = pbFileData + cbFileData;
if((pbFileData = CaptureGuardedBlock1(pbFileData, pbFileEnd)) == NULL)
return ERROR_FILE_CORRUPT;
pIndexHeader = (PFILE_INDEX_HEADER_V2)pbFileData;
if(pIndexHeader->IndexVersion != 0x07 || pIndexHeader->BucketIndex != (BYTE)BucketIndex || pIndexHeader->ExtraBytes != 0x00)
return ERROR_BAD_FORMAT;
if(pIndexHeader->EncodedSizeLength != 0x04 || pIndexHeader->StorageOffsetLength != 0x05 || pIndexHeader->EKeyLength != 0x09)
return ERROR_BAD_FORMAT;
InHeader.IndexVersion = pIndexHeader->IndexVersion;
InHeader.BucketIndex = pIndexHeader->BucketIndex;
InHeader.StorageOffsetLength = pIndexHeader->StorageOffsetLength;
InHeader.EncodedSizeLength = pIndexHeader->EncodedSizeLength;
InHeader.EKeyLength = pIndexHeader->EKeyLength;
InHeader.FileOffsetBits = pIndexHeader->FileOffsetBits;
InHeader.Alignment = 0;
InHeader.SegmentSize = pIndexHeader->SegmentSize;
InHeader.HeaderLength = sizeof(FILE_INDEX_GUARDED_BLOCK) + sizeof(FILE_INDEX_HEADER_V2);
InHeader.HeaderPadding = 8;
InHeader.EntryLength = pIndexHeader->EKeyLength + pIndexHeader->StorageOffsetLength + pIndexHeader->EncodedSizeLength;
InHeader.EKeyCount = 0;
return ERROR_SUCCESS;
}
static DWORD LoadIndexFile_V1(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, EKEY_ENTRY_CALLBACK PfnEKeyEntry, LPBYTE pbFileData, size_t cbFileData)
{
LPBYTE pbEKeyEntries = pbFileData + InHeader.HeaderLength + InHeader.HeaderPadding;
SaveFileOffsetBitsAndEKeyLength(hs, InHeader.FileOffsetBits, InHeader.EKeyLength);
return LoadIndexItems(hs, InHeader, PfnEKeyEntry, pbEKeyEntries, pbFileData + cbFileData);
}
static DWORD LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, EKEY_ENTRY_CALLBACK PfnEKeyEntry, LPBYTE pbFileData, size_t cbFileData)
{
LPBYTE pbEKeyEntry;
LPBYTE pbFileEnd = pbFileData + cbFileData;
LPBYTE pbFilePtr = pbFileData + InHeader.HeaderLength + InHeader.HeaderPadding;
size_t EKeyEntriesLength;
DWORD BlockSize = 0;
DWORD dwErrCode = ERROR_NOT_SUPPORTED;
SaveFileOffsetBitsAndEKeyLength(hs, InHeader.FileOffsetBits, InHeader.EKeyLength);
if((pbEKeyEntry = CaptureGuardedBlock2(pbFilePtr, pbFileEnd, InHeader.EntryLength, &BlockSize)) != NULL)
{
InHeader.HeaderPadding += sizeof(FILE_INDEX_GUARDED_BLOCK);
return LoadIndexItems(hs, InHeader, PfnEKeyEntry, pbEKeyEntry, pbEKeyEntry + BlockSize);
}
EKeyEntriesLength = pbFileEnd - pbFilePtr;
if(EKeyEntriesLength >= 0x7800)
{
LPBYTE pbStartPage = pbFileData + 0x1000;
LPBYTE pbEndPage = pbStartPage + FILE_INDEX_PAGE_SIZE;
size_t AlignedLength = ALIGN_TO_SIZE(InHeader.EntryLength, 4);
while(pbStartPage < pbFileEnd)
{
pbEKeyEntry = pbStartPage;
while(pbEKeyEntry < pbEndPage)
{
if((pbEKeyEntry = CaptureGuardedBlock3(pbEKeyEntry, pbEndPage, InHeader.EntryLength)) == NULL)
break;
if(!PfnEKeyEntry(hs, InHeader, pbEKeyEntry))
return ERROR_INDEX_PARSING_DONE;
pbEKeyEntry += AlignedLength;
}
pbStartPage += FILE_INDEX_PAGE_SIZE;
}
dwErrCode = ERROR_SUCCESS;
}
return dwErrCode;
}
static DWORD LoadIndexFile(TCascStorage * hs, EKEY_ENTRY_CALLBACK PfnEKeyEntry, LPBYTE pbFileData, size_t cbFileData, DWORD BucketIndex)
{
CASC_INDEX_HEADER InHeader;
if(CaptureIndexHeader_V2(InHeader, pbFileData, cbFileData, BucketIndex) == ERROR_SUCCESS)
return LoadIndexFile_V2(hs, InHeader, PfnEKeyEntry, pbFileData, cbFileData);
if(CaptureIndexHeader_V1(InHeader, pbFileData, cbFileData, BucketIndex) == ERROR_SUCCESS)
return LoadIndexFile_V1(hs, InHeader, PfnEKeyEntry, pbFileData, cbFileData);
assert(false);
return ERROR_BAD_FORMAT;
}
static bool InsertEncodingEKeyToMap(TCascStorage * hs, CASC_INDEX_HEADER &, LPBYTE pbEKeyEntry)
{
hs->IndexEKeyMap.InsertObject(pbEKeyEntry, pbEKeyEntry);
return true;
}
static DWORD ProcessLocalIndexFiles(TCascStorage * hs, EKEY_ENTRY_CALLBACK PfnEKeyEntry, DWORD dwIndexCount)
{
DWORD dwErrCode = ERROR_SUCCESS;
for(DWORD i = 0; i < dwIndexCount; i++)
{
CASC_INDEX & IndexFile = hs->IndexFiles[i];
if(InvokeProgressCallback(hs, CascProgressLoadingIndexes, NULL, i, dwIndexCount))
{
dwErrCode = ERROR_CANCELLED;
break;
}
if((dwErrCode = LoadIndexFile(hs, PfnEKeyEntry, IndexFile.FileData.pbData, IndexFile.FileData.cbData, i)) != ERROR_SUCCESS)
break;
}
if(dwErrCode == ERROR_INDEX_PARSING_DONE)
dwErrCode = ERROR_SUCCESS;
hs->LocalFiles = hs->CKeyArray.ItemCount();
return dwErrCode;
}
static DWORD LoadLocalIndexFiles(TCascStorage * hs)
{
ULONGLONG TotalSize = 0;
DWORD dwIndexCount = 0;
DWORD dwErrCode;
if(InvokeProgressCallback(hs, CascProgressLoadingIndexes, NULL, 0, 0))
return ERROR_CANCELLED;
if((dwErrCode = ScanDirectory(hs->szIndexPath, NULL, IndexDirectory_OnFileFound, hs)) == ERROR_SUCCESS)
{
if(hs->szIndexFormat == NULL)
return ERROR_FILE_NOT_FOUND;
for(DWORD i = 0; i < CASC_INDEX_COUNT; i++)
{
CASC_INDEX & IndexFile = hs->IndexFiles[i];
if((IndexFile.szFileName = CreateIndexFileName(hs, i, IndexFile.NewSubIndex)) == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
dwErrCode = LoadFileToMemory(IndexFile.szFileName, IndexFile.FileData);
if(dwErrCode != ERROR_SUCCESS)
{
if((dwErrCode = GetCascError()) == ERROR_FILE_NOT_FOUND)
{
dwErrCode = ERROR_SUCCESS;
break;
}
return dwErrCode;
}
TotalSize += IndexFile.FileData.cbData;
dwIndexCount++;
}
dwErrCode = hs->IndexEKeyMap.Create((size_t)(TotalSize / sizeof(FILE_EKEY_ENTRY)), CASC_EKEY_SIZE, 0);
if(dwErrCode == ERROR_SUCCESS)
{
dwErrCode = ProcessLocalIndexFiles(hs, InsertEncodingEKeyToMap, dwIndexCount);
}
}
return dwErrCode;
}
static DWORD CaptureArchiveIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, size_t cbIndexFile)
{
FILE_INDEX_FOOTER<0x08> * pFooter08;
BYTE checksum_data[0x40] = { 0 };
BYTE md5_hash[MD5_HASH_SIZE];
DWORD checksum_data_length;
memset(&InFooter, 0, sizeof(CASC_ARCINDEX_FOOTER));
pFooter08 = (FILE_INDEX_FOOTER<0x08> *)(pbIndexFile + cbIndexFile - sizeof(FILE_INDEX_FOOTER<0x08>));
if(pFooter08->Version == 1 && pFooter08->Reserved[0] == 0 && pFooter08->Reserved[1] == 0 && pFooter08->FooterHashBytes == 8)
{
memcpy(InFooter.TocHash, pFooter08->TocHash, MD5_HASH_SIZE);
memcpy(InFooter.FooterHash, pFooter08->FooterHash, pFooter08->FooterHashBytes);
InFooter.Version = pFooter08->Version;
InFooter.OffsetBytes = pFooter08->OffsetBytes;
InFooter.SizeBytes = pFooter08->SizeBytes;
InFooter.EKeyLength = pFooter08->EKeyLength;
InFooter.FooterHashBytes = pFooter08->FooterHashBytes;
InFooter.PageLength = ((size_t)pFooter08->PageSizeKB) << 10;
InFooter.ItemLength = pFooter08->EKeyLength + pFooter08->OffsetBytes + pFooter08->SizeBytes;
InFooter.FooterLength = sizeof(FILE_INDEX_FOOTER<0x08>);
InFooter.ElementCount = ConvertBytesToInteger_4_LE(pFooter08->ElementCount);
checksum_data_length = FIELD_OFFSET(FILE_INDEX_FOOTER<0x08>, FooterHash) - FIELD_OFFSET(FILE_INDEX_FOOTER<0x08>, Version);
memcpy(checksum_data, &pFooter08->Version, checksum_data_length);
CascHash_MD5(checksum_data, sizeof(FILE_INDEX_FOOTER<0x08>) - MD5_HASH_SIZE, md5_hash);
if(!memcmp(md5_hash, InFooter.FooterHash, InFooter.FooterHashBytes))
return ERROR_SUCCESS;
}
return ERROR_BAD_FORMAT;
}
static DWORD CaptureIndexEntry(CASC_ARCINDEX_FOOTER & InFooter, CASC_EKEY_ENTRY & EKeyEntry, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd, size_t nArchive)
{
ULONGLONG StorageOffset = nArchive;
ULONGLONG ArchiveOffset;
if((pbIndexPage + InFooter.ItemLength) > pbIndexPageEnd)
return ERROR_BAD_FORMAT;
pbIndexPage = CaptureEncodedKey(EKeyEntry.EKey, pbIndexPage, InFooter.EKeyLength);
ArchiveOffset = ConvertBytesToInteger_X(pbIndexPage + InFooter.SizeBytes, InFooter.OffsetBytes);
if(ArchiveOffset >= 0x10000000)
return ERROR_BAD_FORMAT;
EKeyEntry.StorageOffset = (StorageOffset << (InFooter.OffsetBytes * 8)) | ArchiveOffset;
EKeyEntry.EncodedSize = ConvertBytesToInteger_X(pbIndexPage, InFooter.SizeBytes);
EKeyEntry.Alignment = 0;
return CascIsValidMD5(EKeyEntry.EKey) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
}
static DWORD VerifyIndexSize(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, size_t cbIndexFile, LPBYTE * PtrIndexEnd)
{
size_t nPageCount;
cbIndexFile = cbIndexFile - InFooter.FooterLength;
nPageCount = cbIndexFile / (InFooter.PageLength + MD5_HASH_SIZE);
if(((InFooter.PageLength + MD5_HASH_SIZE) * nPageCount) > cbIndexFile)
return ERROR_BAD_FORMAT;
nPageCount = cbIndexFile / (InFooter.PageLength + MD5_HASH_SIZE);
PtrIndexEnd[0] = pbIndexFile + (nPageCount * InFooter.PageLength);
return ERROR_SUCCESS;
}
static DWORD LoadArchiveIndexPage(TCascStorage * hs, CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd, size_t nArchive)
{
CASC_EKEY_ENTRY EKeyEntry;
DWORD dwErrCode;
while (pbIndexPage <= pbIndexPageEnd)
{
dwErrCode = CaptureIndexEntry(InFooter, EKeyEntry, pbIndexPage, pbIndexPageEnd, nArchive);
if(dwErrCode != ERROR_SUCCESS)
break;
if((hs->IndexArray.Insert(&EKeyEntry, 1)) == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
pbIndexPage += InFooter.ItemLength;
}
return ERROR_SUCCESS;
}
static DWORD LoadArchiveIndexFile(TCascStorage * hs, LPBYTE pbIndexFile, size_t cbIndexFile, size_t nArchive)
{
CASC_ARCINDEX_FOOTER InFooter;
LPBYTE pbIndexEnd = NULL;
DWORD dwErrCode;
dwErrCode = CaptureArchiveIndexFooter(InFooter, pbIndexFile, cbIndexFile);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
SaveFileOffsetBitsAndEKeyLength(hs, InFooter.OffsetBytes * 8, InFooter.EKeyLength);
dwErrCode = VerifyIndexSize(InFooter, pbIndexFile, cbIndexFile, &pbIndexEnd);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
while (pbIndexFile < pbIndexEnd)
{
dwErrCode = LoadArchiveIndexPage(hs, InFooter, pbIndexFile, pbIndexFile + InFooter.PageLength, nArchive);
if(dwErrCode != ERROR_SUCCESS)
break;
pbIndexFile += InFooter.PageLength;
}
return ERROR_SUCCESS;
}
static DWORD BuildMapOfArchiveIndices(TCascStorage * hs)
{
PCASC_EKEY_ENTRY pEKeyEntry;
size_t nItemCount = hs->IndexArray.ItemCount();
DWORD dwErrCode;
dwErrCode = hs->IndexMap.Create(nItemCount, MD5_HASH_SIZE, FIELD_OFFSET(CASC_EKEY_ENTRY, EKey));
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
for(size_t i = 0; i < nItemCount; i++)
{
pEKeyEntry = (PCASC_EKEY_ENTRY)hs->IndexArray.ItemAt(i);
if(pEKeyEntry != NULL)
{
if(!hs->IndexMap.InsertObject(pEKeyEntry, pEKeyEntry->EKey))
{
return ERROR_NOT_ENOUGH_MEMORY;
}
}
}
return dwErrCode;
}
static DWORD LoadArchiveIndexFiles(TCascStorage * hs)
{
CASC_BLOB FileData;
size_t nArchiveCount = (hs->ArchivesKey.cbData / MD5_HASH_SIZE);
DWORD dwErrCode = ERROR_SUCCESS;
dwErrCode = hs->IndexArray.Create(sizeof(CASC_EKEY_ENTRY), 0x10000);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
for(size_t i = 0; i < nArchiveCount; i++)
{
CASC_PATH<TCHAR> LocalPath;
LPBYTE pbIndexHash = hs->ArchivesKey.pbData + (i * MD5_HASH_SIZE);
if(InvokeProgressCallback(hs, CascProgressDownloadingArchiveIndexes, NULL, (DWORD)(i), (DWORD)(nArchiveCount)))
{
dwErrCode = ERROR_CANCELLED;
break;
}
dwErrCode = FetchCascFile(hs, PathTypeData, pbIndexHash, _T(".index"), LocalPath);
if(dwErrCode == ERROR_SUCCESS)
{
if((dwErrCode = LoadFileToMemory(LocalPath, FileData)) == ERROR_SUCCESS)
{
dwErrCode = LoadArchiveIndexFile(hs, FileData.pbData, FileData.cbData, i);
}
}
if(dwErrCode != ERROR_SUCCESS)
break;
}
if(dwErrCode == ERROR_SUCCESS)
dwErrCode = BuildMapOfArchiveIndices(hs);
return dwErrCode;
}
bool CopyEKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry)
{
if(!(hs->dwFeatures & CASC_FEATURE_ONLINE))
{
LPBYTE pbEKeyEntry;
pbEKeyEntry = (LPBYTE)hs->IndexEKeyMap.FindObject(pCKeyEntry->EKey);
if(pbEKeyEntry == NULL)
return false;
pCKeyEntry->StorageOffset = ConvertBytesToInteger_5(pbEKeyEntry + hs->EKeyLength);
pCKeyEntry->EncodedSize = ConvertBytesToInteger_4_LE(pbEKeyEntry + hs->EKeyLength + 5);
pCKeyEntry->Flags |= CASC_CE_FILE_IS_LOCAL;
}
return true;
}
DWORD LoadIndexFiles(TCascStorage * hs)
{
switch(hs->BuildFileType)
{
case CascBuildDb: case CascBuildInfo:
return LoadLocalIndexFiles(hs);
case CascVersions: return LoadArchiveIndexFiles(hs);
default:
assert(false);
return ERROR_NOT_SUPPORTED;
}
}
void FreeIndexFiles(TCascStorage * hs)
{
hs->IndexEKeyMap.Free();
for(size_t i = 0; i < CASC_INDEX_COUNT; i++)
{
CASC_INDEX & IndexFile = hs->IndexFiles[i];
IndexFile.FileData.Free();
CASC_FREE(IndexFile.szFileName);
}
}