#define __CASCLIB_SELF__
#include "CascLib.h"
#include "CascCommon.h"
DWORD LoadApplicationPackageManifestFile(TCascStorage * hs, CASC_FILE_TREE & FileTree, PCASC_CKEY_ENTRY pCKeyEntry, const char * szApmFileName);
DWORD LoadContentManifestFile(TCascStorage * hs, CASC_FILE_TREE & FileTree, PCASC_CKEY_ENTRY pCKeyEntry, const char * szFileName);
typedef struct _APM_HEADER_V3
{
ULONGLONG BuildNumber; ULONGLONG ZeroValue1;
DWORD ZeroValue2;
DWORD PackageCount;
DWORD ZeroValue3;
DWORD EntryCount;
DWORD Checksum;
} APM_HEADER_V3, *PAPM_HEADER_V3;
typedef struct _APM_HEADER_V2
{
ULONGLONG BuildNumber; ULONGLONG ZeroValue1;
DWORD PackageCount;
DWORD ZeroValue2;
DWORD EntryCount;
DWORD Checksum;
} APM_HEADER_V2, *PAPM_HEADER_V2;
typedef struct _APM_HEADER_V1
{
ULONGLONG BuildNumber; DWORD BuildVersion;
DWORD PackageCount;
DWORD EntryCount;
DWORD Checksum;
} APM_HEADER_V1, *PAPM_HEADER_V1;
typedef struct _APM_ENTRY_V1
{
DWORD Index;
DWORD HashA_Lo; DWORD HashA_Hi;
} APM_ENTRY_V1, *PAPM_ENTRY_V1;
typedef struct _APM_ENTRY_V2
{
DWORD Index;
DWORD HashA_Lo; DWORD HashA_Hi;
DWORD HashB_Lo;
DWORD HashB_Hi;
} APM_ENTRY_V2, *PAPM_ENTRY_V2;
typedef struct _APM_PACKAGE_ENTRY_V1
{
ULONGLONG EntryPointGUID; ULONGLONG PrimaryGUID; ULONGLONG SecondaryGUID; ULONGLONG Key; ULONGLONG PackageGUID; ULONGLONG Unknown1;
DWORD Unknown2;
} APM_PACKAGE_ENTRY_V1, *PAPM_PACKAGE_ENTRY_V1;
typedef struct _APM_PACKAGE_ENTRY_V2
{
ULONGLONG PackageGUID; ULONGLONG Unknown1;
DWORD Unknown2;
DWORD Unknown3;
ULONGLONG Unknown4;
} APM_PACKAGE_ENTRY_V2, *PAPM_PACKAGE_ENTRY_V2;
static bool IsManifestFolderName(const char * szFileName, const char * szManifestFolder, size_t nLength)
{
if(!_strnicmp(szFileName, szManifestFolder, nLength))
{
return (szFileName[nLength] == '\\' || szFileName[nLength] == '/');
}
return false;
}
static void BinaryReverse64(LPBYTE GuidReversed, LPBYTE pbGuid)
{
GuidReversed[0] = pbGuid[7];
GuidReversed[1] = pbGuid[6];
GuidReversed[2] = pbGuid[5];
GuidReversed[3] = pbGuid[4];
GuidReversed[4] = pbGuid[3];
GuidReversed[5] = pbGuid[2];
GuidReversed[6] = pbGuid[1];
GuidReversed[7] = pbGuid[0];
}
static const char * ExtractAssetSubString(char * szBuffer, size_t ccBuffer, const char * szPlainName)
{
char * szBufferEnd = szBuffer + ccBuffer - 1;
while(szBuffer < szBufferEnd && szPlainName[0] != 0 && szPlainName[0] != '.' && szPlainName[0] != '_')
*szBuffer++ = *szPlainName++;
if(szBuffer <= szBufferEnd)
szBuffer[0] = 0;
return szPlainName;
}
static const char * AppendAssetSubString(char * szBuffer, size_t ccBuffer, const char * szPlainName)
{
char * szBufferPtr = szBuffer + strlen(szBuffer);
char * szBufferEnd = szBuffer + ccBuffer - 1;
if(szBufferPtr < szBufferEnd)
*szBufferPtr++ = '-';
while(szBufferPtr < szBufferEnd && szPlainName[0] != '_')
*szBufferPtr++ = *szPlainName++;
szBufferPtr[0] = 0;
return szPlainName;
}
size_t BuildAssetFileNameTemplate(
char * szNameTemplate,
size_t ccNameTemplate,
const char * szPrefix,
const char * szAssetName)
{
const char * szFileName = "0000000000000000"; const char * szFileExt = NULL;
char * szBufferEnd = szNameTemplate + ccNameTemplate;
char * szBufferPtr = szNameTemplate;
char * szPlainName;
char szPlatform[64] = {0};
char szLocale[64] = {0};
char szAsset[64] = {0};
while(szAssetName[0] != '.')
{
if(szAssetName[0] == '_')
{
if(szAssetName[1] == 'S' && szAssetName[2] == 'P' && !_strnicmp(szAssetName, "_SPWin_", 7))
{
CascStrCopy(szPlatform, _countof(szPlatform), "Windows");
szAssetName += 6;
continue;
}
if(szAssetName[1] == 'R')
{
szAssetName = AppendAssetSubString(szPlatform, _countof(szPlatform), szAssetName + 1);
continue;
}
if(szAssetName[1] == 'L')
{
szAssetName = ExtractAssetSubString(szLocale, _countof(szLocale), szAssetName + 2);
continue;
}
if(szAssetName[1] == 'E' && szAssetName[2] == 'E')
{
szAssetName += 5;
continue;
}
szAssetName = ExtractAssetSubString(szAsset, _countof(szAsset), szAssetName + 1);
continue;
}
szAssetName++;
}
if(szPrefix && szPrefix[0])
szBufferPtr += CascStrPrintf(szBufferPtr, (szBufferEnd - szBufferPtr), "%s\\", szPrefix);
if(szPlatform[0])
szBufferPtr += CascStrPrintf(szBufferPtr, (szBufferEnd - szBufferPtr), "%s\\", szPlatform);
if(szLocale[0])
szBufferPtr += CascStrPrintf(szBufferPtr, (szBufferEnd - szBufferPtr), "%s\\", szLocale);
if(szAsset[0])
szBufferPtr += CascStrPrintf(szBufferPtr, (szBufferEnd - szBufferPtr), "%s\\", szAsset);
szPlainName = szBufferPtr;
if(szFileName && szFileName[0])
szBufferPtr += CascStrPrintf(szBufferPtr, (szBufferEnd - szBufferPtr), "%s", szFileName);
if(szFileExt && szFileExt[0])
CascStrPrintf(szBufferPtr, (szBufferEnd - szBufferPtr), "%s", szFileExt);
return (szPlainName - szNameTemplate);
}
DWORD InsertAssetFile(
TCascStorage * hs,
CASC_FILE_TREE & FileTree,
char * szFileName,
size_t nPlainName, LPBYTE pbCKey,
LPBYTE pbGuid)
{
PCASC_CKEY_ENTRY pCKeyEntry;
DWORD dwErrCode = ERROR_SUCCESS;
BYTE GuidReversed[8];
if((pCKeyEntry = FindCKeyEntry_CKey(hs, pbCKey)) != NULL)
{
char chSaveChar = szFileName[nPlainName + 16];
BinaryReverse64(GuidReversed, pbGuid);
StringFromBinary(GuidReversed, sizeof(GuidReversed), szFileName + nPlainName);
szFileName[nPlainName + 16] = chSaveChar;
if(FileTree.InsertByName(pCKeyEntry, szFileName) == NULL)
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
return dwErrCode;
}
struct TRootHandler_OW : public TFileTreeRoot
{
TRootHandler_OW() : TFileTreeRoot(0)
{
dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY);
}
DWORD Load(TCascStorage * hs, CASC_CSV & Csv, size_t nFileNameIndex, size_t nCKeyIndex)
{
PCASC_CKEY_ENTRY pCKeyEntry;
size_t nFileCount;
DWORD dwErrCode = ERROR_SUCCESS;
BYTE CKey[MD5_HASH_SIZE];
CASCLIB_UNUSED(hs);
while(Csv.LoadNextLine())
{
const CASC_CSV_COLUMN & FileName = Csv[CSV_ZERO][nFileNameIndex];
const CASC_CSV_COLUMN & CKeyStr = Csv[CSV_ZERO][nCKeyIndex];
if(FileName.szValue && CKeyStr.szValue && CKeyStr.nLength == MD5_STRING_SIZE)
{
if(BinaryFromString(CKeyStr.szValue, MD5_STRING_SIZE, CKey) == ERROR_SUCCESS)
{
if((pCKeyEntry = FindCKeyEntry_CKey(hs, CKey)) != NULL)
{
FileTree.InsertByName(pCKeyEntry, FileName.szValue);
}
}
}
}
nFileCount = FileTree.GetCount();
for(size_t i = 0; i < nFileCount && dwErrCode == ERROR_SUCCESS; i++)
{
PCASC_FILE_NODE pFileNode;
const char * szExtension;
char szFileName[MAX_PATH];
pFileNode = (PCASC_FILE_NODE)FileTree.PathAt(szFileName, _countof(szFileName), i);
if(pFileNode != NULL)
{
if(IsManifestFolderName(szFileName, "Manifest", 8) || IsManifestFolderName(szFileName, "TactManifest", 12))
{
szExtension = GetFileExtension(szFileName);
if(!_stricmp(szExtension, ".cmf"))
{
dwErrCode = LoadContentManifestFile(hs, FileTree, pFileNode->pCKeyEntry, szFileName);
}
else if(!_stricmp(szExtension, ".apm"))
{
dwErrCode = LoadApplicationPackageManifestFile(hs, FileTree, pFileNode->pCKeyEntry, szFileName);
}
}
}
}
return dwErrCode;
}
};
DWORD RootHandler_CreateOverwatch(TCascStorage * hs, CASC_BLOB & RootFile)
{
TRootHandler_OW * pRootHandler = NULL;
CASC_CSV Csv(0, true);
size_t Indices[2];
DWORD dwErrCode;
dwErrCode = Csv.Load(RootFile.pbData, RootFile.cbData);
if(dwErrCode == ERROR_SUCCESS)
{
Indices[0] = Csv.GetColumnIndex("FILENAME");
Indices[1] = Csv.GetColumnIndex("MD5");
if(Indices[0] != CSV_INVALID_INDEX && Indices[1] != CSV_INVALID_INDEX)
{
pRootHandler = new TRootHandler_OW();
if(pRootHandler != NULL)
{
dwErrCode = pRootHandler->Load(hs, Csv, Indices[0], Indices[1]);
if(dwErrCode != ERROR_SUCCESS)
{
delete pRootHandler;
pRootHandler = NULL;
}
}
}
else
{
dwErrCode = ERROR_BAD_FORMAT;
}
}
hs->pRootHandler = pRootHandler;
return dwErrCode;
}