#define __CASCLIB_SELF__
#include "CascLib.h"
#include "CascCommon.h"
static DWORD GetStreamEncodedSize(TFileStream * pStream)
{
ULONGLONG FileSize = 0;
FileStream_GetSize(pStream, &FileSize);
assert((FileSize >> 32) == 0);
return (DWORD)(FileSize);
}
static DWORD OpenDataStream(TCascFile * hf, PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry, bool bDownloadFileIf)
{
TCascStorage * hs = hf->hs;
TFileStream * pStream = NULL;
TCHAR szPlainName[0x80];
DWORD dwErrCode;
if(pCKeyEntry->Flags & CASC_CE_FILE_IS_LOCAL)
{
DWORD dwArchiveIndex = pFileSpan->ArchiveIndex;
CascLock(hs->StorageLock);
if(hs->DataFiles[dwArchiveIndex] == NULL)
{
CascStrPrintf(szPlainName, _countof(szPlainName), _T("data.%03u"), dwArchiveIndex);
CASC_PATH<TCHAR> DataFile(hs->szIndexPath, szPlainName, NULL);
pStream = FileStream_OpenFile(DataFile, STREAM_FLAG_READ_ONLY | STREAM_FLAG_WRITE_SHARE | STREAM_PROVIDER_FLAT | STREAM_FLAG_FILL_MISSING | BASE_PROVIDER_FILE);
hs->DataFiles[dwArchiveIndex] = pStream;
}
CascUnlock(hs->StorageLock);
pFileSpan->pStream = hs->DataFiles[dwArchiveIndex];
return (pFileSpan->pStream != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND;
}
else
{
if(bDownloadFileIf)
{
CASC_ARCHIVE_INFO ArchiveInfo = {0};
CASC_PATH<TCHAR> LocalPath;
CPATH_TYPE PathType = (pCKeyEntry->Flags & CASC_CE_FILE_PATCH) ? PathTypePatch : PathTypeData;
dwErrCode = FetchCascFile(hs, PathType, pCKeyEntry->EKey, NULL, LocalPath, &ArchiveInfo);
if(dwErrCode == ERROR_SUCCESS)
{
pStream = FileStream_OpenFile(LocalPath, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT);
if(pStream != NULL)
{
if(CascIsValidMD5(ArchiveInfo.ArchiveKey))
{
pFileSpan->ArchiveIndex = ArchiveInfo.ArchiveIndex;
pFileSpan->ArchiveOffs = ArchiveInfo.ArchiveOffs;
if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE)
pCKeyEntry->EncodedSize = ArchiveInfo.EncodedSize;
assert(pCKeyEntry->EncodedSize == ArchiveInfo.EncodedSize);
}
else
{
pFileSpan->ArchiveIndex = 0;
pFileSpan->ArchiveOffs = 0;
if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE)
pCKeyEntry->EncodedSize = GetStreamEncodedSize(pStream);
assert(pCKeyEntry->EncodedSize == GetStreamEncodedSize(pStream));
}
pFileSpan->pStream = pStream;
hf->bCloseFileStream = true;
return ERROR_SUCCESS;
}
}
return dwErrCode;
}
return ERROR_FILE_OFFLINE;
}
}
#ifdef CASCLIB_DEBUG
static unsigned int table_16C57A8[0x10] =
{
0x049396B8, 0x72A82A9B, 0xEE626CCA, 0x9917754F,
0x15DE40B1, 0xF5A8A9B6, 0x421EAC7E, 0xA9D55C9A,
0x317FD40C, 0x04FAF80D, 0x3D6BE971, 0x52933CFD,
0x27F64B7D, 0xC6F5C11B, 0xD5757E3A, 0x6C388745
};
static void VerifyHeaderSpan(PBLTE_ENCODED_HEADER pBlteHeader, ULONGLONG HeaderOffset)
{
LPBYTE pbBlteHeader = (LPBYTE)pBlteHeader;
DWORD dwInt32;
BYTE EncodedOffset[4] = { 0 };
BYTE HashedHeader[4] = { 0 };
BYTE JenkinsHash[4];
BYTE Checksum[4];
size_t i, j;
assert(pBlteHeader->field_15 == 0);
dwInt32 = hashlittle(pbBlteHeader, FIELD_OFFSET(BLTE_ENCODED_HEADER, JenkinsHash), 0x3D6BE971);
ConvertIntegerToBytes_4_LE(dwInt32, JenkinsHash);
dwInt32 = (DWORD)(HeaderOffset + FIELD_OFFSET(BLTE_ENCODED_HEADER, Signature));
dwInt32 = table_16C57A8[dwInt32 & 0x0F] ^ dwInt32;
ConvertIntegerToBytes_4_LE(dwInt32, EncodedOffset);
for(i = 0; i < FIELD_OFFSET(BLTE_ENCODED_HEADER, Checksum); i++)
HashedHeader[i & 3] ^= pbBlteHeader[i];
for(j = 0; j < 4; j++, i++)
Checksum[j] = HashedHeader[i & 3] ^ EncodedOffset[i & 3];
}
#endif
static DWORD ParseBlteHeader(PCASC_FILE_SPAN pFileSpan, ULONGLONG HeaderOffset, LPBYTE pbEncodedBuffer, size_t cbEncodedBuffer, size_t * pcbHeaderSize)
{
PBLTE_HEADER pBlteHeader = (PBLTE_HEADER)pbEncodedBuffer;
DWORD ExpectedHeaderSize;
DWORD ExHeaderSize = 0;
DWORD HeaderSize;
DWORD FrameCount = 0;
CASCLIB_UNUSED(HeaderOffset);
if(ConvertBytesToInteger_4_LE(pBlteHeader->Signature) != BLTE_HEADER_SIGNATURE)
{
PBLTE_ENCODED_HEADER pEncodedHeader;
if(cbEncodedBuffer < FIELD_OFFSET(BLTE_ENCODED_HEADER, MustBe0F))
return ERROR_BAD_FORMAT;
pEncodedHeader = (PBLTE_ENCODED_HEADER)pbEncodedBuffer;
if(ConvertBytesToInteger_4_LE(pEncodedHeader->Signature) != BLTE_HEADER_SIGNATURE)
return ERROR_BAD_FORMAT;
pBlteHeader = (PBLTE_HEADER)(pEncodedHeader->Signature);
ExHeaderSize = FIELD_OFFSET(BLTE_ENCODED_HEADER, Signature);
#ifdef CASCLIB_DEBUG
VerifyHeaderSpan(pEncodedHeader, HeaderOffset);
#endif
}
HeaderSize = ConvertBytesToInteger_4(pBlteHeader->HeaderSize);
if(HeaderSize != 0)
{
if(pBlteHeader->MustBe0F != 0x0F)
return ERROR_BAD_FORMAT;
FrameCount = ConvertBytesToInteger_3(pBlteHeader->FrameCount);
ExpectedHeaderSize = 0x0C + FrameCount * sizeof(BLTE_FRAME);
if(ExpectedHeaderSize != HeaderSize)
return ERROR_BAD_FORMAT;
pcbHeaderSize[0] = ExHeaderSize + FIELD_OFFSET(BLTE_HEADER, MustBe0F) + sizeof(DWORD);
}
else
{
pcbHeaderSize[0] = ExHeaderSize + FIELD_OFFSET(BLTE_HEADER, MustBe0F);
}
pFileSpan->FrameCount = FrameCount;
return ERROR_SUCCESS;
}
static LPBYTE ReadMissingHeaderData(PCASC_FILE_SPAN pFileSpan, ULONGLONG DataFileOffset, LPBYTE pbEncodedBuffer, size_t cbEncodedBuffer, size_t cbTotalHeaderSize)
{
LPBYTE pbNewBuffer;
pbNewBuffer = CASC_REALLOC(pbEncodedBuffer, cbTotalHeaderSize);
if(pbNewBuffer != NULL)
{
DataFileOffset += cbEncodedBuffer;
if(FileStream_Read(pFileSpan->pStream, &DataFileOffset, pbNewBuffer + cbEncodedBuffer, (DWORD)(cbTotalHeaderSize - cbEncodedBuffer)))
{
return pbNewBuffer;
}
}
CASC_FREE(pbEncodedBuffer);
return NULL;
}
static LPBYTE CaptureBlteFileFrame(CASC_FILE_FRAME & Frame, LPBYTE pbFramePtr, LPBYTE pbFrameEnd)
{
PBLTE_FRAME pFileFrame = (PBLTE_FRAME)pbFramePtr;
if((pbFramePtr + sizeof(BLTE_FRAME)) > pbFrameEnd)
return NULL;
Frame.FrameHash = pFileFrame->FrameHash;
Frame.ContentSize = ConvertBytesToInteger_4(pFileFrame->ContentSize);
Frame.EncodedSize = ConvertBytesToInteger_4(pFileFrame->EncodedSize);
return pbFramePtr + sizeof(BLTE_FRAME);
}
static DWORD LoadSpanFrames(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry, ULONGLONG DataFileOffset, LPBYTE pbFramePtr, LPBYTE pbFrameEnd, size_t cbHeaderSize)
{
PCASC_FILE_FRAME pFrames = NULL;
DWORD ContentSize = 0;
DWORD dwErrCode = ERROR_SUCCESS;
assert(pFileSpan != NULL);
assert(pFileSpan->pStream != NULL);
assert(pFileSpan->pFrames == NULL);
if(pFileSpan->FrameCount != 0)
{
DataFileOffset += ((ULONGLONG)pFileSpan->FrameCount * sizeof(BLTE_FRAME));
pFrames = CASC_ALLOC<CASC_FILE_FRAME>(pFileSpan->FrameCount);
if(pFrames != NULL)
{
for(DWORD i = 0; i < pFileSpan->FrameCount; i++)
{
CASC_FILE_FRAME & Frame = pFrames[i];
pbFramePtr = CaptureBlteFileFrame(Frame, pbFramePtr, pbFrameEnd);
if(pbFramePtr == NULL)
{
dwErrCode = ERROR_BAD_FORMAT;
break;
}
Frame.StartOffset = pFileSpan->StartOffset + ContentSize;
Frame.EndOffset = Frame.StartOffset + Frame.ContentSize;
ContentSize += Frame.ContentSize;
assert((DataFileOffset + Frame.EncodedSize) > DataFileOffset);
Frame.DataFileOffset = DataFileOffset;
DataFileOffset += Frame.EncodedSize;
}
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
{
pCKeyEntry->ContentSize = ContentSize;
}
}
else
{
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
}
else
{
pFrames = CASC_ALLOC<CASC_FILE_FRAME>(1);
if(pFrames != NULL)
{
memset(&pFrames->FrameHash, 0, sizeof(CONTENT_KEY));
pFrames->StartOffset = pFileSpan->StartOffset;
pFrames->EndOffset = pFileSpan->EndOffset;
pFrames->DataFileOffset = DataFileOffset;
pFrames->EncodedSize = (DWORD)(pCKeyEntry->EncodedSize - cbHeaderSize);
pFrames->ContentSize = pCKeyEntry->ContentSize;
pFileSpan->FrameCount = 1;
}
else
{
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
}
if(dwErrCode != ERROR_SUCCESS)
{
pFileSpan->FrameCount = 0;
CASC_FREE(pFrames);
}
pFileSpan->pFrames = pFrames;
return dwErrCode;
}
static DWORD LoadSpanFramesForPlainFile(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry)
{
PCASC_FILE_FRAME pFrames;
pFrames = CASC_ALLOC<CASC_FILE_FRAME>(1);
if(pFrames != NULL)
{
pFileSpan->EndOffset = pFileSpan->StartOffset + pCKeyEntry->ContentSize;
pCKeyEntry->Flags |= CASC_CE_PLAIN_DATA;
memset(&pFrames->FrameHash, 0, sizeof(CONTENT_KEY));
pFrames->StartOffset = pFileSpan->StartOffset;
pFrames->EndOffset = pFrames->StartOffset + pCKeyEntry->ContentSize;
pFrames->DataFileOffset = 0;
pFrames->EncodedSize = pCKeyEntry->EncodedSize;
pFrames->ContentSize = pCKeyEntry->ContentSize;
pFileSpan->FrameCount = 1;
pFileSpan->pFrames = pFrames;
return ERROR_SUCCESS;
}
return ERROR_NOT_ENOUGH_MEMORY;
}
static DWORD LoadEncodedHeaderAndSpanFrames(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry)
{
LPBYTE pbEncodedBuffer;
size_t cbEncodedBuffer = MAX_ENCODED_HEADER;
DWORD dwErrCode = ERROR_SUCCESS;
assert(pFileSpan->pFrames == NULL);
assert(pFileSpan->FrameCount == 0);
pbEncodedBuffer = CASC_ALLOC<BYTE>(MAX_ENCODED_HEADER);
if(pbEncodedBuffer != NULL)
{
ULONGLONG ReadOffset = pFileSpan->ArchiveOffs;
size_t cbTotalHeaderSize;
size_t cbHeaderSize = 0;
assert(pCKeyEntry->EncodedSize != CASC_INVALID_SIZE);
cbEncodedBuffer = CASCLIB_MIN(cbEncodedBuffer, pCKeyEntry->EncodedSize);
if(FileStream_Read(pFileSpan->pStream, &ReadOffset, pbEncodedBuffer, (DWORD)cbEncodedBuffer))
{
dwErrCode = ParseBlteHeader(pFileSpan, ReadOffset, pbEncodedBuffer, cbEncodedBuffer, &cbHeaderSize);
if(dwErrCode == ERROR_SUCCESS)
{
pFileSpan->HeaderSize = (DWORD)(cbTotalHeaderSize = cbHeaderSize + (pFileSpan->FrameCount * sizeof(BLTE_FRAME)));
if(cbTotalHeaderSize > cbEncodedBuffer)
{
pbEncodedBuffer = ReadMissingHeaderData(pFileSpan, ReadOffset, pbEncodedBuffer, cbEncodedBuffer, cbTotalHeaderSize);
if(pbEncodedBuffer == NULL)
dwErrCode = GetCascError();
cbEncodedBuffer = cbTotalHeaderSize;
}
if(dwErrCode == ERROR_SUCCESS)
{
assert((ReadOffset + cbHeaderSize) > ReadOffset);
dwErrCode = LoadSpanFrames(pFileSpan, pCKeyEntry, ReadOffset + cbHeaderSize, pbEncodedBuffer + cbHeaderSize, pbEncodedBuffer + cbEncodedBuffer, cbHeaderSize);
}
}
else
{
if(pCKeyEntry->EncodedSize == pCKeyEntry->ContentSize)
{
dwErrCode = LoadSpanFramesForPlainFile(pFileSpan, pCKeyEntry);
}
}
}
else
{
dwErrCode = ERROR_FILE_CORRUPT;
}
CASC_FREE(pbEncodedBuffer);
}
else
{
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
return dwErrCode;
}
static DWORD LoadSpanFrames(TCascFile * hf, PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry)
{
DWORD dwErrCode = ERROR_SUCCESS;
assert(pFileSpan->pFrames == NULL);
if(pFileSpan->pStream == NULL)
{
dwErrCode = OpenDataStream(hf, pFileSpan, pCKeyEntry, hf->bDownloadFileIf);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
}
return LoadEncodedHeaderAndSpanFrames(pFileSpan, pCKeyEntry);
}
static DWORD LoadFileSpanFrames(TCascFile * hf)
{
PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry;
PCASC_FILE_SPAN pFileSpan = hf->pFileSpan;
DWORD dwErrCode = ERROR_SUCCESS;
if(hf->ContentSize == CASC_INVALID_SIZE64 || hf->EncodedSize == CASC_INVALID_SIZE64)
{
hf->ContentSize = 0;
hf->EncodedSize = 0;
for(DWORD i = 0; i < hf->SpanCount; i++, pCKeyEntry++, pFileSpan++)
{
pFileSpan->StartOffset = hf->ContentSize;
pFileSpan->EndOffset = hf->ContentSize;
dwErrCode = LoadSpanFrames(hf, pFileSpan, pCKeyEntry);
if(dwErrCode != ERROR_SUCCESS)
break;
hf->ContentSize += pCKeyEntry->ContentSize;
hf->EncodedSize += pCKeyEntry->EncodedSize;
pFileSpan->EndOffset = hf->ContentSize;
}
}
else
{
for(DWORD i = 0; i < hf->SpanCount; i++, pCKeyEntry++, pFileSpan++)
{
dwErrCode = LoadSpanFrames(hf, pFileSpan, pCKeyEntry);
if(dwErrCode != ERROR_SUCCESS)
break;
}
}
return dwErrCode;
}
static DWORD EnsureFileSpanFramesLoaded(TCascFile * hf)
{
DWORD dwErrCode;
if(hf->ContentSize == CASC_INVALID_SIZE64 || hf->pFileSpan->pFrames == NULL)
{
dwErrCode = LoadFileSpanFrames(hf);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
if(hf->ContentSize == CASC_INVALID_SIZE64)
return ERROR_CAN_NOT_COMPLETE;
}
return ERROR_SUCCESS;
}
static DWORD DecodeFileFrame(
TCascFile * hf,
PCASC_CKEY_ENTRY pCKeyEntry,
PCASC_FILE_FRAME pFrame,
LPBYTE pbEncoded,
LPBYTE pbDecoded,
DWORD FrameIndex)
{
TCascStorage * hs = hf->hs;
LPBYTE pbWorkBuffer = NULL;
DWORD cbDecodedExpected = 0;
DWORD cbWorkBuffer = 0;
DWORD dwStepCount = 0;
DWORD dwErrCode = ERROR_SUCCESS;
DWORD cbEncoded = pFrame->EncodedSize;
DWORD cbDecoded = pFrame->ContentSize;
bool bWorkComplete = false;
if(pCKeyEntry->Flags & CASC_CE_PLAIN_DATA)
{
assert(pCKeyEntry->ContentSize == pCKeyEntry->EncodedSize);
assert(pCKeyEntry->ContentSize == pFrame->ContentSize);
assert(pFrame->ContentSize == pFrame->EncodedSize);
memcpy(pbDecoded, pbEncoded, pCKeyEntry->ContentSize);
return ERROR_SUCCESS;
}
if(hf->bVerifyIntegrity)
{
if(!CascVerifyDataBlockHash(pbEncoded, pFrame->EncodedSize, pFrame->FrameHash.Value))
return ERROR_FILE_CORRUPT;
}
while(bWorkComplete == false)
{
assert(dwStepCount < 2);
switch(pbEncoded[0])
{
case 'E':
assert(pbWorkBuffer == NULL && cbWorkBuffer == 0);
pbWorkBuffer = CASC_ALLOC<BYTE>(cbEncoded - 1);
cbWorkBuffer = cbEncoded - 1;
if(pbWorkBuffer == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
dwErrCode = CascDecrypt(hs, pbWorkBuffer, &cbWorkBuffer, pbEncoded + 1, cbEncoded - 1, FrameIndex);
if(dwErrCode != ERROR_SUCCESS)
{
bWorkComplete = true;
break;
}
pbEncoded = pbWorkBuffer;
cbEncoded = cbWorkBuffer;
break;
case 'Z':
cbDecodedExpected = cbDecoded;
dwErrCode = CascDecompress(pbDecoded, &cbDecoded, pbEncoded + 1, cbEncoded - 1);
if(cbDecoded < cbDecodedExpected)
memset(pbDecoded + cbDecoded, 0, (cbDecodedExpected - cbDecoded));
bWorkComplete = true;
break;
case 'N': dwErrCode = CascDirectCopy(pbDecoded, &cbDecoded, pbEncoded + 1, cbEncoded - 1);
bWorkComplete = true;
break;
case 'F': default: dwErrCode = ERROR_NOT_SUPPORTED;
bWorkComplete = true;
assert(false);
break;
}
dwStepCount++;
}
if(dwErrCode == ERROR_FILE_ENCRYPTED && hf->bOvercomeEncrypted)
{
memset(pbDecoded, 0, cbDecoded);
dwErrCode = ERROR_SUCCESS;
}
CASC_FREE(pbWorkBuffer);
return dwErrCode;
}
static bool GetFileFullInfo(TCascFile * hf, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded)
{
PCASC_FILE_FULL_INFO pFileInfo;
PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry;
TCascStorage * hs = hf->hs;
DWORD dwErrCode;
dwErrCode = EnsureFileSpanFramesLoaded(hf);
if(dwErrCode != ERROR_SUCCESS)
{
SetCascError(dwErrCode);
return false;
}
pFileInfo = (PCASC_FILE_FULL_INFO)ProbeOutputBuffer(pvFileInfo, cbFileInfo, sizeof(CASC_FILE_FULL_INFO), pcbLengthNeeded);
if(pFileInfo != NULL)
{
CopyMemory16(pFileInfo->CKey, pCKeyEntry->CKey);
CopyMemory16(pFileInfo->EKey, pCKeyEntry->EKey);
pFileInfo->FileDataId = CASC_INVALID_ID;
pFileInfo->LocaleFlags = CASC_INVALID_ID;
pFileInfo->ContentFlags = CASC_INVALID_ID;
CascStrPrintf(pFileInfo->DataFileName, _countof(pFileInfo->DataFileName), "data.%03u", hf->pFileSpan->ArchiveIndex);
pFileInfo->StorageOffset = pCKeyEntry->StorageOffset;
pFileInfo->SegmentOffset = hf->pFileSpan->ArchiveOffs;
pFileInfo->FileNameHash = 0;
pFileInfo->TagBitMask = pCKeyEntry->TagBitMask;
pFileInfo->ContentSize = hf->ContentSize;
pFileInfo->EncodedSize = hf->EncodedSize;
pFileInfo->SegmentIndex = hf->pFileSpan->ArchiveIndex;
pFileInfo->SpanCount = hf->SpanCount;
hs->pRootHandler->GetInfo(pCKeyEntry, pFileInfo);
}
return (pFileInfo != NULL);
}
static bool GetFileSpanInfo(TCascFile * hf, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded)
{
PCASC_FILE_SPAN_INFO pFileInfo;
PCASC_FILE_SPAN pFileSpan = hf->pFileSpan;
PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry;
DWORD dwErrCode = ERROR_SUCCESS;
dwErrCode = EnsureFileSpanFramesLoaded(hf);
if(dwErrCode != ERROR_SUCCESS)
{
SetCascError(dwErrCode);
return false;
}
pFileInfo = (PCASC_FILE_SPAN_INFO)ProbeOutputBuffer(pvFileInfo, cbFileInfo, sizeof(CASC_FILE_SPAN_INFO) * hf->SpanCount, pcbLengthNeeded);
if(pFileInfo != NULL)
{
for(DWORD i = 0; i < hf->SpanCount; i++, pFileInfo++, pFileSpan++, pCKeyEntry++)
{
CopyMemory16(pFileInfo->CKey, pCKeyEntry->CKey);
CopyMemory16(pFileInfo->EKey, pCKeyEntry->EKey);
pFileInfo->StartOffset = pFileSpan->StartOffset;
pFileInfo->EndOffset = pFileSpan->EndOffset;
pFileInfo->ArchiveIndex = pFileSpan->ArchiveIndex;
pFileInfo->ArchiveOffs = pFileSpan->ArchiveOffs;
pFileInfo->HeaderSize = pFileSpan->HeaderSize;
pFileInfo->FrameCount = pFileSpan->FrameCount;
}
}
return (pFileInfo != NULL);
}
static DWORD ReadFile_Cache(TCascFile * hf, LPBYTE pbBuffer, ULONGLONG StartOffset, ULONGLONG EndOffset)
{
if(hf->pbFileCache != NULL && hf->FileCacheStart <= StartOffset && StartOffset < hf->FileCacheEnd)
{
LPBYTE pbStartBlock = hf->pbFileCache + (size_t)(StartOffset - hf->FileCacheStart);
if(EndOffset <= hf->FileCacheEnd)
{
DWORD dwBytesToCopy = (DWORD)(EndOffset - StartOffset);
memcpy(pbBuffer, pbStartBlock, dwBytesToCopy);
return dwBytesToCopy;
}
else
{
DWORD dwBytesToCopy = (DWORD)(hf->FileCacheEnd - StartOffset);
memcpy(pbBuffer, pbStartBlock, dwBytesToCopy);
return dwBytesToCopy;
}
}
return 0;
}
static DWORD ReadFile_WholeFile(TCascFile * hf, LPBYTE pbBuffer)
{
PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry;
PCASC_FILE_SPAN pFileSpan = hf->pFileSpan;
LPBYTE pbSaveBuffer = pbBuffer;
LPBYTE pbEncoded;
LPBYTE pbEncodedPtr;
DWORD dwErrCode;
for(DWORD SpanIndex = 0; SpanIndex < hf->SpanCount; SpanIndex++, pCKeyEntry++, pFileSpan++)
{
ULONGLONG ByteOffset = pFileSpan->ArchiveOffs + pFileSpan->HeaderSize;
DWORD EncodedSize = pCKeyEntry->EncodedSize - pFileSpan->HeaderSize;
pbEncodedPtr = pbEncoded = CASC_ALLOC<BYTE>(EncodedSize);
if(pbEncoded == NULL)
{
SetCascError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
if(FileStream_Read(pFileSpan->pStream, &ByteOffset, pbEncoded, EncodedSize))
{
PCASC_FILE_FRAME pFileFrame = pFileSpan->pFrames;
for(DWORD FrameIndex = 0; FrameIndex < pFileSpan->FrameCount; FrameIndex++, pFileFrame++)
{
dwErrCode = DecodeFileFrame(hf, pCKeyEntry, pFileFrame, pbEncodedPtr, pbBuffer, FrameIndex);
if(dwErrCode != ERROR_SUCCESS)
break;
pbEncodedPtr += pFileFrame->EncodedSize;
pbBuffer += pFileFrame->ContentSize;
}
}
CASC_FREE(pbEncoded);
}
return (DWORD)(pbBuffer - pbSaveBuffer);
}
static DWORD ReadFile_FrameCached(TCascFile * hf, LPBYTE pbBuffer, ULONGLONG StartOffset, ULONGLONG EndOffset)
{
PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry;
PCASC_FILE_SPAN pFileSpan = hf->pFileSpan;
PCASC_FILE_FRAME pFileFrame = NULL;
LPBYTE pbSaveBuffer = pbBuffer;
LPBYTE pbEncoded = NULL;
LPBYTE pbDecoded = NULL;
DWORD dwBytesRead = 0;
DWORD dwErrCode = ERROR_SUCCESS;
bool bNeedFreeDecoded = true;
for(DWORD SpanIndex = 0; SpanIndex < hf->SpanCount; SpanIndex++, pCKeyEntry++, pFileSpan++)
{
if(pFileSpan->StartOffset <= StartOffset && StartOffset < pFileSpan->EndOffset)
{
for(DWORD FrameIndex = 0; FrameIndex < pFileSpan->FrameCount; FrameIndex++)
{
pFileFrame = pFileSpan->pFrames + FrameIndex;
if(pFileFrame->StartOffset <= StartOffset && StartOffset < pFileFrame->EndOffset)
{
if((dwBytesRead + pFileFrame->ContentSize) < dwBytesRead)
{
SetCascError(ERROR_BUFFER_OVERFLOW);
return 0;
}
if(pFileFrame->StartOffset < StartOffset || EndOffset < pFileFrame->EndOffset)
{
if((pbDecoded = CASC_ALLOC<BYTE>(pFileFrame->ContentSize)) == NULL)
{
SetCascError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
bNeedFreeDecoded = true;
}
else
{
bNeedFreeDecoded = false;
pbDecoded = pbBuffer;
}
if((pbEncoded = CASC_ALLOC<BYTE>(pFileFrame->EncodedSize)) == NULL)
{
CASC_FREE(pbDecoded);
SetCascError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
if(FileStream_Read(pFileSpan->pStream, &pFileFrame->DataFileOffset, pbEncoded, pFileFrame->EncodedSize))
{
ULONGLONG EndOfCopy = CASCLIB_MIN(pFileFrame->EndOffset, EndOffset);
DWORD dwBytesToCopy = (DWORD)(EndOfCopy - StartOffset);
dwErrCode = DecodeFileFrame(hf, pCKeyEntry, pFileFrame, pbEncoded, pbDecoded, FrameIndex);
if(dwErrCode == ERROR_SUCCESS)
{
if(pbDecoded != pbBuffer)
memcpy(pbBuffer, pbDecoded + (DWORD)(StartOffset - pFileFrame->StartOffset), dwBytesToCopy);
StartOffset += dwBytesToCopy;
pbBuffer += dwBytesToCopy;
}
}
CASC_FREE(pbEncoded);
if(dwErrCode != ERROR_SUCCESS || StartOffset >= EndOffset)
goto __WorkComplete;
if(bNeedFreeDecoded)
CASC_FREE(pbDecoded);
}
}
}
}
__WorkComplete:
if(dwErrCode == ERROR_SUCCESS)
{
if(pFileFrame != NULL && pbDecoded != NULL && EndOffset < pFileFrame->EndOffset)
{
CASC_FREE(hf->pbFileCache);
hf->FileCacheStart = pFileFrame->StartOffset;
hf->FileCacheEnd = pFileFrame->EndOffset;
hf->pbFileCache = pbDecoded;
pbDecoded = NULL;
}
}
if(bNeedFreeDecoded)
CASC_FREE(pbDecoded);
pbDecoded = NULL;
SetCascError(dwErrCode);
return (DWORD)(pbBuffer - pbSaveBuffer);
}
static DWORD ReadFile_NonCached(TCascFile * hf, LPBYTE pbBuffer, ULONGLONG StartOffset, ULONGLONG EndOffset)
{
if(StartOffset == 0 && EndOffset == hf->ContentSize)
{
return ReadFile_WholeFile(hf, pbBuffer);
}
else
{
assert(false);
}
return 0;
}
bool WINAPI CascGetFileInfo(HANDLE hFile, CASC_FILE_INFO_CLASS InfoClass, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded)
{
TCascFile * hf;
LPBYTE pbOutputValue = NULL;
LPBYTE pbInfoValue = NULL;
size_t cbInfoValue = 0;
if((hf = TCascFile::IsValid(hFile)) == NULL)
{
SetCascError(ERROR_INVALID_HANDLE);
return false;
}
switch(InfoClass)
{
case CascFileContentKey:
if(hf->pCKeyEntry == NULL || (hf->pCKeyEntry->Flags & CASC_CE_HAS_CKEY) == 0)
{
SetCascError(ERROR_NOT_SUPPORTED);
return false;
}
pbInfoValue = hf->pCKeyEntry->CKey;
cbInfoValue = CASC_CKEY_SIZE;
break;
case CascFileEncodedKey:
if(hf->pCKeyEntry == NULL || (hf->pCKeyEntry->Flags & CASC_CE_HAS_EKEY) == 0)
{
SetCascError(ERROR_NOT_SUPPORTED);
return false;
}
pbInfoValue = hf->pCKeyEntry->EKey;
cbInfoValue = CASC_CKEY_SIZE;
break;
case CascFileFullInfo:
return GetFileFullInfo(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded);
case CascFileSpanInfo:
return GetFileSpanInfo(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded);
default:
SetCascError(ERROR_INVALID_PARAMETER);
return false;
}
assert(pbInfoValue != NULL);
assert(cbInfoValue != 0);
pbOutputValue = (LPBYTE)ProbeOutputBuffer(pvFileInfo, cbFileInfo, cbInfoValue, pcbLengthNeeded);
if(pbOutputValue != NULL)
memcpy(pbOutputValue, pbInfoValue, cbInfoValue);
return (pbOutputValue != NULL);
}
bool WINAPI CascSetFileFlags(HANDLE hFile, DWORD dwOpenFlags)
{
TCascFile * hf;
if((hf = TCascFile::IsValid(hFile)) == NULL)
{
SetCascError(ERROR_INVALID_HANDLE);
return false;
}
if(dwOpenFlags & ~CASC_OVERCOME_ENCRYPTED)
{
SetCascError(ERROR_INVALID_PARAMETER);
return false;
}
hf->bOvercomeEncrypted = (dwOpenFlags & CASC_OVERCOME_ENCRYPTED) ? true : false;
return true;
}
bool WINAPI CascGetFileSize64(HANDLE hFile, PULONGLONG PtrFileSize)
{
TCascFile * hf;
DWORD dwErrCode;
if(PtrFileSize == NULL)
{
SetCascError(ERROR_INVALID_PARAMETER);
return false;
}
if((hf = TCascFile::IsValid(hFile)) == NULL)
{
SetCascError(ERROR_INVALID_HANDLE);
return false;
}
if(hf->ContentSize == 0)
{
PtrFileSize[0] = 0;
return true;
}
dwErrCode = EnsureFileSpanFramesLoaded(hf);
if(dwErrCode != ERROR_SUCCESS)
{
SetCascError(dwErrCode);
return false;
}
PtrFileSize[0] = hf->ContentSize;
return true;
}
DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD PtrFileSizeHigh)
{
ULONGLONG FileSize = 0;
if(!CascGetFileSize64(hFile, &FileSize))
return CASC_INVALID_SIZE;
if(PtrFileSizeHigh != NULL)
PtrFileSizeHigh[0] = (DWORD)(FileSize >> 32);
return (DWORD)(FileSize);
}
bool WINAPI CascSetFilePointer64(HANDLE hFile, LONGLONG DistanceToMove, PULONGLONG PtrNewPos, DWORD dwMoveMethod)
{
ULONGLONG FilePosition;
TCascFile * hf;
hf = TCascFile::IsValid(hFile);
if(hf == NULL)
{
SetCascError(ERROR_INVALID_HANDLE);
return false;
}
switch(dwMoveMethod)
{
case FILE_BEGIN:
FilePosition = 0;
break;
case FILE_CURRENT:
FilePosition = hf->FilePointer;
break;
case FILE_END:
FilePosition = hf->ContentSize;
break;
default:
SetCascError(ERROR_INVALID_PARAMETER);
return false;
}
if(DistanceToMove >= 0)
{
if((FilePosition + DistanceToMove) < FilePosition)
{
SetCascError(ERROR_INVALID_PARAMETER);
return false;
}
if((FilePosition = FilePosition + DistanceToMove) > hf->ContentSize)
FilePosition = hf->ContentSize;
hf->FilePointer = FilePosition;
}
else
{
if((FilePosition + DistanceToMove) > FilePosition)
{
SetCascError(ERROR_INVALID_PARAMETER);
return false;
}
if((LONGLONG)(FilePosition = FilePosition + DistanceToMove) < 0)
FilePosition = 0;
hf->FilePointer = FilePosition;
}
if(PtrNewPos != NULL)
PtrNewPos[0] = hf->FilePointer;
return true;
}
DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * PtrFilePosHigh, DWORD dwMoveMethod)
{
ULONGLONG NewPos = 0;
LONGLONG DistanceToMove;
DistanceToMove = (PtrFilePosHigh != NULL) ? MAKE_OFFSET64(PtrFilePosHigh[0], lFilePos) : (LONGLONG)(LONG)lFilePos;
if(!CascSetFilePointer64(hFile, DistanceToMove, &NewPos, dwMoveMethod))
return CASC_INVALID_POS;
if(PtrFilePosHigh != NULL)
PtrFilePosHigh[0] = (LONG)(NewPos >> 32);
return (DWORD)(NewPos);
}
bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDWORD PtrBytesRead)
{
ULONGLONG SaveFilePointer;
ULONGLONG StartOffset;
ULONGLONG EndOffset;
TCascFile * hf;
LPBYTE pbBuffer = (LPBYTE)pvBuffer;
DWORD dwBytesRead1 = 0; DWORD dwBytesRead2 = 0; DWORD dwErrCode;
if(pvBuffer == NULL)
{
SetCascError(ERROR_INVALID_PARAMETER);
return false;
}
if((hf = TCascFile::IsValid(hFile)) == NULL)
{
SetCascError(ERROR_INVALID_HANDLE);
return false;
}
if(hf->ContentSize == 0)
{
PtrBytesRead[0] = 0;
return true;
}
dwErrCode = EnsureFileSpanFramesLoaded(hf);
if(dwErrCode != ERROR_SUCCESS)
{
SetCascError(dwErrCode);
return false;
}
SaveFilePointer = StartOffset = hf->FilePointer;
if(StartOffset >= hf->ContentSize)
{
PtrBytesRead[0] = 0;
return true;
}
EndOffset = StartOffset + dwBytesToRead;
if(EndOffset > hf->ContentSize)
{
EndOffset = hf->ContentSize;
}
if((dwBytesRead1 = ReadFile_Cache(hf, pbBuffer, StartOffset, EndOffset)) != 0)
{
StartOffset = StartOffset + dwBytesRead1;
pbBuffer += dwBytesRead1;
if(StartOffset == EndOffset)
{
if(PtrBytesRead != NULL)
PtrBytesRead[0] = dwBytesRead1;
hf->FilePointer = EndOffset;
return true;
}
}
switch(hf->CacheStrategy)
{
case CascCacheNothing:
dwBytesRead2 = ReadFile_NonCached(hf, pbBuffer, StartOffset, EndOffset);
break;
case CascCacheLastFrame:
dwBytesRead2 = ReadFile_FrameCached(hf, pbBuffer, StartOffset, EndOffset);
break;
default:
break;
}
if(dwBytesRead2 != 0)
{
if(PtrBytesRead != NULL)
PtrBytesRead[0] = (dwBytesRead1 + dwBytesRead2);
hf->FilePointer = StartOffset + dwBytesRead2;
return true;
}
else
{
if(PtrBytesRead != NULL)
PtrBytesRead[0] = 0;
hf->FilePointer = SaveFilePointer;
return (dwBytesToRead == 0);
}
}