#define __CASCLIB_SELF__
#include "../CascLib.h"
#include "../CascCommon.h"
#include "aes.h"
#include "overwatch.h"
typedef LPBYTE(*GET_KEY)(const CASC_CMF_HEADER & Header, LPBYTE pbKey, int nLength);
typedef LPBYTE(*GET_IV)(const CASC_CMF_HEADER & Header, LPBYTE nameSha1, LPBYTE pbKey, int nLength);
typedef struct _CASC_CMF_KEY_PROVIDER
{
DWORD dwBuildNumber;
GET_KEY PfnGetKey;
GET_IV PfnGetIV;
} CASC_CMF_KEY_PROVIDER;
typedef const CASC_CMF_KEY_PROVIDER *PCASC_CMF_KEY_PROVIDER;
struct TMath
{
template <typename TYPE>
TYPE Max(TYPE value1, TYPE value2)
{
return (value1 > value2) ? value1 : value2;
}
DWORD dwDummy;
} Math;
static uint Constrain(LONGLONG value)
{
return (uint)(value % 0xFFFFFFFFULL);
}
static int SignedMod(LONGLONG p1, LONGLONG p2)
{
int a = (int)p1;
int b = (int)p2;
return (a % b) < 0 ? (a % b + b) : (a % b);
}
#include "cmf-key.cpp"
static PCASC_CMF_KEY_PROVIDER FindCmfKeyProvider(DWORD dwBuildNumber)
{
PCASC_CMF_KEY_PROVIDER pStartEntry = CmfKeyProviders;
PCASC_CMF_KEY_PROVIDER pMidleEntry = NULL;
PCASC_CMF_KEY_PROVIDER pFinalEntry = &CmfKeyProviders[_countof(CmfKeyProviders)];
while(pStartEntry < pFinalEntry)
{
pMidleEntry = pStartEntry + ((pFinalEntry - pStartEntry) / 2);
if(dwBuildNumber == pMidleEntry->dwBuildNumber)
return pMidleEntry;
if(dwBuildNumber > pMidleEntry->dwBuildNumber)
pStartEntry = pMidleEntry + 1;
else
pFinalEntry = pMidleEntry;
}
return NULL;
}
static DWORD DecryptCmfStream(const CASC_CMF_HEADER & Header, const char * szPlainName, LPBYTE pbDataPtr, LPBYTE pbDataEnd)
{
PCASC_CMF_KEY_PROVIDER pKeyProvider;
AES_KEY AesKey;
BYTE RawKey[CASC_AES_KEY_LENGTH];
BYTE RawIV[CASC_AES_IV_LENGTH];
BYTE nameDigest[SHA1_HASH_SIZE];
if((pKeyProvider = FindCmfKeyProvider(Header.m_buildVersion)) == NULL)
return ERROR_FILE_ENCRYPTED;
CascHash_SHA1(szPlainName, strlen(szPlainName), nameDigest);
pKeyProvider->PfnGetKey(Header, RawKey, sizeof(RawKey));
pKeyProvider->PfnGetIV(Header, nameDigest, RawIV, sizeof(RawIV));
AES_set_decrypt_key(RawKey, 256, &AesKey);
AES_cbc_decrypt(pbDataPtr, pbDataPtr, (pbDataEnd - pbDataPtr), &AesKey, RawIV);
return ERROR_SUCCESS;
}
DWORD LoadContentManifestFile(TCascStorage * hs, CASC_FILE_TREE & FileTree, PCASC_CKEY_ENTRY pCKeyEntry, const char * szCmfFileName)
{
CASC_BLOB CmfFile;
const char * szCmfPlainName = GetPlainFileName(szCmfFileName);
DWORD dwErrCode;
if((dwErrCode = LoadInternalFileToMemory(hs, pCKeyEntry, CmfFile)) == ERROR_SUCCESS)
{
PCASC_APM_ENTRY_V2 pApmEntries = NULL;
CASC_CMF_HEADER CmfHeader = {0};
LPBYTE pbDataEnd = CmfFile.pbData + CmfFile.cbData;
LPBYTE pbDataPtr = CmfFile.pbData;
size_t nPlainName;
DWORD dwBuildVersion;
char szFileName[MAX_PATH];
if((pbDataPtr = CaptureInteger32(pbDataPtr, pbDataEnd, &dwBuildVersion)) == NULL)
return ERROR_BAD_FORMAT;
pbDataPtr = CmfFile.pbData;
if(dwBuildVersion > CASC_OVERWATCH_VERSION_148_PTR)
{
CASC_CMF_HEADER_148 * pHeader148;
if((pbDataPtr = CaptureStructure(pbDataPtr, pbDataEnd, &pHeader148)) == NULL)
return ERROR_BAD_FORMAT;
CmfHeader = *pHeader148;
}
else if(dwBuildVersion > CASC_OVERWATCH_VERSION_122_PTR)
{
CASC_CMF_HEADER_122 * pHeader122;
if((pbDataPtr = CaptureStructure(pbDataPtr, pbDataEnd, &pHeader122)) == NULL)
return ERROR_BAD_FORMAT;
CmfHeader = *pHeader122;
}
else
{
CASC_CMF_HEADER_100 * pHeader100;
if((pbDataPtr = CaptureStructure(pbDataPtr, pbDataEnd, &pHeader100)) == NULL)
return ERROR_BAD_FORMAT;
CmfHeader = *pHeader100;
}
if(CmfHeader.IsEncrypted())
{
if((dwErrCode = DecryptCmfStream(CmfHeader, szCmfPlainName, pbDataPtr, pbDataEnd)) != ERROR_SUCCESS)
{
return dwErrCode;
}
}
if((pbDataPtr = CaptureArray(pbDataPtr, pbDataEnd, &pApmEntries, CmfHeader.m_entryCount)) == NULL)
{
return ERROR_BAD_FORMAT;
}
nPlainName = BuildAssetFileNameTemplate(szFileName,
_countof(szFileName),
"ContentManifestFiles",
szCmfPlainName);
if(CmfHeader.m_buildVersion >= 57230)
{
PCASC_CMF_HASH_ENTRY_135 pHashList;
if((pbDataPtr = CaptureArray(pbDataPtr, pbDataEnd, &pHashList, CmfHeader.m_dataCount)) == NULL)
return ERROR_BAD_FORMAT;
dwErrCode = InsertAssetFiles(hs, FileTree, szFileName, nPlainName, pHashList, CmfHeader.m_dataCount);
}
else
{
PCASC_CMF_HASH_ENTRY_100 pHashList;
if((pbDataPtr = CaptureArray(pbDataPtr, pbDataEnd, &pHashList, CmfHeader.m_dataCount)) == NULL)
return ERROR_BAD_FORMAT;
dwErrCode = InsertAssetFiles(hs, FileTree, szFileName, nPlainName, pHashList, CmfHeader.m_dataCount);
}
}
return dwErrCode;
}