Closes #29102
Closes #29132
This commit is contained in:
Shauren
2023-07-13 00:12:42 +02:00
parent b6a0fa3518
commit 26f37152cc
7 changed files with 54 additions and 59 deletions
+1 -1
View File
@@ -296,7 +296,7 @@ struct TCascStorage
LPTSTR szCdnPath; // Remote CDN sub path for the product LPTSTR szCdnPath; // Remote CDN sub path for the product
LPSTR szRegion; // Product region. Only when "versions" is used as storage root file LPSTR szRegion; // Product region. Only when "versions" is used as storage root file
LPSTR szBuildKey; // Product build key, aka MD5 of the build file LPSTR szBuildKey; // Product build key, aka MD5 of the build file
DWORD dwDefaultLocale; // Default locale, read from ".build.info" DWORD dwDefaultLocale; // Mask of installed localles
DWORD dwBuildNumber; // Product build number DWORD dwBuildNumber; // Product build number
DWORD dwRefCount; // Number of references DWORD dwRefCount; // Number of references
DWORD dwFeatures; // List of CASC features. See CASC_FEATURE_XXX DWORD dwFeatures; // List of CASC features. See CASC_FEATURE_XXX
+35 -18
View File
@@ -274,9 +274,8 @@ static DWORD GetLocaleValue(LPCSTR szTag)
case 0x64654445: return CASC_LOCALE_DEDE; case 0x64654445: return CASC_LOCALE_DEDE;
case 0x72755255: return CASC_LOCALE_RURU; case 0x72755255: return CASC_LOCALE_RURU;
case 0x69744954: return CASC_LOCALE_ITIT; case 0x69744954: return CASC_LOCALE_ITIT;
default: return CASC_LOCALE_NONE;
} }
return 0;
} }
static bool CheckConfigFileVariable( static bool CheckConfigFileVariable(
@@ -579,29 +578,41 @@ static DWORD GetDefaultCdnPath(TCascStorage * hs, const CASC_CSV_COLUMN & Column
return ERROR_SUCCESS; return ERROR_SUCCESS;
} }
static DWORD GetDefaultLocaleMask(TCascStorage * hs, const CASC_CSV_COLUMN & Column) static DWORD GetDefaultLocaleByRegion(LPCSTR szRegion)
{
#define CASC_REGION_INT(hi, lo) (((DWORD)(hi) << 0x08) | lo)
// Setup the default locale
switch(CASC_REGION_INT(szRegion[0], szRegion[1]))
{
case CASC_REGION_INT('u', 's'): return CASC_LOCALE_ENUS;
case CASC_REGION_INT('e', 'u'): return CASC_LOCALE_ENGB;
case CASC_REGION_INT('c', 'n'): return CASC_LOCALE_ZHCN;
case CASC_REGION_INT('k', 'r'): return CASC_LOCALE_KOKR;
case CASC_REGION_INT('t', 'w'): return CASC_LOCALE_ZHTW;
// case CASC_REGION_INT('s', 'g'): return CASC_LOCALE_????;
default: return CASC_LOCALE_ENUS;
}
}
static DWORD GetDefaultLocaleMask(const CASC_CSV_COLUMN & Column)
{ {
LPCSTR szTagEnd = Column.szValue + Column.nLength - 4; LPCSTR szTagEnd = Column.szValue + Column.nLength - 4;
LPCSTR szTagPtr = Column.szValue; LPCSTR szTagPtr;
DWORD dwLocaleValue;
DWORD dwLocaleMask = 0; DWORD dwLocaleMask = 0;
while(szTagPtr < szTagEnd) // Go through the whole tag string
for(szTagPtr = Column.szValue; szTagPtr <= szTagEnd; szTagPtr++)
{ {
DWORD dwLocaleValue = GetLocaleValue(szTagPtr); // Try to recognize the 4-char locale code
if((dwLocaleValue = GetLocaleValue(szTagPtr)) != CASC_LOCALE_NONE)
if(dwLocaleValue != 0)
{ {
dwLocaleMask = dwLocaleMask | GetLocaleValue(szTagPtr); dwLocaleMask |= dwLocaleValue;
szTagPtr += 4; szTagPtr += 3; // Will be moved by 1 more at the end of the loop
}
else
{
szTagPtr++;
} }
} }
return dwLocaleMask;
hs->dwDefaultLocale = dwLocaleMask;
return ERROR_SUCCESS;
} }
static DWORD ParseFile_BuildInfo(TCascStorage * hs, CASC_CSV & Csv) static DWORD ParseFile_BuildInfo(TCascStorage * hs, CASC_CSV & Csv)
@@ -703,7 +714,7 @@ static DWORD ParseFile_BuildInfo(TCascStorage * hs, CASC_CSV & Csv)
return dwErrCode; return dwErrCode;
// If we found tags, we can extract language build from it // If we found tags, we can extract language build from it
GetDefaultLocaleMask(hs, Csv[nSelected]["Tags!STRING:0"]); hs->dwDefaultLocale = GetDefaultLocaleMask(Csv[nSelected]["Tags!STRING:0"]);
// Get the CDN servers and hosts // Get the CDN servers and hosts
if(hs->dwFeatures & CASC_FEATURE_ONLINE) if(hs->dwFeatures & CASC_FEATURE_ONLINE)
@@ -734,6 +745,9 @@ static DWORD ParseRegionLine_Versions(TCascStorage * hs, CASC_CSV & Csv, size_t
if(hs->szRegion == NULL) if(hs->szRegion == NULL)
hs->szRegion = CascNewStr(Csv[nLine]["Region!STRING:0"].szValue); hs->szRegion = CascNewStr(Csv[nLine]["Region!STRING:0"].szValue);
// Get the default locale mask based on the region
hs->dwDefaultLocale = GetDefaultLocaleByRegion(hs->szRegion);
// Extract the CDN build key // Extract the CDN build key
dwErrCode = LoadQueryKey(Csv[nLine]["BuildConfig!HEX:16"], hs->CdnBuildKey); dwErrCode = LoadQueryKey(Csv[nLine]["BuildConfig!HEX:16"], hs->CdnBuildKey);
if(dwErrCode != ERROR_SUCCESS) if(dwErrCode != ERROR_SUCCESS)
@@ -783,6 +797,9 @@ static DWORD ParseFile_BuildDb(TCascStorage * hs, CASC_CSV & Csv)
if(dwErrCode != ERROR_SUCCESS) if(dwErrCode != ERROR_SUCCESS)
return dwErrCode; return dwErrCode;
// Extract tags
hs->dwDefaultLocale = GetDefaultLocaleMask(Csv[CSV_ZERO][2]);
// Verify all variables // Verify all variables
return (hs->CdnBuildKey.pbData != NULL && hs->CdnConfigKey.pbData != NULL) ? ERROR_SUCCESS : ERROR_BAD_FORMAT; return (hs->CdnBuildKey.pbData != NULL && hs->CdnConfigKey.pbData != NULL) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
} }
+3
View File
@@ -164,6 +164,9 @@ extern "C" {
// Default format string for the file ID // Default format string for the file ID
#define CASC_FILEID_FORMAT "FILE%08X.dat" #define CASC_FILEID_FORMAT "FILE%08X.dat"
// Separator char for path-product delimiter
#define CASC_PARAM_SEPARATOR '*'
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Structures // Structures
+6 -9
View File
@@ -1072,7 +1072,7 @@ static bool GetStoragePathProduct(TCascStorage * hs, void * pvStorageInfo, size_
// Append the product code name, if any // Append the product code name, if any
if(hs->szCodeName != NULL) if(hs->szCodeName != NULL)
{ {
*szBuffer++ = _T(':'); *szBuffer++ = _T(CASC_PARAM_SEPARATOR);
CascStrCopy(szBuffer, (szBufferEnd - szBuffer), hs->szCodeName); CascStrCopy(szBuffer, (szBufferEnd - szBuffer), hs->szCodeName);
szBuffer += _tcslen(hs->szCodeName); szBuffer += _tcslen(hs->szCodeName);
} }
@@ -1080,7 +1080,7 @@ static bool GetStoragePathProduct(TCascStorage * hs, void * pvStorageInfo, size_
// Append the product region, if any // Append the product region, if any
if(hs->szRegion != NULL) if(hs->szRegion != NULL)
{ {
*szBuffer++ = _T(':'); *szBuffer++ = _T(CASC_PARAM_SEPARATOR);
CascStrCopy(szBuffer, (szBufferEnd - szBuffer), hs->szRegion); CascStrCopy(szBuffer, (szBufferEnd - szBuffer), hs->szRegion);
} }
} }
@@ -1209,10 +1209,7 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs, L
// Failing to select storage on them will lead to the first-in-order file in the list being loaded. // 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 // 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) // 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 = (dwLocaleMask != 0) ? dwLocaleMask : hs->dwDefaultLocale;
{
dwLocaleMask = hs->dwDefaultLocale;
}
// Continue loading the manifest // Continue loading the manifest
dwErrCode = LoadBuildManifest(hs, dwLocaleMask); dwErrCode = LoadBuildManifest(hs, dwLocaleMask);
@@ -1250,10 +1247,10 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs, L
return dwErrCode; return dwErrCode;
} }
// Check for URL pattern. Note that the string may be terminated by ':' instead of '\0' // Check for URL pattern. Note that the string may be terminated by CASC_PARAM_SEPARATOR instead of '\0'
static bool IsUrl(LPCTSTR szString) static bool IsUrl(LPCTSTR szString)
{ {
while(szString[0] != 0 && szString[0] != '*') while(szString[0] != 0 && szString[0] != CASC_PARAM_SEPARATOR)
{ {
// Check for "://" // Check for "://"
if(!_tcsncmp(szString, _T("://"), 3)) if(!_tcsncmp(szString, _T("://"), 3))
@@ -1276,7 +1273,7 @@ static LPTSTR GetNextParam(LPTSTR szParamsPtr, bool bMustBeUrl = false)
if(szParamsPtr != NULL) if(szParamsPtr != NULL)
{ {
// Find the separator ("*") or end of string // Find the separator ("*") or end of string
if((szSeparator = _tcschr(szParamsPtr, _T('*'))) != NULL) if((szSeparator = _tcschr(szParamsPtr, _T(CASC_PARAM_SEPARATOR))) != NULL)
{ {
// Check for URL pattern, if needed // Check for URL pattern, if needed
if(bMustBeUrl && IsUrl(szSeparator + 1) == false) if(bMustBeUrl && IsUrl(szSeparator + 1) == false)
+6 -3
View File
@@ -173,13 +173,16 @@ static DWORD ParseBlteHeader(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEn
CASCLIB_UNUSED(HeaderOffset); CASCLIB_UNUSED(HeaderOffset);
// On files within storage segments ("data.###"), there is BLTE_ENCODED_HEADER // On files within storage segments ("data.###"), there is BLTE_ENCODED_HEADER
// On local files, there is just PBLTE_HEADER // On local files, there is just BLTE_HEADER
if(ConvertBytesToInteger_4_LE(pBlteHeader->Signature) != BLTE_HEADER_SIGNATURE) if(ConvertBytesToInteger_4_LE(pBlteHeader->Signature) != BLTE_HEADER_SIGNATURE)
{ {
// There must be at least some bytes // There must be at least some bytes
if(cbEncodedBuffer < FIELD_OFFSET(BLTE_ENCODED_HEADER, MustBe0F)) if(cbEncodedBuffer < FIELD_OFFSET(BLTE_ENCODED_HEADER, MustBe0F))
return ERROR_BAD_FORMAT; return ERROR_BAD_FORMAT;
if(pEncodedHeader->EncodedSize != pCKeyEntry->EncodedSize)
// Note that some newer WoW builds have the entire encoded part zeroed
// Tested on WoW retail 50401, file DBFilesClient\\LoreTextPublic.db2
if(pEncodedHeader->EncodedSize != 0 && pEncodedHeader->EncodedSize != pCKeyEntry->EncodedSize)
return ERROR_BAD_FORMAT; return ERROR_BAD_FORMAT;
#ifdef CASCLIB_DEBUG #ifdef CASCLIB_DEBUG
@@ -1039,7 +1042,7 @@ bool WINAPI CascSetFileFlags(HANDLE hFile, DWORD dwOpenFlags)
} }
// Set "overcome encrypted" flag. Will apply on next CascReadFile // Set "overcome encrypted" flag. Will apply on next CascReadFile
hf->bOvercomeEncrypted = (dwOpenFlags & CASC_OVERCOME_ENCRYPTED) != 0; hf->bOvercomeEncrypted = (dwOpenFlags & CASC_OVERCOME_ENCRYPTED) ? true : false;
return true; return true;
} }
+2 -27
View File
@@ -78,31 +78,6 @@ typedef struct _FILE_ROOT_GROUP
} FILE_ROOT_GROUP, *PFILE_ROOT_GROUP; } FILE_ROOT_GROUP, *PFILE_ROOT_GROUP;
//-----------------------------------------------------------------------------
// Debug local stuff
#if defined(_MSC_VER) && defined(CASCLIB_DEBUG)
static FILE * fp = NULL;
static bool bLogEntries = true;
static void LogEntry(DWORD FileDataId, PCONTENT_KEY pCKey)
{
if(fp && bLogEntries)
{
char szCKey[MD5_STRING_SIZE + 1];
if(FileDataId == 1260179)
{
bLogEntries = false;
__debugbreak();
}
StringFromBinary(pCKey->Value, MD5_HASH_SIZE, szCKey);
fprintf(fp, "File Data ID: %u, CKey = %s\n", FileDataId, szCKey);
}
}
#endif
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// TRootHandler_WoW interface / implementation // TRootHandler_WoW interface / implementation
@@ -123,11 +98,11 @@ struct TRootHandler_WoW : public TFileTreeRoot
switch(RootFormat) switch(RootFormat)
{ {
case RootFormatWoW6x: case RootFormatWoW6x:
dwFeatures |= CASC_FEATURE_ROOT_CKEY | CASC_FEATURE_FNAME_HASHES | CASC_FEATURE_FILE_DATA_IDS | CASC_FEATURE_LOCALE_FLAGS | CASC_FEATURE_CONTENT_FLAGS; dwFeatures |= CASC_FEATURE_ROOT_CKEY | CASC_FEATURE_LOCALE_FLAGS | CASC_FEATURE_CONTENT_FLAGS | CASC_FEATURE_FNAME_HASHES;
break; break;
case RootFormatWoW82: case RootFormatWoW82:
dwFeatures |= CASC_FEATURE_ROOT_CKEY | CASC_FEATURE_FNAME_HASHES_OPTIONAL | CASC_FEATURE_FILE_DATA_IDS | CASC_FEATURE_LOCALE_FLAGS | CASC_FEATURE_CONTENT_FLAGS; dwFeatures |= CASC_FEATURE_ROOT_CKEY | CASC_FEATURE_LOCALE_FLAGS | CASC_FEATURE_CONTENT_FLAGS | CASC_FEATURE_FILE_DATA_IDS | CASC_FEATURE_FNAME_HASHES_OPTIONAL;
break; break;
} }
} }
+1 -1
View File
@@ -63,7 +63,7 @@ catch2
CascLib (An open-source implementation of library for reading CASC storage from Blizzard games since 2014) CascLib (An open-source implementation of library for reading CASC storage from Blizzard games since 2014)
https://github.com/ladislav-zezula/CascLib https://github.com/ladislav-zezula/CascLib
Version: ebd79e8fd43279343c543a27fce620f6b1b53cb9 Version: a27d2b3b9ccb326768e57b4cc7c49d15a0da9e0c
rapidjson (A fast JSON parser/generator for C++ with both SAX/DOM style API http://rapidjson.org/) rapidjson (A fast JSON parser/generator for C++ with both SAX/DOM style API http://rapidjson.org/)
https://github.com/Tencent/rapidjson https://github.com/Tencent/rapidjson