This commit is contained in:
Shauren
2019-10-29 19:01:39 +01:00
parent 4328e1dfb2
commit c7b4157437
16 changed files with 431 additions and 233 deletions

View File

@@ -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

View 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;
}

View File

@@ -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

View 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);
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
{

View File

@@ -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"

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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
);

View File

@@ -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

View File

@@ -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:

View File

@@ -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)
{