diff options
Diffstat (limited to 'dep/CascLib/src/CascOpenStorage.cpp')
-rw-r--r-- | dep/CascLib/src/CascOpenStorage.cpp | 144 |
1 files changed, 95 insertions, 49 deletions
diff --git a/dep/CascLib/src/CascOpenStorage.cpp b/dep/CascLib/src/CascOpenStorage.cpp index abc17e1e9ce..aab10d2c88a 100644 --- a/dep/CascLib/src/CascOpenStorage.cpp +++ b/dep/CascLib/src/CascOpenStorage.cpp @@ -21,6 +21,27 @@ #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 TCascStorage::TCascStorage() @@ -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; + + // Do we know any of them? + if(nNumberOfFiles1 || nNumberOfFiles2) + return CASCLIB_MAX(nNumberOfFiles1, nNumberOfFiles2); - // 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; + // 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; } |