mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Dep/CascLib: Update to ladislav-zezula/CascLib@2257774940
This commit is contained in:
@@ -86,6 +86,16 @@ typedef struct _CASC_TAG_ENTRY
|
||||
char TagName[1]; // Tag name. Variable length.
|
||||
} CASC_TAG_ENTRY, *PCASC_TAG_ENTRY;
|
||||
|
||||
// Information about index file
|
||||
typedef struct _CASC_INDEX
|
||||
{
|
||||
LPTSTR szFileName; // Full name of the index file
|
||||
LPBYTE pbFileData; // Loaded content of the index file
|
||||
size_t cbFileData; // Size of the index file
|
||||
DWORD NewSubIndex; // New subindex
|
||||
DWORD OldSubIndex; // Old subindex
|
||||
} CASC_INDEX, *PCASC_INDEX;
|
||||
|
||||
// Normalized header of the index files.
|
||||
// Both version 1 and version 2 are converted to this structure
|
||||
typedef struct _CASC_INDEX_HEADER
|
||||
@@ -292,6 +302,8 @@ struct TCascStorage
|
||||
QUERY_KEY BuildFiles; // List of supported build files
|
||||
|
||||
TFileStream * DataFiles[CASC_MAX_DATA_FILES]; // Array of open data files
|
||||
CASC_INDEX IndexFiles[CASC_INDEX_COUNT]; // Array of found index files
|
||||
CASC_MAP IndexEKeyMap;
|
||||
|
||||
CASC_CKEY_ENTRY EncodingCKey; // Information about ENCODING file
|
||||
CASC_CKEY_ENTRY DownloadCKey; // Information about DOWNLOAD file
|
||||
@@ -317,6 +329,7 @@ struct TCascStorage
|
||||
|
||||
CASC_ARRAY ExtraKeysList; // List additional encryption keys
|
||||
CASC_MAP EncryptionKeys; // Map of encryption keys
|
||||
ULONGLONG LastFailKeyName; // The value of the encryption key that recently was NOT found.
|
||||
|
||||
};
|
||||
|
||||
@@ -465,7 +478,10 @@ DWORD CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LP
|
||||
//-----------------------------------------------------------------------------
|
||||
// Support for index files
|
||||
|
||||
bool CopyEKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry);
|
||||
|
||||
DWORD LoadIndexFiles(TCascStorage * hs);
|
||||
void FreeIndexFiles(TCascStorage * hs);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Support for ROOT file
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// Local structures
|
||||
|
||||
#define CASC_EXTRA_KEYS 0x80
|
||||
#define CASC_KEY_LENGTH 0x10
|
||||
|
||||
typedef struct _CASC_ENCRYPTION_KEY
|
||||
{
|
||||
@@ -48,6 +47,10 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
|
||||
// Starcraft
|
||||
// { 0xD0CAE11366CEEA83ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? }}, // 1.12.3.2609 (build 45364)
|
||||
|
||||
// Warcraft III Reforged beta (build)
|
||||
{ 0x6E4296823E7D561EULL, { 0xC0, 0xBF, 0xA2, 0x94, 0x3A, 0xC3, 0xE9, 0x22, 0x86, 0xE4, 0x44, 0x3E, 0xE3, 0x56, 0x0D, 0x65 }}, // Build 13369
|
||||
{ 0xE04D60E31DDEBF63ULL, { 0x26, 0x3D, 0xB5, 0xC4, 0x02, 0xDA, 0x8D, 0x4D, 0x68, 0x63, 0x09, 0xCB, 0x2E, 0x32, 0x54, 0xD0 }}, // Build 13445
|
||||
|
||||
// Overwatch
|
||||
{ 0xFB680CB6A8BF81F3ULL, { 0x62, 0xD9, 0x0E, 0xFA, 0x7F, 0x36, 0xD7, 0x1C, 0x39, 0x8A, 0xE2, 0xF1, 0xFE, 0x37, 0xBD, 0xB9 } }, // 0.8.0.24919_retailx64 (hardcoded)
|
||||
{ 0x402CD9D8D6BFED98ULL, { 0xAE, 0xB0, 0xEA, 0xDE, 0xA4, 0x76, 0x12, 0xFE, 0x6C, 0x04, 0x1A, 0x03, 0x95, 0x8D, 0xF2, 0x41 } }, // 0.8.0.24919_retailx64 (hardcoded)
|
||||
@@ -208,7 +211,7 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
|
||||
{ 0x0C9ABD5081C06411ULL, { 0x25, 0xA7, 0x7C, 0xD8, 0x00, 0x19, 0x7E, 0xE6, 0xA3, 0x2D, 0xD6, 0x3F, 0x04, 0xE1, 0x15, 0xFA } }, // 230 WOW-26871patch8.0.1_Beta zcf cinematic
|
||||
{ 0x3C6243057F3D9B24ULL, { 0x58, 0xAE, 0x3E, 0x06, 0x42, 0x10, 0xE3, 0xED, 0xF9, 0xC1, 0x25, 0x9C, 0xDE, 0x91, 0x4C, 0x5D } }, // 231 WOW-26871patch8.0.1_Beta ktf cinematic
|
||||
{ 0x7827FBE24427E27DULL, { 0x34, 0xA4, 0x32, 0x04, 0x20, 0x73, 0xCD, 0x0B, 0x51, 0x62, 0x70, 0x68, 0xD2, 0xE0, 0xBD, 0x3E } }, // 232 WOW-26871patch8.0.1_Beta rot cinematic
|
||||
{ 0xFAF9237E1186CF66ULL, { 0xAE, 0x78, 0x78, 0x40, 0x04, 0x1E, 0x9B, 0x41, 0x98, 0xF4, 0x79, 0x71, 0x4D, 0xAD, 0x56, 0x2C } }, // 233 WOW-28048patch8.1.0_PTR encrypted db2 sections (battle pet?)
|
||||
{ 0xFAF9237E1186CF66ULL, { 0xAE, 0x78, 0x78, 0x40, 0x04, 0x1E, 0x9B, 0x41, 0x98, 0xF4, 0x79, 0x71, 0x4D, 0xAD, 0x56, 0x2C } }, // 233 WOW-28048patch8.1.0_PTR DB2 partial encryption test battle pet
|
||||
// { 0x5DD92EE32BBF9ABDULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 234 WOW-27004patch8.0.1_Subm filedataid 2238294
|
||||
{ 0x0B68A7AF5F85F7EEULL, { 0x27, 0xAA, 0x01, 0x10, 0x82, 0xF5, 0xE8, 0xBB, 0xBD, 0x71, 0xD1, 0xBA, 0x04, 0xF6, 0xAB, 0xA4 } }, // 236 WOW-28151patch8.1.0_PTR encrypted06 Flying pig mount
|
||||
// { 0x01531713C83FCC39ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 237 WOW-28151patch8.1.0_PTR fdid 2460009, 2460732
|
||||
@@ -218,31 +221,37 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
|
||||
{ 0xE2F6BD41298A2AB9ULL, { 0xC5, 0xDC, 0x1B, 0xB4, 0x3B, 0x8C, 0xF3, 0xF0, 0x85, 0xD6, 0x98, 0x68, 0x26, 0xB9, 0x28, 0xEC } }, // 241 WOW-28151patch8.1.0_PTR Horde fireworks
|
||||
{ 0x14C4257E557B49A1ULL, { 0x06, 0x4A, 0x97, 0x09, 0xF4, 0x2D, 0x50, 0xCB, 0x5F, 0x8B, 0x94, 0xBC, 0x1A, 0xCF, 0xDD, 0x5D } }, // 242 WOW-28440patch8.1.0_PTR dor cinematic
|
||||
{ 0x1254E65319C6EEFFULL, { 0x79, 0xD2, 0xB3, 0xD1, 0xCC, 0xB0, 0x15, 0x47, 0x4E, 0x71, 0x58, 0x81, 0x38, 0x64, 0xB8, 0xE6 } }, // 243 WOW-28440patch8.1.0_PTR akt cinematic
|
||||
{ 0xC8753773ADF1174CULL, { 0x1E, 0x0E, 0x37, 0xD4, 0x2E, 0xE5, 0xCE, 0x5E, 0x80, 0x67, 0xF0, 0x39, 0x4B, 0x09, 0x05, 0xF2 } }, // 244 WOW-28938patch8.1.5_PTR starts at fdid 2615771, total of 15 fdids
|
||||
{ 0xC8753773ADF1174CULL, { 0x1E, 0x0E, 0x37, 0xD4, 0x2E, 0xE5, 0xCE, 0x5E, 0x80, 0x67, 0xF0, 0x39, 0x4B, 0x09, 0x05, 0xF2 } }, // 244 WOW-28938patch8.1.5_PTR Obsidian Worldbreaker mount & Lil' Nefarian pet
|
||||
// { 0x2170BCAA9FA96E22ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 245 WOW-28938patch8.1.5_PTR alpaca mount
|
||||
// { 0x75485627AA225F4DULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 246 WOW-28938patch8.1.5_PTR fdid 2741546, 2741548, 2741549
|
||||
{ 0x08717B15BF3C7955ULL, { 0x4B, 0x06, 0xBF, 0x9D, 0x17, 0x66, 0x3C, 0xEB, 0x33, 0x12, 0xEA, 0x3C, 0x69, 0xFB, 0xC5, 0xDD } }, // 248 WOW-29220patch8.1.5_PTR inv_encrypted20.blp (fdid 2823166)
|
||||
// { 0xD19DCF7ACA8D96D6ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 249 WOW-30080patch8.2.0_PTR starts at fdid 2843110, total of 10 fdids
|
||||
// { 0xD19DCF7ACA8D96D6ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 249 WOW-30080patch8.2.0_PTR starts at fdid 2843110, 10 files
|
||||
{ 0x9FD609902B4B2E07ULL, { 0xAB, 0xE0, 0xC5, 0xF9, 0xC1, 0x23, 0xE6, 0xE2, 0x4E, 0x7B, 0xEA, 0x43, 0xC2, 0xBF, 0x00, 0xAC } }, // 250 WOW-29418patch8.1.5_PTR Derek Proudmoore cinematic (dpr, 5 files)
|
||||
// { 0xCB26B441FAE4C8CDULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 251 WOW-30080patch8.2.0_PTR fdid 2888623, 2892270, 2892271, 2892272, 2892274, 2892275
|
||||
// { 0xA98C7594F55C02F0ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 252 WOW-30080patch8.2.0_PTR starts at fdid 2921871, total of 24 fdids
|
||||
{ 0x259EE68CD9E76DBAULL, { 0x46, 0x5D, 0x78, 0x4F, 0x10, 0x19, 0x66, 0x1C, 0xCF, 0x41, 0x7F, 0xE4, 0x66, 0x80, 0x12, 0x83 } }, // 253 WOW-30080patch8.2.0_PTR starts at fdid 2957406, total of 30 fdids
|
||||
// { 0x6A026290FBDB3754ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 255 WOW-30080patch8.2.0_PTR starts at fdid 2976294, total of 201 fdids
|
||||
{ 0xA98C7594F55C02F0ULL, { 0xEE, 0xDB, 0x77, 0x47, 0x3B, 0x72, 0x1D, 0xED, 0x62, 0x04, 0xA9, 0x76, 0xC9, 0xA6, 0x61, 0xE7 } }, // 252 WOW-30080patch8.2.0_PTR BlizzCon 2019 - Murloc pets
|
||||
{ 0x259EE68CD9E76DBAULL, { 0x46, 0x5D, 0x78, 0x4F, 0x10, 0x19, 0x66, 0x1C, 0xCF, 0x41, 0x7F, 0xE4, 0x66, 0x80, 0x12, 0x83 } }, // 253 WOW-30080patch8.2.0_PTR Alabaster mounts (30 files)
|
||||
{ 0x6A026290FBDB3754ULL, { 0x3D, 0x2D, 0x62, 0x08, 0x50, 0xA6, 0x76, 0x5D, 0xD5, 0x91, 0x22, 0x4F, 0x60, 0x5B, 0x94, 0x9A } }, // 255 WOW-30080patch8.2.0_PTR BlizzCon 2019 - Wendigo transmog set
|
||||
{ 0xCF72FD04608D36EDULL, { 0xA0, 0xA8, 0x89, 0x97, 0x6D, 0x02, 0xFA, 0x8D, 0x00, 0xF7, 0xAF, 0x00, 0x17, 0xAD, 0x72, 0x1F } }, // 257 WOW-30262patch8.2.0_PTR Azshara Warbringer cinematic (5 files)
|
||||
{ 0x17F07C2E3A45DB3DULL, { 0x6D, 0x38, 0x86, 0xBD, 0xB9, 0x1E, 0x71, 0x5A, 0xE7, 0x18, 0x2D, 0x9F, 0x3A, 0x08, 0xF2, 0xC9 } }, // 258 WOW-30262patch8.2.0_PTR Solesa Naksu Nazjatar phase (17 files)
|
||||
// { 0xDFAB5841B87802B5ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 259 WOW-31337patch8.2.5_PTR starts at fdid 3016206, total of 8 fdids
|
||||
{ 0x17F07C2E3A45DB3DULL, { 0x6D, 0x38, 0x86, 0xBD, 0xB9, 0x1E, 0x71, 0x5A, 0xE7, 0x18, 0x2D, 0x9F, 0x3A, 0x08, 0xF2, 0xC9 } }, // 258 WOW-30262patch8.2.0_PTR Solesa Naksu Nazjatar phase (34 files)
|
||||
// { 0xDFAB5841B87802B5ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 259 WOW-31337patch8.2.5_PTR starts at fdid 3016206, 8 files, 3016206 is a creature
|
||||
{ 0xC050FA06BB0538F6ULL, { 0xC5, 0x52, 0xF5, 0xD0, 0xB7, 0x22, 0x31, 0x50, 0x2D, 0x25, 0x47, 0x31, 0x4E, 0x60, 0x15, 0xF7 } }, // 260 WOW-30495patch8.2.0_PTR Crossroads cinematic (5 files)
|
||||
{ 0xAB5CDD3FC321831FULL, { 0xE1, 0x38, 0x4F, 0x5B, 0x06, 0xEB, 0xBC, 0xD3, 0x33, 0x69, 0x5A, 0xA6, 0xFF, 0xC6, 0x83, 0x18 } }, // 261 WOW-30495patch8.2.0_PTR Azshara kill cinematic (5 files)
|
||||
{ 0xA7B7D1F12395040EULL, { 0x36, 0xAD, 0x3B, 0x31, 0x27, 0x3F, 0x1E, 0xBC, 0xEE, 0x85, 0x20, 0xAA, 0xA7, 0x4B, 0x12, 0xF2 } }, // 262 WOW-30495patch8.2.0_PTR Nazjatar intro cinematics (9 files)
|
||||
// { 0x83A2AB72DD8AE992ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 263 WOW-31337patch8.2.5_PTR starts at fdid 801707, total of 372 fdids
|
||||
// { 0xBEAF567CC45362F0ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 264 WOW-31337patch8.2.5_PTR starts at fdid 1002186, total of 71 fdids
|
||||
// { 0x7BB3A77FD8D14783ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 265 WOW-31337patch8.2.5_PTR fdid 1251882, 1251883
|
||||
// { 0x8F4098E2470FE0C8ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 266 WOW-31337patch8.2.5_PTR starts at fdid 841604, total of 58 fdids
|
||||
// { 0x6AC5C837A2027A6BULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 267 WOW-31337patch8.2.5_PTR starts at fdid 3037834, total of 263 fdids
|
||||
// { 0x302AAD8B1F441D95ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 271 WOW-31337patch8.2.5_PTR starts at fdid 1376212, total of 300 fdids
|
||||
// { 0x5C909F00088734B9ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 272 WOW-31337patch8.2.5_PTR
|
||||
// { 0xF785977C76DE9C77ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 273 WOW-31337patch8.2.5_PTR starts at fdid 3071600, total of 295 fdids
|
||||
// { 0x1CDAF3931871BEC3ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 275 WOW-31337patch8.2.5_PTR fdid 988200, 1140079, 1263818, 1347275
|
||||
{ 0x83A2AB72DD8AE992ULL, { 0x02, 0x3C, 0xFF, 0x06, 0x2B, 0x19, 0xA5, 0x29, 0xB9, 0xF1, 0x4F, 0x9B, 0x7A, 0xAA, 0xC5, 0xBB } }, // 263 WOW-31337patch8.2.5_PTR 8.2.5 War Campaign scenario/models
|
||||
{ 0xBEAF567CC45362F0ULL, { 0x8B, 0xD3, 0xED, 0x79, 0x24, 0x05, 0xD9, 0xEE, 0x74, 0x2B, 0xF6, 0xAF, 0xA9, 0x44, 0x57, 0x8A } }, // 264 WOW-31337patch8.2.5_PTR 8.2.5 War Campaign quests/vo
|
||||
{ 0x7BB3A77FD8D14783ULL, { 0x4C, 0x94, 0xE3, 0x60, 0x9C, 0xFE, 0x0A, 0x82, 0x00, 0x0A, 0x0B, 0xD4, 0x60, 0x69, 0xAC, 0x6F } }, // 265 WOW-31337patch8.2.5_PTR 8.2.5 War Campaign epilogue quests
|
||||
{ 0x8F4098E2470FE0C8ULL, { 0xAA, 0x71, 0x8D, 0x1F, 0x1A, 0x23, 0x07, 0x8D, 0x49, 0xAD, 0x0C, 0x60, 0x6A, 0x72, 0xF3, 0xD5 } }, // 266 WOW-31337patch8.2.5_PTR 8.2.5 War Campaign epilogue in-game cinematic
|
||||
// { 0x6AC5C837A2027A6BULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 267 WOW-31337patch8.2.5_PTR starts at fdid 3037834, 263 files, 3037834 & 3040716 are creatures
|
||||
{ 0x302AAD8B1F441D95ULL, { 0x24, 0xB8, 0x64, 0x38, 0xCF, 0x02, 0x53, 0x86, 0x49, 0xE5, 0xBA, 0x67, 0x2F, 0xD5, 0x99, 0x3A } }, // 271 WOW-31337patch8.2.5_PTR RaF mounts & armor
|
||||
// { 0x5C909F00088734B9ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 272 WOW-31337patch8.2.5_PTR Unused in 8.2.5
|
||||
// { 0xF785977C76DE9C77ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 273 WOW-31337patch8.2.5_PTR starts at fdid 3071600, 313 files, Winter Veil?
|
||||
// { 0x1CDAF3931871BEC3ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 275 WOW-31337patch8.2.5_PTR unknown toy/achievement, 28 files
|
||||
{ 0x814E1AB43F3F9345ULL, { 0xB6, 0x5E, 0x2A, 0x63, 0xA1, 0x16, 0xAA, 0x25, 0x1F, 0xA5, 0xD7, 0xB0, 0xBA, 0xAB, 0xF7, 0x78 } }, // 276 WOW-31599patch8.2.5_PTR The Negotiation cinematic (5 files)
|
||||
{ 0x1FBE97A317FFBEFAULL, { 0xBD, 0x71, 0xF7, 0x8D, 0x43, 0x11, 0x7C, 0x68, 0x72, 0x4B, 0xB6, 0xE0, 0xD9, 0x57, 0x7E, 0x08 } }, // 277 WOW-31599patch8.2.5_PTR Reckoning cinematic (5 files)
|
||||
// { 0x4287F49A5BB366DAULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 279 WOW-31599patch8.2.5_PTR Unused in 8.2.5
|
||||
// { 0x01C82EE0725EDA3AULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 281 WOW-31812patch8.2.5_PTR Unused in 8.2.5
|
||||
// { 0x04C0C50B5BE0CC78ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 282 WOW-31812patch8.2.5_PTR Unused in 8.2.5
|
||||
// { 0xA26FD104489B3DE5ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 283 WOW-31812patch8.2.5_PTR Unused in 8.2.5
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -458,7 +467,10 @@ DWORD CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LP
|
||||
// Check if we know the key
|
||||
pbKey = CascFindKey(hs, KeyName);
|
||||
if(pbKey == NULL)
|
||||
{
|
||||
hs->LastFailKeyName = KeyName;
|
||||
return ERROR_FILE_ENCRYPTED;
|
||||
}
|
||||
|
||||
// Shuffle the Vector with the block index
|
||||
// Note that there's no point to go beyond 32 bits, unless the file has
|
||||
@@ -537,39 +549,39 @@ bool WINAPI CascAddEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPBYTE Key)
|
||||
pEncKey->KeyName = KeyName;
|
||||
|
||||
// Also insert the key to the map
|
||||
if (!hs->EncryptionKeys.InsertObject(pEncKey, &pEncKey->KeyName))
|
||||
{
|
||||
SetLastError(ERROR_ALREADY_EXISTS);
|
||||
return false;
|
||||
}
|
||||
if (!hs->EncryptionKeys.InsertObject(pEncKey, &pEncKey->KeyName))
|
||||
{
|
||||
SetLastError(ERROR_ALREADY_EXISTS);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WINAPI CascAddStringEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPCSTR szKey)
|
||||
{
|
||||
BYTE Key[CASC_KEY_LENGTH];
|
||||
BYTE Key[CASC_KEY_LENGTH];
|
||||
|
||||
// Check the length of the string key
|
||||
if(strlen(szKey) != CASC_KEY_LENGTH * 2)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
// Check the length of the string key
|
||||
if(strlen(szKey) != CASC_KEY_LENGTH * 2)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the string key to the binary array
|
||||
if(ConvertStringToBinary(szKey, CASC_KEY_LENGTH * 2, Key) != ERROR_SUCCESS)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
// Convert the string key to the binary array
|
||||
if(ConvertStringToBinary(szKey, CASC_KEY_LENGTH * 2, Key) != ERROR_SUCCESS)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
return CascAddEncryptionKey(hStorage, KeyName, Key);
|
||||
return CascAddEncryptionKey(hStorage, KeyName, Key);
|
||||
}
|
||||
|
||||
LPBYTE WINAPI CascFindEncryptionKey(HANDLE hStorage, ULONGLONG KeyName)
|
||||
{
|
||||
TCascStorage* hs;
|
||||
TCascStorage * hs;
|
||||
|
||||
// Validate the storage handle
|
||||
hs = TCascStorage::IsValid(hStorage);
|
||||
@@ -581,3 +593,26 @@ LPBYTE WINAPI CascFindEncryptionKey(HANDLE hStorage, ULONGLONG KeyName)
|
||||
|
||||
return CascFindKey(hs, KeyName);
|
||||
}
|
||||
|
||||
bool WINAPI CascGetNotFoundEncryptionKey(HANDLE hStorage, ULONGLONG * KeyName)
|
||||
{
|
||||
TCascStorage * hs;
|
||||
|
||||
// Validate the storage handle
|
||||
if ((hs = TCascStorage::IsValid(hStorage)) == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there was no decryption key error, just return false with ERROR_SUCCESS
|
||||
if(hs->LastFailKeyName == 0)
|
||||
{
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Give the name of the key that failed most recently
|
||||
KeyName[0] = hs->LastFailKeyName;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -503,7 +503,20 @@ static void SetProductCodeName(TCascStorage * hs, LPCSTR szCodeName)
|
||||
}
|
||||
}
|
||||
|
||||
static int GetDefaultLocaleMask(TCascStorage * hs, const CASC_CSV_COLUMN & Column)
|
||||
static DWORD GetDefaultCdnPath(TCascStorage * hs, const CASC_CSV_COLUMN & Column)
|
||||
{
|
||||
TCHAR szCdnPath[MAX_PATH];
|
||||
|
||||
if(hs->szCdnPath == NULL && Column.nLength != 0)
|
||||
{
|
||||
CascStrCopy(szCdnPath, _countof(szCdnPath), Column.szValue);
|
||||
hs->szCdnPath = CascNewStr(szCdnPath);
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static DWORD GetDefaultLocaleMask(TCascStorage * hs, const CASC_CSV_COLUMN & Column)
|
||||
{
|
||||
LPCSTR szTagEnd = Column.szValue + Column.nLength - 4;
|
||||
LPCSTR szTagPtr = Column.szValue;
|
||||
@@ -662,6 +675,9 @@ static DWORD ParseFile_BuildInfo(TCascStorage * hs, CASC_CSV & Csv)
|
||||
if (dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Get the CDN path
|
||||
GetDefaultCdnPath(hs, Csv[nSelected]["CDN Path!STRING:0"]);
|
||||
|
||||
// If we found tags, we can extract language build from it
|
||||
GetDefaultLocaleMask(hs, Csv[nSelected]["Tags!STRING:0"]);
|
||||
|
||||
@@ -1228,7 +1244,9 @@ DWORD GetFileSpanInfo(PCASC_CKEY_ENTRY pCKeyEntry, PULONGLONG PtrContentSize, PU
|
||||
// Sanity check
|
||||
assert(pCKeyEntry->SpanCount != 0);
|
||||
|
||||
// Sum all span size
|
||||
// Sum the file size over all file spans
|
||||
// Note: The first file span, if referenced by the ROOT folder, gets the same size
|
||||
// like the entire file (example: zone\base.xpak, zone\base.xpak_1)
|
||||
for(DWORD i = 0; i < dwSpanCount; i++, pCKeyEntry++)
|
||||
{
|
||||
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
|
||||
@@ -1433,7 +1451,7 @@ LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry,
|
||||
cbFileData = pCKeyEntry->ContentSize;
|
||||
}
|
||||
|
||||
// Retrieve the size of the ENCODING file
|
||||
// Load the entire file to memory
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Allocate space for the ENCODING file
|
||||
|
||||
@@ -15,13 +15,15 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local variables
|
||||
|
||||
static const TCHAR * szAllowedHexChars = _T("0123456789aAbBcCdDeEfF");
|
||||
static const TCHAR * szIndexFormat_V1 = _T("data.i%x%x");
|
||||
static const TCHAR * szIndexFormat_V2 = _T("%02x%08x.idx");
|
||||
static LPCTSTR szAllowedHexChars = _T("0123456789aAbBcCdDeEfF");
|
||||
static LPCTSTR szIndexFormat_V1 = _T("data.i%x%x");
|
||||
static LPCTSTR szIndexFormat_V2 = _T("%02x%08x.idx");
|
||||
|
||||
// Limit for "orphaned" items - those that are in index files, but are not in ENCODING manifest
|
||||
#define CASC_MAX_ORPHANED_ITEMS 0x100
|
||||
|
||||
typedef bool (*EKEY_ENTRY_CALLBACK)(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbEKeyEntry);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
@@ -43,12 +45,11 @@ static bool IsIndexFileName_V2(const TCHAR * szFileName)
|
||||
}
|
||||
|
||||
static bool IndexDirectory_OnFileFound(
|
||||
const TCHAR * szFileName,
|
||||
PDWORD IndexArray,
|
||||
PDWORD OldIndexArray,
|
||||
LPCTSTR szFileName,
|
||||
void * pvContext)
|
||||
{
|
||||
TCascStorage * hs = (TCascStorage *)pvContext;
|
||||
PCASC_INDEX pIndexFile;
|
||||
DWORD IndexValue = 0;
|
||||
DWORD IndexVersion = 0;
|
||||
|
||||
@@ -97,17 +98,17 @@ static bool IndexDirectory_OnFileFound(
|
||||
// The index value must not be greater than 0x0F
|
||||
if(IndexValue >= CASC_INDEX_COUNT)
|
||||
return false;
|
||||
pIndexFile = &hs->IndexFiles[IndexValue];
|
||||
|
||||
// If the new subindex is greater than the previous one,
|
||||
// use this one instead
|
||||
if(IndexVersion > IndexArray[IndexValue])
|
||||
// If the new subindex is greater than the previous one, use this one instead
|
||||
if(IndexVersion > pIndexFile->NewSubIndex)
|
||||
{
|
||||
OldIndexArray[IndexValue] = IndexArray[IndexValue];
|
||||
IndexArray[IndexValue] = IndexVersion;
|
||||
pIndexFile->OldSubIndex = pIndexFile->NewSubIndex;
|
||||
pIndexFile->NewSubIndex = IndexVersion;
|
||||
}
|
||||
else if(IndexVersion > OldIndexArray[IndexValue])
|
||||
else if(IndexVersion > pIndexFile->OldSubIndex)
|
||||
{
|
||||
OldIndexArray[IndexValue] = IndexVersion;
|
||||
pIndexFile->OldSubIndex = IndexVersion;
|
||||
}
|
||||
|
||||
// Note: WoW6 only keeps last two index files
|
||||
@@ -216,7 +217,7 @@ static LPBYTE CaptureGuardedBlock3(LPBYTE pbFileData, LPBYTE pbFileEnd, size_t E
|
||||
// Give the output
|
||||
return (LPBYTE)(PtrEntryHash + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
static bool CaptureIndexEntry(CASC_INDEX_HEADER & InHeader, PCASC_EKEY_ENTRY pEKeyEntry, LPBYTE pbEKeyEntry)
|
||||
{
|
||||
// Copy the EKey of the variable length
|
||||
@@ -278,24 +279,17 @@ static void InsertCKeyEntry(TCascStorage * hs, CASC_EKEY_ENTRY & EKeyEntry, DWOR
|
||||
// Add the extra flag
|
||||
pCKeyEntry->Flags |= Flags;
|
||||
}
|
||||
|
||||
static DWORD LoadIndexItems(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbEKeyEntry, LPBYTE pbEKeyEnd)
|
||||
*/
|
||||
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)
|
||||
{
|
||||
CASC_EKEY_ENTRY EKeyEntry;
|
||||
|
||||
// Capture the index entry and verify it.
|
||||
if(CaptureIndexEntry(InHeader, &EKeyEntry, pbEKeyEntry))
|
||||
{
|
||||
// DOWNLOAD in HOTS
|
||||
//BREAK_ON_XKEY3(EKeyEntry.EKey, 0x09, 0xF3, 0xCD);
|
||||
|
||||
// Insert the index entry to the central table
|
||||
InsertCKeyEntry(hs, EKeyEntry, CASC_CE_FILE_IS_LOCAL);
|
||||
}
|
||||
// DOWNLOAD in HOTS
|
||||
//BREAK_ON_XKEY3(EKeyEntry.EKey, 0x09, 0xF3, 0xCD);
|
||||
if(!PfnEKeyEntry(hs, InHeader, pbEKeyEntry))
|
||||
return ERROR_INDEX_PARSING_DONE;
|
||||
|
||||
pbEKeyEntry += EntryLength;
|
||||
}
|
||||
@@ -303,7 +297,7 @@ static DWORD LoadIndexItems(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPB
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static DWORD CaptureIndexHeader_V1(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
|
||||
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;
|
||||
@@ -363,7 +357,7 @@ static DWORD CaptureIndexHeader_V1(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileDa
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static DWORD CaptureIndexHeader_V2(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
|
||||
static DWORD CaptureIndexHeader_V2(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, size_t cbFileData, DWORD BucketIndex)
|
||||
{
|
||||
PFILE_INDEX_HEADER_V2 pIndexHeader;
|
||||
LPBYTE pbFileEnd = pbFileData + cbFileData;
|
||||
@@ -397,7 +391,7 @@ static DWORD CaptureIndexHeader_V2(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileDa
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static DWORD LoadIndexFile_V1(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData)
|
||||
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;
|
||||
|
||||
@@ -405,10 +399,10 @@ static DWORD LoadIndexFile_V1(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, L
|
||||
SaveFileOffsetBitsAndEKeyLength(hs, InHeader.FileOffsetBits, InHeader.EKeyLength);
|
||||
|
||||
// Load the entries from a continuous array
|
||||
return LoadIndexItems(hs, InHeader, pbEKeyEntries, pbFileData + cbFileData);
|
||||
return LoadIndexItems(hs, InHeader, PfnEKeyEntry, pbEKeyEntries, pbFileData + cbFileData);
|
||||
}
|
||||
|
||||
static DWORD LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD 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;
|
||||
@@ -427,7 +421,7 @@ static DWORD LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, L
|
||||
InHeader.HeaderPadding += sizeof(FILE_INDEX_GUARDED_BLOCK);
|
||||
|
||||
// Load the continuous array of EKeys
|
||||
return LoadIndexItems(hs, InHeader, pbEKeyEntry, pbEKeyEntry + BlockSize);
|
||||
return LoadIndexItems(hs, InHeader, PfnEKeyEntry, pbEKeyEntry, pbEKeyEntry + BlockSize);
|
||||
}
|
||||
|
||||
// Get the pointer to the second block of EKey entries.
|
||||
@@ -446,8 +440,6 @@ static DWORD LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, L
|
||||
|
||||
while(pbEKeyEntry < pbEndPage)
|
||||
{
|
||||
CASC_EKEY_ENTRY EKeyEntry;
|
||||
|
||||
// Check the EKey entry protected by 32-bit hash
|
||||
if((pbEKeyEntry = CaptureGuardedBlock3(pbEKeyEntry, pbEndPage, InHeader.EntryLength)) == NULL)
|
||||
break;
|
||||
@@ -455,13 +447,10 @@ static DWORD LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, L
|
||||
// CASC\\0001: Encoding
|
||||
//BREAK_ON_XKEY3(pbEKeyEntry, 0xbc, 0xe8, 0x23);
|
||||
|
||||
// Capture the index entry and verify it.
|
||||
if(CaptureIndexEntry(InHeader, &EKeyEntry, pbEKeyEntry))
|
||||
{
|
||||
InsertCKeyEntry(hs, EKeyEntry, CASC_CE_FILE_IS_LOCAL);
|
||||
}
|
||||
// Call the EKey entry callback
|
||||
if(!PfnEKeyEntry(hs, InHeader, pbEKeyEntry))
|
||||
return ERROR_INDEX_PARSING_DONE;
|
||||
|
||||
// Move to the next entry
|
||||
pbEKeyEntry += AlignedLength;
|
||||
}
|
||||
|
||||
@@ -474,80 +463,95 @@ static DWORD LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, L
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
static DWORD LoadIndexFile(TCascStorage * hs, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
|
||||
static DWORD LoadIndexFile(TCascStorage * hs, EKEY_ENTRY_CALLBACK PfnEKeyEntry, LPBYTE pbFileData, size_t cbFileData, DWORD BucketIndex)
|
||||
{
|
||||
CASC_INDEX_HEADER InHeader;
|
||||
|
||||
// Check for CASC version 2
|
||||
if(CaptureIndexHeader_V2(InHeader, pbFileData, cbFileData, BucketIndex) == ERROR_SUCCESS)
|
||||
return LoadIndexFile_V2(hs, InHeader, pbFileData, cbFileData);
|
||||
return LoadIndexFile_V2(hs, InHeader, PfnEKeyEntry, pbFileData, cbFileData);
|
||||
|
||||
// Check for CASC index version 1
|
||||
if(CaptureIndexHeader_V1(InHeader, pbFileData, cbFileData, BucketIndex) == ERROR_SUCCESS)
|
||||
return LoadIndexFile_V1(hs, InHeader, pbFileData, cbFileData);
|
||||
return LoadIndexFile_V1(hs, InHeader, PfnEKeyEntry, pbFileData, cbFileData);
|
||||
|
||||
// Should never happen
|
||||
assert(false);
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
static DWORD LoadIndexFile(TCascStorage * hs, const TCHAR * szFileName, DWORD BucketIndex)
|
||||
// Checks the EKey entry for EKey of the ENCODING manifest
|
||||
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)
|
||||
{
|
||||
LPBYTE pbFileData;
|
||||
DWORD cbFileData;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// WoW6 actually reads THE ENTIRE file to memory. Verified on Mac build (x64).
|
||||
pbFileData = LoadFileToMemory(szFileName, &cbFileData);
|
||||
if(pbFileData && cbFileData)
|
||||
// Load each index file
|
||||
for(DWORD i = 0; i < CASC_INDEX_COUNT; i++)
|
||||
{
|
||||
// Parse and load the index file
|
||||
dwErrCode = LoadIndexFile(hs, pbFileData, cbFileData, BucketIndex);
|
||||
CASC_FREE(pbFileData);
|
||||
}
|
||||
else
|
||||
{
|
||||
dwErrCode = GetLastError();
|
||||
CASC_INDEX & IndexFile = hs->IndexFiles[i];
|
||||
|
||||
// Inform the user about what we are doing
|
||||
if(InvokeProgressCallback(hs, "Loading index files", NULL, i, CASC_INDEX_COUNT))
|
||||
{
|
||||
dwErrCode = ERROR_CANCELLED;
|
||||
break;
|
||||
}
|
||||
|
||||
// Load the index file
|
||||
if((dwErrCode = LoadIndexFile(hs, PfnEKeyEntry, IndexFile.pbFileData, IndexFile.cbFileData, i)) != ERROR_SUCCESS)
|
||||
break;
|
||||
}
|
||||
|
||||
// Swallow the "done parsing" error
|
||||
if(dwErrCode == ERROR_INDEX_PARSING_DONE)
|
||||
dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// Remember the number of files that are present locally
|
||||
hs->LocalFiles = hs->CKeyArray.ItemCount();
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
static DWORD LoadLocalIndexFiles(TCascStorage * hs)
|
||||
{
|
||||
TCHAR * szFileName;
|
||||
DWORD OldIndexArray[CASC_INDEX_COUNT];
|
||||
DWORD IndexArray[CASC_INDEX_COUNT];
|
||||
ULONGLONG TotalSize = 0;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Scan all index files and load contained EKEY entries
|
||||
memset(OldIndexArray, 0, sizeof(OldIndexArray));
|
||||
memset(IndexArray, 0, sizeof(IndexArray));
|
||||
dwErrCode = ScanIndexDirectory(hs->szIndexPath, IndexDirectory_OnFileFound, IndexArray, OldIndexArray, hs);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
// Inform the user about what we are doing
|
||||
if(InvokeProgressCallback(hs, "Loading index files", NULL, 0, 0))
|
||||
return ERROR_CANCELLED;
|
||||
|
||||
// Perform the directory scan
|
||||
if((dwErrCode = ScanIndexDirectory(hs->szIndexPath, IndexDirectory_OnFileFound, hs)) == ERROR_SUCCESS)
|
||||
{
|
||||
// Load each index file
|
||||
for(DWORD i = 0; i < CASC_INDEX_COUNT; i++)
|
||||
{
|
||||
// Create the name of the index file
|
||||
if((szFileName = CreateIndexFileName(hs, i, IndexArray[i])) != NULL)
|
||||
{
|
||||
// Inform the user about what we are doing
|
||||
if(InvokeProgressCallback(hs, "Loading index files", NULL, i, CASC_INDEX_COUNT))
|
||||
{
|
||||
dwErrCode = ERROR_CANCELLED;
|
||||
break;
|
||||
}
|
||||
CASC_INDEX & IndexFile = hs->IndexFiles[i];
|
||||
DWORD cbFileData = 0;
|
||||
|
||||
// Load the index file
|
||||
if((dwErrCode = LoadIndexFile(hs, szFileName, i)) != ERROR_SUCCESS)
|
||||
break;
|
||||
CASC_FREE(szFileName);
|
||||
}
|
||||
// Create the file name
|
||||
if((IndexFile.szFileName = CreateIndexFileName(hs, i, IndexFile.NewSubIndex)) == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// WoW6 actually reads THE ENTIRE file to memory. Verified on Mac build (x64).
|
||||
if((IndexFile.pbFileData = LoadFileToMemory(IndexFile.szFileName, &cbFileData)) == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
IndexFile.cbFileData = cbFileData;
|
||||
TotalSize += cbFileData;
|
||||
}
|
||||
|
||||
// Remember the number of files that are present locally
|
||||
hs->LocalFiles = hs->CKeyArray.ItemCount();
|
||||
// Build the map of EKey -> IndexEKeyEntry
|
||||
dwErrCode = hs->IndexEKeyMap.Create((size_t)(TotalSize / sizeof(FILE_EKEY_ENTRY)), CASC_EKEY_SIZE, 0);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
dwErrCode = ProcessLocalIndexFiles(hs, InsertEncodingEKeyToMap);
|
||||
}
|
||||
}
|
||||
|
||||
return dwErrCode;
|
||||
@@ -785,9 +789,31 @@ static DWORD LoadArchiveIndexFiles(TCascStorage * hs)
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
bool CopyEKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry)
|
||||
{
|
||||
LPBYTE pbEKeyEntry = (LPBYTE)hs->IndexEKeyMap.FindObject(pCKeyEntry->EKey);
|
||||
|
||||
// Don't do this on online storages
|
||||
if(!(hs->dwFeatures & CASC_FEATURE_ONLINE))
|
||||
{
|
||||
// If the file was found, then copy the content to the CKey entry
|
||||
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)
|
||||
{
|
||||
if (hs->dwFeatures & CASC_FEATURE_ONLINE)
|
||||
// For local storages, load the index files from the disk
|
||||
// For online storages, load the index files from the cache / internet
|
||||
if(hs->dwFeatures & CASC_FEATURE_ONLINE)
|
||||
{
|
||||
return LoadArchiveIndexFiles(hs);
|
||||
}
|
||||
@@ -795,4 +821,23 @@ DWORD LoadIndexFiles(TCascStorage * hs)
|
||||
{
|
||||
return LoadLocalIndexFiles(hs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FreeIndexFiles(TCascStorage * hs)
|
||||
{
|
||||
// Free the map of EKey -> Index Ekey item
|
||||
hs->IndexEKeyMap.Free();
|
||||
|
||||
// Free all loaded index files
|
||||
for(size_t i = 0; i < CASC_INDEX_COUNT; i++)
|
||||
{
|
||||
CASC_INDEX & IndexFile = hs->IndexFiles[i];
|
||||
|
||||
// Free the file data
|
||||
CASC_FREE(IndexFile.pbFileData);
|
||||
IndexFile.cbFileData = 0;
|
||||
|
||||
// Free the file name
|
||||
CASC_FREE(IndexFile.szFileName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@ EXPORTS
|
||||
CascOpenStorageEx
|
||||
CascOpenOnlineStorage
|
||||
CascGetStorageInfo
|
||||
CascAddEncryptionKey
|
||||
CascAddStringEncryptionKey
|
||||
CascFindEncryptionKey
|
||||
CascCloseStorage
|
||||
|
||||
CascOpenFile
|
||||
@@ -31,5 +28,10 @@ EXPORTS
|
||||
CascFindNextFile
|
||||
CascFindClose
|
||||
|
||||
CascAddEncryptionKey
|
||||
CascAddStringEncryptionKey
|
||||
CascFindEncryptionKey
|
||||
CascGetNotFoundEncryptionKey
|
||||
|
||||
GetLastError=Kernel32.GetLastError
|
||||
SetLastError=Kernel32.SetLastError
|
||||
|
||||
@@ -86,6 +86,7 @@ extern "C" {
|
||||
#define CASC_OVERCOME_ENCRYPTED 0x00000020 // When CascReadFile encounters a block encrypted with a key that is missing, the block is filled with zeros and returned as success
|
||||
|
||||
#define CASC_LOCALE_ALL 0xFFFFFFFF
|
||||
#define CASC_LOCALE_ALL_WOW 0x0001F3F6 // All except enCN and enTW
|
||||
#define CASC_LOCALE_NONE 0x00000000
|
||||
#define CASC_LOCALE_UNKNOWN1 0x00000001
|
||||
#define CASC_LOCALE_ENUS 0x00000002
|
||||
@@ -142,6 +143,9 @@ extern "C" {
|
||||
#define CASC_FILE_DATA_ID(FileDataId) ((LPCSTR)(size_t)FileDataId)
|
||||
#define CASC_FILE_DATA_ID_FROM_STRING(szFileName) ((DWORD)(size_t)szFileName)
|
||||
|
||||
// Maximum length of encryption key
|
||||
#define CASC_KEY_LENGTH 0x10
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structures
|
||||
|
||||
@@ -342,9 +346,6 @@ bool WINAPI CascOpenStorageEx(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs,
|
||||
bool WINAPI CascOpenStorage(LPCTSTR szParams, DWORD dwLocaleMask, HANDLE * phStorage);
|
||||
bool WINAPI CascOpenOnlineStorage(LPCTSTR szParams, DWORD dwLocaleMask, HANDLE * phStorage);
|
||||
bool WINAPI CascGetStorageInfo(HANDLE hStorage, CASC_STORAGE_INFO_CLASS InfoClass, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded);
|
||||
bool WINAPI CascAddEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPBYTE Key);
|
||||
bool WINAPI CascAddStringEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPCSTR szKey);
|
||||
LPBYTE WINAPI CascFindEncryptionKey(HANDLE hStorage, ULONGLONG KeyName);
|
||||
bool WINAPI CascCloseStorage(HANDLE hStorage);
|
||||
|
||||
bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocaleFlags, DWORD dwOpenFlags, HANDLE * PtrFileHandle);
|
||||
@@ -362,6 +363,11 @@ HANDLE WINAPI CascFindFirstFile(HANDLE hStorage, LPCSTR szMask, PCASC_FIND_DATA
|
||||
bool WINAPI CascFindNextFile(HANDLE hFind, PCASC_FIND_DATA pFindData);
|
||||
bool WINAPI CascFindClose(HANDLE hFind);
|
||||
|
||||
bool WINAPI CascAddEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPBYTE Key);
|
||||
bool WINAPI CascAddStringEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPCSTR szKey);
|
||||
LPBYTE WINAPI CascFindEncryptionKey(HANDLE hStorage, ULONGLONG KeyName);
|
||||
bool WINAPI CascGetNotFoundEncryptionKey(HANDLE hStorage, ULONGLONG * KeyName);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GetLastError/SetLastError support for non-Windows platform
|
||||
|
||||
|
||||
@@ -20,6 +20,27 @@
|
||||
// Limit for "additional" items in CKey table
|
||||
#define CASC_MAX_EXTRA_ITEMS 0x40
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DEBUG functions
|
||||
|
||||
//#define CHECKED_KEY "2a378c"
|
||||
|
||||
#if defined(_DEBUG) && defined(CHECKED_KEY)
|
||||
|
||||
inline bool CheckForXKey(LPBYTE XKey)
|
||||
{
|
||||
BYTE CheckedKey[4];
|
||||
ConvertStringToBinary(CHECKED_KEY, 6, CheckedKey);
|
||||
return (XKey[0] == CheckedKey[0] && XKey[1] == CheckedKey[1] && XKey[2] == CheckedKey[2]);
|
||||
}
|
||||
#define BREAK_ON_WATCHED(XKey) if(CheckForXKey((LPBYTE)XKey)) { __debugbreak(); }
|
||||
|
||||
#else
|
||||
|
||||
#define BREAK_ON_WATCHED(XKey) { /* NOTHING */ }
|
||||
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// TCascStorage class functions
|
||||
|
||||
@@ -35,6 +56,7 @@ TCascStorage::TCascStorage()
|
||||
szRegion = NULL;
|
||||
|
||||
memset(DataFiles, 0, sizeof(DataFiles));
|
||||
memset(IndexFiles, 0, sizeof(IndexFiles));
|
||||
dwBuildNumber = 0;
|
||||
dwFeatures = 0;
|
||||
BuildFileType = CascBuildNone;
|
||||
@@ -56,6 +78,9 @@ TCascStorage::~TCascStorage()
|
||||
DataFiles[i] = NULL;
|
||||
}
|
||||
|
||||
// Cleanup space occupied by index files
|
||||
FreeIndexFiles(this);
|
||||
|
||||
// Free the file paths
|
||||
CASC_FREE(szDataPath);
|
||||
CASC_FREE(szRootPath);
|
||||
@@ -113,9 +138,9 @@ void * ProbeOutputBuffer(void * pvBuffer, size_t cbLength, size_t cbMinLength, s
|
||||
return pvBuffer;
|
||||
}
|
||||
|
||||
static TCHAR * CheckForIndexDirectory(TCascStorage * hs, const TCHAR * szSubDir)
|
||||
static LPTSTR CheckForIndexDirectory(TCascStorage * hs, LPCTSTR szSubDir)
|
||||
{
|
||||
TCHAR * szIndexPath;
|
||||
LPTSTR szIndexPath;
|
||||
|
||||
// Combine the index path
|
||||
szIndexPath = CombinePath(hs->szDataPath, szSubDir);
|
||||
@@ -132,6 +157,9 @@ static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, CASC_CKEY_ENTRY & CKe
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry = NULL;
|
||||
|
||||
// Stop on file-of-interest
|
||||
BREAK_ON_WATCHED(CKeyEntry.EKey);
|
||||
|
||||
// Skip entries without any key
|
||||
if(CKeyEntry.Flags & (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY))
|
||||
{
|
||||
@@ -171,14 +199,14 @@ static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, PFILE_CKEY_ENTRY pFil
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
|
||||
// Check whether the entry is already there
|
||||
if((pCKeyEntry = FindCKeyEntry_EKey(hs, pFileEntry->EKey)) == NULL)
|
||||
{
|
||||
// Insert a new entry to the array. DO NOT ALLOW enlarge array here
|
||||
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false);
|
||||
if(pCKeyEntry == NULL)
|
||||
return NULL;
|
||||
// Stop on file-of-interest
|
||||
BREAK_ON_WATCHED(pFileEntry->EKey);
|
||||
|
||||
// Insert a new entry to the array. DO NOT ALLOW enlarge array here
|
||||
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false);
|
||||
if(pCKeyEntry != NULL)
|
||||
{
|
||||
// Initialize the entry
|
||||
CopyMemory16(pCKeyEntry->CKey, pFileEntry->CKey);
|
||||
CopyMemory16(pCKeyEntry->EKey, pFileEntry->EKey);
|
||||
pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64;
|
||||
@@ -190,24 +218,16 @@ static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, PFILE_CKEY_ENTRY pFil
|
||||
pCKeyEntry->SpanCount = 1;
|
||||
pCKeyEntry->Priority = 0;
|
||||
|
||||
// Copy the information from index files to the CKey entry
|
||||
CopyEKeyEntry(hs, pCKeyEntry);
|
||||
|
||||
// Insert the item into both maps
|
||||
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
|
||||
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Supply both CKey and EKey. Rewrite EKey regardless, because ENCODING manifest contains a full one
|
||||
CopyMemory16(pCKeyEntry->CKey, pFileEntry->CKey);
|
||||
CopyMemory16(pCKeyEntry->EKey, pFileEntry->EKey);
|
||||
|
||||
// Supply the content size
|
||||
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
|
||||
pCKeyEntry->ContentSize = ConvertBytesToInteger_4(pFileEntry->ContentSize);
|
||||
pCKeyEntry->Flags |= CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_IN_ENCODING;
|
||||
pCKeyEntry->Flags &= ~CASC_CE_HAS_EKEY_PARTIAL;
|
||||
|
||||
// Insert the item into CKey map
|
||||
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return pCKeyEntry;
|
||||
@@ -218,13 +238,19 @@ static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, CASC_DOWNLOAD_ENTRY &
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
|
||||
// Stop on file-of-interest
|
||||
BREAK_ON_WATCHED(DlEntry.EKey);
|
||||
|
||||
// Check whether the entry is already there
|
||||
if((pCKeyEntry = FindCKeyEntry_EKey(hs, DlEntry.EKey)) == NULL)
|
||||
{
|
||||
// Insert dummy CKey entry to the array. DO NOT allow to enlarge the array
|
||||
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false);
|
||||
if(pCKeyEntry == NULL)
|
||||
{
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Copy the entry
|
||||
ZeroMemory16(pCKeyEntry->CKey);
|
||||
@@ -237,6 +263,9 @@ static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, CASC_DOWNLOAD_ENTRY &
|
||||
pCKeyEntry->RefCount = 0;
|
||||
pCKeyEntry->SpanCount = 1;
|
||||
|
||||
// Copy the information from index files to the CKey entry
|
||||
CopyEKeyEntry(hs, pCKeyEntry);
|
||||
|
||||
// Insert the entry to the map. Only insert it to the EKey map, as there is no CKey present
|
||||
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
|
||||
}
|
||||
@@ -278,25 +307,34 @@ static DWORD CopyBuildFileItemsToCKeyArray(TCascStorage * hs)
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// Estimate the total number of files, so we won't have to re-allocate the array
|
||||
// Estimate the total number of files, so we won't have to re-allocate arrays and maps
|
||||
// and thus speed-up storage loading. In theory, we could guess the file count by
|
||||
// measuring size of ENCODING or DOWNLOAD manifests.
|
||||
static size_t GetEstimatedNumberOfFiles(TCascStorage * hs)
|
||||
{
|
||||
size_t nNumberOfFiles1 = 0;
|
||||
size_t nNumberOfFiles2 = 0;
|
||||
|
||||
// If we know the size of DOWNLOAD at this point, we estimate number of files from it.
|
||||
// Size of one entry in DOWNLOAD is at least 26 bytes. This is the most reliable method.
|
||||
// Size of one entry in DOWNLOAD is at least 22 bytes. This is the most reliable method.
|
||||
// However, for some online storages ("agent"), this is a very small value
|
||||
if(hs->DownloadCKey.ContentSize != CASC_INVALID_SIZE)
|
||||
return (hs->DownloadCKey.ContentSize / 26) + CASC_MAX_EXTRA_ITEMS;
|
||||
nNumberOfFiles1 = (hs->DownloadCKey.ContentSize / sizeof(FILE_DOWNLOAD_ENTRY)) + CASC_MAX_EXTRA_ITEMS;
|
||||
|
||||
// If we know the size of ENCODING at this point, we estimate number of files from it.
|
||||
// Size of one entry in ENCODING is at least 38 bytes. This method fails on storages
|
||||
// with TVFS file system, as ENCODING only contains a small subset of file.
|
||||
// Fortunately, all known TVFS-based storages have "download-size" present
|
||||
if(hs->EncodingCKey.ContentSize != CASC_INVALID_SIZE)
|
||||
return (hs->EncodingCKey.ContentSize / 26) + CASC_MAX_EXTRA_ITEMS;
|
||||
nNumberOfFiles2 = (hs->EncodingCKey.ContentSize / sizeof(FILE_CKEY_ENTRY)) + CASC_MAX_EXTRA_ITEMS;
|
||||
|
||||
// By default, it's gonna be half a million, which is the maximum observed number of files
|
||||
// for all older storages (HOTS before 39445, WoW before 19116)
|
||||
return 500000;
|
||||
// Do we know any of them?
|
||||
if(nNumberOfFiles1 || nNumberOfFiles2)
|
||||
return CASCLIB_MAX(nNumberOfFiles1, nNumberOfFiles2);
|
||||
|
||||
// Older storages (HOTS before 39445, WoW before 19116) don't state sizes of ENCODING
|
||||
// and DOWNLOAD in the Build Config files. Solution: Assume there is max 1M of files
|
||||
return 1000000;
|
||||
}
|
||||
|
||||
static DWORD InitCKeyArray(TCascStorage * hs)
|
||||
@@ -373,7 +411,7 @@ static int LoadEncodingCKeyPage(TCascStorage * hs, CASC_ENCODING_HEADER & EnHead
|
||||
// Example of a file entry with multiple EKeys:
|
||||
// Overwatch build 24919, CKey: 0e 90 94 fa d2 cb 85 ac d0 7c ea 09 f9 c5 ba 00
|
||||
// BREAKIF(pFileEntry->EKeyCount > 1);
|
||||
// BREAK_ON_XKEY3(pFileEntry->EKey, 0x09, 0xF3, 0xCD);
|
||||
// BREAK_ON_XKEY3(pFileEntry->CKey, 0x34, 0x82, 0x1f);
|
||||
|
||||
// Insert the entry to the central CKey table
|
||||
InsertCKeyEntry(hs, pFileEntry);
|
||||
@@ -386,7 +424,7 @@ static int LoadEncodingCKeyPage(TCascStorage * hs, CASC_ENCODING_HEADER & EnHead
|
||||
|
||||
static int LoadEncodingManifest(TCascStorage * hs)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->EncodingCKey.CKey);
|
||||
CASC_CKEY_ENTRY & CKeyEntry = hs->EncodingCKey;
|
||||
LPBYTE pbEncodingFile;
|
||||
DWORD cbEncodingFile = 0;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
@@ -395,8 +433,12 @@ static int LoadEncodingManifest(TCascStorage * hs)
|
||||
if(InvokeProgressCallback(hs, "Loading ENCODING manifest", NULL, 0, 0))
|
||||
return ERROR_CANCELLED;
|
||||
|
||||
// Fill-in the information from the index entry
|
||||
if(!CopyEKeyEntry(hs, &CKeyEntry))
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
|
||||
// Load the entire encoding file to memory
|
||||
pbEncodingFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbEncodingFile);
|
||||
pbEncodingFile = LoadInternalFileToMemory(hs, &hs->EncodingCKey, &cbEncodingFile);
|
||||
if(pbEncodingFile != NULL && cbEncodingFile != 0)
|
||||
{
|
||||
CASC_ENCODING_HEADER EnHeader;
|
||||
@@ -451,12 +493,6 @@ static int LoadEncodingManifest(TCascStorage * hs)
|
||||
dwErrCode = CopyBuildFileItemsToCKeyArray(hs);
|
||||
}
|
||||
|
||||
// Now supply all the entries from the index files
|
||||
//if(dwErrCode == ERROR_SUCCESS)
|
||||
//{
|
||||
// dwErrCode = CopyIndexItemsToCKeyArray(hs);
|
||||
//}
|
||||
|
||||
// Free the loaded ENCODING file
|
||||
CASC_FREE(pbEncodingFile);
|
||||
}
|
||||
@@ -807,6 +843,10 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
|
||||
assert(hs->CKeyMap.IsInitialized() == true);
|
||||
assert(hs->pRootHandler == NULL);
|
||||
|
||||
// Inform the user about what we are doing
|
||||
if(InvokeProgressCallback(hs, "Loading ROOT manifest", NULL, 0, 0))
|
||||
return ERROR_CANCELLED;
|
||||
|
||||
// Locale: The default parameter is 0 - in that case, we load all locales
|
||||
dwLocaleMask = (dwLocaleMask != 0) ? dwLocaleMask : 0xFFFFFFFF;
|
||||
|
||||
@@ -814,10 +854,6 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
|
||||
pCKeyEntry = (hs->VfsRoot.ContentSize != CASC_INVALID_SIZE) ? &hs->VfsRoot : &hs->RootFile;
|
||||
pCKeyEntry = FindCKeyEntry_CKey(hs, pCKeyEntry->CKey);
|
||||
|
||||
// Inform the user about what we are doing
|
||||
if(InvokeProgressCallback(hs, "Loading ROOT manifest", NULL, 0, 0))
|
||||
return ERROR_CANCELLED;
|
||||
|
||||
// Load the entire ROOT file to memory
|
||||
pbRootFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbRootFile);
|
||||
if(pbRootFile != NULL)
|
||||
@@ -1020,7 +1056,7 @@ static bool GetStoragePathProduct(TCascStorage * hs, void * pvStorageInfo, size_
|
||||
|
||||
static DWORD InitializeLocalDirectories(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
|
||||
{
|
||||
TCHAR * szWorkPath;
|
||||
LPTSTR szWorkPath;
|
||||
DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Find the root directory of the storage. The root directory
|
||||
@@ -1135,13 +1171,13 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
|
||||
dwErrCode = LoadCdnBuildFile(hs);
|
||||
}
|
||||
|
||||
// Create the central file array
|
||||
// Create the array of CKey entries. Each entry represents a file in the storage
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
dwErrCode = InitCKeyArray(hs);
|
||||
}
|
||||
|
||||
// Load the index files. Store information from the index files to the CKeyArray.
|
||||
// Pre-load the local index files
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
dwErrCode = LoadIndexFiles(hs);
|
||||
@@ -1153,8 +1189,7 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
|
||||
dwErrCode = LoadEncodingManifest(hs);
|
||||
}
|
||||
|
||||
// We need to load the DOWNLOAD manifest. This will give us the information about
|
||||
// how many physical files are in the storage, so we can start building file tables
|
||||
// We need to load the DOWNLOAD manifest
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
dwErrCode = LoadDownloadManifest(hs);
|
||||
@@ -1163,10 +1198,20 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
|
||||
// Load the build manifest ("ROOT" file)
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// If we fail to load the ROOT file, we take the file names from the INSTALL manifest
|
||||
// For WoW storages, multiple files are present in the storage (same name, same file data ID, different locale).
|
||||
// Failing to select storage on them will lead to the first-in-order file in the list being loaded.
|
||||
// Example: WoW build 32144, file: DBFilesClient\Achievement.db2, file data ID: 1260179
|
||||
// Locales: koKR frFR deDE zhCN esES zhTW enUS&enGB esMX ruRU itIT ptBT&ptPT (in order of appearance in the build manifest)
|
||||
if(dwLocaleMask == 0)
|
||||
{
|
||||
dwLocaleMask = hs->dwDefaultLocale;
|
||||
}
|
||||
|
||||
// Continue loading the manifest
|
||||
dwErrCode = LoadBuildManifest(hs, dwLocaleMask);
|
||||
if (dwErrCode != ERROR_SUCCESS)
|
||||
{
|
||||
// If we fail to load the ROOT file, we take the file names from the INSTALL manifest
|
||||
dwErrCode = LoadInstallManifest(hs);
|
||||
}
|
||||
}
|
||||
@@ -1192,8 +1237,9 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
|
||||
dwErrCode = CascLoadEncryptionKeys(hs);
|
||||
}
|
||||
|
||||
// Clear the arg structure
|
||||
hs->pArgs = pArgs;
|
||||
// Cleanup and exit
|
||||
FreeIndexFiles(hs);
|
||||
hs->pArgs = NULL;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,15 +80,19 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <wchar.h>
|
||||
#include <cassert>
|
||||
#include <errno.h>
|
||||
|
||||
// Support for PowerPC on Max OS X
|
||||
#if (__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1)
|
||||
@@ -261,6 +265,10 @@
|
||||
#define ERROR_CANCELLED 1009
|
||||
#endif
|
||||
|
||||
#ifndef ERROR_INDEX_PARSING_DONE
|
||||
#define ERROR_INDEX_PARSING_DONE 1010
|
||||
#endif
|
||||
|
||||
#ifndef _countof
|
||||
#define _countof(x) (sizeof(x) / sizeof(x[0]))
|
||||
#endif
|
||||
@@ -321,7 +329,7 @@ inline DWORD CascInterlockedIncrement(PDWORD PtrValue)
|
||||
inline DWORD CascInterlockedDecrement(PDWORD PtrValue)
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
return (DWORD)InterlockedIncrement((LONG *)(PtrValue));
|
||||
return (DWORD)InterlockedDecrement((LONG *)(PtrValue));
|
||||
#else
|
||||
return --PtrValue[0];
|
||||
#endif
|
||||
|
||||
@@ -397,6 +397,32 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
PCASC_CKEY_ENTRY InsertUnknownCKeyEntry(TCascStorage * hs, LPBYTE pbEKey, size_t cbEKey, DWORD ContentSize)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
|
||||
// Insert a new entry to the array. DO NOT ALLOW enlarge array here
|
||||
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false);
|
||||
if(pCKeyEntry != NULL)
|
||||
{
|
||||
memset(pCKeyEntry, 0, sizeof(CASC_CKEY_ENTRY));
|
||||
memcpy(pCKeyEntry->EKey, pbEKey, cbEKey);
|
||||
pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64;
|
||||
pCKeyEntry->ContentSize = ContentSize;
|
||||
pCKeyEntry->EncodedSize = CASC_INVALID_SIZE;
|
||||
pCKeyEntry->Flags = CASC_CE_HAS_EKEY | CASC_CE_HAS_EKEY_PARTIAL;
|
||||
pCKeyEntry->SpanCount = 1;
|
||||
|
||||
// Copy the information from index files to the CKey entry
|
||||
CopyEKeyEntry(hs, pCKeyEntry);
|
||||
|
||||
// Insert the item into EKey map
|
||||
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
|
||||
}
|
||||
|
||||
return pCKeyEntry;
|
||||
}
|
||||
|
||||
void InsertRootVfsEntry(TCascStorage * hs, LPBYTE pbCKey, const char * szFormat, size_t nIndex)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
@@ -474,30 +500,38 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
|
||||
// Find the CKey entry
|
||||
pCKeyEntry = FindCKeyEntry_EKey(hs, SpanEntry.EKey);
|
||||
if(pCKeyEntry != NULL)
|
||||
if(pCKeyEntry == NULL)
|
||||
{
|
||||
// We need to check whether this is another TVFS directory file
|
||||
if (IsVfsSubDirectory(hs, DirHeader, SubHeader, SpanEntry.EKey, SpanEntry.ContentSize) == ERROR_SUCCESS)
|
||||
// Some files are in the ROOT manifest even if they are not in ENCODING and DOWNLOAD.
|
||||
// Example: "2018 - New CASC\00001", file "DivideAndConquer.w3m:war3mapMap.blp"
|
||||
pCKeyEntry = InsertUnknownCKeyEntry(hs, SpanEntry.EKey, DirHeader.EKeySize, SpanEntry.ContentSize);
|
||||
if(pCKeyEntry == NULL)
|
||||
{
|
||||
// Add colon (':')
|
||||
PathBuffer.AppendChar(':');
|
||||
|
||||
// The file content size should already be there
|
||||
assert(pCKeyEntry->ContentSize == SpanEntry.ContentSize);
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer);
|
||||
|
||||
// Parse the subdir
|
||||
ParseDirectoryData(hs, SubHeader, PathBuffer);
|
||||
CASC_FREE(SubHeader.pbDirectoryData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the content content size is not there, supply it now
|
||||
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
|
||||
pCKeyEntry->ContentSize = SpanEntry.ContentSize;
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer);
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
// We need to check whether this is another TVFS directory file
|
||||
if (IsVfsSubDirectory(hs, DirHeader, SubHeader, SpanEntry.EKey, SpanEntry.ContentSize) == ERROR_SUCCESS)
|
||||
{
|
||||
// Add colon (':')
|
||||
PathBuffer.AppendChar(':');
|
||||
|
||||
// The file content size should already be there
|
||||
assert(pCKeyEntry->ContentSize == SpanEntry.ContentSize);
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer);
|
||||
|
||||
// Parse the subdir
|
||||
ParseDirectoryData(hs, SubHeader, PathBuffer);
|
||||
CASC_FREE(SubHeader.pbDirectoryData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the content content size is not there, supply it now
|
||||
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
|
||||
pCKeyEntry->ContentSize = SpanEntry.ContentSize;
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -25,8 +25,8 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,50,0,158
|
||||
PRODUCTVERSION 1,50,0,158
|
||||
FILEVERSION 1,50,0,181
|
||||
PRODUCTVERSION 1,50,0,181
|
||||
FILEFLAGSMASK 0x17L
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -42,12 +42,12 @@ BEGIN
|
||||
BLOCK "040504b0"
|
||||
BEGIN
|
||||
VALUE "FileDescription", "CascLib library for reading Blizzard CASC storages"
|
||||
VALUE "FileVersion", "1, 50, 0, 158\0"
|
||||
VALUE "FileVersion", "1, 50, 0, 181\0"
|
||||
VALUE "InternalName", "CascLib"
|
||||
VALUE "LegalCopyright", "Copyright (c) 2014 - 2019 Ladislav Zezula"
|
||||
VALUE "OriginalFilename", "CascLib.dll"
|
||||
VALUE "ProductName", "CascLib"
|
||||
VALUE "ProductVersion", "1, 50, 0, 158\0"
|
||||
VALUE "ProductVersion", "1, 50, 0, 181\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -438,17 +438,11 @@ bool CascVerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expec
|
||||
//-----------------------------------------------------------------------------
|
||||
// Scanning a directory
|
||||
|
||||
typedef bool (*INDEX_FILE_FOUND)(const TCHAR * szFileName, PDWORD IndexArray, PDWORD OldIndexArray, void * pvContext);
|
||||
typedef bool (*INDEX_FILE_FOUND)(LPCTSTR szFileName, void * pvContext);
|
||||
|
||||
bool DirectoryExists(const TCHAR * szDirectory);
|
||||
bool DirectoryExists(LPCTSTR szDirectory);
|
||||
|
||||
int ScanIndexDirectory(
|
||||
const TCHAR * szIndexPath,
|
||||
INDEX_FILE_FOUND pfnOnFileFound,
|
||||
PDWORD IndexArray,
|
||||
PDWORD OldIndexArray,
|
||||
void * pvContext
|
||||
);
|
||||
int ScanIndexDirectory(LPCTSTR szIndexPath, INDEX_FILE_FOUND pfnOnFileFound, void * pvContext);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Argument structure versioning
|
||||
|
||||
@@ -53,10 +53,8 @@ bool MakeDirectory(const TCHAR * szDirectory)
|
||||
}
|
||||
|
||||
int ScanIndexDirectory(
|
||||
const TCHAR * szIndexPath,
|
||||
LPCTSTR szIndexPath,
|
||||
INDEX_FILE_FOUND pfnOnFileFound,
|
||||
PDWORD MainIndexes,
|
||||
PDWORD OldIndexArray,
|
||||
void * pvContext)
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
@@ -81,7 +79,7 @@ int ScanIndexDirectory(
|
||||
if(!(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
// Let the callback scan the file name
|
||||
pfnOnFileFound(wf.cFileName, MainIndexes, OldIndexArray, pvContext);
|
||||
pfnOnFileFound(wf.cFileName, pvContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +101,7 @@ int ScanIndexDirectory(
|
||||
{
|
||||
if(dir_entry->d_type != DT_DIR)
|
||||
{
|
||||
pfnOnFileFound(dir_entry->d_name, MainIndexes, OldIndexArray, pvContext);
|
||||
pfnOnFileFound(dir_entry->d_name, pvContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,17 +14,13 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Scanning a directory
|
||||
|
||||
typedef bool (*INDEX_FILE_FOUND)(const TCHAR * szFileName, PDWORD IndexArray, PDWORD OldIndexArray, void * pvContext);
|
||||
bool DirectoryExists(LPCTSTR szDirectory);
|
||||
|
||||
bool DirectoryExists(const TCHAR * szDirectory);
|
||||
|
||||
bool MakeDirectory(const TCHAR * szDirectory);
|
||||
bool MakeDirectory(LPCTSTR szDirectory);
|
||||
|
||||
int ScanIndexDirectory(
|
||||
const TCHAR * szIndexPath,
|
||||
LPCTSTR szIndexPath,
|
||||
INDEX_FILE_FOUND pfnOnFileFound,
|
||||
PDWORD IndexArray,
|
||||
PDWORD OldIndexArray,
|
||||
void * pvContext
|
||||
);
|
||||
|
||||
|
||||
@@ -680,7 +680,7 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
|
||||
if (HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, szStatusCode, &dwDataSize, &dwIndex))
|
||||
dwStatusCode = _tcstoul(szStatusCode, &szEndPtr, 10);
|
||||
|
||||
// Copare the statuc code
|
||||
// Compare the status code
|
||||
if (dwStatusCode == 200)
|
||||
{
|
||||
// Check if the archive has Last Modified field
|
||||
|
||||
@@ -199,7 +199,7 @@ size_t ListFile_GetNextLine(void * pvListFile, const char ** pszLineBegin, const
|
||||
{
|
||||
// If we have found a newline, stop loading
|
||||
// Note: the 0x85 char came from Overwatch build 24919
|
||||
if(pCache->pPos[0] == 0x0A || pCache->pPos[0] == 0x0D || pCache->pPos[0] == 0x85)
|
||||
if(pCache->pPos[0] == '\x0A' || pCache->pPos[0] == '\x0D' || pCache->pPos[0] == '\x85')
|
||||
break;
|
||||
|
||||
// Blizzard listfiles can also contain information about patch:
|
||||
|
||||
@@ -94,7 +94,7 @@ class CASC_MAP
|
||||
Free();
|
||||
}
|
||||
|
||||
int Create(size_t MaxItems, size_t KeyLength, size_t KeyOffset, KEY_TYPE KeyType = KeyIsHash)
|
||||
DWORD Create(size_t MaxItems, size_t KeyLength, size_t KeyOffset, KEY_TYPE KeyType = KeyIsHash)
|
||||
{
|
||||
// Set the class variables
|
||||
m_KeyLength = CASCLIB_MAX(KeyLength, 8);
|
||||
@@ -331,7 +331,7 @@ class CASC_MAP
|
||||
size_t PowerOfTwo;
|
||||
|
||||
// Round the hash table size up to the nearest power of two
|
||||
for(PowerOfTwo = MIN_HASH_TABLE_SIZE; PowerOfTwo < MAX_HASH_TABLE_SIZE; PowerOfTwo <<= 1)
|
||||
for(PowerOfTwo = MIN_HASH_TABLE_SIZE; PowerOfTwo <= MAX_HASH_TABLE_SIZE; PowerOfTwo <<= 1)
|
||||
{
|
||||
if(PowerOfTwo > MaxItems)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user