diff options
author | Shauren <shauren.trinity@gmail.com> | 2023-02-06 20:08:39 +0100 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2023-02-06 20:08:39 +0100 |
commit | fd154940eddc54e556d6bfb5147cedbda4750c3e (patch) | |
tree | 8ff9e3974e8479c1b8157f8aa40bc094cba24bc2 | |
parent | 99320464997a5411b7245cb952eaf6cdf8a2a978 (diff) |
Dep/CascLib: Update to ladislav-zezula/CascLib@a5080b5794027a25d98aa6024b2bef17d06fe0ea
39 files changed, 2066 insertions, 1625 deletions
diff --git a/dep/CascLib/CMakeLists.txt b/dep/CascLib/CMakeLists.txt index 273b7155468..078d4f0d5cd 100644 --- a/dep/CascLib/CMakeLists.txt +++ b/dep/CascLib/CMakeLists.txt @@ -4,6 +4,7 @@ set(HEADER_FILES src/CascPort.h src/CascStructs.h src/common/Array.h + src/common/ArraySparse.h src/common/Common.h src/common/Csv.h src/common/Directory.h diff --git a/dep/CascLib/src/CascCommon.h b/dep/CascLib/src/CascCommon.h index 44f94b0b40d..19a6aa98444 100644 --- a/dep/CascLib/src/CascCommon.h +++ b/dep/CascLib/src/CascCommon.h @@ -24,6 +24,7 @@ #include "CascPort.h" #include "common/Common.h" #include "common/Array.h" +#include "common/ArraySparse.h" #include "common/Map.h" #include "common/FileTree.h" #include "common/FileStream.h" @@ -59,9 +60,6 @@ #define CASC_MAGIC_FILE 0x454C494643534143 // 'CASCFILE' #define CASC_MAGIC_FIND 0x444E494643534143 // 'CASCFIND' -// For CASC_CDN_DOWNLOAD::Flags -#define CASC_CDN_FORCE_DOWNLOAD 0x0001 // Force downloading the file even if in the cache - // The maximum size of an inline file #define CASC_MAX_ONLINE_FILE_SIZE 0x40000000 @@ -71,11 +69,18 @@ typedef enum _CBLD_TYPE { CascBuildNone = 0, // No build type found - CascBuildInfo, // .build.info CascBuildDb, // .build.db (older storages) - CascVersionsDb // versions (downloaded online) + CascBuildInfo, // .build.info + CascVersions // versions (cached or online) } CBLD_TYPE, *PCBLD_TYPE; +typedef enum _CPATH_TYPE +{ + PathTypeConfig, // The "config" subfolder + PathTypeData, // The "data" subfolder + PathTypePatch // The "patch" subfolder +} CPATH_TYPE, *PCPATH_TYPE; + typedef enum _CSTRTG { CascCacheInvalid, // Do not cache anything. Used as invalid value @@ -90,15 +95,23 @@ typedef struct _CASC_TAG_ENTRY char TagName[1]; // Tag name. Variable length. } CASC_TAG_ENTRY, *PCASC_TAG_ENTRY; +// Information about CASC main file +typedef struct _CASC_BUILD_FILE +{ + LPCTSTR szPlainName; // Plain file name + CBLD_TYPE BuildFileType; // Build file type + TCHAR szFullPath[MAX_PATH]; // Full file name +} CASC_BUILD_FILE, *PCASC_BUILD_FILE; + // Information about index file -typedef struct _CASC_INDEX +struct CASC_INDEX { + CASC_BLOB FileData; 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; +}; +typedef CASC_INDEX * PCASC_INDEX; // Normalized header of the index files. // Both version 1 and version 2 are converted to this structure @@ -229,30 +242,15 @@ typedef struct _CASC_FILE_SPAN } CASC_FILE_SPAN, *PCASC_FILE_SPAN; -// Structure for downloading a file from the CDN (https://wowdev.wiki/TACT#File_types) -// Remote path is combined as the following: -// [szCdnsHost] /[szCdnsPath]/[szPathType]/EKey[0-1]/EKey[2-3]/[EKey].[Extension] -// level3.blizzard.com/tpr/bnt001 /data /fe /3d /fe3d7cf9d04e07066de32bd95a5c2627.index -typedef struct _CASC_CDN_DOWNLOAD +// Archive information for a remote file +typedef struct _CASC_ARCHIVE_INFO { - ULONGLONG ArchiveOffs; // Archive offset (if pbArchiveKey != NULL) - LPCTSTR szCdnsHost; // Address of the remote CDN server. ("level3.blizzard.com") - // If NULL, the downloader will try all CDN servers from the storage - LPCTSTR szCdnsPath; // Remote CDN path ("tpr/bnt001") - LPCTSTR szPathType; // Path type ("config", "data", "patch") - LPCTSTR szLoPaType; // Local path type ("config", "data", "patch"). If NULL, it's gonna be the same like szPathType, If "", then it's not used - LPCTSTR szFileName; // Plain file name, without path and extension - LPBYTE pbArchiveKey; // If non-NULL, then the file is present in the archive. - LPBYTE pbEKey; // 16-byte EKey of the file of of the archive - LPCTSTR szExtension; // Extension for the file. Can be NULL. - - LPTSTR szLocalPath; // Pointer to the variable that, upon success, reveives the local path where the file was downloaded - size_t ccLocalPath; // Maximum length of szLocalPath, in TCHARs - DWORD ArchiveIndex; // Index of the archive (if pbArchiveKey != NULL) - DWORD EncodedSize; // Encoded length (if pbArchiveKey != NULL) - DWORD Flags; // See CASC_CDN_FLAG_XXX - -} CASC_CDN_DOWNLOAD, *PCASC_CDN_DOWNLOAD; + DWORD ArchiveIndex; // This is the index of the archive + DWORD ArchiveOffs; // The file is within an archive at this offset + DWORD EncodedSize; // The size of the file within the archive + BYTE ArchiveKey[MD5_HASH_SIZE]; // This is the CKey of the archive + +} CASC_ARCHIVE_INFO, *PCASC_ARCHIVE_INFO; //----------------------------------------------------------------------------- // Structures for CASC storage and CASC file @@ -282,13 +280,15 @@ struct TCascStorage CASC_LOCK StorageLock; // Lock for multi-threaded operations LPCTSTR szIndexFormat; // Format of the index file name - LPTSTR szCdnHostUrl; // CDN host URL for online storage LPTSTR szCodeName; // On local storage, this select a product in a multi-product storage. For online storage, this selects a product LPTSTR szRootPath; // Path where the build file is - LPTSTR szDataPath; // This is the directory where data files are - LPTSTR szIndexPath; // This is the directory where index files are - LPTSTR szBuildFile; // Build file name (.build.info or .build.db) - LPTSTR szCdnServers; // Multi-SZ list of CDN servers + LPTSTR szDataPath; // The directory where data files are + LPTSTR szIndexPath; // The directory where index files are + LPTSTR szFilesPath; // The directory where raw files are + LPTSTR szConfigPath; // The directory with configs + LPTSTR szMainFile; // Main storage file (".build.info", ".build.db", "versions") + LPTSTR szCdnHostUrl; // URL of the ribbit/http server where to download the "versions" and "cdns" files + LPTSTR szCdnServers; // List of CDN servers, separated by space LPTSTR szCdnPath; // Remote CDN sub path for the product LPSTR szRegion; // Product region. Only when "versions" is used as storage root file LPSTR szBuildKey; // Product build key, aka MD5 of the build file @@ -299,14 +299,14 @@ struct TCascStorage CBLD_TYPE BuildFileType; // Type of the build file - QUERY_KEY CdnConfigKey; // Currently selected CDN config file. Points to "config\%02X\%02X\%s - QUERY_KEY CdnBuildKey; // Currently selected CDN build file. Points to "config\%02X\%02X\%s + CASC_BLOB CdnConfigKey; // Currently selected CDN config file. Points to "config\%02X\%02X\%s + CASC_BLOB CdnBuildKey; // Currently selected CDN build file. Points to "config\%02X\%02X\%s - QUERY_KEY ArchiveGroup; // Key array of the "archive-group" - QUERY_KEY ArchivesKey; // Key array of the "archives" - QUERY_KEY PatchArchivesKey; // Key array of the "patch-archives" - QUERY_KEY PatchArchivesGroup; // Key array of the "patch-archive-group" - QUERY_KEY BuildFiles; // List of supported build files + CASC_BLOB ArchiveGroup; // Key array of the "archive-group" + CASC_BLOB ArchivesKey; // Key array of the "archives" + CASC_BLOB PatchArchivesKey; // Key array of the "patch-archives" + CASC_BLOB PatchArchivesGroup; // Key array of the "patch-archive-group" + CASC_BLOB 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 @@ -386,9 +386,8 @@ struct TCascSearch TCascSearch(TCascStorage * ahs, LPCTSTR aszListFile, const char * aszMask) { // Init the class - if(ahs != NULL) - hs = ahs->AddRef(); ClassName = CASC_MAGIC_FIND; + hs = (ahs != NULL) ? ahs->AddRef() : NULL; // Init provider-specific data pCache = NULL; @@ -440,7 +439,7 @@ struct TCascSearch //----------------------------------------------------------------------------- // Common functions (CascCommon.cpp) -inline void FreeCascBlob(PQUERY_KEY pBlob) +inline void FreeCascBlob(PCASC_BLOB pBlob) { if(pBlob != NULL) { @@ -454,15 +453,18 @@ inline void FreeCascBlob(PQUERY_KEY pBlob) bool InvokeProgressCallback(TCascStorage * hs, LPCSTR szMessage, LPCSTR szObject, DWORD CurrentValue, DWORD TotalValue); DWORD GetFileSpanInfo(PCASC_CKEY_ENTRY pCKeyEntry, PULONGLONG PtrContentSize, PULONGLONG PtrEncodedSize = NULL); -DWORD DownloadFileFromCDN(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInfo); -DWORD CheckGameDirectory(TCascStorage * hs, LPTSTR szDirectory); -DWORD LoadCdnsFile(TCascStorage * hs); -DWORD LoadBuildInfo(TCascStorage * hs); +DWORD FetchCascFile(TCascStorage * hs, CPATH_TYPE PathType, LPBYTE pbEKey, LPCTSTR szExtension, CASC_PATH<TCHAR> & LocalPath, PCASC_ARCHIVE_INFO pArchiveInfo = NULL); +DWORD CheckCascBuildFileExact(CASC_BUILD_FILE & BuildFile, LPCTSTR szLocalPath); +DWORD CheckCascBuildFileDirs(CASC_BUILD_FILE & BuildFile, LPCTSTR szLocalPath); +DWORD CheckOnlineStorage(PCASC_OPEN_STORAGE_ARGS pArgs, CASC_BUILD_FILE & BuildFile, DWORD dwFeatures); +DWORD CheckArchiveFilesDirectories(TCascStorage * hs); +DWORD CheckDataFilesDirectory(TCascStorage * hs); +DWORD LoadMainFile(TCascStorage * hs); DWORD LoadCdnConfigFile(TCascStorage * hs); DWORD LoadCdnBuildFile(TCascStorage * hs); -LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD * pcbFileData); -LPBYTE LoadFileToMemory(LPCTSTR szFileName, DWORD * pcbFileData); +DWORD LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, CASC_BLOB & FileData); +DWORD LoadFileToMemory(LPCTSTR szFileName, CASC_BLOB & FileData); bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD dwOpenFlags, HANDLE * PtrFileHandle); bool SetCacheStrategy(HANDLE hFile, CSTRTG CacheStrategy); @@ -493,18 +495,19 @@ void FreeIndexFiles(TCascStorage * hs); //----------------------------------------------------------------------------- // Support for ROOT file -DWORD RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); -DWORD RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); -DWORD RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); -DWORD RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask); -DWORD RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); -DWORD RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); -DWORD RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile); +DWORD RootHandler_CreateMNDX(TCascStorage * hs, CASC_BLOB & RootFile); +DWORD RootHandler_CreateTVFS(TCascStorage * hs, CASC_BLOB & RootFile); +DWORD RootHandler_CreateDiablo3(TCascStorage * hs, CASC_BLOB & RootFile); +DWORD RootHandler_CreateWoW(TCascStorage * hs, CASC_BLOB & RootFile, DWORD dwLocaleMask); +DWORD RootHandler_CreateOverwatch(TCascStorage * hs, CASC_BLOB & RootFile); +DWORD RootHandler_CreateStarcraft1(TCascStorage * hs, CASC_BLOB & RootFile); +DWORD RootHandler_CreateInstall(TCascStorage * hs, CASC_BLOB & InstallFile); //----------------------------------------------------------------------------- // Dumpers (CascDumpData.cpp) #ifdef _DEBUG +void CascDumpData(LPCSTR szFileName, const void * pvData, size_t cbData); void CascDumpFile(HANDLE hFile, const char * szDumpFile = NULL); void CascDumpStorage(HANDLE hStorage, const char * szDumpFile = NULL); #endif diff --git a/dep/CascLib/src/CascDecompress.cpp b/dep/CascLib/src/CascDecompress.cpp index 42c4758eb79..19d7d0a88c4 100644 --- a/dep/CascLib/src/CascDecompress.cpp +++ b/dep/CascLib/src/CascDecompress.cpp @@ -40,7 +40,7 @@ DWORD CascDecompress(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, { // Call zlib to decompress the data nResult = inflate(&z, Z_NO_FLUSH); - if (nResult == Z_OK || nResult == Z_STREAM_END) + if(nResult == Z_OK || nResult == Z_STREAM_END) { // Give the size of the uncompressed data cbOutBuffer = z.total_out; diff --git a/dep/CascLib/src/CascDecrypt.cpp b/dep/CascLib/src/CascDecrypt.cpp index 3dbb5ce31a6..2eba7a5ebff 100644 --- a/dep/CascLib/src/CascDecrypt.cpp +++ b/dep/CascLib/src/CascDecrypt.cpp @@ -767,7 +767,7 @@ bool WINAPI CascAddEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPBYTE Key) // Validate the storage handle hs = TCascStorage::IsValid(hStorage); - if (hs == NULL) + if(hs == NULL) { SetCascError(ERROR_INVALID_HANDLE); return false; @@ -804,7 +804,7 @@ LPBYTE WINAPI CascFindEncryptionKey(HANDLE hStorage, ULONGLONG KeyName) // Validate the storage handle hs = TCascStorage::IsValid(hStorage); - if (hs == NULL) + if(hs == NULL) { SetCascError(ERROR_INVALID_HANDLE); return NULL; @@ -819,7 +819,7 @@ bool WINAPI CascGetNotFoundEncryptionKey(HANDLE hStorage, ULONGLONG * KeyName) TCascStorage * hs; // Validate the storage handle - if ((hs = TCascStorage::IsValid(hStorage)) == NULL) + if((hs = TCascStorage::IsValid(hStorage)) == NULL) { SetCascError(ERROR_INVALID_HANDLE); return false; diff --git a/dep/CascLib/src/CascDumpData.cpp b/dep/CascLib/src/CascDumpData.cpp index 7a41d3da9f0..e29e93d093c 100644 --- a/dep/CascLib/src/CascDumpData.cpp +++ b/dep/CascLib/src/CascDumpData.cpp @@ -456,7 +456,18 @@ void DumpDownloadManifest(TCascStorage * hs, FILE * fp) //----------------------------------------------------------------------------- // Public dumping functions -void CascDumpFile(const char * szDumpFile, HANDLE hFile) +void CascDumpData(LPCSTR szFileName, const void * pvData, size_t cbData) +{ + FILE * fp; + + if((fp = fopen(szFileName, "wb")) != NULL) + { + fwrite(pvData, 1, cbData, fp); + fclose(fp); + } +} + +void CascDumpFile(HANDLE hFile, const char * szDumpFile) { FILE * fp; DWORD dwBytesRead = 1; @@ -506,9 +517,8 @@ void CascDumpStorage(HANDLE hStorage, const char * szDumpFile) fprintf(fp, "=== Basic Storage Info ======================================================\n"); fprintf(fp, "DataPath: %s\n", StringFromLPTSTR(hs->szDataPath, szStringBuff, sizeof(szStringBuff))); fprintf(fp, "IndexPath: %s\n", StringFromLPTSTR(hs->szIndexPath, szStringBuff, sizeof(szStringBuff))); - fprintf(fp, "BuildFile: %s\n", StringFromLPTSTR(hs->szBuildFile, szStringBuff, sizeof(szStringBuff))); + fprintf(fp, "Main File: %s\n", StringFromLPTSTR(hs->szMainFile, szStringBuff, sizeof(szStringBuff))); fprintf(fp, "CDN Server: %s\n", StringFromLPTSTR(hs->szCdnServers, szStringBuff, sizeof(szStringBuff))); - fprintf(fp, "CDN Host Url: %s\n", StringFromLPTSTR(hs->szCdnHostUrl, szStringBuff, sizeof(szStringBuff))); fprintf(fp, "CDN Path: %s\n", StringFromLPTSTR(hs->szCdnPath, szStringBuff, sizeof(szStringBuff))); DumpKey(fp, "CDN Config Key: %s\n", hs->CdnConfigKey.pbData, hs->CdnConfigKey.cbData); DumpKey(fp, "CDN Build Key: %s\n", hs->CdnBuildKey.pbData, hs->CdnBuildKey.cbData); diff --git a/dep/CascLib/src/CascFiles.cpp b/dep/CascLib/src/CascFiles.cpp index b18b8847a3d..f2546e1befa 100644 --- a/dep/CascLib/src/CascFiles.cpp +++ b/dep/CascLib/src/CascFiles.cpp @@ -20,11 +20,14 @@ //----------------------------------------------------------------------------- // Local defines -typedef DWORD (*PARSECSVFILE)(TCascStorage * hs, CASC_CSV & Csv); -typedef DWORD (*PARSETEXTFILE)(TCascStorage * hs, void * pvListFile); +#define MAX_VAR_NAME 80 + +typedef DWORD (*PARSE_CSV_FILE)(TCascStorage * hs, CASC_CSV & Csv); +typedef DWORD (*PARSE_TEXT_FILE)(TCascStorage * hs, void * pvListFile); typedef DWORD (*PARSE_VARIABLE)(TCascStorage * hs, const char * szVariableName, const char * szDataBegin, const char * szDataEnd, void * pvParam); +typedef DWORD (*PARSE_REGION_LINE)(TCascStorage * hs, CASC_CSV & Csv, size_t nLine); -#define MAX_VAR_NAME 80 +static DWORD RibbitDownloadFile(LPCTSTR szCdnHostUrl, LPCTSTR szProduct, LPCTSTR szFileName, CASC_PATH<TCHAR> & LocalPath, CASC_BLOB & FileData); //----------------------------------------------------------------------------- // Local structures @@ -32,6 +35,7 @@ typedef DWORD (*PARSE_VARIABLE)(TCascStorage * hs, const char * szVariableName, struct TBuildFileInfo { LPCTSTR szFileName; + size_t nLength; CBLD_TYPE BuildFileType; }; @@ -43,15 +47,14 @@ struct TGameLocaleString static const TBuildFileInfo BuildTypes[] = { - {_T(".build.info"), CascBuildInfo}, // Since HOTS build 30027, the game uses .build.info file for storage info - {_T(".build.db"), CascBuildDb}, // Older CASC storages - {_T("versions"), CascVersionsDb}, // Older CASC storages - {NULL, CascBuildNone} + {_T(".build.info"), 11, CascBuildInfo}, // Since HOTS build 30027, the game uses .build.info file for storage info + {_T(".build.db"), 9, CascBuildDb}, // Older CASC storages + {_T("versions"), 8, CascVersions}, // Online/cached CASC storages }; static LPCTSTR DataDirs[] = { - _T("data") _T(PATH_SEP_STRING) _T("casc"), // Overwatch + _T("data") _T(PATH_SEP_STRING) _T("casc"), // Overwatch. This item must be the first in the list _T("data"), // TACT casc (for Linux systems) _T("Data"), // World of Warcraft, Diablo _T("SC2Data"), // Starcraft II (Legacy of the Void) build 38749 @@ -61,6 +64,7 @@ static LPCTSTR DataDirs[] = }; static const LPCTSTR szDefaultCDN = _T("ribbit://us.version.battle.net/v1/products"); +static const ULONGLONG ValueOne64 = 1; //----------------------------------------------------------------------------- // Local functions @@ -85,6 +89,60 @@ static bool StringEndsWith(LPCSTR szString, size_t nLength, LPCSTR szSuffix, siz return ((nLength > nSuffixLength) && !strcmp(szString + nLength - nSuffixLength, szSuffix)); } +static LPCTSTR GetSubFolder(CPATH_TYPE PathType) +{ + switch(PathType) + { + case PathTypeConfig: return _T("config"); + case PathTypeData: return _T("data"); + case PathTypePatch: return _T("patch"); + + default: + assert(false); + return _T(""); + } +} + +static bool CheckForTwoDigitFolder(LPCTSTR szPathName, void * pvContext) +{ + BYTE Binary[8]; + + // Must be a two-digit hexa string (the first two digits of the CKey) + if(_tcslen(szPathName) == 2) + { + if(BinaryFromString(szPathName, 2, Binary) == ERROR_SUCCESS) + { + *(bool *)pvContext = true; + return false; + } + } + + // Keep searching + return true; +} + +static bool ReplaceVersionsWithCdns(LPTSTR szBuffer, size_t ccBuffer, LPCTSTR szVersions) +{ + LPCTSTR szVersions0 = _T("versions"); + LPCTSTR szCdns0 = _T("cdns"); + size_t nLength; + + // Copy the existing file name into new buffer + CascStrCopy(szBuffer, ccBuffer, GetPlainFileName(szVersions)); + + // Find the ending "versions" string + if((nLength = _tcslen(szBuffer)) >= 8) + { + szBuffer = szBuffer + nLength - 8; + if(!_tcsicmp(szBuffer, szVersions0)) + { + CascStrCopy(szBuffer, 5, szCdns0); + return true; + } + } + return false; +} + static const char * CaptureDecimalInteger(const char * szDataPtr, const char * szDataEnd, PDWORD PtrValue) { const char * szSaveDataPtr = szDataPtr; @@ -147,7 +205,7 @@ static const char * CaptureSingleHash(const char * szDataPtr, const char * szDat szHashString = szDataPtr; // Count all hash characters - for (size_t i = 0; i < HashStringLength; i++) + for(size_t i = 0; i < HashStringLength; i++) { if(szDataPtr >= szDataEnd || isxdigit(szDataPtr[0]) == 0) return NULL; @@ -252,7 +310,7 @@ static bool CheckConfigFileVariable( } static DWORD LoadHashArray( - PQUERY_KEY pBlob, + PCASC_BLOB pBlob, const char * szLinePtr, const char * szLineEnd, size_t HashCount) @@ -266,7 +324,7 @@ static DWORD LoadHashArray( { LPBYTE pbBuffer = pBlob->pbData; - for (size_t i = 0; i < HashCount; i++) + for(size_t i = 0; i < HashCount; i++) { // Capture the hash value szLinePtr = CaptureSingleHash(szLinePtr, szLineEnd, pbBuffer, MD5_HASH_SIZE); @@ -283,7 +341,7 @@ static DWORD LoadHashArray( return dwErrCode; } -static DWORD LoadMultipleHashes(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd) +static DWORD LoadMultipleHashes(PCASC_BLOB pBlob, const char * szLineBegin, const char * szLineEnd) { size_t HashCount = 0; DWORD dwErrCode = ERROR_SUCCESS; @@ -305,7 +363,7 @@ static DWORD LoadMultipleHashes(PQUERY_KEY pBlob, const char * szLineBegin, cons // A QueryKey is an array of "ContentKey EncodedKey1 ... EncodedKeyN" static DWORD LoadQueryKey(TCascStorage * /* hs */, const char * /* szVariableName */, const char * szDataBegin, const char * szDataEnd, void * pvParam) { - return LoadMultipleHashes((PQUERY_KEY)pvParam, szDataBegin, szDataEnd); + return LoadMultipleHashes((PCASC_BLOB)pvParam, szDataBegin, szDataEnd); } static DWORD LoadCKeyEntry(TCascStorage * hs, const char * szVariableName, const char * szDataPtr, const char * szDataEnd, void * pvParam) @@ -488,7 +546,7 @@ static DWORD LoadBuildNumber(TCascStorage * hs, const char * /* szVariableName * return ERROR_BAD_FORMAT; } -static int LoadQueryKey(const CASC_CSV_COLUMN & Column, QUERY_KEY & Key) +static int LoadQueryKey(const CASC_CSV_COLUMN & Column, CASC_BLOB & Key) { // Check the input data if(Column.szValue == NULL) @@ -507,13 +565,17 @@ static void SetProductCodeName(TCascStorage * hs, LPCSTR szCodeName) } } +static DWORD GetDefaultCdnServers(TCascStorage * hs, const CASC_CSV_COLUMN & Column) +{ + if(hs->szCdnServers == NULL && Column.nLength != 0) + hs->szCdnServers = CascNewStrA2T(Column.szValue); + return ERROR_SUCCESS; +} + static DWORD GetDefaultCdnPath(TCascStorage * hs, const CASC_CSV_COLUMN & Column) { if(hs->szCdnPath == NULL && Column.nLength != 0) - { hs->szCdnPath = CascNewStrA2T(Column.szValue); - } - return ERROR_SUCCESS; } @@ -542,43 +604,6 @@ static DWORD GetDefaultLocaleMask(TCascStorage * hs, const CASC_CSV_COLUMN & Col return ERROR_SUCCESS; } -static DWORD ParseFile_CDNS(TCascStorage * hs, CASC_CSV & Csv) -{ - const char * szWantedRegion = hs->szRegion; - const char * szRegion; - size_t nLineCount; - - // Fix the region - if(szWantedRegion == NULL || !_stricmp(szWantedRegion, "beta") || !_stricmp(szWantedRegion, "xx")) - szWantedRegion = "us"; - - // Determine the row count - nLineCount = Csv.GetLineCount(); - - // Find the active config - for(size_t i = 0; i < nLineCount; i++) - { - // Retrieve the region - if((szRegion = Csv[i]["Name!STRING:0"].szValue) != NULL) - { - // Is it the version we are looking for? - if(!strcmp(Csv[i]["Name!STRING:0"].szValue, szWantedRegion)) - { - // Save the list of CDN servers - hs->szCdnServers = CascNewStrA2T(Csv[i]["Hosts!STRING:0"].szValue); - - // Save the CDN subpath - hs->szCdnPath = CascNewStrA2T(Csv[i]["Path!STRING:0"].szValue); - - // Check and return result - return (hs->szCdnServers && hs->szCdnPath) ? ERROR_SUCCESS : ERROR_BAD_FORMAT; - } - } - } - - return ERROR_FILE_NOT_FOUND; -} - static DWORD ParseFile_BuildInfo(TCascStorage * hs, CASC_CSV & Csv) { PFNPRODUCTCALLBACK PfnProductCallback = hs->pArgs->PfnProductCallback; @@ -677,12 +702,16 @@ 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"]); + // Get the CDN servers and hosts + if(hs->dwFeatures & CASC_FEATURE_ONLINE) + { + GetDefaultCdnServers(hs, Csv[nSelected]["CDN Hosts!STRING:0"]); + GetDefaultCdnPath(hs, Csv[nSelected]["CDN Path!STRING:0"]); + } + // If we found version, extract a build number const CASC_CSV_COLUMN & VerColumn = Csv[nSelected]["Version!STRING:0"]; if(VerColumn.szValue && VerColumn.nLength) @@ -697,47 +726,47 @@ static DWORD ParseFile_BuildInfo(TCascStorage * hs, CASC_CSV & Csv) return ERROR_FILE_NOT_FOUND; } -static DWORD ParseFile_VersionsDb(TCascStorage * hs, CASC_CSV & Csv) +static DWORD ParseRegionLine_Versions(TCascStorage * hs, CASC_CSV & Csv, size_t nLine) { - size_t nLineCount = Csv.GetLineCount(); - DWORD dwErrCode = ERROR_SUCCESS; + DWORD dwErrCode; - // Find the active config - for (size_t i = 0; i < nLineCount; i++) - { - // Either take the version required or take the first one - if(hs->szRegion == NULL || !strcmp(Csv[i]["Region!STRING:0"].szValue, hs->szRegion)) - { - // Extract the CDN build key - dwErrCode = LoadQueryKey(Csv[i]["BuildConfig!HEX:16"], hs->CdnBuildKey); - if(dwErrCode != ERROR_SUCCESS) - return dwErrCode; + // If the region line is not there yet, supply default one + if(hs->szRegion == NULL) + hs->szRegion = CascNewStr(Csv[nLine]["Region!STRING:0"].szValue); - // Extract the CDN config key - dwErrCode = LoadQueryKey(Csv[i]["CDNConfig!HEX:16"], hs->CdnConfigKey); - if(dwErrCode != ERROR_SUCCESS) - return dwErrCode; + // Extract the CDN build key + dwErrCode = LoadQueryKey(Csv[nLine]["BuildConfig!HEX:16"], hs->CdnBuildKey); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; - const CASC_CSV_COLUMN & VerColumn = Csv[i]["VersionsName!String:0"]; - if(VerColumn.szValue && VerColumn.nLength) - { - LoadBuildNumber(hs, NULL, VerColumn.szValue, VerColumn.szValue + VerColumn.nLength, NULL); - } + // Extract the CDN config key + dwErrCode = LoadQueryKey(Csv[nLine]["CDNConfig!HEX:16"], hs->CdnConfigKey); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; - // Verify all variables - if(hs->CdnBuildKey.pbData != NULL && hs->CdnConfigKey.pbData != NULL) - { - // If we have manually given build key, override the value - if(hs->szBuildKey != NULL) - dwErrCode = BinaryFromString(hs->szBuildKey, MD5_STRING_SIZE, hs->CdnBuildKey.pbData); - return dwErrCode; - } + const CASC_CSV_COLUMN & VerColumn = Csv[nLine]["VersionsName!String:0"]; + if(VerColumn.szValue && VerColumn.nLength) + { + LoadBuildNumber(hs, NULL, VerColumn.szValue, VerColumn.szValue + VerColumn.nLength, NULL); + } - return ERROR_BAD_FORMAT; - } + // Verify all variables + if(hs->CdnBuildKey.pbData != NULL && hs->CdnConfigKey.pbData != NULL) + { + // If we have manually given build key, override the value + if(hs->szBuildKey != NULL) + dwErrCode = BinaryFromString(hs->szBuildKey, MD5_STRING_SIZE, hs->CdnBuildKey.pbData); + return dwErrCode; } - return ERROR_FILE_NOT_FOUND; + return ERROR_BAD_FORMAT; +} + +static DWORD ParseRegionLine_Cdns(TCascStorage * hs, CASC_CSV & Csv, size_t nLine) +{ + hs->szCdnServers = CascNewStrA2T(Csv[nLine]["Hosts!STRING:0"].szValue); + hs->szCdnPath = CascNewStrA2T(Csv[nLine]["Path!STRING:0"].szValue); + return (hs->szCdnServers && hs->szCdnPath) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND; } static DWORD ParseFile_BuildDb(TCascStorage * hs, CASC_CSV & Csv) @@ -870,109 +899,164 @@ static DWORD ParseFile_CdnBuild(TCascStorage * hs, void * pvListFile) return dwErrCode; } -static DWORD CheckDataDirectory(TCascStorage * hs, LPTSTR szDirectory) -{ - TCHAR szDataPath[MAX_PATH]; - DWORD dwErrCode = ERROR_FILE_NOT_FOUND; - - // Try all known subdirectories - for(size_t i = 0; DataDirs[i] != NULL; i++) - { - // Create the eventual data path - CombinePath(szDataPath, _countof(szDataPath), szDirectory, DataDirs[i], NULL); - - // Does that directory exist? - if(DirectoryExists(szDataPath)) - { - hs->szRootPath = CascNewStr(szDirectory); - hs->szDataPath = CascNewStr(szDataPath); - return ERROR_SUCCESS; - } - } - - return dwErrCode; -} - -static DWORD LoadCsvFile(TCascStorage * hs, LPBYTE pbFileData, size_t cbFileData, PARSECSVFILE PfnParseProc, bool bHasHeader) +// Loads a local CSV file +static DWORD LoadCsvFile(TCascStorage * hs, LPCTSTR szFileName, PARSE_CSV_FILE PfnParseProc, bool bHasHeader) { CASC_CSV Csv(0x40, bHasHeader); DWORD dwErrCode; // Load the external file to memory - if((dwErrCode = Csv.Load(pbFileData, cbFileData)) == ERROR_SUCCESS) + if((dwErrCode = Csv.Load(szFileName)) == ERROR_SUCCESS) dwErrCode = PfnParseProc(hs, Csv); return dwErrCode; } -static DWORD LoadCsvFile(TCascStorage * hs, LPCTSTR szFileName, PARSECSVFILE PfnParseProc, bool bHasHeader) +// Loading an online file (VERSIONS or CDNS) +static DWORD LoadCsvFile(TCascStorage * hs, PARSE_REGION_LINE PfnParseRegionLine, LPCTSTR szFileName, LPCSTR szColumnName, bool bForceDownload) { - CASC_CSV Csv(0x40, bHasHeader); - DWORD dwErrCode; + CASC_PATH<TCHAR> LocalPath; + CASC_BLOB FileData; + DWORD dwErrCode = ERROR_SUCCESS; + char szFileNameA[0x20]; - // Load the external file to memory - if((dwErrCode = Csv.Load(szFileName)) == ERROR_SUCCESS) - dwErrCode = PfnParseProc(hs, Csv); + // Prepare the loading / downloading + LocalPath.SetPathRoot(hs->szRootPath); + LocalPath.AppendString(szFileName, true); + LocalPath.SetLocalCaching(bForceDownload == false); + + // If the storage is defined as online, we can download the file, if it doesn't exist + if(hs->dwFeatures & CASC_FEATURE_ONLINE) + { + // Inform the user that we are downloading something + CascStrCopy(szFileNameA, _countof(szFileNameA), szFileName); + if(InvokeProgressCallback(hs, "Downloading the \"%s\" file", szFileNameA, 0, 0)) + return ERROR_CANCELLED; + + // Download the file using Ribbit/HTTP protocol + dwErrCode = RibbitDownloadFile(hs->szCdnHostUrl, hs->szCodeName, szFileName, LocalPath, FileData); + } + else + { + // Load the local file + dwErrCode = LoadFileToMemory(LocalPath, FileData); + } + + // Load the VERSIONS file + if(dwErrCode == ERROR_SUCCESS) + { + CASC_CSV Csv(0x40, true); + + // Load the external file to memory + if((dwErrCode = Csv.Load(FileData.pbData, FileData.cbData)) == ERROR_SUCCESS) + { + size_t nLineCount = Csv.GetLineCount(); + size_t nRegionXX = CASC_INVALID_SIZE_T; + size_t nRegionUS = CASC_INVALID_SIZE_T; + size_t nRegionEU = CASC_INVALID_SIZE_T; + + // Find a matching config + for(size_t i = 0; i < nLineCount; i++) + { + const char * szRegion = Csv[i][szColumnName].szValue; + + if(hs->szRegion && szRegion && !strcmp(hs->szRegion, szRegion)) + return PfnParseRegionLine(hs, Csv, i); + + if(szRegion != NULL) + { + if(!strcmp(szRegion, "xx")) + { + nRegionXX = i; + continue; + } + + if(!strcmp(szRegion, "us")) + { + nRegionUS = i; + continue; + } + + if(!strcmp(szRegion, "eu")) + { + nRegionEU = i; + continue; + } + } + } + + // Now load the regions in this order: + // 1) US region + // 2. EU region + // 3. XX region + // 4. The first line + if(nRegionUS != CASC_INVALID_SIZE_T) + return PfnParseRegionLine(hs, Csv, nRegionUS); + if(nRegionEU != CASC_INVALID_SIZE_T) + return PfnParseRegionLine(hs, Csv, nRegionEU); + if(nRegionXX != CASC_INVALID_SIZE_T) + return PfnParseRegionLine(hs, Csv, nRegionXX); + if(nLineCount != 0) + return PfnParseRegionLine(hs, Csv, 0); + dwErrCode = ERROR_FILE_NOT_FOUND; + } + } return dwErrCode; } static DWORD ForcePathExist(LPCTSTR szFileName, bool bIsFileName) { - LPTSTR szLocalPath; - size_t nIndex; - bool bFirstSeparator = false; DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY; + bool bFirstSeparator = false; // Sanity checks if(szFileName && szFileName[0]) { - szLocalPath = CascNewStr(szFileName); - if(szLocalPath != NULL) + CASC_PATH<TCHAR> LocalPath(szFileName, NULL); + + // Get the end of search + if(bIsFileName) + LocalPath.CutLastPart(); + + // Check whether the path exists + if(_taccess(LocalPath, 0) != 0) { - // Get the end of search - if(bIsFileName) - CutLastPathPart(szLocalPath); + LPTSTR szLocalPath = (LPTSTR)(LPCTSTR)LocalPath; - // Check the entire path - if(_taccess(szLocalPath, 0) != 0) + // Search the entire path + for(size_t nIndex = 0; nIndex < LocalPath.Length(); nIndex++) { - // Searth the entire path - for(nIndex = 0; szLocalPath[nIndex] != 0; nIndex++) + if(szLocalPath[nIndex] == '\\' || szLocalPath[nIndex] == '/') { - if(szLocalPath[nIndex] == '\\' || szLocalPath[nIndex] == '/') - { - // Cut the path and verify whether the folder/file exists - szLocalPath[nIndex] = 0; + // Cut the path and verify whether the folder/file exists + szLocalPath[nIndex] = 0; - // Skip the very first separator - if(bFirstSeparator == true) + // Skip the very first separator + if(bFirstSeparator == true) + { + // Is it there? + if(DirectoryExists(szLocalPath) == false && MakeDirectory(szLocalPath) == false) { - // Is it there? - if(DirectoryExists(szLocalPath) == false && MakeDirectory(szLocalPath) == false) - { - dwErrCode = ERROR_PATH_NOT_FOUND; - break; - } + dwErrCode = ERROR_PATH_NOT_FOUND; + break; } - - // Restore the character - szLocalPath[nIndex] = PATH_SEP_CHAR; - bFirstSeparator = true; - dwErrCode = ERROR_SUCCESS; } - } - // Now check the final path - if(DirectoryExists(szLocalPath) || MakeDirectory(szLocalPath)) - { + // Restore the character + szLocalPath[nIndex] = PATH_SEP_CHAR; + bFirstSeparator = true; dwErrCode = ERROR_SUCCESS; } } - else + + // Now check the final path + if(DirectoryExists(szLocalPath) || MakeDirectory(szLocalPath)) { dwErrCode = ERROR_SUCCESS; } - - CASC_FREE(szLocalPath); + } + else + { + dwErrCode = ERROR_SUCCESS; } } @@ -1029,78 +1113,87 @@ static LPCTSTR ExtractCdnServerName(LPTSTR szServerName, size_t cchServerName, L return NULL; } -static void CreateRemoteAndLocalPath(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInfo, CASC_PATH<TCHAR> & RemotePath, CASC_PATH<TCHAR> & LocalPath) +static bool FileAlreadyExists(LPCTSTR szFileName) { - PCASC_EKEY_ENTRY pEKeyEntry; - ULONGLONG ByteMask = 1; - DWORD ArchiveIndex; - - // Append the CDN host / root folder - RemotePath.SetPathRoot(CdnsInfo.szCdnsHost); - RemotePath.AppendString(CdnsInfo.szCdnsPath, true); - LocalPath.SetPathRoot(hs->szRootPath); - - // If there is an EKey, take EKey - if(CdnsInfo.pbEKey != NULL) - { - // The file is given by EKey. It's either a loose file, or it's stored in an archive. - // We check that using the EKey map - if((pEKeyEntry = (PCASC_EKEY_ENTRY)hs->IndexMap.FindObject(CdnsInfo.pbEKey)) != NULL) - { - // Change the path type to "data" - RemotePath.AppendString(_T("data"), true); - LocalPath.AppendString(_T("data"), true); - - // Append the EKey of the archive instead of the file itself - ArchiveIndex = (DWORD)(pEKeyEntry->StorageOffset >> hs->FileOffsetBits); - CdnsInfo.pbArchiveKey = hs->ArchivesKey.pbData + (MD5_HASH_SIZE * ArchiveIndex); - RemotePath.AppendEKey(CdnsInfo.pbArchiveKey); - LocalPath.AppendEKey(CdnsInfo.pbArchiveKey); - - // Get the archive index and archive offset - CdnsInfo.ArchiveIndex = ArchiveIndex; - CdnsInfo.ArchiveOffs = pEKeyEntry->StorageOffset & ((ByteMask << hs->FileOffsetBits) - 1); - CdnsInfo.EncodedSize = pEKeyEntry->EncodedSize; - } - else - { - // Append the path type - RemotePath.AppendString(CdnsInfo.szPathType, true); - LocalPath.AppendString((CdnsInfo.szLoPaType != NULL) ? CdnsInfo.szLoPaType : CdnsInfo.szPathType, true); + TFileStream * pStream; + ULONGLONG FileSize = 0; - // Append the EKey - RemotePath.AppendEKey(CdnsInfo.pbEKey); - LocalPath.AppendEKey(CdnsInfo.pbEKey); - } - } - else + // The file open must succeed and also must be of non-zero size + if((pStream = FileStream_OpenFile(szFileName, 0)) != NULL) { - assert(CdnsInfo.szFileName != NULL); - RemotePath.AppendString(CdnsInfo.szFileName, true); - LocalPath.AppendString(CdnsInfo.szFileName, true); + FileStream_GetSize(pStream, &FileSize); + FileStream_Close(pStream); } - // Append extension - RemotePath.AppendString(CdnsInfo.szExtension, false); - LocalPath.AppendString(CdnsInfo.szExtension, false); + return (FileSize != 0); } -static bool FileAlreadyExists(LPCTSTR szFileName) +static DWORD RibbitDownloadFile(LPCTSTR szCdnHostUrl, LPCTSTR szProduct, LPCTSTR szFileName, CASC_PATH<TCHAR> & LocalPath, CASC_BLOB & FileData) { + CASC_PATH<TCHAR> LocalFile; TFileStream * pStream; ULONGLONG FileSize = 0; + TCHAR szRemoteUrl[256]; + DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE; - // The file open must succeed and also must be of non-zero size - if((pStream = FileStream_OpenFile(szFileName, 0)) != NULL) + // If required, try to load the local name first + if(LocalPath.Length() && LocalPath.LocalCaching()) { - FileStream_GetSize(pStream, &FileSize); + // Load the local file into memory + if((dwErrCode = LoadFileToMemory(LocalPath, FileData)) == ERROR_SUCCESS) + { + // Pass the file data to the caller + return ERROR_SUCCESS; + } + } + + // Supply the default CDN URL, if not present + if(szCdnHostUrl == NULL || szCdnHostUrl[0] == 0) + szCdnHostUrl = szDefaultCDN; + + // Construct the full URL (https://wowdev.wiki/Ribbit) + // Old (HTTP) download: wget http://us.patch.battle.net:1119/wow_classic/cdns + CascStrPrintf(szRemoteUrl, _countof(szRemoteUrl), _T("%s/%s/%s"), szCdnHostUrl, szProduct, szFileName); + + // Open the file stream + if((pStream = FileStream_OpenFile(szRemoteUrl, 0)) != NULL) + { + if(FileStream_GetSize(pStream, &FileSize) && FileSize <= 0x04000000) + { + // Fill-in the file pointer and size + if((dwErrCode = FileData.SetSize((size_t)FileSize)) == ERROR_SUCCESS) + { + if(FileStream_Read(pStream, NULL, FileData.pbData, (DWORD)FileSize)) + { + dwErrCode = ERROR_SUCCESS; + } + else + { + dwErrCode = GetCascError(); + FileData.Free(); + } + } + } + else + { + dwErrCode = GetCascError(); + } + + // Close the remote stream FileStream_Close(pStream); } + else + { + dwErrCode = GetCascError(); + } - return (FileSize != 0); + // Save the file to the local cache + if(LocalPath.Length() && FileData.pbData && FileData.cbData && dwErrCode == ERROR_SUCCESS) + SaveLocalFile(LocalPath, FileData.pbData, FileData.cbData); + return dwErrCode; } -static DWORD DownloadFile( +static DWORD HttpDownloadFile( LPCTSTR szRemoteName, LPCTSTR szLocalName, PULONGLONG PtrByteOffset, @@ -1162,208 +1255,161 @@ static DWORD DownloadFile( return dwErrCode; } -static DWORD RibbitDownloadFile(LPCTSTR szCdnHostUrl, LPCTSTR szProduct, LPCTSTR szFileName, CASC_PATH<TCHAR> & LocalPath, QUERY_KEY & FileData) +DWORD FetchCascFile( + TCascStorage * hs, + LPCTSTR szRootPath, + CPATH_TYPE PathType, + LPBYTE pbEKey, + LPCTSTR szExtension, + CASC_PATH<TCHAR> & LocalPath) { - CASC_PATH<TCHAR> LocalFile; - TFileStream * pStream; - ULONGLONG FileSize = 0; - TCHAR szRemoteUrl[256]; - DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE; + LPCTSTR szCdnServers = hs->szCdnServers; + DWORD dwErrCode = ERROR_SUCCESS; + TCHAR szCdnServer[MAX_PATH] = _T(""); - // If required, try to load the local name first - if(LocalPath.Length() && LocalPath.LocalCaching()) + // First, construct the local path + LocalPath.Create(szRootPath, GetSubFolder(PathType), NULL); + LocalPath.AppendEKey(pbEKey); + LocalPath.AppendString(szExtension, false); + + // Check whether the file already exists + if(!FileAlreadyExists(LocalPath)) { - LPBYTE pbFileData; - DWORD cbFileData = 0; + // If this is not an online version, do nothing and return error + if(!(hs->dwFeatures & CASC_FEATURE_ONLINE)) + return ERROR_FILE_NOT_FOUND; - // Load the local file into memory - pbFileData = LoadFileToMemory(LocalPath, &cbFileData); - if(pbFileData && cbFileData) + // Force-create the local path + if((dwErrCode = ForcePathExist(LocalPath, true)) != ERROR_SUCCESS) + return dwErrCode; + + // Try all download servers + while((szCdnServers = ExtractCdnServerName(szCdnServer, _countof(szCdnServer), szCdnServers)) != NULL) { - // Pass the file data to the caller - FileData.pbData = pbFileData; - FileData.cbData = cbFileData; - return ERROR_SUCCESS; - } - } + CASC_PATH<TCHAR> RemotePath(URL_SEP_CHAR); - // Supply the default CDN URL, if not present - if(szCdnHostUrl == NULL || szCdnHostUrl[0] == 0) - szCdnHostUrl = szDefaultCDN; + // Construct the full remote URL path + RemotePath.Create(szCdnServer, hs->szCdnPath, GetSubFolder(PathType), NULL); + RemotePath.AppendEKey(pbEKey); + RemotePath.AppendString(szExtension, false); - // Construct the full URL (https://wowdev.wiki/Ribbit) - // Old (HTTP) download: wget http://us.patch.battle.net:1119/wow_classic/cdns - CascStrPrintf(szRemoteUrl, _countof(szRemoteUrl), _T("%s/%s/%s"), szCdnHostUrl, szProduct, szFileName); + // Attempt to download the file + dwErrCode = HttpDownloadFile(RemotePath, LocalPath, NULL, 0, 0); - // Open the file stream - if((pStream = FileStream_OpenFile(szRemoteUrl, 0)) != NULL) - { - if(FileStream_GetSize(pStream, &FileSize) && FileSize <= 0x04000000) - { - // Fill-in the file pointer and size - FileData.pbData = CASC_ALLOC<BYTE>((size_t)FileSize); - if(FileData.pbData != NULL) - { - if(FileStream_Read(pStream, NULL, FileData.pbData, (DWORD)FileSize)) - { - FileData.cbData = (size_t)FileSize; - dwErrCode = ERROR_SUCCESS; - } - else - { - dwErrCode = GetCascError(); - CASC_FREE(FileData.pbData); - FileData.pbData = NULL; - } - } - else - { - dwErrCode = ERROR_NOT_ENOUGH_MEMORY; - } - } - else - { - dwErrCode = GetCascError(); + // Stop on low memory condition, as it will most likely + // end up with low memory on next download + if(dwErrCode == ERROR_SUCCESS || dwErrCode == ERROR_NOT_ENOUGH_MEMORY) + return dwErrCode; } - - // Close the remote stream - FileStream_Close(pStream); - } - else - { - dwErrCode = GetCascError(); + + // Sorry, the file was not found + dwErrCode = ERROR_FILE_NOT_FOUND; } - - // Save the file to the local cache - if(LocalPath.Length() && FileData.pbData && FileData.cbData && dwErrCode == ERROR_SUCCESS) - SaveLocalFile(LocalPath, FileData.pbData, FileData.cbData); return dwErrCode; } -static DWORD DownloadFileFromCDN2(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInfo) +DWORD FetchCascFile(TCascStorage * hs, CPATH_TYPE PathType, LPBYTE pbEKey, LPCTSTR szExtension, CASC_PATH<TCHAR> & LocalPath, PCASC_ARCHIVE_INFO pArchiveInfo) { - CASC_PATH<TCHAR> RemotePath(URL_SEP_CHAR); - CASC_PATH<TCHAR> LocalPath(PATH_SEP_CHAR); - DWORD dwErrCode; - - // Assemble both the remote and local path - assert(CdnsInfo.szCdnsHost != NULL && CdnsInfo.szCdnsHost[0] != 0); - CreateRemoteAndLocalPath(hs, CdnsInfo, RemotePath, LocalPath); + PCASC_EKEY_ENTRY pEKeyEntry; + LPBYTE pbArchiveKey; + DWORD dwErrCode = ERROR_SUCCESS; - // Check whether the local file exists - if((CdnsInfo.Flags & CASC_CDN_FORCE_DOWNLOAD) || !FileAlreadyExists(LocalPath)) + // Data files may be stored in archives, therefore we need to check + if((pbEKey != NULL) && (pEKeyEntry = (PCASC_EKEY_ENTRY)hs->IndexMap.FindObject(pbEKey)) != NULL) { - // Make sure that the path exists - dwErrCode = ForcePathExist(LocalPath, true); - if(dwErrCode != ERROR_SUCCESS) - return dwErrCode; - - // Attempt to download the file - dwErrCode = DownloadFile(RemotePath, LocalPath, NULL, 0, 0); - if(dwErrCode != ERROR_SUCCESS) - return dwErrCode; + // Can't complete if the caller doesn't know the archive info + if(pArchiveInfo != NULL) + { + // Fill-in the archive info + pArchiveInfo->ArchiveIndex = (DWORD)(pEKeyEntry->StorageOffset >> hs->FileOffsetBits); + pArchiveInfo->ArchiveOffs = (DWORD)(pEKeyEntry->StorageOffset & ((ValueOne64 << hs->FileOffsetBits) - 1)); + pArchiveInfo->EncodedSize = pEKeyEntry->EncodedSize; + + // Fill-in the archive key + pbArchiveKey = pbEKey = hs->ArchivesKey.pbData + (MD5_HASH_SIZE * pArchiveInfo->ArchiveIndex); + memcpy(pArchiveInfo->ArchiveKey, pbArchiveKey, MD5_HASH_SIZE); + + // Remap the path type to "data" + PathType = PathTypeData; + } + else + { + dwErrCode = ERROR_CAN_NOT_COMPLETE; + } } - // Give the path to the caller, if any - LocalPath.Copy(CdnsInfo.szLocalPath, CdnsInfo.ccLocalPath); - return ERROR_SUCCESS; -} - -DWORD DownloadFileFromCDN(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInfo) -{ - LPCTSTR szCdnServers = hs->szCdnServers; - TCHAR szCdnHost[MAX_PATH] = _T(""); - DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE; - - // If we have a given CDN server, use it. If not, try all CDNs - // from the storage's configuration - if(CdnsInfo.szCdnsHost == NULL) + if(dwErrCode == ERROR_SUCCESS) { - // Supply the CDN host - CdnsInfo.szCdnsHost = szCdnHost; + // Try the local archives + if(hs->dwFeatures & CASC_FEATURE_DATA_ARCHIVES) + { + dwErrCode = FetchCascFile(hs, hs->szDataPath, PathType, pbEKey, szExtension, LocalPath); + if(dwErrCode == ERROR_SUCCESS) + return ERROR_SUCCESS; + } - // Try all download servers - while((szCdnServers = ExtractCdnServerName(szCdnHost, _countof(szCdnHost), szCdnServers)) != NULL) + // Try to download the file into the "data/<type>" path + if(hs->szDataPath != NULL) { - // Attempt to download the file from the remote server - dwErrCode = DownloadFileFromCDN2(hs, CdnsInfo); - - // If the download succeeded, exit immediately. - // Also exit when there is a low memory condition, - // as it will most likely end up with low memory on next download - if(dwErrCode == ERROR_SUCCESS || dwErrCode == ERROR_NOT_ENOUGH_MEMORY) - break; + dwErrCode = FetchCascFile(hs, hs->szDataPath, PathType, pbEKey, szExtension, LocalPath); + if(dwErrCode == ERROR_SUCCESS) + return ERROR_SUCCESS; } - // Don't give the local buffer pointer to the caller - CdnsInfo.szCdnsHost = NULL; - } - else - { - dwErrCode = DownloadFileFromCDN2(hs, CdnsInfo); + // Try to download the file into the "<type>" path + if(hs->szRootPath != NULL) + { + dwErrCode = FetchCascFile(hs, hs->szRootPath, PathType, pbEKey, szExtension, LocalPath); + if(dwErrCode == ERROR_SUCCESS) + return ERROR_SUCCESS; + } } - return dwErrCode; } -static DWORD FetchAndLoadConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey, PARSETEXTFILE PfnParseProc) +static DWORD FetchAndLoadConfigFile(TCascStorage * hs, PCASC_BLOB pFileKey, PARSE_TEXT_FILE PfnParseProc) { - LPCTSTR szPathType = _T("config"); - TCHAR szLocalPath[MAX_PATH]; - TCHAR szSubDir[0x80] = _T(""); + CASC_PATH<TCHAR> LocalPath; void * pvListFile = NULL; - DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE; - - // If online storage, we download the file. Otherwise, create a local path - if(hs->dwFeatures & CASC_FEATURE_ONLINE) - { - CASC_CDN_DOWNLOAD CdnsInfo = {0}; - - // Prepare the download structure for "%CDNS_HOST%/%CDNS_PATH%/##/##/EKey" file - CdnsInfo.szCdnsPath = hs->szCdnPath; - CdnsInfo.szPathType = szPathType; - CdnsInfo.pbEKey = pFileKey->pbData; - CdnsInfo.szLocalPath = szLocalPath; - CdnsInfo.ccLocalPath = _countof(szLocalPath); - - // Download the config file - dwErrCode = DownloadFileFromCDN(hs, CdnsInfo); - if(dwErrCode != ERROR_SUCCESS) - return dwErrCode; - } - else - { - CASC_PATH<TCHAR> Path; - - Path.AppendString(hs->szDataPath, false); - Path.AppendString(szSubDir, true); - Path.AppendString(szPathType, true); - Path.AppendEKey(pFileKey->pbData); - Path.Copy(szLocalPath, _countof(szLocalPath)); - } + DWORD dwErrCode; - // Load and verify the external listfile - pvListFile = ListFile_OpenExternal(szLocalPath); - if(pvListFile != NULL) + // Make sure there is a local copy of the file + dwErrCode = FetchCascFile(hs, PathTypeConfig, pFileKey->pbData, NULL, LocalPath); + if(dwErrCode == ERROR_SUCCESS) { - if(ListFile_VerifyMD5(pvListFile, pFileKey->pbData)) + // Load and verify the external listfile + pvListFile = ListFile_OpenExternal(LocalPath); + if(pvListFile != NULL) { - dwErrCode = PfnParseProc(hs, pvListFile); + if(ListFile_VerifyMD5(pvListFile, pFileKey->pbData)) + { + dwErrCode = PfnParseProc(hs, pvListFile); + } + else + { + dwErrCode = ERROR_FILE_CORRUPT; + } + CASC_FREE(pvListFile); } else { - dwErrCode = ERROR_FILE_CORRUPT; + dwErrCode = ERROR_FILE_NOT_FOUND; } - CASC_FREE(pvListFile); - } - else - { - dwErrCode = ERROR_FILE_NOT_FOUND; } - return dwErrCode; } +static LPTSTR CheckForDirectory(LPCTSTR szParentFolder, LPCTSTR szSubFolder) +{ + CASC_PATH<TCHAR> LocalPath(szParentFolder, szSubFolder, NULL); + LPTSTR szLocalPath = NULL; + + // Check whether the path exists + if(DirectoryExists(LocalPath)) + szLocalPath = LocalPath.New(); + return szLocalPath; +} + //----------------------------------------------------------------------------- // Public functions @@ -1408,128 +1454,220 @@ DWORD GetFileSpanInfo(PCASC_CKEY_ENTRY pCKeyEntry, PULONGLONG PtrContentSize, PU return dwSpanCount; } -// Checks whether there is a ".build.info" or ".build.db". -// If yes, the function sets "szDataPath" and "szIndexPath" -// in the storage structure and returns ERROR_SUCCESS -DWORD CheckGameDirectory(TCascStorage * hs, LPTSTR szDirectory) +DWORD CheckCascBuildFileExact(CASC_BUILD_FILE & BuildFile, LPCTSTR szLocalPath) { + // Calculate the length of the local path TFileStream * pStream; - TCHAR szBuildFile[MAX_PATH]; - DWORD dwErrCode = ERROR_FILE_NOT_FOUND; + LPCTSTR szFileType; + size_t nLength = _tcslen(szLocalPath); - // Try to find any of the root files used in the history - for (size_t i = 0; BuildTypes[i].szFileName != NULL; i++) - { - // Create the full name of the .agent.db file - CombinePath(szBuildFile, _countof(szBuildFile), szDirectory, BuildTypes[i].szFileName, NULL); + // Clear the build file structure + memset(&BuildFile, 0, sizeof(CASC_BUILD_FILE)); - // Attempt to open the file - pStream = FileStream_OpenFile(szBuildFile, STREAM_FLAG_READ_ONLY); - if(pStream != NULL) + // Check every type of the build file + for(size_t i = 0; i < _countof(BuildTypes); i++) + { + // We support any file name with the appropriate ending, + // for example wow-19342.build.info or wow-47186.versions + szFileType = szLocalPath + nLength - BuildTypes[i].nLength; + if(!_tcsicmp(BuildTypes[i].szFileName, szFileType)) { - // Free the stream - FileStream_Close(pStream); - - // Check for the data directory - dwErrCode = CheckDataDirectory(hs, szDirectory); - if(dwErrCode == ERROR_SUCCESS) + // We also try to open the file + if((pStream = FileStream_OpenFile(szLocalPath, 0)) != NULL) { - hs->szBuildFile = CascNewStr(szBuildFile); - hs->BuildFileType = BuildTypes[i].BuildFileType; + CascStrCopy(BuildFile.szFullPath, _countof(BuildFile.szFullPath), szLocalPath); + BuildFile.szPlainName = GetPlainFileName(BuildFile.szFullPath); + BuildFile.BuildFileType = BuildTypes[i].BuildFileType; + FileStream_Close(pStream); return ERROR_SUCCESS; } } } - return dwErrCode; + // Unrecognized file name + return ERROR_BAD_FORMAT; } -DWORD LoadBuildInfo(TCascStorage * hs) +DWORD CheckCascBuildFileDirs(CASC_BUILD_FILE & BuildFile, LPCTSTR szLocalPath) { - PARSECSVFILE PfnParseProc = NULL; - DWORD dwErrCode; - bool bHasHeader = true; + CASC_PATH<TCHAR> WorkPath(szLocalPath, NULL); + DWORD dwErrCode = ERROR_FILE_NOT_FOUND; + + // Clear the build file structure + memset(&BuildFile, 0, sizeof(CASC_BUILD_FILE)); - // We support ".build.info", ".build.db" or "versions" - switch (hs->BuildFileType) + for(;;) { - case CascBuildInfo: - PfnParseProc = ParseFile_BuildInfo; - break; + // Try all supported build file types + for(size_t i = 0; i < _countof(BuildTypes); i++) + { + CASC_PATH<TCHAR> FilePath(WorkPath, BuildTypes[i].szFileName, NULL); - case CascVersionsDb: - PfnParseProc = ParseFile_VersionsDb; - break; + // If the file exists there, we found it + if(CheckCascBuildFileExact(BuildFile, FilePath) == ERROR_SUCCESS) + { + return ERROR_SUCCESS; + } + } - case CascBuildDb: - PfnParseProc = ParseFile_BuildDb; - bHasHeader = false; + // Try to cut off one path path + if(!WorkPath.CutLastPart()) + { + dwErrCode = ERROR_PATH_NOT_FOUND; break; - - default: - return ERROR_NOT_SUPPORTED; + } } - // If the storage is online storage, we need to download "versions" - if(hs->dwFeatures & CASC_FEATURE_ONLINE) + // Unrecognized file name + return dwErrCode; +} + +DWORD CheckOnlineStorage(PCASC_OPEN_STORAGE_ARGS pArgs, CASC_BUILD_FILE & BuildFile, DWORD dwFeatures) +{ + // If the online storage is required, we try to extract the product code + if((dwFeatures & CASC_FEATURE_ONLINE) && (pArgs->szCodeName != NULL)) { - CASC_PATH<TCHAR> LocalPath; - QUERY_KEY FileData; - LPCTSTR szVersionsName = _T("versions"); + CASC_PATH<TCHAR> FilePath(pArgs->szLocalPath, _T("versions"), NULL); - // Inform the user about loading the build.info/build.db/versions - if(InvokeProgressCallback(hs, "Downloading the \"versions\" file", NULL, 0, 0)) - return ERROR_CANCELLED; + FilePath.CopyTo(BuildFile.szFullPath, _countof(BuildFile.szFullPath)); + BuildFile.szPlainName = GetPlainFileName(BuildFile.szFullPath); + BuildFile.BuildFileType = CascVersions; + return ERROR_SUCCESS; + } + return ERROR_FILE_NOT_FOUND; +} - // Shall we prefer the locally cached file? - LocalPath.SetPathRoot(hs->szRootPath); - LocalPath.AppendString(szVersionsName, true); - LocalPath.SetLocalCaching(hs->dwFeatures & CASC_FEATURE_LOCAL_VERSIONS); +DWORD CheckArchiveFilesDirectories(TCascStorage * hs) +{ + // Sanity checks + assert((hs->dwFeatures & CASC_FEATURE_DATA_ARCHIVES) != 0); + assert(hs->szRootPath != NULL); + assert(hs->szDataPath == NULL); + assert(hs->szIndexPath == NULL); - // Download the file using Ribbit/HTTP protocol - dwErrCode = RibbitDownloadFile(hs->szCdnHostUrl, hs->szCodeName, szVersionsName, LocalPath, FileData); - if(dwErrCode == ERROR_SUCCESS) + LPTSTR szDataPath = NULL; + LPTSTR szConfigPath = NULL; + LPTSTR szIndexPath = NULL; + + // Try all known subdirectories for the game data sub dirs + for(size_t i = 0; i < _countof(DataDirs); i++) + { + if((szDataPath = CheckForDirectory(hs->szRootPath, DataDirs[i])) != NULL) { - // Parse the downloaded file - dwErrCode = LoadCsvFile(hs, FileData.pbData, FileData.cbData, ParseFile_VersionsDb, true); + // If we found the data path, we also need to initialize the index path + + // Check the config folder + if((szConfigPath = CheckForDirectory(szDataPath, _T("config"))) != NULL) + { + // First, check for more common "data" subdirectory + if((szIndexPath = CheckForDirectory(szDataPath, _T("data"))) == NULL) + { + // Second, try the "darch" subdirectory (older builds of HOTS - Alpha) + szIndexPath = CheckForDirectory(szDataPath, _T("darch")); + } + + if(szIndexPath != NULL) + { + hs->szDataPath = szDataPath; + hs->szConfigPath = szConfigPath; + hs->szIndexPath = szIndexPath; + return ERROR_SUCCESS; + } + } } - } - else - { - assert(hs->szBuildFile != NULL); - dwErrCode = LoadCsvFile(hs, hs->szBuildFile, PfnParseProc, bHasHeader); + + CASC_FREE(szDataPath); + CASC_FREE(szConfigPath); + CASC_FREE(szIndexPath); } - return dwErrCode; + // One of the paths was not found + return ERROR_FILE_NOT_FOUND; } -DWORD LoadCdnsFile(TCascStorage * hs) +DWORD CheckDataFilesDirectory(TCascStorage * hs) { - CASC_PATH<TCHAR> LocalPath; - QUERY_KEY FileData; - LPCTSTR szCdnsName = _T("cdns"); - DWORD dwErrCode = ERROR_SUCCESS; + CASC_PATH<TCHAR> DataPath(hs->szRootPath, _T("data"), NULL); + bool bTwoDigitFolderFound = false; - // Sanity checks - assert(hs->dwFeatures & CASC_FEATURE_ONLINE); + // When CASC_FEATURE_ONLINE is not set, then the folder must exist + if((hs->dwFeatures & CASC_FEATURE_ONLINE) == 0) + { + // Check if there are subfolders at all. If not, do not bother + // the file system with open requests into data files folder + if(ScanDirectory(DataPath, CheckForTwoDigitFolder, NULL, &bTwoDigitFolderFound) != ERROR_SUCCESS) + return ERROR_PATH_NOT_FOUND; - // Inform the user about what we are doing - if(InvokeProgressCallback(hs, "Downloading the \"cdns\" file", NULL, 0, 0)) - return ERROR_CANCELLED; + if(bTwoDigitFolderFound == false) + return ERROR_PATH_NOT_FOUND; + } - // Construct the local path of the file - LocalPath.SetPathRoot(hs->szRootPath); - LocalPath.AppendString(szCdnsName, true); - LocalPath.SetLocalCaching(hs->dwFeatures & CASC_FEATURE_LOCAL_CDNS); + // Create the path for raw files + if((hs->szFilesPath = DataPath.New()) == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + return ERROR_SUCCESS; +} + +DWORD LoadBuildFile_Versions_Cdns(TCascStorage * hs) +{ + LPCTSTR szVersions = _T("versions"); + DWORD dwErrCode; + TCHAR szBuffer[MAX_PATH]; + bool bForceDownload = (hs->dwFeatures & CASC_FEATURE_FORCE_DOWNLOAD) ? true : false; - // Download the file using Ribbit protocol - dwErrCode = RibbitDownloadFile(hs->szCdnHostUrl, hs->szCodeName, szCdnsName, LocalPath, FileData); + // The default name of the build file is "versions". However, the caller may select different file + if(hs->szMainFile && hs->szMainFile[0]) + szVersions = GetPlainFileName(hs->szMainFile); + dwErrCode = LoadCsvFile(hs, ParseRegionLine_Versions, szVersions, "Region!STRING:0", bForceDownload); + + // We also need to load the "cdns" file if(dwErrCode == ERROR_SUCCESS) { - // Parse the downloaded file - dwErrCode = LoadCsvFile(hs, FileData.pbData, FileData.cbData, ParseFile_CDNS, true); + // The default CDNS file is "cdns". However, if the build file is different, we change "cdns" as well + // Example: If the build file name is "hearthstone-25.0.3.160183.159202.versions", then we want "hearthstone-25.0.3.160183.159202.cdns" + if(hs->szMainFile && hs->szMainFile[0]) + { + if(ReplaceVersionsWithCdns(szBuffer, _countof(szBuffer), hs->szMainFile)) + { + dwErrCode = LoadCsvFile(hs, ParseRegionLine_Cdns, szBuffer, "Name!STRING:0", bForceDownload); + if(dwErrCode == ERROR_SUCCESS) + return dwErrCode; + } + } + + // Fall back to the default "cdns" file + dwErrCode = LoadCsvFile(hs, ParseRegionLine_Cdns, _T("cdns"), "Name!STRING:0", bForceDownload); } + return dwErrCode; +} + +DWORD LoadMainFile(TCascStorage * hs) +{ + DWORD dwErrCode = ERROR_NOT_SUPPORTED; + // The build file must be known at this point, even for online storage + // that are to bo created + assert(hs->szMainFile != NULL); + + // Perform support for each build file type + switch(hs->BuildFileType) + { + case CascBuildDb: // Older storages have "build.db" + dwErrCode = LoadCsvFile(hs, hs->szMainFile, ParseFile_BuildDb, false); + break; + + case CascBuildInfo: // Current storages have "build.db" + dwErrCode = LoadCsvFile(hs, hs->szMainFile, ParseFile_BuildInfo, true); + break; + + case CascVersions: // Online storages have "versions+cdns" + dwErrCode = LoadBuildFile_Versions_Cdns(hs); + break; + + default: + assert(false); + break; + } return dwErrCode; } @@ -1560,9 +1698,8 @@ DWORD LoadCdnBuildFile(TCascStorage * hs) return FetchAndLoadConfigFile(hs, &hs->CdnBuildKey, ParseFile_CdnBuild); } -LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD * pcbFileData) +DWORD LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, CASC_BLOB & FileData) { - LPBYTE pbFileData = NULL; HANDLE hFile = NULL; DWORD dwFileSizeHi = 0; DWORD cbFileData = 0; @@ -1594,11 +1731,10 @@ LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, if(dwErrCode == ERROR_SUCCESS) { // Allocate space for the ENCODING file - pbFileData = CASC_ALLOC<BYTE>(cbFileData); - if(pbFileData != NULL) + if((dwErrCode = FileData.SetSize(cbFileData)) == ERROR_SUCCESS) { // Read the entire file to memory - CascReadFile(hFile, pbFileData, cbFileData, &dwBytesRead); + CascReadFile(hFile, FileData.pbData, cbFileData, &dwBytesRead); if(dwBytesRead != cbFileData) { dwErrCode = ERROR_FILE_CORRUPT; @@ -1620,27 +1756,15 @@ LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, // Handle errors if(dwErrCode != ERROR_SUCCESS) - { - // Free the file data - CASC_FREE(pbFileData); - cbFileData = 0; - - // Set the last error - SetCascError(dwErrCode); - } - - // Give the loaded file length - if(pcbFileData != NULL) - *pcbFileData = cbFileData; - return pbFileData; + FileData.Free(); + return dwErrCode; } -LPBYTE LoadFileToMemory(LPCTSTR szFileName, DWORD * pcbFileData) +DWORD LoadFileToMemory(LPCTSTR szFileName, CASC_BLOB & FileData) { TFileStream * pStream; ULONGLONG FileSize = 0; - LPBYTE pbFileData = NULL; - DWORD cbFileData = 0; + DWORD dwErrCode = ERROR_SUCCESS; // Open the stream for read-only access and read the file pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_FLAG_WRITE_SHARE | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE); @@ -1648,47 +1772,44 @@ LPBYTE LoadFileToMemory(LPCTSTR szFileName, DWORD * pcbFileData) { // Retrieve the file size FileStream_GetSize(pStream, &FileSize); - cbFileData = (DWORD)FileSize; // Do not load zero files or too large files if(0 < FileSize && FileSize <= 0x2000000) { // Allocate file data buffer. Make it 1 byte longer // so string functions can put '\0' there - pbFileData = CASC_ALLOC<BYTE>(cbFileData + 1); - if(pbFileData != NULL) + dwErrCode = FileData.SetSize((size_t)(FileSize)); + if(dwErrCode == ERROR_SUCCESS) { - if(FileStream_Read(pStream, NULL, pbFileData, cbFileData)) + if(FileStream_Read(pStream, NULL, FileData.pbData, (DWORD)(FileData.cbData))) { // Terminate the data with zero so various string-based functions can process it - pbFileData[cbFileData] = 0; + FileData.pbData[FileData.cbData] = 0; } else { - CASC_FREE(pbFileData); - cbFileData = 0; + dwErrCode = GetCascError(); } } else { - SetCascError(ERROR_NOT_ENOUGH_MEMORY); - cbFileData = 0; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } } else { - SetCascError(ERROR_BAD_FORMAT); - cbFileData = 0; + dwErrCode = ERROR_BAD_FORMAT; } // Close the file stream FileStream_Close(pStream); } - - // Give out values - if(pcbFileData != NULL) - pcbFileData[0] = cbFileData; - return pbFileData; + else + { + dwErrCode = GetCascError(); + } + + return dwErrCode; } //----------------------------------------------------------------------------- @@ -1702,7 +1823,7 @@ LPCTSTR WINAPI CascCdnGetDefault() LPBYTE WINAPI CascCdnDownload(LPCTSTR szCdnHostUrl, LPCTSTR szProduct, LPCTSTR szFileName, DWORD * PtrSize) { CASC_PATH<TCHAR> LocalPath; - QUERY_KEY FileData; + CASC_BLOB FileData; LPBYTE pbFileData = NULL; size_t cbFileData = 0; DWORD dwErrCode; diff --git a/dep/CascLib/src/CascIndexFiles.cpp b/dep/CascLib/src/CascIndexFiles.cpp index 98416c2b360..45649eaa623 100644 --- a/dep/CascLib/src/CascIndexFiles.cpp +++ b/dep/CascLib/src/CascIndexFiles.cpp @@ -27,21 +27,40 @@ typedef bool (*EKEY_ENTRY_CALLBACK)(TCascStorage * hs, CASC_INDEX_HEADER & InHea //----------------------------------------------------------------------------- // Local functions -// "data.iXY" +// Check a file name with mask +static bool IsFileNameMask(LPCTSTR szFileName, LPCTSTR szMask) +{ + size_t i; + + for(i = 0; szFileName[i] != 0 && szMask[i] != 0; i++) + { + // '#' in the mask means any hexa number + if(szMask[i] == _T('#')) + { + if(!IsHexadecimalDigit(szFileName[i])) + return false; + continue; + } + + // Compare both characters, converted to lowercase + if(AsciiToUpperTable_BkSlash[(BYTE)(szMask[i])] != AsciiToUpperTable_BkSlash[(BYTE)(szFileName[i])]) + return false; + } + + // If we found end of both strings, it's a match + return (szFileName[i] == 0 && szMask[i] == 0); +} + +// Heroes of the Storm, build 29049: "data.i##" static bool IsIndexFileName_V1(LPCTSTR szFileName) { - // Check if the name looks like a valid index file - return (_tcslen(szFileName) == 8 && - _tcsnicmp(szFileName, _T("data.i"), 6) == 0 && - _tcsspn(szFileName + 6, szAllowedHexChars) == 2); + return IsFileNameMask(szFileName, _T("data.i##")); } +// Current CASC storages: ##########.idx static bool IsIndexFileName_V2(LPCTSTR szFileName) { - // Check if the name looks like a valid index file - return (_tcslen(szFileName) == 14 && - _tcsspn(szFileName, _T("0123456789aAbBcCdDeEfF")) == 0x0A && - _tcsicmp(szFileName + 0x0A, _T(".idx")) == 0); + return IsFileNameMask(szFileName, _T("##########.idx")); } static bool IndexDirectory_OnFileFound( @@ -61,32 +80,32 @@ static bool IndexDirectory_OnFileFound( else if(IsIndexFileName_V1(szFileName)) hs->szIndexFormat = szIndexFormat_V1; else - return false; + return true; } if(hs->szIndexFormat == szIndexFormat_V2) { // Check the index file name format if(!IsIndexFileName_V2(szFileName)) - return false; + return true; // Get the main index from the first two digits if(ConvertStringToInt(szFileName + 0, 2, IndexValue) != ERROR_SUCCESS) - return false; + return true; if(ConvertStringToInt(szFileName + 2, 8, IndexVersion) != ERROR_SUCCESS) - return false; + return true; } else if(hs->szIndexFormat == szIndexFormat_V1) { // Check the index file name format if(!IsIndexFileName_V1(szFileName)) - return false; + return true; // Get the main index from the first two digits if(ConvertStringToInt(szFileName + 6, 1, IndexValue) != ERROR_SUCCESS) - return false; + return true; if(ConvertStringToInt(szFileName + 7, 1, IndexVersion) != ERROR_SUCCESS) - return false; + return true; } else { @@ -118,7 +137,6 @@ static bool IndexDirectory_OnFileFound( static LPTSTR CreateIndexFileName(TCascStorage * hs, DWORD IndexValue, DWORD IndexVersion) { - TCHAR szFullName[MAX_PATH]; TCHAR szPlainName[0x40]; // Sanity checks @@ -126,12 +144,11 @@ static LPTSTR CreateIndexFileName(TCascStorage * hs, DWORD IndexValue, DWORD Ind assert(hs->szIndexPath != NULL); assert(IndexValue <= 0x0F); - // Create the full path + // Create the plain name of the index file CascStrPrintf(szPlainName, _countof(szPlainName), hs->szIndexFormat, IndexValue, IndexVersion); - CombinePath(szFullName, _countof(szFullName), hs->szIndexPath, szPlainName, NULL); - // Return allocated path - return CascNewStr(szFullName); + // Allocate path + return CASC_PATH<TCHAR>(hs->szIndexPath, szPlainName, NULL).New(); } static void SaveFileOffsetBitsAndEKeyLength(TCascStorage * hs, BYTE FileOffsetBits, BYTE EKeyLength) @@ -177,9 +194,9 @@ static LPBYTE CaptureGuardedBlock2(LPBYTE pbFileData, LPBYTE pbFileEnd, size_t E // Check the guarded block. There must be enough bytes to contain FILE_INDEX_GUARDED_BLOCK // and also the block length must not be NULL - if ((pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK)) >= pbFileEnd) + if((pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK)) >= pbFileEnd) return NULL; - if (pBlock->BlockSize == 0 || (pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK) + pBlock->BlockSize) > pbFileEnd) + if(pBlock->BlockSize == 0 || (pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK) + pBlock->BlockSize) > pbFileEnd) return NULL; // @@ -188,17 +205,17 @@ static LPBYTE CaptureGuardedBlock2(LPBYTE pbFileData, LPBYTE pbFileEnd, size_t E pbEntryPtr = pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK); EntryCount = pBlock->BlockSize / EntryLength; - for (size_t i = 0; i < EntryCount; i++) + for(size_t i = 0; i < EntryCount; i++) { hashlittle2(pbEntryPtr, EntryLength, &HashHigh, &HashLow); pbEntryPtr += EntryLength; } // Verify hash - if (HashHigh == pBlock->BlockHash) + if(HashHigh == pBlock->BlockHash) { // Give the output - if (PtrBlockSize != NULL) + if(PtrBlockSize != NULL) PtrBlockSize[0] = pBlock->BlockSize; return (LPBYTE)(pBlock + 1); } @@ -211,17 +228,17 @@ static LPBYTE CaptureGuardedBlock2(LPBYTE pbFileData, LPBYTE pbFileEnd, size_t E pbEntryPtr = pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK); EntryCount = pBlock->BlockSize / EntryLength; - for (size_t i = 0; i < EntryCount; i++) + for(size_t i = 0; i < EntryCount; i++) { HashBlizzGet = hashlittle(pbEntryPtr, EntryLength, HashBlizzGet); pbEntryPtr += EntryLength; } // Verify hash - if (HashBlizzGet == pBlock->BlockHash) + if(HashBlizzGet == pBlock->BlockHash) { // Give the output - if (PtrBlockSize != NULL) + if(PtrBlockSize != NULL) PtrBlockSize[0] = pBlock->BlockSize; return (LPBYTE)(pBlock + 1); } @@ -239,9 +256,9 @@ static LPBYTE CaptureGuardedBlock3(LPBYTE pbFileData, LPBYTE pbFileEnd, size_t E // Check the guarded block. There must be enough bytes to contain single entry and the hash // Also, the hash must be present - if ((pbFileData + sizeof(DWORD) + EntryLength) >= pbFileEnd) + if((pbFileData + sizeof(DWORD) + EntryLength) >= pbFileEnd) return NULL; - if (PtrEntryHash[0] == 0) + if(PtrEntryHash[0] == 0) return NULL; EntryHash = hashlittle(pbFileData + sizeof(DWORD), EntryLength+1, 0) | 0x80000000; @@ -251,69 +268,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 - pbEKeyEntry = CaptureEncodedKey(pEKeyEntry->EKey, pbEKeyEntry, InHeader.EKeyLength); - - // Copy the storage offset and encoded size - pEKeyEntry->StorageOffset = ConvertBytesToInteger_5(pbEKeyEntry); - pEKeyEntry->EncodedSize = ConvertBytesToInteger_4_LE(pbEKeyEntry + InHeader.StorageOffsetLength); - pEKeyEntry->Alignment = 0; - - // We ignore items that have EncodedSize of 0x1E - return (pEKeyEntry->EncodedSize > FIELD_OFFSET(BLTE_ENCODED_HEADER, Signature)); -} -static void InsertCKeyEntry(TCascStorage * hs, CASC_EKEY_ENTRY & EKeyEntry, DWORD Flags) -{ - PCASC_CKEY_ENTRY pCKeyEntry; - - // Multiple items with the same EKey in the index files may exist. - // Example: "2018 - New CASC\00001", EKey 37 89 16 5b 2d cc 71 c1 25 00 00 00 00 00 00 00 - // Positions: 0x1D, 0x1E, 0x1F - // In that case, we only take the first one into account - // BREAK_ON_XKEY3(EKeyEntry.EKey, 0x09, 0xF3, 0xCD); - - // If the item is not there yet, insert a new one - if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKeyEntry.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; - - // Fill-in the information - ZeroMemory16(pCKeyEntry->CKey); - CopyMemory16(pCKeyEntry->EKey, EKeyEntry.EKey); - pCKeyEntry->StorageOffset = EKeyEntry.StorageOffset; - pCKeyEntry->TagBitMask = 0; - pCKeyEntry->ContentSize = CASC_INVALID_SIZE; - pCKeyEntry->EncodedSize = EKeyEntry.EncodedSize; - pCKeyEntry->Flags = CASC_CE_HAS_EKEY | CASC_CE_HAS_EKEY_PARTIAL; - pCKeyEntry->RefCount = 0; - pCKeyEntry->SpanCount = 1; - pCKeyEntry->Priority = 0; - - // Insert the item to the EKey table - hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey); - } - else - { - // The entry already exists. True e.g. for ENCODING. - // Only copy the storage offset and sizes if not available yet - if(pCKeyEntry->StorageOffset == CASC_INVALID_OFFS64) - { - pCKeyEntry->StorageOffset = EKeyEntry.StorageOffset; - pCKeyEntry->EncodedSize = EKeyEntry.EncodedSize; - } - } - - // Add the extra flag - pCKeyEntry->Flags |= Flags; -} -*/ static DWORD LoadIndexItems(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, EKEY_ENTRY_CALLBACK PfnEKeyEntry, LPBYTE pbEKeyEntry, LPBYTE pbEKeyEnd) { size_t EntryLength = InHeader.EntryLength; @@ -539,7 +494,7 @@ static DWORD ProcessLocalIndexFiles(TCascStorage * hs, EKEY_ENTRY_CALLBACK PfnEK } // Load the index file - if((dwErrCode = LoadIndexFile(hs, PfnEKeyEntry, IndexFile.pbFileData, IndexFile.cbFileData, i)) != ERROR_SUCCESS) + if((dwErrCode = LoadIndexFile(hs, PfnEKeyEntry, IndexFile.FileData.pbData, IndexFile.FileData.cbData, i)) != ERROR_SUCCESS) break; } @@ -563,7 +518,7 @@ static DWORD LoadLocalIndexFiles(TCascStorage * hs) return ERROR_CANCELLED; // Perform the directory scan - if((dwErrCode = ScanIndexDirectory(hs->szIndexPath, IndexDirectory_OnFileFound, hs)) == ERROR_SUCCESS) + if((dwErrCode = ScanDirectory(hs->szIndexPath, NULL, IndexDirectory_OnFileFound, hs)) == ERROR_SUCCESS) { // If no index file was found, we cannot load anything if(hs->szIndexFormat == NULL) @@ -573,14 +528,14 @@ static DWORD LoadLocalIndexFiles(TCascStorage * hs) for(DWORD i = 0; i < CASC_INDEX_COUNT; i++) { CASC_INDEX & IndexFile = hs->IndexFiles[i]; - DWORD cbFileData = 0; // 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) + dwErrCode = LoadFileToMemory(IndexFile.szFileName, IndexFile.FileData); + if(dwErrCode != ERROR_SUCCESS) { // Storages downloaded by Blizzget tool don't have all index files present if((dwErrCode = GetCascError()) == ERROR_FILE_NOT_FOUND) @@ -588,13 +543,11 @@ static DWORD LoadLocalIndexFiles(TCascStorage * hs) dwErrCode = ERROR_SUCCESS; break; } - return dwErrCode; } // Add to the total size of the index files - IndexFile.cbFileData = cbFileData; - TotalSize += cbFileData; + TotalSize += IndexFile.FileData.cbData; dwIndexCount++; } @@ -613,7 +566,7 @@ static DWORD LoadLocalIndexFiles(TCascStorage * hs) // Online index files // https://wowdev.wiki/TACT#CDN_File_Organization -static DWORD CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, DWORD cbIndexFile) +static DWORD CaptureArchiveIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, size_t cbIndexFile) { FILE_INDEX_FOOTER<0x08> * pFooter08; BYTE checksum_data[0x40] = { 0 }; @@ -625,7 +578,7 @@ static DWORD CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbInd // Check the variant for checksum == 0x08 pFooter08 = (FILE_INDEX_FOOTER<0x08> *)(pbIndexFile + cbIndexFile - sizeof(FILE_INDEX_FOOTER<0x08>)); - if (pFooter08->Version == 1 && pFooter08->Reserved[0] == 0 && pFooter08->Reserved[1] == 0 && pFooter08->FooterHashBytes == 8) + if(pFooter08->Version == 1 && pFooter08->Reserved[0] == 0 && pFooter08->Reserved[1] == 0 && pFooter08->FooterHashBytes == 8) { // Copy the entire structure memcpy(InFooter.TocHash, pFooter08->TocHash, MD5_HASH_SIZE); @@ -635,7 +588,7 @@ static DWORD CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbInd InFooter.SizeBytes = pFooter08->SizeBytes; InFooter.EKeyLength = pFooter08->EKeyLength; InFooter.FooterHashBytes = pFooter08->FooterHashBytes; - InFooter.PageLength = pFooter08->PageSizeKB << 10; + InFooter.PageLength = ((size_t)pFooter08->PageSizeKB) << 10; InFooter.ItemLength = pFooter08->EKeyLength + pFooter08->OffsetBytes + pFooter08->SizeBytes; InFooter.FooterLength = sizeof(FILE_INDEX_FOOTER<0x08>); InFooter.ElementCount = ConvertBytesToInteger_4_LE(pFooter08->ElementCount); @@ -647,8 +600,6 @@ static DWORD CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbInd if(!memcmp(md5_hash, InFooter.FooterHash, InFooter.FooterHashBytes)) return ERROR_SUCCESS; } - - assert(false); return ERROR_BAD_FORMAT; } @@ -658,7 +609,7 @@ static DWORD CaptureIndexEntry(CASC_ARCINDEX_FOOTER & InFooter, CASC_EKEY_ENTRY ULONGLONG ArchiveOffset; // If there enough bytes for one entry/ - if ((pbIndexPage + InFooter.ItemLength) > pbIndexPageEnd) + if((pbIndexPage + InFooter.ItemLength) > pbIndexPageEnd) return ERROR_BAD_FORMAT; // Capture the EKey (variable length) @@ -666,7 +617,7 @@ static DWORD CaptureIndexEntry(CASC_ARCINDEX_FOOTER & InFooter, CASC_EKEY_ENTRY // Copy the archive offset ArchiveOffset = ConvertBytesToInteger_X(pbIndexPage + InFooter.SizeBytes, InFooter.OffsetBytes); - if (ArchiveOffset >= 0x10000000) + if(ArchiveOffset >= 0x10000000) return ERROR_BAD_FORMAT; // Capture the storage offset and encoded size @@ -705,7 +656,7 @@ static DWORD LoadArchiveIndexPage(TCascStorage * hs, CASC_ARCINDEX_FOOTER & InFo { // Capture the index entry dwErrCode = CaptureIndexEntry(InFooter, EKeyEntry, pbIndexPage, pbIndexPageEnd, nArchive); - if (dwErrCode != ERROR_SUCCESS) + if(dwErrCode != ERROR_SUCCESS) break; // Insert a new entry to the index array @@ -719,15 +670,15 @@ static DWORD LoadArchiveIndexPage(TCascStorage * hs, CASC_ARCINDEX_FOOTER & InFo return ERROR_SUCCESS; } -static DWORD LoadArchiveIndexFile(TCascStorage * hs, LPBYTE pbIndexFile, DWORD cbIndexFile, size_t nArchive) +static DWORD LoadArchiveIndexFile(TCascStorage * hs, LPBYTE pbIndexFile, size_t cbIndexFile, size_t nArchive) { CASC_ARCINDEX_FOOTER InFooter; LPBYTE pbIndexEnd = NULL; DWORD dwErrCode; // Validate and capture the footer - dwErrCode = CaptureArcIndexFooter(InFooter, pbIndexFile, cbIndexFile); - if (dwErrCode != ERROR_SUCCESS) + dwErrCode = CaptureArchiveIndexFooter(InFooter, pbIndexFile, cbIndexFile); + if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Remember the file offset and EKey length @@ -735,7 +686,7 @@ static DWORD LoadArchiveIndexFile(TCascStorage * hs, LPBYTE pbIndexFile, DWORD c // Verify the size of the index file dwErrCode = VerifyIndexSize(InFooter, pbIndexFile, cbIndexFile, &pbIndexEnd); - if (dwErrCode != ERROR_SUCCESS) + if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Parse all pages @@ -743,7 +694,7 @@ static DWORD LoadArchiveIndexFile(TCascStorage * hs, LPBYTE pbIndexFile, DWORD c { // Load the entire page dwErrCode = LoadArchiveIndexPage(hs, InFooter, pbIndexFile, pbIndexFile + InFooter.PageLength, nArchive); - if (dwErrCode != ERROR_SUCCESS) + if(dwErrCode != ERROR_SUCCESS) break; // Move to the next page @@ -761,16 +712,16 @@ static DWORD BuildMapOfArchiveIndices(TCascStorage * hs) // Create the map dwErrCode = hs->IndexMap.Create(nItemCount, MD5_HASH_SIZE, FIELD_OFFSET(CASC_EKEY_ENTRY, EKey)); - if (dwErrCode != ERROR_SUCCESS) + if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Insert all items for(size_t i = 0; i < nItemCount; i++) { pEKeyEntry = (PCASC_EKEY_ENTRY)hs->IndexArray.ItemAt(i); - if (pEKeyEntry != NULL) + if(pEKeyEntry != NULL) { - if (!hs->IndexMap.InsertObject(pEKeyEntry, pEKeyEntry->EKey)) + if(!hs->IndexMap.InsertObject(pEKeyEntry, pEKeyEntry->EKey)) { return ERROR_NOT_ENOUGH_MEMORY; } @@ -782,21 +733,19 @@ static DWORD BuildMapOfArchiveIndices(TCascStorage * hs) static DWORD LoadArchiveIndexFiles(TCascStorage * hs) { - LPBYTE pbFileData; - TCHAR szLocalPath[MAX_PATH]; - DWORD cbFileData = 0; + CASC_BLOB FileData; size_t nArchiveCount = (hs->ArchivesKey.cbData / MD5_HASH_SIZE); DWORD dwErrCode = ERROR_SUCCESS; // Create the array object for the indices dwErrCode = hs->IndexArray.Create(sizeof(CASC_EKEY_ENTRY), 0x10000); - if (dwErrCode != ERROR_SUCCESS) + if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Load all the indices - for (size_t i = 0; i < nArchiveCount; i++) + for(size_t i = 0; i < nArchiveCount; i++) { - CASC_CDN_DOWNLOAD CdnsInfo = {0}; + CASC_PATH<TCHAR> LocalPath; LPBYTE pbIndexHash = hs->ArchivesKey.pbData + (i * MD5_HASH_SIZE); // Inform the user about what we are doing @@ -806,34 +755,24 @@ static DWORD LoadArchiveIndexFiles(TCascStorage * hs) break; } - // Prepare the download structure for "%CDNS_HOST%/%CDNS_PATH%/##/##/EKey" file - CdnsInfo.szCdnsPath = hs->szCdnPath; - CdnsInfo.szPathType = _T("data"); - CdnsInfo.pbEKey = pbIndexHash; - CdnsInfo.szExtension = _T(".index"); - CdnsInfo.szLocalPath = szLocalPath; - CdnsInfo.ccLocalPath = _countof(szLocalPath); - dwErrCode = DownloadFileFromCDN(hs, CdnsInfo); - - // Load and parse the archive index - if (dwErrCode == ERROR_SUCCESS) + // Fetch and parse the archive index + dwErrCode = FetchCascFile(hs, PathTypeData, pbIndexHash, _T(".index"), LocalPath); + if(dwErrCode == ERROR_SUCCESS) { // Load the index file to memory - pbFileData = LoadFileToMemory(szLocalPath, &cbFileData); - if (pbFileData && cbFileData) + if((dwErrCode = LoadFileToMemory(LocalPath, FileData)) == ERROR_SUCCESS) { - dwErrCode = LoadArchiveIndexFile(hs, pbFileData, cbFileData, i); - CASC_FREE(pbFileData); + dwErrCode = LoadArchiveIndexFile(hs, FileData.pbData, FileData.cbData, i); } } // Break if an error - if (dwErrCode != ERROR_SUCCESS) + if(dwErrCode != ERROR_SUCCESS) break; } // Build map of EKey -> CASC_EKEY_ENTRY - if (dwErrCode == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) dwErrCode = BuildMapOfArchiveIndices(hs); return dwErrCode; } @@ -843,11 +782,11 @@ static DWORD LoadArchiveIndexFiles(TCascStorage * hs) 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)) { + LPBYTE pbEKeyEntry; + // If the file was found, then copy the content to the CKey entry pbEKeyEntry = (LPBYTE)hs->IndexEKeyMap.FindObject(pCKeyEntry->EKey); if(pbEKeyEntry == NULL) @@ -863,15 +802,18 @@ bool CopyEKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry) DWORD LoadIndexFiles(TCascStorage * hs) { - // 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) + switch(hs->BuildFileType) { - return LoadArchiveIndexFiles(hs); - } - else - { - return LoadLocalIndexFiles(hs); + case CascBuildDb: // Load the index files from the disk + case CascBuildInfo: + return LoadLocalIndexFiles(hs); + + case CascVersions: // Load the index files from the cache / internet + return LoadArchiveIndexFiles(hs); + + default: + assert(false); + return ERROR_NOT_SUPPORTED; } } @@ -886,8 +828,7 @@ void FreeIndexFiles(TCascStorage * hs) CASC_INDEX & IndexFile = hs->IndexFiles[i]; // Free the file data - CASC_FREE(IndexFile.pbFileData); - IndexFile.cbFileData = 0; + IndexFile.FileData.Free(); // Free the file name CASC_FREE(IndexFile.szFileName); diff --git a/dep/CascLib/src/CascLib.h b/dep/CascLib/src/CascLib.h index 3f4445e9351..b63869a49b4 100644 --- a/dep/CascLib/src/CascLib.h +++ b/dep/CascLib/src/CascLib.h @@ -1,7 +1,7 @@ /*****************************************************************************/ -/* CascLib.h Copyright (c) Ladislav Zezula 2014 */ +/* CascLib.h Copyright (c) Ladislav Zezula 2022 */ /*---------------------------------------------------------------------------*/ -/* CascLib library v 1.00 */ +/* CascLib library v 3.0 */ /* */ /* Author : Ladislav Zezula */ /* E-mail : ladik@zezula.net */ @@ -10,13 +10,14 @@ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 29.04.14 1.00 Lad Created */ +/* 19.12.22 1.00 Lad Version 3.0 */ /*****************************************************************************/ #ifndef __CASCLIB_H__ #define __CASCLIB_H__ #ifdef _MSC_VER -#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' +#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' #pragma warning(disable:4820) // 'XXX' : '2' bytes padding added after data member 'XXX::yyy' #endif @@ -74,8 +75,8 @@ extern "C" { //----------------------------------------------------------------------------- // Defines -#define CASCLIB_VERSION 0x0210 // CascLib version - integral (2.1) -#define CASCLIB_VERSION_STRING "2.1" // CascLib version - string +#define CASCLIB_VERSION 0x0300 // CascLib version - integral (3.0) +#define CASCLIB_VERSION_STRING "3.0" // CascLib version - string // Values for CascOpenFile #define CASC_OPEN_BY_NAME 0x00000000 // Open the file by name. This is the default value @@ -122,13 +123,14 @@ extern "C" { #define MD5_STRING_SIZE 0x20 #endif -// Return value for CascGetFileSize and CascSetFilePointer +// Invalid values of all kind #define CASC_INVALID_INDEX 0xFFFFFFFF #define CASC_INVALID_SIZE 0xFFFFFFFF #define CASC_INVALID_POS 0xFFFFFFFF #define CASC_INVALID_ID 0xFFFFFFFF #define CASC_INVALID_OFFS64 0xFFFFFFFFFFFFFFFF #define CASC_INVALID_SIZE64 0xFFFFFFFFFFFFFFFF +#define CASC_INVALID_SIZE_T ((size_t)(-1)) // Flags for CASC_STORAGE_FEATURES::dwFeatures #define CASC_FEATURE_FILE_NAMES 0x00000001 // File names are supported by the storage @@ -139,9 +141,10 @@ extern "C" { #define CASC_FEATURE_FILE_DATA_IDS 0x00000020 // The storage indexes files by FileDataId #define CASC_FEATURE_LOCALE_FLAGS 0x00000040 // Locale flags are supported #define CASC_FEATURE_CONTENT_FLAGS 0x00000080 // Content flags are supported -#define CASC_FEATURE_ONLINE 0x00000100 // The storage is an online storage -#define CASC_FEATURE_LOCAL_CDNS 0x00000200 // (Online) use cached "cdns" file, if available -#define CASC_FEATURE_LOCAL_VERSIONS 0x00000400 // (Online) use cached "versions" file, if available +#define CASC_FEATURE_DATA_ARCHIVES 0x00000100 // The storage supports files stored in data.### archives +#define CASC_FEATURE_DATA_FILES 0x00000200 // The storage supports raw files stored in %CascRoot%\xx\yy\xxyy## (CKey-based) +#define CASC_FEATURE_ONLINE 0x00000400 // Load the missing files from online CDNs +#define CASC_FEATURE_FORCE_DOWNLOAD 0x00001000 // (Online) always download "versions" and "cdns" even if it exists locally // Macro to convert FileDataId to the argument of CascOpenFile #define CASC_FILE_DATA_ID(FileDataId) ((LPCSTR)(size_t)FileDataId) diff --git a/dep/CascLib/src/CascOpenFile.cpp b/dep/CascLib/src/CascOpenFile.cpp index dd1d5407575..eee4566db5d 100644 --- a/dep/CascLib/src/CascOpenFile.cpp +++ b/dep/CascLib/src/CascOpenFile.cpp @@ -41,7 +41,7 @@ TCascFile::TCascFile(TCascStorage * ahs, PCASC_CKEY_ENTRY apCKeyEntry) TCascFile::~TCascFile() { // Free all stuff related to file spans - if (pFileSpan != NULL) + if(pFileSpan != NULL) { PCASC_FILE_SPAN pSpanPtr = pFileSpan; @@ -430,7 +430,7 @@ bool WINAPI CascCloseFile(HANDLE hFile) TCascFile * hf; hf = TCascFile::IsValid(hFile); - if (hf != NULL) + if(hf != NULL) { delete hf; return true; diff --git a/dep/CascLib/src/CascOpenStorage.cpp b/dep/CascLib/src/CascOpenStorage.cpp index 1231ef58bfe..24afbc73e7b 100644 --- a/dep/CascLib/src/CascOpenStorage.cpp +++ b/dep/CascLib/src/CascOpenStorage.cpp @@ -61,7 +61,8 @@ TCascStorage::TCascStorage() pRootHandler = NULL; dwRefCount = 1; - szRootPath = szDataPath = szIndexPath = szBuildFile = szCdnServers = szCdnPath = szCdnHostUrl = szCodeName = NULL; + szRootPath = szDataPath = szIndexPath = szFilesPath = szConfigPath = szMainFile = NULL; + szCdnHostUrl = szCdnServers = szCdnPath = szCodeName = NULL; szIndexFormat = NULL; szRegion = NULL; szBuildKey = NULL; @@ -100,14 +101,16 @@ TCascStorage::~TCascStorage() CascFreeLock(StorageLock); // Free the file paths - CASC_FREE(szDataPath); CASC_FREE(szRootPath); - CASC_FREE(szBuildFile); + CASC_FREE(szDataPath); CASC_FREE(szIndexPath); + CASC_FREE(szFilesPath); + CASC_FREE(szConfigPath); + CASC_FREE(szMainFile); + CASC_FREE(szCdnHostUrl); CASC_FREE(szCdnServers); CASC_FREE(szCdnPath); CASC_FREE(szCodeName); - CASC_FREE(szCdnHostUrl); CASC_FREE(szRegion); CASC_FREE(szBuildKey); @@ -166,20 +169,6 @@ void * ProbeOutputBuffer(void * pvBuffer, size_t cbLength, size_t cbMinLength, s return pvBuffer; } -static LPTSTR CheckForIndexDirectory(TCascStorage * hs, LPCTSTR szSubDir) -{ - TCHAR szIndexPath[MAX_PATH]; - - // Combine the index path - CombinePath(szIndexPath, _countof(szIndexPath), hs->szDataPath, szSubDir, NULL); - - // Check whether the path exists - if(!DirectoryExists(szIndexPath)) - return NULL; - - return CascNewStr(szIndexPath); -} - // Inserts an entry from the text build file static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, CASC_CKEY_ENTRY & CKeyEntry) { @@ -451,8 +440,7 @@ static int LoadEncodingCKeyPage(TCascStorage * hs, CASC_ENCODING_HEADER & EnHead static int LoadEncodingManifest(TCascStorage * hs) { CASC_CKEY_ENTRY & CKeyEntry = hs->EncodingCKey; - LPBYTE pbEncodingFile; - DWORD cbEncodingFile = 0; + CASC_BLOB EncodingFile; DWORD dwErrCode = ERROR_SUCCESS; // Inform the user about what we are doing @@ -465,24 +453,25 @@ static int LoadEncodingManifest(TCascStorage * hs) InsertCKeyEntry(hs, CKeyEntry); // Load the entire encoding file to memory - pbEncodingFile = LoadInternalFileToMemory(hs, &hs->EncodingCKey, &cbEncodingFile); - if(pbEncodingFile != NULL && cbEncodingFile != 0) + dwErrCode = LoadInternalFileToMemory(hs, &hs->EncodingCKey, EncodingFile); + if(dwErrCode == ERROR_SUCCESS && EncodingFile.cbData != 0) { CASC_ENCODING_HEADER EnHeader; // Capture the header of the ENCODING file - dwErrCode = CaptureEncodingHeader(EnHeader, pbEncodingFile, cbEncodingFile); + dwErrCode = CaptureEncodingHeader(EnHeader, EncodingFile.pbData, EncodingFile.cbData); if(dwErrCode == ERROR_SUCCESS) { // Get the CKey page header and the first page - PFILE_CKEY_PAGE pPageHeader = (PFILE_CKEY_PAGE)(pbEncodingFile + sizeof(FILE_ENCODING_HEADER) + EnHeader.ESpecBlockSize); + PFILE_CKEY_PAGE pPageHeader = (PFILE_CKEY_PAGE)(EncodingFile.pbData + sizeof(FILE_ENCODING_HEADER) + EnHeader.ESpecBlockSize); + LPBYTE pbEncodingEnd = EncodingFile.pbData + EncodingFile.cbData; LPBYTE pbCKeyPage = (LPBYTE)(pPageHeader + EnHeader.CKeyPageCount); // Go through all CKey pages and verify them for(DWORD i = 0; i < EnHeader.CKeyPageCount; i++) { // Check if there is enough space in the buffer - if((pbCKeyPage + EnHeader.CKeyPageSize) > (pbEncodingFile + cbEncodingFile)) + if((pbCKeyPage + EnHeader.CKeyPageSize) > pbEncodingEnd) { dwErrCode = ERROR_FILE_CORRUPT; break; @@ -519,9 +508,6 @@ static int LoadEncodingManifest(TCascStorage * hs) { dwErrCode = CopyBuildFileItemsToCKeyArray(hs); } - - // Free the loaded ENCODING file - CASC_FREE(pbEncodingFile); } else { @@ -735,15 +721,18 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead // Insert the entry to the central CKey table if((pCKeyEntry = InsertCKeyEntry(hs, DlEntry)) != NULL) { - // Supply the tag bits - for(size_t j = 0; j < TagItemCount; j++) + if(TagArray != NULL) { - // Set the bit in the entry, if the tag for it is present - if((BitMaskOffset < TagArray[j].BitmapLength) && (TagArray[j].Bitmap[BitMaskOffset] & BitMaskBit)) - pCKeyEntry->TagBitMask |= TagBit; + // Supply the tag bits + for(size_t j = 0; j < TagItemCount; j++) + { + // Set the bit in the entry, if the tag for it is present + if((BitMaskOffset < TagArray[j].BitmapLength) && (TagArray[j].Bitmap[BitMaskOffset] & BitMaskBit)) + pCKeyEntry->TagBitMask |= TagBit; - // Move to the next bit - TagBit <<= 1; + // Move to the next bit + TagBit <<= 1; + } } } @@ -762,8 +751,7 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead static int LoadDownloadManifest(TCascStorage * hs) { PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->DownloadCKey.CKey); - LPBYTE pbDownloadFile = NULL; - DWORD cbDownloadFile = 0; + CASC_BLOB DownloadFile; DWORD dwErrCode = ERROR_SUCCESS; // Inform the user about what we are doing @@ -771,21 +759,18 @@ static int LoadDownloadManifest(TCascStorage * hs) return ERROR_CANCELLED; // Load the entire DOWNLOAD file to memory - pbDownloadFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbDownloadFile); - if(pbDownloadFile != NULL && cbDownloadFile != 0) + dwErrCode = LoadInternalFileToMemory(hs, pCKeyEntry, DownloadFile); + if(dwErrCode == ERROR_SUCCESS && DownloadFile.cbData != 0) { CASC_DOWNLOAD_HEADER DlHeader; // Capture the header of the DOWNLOAD file - dwErrCode = CaptureDownloadHeader(DlHeader, pbDownloadFile, cbDownloadFile); + dwErrCode = CaptureDownloadHeader(DlHeader, DownloadFile.pbData, DownloadFile.cbData); if(dwErrCode == ERROR_SUCCESS) { // Parse the entire download manifest - dwErrCode = LoadDownloadManifest(hs, DlHeader, pbDownloadFile, pbDownloadFile + cbDownloadFile); + dwErrCode = LoadDownloadManifest(hs, DlHeader, DownloadFile.pbData, DownloadFile.pbData + DownloadFile.cbData); } - - // Free the loaded manifest - CASC_FREE(pbDownloadFile); } // If the DOWNLOAD manifest is not present, we won't abort the downloading process. @@ -799,8 +784,7 @@ static int LoadDownloadManifest(TCascStorage * hs) static int LoadInstallManifest(TCascStorage * hs) { PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->InstallCKey.CKey); - LPBYTE pbInstallFile = NULL; - DWORD cbInstallFile = 0; + CASC_BLOB InstallFile; DWORD dwErrCode = ERROR_SUCCESS; // Inform the user about what we are doing @@ -808,11 +792,10 @@ static int LoadInstallManifest(TCascStorage * hs) return ERROR_CANCELLED; // Load the entire DOWNLOAD file to memory - pbInstallFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbInstallFile); - if(pbInstallFile != NULL && cbInstallFile != 0) + dwErrCode = LoadInternalFileToMemory(hs, pCKeyEntry, InstallFile); + if(dwErrCode == ERROR_SUCCESS && InstallFile.cbData != 0) { - dwErrCode = RootHandler_CreateInstall(hs, pbInstallFile, cbInstallFile); - CASC_FREE(pbInstallFile); + dwErrCode = RootHandler_CreateInstall(hs, InstallFile); } else { @@ -864,9 +847,8 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask) { PCASC_CKEY_ENTRY pCKeyEntry = &hs->RootFile; TRootHandler * pOldRootHandler = NULL; + CASC_BLOB RootFile; PDWORD FileSignature; - LPBYTE pbRootFile = NULL; - DWORD cbRootFile = 0; DWORD dwErrCode = ERROR_BAD_FORMAT; // Sanity checks @@ -888,30 +870,30 @@ __LoadRootFile: // Load the entire ROOT file to memory pCKeyEntry = FindCKeyEntry_CKey(hs, pCKeyEntry->CKey); - pbRootFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbRootFile); - if(pbRootFile != NULL) + dwErrCode = LoadInternalFileToMemory(hs, pCKeyEntry, RootFile); + if(dwErrCode == ERROR_SUCCESS) { // Ignore ROOT files that contain just a MD5 hash - if(cbRootFile > MD5_STRING_SIZE) + if(RootFile.cbData > MD5_STRING_SIZE) { // Check the type of the ROOT file - FileSignature = (PDWORD)pbRootFile; + FileSignature = (PDWORD)(RootFile.pbData); switch(FileSignature[0]) { case CASC_MNDX_ROOT_SIGNATURE: - dwErrCode = RootHandler_CreateMNDX(hs, pbRootFile, cbRootFile); + dwErrCode = RootHandler_CreateMNDX(hs, RootFile); break; case CASC_DIABLO3_ROOT_SIGNATURE: - dwErrCode = RootHandler_CreateDiablo3(hs, pbRootFile, cbRootFile); + dwErrCode = RootHandler_CreateDiablo3(hs, RootFile); break; case CASC_TVFS_ROOT_SIGNATURE: - dwErrCode = RootHandler_CreateTVFS(hs, pbRootFile, cbRootFile); + dwErrCode = RootHandler_CreateTVFS(hs, RootFile); break; case CASC_WOW82_ROOT_SIGNATURE: - dwErrCode = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask); + dwErrCode = RootHandler_CreateWoW(hs, RootFile, dwLocaleMask); break; default: @@ -921,21 +903,18 @@ __LoadRootFile: // If the format was not recognized, they need to return ERROR_BAD_FORMAT // - dwErrCode = RootHandler_CreateOverwatch(hs, pbRootFile, cbRootFile); + dwErrCode = RootHandler_CreateOverwatch(hs, RootFile); if(dwErrCode == ERROR_BAD_FORMAT) { - dwErrCode = RootHandler_CreateStarcraft1(hs, pbRootFile, cbRootFile); + dwErrCode = RootHandler_CreateStarcraft1(hs, RootFile); if(dwErrCode == ERROR_BAD_FORMAT) { - dwErrCode = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask); + dwErrCode = RootHandler_CreateWoW(hs, RootFile, dwLocaleMask); } } break; } } - - // Free the root file - CASC_FREE(pbRootFile); } else { @@ -1109,73 +1088,7 @@ static bool GetStoragePathProduct(TCascStorage * hs, void * pvStorageInfo, size_ return (szBuffer != NULL); } -static DWORD InitializeLocalDirectories(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs) -{ - LPTSTR szWorkPath; - DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY; - - // Find the root directory of the storage. The root directory - // is the one with ".build.info" or ".build.db". - szWorkPath = CascNewStr(pArgs->szLocalPath); - if(szWorkPath != NULL) - { - // Get the length and go up until we find the ".build.info" or ".build.db" - for(;;) - { - // Is this a game directory? - dwErrCode = CheckGameDirectory(hs, szWorkPath); - if(dwErrCode == ERROR_SUCCESS) - { - dwErrCode = ERROR_SUCCESS; - break; - } - - // Cut one path part - if(!CutLastPathPart(szWorkPath)) - { - dwErrCode = ERROR_FILE_NOT_FOUND; - break; - } - } - - // Find the index directory - if(dwErrCode == ERROR_SUCCESS) - { - // First, check for more common "data" subdirectory - if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("data"))) != NULL) - dwErrCode = ERROR_SUCCESS; - - // Second, try the "darch" subdirectory (older builds of HOTS - Alpha) - else if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("darch"))) != NULL) - dwErrCode = ERROR_SUCCESS; - - else - dwErrCode = ERROR_FILE_NOT_FOUND; - } - - // Free the work path buffer - CASC_FREE(szWorkPath); - } - - return dwErrCode; -} - -static DWORD InitializeOnlineDirectories(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs) -{ - // Create the root path - hs->szRootPath = CascNewStr(pArgs->szLocalPath); - if(hs->szRootPath != NULL) - { - hs->BuildFileType = CascVersionsDb; - hs->dwFeatures |= CASC_FEATURE_ONLINE; - hs->dwFeatures |= (pArgs->dwFlags & (CASC_FEATURE_LOCAL_CDNS | CASC_FEATURE_LOCAL_VERSIONS)); - return ERROR_SUCCESS; - } - - return ERROR_NOT_ENOUGH_MEMORY; -} - -static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs) +static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs, LPCTSTR szMainFile, CBLD_TYPE BuildFileType, DWORD dwFeatures) { LPCTSTR szCdnHostUrl = NULL; LPCTSTR szCodeName = NULL; @@ -1206,21 +1119,48 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs) if(ExtractVersionedArgument(pArgs, FIELD_OFFSET(CASC_OPEN_STORAGE_ARGS, szBuildKey), &szBuildKey) && szBuildKey != NULL) hs->szBuildKey = CascNewStrT2A(szBuildKey); - // Special handling to online storages - if(hs->dwFeatures & CASC_FEATURE_ONLINE) - { - // Enable caching of the sockets. This will add references - // to all existing and all future sockets - sockets_set_caching(true); + // Merge features + hs->dwFeatures |= (dwFeatures & (CASC_FEATURE_DATA_ARCHIVES | CASC_FEATURE_DATA_FILES | CASC_FEATURE_ONLINE)); + hs->dwFeatures |= (pArgs->dwFlags & CASC_FEATURE_FORCE_DOWNLOAD); + hs->BuildFileType = BuildFileType; + + // Copy the name of the build file + hs->szMainFile = CascNewStr(szMainFile); - // For online storages, we need to load CDN servers - dwErrCode = LoadCdnsFile(hs); + // Construct the root path from the name of the build file + CASC_PATH<TCHAR> RootPath(szMainFile, NULL); + hs->szRootPath = RootPath.New(true); + + // If either of the root path or build file is known, it's an error + if(hs->szRootPath == NULL || hs->szMainFile == NULL) + { + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } - // Now, load the main storage file ".build.info" (or ".build.db" in old storages) + // Initialize variables for local CASC storages if(dwErrCode == ERROR_SUCCESS) { - dwErrCode = LoadBuildInfo(hs); + // For local (game) storages, we need the data and indices subdirectory + if(hs->dwFeatures & CASC_FEATURE_DATA_ARCHIVES) + { + if(CheckArchiveFilesDirectories(hs) != ERROR_SUCCESS) + hs->dwFeatures &= ~CASC_FEATURE_DATA_ARCHIVES; + } + + // For data files storage, we need that folder + if(hs->dwFeatures & CASC_FEATURE_DATA_FILES) + { + if(CheckDataFilesDirectory(hs) != ERROR_SUCCESS) + hs->dwFeatures &= ~CASC_FEATURE_DATA_FILES; + } + + // Enable caching of the sockets. This will add references + // to all existing and all future sockets + if(hs->dwFeatures & CASC_FEATURE_ONLINE) + sockets_set_caching(true); + + // Now, load the main storage file (".build.info", ".build.db" or "versions") + dwErrCode = LoadMainFile(hs); } // Proceed with loading the CDN config file @@ -1439,33 +1379,49 @@ bool WINAPI CascOpenStorageEx(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs, b } } - // Allocate the storage structure + // Now we need to get the CASC main file, which is either + // [*] .build.info - for current local storages + // [*] .build.db - for older local storages + // [*] versions - for cached online storages + // If there is none of these and `bOnlineStorage` is specified, + // CascLib will download it, as long as the product code was specified if(dwErrCode == ERROR_SUCCESS) { if((hs = new TCascStorage()) != NULL) { - // Setup the directories - dwErrCode = (bOnlineStorage) ? InitializeOnlineDirectories(hs, pArgs) : InitializeLocalDirectories(hs, pArgs); - if(dwErrCode == ERROR_SUCCESS) + CASC_BUILD_FILE BuildFile = {NULL}; + DWORD dwFeatures = bOnlineStorage ? CASC_FEATURE_ONLINE : 0; + + // Check for one of the supported main files (.build.info, .build.db, versions) + if((dwErrCode = CheckCascBuildFileExact(BuildFile, pArgs->szLocalPath)) == ERROR_SUCCESS) + { + dwErrCode = LoadCascStorage(hs, pArgs, BuildFile.szFullPath, BuildFile.BuildFileType, dwFeatures | CASC_FEATURE_DATA_ARCHIVES | CASC_FEATURE_DATA_FILES); + } + + // Search the folder and upper folders for the build file + else if((dwErrCode = CheckCascBuildFileDirs(BuildFile, pArgs->szLocalPath)) == ERROR_SUCCESS) { - // Perform the entire storage loading - dwErrCode = LoadCascStorage(hs, pArgs); + dwErrCode = LoadCascStorage(hs, pArgs, BuildFile.szFullPath, BuildFile.BuildFileType, dwFeatures | CASC_FEATURE_DATA_ARCHIVES | CASC_FEATURE_DATA_FILES); + } + + // If the caller requested an online storage, we must have the code name + else if((dwErrCode = CheckOnlineStorage(pArgs, BuildFile, dwFeatures)) == ERROR_SUCCESS) + { + dwErrCode = LoadCascStorage(hs, pArgs, BuildFile.szFullPath, BuildFile.BuildFileType, dwFeatures | CASC_FEATURE_DATA_FILES); } } } - // Handle errors + // Delete the storage on error if(dwErrCode != ERROR_SUCCESS) - { - SetCascError(dwErrCode); hs = hs->Release(); - } - - // Free the copy of the parameters CASC_FREE(szParamsCopy); // Give the output parameter to the caller - *phStorage = (HANDLE)hs; + if(phStorage != NULL) + phStorage[0] = (HANDLE)hs; + if(dwErrCode != ERROR_SUCCESS) + SetCascError(dwErrCode); return (dwErrCode == ERROR_SUCCESS); } diff --git a/dep/CascLib/src/CascPort.h b/dep/CascLib/src/CascPort.h index 9a136a3385c..c8145a4512d 100644 --- a/dep/CascLib/src/CascPort.h +++ b/dep/CascLib/src/CascPort.h @@ -93,7 +93,7 @@ #include <netdb.h> // Support for PowerPC on Max OS X - #if (__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1) + #if(__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1) #include <stdint.h> #include <CoreFoundation/CFByteOrder.h> #endif diff --git a/dep/CascLib/src/CascReadFile.cpp b/dep/CascLib/src/CascReadFile.cpp index c89445c7078..35f91addf75 100644 --- a/dep/CascLib/src/CascReadFile.cpp +++ b/dep/CascLib/src/CascReadFile.cpp @@ -29,8 +29,6 @@ static DWORD OpenDataStream(TCascFile * hf, PCASC_FILE_SPAN pFileSpan, PCASC_CKE { TCascStorage * hs = hf->hs; TFileStream * pStream = NULL; - TCHAR szCachePath[MAX_PATH]; - TCHAR szDataFile[MAX_PATH]; TCHAR szPlainName[0x80]; DWORD dwErrCode; @@ -48,11 +46,13 @@ static DWORD OpenDataStream(TCascFile * hf, PCASC_FILE_SPAN pFileSpan, PCASC_CKE { // Prepare the name of the data file CascStrPrintf(szPlainName, _countof(szPlainName), _T("data.%03u"), dwArchiveIndex); - CombinePath(szDataFile, _countof(szDataFile), hs->szIndexPath, szPlainName, NULL); + + // Create the full path of the data file + CASC_PATH<TCHAR> DataFile(hs->szIndexPath, szPlainName, NULL); // Open the data stream with read+write sharing to prevent Battle.net agent // detecting a corruption and redownloading the entire package - pStream = FileStream_OpenFile(szDataFile, STREAM_FLAG_READ_ONLY | STREAM_FLAG_WRITE_SHARE | STREAM_PROVIDER_FLAT | STREAM_FLAG_FILL_MISSING | BASE_PROVIDER_FILE); + pStream = FileStream_OpenFile(DataFile, STREAM_FLAG_READ_ONLY | STREAM_FLAG_WRITE_SHARE | STREAM_PROVIDER_FLAT | STREAM_FLAG_FILL_MISSING | BASE_PROVIDER_FILE); hs->DataFiles[dwArchiveIndex] = pStream; } @@ -67,35 +67,29 @@ static DWORD OpenDataStream(TCascFile * hf, PCASC_FILE_SPAN pFileSpan, PCASC_CKE { if(bDownloadFileIf) { - CASC_CDN_DOWNLOAD CdnsInfo = {0}; - LPCTSTR szPathType = (pCKeyEntry->Flags & CASC_CE_FILE_PATCH) ? _T("patch") : _T("data"); - - // Prepare the download structure for "%CDNS_HOST%/%CDNS_PATH%/##/##/EKey" file - CdnsInfo.szCdnsPath = hs->szCdnPath; - CdnsInfo.szPathType = szPathType; - CdnsInfo.pbEKey = pCKeyEntry->EKey; - CdnsInfo.szLocalPath = szCachePath; - CdnsInfo.ccLocalPath = _countof(szCachePath); - - // Download the file from CDN - dwErrCode = DownloadFileFromCDN(hs, CdnsInfo); + CASC_ARCHIVE_INFO ArchiveInfo = {0}; + CASC_PATH<TCHAR> LocalPath; + CPATH_TYPE PathType = (pCKeyEntry->Flags & CASC_CE_FILE_PATCH) ? PathTypePatch : PathTypeData; + + // Fetch the file + dwErrCode = FetchCascFile(hs, PathType, pCKeyEntry->EKey, NULL, LocalPath, &ArchiveInfo); if(dwErrCode == ERROR_SUCCESS) { - pStream = FileStream_OpenFile(szCachePath, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT); + pStream = FileStream_OpenFile(LocalPath, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT); if(pStream != NULL) { // Initialize information about the position and size of the file in archive // On loose files, their position is zero and encoded size is length of the file - if(CdnsInfo.pbArchiveKey != NULL) + if(CascIsValidMD5(ArchiveInfo.ArchiveKey)) { // Archive position - pFileSpan->ArchiveIndex = CdnsInfo.ArchiveIndex; - pFileSpan->ArchiveOffs = (DWORD)CdnsInfo.ArchiveOffs; + pFileSpan->ArchiveIndex = ArchiveInfo.ArchiveIndex; + pFileSpan->ArchiveOffs = ArchiveInfo.ArchiveOffs; // Encoded size if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE) - pCKeyEntry->EncodedSize = CdnsInfo.EncodedSize; - assert(pCKeyEntry->EncodedSize == CdnsInfo.EncodedSize); + pCKeyEntry->EncodedSize = ArchiveInfo.EncodedSize; + assert(pCKeyEntry->EncodedSize == ArchiveInfo.EncodedSize); } else { @@ -157,11 +151,11 @@ static void VerifyHeaderSpan(PBLTE_ENCODED_HEADER pBlteHeader, ULONGLONG HeaderO ConvertIntegerToBytes_4_LE(dwInt32, EncodedOffset); // Calculate checksum of the so-far filled structure - for (i = 0; i < FIELD_OFFSET(BLTE_ENCODED_HEADER, Checksum); i++) + for(i = 0; i < FIELD_OFFSET(BLTE_ENCODED_HEADER, Checksum); i++) HashedHeader[i & 3] ^= pbBlteHeader[i]; // XOR the two values together to get the final checksum. - for (j = 0; j < 4; j++, i++) + for(j = 0; j < 4; j++, i++) Checksum[j] = HashedHeader[i & 3] ^ EncodedOffset[i & 3]; // assert(memcmp(pBlteHeader->Checksum, Checksum, sizeof(Checksum)) == 0); } @@ -284,7 +278,7 @@ static DWORD LoadSpanFrames(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEnt if(pFrames != NULL) { // Copy the frames to the file structure - for (DWORD i = 0; i < pFileSpan->FrameCount; i++) + for(DWORD i = 0; i < pFileSpan->FrameCount; i++) { CASC_FILE_FRAME & Frame = pFrames[i]; diff --git a/dep/CascLib/src/CascRootFile_Diablo3.cpp b/dep/CascLib/src/CascRootFile_Diablo3.cpp index c29f13f74a9..c1d09c3ab23 100644 --- a/dep/CascLib/src/CascRootFile_Diablo3.cpp +++ b/dep/CascLib/src/CascRootFile_Diablo3.cpp @@ -64,11 +64,26 @@ typedef struct _DIABLO3_CORE_TOC_ENTRY } DIABLO3_CORE_TOC_ENTRY, *PDIABLO3_CORE_TOC_ENTRY; +// Structure for conversion DirectoryID -> Directory name +typedef struct _DIABLO3_ASSET_INFO +{ + const char * szDirectoryName; // Directory name + const char * szExtension; + +} DIABLO3_ASSET_INFO; +typedef const DIABLO3_ASSET_INFO * PDIABLO3_ASSET_INFO; + // In-memory structure of parsed directory data -typedef struct _DIABLO3_DIRECTORY +struct DIABLO3_DIRECTORY { - LPBYTE pbDirectoryData; // The begin of the directory data block - LPBYTE pbDirectoryEnd; // The end of the directory data block + DIABLO3_DIRECTORY() + { + pbAssetEntries = pbAssetIdxEntries = pbNamedEntries = NULL; + dwAssetEntries = dwAssetIdxEntries = dwNamedEntries = 0; + dwNodeIndex = 0; + } + + CASC_BLOB Data; // The complete copy of the directory data LPBYTE pbAssetEntries; // Pointer to asset entries without subitem number. Example: "SoundBank\SoundFile.smp" LPBYTE pbAssetIdxEntries; // Pointer to asset entries with subitem number LPBYTE pbNamedEntries; // Pointer to named entries. These are for files with arbitrary names, and they do not belong to an asset @@ -76,16 +91,7 @@ typedef struct _DIABLO3_DIRECTORY DWORD dwAssetIdxEntries; DWORD dwNamedEntries; DWORD dwNodeIndex; // Index of file node for this folder -} DIABLO3_DIRECTORY, *PDIABLO3_DIRECTORY; - -// Structure for conversion DirectoryID -> Directory name -typedef struct _DIABLO3_ASSET_INFO -{ - const char * szDirectoryName; // Directory name - const char * szExtension; - -} DIABLO3_ASSET_INFO; -typedef const DIABLO3_ASSET_INFO * PDIABLO3_ASSET_INFO; +}; //----------------------------------------------------------------------------- // Local variables @@ -175,16 +181,12 @@ struct TDiabloRoot : public TFileTreeRoot TDiabloRoot() : TFileTreeRoot(0) { memset(RootFolders, 0, sizeof(RootFolders)); - pFileIndices = NULL; - pbCoreTocFile = NULL; pbCoreTocData = NULL; + pFileIndices = NULL; nFileIndices = 0; - cbCoreTocFile = 0; // Map for searching a real file extension memset(&PackagesMap, 0, sizeof(CASC_MAP)); - pbPackagesDat = NULL; - cbPackagesDat = 0; // We have file names and return CKey as result of search dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY); @@ -212,26 +214,24 @@ struct TDiabloRoot : public TFileTreeRoot return (char *)PackagesMap.FindString(szFileName, szFileName + nLength); } - LPBYTE LoadFileToMemory(TCascStorage * hs, const char * szFileName, DWORD * pcbFileData) + DWORD LoadFileToMemory(TCascStorage * hs, const char * szFileName, CASC_BLOB & FileData) { PCASC_CKEY_ENTRY pCKeyEntry; - LPBYTE pbFileData = NULL; + DWORD dwErrCode = ERROR_FILE_NOT_FOUND; // Try to find CKey for the file pCKeyEntry = GetFile(hs, szFileName); if(pCKeyEntry != NULL) - pbFileData = LoadInternalFileToMemory(hs, pCKeyEntry, pcbFileData); - - return pbFileData; + dwErrCode = LoadInternalFileToMemory(hs, pCKeyEntry, FileData); + return dwErrCode; } - static LPBYTE CaptureDirectoryData( + static DWORD CaptureDirectoryData( DIABLO3_DIRECTORY & DirHeader, - LPBYTE pbDirectory, - DWORD cbDirectory) + CASC_BLOB & Directory) { - LPBYTE pbDirectoryData = pbDirectory; - LPBYTE pbDataEnd = pbDirectory + cbDirectory; + LPBYTE pbDirectory; + LPBYTE pbDataEnd; DWORD Signature = 0; // @@ -245,13 +245,15 @@ struct TDiabloRoot : public TFileTreeRoot // 7) Array of DIABLO3_NAMED_ENTRY entries // - // Prepare the header signature - memset(&DirHeader, 0, sizeof(DIABLO3_DIRECTORY)); + // Clone the input data + DirHeader.Data.MoveFrom(Directory); + pbDirectory = DirHeader.Data.pbData; + pbDataEnd = DirHeader.Data.End(); // Get the header signature pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &Signature); if((pbDirectory == NULL) || (Signature != CASC_DIABLO3_ROOT_SIGNATURE && Signature != DIABLO3_SUBDIR_SIGNATURE)) - return NULL; + return ERROR_BAD_FORMAT; // Subdirectories have extra two arrays if(Signature == DIABLO3_SUBDIR_SIGNATURE) @@ -259,37 +261,33 @@ struct TDiabloRoot : public TFileTreeRoot // Capture the number of DIABLO3_ASSET_ENTRY items pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &DirHeader.dwAssetEntries); if(pbDirectory == NULL) - return NULL; + return ERROR_BAD_FORMAT; // Capture the array of DIABLO3_ASSET_ENTRY pbDirectory = CaptureArray(pbDirectory, pbDataEnd, &DirHeader.pbAssetEntries, DIABLO3_ASSET_ENTRY, DirHeader.dwAssetEntries); if(pbDirectory == NULL) - return NULL; + return ERROR_BAD_FORMAT; // Capture the number of DIABLO3_ASSETIDX_ENTRY items pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &DirHeader.dwAssetIdxEntries); if(pbDirectory == NULL) - return NULL; + return ERROR_BAD_FORMAT; // Capture the array of DIABLO3_ASSETIDX_ENTRY pbDirectory = CaptureArray(pbDirectory, pbDataEnd, &DirHeader.pbAssetIdxEntries, DIABLO3_ASSETIDX_ENTRY, DirHeader.dwAssetIdxEntries); if(pbDirectory == NULL) - return NULL; + return ERROR_BAD_FORMAT; } // Capture the number of DIABLO3_NAMED_ENTRY array pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &DirHeader.dwNamedEntries); if(pbDirectory == NULL) - return NULL; + return ERROR_BAD_FORMAT; // Note: Do not capture the array here. We will do that later, // when we will be parsing the directory DirHeader.pbNamedEntries = pbDirectory; - - // Put the directory range - DirHeader.pbDirectoryData = pbDirectoryData; - DirHeader.pbDirectoryEnd = pbDirectoryData + cbDirectory; - return pbDirectory; + return ERROR_SUCCESS; } LPBYTE CaptureCoreTocHeader( @@ -357,22 +355,17 @@ struct TDiabloRoot : public TFileTreeRoot return NULL; } - int LoadDirectoryFile(TCascStorage * hs, DIABLO3_DIRECTORY & DirHeader, PCASC_CKEY_ENTRY pCKeyEntry) + DWORD LoadDirectoryFile(TCascStorage * hs, DIABLO3_DIRECTORY & DirHeader, PCASC_CKEY_ENTRY pCKeyEntry) { - LPBYTE pbData; - DWORD cbData = 0; + CASC_BLOB Data; + DWORD dwErrCode; - // Load the n-th folder - pbData = LoadInternalFileToMemory(hs, pCKeyEntry, &cbData); - if(pbData && cbData) - { - if(CaptureDirectoryData(DirHeader, pbData, cbData) == NULL) - { - // Clear the directory - CASC_FREE(pbData); - return ERROR_BAD_FORMAT; - } - } + // Load the n-th folder, if exists + dwErrCode = LoadInternalFileToMemory(hs, pCKeyEntry, Data); + if(dwErrCode == ERROR_SUCCESS && Data.cbData) + return CaptureDirectoryData(DirHeader, Data); + + // If the folder is not there, ignore the error return ERROR_SUCCESS; } @@ -546,7 +539,7 @@ struct TDiabloRoot : public TFileTreeRoot PCASC_CKEY_ENTRY pCKeyEntry; PCASC_FILE_NODE pFileNode; LPBYTE pbDataPtr = Directory.pbNamedEntries; - LPBYTE pbDataEnd = Directory.pbDirectoryEnd; + LPBYTE pbDataEnd = Directory.Data.End(); DWORD dwNodeIndex; // Parse all entries @@ -609,7 +602,7 @@ struct TDiabloRoot : public TFileTreeRoot for(size_t i = 0; i < DIABLO3_MAX_ROOT_FOLDERS; i++) { // Is this root folder loaded? - if(RootFolders[i].pbDirectoryData != NULL) + if(RootFolders[i].Data.pbData != NULL) { // Retrieve the parent name if(RootFolders[i].dwNodeIndex != 0) @@ -638,15 +631,15 @@ struct TDiabloRoot : public TFileTreeRoot DWORD CreateMapOfFileIndices(TCascStorage * hs, const char * szFileName) { PDIABLO3_CORE_TOC_HEADER pTocHeader = NULL; - LPBYTE pbCoreTocPtr = pbCoreTocFile; DWORD dwMaxFileIndex = 0; - DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE; + DWORD dwErrCode; // Load the entire file to memory - pbCoreTocFile = pbCoreTocPtr = LoadFileToMemory(hs, szFileName, &cbCoreTocFile); - if(pbCoreTocFile && cbCoreTocFile) + dwErrCode = LoadFileToMemory(hs, szFileName, CoreTocFile); + if(dwErrCode == ERROR_SUCCESS && CoreTocFile.cbData) { - LPBYTE pbCoreTocEnd = pbCoreTocFile + cbCoreTocFile; + LPBYTE pbCoreTocPtr = CoreTocFile.pbData; + LPBYTE pbCoreTocEnd = CoreTocFile.End(); // Capture the header if((pbCoreTocPtr = CaptureCoreTocHeader(&pTocHeader, &dwMaxFileIndex, pbCoreTocPtr, pbCoreTocEnd)) == NULL) @@ -693,17 +686,18 @@ struct TDiabloRoot : public TFileTreeRoot // Packages.dat contains a list of full file names (without locale prefix). // They are not sorted, nor they correspond to file IDs. // Does the sort order mean something? Perhaps we could use them as listfile? - int CreateMapOfRealNames(TCascStorage * hs, const char * szFileName) + DWORD CreateMapOfRealNames(TCascStorage * hs, const char * szFileName) { DWORD Signature = 0; DWORD NumberOfNames = 0; + DWORD dwErrCode; // Load the entire file to memory - pbPackagesDat = LoadFileToMemory(hs, szFileName, &cbPackagesDat); - if(pbPackagesDat && cbPackagesDat) + dwErrCode = LoadFileToMemory(hs, szFileName, PackagesDat); + if(dwErrCode == ERROR_SUCCESS && PackagesDat.cbData) { - LPBYTE pbPackagesPtr = pbPackagesDat; - LPBYTE pbPackagesEnd = pbPackagesDat + cbPackagesDat; + LPBYTE pbPackagesPtr = PackagesDat.pbData; + LPBYTE pbPackagesEnd = PackagesDat.End(); // Get the header. There is just Signature + NumberOfNames if((pbPackagesPtr = CaptureInteger32(pbPackagesPtr, pbPackagesEnd, &Signature)) == NULL) @@ -768,21 +762,11 @@ struct TDiabloRoot : public TFileTreeRoot void FreeLoadingStuff() { - // Free the captured root sub-directories - for(size_t i = 0; i < DIABLO3_MAX_SUBDIRS; i++) - CASC_FREE(RootFolders[i].pbDirectoryData); - // Free the package map PackagesMap.Free(); // Free the array of file indices CASC_FREE(pFileIndices); - - // Free the loaded CoreTOC.dat file - CASC_FREE(pbCoreTocFile); - - // Free the loaded Packages.dat file - CASC_FREE(pbPackagesDat); } // Array of root directory subdirectories @@ -791,32 +775,29 @@ struct TDiabloRoot : public TFileTreeRoot // Array of DIABLO3_TOC_ENTRY structures, sorted by the file index // Used for converting FileIndex -> Asset+PlainName during loading PDIABLO3_CORE_TOC_ENTRY pFileIndices; - LPBYTE pbCoreTocFile; + CASC_BLOB CoreTocFile; LPBYTE pbCoreTocData; size_t nFileIndices; - DWORD cbCoreTocFile; // Map for searching a real file extension + CASC_BLOB PackagesDat; CASC_MAP PackagesMap; - LPBYTE pbPackagesDat; - DWORD cbPackagesDat; }; //----------------------------------------------------------------------------- // Public functions -DWORD RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) +DWORD RootHandler_CreateDiablo3(TCascStorage * hs, CASC_BLOB & RootFile) { TDiabloRoot * pRootHandler = NULL; DIABLO3_DIRECTORY RootDirectory; DWORD dwErrCode = ERROR_BAD_FORMAT; // Verify the header of the ROOT file - if(TDiabloRoot::CaptureDirectoryData(RootDirectory, pbRootFile, cbRootFile) != NULL) + if((dwErrCode = TDiabloRoot::CaptureDirectoryData(RootDirectory, RootFile)) == ERROR_SUCCESS) { // Allocate the root handler object - pRootHandler = new TDiabloRoot(); - if(pRootHandler != NULL) + if((pRootHandler = new TDiabloRoot()) != NULL) { // Load the root directory. If load failed, we free the object dwErrCode = pRootHandler->Load(hs, RootDirectory); diff --git a/dep/CascLib/src/CascRootFile_Install.cpp b/dep/CascLib/src/CascRootFile_Install.cpp index 86d621e2e47..2a01acb8590 100644 --- a/dep/CascLib/src/CascRootFile_Install.cpp +++ b/dep/CascLib/src/CascRootFile_Install.cpp @@ -36,7 +36,7 @@ struct TRootHandler_Install : public TFileTreeRoot pbInstallFile += InHeader.HeaderLength; // Skip the tags - for (DWORD i = 0; i < InHeader.TagCount; i++) + for(DWORD i = 0; i < InHeader.TagCount; i++) { szString = (const char *)pbInstallFile; nBitmapLength = GetTagBitmapLength(pbInstallFile, pbInstallEnd, InHeader.EntryCount); @@ -55,7 +55,7 @@ struct TRootHandler_Install : public TFileTreeRoot pbInstallFile += MD5_HASH_SIZE + sizeof(DWORD); // Insert the FileName+CKey to the file tree - if (pCKeyEntry != NULL) + if(pCKeyEntry != NULL) FileTree.InsertByName(pCKeyEntry, szString); nFileCount--; } @@ -72,11 +72,11 @@ DWORD CaptureInstallHeader(CASC_INSTALL_HEADER & InHeader, LPBYTE pbFileData, si PFILE_INSTALL_HEADER pFileHeader = (PFILE_INSTALL_HEADER)pbFileData; // Check the signature ('DL') and version - if (cbFileData < sizeof(FILE_INSTALL_HEADER) || pFileHeader->Magic != FILE_MAGIC_INSTALL || pFileHeader->Version != 1) + if(cbFileData < sizeof(FILE_INSTALL_HEADER) || pFileHeader->Magic != FILE_MAGIC_INSTALL || pFileHeader->Version != 1) return ERROR_BAD_FORMAT; // Note that we don't support CKey sizes greater than 0x10 in the INSTALL file - if (pFileHeader->EKeyLength > MD5_HASH_SIZE) + if(pFileHeader->EKeyLength > MD5_HASH_SIZE) return ERROR_BAD_FORMAT; // Capture the header version 1 @@ -90,22 +90,22 @@ DWORD CaptureInstallHeader(CASC_INSTALL_HEADER & InHeader, LPBYTE pbFileData, si return ERROR_SUCCESS; } -DWORD RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbInstallFile, DWORD cbInstallFile) +DWORD RootHandler_CreateInstall(TCascStorage * hs, CASC_BLOB & InstallFile) { CASC_INSTALL_HEADER InHeader; TRootHandler_Install * pRootHandler = NULL; DWORD dwErrCode = ERROR_BAD_FORMAT; // Capture the header of the DOWNLOAD file - dwErrCode = CaptureInstallHeader(InHeader, pbInstallFile, cbInstallFile); - if (dwErrCode == ERROR_SUCCESS) + dwErrCode = CaptureInstallHeader(InHeader, InstallFile.pbData, InstallFile.cbData); + if(dwErrCode == ERROR_SUCCESS) { // Allocate the root handler object pRootHandler = new TRootHandler_Install(); - if (pRootHandler != NULL) + if(pRootHandler != NULL) { // Parse the entire install manifest - dwErrCode = pRootHandler->Load(hs, InHeader, pbInstallFile, pbInstallFile + cbInstallFile); + dwErrCode = pRootHandler->Load(hs, InHeader, InstallFile.pbData, InstallFile.End()); hs->pRootHandler = pRootHandler; } } diff --git a/dep/CascLib/src/CascRootFile_MNDX.cpp b/dep/CascLib/src/CascRootFile_MNDX.cpp index c049d813967..9b820c0d639 100644 --- a/dep/CascLib/src/CascRootFile_MNDX.cpp +++ b/dep/CascLib/src/CascRootFile_MNDX.cpp @@ -419,10 +419,10 @@ class TByteStream return ERROR_NOT_ENOUGH_MEMORY; // Allocate bytes for the array - if (Pointer != NULL) + if(Pointer != NULL) { Pointer[0] = CASC_ALLOC<T>(ItemCount); - if (Pointer[0] == NULL) + if(Pointer[0] == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Get the pointer to the array @@ -804,7 +804,7 @@ class TSparseArray // Search the groups and find the BASEVALS structure // For spans less than 10 groups, use sequential search, otherwise binary search. - if ((maxGroup - minGroup) < 10) + if((maxGroup - minGroup) < 10) { // HOTS: 1959CF7 while (index >= GROUP_TO_INDEX(minGroup) - BaseVals[minGroup + 1].BaseValue200 + 0x200) @@ -821,7 +821,7 @@ class TSparseArray // HOTS: 1959D38 DWORD middleValue = (maxGroup + minGroup) >> 1; - if (index < (maxGroup << 0x09) - BaseVals[maxGroup].BaseValue200) + if(index < (maxGroup << 0x09) - BaseVals[maxGroup].BaseValue200) { // HOTS: 01959D4B maxGroup = middleValue; @@ -845,7 +845,7 @@ class TSparseArray // Find the BASEVALS structure which the start index belongs to // For less than 10 values, use sequential search. Otherwise, use binary search - if ((nextValue - startValue) < 10) + if((nextValue - startValue) < 10) { // HOTS: 01959F94 while (index >= BaseVals[startValue + 1].BaseValue200) @@ -857,12 +857,12 @@ class TSparseArray else { // Binary search (HOTS: 1959FAD) - if ((startValue + 1) < nextValue) + if((startValue + 1) < nextValue) { // HOTS: 1959FB4 DWORD middleValue = (nextValue + startValue) >> 1; - if (index < BaseVals[middleValue].BaseValue200) + if(index < BaseVals[middleValue].BaseValue200) { // HOTS: 1959FC4 nextValue = middleValue; @@ -889,7 +889,7 @@ class TSparseArray DWORD edx = index; #ifdef _DEBUG - //if (TotalItemCount > 0x200) + //if(TotalItemCount > 0x200) //{ // FILE * fp = fopen("e:\\Ladik\\Appdir\\CascLib\\doc\\mndx-sparse-array.txt", "wt"); // Dump(fp); @@ -898,7 +898,7 @@ class TSparseArray #endif // If the index is at begin of the group, we just return the start value - if ((index & 0x1FF) == 0) + if((index & 0x1FF) == 0) return IndexToItem0[INDEX_TO_GROUP(index)]; // Find the group where the index belongs to @@ -908,13 +908,13 @@ class TSparseArray edx += BaseVals[groupIndex].BaseValue200 - (groupIndex << 0x09); dwordIndex = (groupIndex << 4); - if (edx < 0x100 - BaseVals[groupIndex].AddValue100) + if(edx < 0x100 - BaseVals[groupIndex].AddValue100) { // HOTS: 1959D8C - if (edx < 0x80 - BaseVals[groupIndex].AddValue80) + if(edx < 0x80 - BaseVals[groupIndex].AddValue80) { // HOTS: 01959DA2 - if (edx >= 0x40 - BaseVals[groupIndex].AddValue40) + if(edx >= 0x40 - BaseVals[groupIndex].AddValue40) { // HOTS: 01959DB7 dwordIndex += 2; @@ -924,7 +924,7 @@ class TSparseArray else { // HOTS: 1959DC0 - if (edx < 0xC0 - BaseVals[groupIndex].AddValueC0) + if(edx < 0xC0 - BaseVals[groupIndex].AddValueC0) { // HOTS: 1959DD3 dwordIndex += 4; @@ -941,10 +941,10 @@ class TSparseArray else { // HOTS: 1959DE8 - if (edx < 0x180 - BaseVals[groupIndex].AddValue180) + if(edx < 0x180 - BaseVals[groupIndex].AddValue180) { // HOTS: 01959E00 - if (edx < 0x140 - BaseVals[groupIndex].AddValue140) + if(edx < 0x140 - BaseVals[groupIndex].AddValue140) { // HOTS: 1959E11 dwordIndex += 8; @@ -960,7 +960,7 @@ class TSparseArray else { // HOTS: 1959E29 - if (edx < 0x1C0 - BaseVals[groupIndex].AddValue1C0) + if(edx < 0x1C0 - BaseVals[groupIndex].AddValue1C0) { // HOTS: 1959E3D dwordIndex += 12; @@ -980,7 +980,7 @@ class TSparseArray bitGroup = ~ItemBits[dwordIndex]; zeroBits = GetNumberOfSetBits(bitGroup); - if (edx >= zeroBits.u.Lower32) + if(edx >= zeroBits.u.Lower32) { // HOTS: 1959ea4 bitGroup = ~ItemBits[++dwordIndex]; @@ -992,10 +992,10 @@ class TSparseArray itemIndex = (dwordIndex << 0x05); // HOTS: 1959eea - if (edx < zeroBits.u.Lower16) + if(edx < zeroBits.u.Lower16) { // HOTS: 1959EFC - if (edx >= zeroBits.u.Lower08) + if(edx >= zeroBits.u.Lower08) { // HOTS: 1959F05 bitGroup >>= 0x08; @@ -1006,7 +1006,7 @@ class TSparseArray else { // HOTS: 1959F0D - if (edx < zeroBits.u.Lower24) + if(edx < zeroBits.u.Lower24) { // HOTS: 1959F19 bitGroup >>= 0x10; @@ -1043,7 +1043,7 @@ class TSparseArray DWORD bitGroup; // If the index is at begin of the group, we just return the start value - if ((index & 0x1FF) == 0) + if((index & 0x1FF) == 0) return IndexToItem1[INDEX_TO_GROUP(index)]; // Find the group where the index belongs to @@ -1054,13 +1054,13 @@ class TSparseArray dwordIndex = groupIndex << 0x04; // Calculate the dword index including the sub-checkpoint - if (distFromBase < BaseVals[groupIndex].AddValue100) + if(distFromBase < BaseVals[groupIndex].AddValue100) { // HOTS: 1959FF1 - if (distFromBase < BaseVals[groupIndex].AddValue80) + if(distFromBase < BaseVals[groupIndex].AddValue80) { // HOTS: 0195A000 - if (distFromBase >= BaseVals[groupIndex].AddValue40) + if(distFromBase >= BaseVals[groupIndex].AddValue40) { // HOTS: 195A007 distFromBase = distFromBase - BaseVals[groupIndex].AddValue40; @@ -1070,7 +1070,7 @@ class TSparseArray else { // HOTS: 195A00E - if (distFromBase < BaseVals[groupIndex].AddValueC0) + if(distFromBase < BaseVals[groupIndex].AddValueC0) { // HOTS: 195A01A distFromBase = distFromBase - BaseVals[groupIndex].AddValue80; @@ -1087,10 +1087,10 @@ class TSparseArray else { // HOTS: 195A026 - if (distFromBase < BaseVals[groupIndex].AddValue180) + if(distFromBase < BaseVals[groupIndex].AddValue180) { // HOTS: 195A037 - if (distFromBase < BaseVals[groupIndex].AddValue140) + if(distFromBase < BaseVals[groupIndex].AddValue140) { // HOTS: 195A041 distFromBase = distFromBase - BaseVals[groupIndex].AddValue100; @@ -1106,7 +1106,7 @@ class TSparseArray else { // HOTS: 195A04D - if (distFromBase < BaseVals[groupIndex].AddValue1C0) + if(distFromBase < BaseVals[groupIndex].AddValue1C0) { // HOTS: 195A05A distFromBase = distFromBase - BaseVals[groupIndex].AddValue180; @@ -1126,7 +1126,7 @@ class TSparseArray setBits = GetNumberOfSetBits(bitGroup); // Get total number of set bits in the bit group - if (distFromBase >= setBits.u.Lower32) + if(distFromBase >= setBits.u.Lower32) { // HOTS: 195A0B2 bitGroup = ItemBits[++dwordIndex]; @@ -1138,10 +1138,10 @@ class TSparseArray itemIndex = (dwordIndex << 0x05); // Get the number of set bits in the lower word (HOTS: 195A0F6) - if (distFromBase < setBits.u.Lower16) + if(distFromBase < setBits.u.Lower16) { // HOTS: 195A111 - if (distFromBase >= setBits.u.Lower08) + if(distFromBase >= setBits.u.Lower08) { // HOTS: 195A111 itemIndex = itemIndex + 0x08; @@ -1152,7 +1152,7 @@ class TSparseArray else { // HOTS: 195A119 - if (distFromBase < setBits.u.Lower24) + if(distFromBase < setBits.u.Lower24) { // HOTS: 195A125 bitGroup = bitGroup >> 0x10; @@ -1190,7 +1190,7 @@ class TSparseArray "========================================================\n" " Index Base200h +40 +80 +C0 +100 +140 +180 +1C0\n" "--------------------------------------------------------\n"); - for (size_t i = 0; i < BaseVals.ItemCount; i++) + for(size_t i = 0; i < BaseVals.ItemCount; i++) { fprintf(fp, "[%08zX]: %08x %04x %04x %04x %04x %04x %04x %04x\n", GROUP_TO_INDEX(i), BaseVals[i].BaseValue200, BaseVals[i].AddValue40, @@ -1208,7 +1208,7 @@ class TSparseArray "========================================\n" " Index Item0 Item1\n" "-----------------------------\n"); - for (size_t i = 0; i < IndexToItem0.ItemCount; i++) + for(size_t i = 0; i < IndexToItem0.ItemCount; i++) { fprintf(fp, "[%08zX]: %08x %08x\n", GROUP_TO_INDEX(i), IndexToItem0[i], IndexToItem1[i]); } @@ -1218,16 +1218,16 @@ class TSparseArray // Output values of Item1 and Item0 for every index ArrayNormal = new size_t[TotalItemCount]; ArrayInvert = new size_t[TotalItemCount]; - if (ArrayNormal && ArrayInvert) + if(ArrayNormal && ArrayInvert) { // Invalidate both arrays memset(ArrayNormal, 0xFF, TotalItemCount * sizeof(size_t)); memset(ArrayInvert, 0xFF, TotalItemCount * sizeof(size_t)); // Load the both arrays - for (size_t i = 0; i < TotalItemCount; i++) + for(size_t i = 0; i < TotalItemCount; i++) { - if (IsItemPresent(i)) + if(IsItemPresent(i)) ArrayNormal[IndexNormal++] = i; else ArrayInvert[IndexInvert++] = i; @@ -1238,12 +1238,12 @@ class TSparseArray "========================================\n" " Index Item0 Item1\n" "-----------------------------\n"); - for (size_t i = 0; i < TotalItemCount; i++) + for(size_t i = 0; i < TotalItemCount; i++) { char NormalValue[0x20]; char InvertValue[0x20]; - if (ArrayNormal[i] == MNDX_INVALID_SIZE_T && ArrayInvert[i] == MNDX_INVALID_SIZE_T) + if(ArrayNormal[i] == MNDX_INVALID_SIZE_T && ArrayInvert[i] == MNDX_INVALID_SIZE_T) break; fprintf(fp, "[%08zX]: %8s %8s\n", i, DumpValue(InvertValue, _countof(InvertValue), ArrayInvert[i]), DumpValue(NormalValue, _countof(NormalValue), ArrayNormal[i])); } @@ -1256,9 +1256,9 @@ class TSparseArray // Output array of all values fprintf(fp, "Item List: Index -> Value\n==========================\n"); - for (size_t i = 0; i < TotalItemCount; i++) + for(size_t i = 0; i < TotalItemCount; i++) { - if (IsItemPresent(i)) + if(IsItemPresent(i)) { fprintf(fp, "[%08zX]: %08x\n", i, GetItemValueAt(i)); } @@ -1441,7 +1441,7 @@ class TPathFragmentTable TStruct40 * pStruct40 = &pSearch->Struct40; // Do we have path fragment separators in an external structure? - if (PathMarks.IsEmpty()) + if(PathMarks.IsEmpty()) { // HOTS: 195A40C while (PathFragments[nFragmentOffset] != 0) @@ -1694,13 +1694,13 @@ class TFileNameDatabase DWORD GetPathFragmentOffset2(DWORD & index_hibits, DWORD index_lobits) { // If the hi-bits index is invalid, we need to get its starting value - if (index_hibits == CASC_INVALID_INDEX) + if(index_hibits == CASC_INVALID_INDEX) { /* printf("\n"); - for (DWORD i = 0; i < CollisionHiBitsIndexes.TotalItemCount; i++) + for(DWORD i = 0; i < CollisionHiBitsIndexes.TotalItemCount; i++) { - if (CollisionHiBitsIndexes.IsItemPresent(i)) + if(CollisionHiBitsIndexes.IsItemPresent(i)) printf("[%02X] = %02X\n", i, CollisionHiBitsIndexes.GetIntValueAt(i)); else printf("[%02X] = NOT_PRESENT\n", i); @@ -1775,17 +1775,17 @@ class TFileNameDatabase if(pHashEntry->NodeIndex == pStruct40->NodeIndex) { // Check if there is single character match - if (!IsPathFragmentSingleChar(pHashEntry)) + if(!IsPathFragmentSingleChar(pHashEntry)) { // Check if there is a name fragment match - if (pChildDB != NULL) + if(pChildDB != NULL) { - if (!pChildDB->ComparePathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) + if(!pChildDB->ComparePathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) return false; } else { - if (!PathFragmentTable.ComparePathFragment(pSearch, pHashEntry->FragmentOffset)) + if(!PathFragmentTable.ComparePathFragment(pSearch, pHashEntry->FragmentOffset)) return false; } } @@ -1863,82 +1863,82 @@ class TFileNameDatabase DWORD eax; // HOTS: 1957B95 - for (;;) + for(;;) { // Get the hasn table item pHashEntry = &HashTable[TableIndex & HashTableMask]; // - if (TableIndex == pHashEntry->NextIndex) + if(TableIndex == pHashEntry->NextIndex) { // HOTS: 01957BB4 - if (!IsPathFragmentSingleChar(pHashEntry)) + if(!IsPathFragmentSingleChar(pHashEntry)) { // HOTS: 1957BC7 - if (pChildDB != NULL) + if(pChildDB != NULL) { // HOTS: 1957BD3 - if (!pChildDB->ComparePathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) + if(!pChildDB->ComparePathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) return false; } else { // HOTS: 1957BE0 - if (!PathFragmentTable.ComparePathFragment(pSearch, pHashEntry->FragmentOffset)) + if(!PathFragmentTable.ComparePathFragment(pSearch, pHashEntry->FragmentOffset)) return false; } } else { // HOTS: 1957BEE - if (pSearch->szSearchMask[pStruct40->PathLength] != pHashEntry->SingleChar) + if(pSearch->szSearchMask[pStruct40->PathLength] != pHashEntry->SingleChar) return false; pStruct40->PathLength++; } // HOTS: 1957C05 TableIndex = pHashEntry->NodeIndex; - if (TableIndex == 0) + if(TableIndex == 0) return true; - if (pStruct40->PathLength >= pSearch->cchSearchMask) + if(pStruct40->PathLength >= pSearch->cchSearchMask) return false; } else { // HOTS: 1957C30 - if (IsPathFragmentString(TableIndex)) + if(IsPathFragmentString(TableIndex)) { DWORD FragmentOffset = GetPathFragmentOffset1(TableIndex); // HOTS: 1957C4C - if (pChildDB != NULL) + if(pChildDB != NULL) { // HOTS: 1957C58 - if (!pChildDB->ComparePathFragmentByIndex(pSearch, FragmentOffset)) + if(!pChildDB->ComparePathFragmentByIndex(pSearch, FragmentOffset)) return false; } else { // HOTS: 1957350 - if (!PathFragmentTable.ComparePathFragment(pSearch, FragmentOffset)) + if(!PathFragmentTable.ComparePathFragment(pSearch, FragmentOffset)) return false; } } else { // HOTS: 1957C8E - if (LoBitsTable[TableIndex] != pSearch->szSearchMask[pStruct40->PathLength]) + if(LoBitsTable[TableIndex] != pSearch->szSearchMask[pStruct40->PathLength]) return false; pStruct40->PathLength++; } // HOTS: 1957CB2 - if (TableIndex <= field_214) + if(TableIndex <= field_214) return true; - if (pStruct40->PathLength >= pSearch->cchSearchMask) + if(pStruct40->PathLength >= pSearch->cchSearchMask) return false; eax = CollisionTable.GetItem1(TableIndex); @@ -1954,16 +1954,16 @@ class TFileNameDatabase PHASH_ENTRY pHashEntry; // HOTS: 1958D84 - for (;;) + for(;;) { pHashEntry = &HashTable[TableIndex & HashTableMask]; - if (TableIndex == pHashEntry->NextIndex) + if(TableIndex == pHashEntry->NextIndex) { // HOTS: 1958DA6 - if (!IsPathFragmentSingleChar(pHashEntry)) + if(!IsPathFragmentSingleChar(pHashEntry)) { // HOTS: 1958DBA - if (pChildDB != NULL) + if(pChildDB != NULL) { pChildDB->CopyPathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex); } @@ -1981,18 +1981,18 @@ class TFileNameDatabase // HOTS: 1958E71 TableIndex = pHashEntry->NodeIndex; - if (TableIndex == 0) + if(TableIndex == 0) return; } else { // HOTS: 1958E8E - if (IsPathFragmentString(TableIndex)) + if(IsPathFragmentString(TableIndex)) { DWORD FragmentOffset = GetPathFragmentOffset1(TableIndex); // HOTS: 1958EAF - if (pChildDB != NULL) + if(pChildDB != NULL) { pChildDB->CopyPathFragmentByIndex(pSearch, FragmentOffset); } @@ -2009,7 +2009,7 @@ class TFileNameDatabase } // HOTS: 1958FDE - if (TableIndex <= field_214) + if(TableIndex <= field_214) return; TableIndex = 0xFFFFFFFF - TableIndex + CollisionTable.GetItem1(TableIndex); @@ -2027,9 +2027,9 @@ class TFileNameDatabase DWORD TableIndex; /* FILE * fp = fopen("E:\\PathFragmentTable.txt", "wt"); - if (fp != NULL) + if(fp != NULL) { - for (DWORD i = 0; i < HashTable.ItemCount; i++) + for(DWORD i = 0; i < HashTable.ItemCount; i++) { FragOffs = HashTable[i].FragOffs; fprintf(fp, "%02x ('%c') %08X %08X %08X", i, (0x20 <= i && i < 0x80) ? i : 0x20, HashTable[i].ItemIndex, HashTable[i].NextIndex, FragOffs); @@ -2059,14 +2059,14 @@ class TFileNameDatabase if(!IsPathFragmentSingleChar(pHashEntry)) { // HOTS: 1958B59 - if (pChildDB != NULL) + if(pChildDB != NULL) { - if (!pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) + if(!pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) return false; } else { - if (!PathFragmentTable.CompareAndCopyPathFragment(pSearch, pHashEntry->FragmentOffset)) + if(!PathFragmentTable.CompareAndCopyPathFragment(pSearch, pHashEntry->FragmentOffset)) return false; } } @@ -2232,7 +2232,7 @@ class TFileNameDatabase DWORD edi; // Perform action based on the search phase - switch (pStruct40->SearchPhase) + switch(pStruct40->SearchPhase) { case MNDX_SEARCH_INITIALIZING: { @@ -2242,7 +2242,7 @@ class TFileNameDatabase // If the caller passed a part of the search path, we need to find that one while (pStruct40->PathLength < pSearch->cchSearchMask) { - if (!CompareAndCopyPathFragment(pSearch)) + if(!CompareAndCopyPathFragment(pSearch)) { pStruct40->SearchPhase = MNDX_SEARCH_FINISHED; return false; @@ -2254,7 +2254,7 @@ class TFileNameDatabase pStruct40->PathStops.Insert(PathStop); pStruct40->ItemCount = 1; - if (FileNameIndexes.IsItemPresent(pStruct40->NodeIndex)) + if(FileNameIndexes.IsItemPresent(pStruct40->NodeIndex)) { pSearch->szFoundPath = &pStruct40->PathBuffer[0]; pSearch->cchFoundPath = pStruct40->PathBuffer.ItemCount; @@ -2267,10 +2267,10 @@ class TFileNameDatabase case MNDX_SEARCH_SEARCHING: { // HOTS: 1959522 - for (;;) + for(;;) { // HOTS: 1959530 - if (pStruct40->ItemCount == pStruct40->PathStops.ItemCount) + if(pStruct40->ItemCount == pStruct40->PathStops.ItemCount) { TPathStop * pLastStop; DWORD ColTableIndex; @@ -2288,17 +2288,17 @@ class TFileNameDatabase pPathStop = &pStruct40->PathStops[pStruct40->ItemCount]; // HOTS: 19595CC - if (CollisionTable.IsItemPresent(pPathStop->field_4++)) + if(CollisionTable.IsItemPresent(pPathStop->field_4++)) { // HOTS: 19595F2 pStruct40->ItemCount++; - if (IsPathFragmentString(pPathStop->LoBitsIndex)) + if(IsPathFragmentString(pPathStop->LoBitsIndex)) { DWORD FragmentOffset = GetPathFragmentOffset2(pPathStop->HiBitsIndex_PathFragment, pPathStop->LoBitsIndex); // HOTS: 1959630 - if (pChildDB != NULL) + if(pChildDB != NULL) { // HOTS: 1959649 pChildDB->CopyPathFragmentByIndex(pSearch, FragmentOffset); @@ -2320,10 +2320,10 @@ class TFileNameDatabase pPathStop->Count = pStruct40->PathBuffer.ItemCount; // HOTS: 19596b6 - if (FileNameIndexes.IsItemPresent(pPathStop->LoBitsIndex)) + if(FileNameIndexes.IsItemPresent(pPathStop->LoBitsIndex)) { // HOTS: 19596D1 - if (pPathStop->field_10 == 0xFFFFFFFF) + if(pPathStop->field_10 == 0xFFFFFFFF) { // HOTS: 19596D9 pPathStop->field_10 = FileNameIndexes.GetItemValueAt(pPathStop->LoBitsIndex); @@ -2343,7 +2343,7 @@ class TFileNameDatabase else { // HOTS: 19596E9 - if (pStruct40->ItemCount == 1) + if(pStruct40->ItemCount == 1) { pStruct40->SearchPhase = MNDX_SEARCH_FINISHED; return false; @@ -2435,7 +2435,7 @@ class TFileNameDatabase TFileNameDatabase * pNewDB; pNewDB = new TFileNameDatabase; - if (pNewDB == NULL) + if(pNewDB == NULL) return ERROR_NOT_ENOUGH_MEMORY; dwErrCode = SetChildDatabase(pNewDB); @@ -2627,11 +2627,11 @@ struct TMndxHandler { // Capture the root header pbRootPtr = CaptureData(pbRootPtr, pbRootEnd, &MndxHeader, sizeof(FILE_MNDX_HEADER)); - if (pbRootPtr == NULL) + if(pbRootPtr == NULL) return NULL; // Check signature and version - if (MndxHeader.Signature != CASC_MNDX_ROOT_SIGNATURE || MndxHeader.FormatVersion > 2 || MndxHeader.FormatVersion < 1) + if(MndxHeader.Signature != CASC_MNDX_ROOT_SIGNATURE || MndxHeader.FormatVersion > 2 || MndxHeader.FormatVersion < 1) return NULL; // Passed @@ -2665,14 +2665,14 @@ struct TMndxHandler // Insert new package to the array assert(Search.nIndex < nPackageCount); pPackage = (PMNDX_PACKAGE)Packages.InsertAt(Search.nIndex); - if (pPackage != NULL) + if(pPackage != NULL) { // The package mut not be initialized yet assert(pPackage->szFileName == NULL); // Allocate space for the file name pPackage->szFileName = CASC_ALLOC<char>(Search.cchFoundPath + 1); - if (pPackage->szFileName == NULL) + if(pPackage->szFileName == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Fill the package structure @@ -2765,7 +2765,7 @@ struct TMndxHandler if(pMarFile->GetFileNameCount(&FileNameCount) == ERROR_SUCCESS && FileNameCount == MndxInfo.FileNameCount) { CKeyEntriesSize = MndxInfo.CKeyEntriesCount * MndxInfo.CKeyEntrySize; - if ((pbRootFile + MndxInfo.CKeyEntriesOffset + CKeyEntriesSize) <= pbRootEnd) + if((pbRootFile + MndxInfo.CKeyEntriesOffset + CKeyEntriesSize) <= pbRootEnd) { pCKeyEntries = (PMNDX_CKEY_ENTRY)(pbRootFile + MndxInfo.CKeyEntriesOffset); dwErrCode = ERROR_SUCCESS; @@ -2789,17 +2789,17 @@ struct TMndxHandler // Get the remaining file name groups for(i = 0; i < MndxInfo.CKeyEntriesCount; i++, pRootEntry++) { - if (nFileNameIndex > MndxInfo.FileNameCount) + if(nFileNameIndex > MndxInfo.FileNameCount) break; - if (pRootEntry->Flags & MNDX_LAST_CKEY_ENTRY) + if(pRootEntry->Flags & MNDX_LAST_CKEY_ENTRY) { FileNameIndexToCKeyIndex[nFileNameIndex++] = pRootEntry + 1; } } // Verify the final number of file names - if ((nFileNameIndex - 1) != MndxInfo.FileNameCount) + if((nFileNameIndex - 1) != MndxInfo.FileNameCount) dwErrCode = ERROR_BAD_FORMAT; } else @@ -2837,7 +2837,7 @@ struct TMndxHandler assert(Search.cchFoundPath < MAX_PATH); // The found file name index must fall into range of file names - if (Search.nIndex < MndxInfo.FileNameCount) + if(Search.nIndex < MndxInfo.FileNameCount) { // Retrieve the first-in-group CKey entry of that name pRootEntry = FileNameIndexToCKeyIndex[Search.nIndex]; @@ -2847,13 +2847,13 @@ struct TMndxHandler { // Find the appropriate CKey entry in the central storage pCKeyEntry = FindCKeyEntry_CKey(hs, pRootEntry->CKey); - if (pCKeyEntry != NULL) + if(pCKeyEntry != NULL) { size_t nPackageIndex = pRootEntry->Flags & 0x00FFFFFF; // Retrieve the package for this entry pPackage = (PMNDX_PACKAGE)Packages.ItemAt(nPackageIndex); - if (pPackage != NULL) + if(pPackage != NULL) { // Sanity check assert(pPackage->nIndex == nPackageIndex); @@ -2867,7 +2867,7 @@ struct TMndxHandler } // Is this the last-in-group entry? - if (pRootEntry->Flags & MNDX_LAST_CKEY_ENTRY) + if(pRootEntry->Flags & MNDX_LAST_CKEY_ENTRY) break; pRootEntry++; } @@ -2889,18 +2889,18 @@ struct TMndxHandler assert((pPackage->nLength + 1 + pSearch->cchFoundPath + 1) < cchBuffer); // Copy the package name - if ((szBuffer + pPackage->nLength) < szBufferEnd) + if((szBuffer + pPackage->nLength) < szBufferEnd) { memcpy(szBuffer, pPackage->szFileName, pPackage->nLength); szBuffer += pPackage->nLength; } // Append slash - if ((szBuffer + 1) < szBufferEnd) + if((szBuffer + 1) < szBufferEnd) *szBuffer++ = '/'; // Append file name - if ((szBuffer + pSearch->cchFoundPath) < szBufferEnd) + if((szBuffer + pSearch->cchFoundPath) < szBufferEnd) { memcpy(szBuffer, pSearch->szFoundPath, pSearch->cchFoundPath); szBuffer += pSearch->cchFoundPath; @@ -2938,7 +2938,7 @@ struct TRootHandler_MNDX : public TFileTreeRoot // Load and parse the entire MNDX structure dwErrCode = Handler.Load(MndxHeader, pbRootFile, pbRootEnd); - if (dwErrCode == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { // Search all file names and insert them into the file tree dwErrCode = Handler.LoadFileNames(hs, FileTree); @@ -2951,11 +2951,12 @@ struct TRootHandler_MNDX : public TFileTreeRoot //----------------------------------------------------------------------------- // Public functions - MNDX info -DWORD RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) +DWORD RootHandler_CreateMNDX(TCascStorage * hs, CASC_BLOB & RootFile) { TRootHandler_MNDX * pRootHandler = NULL; FILE_MNDX_HEADER MndxHeader; - LPBYTE pbRootEnd = pbRootFile + cbRootFile; + LPBYTE pbRootFile = RootFile.pbData; + LPBYTE pbRootEnd = RootFile.End(); DWORD dwErrCode = ERROR_BAD_FORMAT; // Verify the header of the ROOT file diff --git a/dep/CascLib/src/CascRootFile_OW.cpp b/dep/CascLib/src/CascRootFile_OW.cpp index b270ec76206..b1fbd5b9fec 100644 --- a/dep/CascLib/src/CascRootFile_OW.cpp +++ b/dep/CascLib/src/CascRootFile_OW.cpp @@ -175,7 +175,7 @@ struct TCmfFile { PCMF_HEADER_V3 pHeader3 = (PCMF_HEADER_V3)pbCmfData; - if ((LPBYTE)(pHeader3 + 1) > pbCmfEnd) + if((LPBYTE)(pHeader3 + 1) > pbCmfEnd) return NULL; BuildVersion = pHeader3->BuildVersion; @@ -559,7 +559,7 @@ struct TRootHandler_OW : public TFileTreeRoot // Public functions // TODO: There is way more files in the Overwatch CASC storage than present in the ROOT file. -DWORD RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) +DWORD RootHandler_CreateOverwatch(TCascStorage * hs, CASC_BLOB & RootFile) { TRootHandler_OW * pRootHandler = NULL; CASC_CSV Csv(0, true); @@ -567,7 +567,7 @@ DWORD RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cb DWORD dwErrCode; // Load the ROOT file - dwErrCode = Csv.Load(pbRootFile, cbRootFile); + dwErrCode = Csv.Load(RootFile.pbData, RootFile.cbData); if(dwErrCode == ERROR_SUCCESS) { // Retrieve the indices of the file name and MD5 columns @@ -578,11 +578,11 @@ DWORD RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cb if(Indices[0] != CSV_INVALID_INDEX && Indices[1] != CSV_INVALID_INDEX) { pRootHandler = new TRootHandler_OW(); - if (pRootHandler != NULL) + if(pRootHandler != NULL) { // Load the root directory. If load failed, we free the object dwErrCode = pRootHandler->Load(hs, Csv, Indices[0], Indices[1]); - if (dwErrCode != ERROR_SUCCESS) + if(dwErrCode != ERROR_SUCCESS) { delete pRootHandler; pRootHandler = NULL; diff --git a/dep/CascLib/src/CascRootFile_TVFS.cpp b/dep/CascLib/src/CascRootFile_TVFS.cpp index 5c099ad3460..52408bf6660 100644 --- a/dep/CascLib/src/CascRootFile_TVFS.cpp +++ b/dep/CascLib/src/CascRootFile_TVFS.cpp @@ -36,8 +36,18 @@ // Local structures // In-memory layout of the TVFS file header -typedef struct _TVFS_DIRECTORY_HEADER +struct TVFS_DIRECTORY_HEADER { + TVFS_DIRECTORY_HEADER() + { + memset(this, 0, sizeof(TVFS_DIRECTORY_HEADER) - FIELD_OFFSET(TVFS_DIRECTORY_HEADER, Data)); + } + + LPBYTE DataAt(DWORD dwOffset) + { + return Data.pbData + dwOffset; + } + DWORD Signature; // Must be CASC_TVFS_ROOT_SIGNATURE BYTE FormatVersion; // Version of the format. Should be 1. BYTE HeaderSize; // Size of the header, in bytes @@ -59,9 +69,8 @@ typedef struct _TVFS_DIRECTORY_HEADER DWORD CftOffsSize; // Byte length of the offset in the Content File Table entry DWORD EstOffsSize; // Byte length of the offset in the Encoding Specifier Table entry - LPBYTE pbDirectoryData; // Pointer to the begin of directory data - LPBYTE pbDirectoryEnd; // Pointer to the end of directory data - + CASC_BLOB Data; // The complete directory data + // LPBYTE pbPathFileTable; // Begin and end of the path table // LPBYTE pbPathTableEnd; @@ -71,7 +80,7 @@ typedef struct _TVFS_DIRECTORY_HEADER // LPBYTE pbCftFileTable; // Begin and end of the content file table // LPBYTE pbCftTableEnd; -} TVFS_DIRECTORY_HEADER, *PTVFS_DIRECTORY_HEADER; +}; /* // Minimum size of a valid path table entry. 1 byte + 1-byte name + 1 byte + DWORD @@ -157,12 +166,15 @@ struct TRootHandler_TVFS : public TFileTreeRoot return true; } - static DWORD CaptureDirectoryHeader(TVFS_DIRECTORY_HEADER & DirHeader, LPBYTE pbDataPtr, LPBYTE pbDataEnd) + static DWORD CaptureDirectoryHeader(TVFS_DIRECTORY_HEADER & DirHeader, CASC_BLOB & Data) { - // Fill the header structure with zeros - memset(&DirHeader, 0, sizeof(TVFS_DIRECTORY_HEADER)); - DirHeader.pbDirectoryData = pbDataPtr; - DirHeader.pbDirectoryEnd = pbDataEnd; + LPBYTE pbDataPtr = NULL; + LPBYTE pbDataEnd = NULL; + + // Extract the data out of the buffer + DirHeader.Data.MoveFrom(Data); + pbDataPtr = DirHeader.Data.pbData; + pbDataEnd = DirHeader.Data.End(); // Capture the signature pbDataPtr = CaptureInteger32(pbDataPtr, pbDataEnd, &DirHeader.Signature); @@ -189,7 +201,7 @@ struct TRootHandler_TVFS : public TFileTreeRoot DirHeader.VfsTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.VfsTableSize)); DirHeader.CftTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.CftTableOffset)); DirHeader.CftTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.CftTableSize)); - DirHeader.MaxDepth = (USHORT)ConvertBytesToInteger_2((LPBYTE)(&DirHeader.MaxDepth)); + DirHeader.MaxDepth = ConvertBytesToInteger_2((LPBYTE)(&DirHeader.MaxDepth)); DirHeader.EstTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.EstTableOffset)); DirHeader.EstTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.EstTableSize)); @@ -220,7 +232,7 @@ struct TRootHandler_TVFS : public TFileTreeRoot LPBYTE CaptureVfsSpanCount(TVFS_DIRECTORY_HEADER & DirHeader, DWORD dwVfsOffset, DWORD & SpanCount) { - LPBYTE pbVfsFileTable = DirHeader.pbDirectoryData + DirHeader.VfsTableOffset; + LPBYTE pbVfsFileTable = DirHeader.DataAt(DirHeader.VfsTableOffset); LPBYTE pbVfsFileEntry = pbVfsFileTable + dwVfsOffset; LPBYTE pbVfsFileEnd = pbVfsFileTable + DirHeader.VfsTableSize; @@ -239,7 +251,7 @@ struct TRootHandler_TVFS : public TFileTreeRoot LPBYTE pbCftFileTable; LPBYTE pbCftFileEntry; LPBYTE pbCftFileEnd; - LPBYTE pbVfsFileTable = DirHeader.pbDirectoryData + DirHeader.VfsTableOffset; + LPBYTE pbVfsFileTable = DirHeader.DataAt(DirHeader.VfsTableOffset); LPBYTE pbVfsFileEnd = pbVfsFileTable + DirHeader.VfsTableSize; size_t ItemSize = sizeof(DWORD) + sizeof(DWORD) + DirHeader.CftOffsSize; @@ -260,7 +272,7 @@ struct TRootHandler_TVFS : public TFileTreeRoot // // Resolve the Container File Table entry - pbCftFileTable = DirHeader.pbDirectoryData + DirHeader.CftTableOffset; + pbCftFileTable = DirHeader.DataAt(DirHeader.CftTableOffset); pbCftFileEntry = pbCftFileTable + dwCftOffset; pbCftFileEnd = pbCftFileTable + DirHeader.CftTableSize; @@ -380,10 +392,12 @@ struct TRootHandler_TVFS : public TFileTreeRoot DWORD IsVfsSubDirectory(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, TVFS_DIRECTORY_HEADER & SubHeader, LPBYTE EKey, DWORD dwFileSize) { PCASC_CKEY_ENTRY pCKeyEntry; - LPBYTE pbVfsData = NULL; - DWORD cbVfsData = dwFileSize; + CASC_BLOB VfsData; DWORD dwErrCode = ERROR_BAD_FORMAT; + // Keep compiler happy + CASCLIB_UNUSED(dwFileSize); + // Verify whether the EKey is in the list of VFS root files if(IsVfsFileEKey(hs, EKey, DirHeader.EKeySize)) { @@ -391,17 +405,16 @@ struct TRootHandler_TVFS : public TFileTreeRoot if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey)) != NULL) { // Load the entire file into memory - pbVfsData = LoadInternalFileToMemory(hs, pCKeyEntry, &cbVfsData); - if(pbVfsData && cbVfsData) + dwErrCode = LoadInternalFileToMemory(hs, pCKeyEntry, VfsData); + if(dwErrCode == ERROR_SUCCESS && VfsData.cbData) { // Capture the file folder. This also serves as test - dwErrCode = CaptureDirectoryHeader(SubHeader, pbVfsData, pbVfsData + cbVfsData); + dwErrCode = CaptureDirectoryHeader(SubHeader, VfsData); if(dwErrCode == ERROR_SUCCESS) return dwErrCode; // Clear the captured header memset(&SubHeader, 0, sizeof(TVFS_DIRECTORY_HEADER)); - CASC_FREE(pbVfsData); } } } @@ -533,11 +546,8 @@ struct TRootHandler_TVFS : public TFileTreeRoot assert(pCKeyEntry->ContentSize == SpanEntry.ContentSize); FileTree.InsertByName(pCKeyEntry, PathBuffer); - // Parse the subdir + // Parse the subdir. On error, stop the parsing dwErrCode = ParseDirectoryData(hs, SubHeader, PathBuffer); - CASC_FREE(SubHeader.pbDirectoryData); - - // On error, stop the parsing if(dwErrCode != ERROR_SUCCESS) return dwErrCode; } @@ -652,7 +662,7 @@ struct TRootHandler_TVFS : public TFileTreeRoot DWORD ParseDirectoryData(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, CASC_PATH<char> & PathBuffer) { - LPBYTE pbRootDirectory = DirHeader.pbDirectoryData + DirHeader.PathTableOffset; + LPBYTE pbRootDirectory = DirHeader.DataAt(DirHeader.PathTableOffset); LPBYTE pbRootDirPtr = pbRootDirectory; LPBYTE pbRootDirEnd = pbRootDirPtr + DirHeader.PathTableSize; DWORD dwNodeValue = 0; @@ -766,14 +776,14 @@ struct TRootHandler_TVFS : public TFileTreeRoot //----------------------------------------------------------------------------- // Public functions - TVFS root -DWORD RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) +DWORD RootHandler_CreateTVFS(TCascStorage * hs, CASC_BLOB & RootFile) { TRootHandler_TVFS * pRootHandler = NULL; TVFS_DIRECTORY_HEADER RootHeader; DWORD dwErrCode; // Capture the entire root directory - dwErrCode = TRootHandler_TVFS::CaptureDirectoryHeader(RootHeader, pbRootFile, pbRootFile + cbRootFile); + dwErrCode = TRootHandler_TVFS::CaptureDirectoryHeader(RootHeader, RootFile); if(dwErrCode == ERROR_SUCCESS) { // Allocate the root handler object diff --git a/dep/CascLib/src/CascRootFile_Text.cpp b/dep/CascLib/src/CascRootFile_Text.cpp index 655080bd2ee..ba4de629b5a 100644 --- a/dep/CascLib/src/CascRootFile_Text.cpp +++ b/dep/CascLib/src/CascRootFile_Text.cpp @@ -25,7 +25,7 @@ struct TRootHandler_SC1 : public TFileTreeRoot dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY); } - static bool IsRootFile(LPBYTE pbRootFile, DWORD cbRootFile) + static bool IsRootFile(LPBYTE pbRootFile, size_t cbRootFile) { CASC_CSV Csv(1, false); size_t nColumns; @@ -36,7 +36,7 @@ struct TRootHandler_SC1 : public TFileTreeRoot { // There must be 2 or 3 elements nColumns = Csv[CSV_ZERO].GetColumnCount(); - if (nColumns == 2 || nColumns == 3) + if(nColumns == 2 || nColumns == 3) { const CASC_CSV_COLUMN & FileName = Csv[CSV_ZERO][CSV_ZERO]; const CASC_CSV_COLUMN & CKeyStr = Csv[CSV_ZERO][1]; @@ -49,7 +49,7 @@ struct TRootHandler_SC1 : public TFileTreeRoot return bResult; } - DWORD Load(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) + DWORD Load(TCascStorage * hs, LPBYTE pbRootFile, size_t cbRootFile) { PCASC_CKEY_ENTRY pCKeyEntry; CASC_CSV Csv(0, false); @@ -93,20 +93,20 @@ struct TRootHandler_SC1 : public TFileTreeRoot // locales/zhCN/Assets/sound/terran/ghost/tghdth01.wav|6637ed776bd22089e083b8b0b2c0374c // -DWORD RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) +DWORD RootHandler_CreateStarcraft1(TCascStorage * hs, CASC_BLOB & RootFile) { TRootHandler_SC1 * pRootHandler = NULL; DWORD dwErrCode = ERROR_BAD_FORMAT; // Verify whether this looks like a Starcraft I root file - if(TRootHandler_SC1::IsRootFile(pbRootFile, cbRootFile)) + if(TRootHandler_SC1::IsRootFile(RootFile.pbData, RootFile.cbData)) { // Allocate the root handler object pRootHandler = new TRootHandler_SC1(); if(pRootHandler != NULL) { // Load the root directory. If load failed, we free the object - dwErrCode = pRootHandler->Load(hs, pbRootFile, cbRootFile); + dwErrCode = pRootHandler->Load(hs, RootFile.pbData, RootFile.cbData); if(dwErrCode != ERROR_SUCCESS) { delete pRootHandler; diff --git a/dep/CascLib/src/CascRootFile_WoW.cpp b/dep/CascLib/src/CascRootFile_WoW.cpp index e5d0d0426ed..5b5375bcc7b 100644 --- a/dep/CascLib/src/CascRootFile_WoW.cpp +++ b/dep/CascLib/src/CascRootFile_WoW.cpp @@ -367,7 +367,7 @@ struct TRootHandler_WoW : public TFileTreeRoot // Load the locale as-is dwErrCode = ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, dwLocaleMask, false, bAudioLocale); - if (dwErrCode != ERROR_SUCCESS) + if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // If we wanted enGB, we also load enUS for the missing files @@ -386,9 +386,13 @@ struct TRootHandler_WoW : public TFileTreeRoot DWORD dwErrCode; dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 0); - if (dwErrCode == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 1); +#ifdef _DEBUG + // Dump the array of the file data IDs + //FileTree.DumpFileDataIds("e:\\file-data-ids.bin"); +#endif return dwErrCode; } @@ -466,12 +470,13 @@ struct TRootHandler_WoW : public TFileTreeRoot //----------------------------------------------------------------------------- // Public functions -DWORD RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask) +DWORD RootHandler_CreateWoW(TCascStorage * hs, CASC_BLOB & RootFile, DWORD dwLocaleMask) { TRootHandler_WoW * pRootHandler = NULL; FILE_ROOT_HEADER_82 RootHeader; ROOT_FORMAT RootFormat = RootFormatWoW6x; - LPBYTE pbRootEnd = pbRootFile + cbRootFile; + LPBYTE pbRootFile = RootFile.pbData; + LPBYTE pbRootEnd = RootFile.End(); LPBYTE pbRootPtr; DWORD FileCounterHashless = 0; DWORD dwErrCode = ERROR_BAD_FORMAT; diff --git a/dep/CascLib/src/common/Array.h b/dep/CascLib/src/common/Array.h index 832a230a8ed..b46f257e8a4 100644 --- a/dep/CascLib/src/common/Array.h +++ b/dep/CascLib/src/common/Array.h @@ -42,8 +42,11 @@ class CASC_ARRAY // Creates an array with a custom element size int Create(size_t ItemSize, size_t ItemCountMax) { + // Sanity check + assert(ItemCountMax != 0); + // Create the array - if ((m_pItemArray = CASC_ALLOC<BYTE>(ItemSize * ItemCountMax)) == NULL) + if((m_pItemArray = CASC_ALLOC<BYTE>(ItemSize * ItemCountMax)) == NULL) return ERROR_NOT_ENOUGH_MEMORY; m_ItemCountMax = ItemCountMax; @@ -58,7 +61,7 @@ class CASC_ARRAY void * pNewItems; // Try to enlarge the buffer, if needed - if (!EnlargeArray(m_ItemCount + NewItemCount, bEnlargeAllowed)) + if(!EnlargeArray(m_ItemCount + NewItemCount, bEnlargeAllowed)) return NULL; pNewItems = m_pItemArray + (m_ItemCount * m_ItemSize); @@ -75,7 +78,7 @@ class CASC_ARRAY void * pNewItem = Insert(NewItemCount, bEnlargeAllowed); // Copy the item(s) to the array, if any - if (pNewItem && NewItems) + if(pNewItem && NewItems) memcpy(pNewItem, NewItems, (NewItemCount * m_ItemSize)); return pNewItem; } @@ -108,7 +111,7 @@ class CASC_ARRAY m_ItemCount = CASCLIB_MAX(m_ItemCount, ItemIndex+1); // If we inserted an item past the current end, we need to clear the items in-between - if (pbNewItem > pbLastItem) + if(pbNewItem > pbLastItem) { memset(pbLastItem, 0, (pbNewItem - pbLastItem)); m_ItemCount = ItemIndex + 1; @@ -167,6 +170,24 @@ class CASC_ARRAY m_ItemCountMax = m_ItemCount = m_ItemSize = 0; } +#ifdef _DEBUG + size_t BytesAllocated() + { + return m_ItemCountMax * m_ItemSize; + } + + void Dump(const char * szFileName) + { + FILE * fp; + + if((fp = fopen(szFileName, "wb")) != NULL) + { + fwrite(m_pItemArray, m_ItemSize, m_ItemCount, fp); + fclose(fp); + } + } +#endif + protected: bool EnlargeArray(size_t NewItemCount, bool bEnlargeAllowed) @@ -179,7 +200,7 @@ class CASC_ARRAY assert(m_ItemCountMax != 0); // Shall we enlarge the table? - if (NewItemCount > m_ItemCountMax) + if(NewItemCount > m_ItemCountMax) { // Deny enlarge if not allowed if(bEnlargeAllowed == false) @@ -192,7 +213,7 @@ class CASC_ARRAY // Allocate new table NewItemArray = CASC_REALLOC(m_pItemArray, (ItemCountMax * m_ItemSize)); - if (NewItemArray == NULL) + if(NewItemArray == NULL) return false; // Set the new table size diff --git a/dep/CascLib/src/common/ArraySparse.h b/dep/CascLib/src/common/ArraySparse.h new file mode 100644 index 00000000000..eb7d478cf3e --- /dev/null +++ b/dep/CascLib/src/common/ArraySparse.h @@ -0,0 +1,286 @@ +/*****************************************************************************/ +/* ArraySparse.h Copyright (c) Ladislav Zezula 2022 */ +/*---------------------------------------------------------------------------*/ +/* This is somewhat more effective version of CASC_ARRAY, used for mapping */ +/* of uint32_t -> pointer. Works better when there are large gaps in seqs */ +/* of the source integers and when there is large gap at the beginning */ +/* of the array. */ +/* */ +/* This ofject works as multi-level table, where each byte from the mapping */ +/* integer is an index to the appropriate sub-table */ +/* */ +/* Example: Mapping of 11223344 -> POINTER */ +/* */ +/* The source uint_32_t is divided into 4 bytes. Each byte is an index to */ +/* the corresponding level-N sub-table */ +/* */ +/* [*] 0x11223344 -> {0x11, 0x22, 0x33, 0x44} */ +/* */ +/* 0x11 is the index to the level-0 table. Contains pointer to level-1 table */ +/* 0x22 is the index to the level-1 table. Contains pointer to level-2 table */ +/* 0x33 is the index to the level-2 table. Contains pointer to level-3 table */ +/* 0x44 is the index to the level-3 table. Contains the result POINTER */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 14.12.22 1.00 Lad Created */ +/*****************************************************************************/ + +#ifndef __CASC_SPARSE_ARRAY_H__ +#define __CASC_SPARSE_ARRAY_H__ + +//----------------------------------------------------------------------------- +// Structure of the 256-item sub-table. Each table item contains either +// pointer to the lower sub-table (if present) or the pointer to the target item + +struct CASC_ARRAY_256 +{ + CASC_ARRAY_256() + { + memset(Pointers, 0, sizeof(Pointers)); + } + + CASC_ARRAY_256 * GetLowerLevel(size_t nIndex) + { + return (CASC_ARRAY_256 *)(Pointers[nIndex & 0xFF]); + } + + CASC_ARRAY_256 ** SubTable(size_t ItemIndex, size_t nLevel) + { + // Calculate the bit shift for getting the level index + size_t nShift = 0x20 - (nLevel * 8); + size_t nIndex = (ItemIndex >> nShift) & 0xFF; + + // Return reference to the item + return (CASC_ARRAY_256 **)(&Pointers[nIndex]); + } + + void Reset(size_t nLevel) + { + CASC_ARRAY_256 * pLower; + + // For levels 0, 1, 2, free the sub-items, if any + if(nLevel < 3) + { + for(size_t i = 0; i < _countof(Pointers); i++) + { + if((pLower = GetLowerLevel(i)) != NULL) + { + pLower->Reset(nLevel + 1); + } + } + } + + // Set all pointers to NULL for level 3 + else + { + memset(Pointers, 0, sizeof(CASC_ARRAY_256)); + } + } + + void Free(size_t nLevel) + { + CASC_ARRAY_256 * pLower; + + // For levels 0, 1, 2, free the sub-items, if any + if(nLevel < 3) + { + for(size_t i = 0; i < _countof(Pointers); i++) + { + if((pLower = GetLowerLevel(i)) != NULL) + { + pLower->Free(nLevel + 1); + delete pLower; + } + } + } + + // Set all pointers to NULL + memset(Pointers, 0, sizeof(CASC_ARRAY_256)); + } + + void * Pointers[0x100]; +}; + +//----------------------------------------------------------------------------- +// Interface to the sparse array class + +class CASC_SPARSE_ARRAY +{ + public: + + CASC_SPARSE_ARRAY() + { + m_LevelsAllocated = 0; + m_ItemCount = 0; + m_pLevel0 = NULL; + } + + ~CASC_SPARSE_ARRAY() + { + Free(); + } + + // Creates an array with a custom element type + template<typename TYPE> + DWORD Create(size_t /* ItemCountMax */) + { + if((m_pLevel0 = new CASC_ARRAY_256()) != NULL) + { + m_LevelsAllocated++; + return ERROR_SUCCESS; + } + return ERROR_NOT_ENOUGH_MEMORY; + } + + // Returns pointer to the cell given index + void ** ItemAt(size_t ItemIndex) + { + CASC_ARRAY_256 * pLevelN; + + // The index must be 32-bit only + assert((DWORD)(ItemIndex) == ItemIndex); + + // Get the level-0 index + if((pLevelN = m_pLevel0) != NULL) + { + if((pLevelN = GetLowerLevelTable(pLevelN, ItemIndex, 1)) != NULL) + { + if((pLevelN = GetLowerLevelTable(pLevelN, ItemIndex, 2)) != NULL) + { + if((pLevelN = GetLowerLevelTable(pLevelN, ItemIndex, 3)) != NULL) + { + return &pLevelN->Pointers[ItemIndex & 0xFF]; + } + } + } + } + + // Not present + return NULL; + } + + // Inserts an item at a given index and returns pointer to its cell + void ** InsertAt(size_t ItemIndex) + { + CASC_ARRAY_256 * pLevelN; + + // The index must be 32-bit only + assert((DWORD)(ItemIndex) == ItemIndex); + + // Get the level-0 index + if((pLevelN = m_pLevel0) != NULL) + { + if((pLevelN = EnsureLowerLevelTable(pLevelN, ItemIndex, 1)) != NULL) + { + if((pLevelN = EnsureLowerLevelTable(pLevelN, ItemIndex, 2)) != NULL) + { + if((pLevelN = EnsureLowerLevelTable(pLevelN, ItemIndex, 3)) != NULL) + { + // Increment the max index and return the pointer to the appropriate cell + if(ItemIndex >= m_ItemCount) + m_ItemCount = ItemIndex + 1; + return &pLevelN->Pointers[ItemIndex & 0xFF]; + } + } + } + } + + // Not enough memory + return NULL; + } + + // Invalidates the entire array, but keeps memory allocated + void Reset() + { + if(m_pLevel0 != NULL) + { + m_pLevel0->Reset(0); + } + } + + // Frees the array + void Free() + { + if(m_pLevel0 != NULL) + { + m_pLevel0->Free(0); + delete m_pLevel0; + } + } + + size_t ItemCount() + { + return m_ItemCount; + } + + size_t ItemCountMax() + { + return 0xFFFFFFFF; + } + + size_t ItemSize() + { + return sizeof(void *); + } + + bool IsInitialized() + { + return (m_pLevel0 != NULL); + } + +#ifdef _DEBUG + size_t BytesAllocated() + { + return m_LevelsAllocated * sizeof(CASC_ARRAY_256); + } + + void Dump(const char * szFileName) + { + FILE * fp; + size_t * RefItem; + size_t Item; + + if((fp = fopen(szFileName, "wb")) != NULL) + { + for(size_t i = 0; i < m_ItemCount; i++) + { + RefItem = (size_t *)ItemAt(i); + Item = (RefItem != NULL) ? RefItem[0] : 0; + fwrite(&Item, ItemSize(), 1, fp); + } + fclose(fp); + } + } +#endif + + protected: + + CASC_ARRAY_256 * GetLowerLevelTable(CASC_ARRAY_256 * pTable, size_t ItemIndex, size_t nLevel) + { + CASC_ARRAY_256 ** SubTable = pTable->SubTable(ItemIndex, nLevel); + + // Get the n-th item from the table + return SubTable[0]; + } + + CASC_ARRAY_256 * EnsureLowerLevelTable(CASC_ARRAY_256 * pTable, size_t ItemIndex, size_t nLevel) + { + CASC_ARRAY_256 ** SubTable = pTable->SubTable(ItemIndex, nLevel); + + // Is there an item? + if(SubTable[0] == NULL) + { + SubTable[0] = new CASC_ARRAY_256(); + m_LevelsAllocated++; + } + return SubTable[0]; + } + + // Level-0 subitem table + CASC_ARRAY_256 * m_pLevel0; // Array of level 0 of pointers + size_t m_LevelsAllocated; // Number of CASC_ARRAY_256's allocated (for debugging purposes) + size_t m_ItemCount; // The number of items inserted +}; + +#endif // __CASC_SPARSE_ARRAY_H__ diff --git a/dep/CascLib/src/common/Common.cpp b/dep/CascLib/src/common/Common.cpp index ac06e8df53e..63fb4301d57 100644 --- a/dep/CascLib/src/common/Common.cpp +++ b/dep/CascLib/src/common/Common.cpp @@ -69,7 +69,7 @@ unsigned char AsciiToHexTable[128] = 0xFF, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; unsigned char IntToHexChar[] = "0123456789abcdef"; @@ -229,10 +229,10 @@ void CascStrCopy(char * szTarget, size_t cchTarget, const char * szSource, size_ { size_t cchToCopy; - if (cchTarget > 0) + if(cchTarget > 0) { // Make sure we know the length - if (cchSource == -1) + if(cchSource == -1) cchSource = strlen(szSource); cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource); @@ -246,10 +246,10 @@ void CascStrCopy(char * szTarget, size_t cchTarget, const wchar_t * szSource, si { size_t cchToCopy; - if (cchTarget > 0) + if(cchTarget > 0) { // Make sure we know the length - if (cchSource == -1) + if(cchSource == -1) cchSource = wcslen(szSource); cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource); @@ -262,10 +262,10 @@ void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const char * szSource, si { size_t cchToCopy; - if (cchTarget > 0) + if(cchTarget > 0) { // Make sure we know the length - if (cchSource == -1) + if(cchSource == -1) cchSource = strlen(szSource); cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource); @@ -278,10 +278,10 @@ void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const wchar_t * szSource, { size_t cchToCopy; - if (cchTarget > 0) + if(cchTarget > 0) { // Make sure we know the length - if (cchSource == -1) + if(cchSource == -1) cchSource = wcslen(szSource); cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource); @@ -293,46 +293,58 @@ void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const wchar_t * szSource, //----------------------------------------------------------------------------- // Safe version of s(w)printf -size_t CascStrPrintf(char * buffer, size_t nCount, const char * format, ...) +size_t CascStrPrintfV(char * buffer, size_t nCount, const char * format, va_list argList) { char * buffend; - va_list argList; - // Start the argument list - va_start(argList, format); - #ifdef CASCLIB_PLATFORM_WINDOWS StringCchVPrintfExA(buffer, nCount, &buffend, NULL, 0, format, argList); -// buffend = buffer + vsnprintf(buffer, nCount, format, argList); #else buffend = buffer + vsnprintf(buffer, nCount, format, argList); #endif - - // End the argument list - va_end(argList); + return (buffend - buffer); } -size_t CascStrPrintf(wchar_t * buffer, size_t nCount, const wchar_t * format, ...) +size_t CascStrPrintf(char * buffer, size_t nCount, const char * format, ...) { - wchar_t * buffend; va_list argList; + size_t length; // Start the argument list va_start(argList, format); + length = CascStrPrintfV(buffer, nCount, format, argList); + va_end(argList); + + return length; +} + +size_t CascStrPrintfV(wchar_t * buffer, size_t nCount, const wchar_t * format, va_list argList) +{ + wchar_t * buffend; #ifdef CASCLIB_PLATFORM_WINDOWS StringCchVPrintfExW(buffer, nCount, &buffend, NULL, 0, format, argList); -// buffend = buffer + vswprintf(buffer, nCount, format, argList); #else buffend = buffer + vswprintf(buffer, nCount, format, argList); #endif - // End the argument list - va_end(argList); return (buffend - buffer); } +size_t CascStrPrintf(wchar_t * buffer, size_t nCount, const wchar_t * format, ...) +{ + va_list argList; + size_t length; + + // Start the argument list + va_start(argList, format); + length = CascStrPrintfV(buffer, nCount, format, argList); + va_end(argList); + + return length; +} + //----------------------------------------------------------------------------- // String allocation @@ -413,74 +425,7 @@ LPTSTR CascNewStrA2T(LPCSTR szString, size_t nCharsToReserve) } //----------------------------------------------------------------------------- -// String merging - -LPTSTR GetLastPathPart(LPTSTR szWorkPath) -{ - size_t nLength = _tcslen(szWorkPath); - - // Go one character back - if(nLength > 0) - nLength--; - - // Cut ending (back)slashes, if any - while(nLength > 0 && (szWorkPath[nLength] == _T('\\') || szWorkPath[nLength] == _T('/'))) - nLength--; - - // Cut the last path part - while(nLength > 0) - { - // End of path? - if(szWorkPath[nLength] == _T('\\') || szWorkPath[nLength] == _T('/')) - { - return szWorkPath + nLength; - } - - // Go one character back - nLength--; - } - - return NULL; -} - -bool CutLastPathPart(LPTSTR szWorkPath) -{ - // Get the last part of the path - szWorkPath = GetLastPathPart(szWorkPath); - if(szWorkPath == NULL) - return false; - - szWorkPath[0] = 0; - return true; -} - -size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, va_list argList) -{ - CASC_PATH<TCHAR> Path(PATH_SEP_CHAR); - LPCTSTR szFragment; - bool bWithSeparator = false; - - // Combine all parts of the path here - while((szFragment = va_arg(argList, LPTSTR)) != NULL) - { - Path.AppendString(szFragment, bWithSeparator); - bWithSeparator = true; - } - - return Path.Copy(szBuffer, nMaxChars); -} - -size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, ...) -{ - va_list argList; - size_t nLength; - - va_start(argList, nMaxChars); - nLength = CombinePath(szBuffer, nMaxChars, argList); - va_end(argList); - - return nLength; -} +// String normalization size_t NormalizeFileName(const unsigned char * NormTable, char * szNormName, const char * szFileName, size_t cchMaxChars) { diff --git a/dep/CascLib/src/common/Common.h b/dep/CascLib/src/common/Common.h index 3fbbf0d842c..5abc5c77b9a 100644 --- a/dep/CascLib/src/common/Common.h +++ b/dep/CascLib/src/common/Common.h @@ -124,7 +124,7 @@ typedef CASC_CKEY_ENTRY *PCASC_CKEY_ENTRY; extern unsigned char AsciiToLowerTable_Slash[256]; extern unsigned char AsciiToUpperTable_BkSlash[256]; -extern unsigned char AsciiToHexTable[0x80]; +extern unsigned char AsciiToHexTable[128]; extern unsigned char IntToHexChar[]; //----------------------------------------------------------------------------- @@ -186,7 +186,7 @@ inline DWORD Rol32(DWORD dwValue, DWORD dwRolCount) //----------------------------------------------------------------------------- // Big endian number manipulation -inline DWORD ConvertBytesToInteger_2(LPBYTE ValueAsBytes) +inline USHORT ConvertBytesToInteger_2(LPBYTE ValueAsBytes) { USHORT Value = 0; @@ -325,7 +325,9 @@ void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const wchar_t * szSource, //----------------------------------------------------------------------------- // Safe version of s(w)printf +size_t CascStrPrintfV(char * buffer, size_t nCount, const char * format, va_list argList); size_t CascStrPrintf(char * buffer, size_t nCount, const char * format, ...); +size_t CascStrPrintfV(wchar_t * buffer, size_t nCount, const wchar_t * format, va_list argList); size_t CascStrPrintf(wchar_t * buffer, size_t nCount, const wchar_t * format, ...); //----------------------------------------------------------------------------- @@ -337,11 +339,6 @@ wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve = 0); LPSTR CascNewStrT2A(LPCTSTR szString, size_t nCharsToReserve = 0); LPTSTR CascNewStrA2T(LPCSTR szString, size_t nCharsToReserve = 0); -size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, va_list argList); -size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, ...); -LPTSTR GetLastPathPart(LPTSTR szWorkPath); -bool CutLastPathPart(LPTSTR szWorkPath); - size_t NormalizeFileName_UpperBkSlash(char * szNormName, const char * szFileName, size_t cchMaxChars); size_t NormalizeFileName_LowerSlash(char * szNormName, const char * szFileName, size_t cchMaxChars); @@ -351,6 +348,12 @@ ULONGLONG CalcFileNameHash(const char * szFileName); //----------------------------------------------------------------------------- // String conversion functions +template <typename xchar> +bool IsHexadecimalDigit(xchar ch) +{ + return ((ch < sizeof(AsciiToHexTable)) && (AsciiToHexTable[ch] != 0xFF)); +} + template <typename xchar, typename INTXX> DWORD ConvertStringToInt(const xchar * szString, size_t nMaxDigits, INTXX & RefValue, const xchar ** PtrStringEnd = NULL) { @@ -448,34 +451,75 @@ xchar * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, xchar * szBuffer) //----------------------------------------------------------------------------- // Structures for data blobs -struct QUERY_KEY +struct CASC_BLOB { - QUERY_KEY() + CASC_BLOB() { - pbData = NULL; - cbData = 0; + Reset(); } - ~QUERY_KEY() + ~CASC_BLOB() { - CASC_FREE(pbData); - cbData = 0; + Free(); + } + + void MoveFrom(CASC_BLOB & Source) + { + // Free current data, if any + Free(); + + // Take the source data + pbData = Source.pbData; + cbData = Source.cbData; + + // Reset the source data without freeing + Source.Reset(); } DWORD SetData(const void * pv, size_t cb) { - if((pbData = CASC_ALLOC<BYTE>(cb)) == NULL) + if(SetSize(cb) != ERROR_SUCCESS) return ERROR_NOT_ENOUGH_MEMORY; memcpy(pbData, pv, cb); + return ERROR_SUCCESS; + } + + DWORD SetSize(size_t cb) + { + Free(); + + // Always leave one extra byte for NUL character + if((pbData = CASC_ALLOC<BYTE>(cb + 1)) == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + cbData = cb; return ERROR_SUCCESS; } + void Reset() + { + pbData = NULL; + cbData = 0; + } + + void Free() + { + if(pbData != NULL) + CASC_FREE(pbData); + pbData = NULL; + cbData = 0; + } + + LPBYTE End() const + { + return pbData + cbData; + } + LPBYTE pbData; size_t cbData; }; -typedef QUERY_KEY *PQUERY_KEY; +typedef CASC_BLOB *PCASC_BLOB; //----------------------------------------------------------------------------- // File name utilities @@ -526,22 +570,11 @@ bool CascCheckWildCard(const char * szString, const char * szWildCard); //----------------------------------------------------------------------------- // Hashing functions -ULONGLONG HashStringJenkins(const char * szFileName); - bool CascIsValidMD5(LPBYTE pbMd5); void CascCalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash); bool CascVerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5); //----------------------------------------------------------------------------- -// Scanning a directory - -typedef bool (*INDEX_FILE_FOUND)(LPCTSTR szFileName, void * pvContext); - -bool DirectoryExists(LPCTSTR szDirectory); - -int ScanIndexDirectory(LPCTSTR szIndexPath, INDEX_FILE_FOUND pfnOnFileFound, void * pvContext); - -//----------------------------------------------------------------------------- // Argument structure versioning // Safely retrieves field value from a structure // intended for cases where users upgrade CascLib by simply dropping in a new .dll without recompiling their app diff --git a/dep/CascLib/src/common/Csv.cpp b/dep/CascLib/src/common/Csv.cpp index 589c711571a..ba333399479 100644 --- a/dep/CascLib/src/common/Csv.cpp +++ b/dep/CascLib/src/common/Csv.cpp @@ -42,7 +42,7 @@ static char * NextColumn_Default(void * /* pvUserData */, char * szColumn) szColumn++; // Terminate the column - if (szColumn[0] == '|') + if(szColumn[0] == '|') { *szColumn++ = 0; return szColumn; @@ -158,7 +158,6 @@ CASC_CSV::CASC_CSV(size_t nLinesMax, bool bHasHeader) m_pvUserData = NULL; m_szCsvFile = NULL; m_szCsvPtr = NULL; - m_nCsvFile = 0; m_nLines = 0; m_bHasHeader = bHasHeader; @@ -185,8 +184,9 @@ CASC_CSV::CASC_CSV(size_t nLinesMax, bool bHasHeader) CASC_CSV::~CASC_CSV() { if(m_pLines != NULL) + { delete[] m_pLines; - CASC_FREE(m_szCsvFile); + } } DWORD CASC_CSV::SetNextLineProc(CASC_CSV_NEXTPROC PfnNextLineProc, CASC_CSV_NEXTPROC PfnNextColProc, void * pvUserData) @@ -206,24 +206,18 @@ DWORD CASC_CSV::SetNextLineProc(CASC_CSV_NEXTPROC PfnNextLineProc, CASC_CSV_NEXT DWORD CASC_CSV::Load(LPCTSTR szFileName) { - DWORD cbFileData = 0; - DWORD dwErrCode = ERROR_SUCCESS; + DWORD dwErrCode; - m_szCsvFile = (char *)LoadFileToMemory(szFileName, &cbFileData); - if (m_szCsvFile != NULL) + dwErrCode = LoadFileToMemory(szFileName, CsvFile); + if(dwErrCode == ERROR_SUCCESS) { // Assign the data to the CSV object + m_szCsvFile = (char *)CsvFile.pbData; m_szCsvPtr = m_szCsvFile; - m_nCsvFile = cbFileData; // Parse the data dwErrCode = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT; } - else - { - dwErrCode = GetCascError(); - } - return dwErrCode; } @@ -231,19 +225,16 @@ DWORD CASC_CSV::Load(LPBYTE pbData, size_t cbData) { DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY; - m_szCsvFile = CASC_ALLOC<char>(cbData + 1); - if (m_szCsvFile != NULL) + if((dwErrCode = CsvFile.SetData(pbData, cbData)) == ERROR_SUCCESS) { // Copy the entire data and terminate them with zero - memcpy(m_szCsvFile, pbData, cbData); - m_szCsvFile[cbData] = 0; + m_szCsvFile = (char *)CsvFile.pbData; + m_szCsvFile[CsvFile.cbData] = 0; m_szCsvPtr = m_szCsvFile; - m_nCsvFile = cbData; // Parse the data dwErrCode = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT; } - return dwErrCode; } @@ -303,7 +294,7 @@ bool CASC_CSV::LoadNextLine(CASC_CSV_LINE & Line) m_szCsvPtr = PfnNextLine(m_pvUserData, m_szCsvPtr); // Initialize the line - if (Line.SetLine(this, szCsvLine)) + if(Line.SetLine(this, szCsvLine)) return true; } @@ -320,7 +311,7 @@ bool CASC_CSV::ParseCsvData() if(m_bHasHeader) { // Load the current line to the header - if (!LoadNextLine(Header)) + if(!LoadNextLine(Header)) return false; // Initialize the hash table @@ -345,14 +336,14 @@ bool CASC_CSV::ParseCsvData() const CASC_CSV_COLUMN & CASC_CSV::operator[](const char * szColumnName) const { - if (m_pLines == NULL || m_nLines == 0) + if(m_pLines == NULL || m_nLines == 0) return NullColumn; return m_pLines[0][GetColumnIndex(szColumnName)]; } const CASC_CSV_LINE & CASC_CSV::operator[](size_t nIndex) const { - if (m_pLines == NULL || nIndex >= m_nLines) + if(m_pLines == NULL || nIndex >= m_nLines) return NullLine; return m_pLines[nIndex]; } diff --git a/dep/CascLib/src/common/Csv.h b/dep/CascLib/src/common/Csv.h index 8df99c63456..1878344b24c 100644 --- a/dep/CascLib/src/common/Csv.h +++ b/dep/CascLib/src/common/Csv.h @@ -113,11 +113,11 @@ class CASC_CSV CASC_CSV_LINE * m_pLines; CASC_CSV_LINE Header; + CASC_BLOB CsvFile; BYTE HashTable[CSV_HASH_TABLE_SIZE]; void * m_pvUserData; char * m_szCsvFile; char * m_szCsvPtr; - size_t m_nCsvFile; size_t m_nLinesMax; size_t m_nLines; bool m_bHasHeader; diff --git a/dep/CascLib/src/common/Directory.cpp b/dep/CascLib/src/common/Directory.cpp index efabda45171..cef7dfa6319 100644 --- a/dep/CascLib/src/common/Directory.cpp +++ b/dep/CascLib/src/common/Directory.cpp @@ -52,37 +52,47 @@ bool MakeDirectory(LPCTSTR szDirectory) #endif } -int ScanIndexDirectory( - LPCTSTR szIndexPath, - INDEX_FILE_FOUND pfnOnFileFound, +DWORD ScanDirectory( + LPCTSTR szDirectory, + DIRECTORY_CALLBACK PfnFolderCallback, + DIRECTORY_CALLBACK PfnFileCallback, void * pvContext) { #ifdef CASCLIB_PLATFORM_WINDOWS + CASC_PATH<TCHAR> SearchMask(szDirectory, _T("*"), NULL); WIN32_FIND_DATA wf; HANDLE hFind; - TCHAR szSearchMask[MAX_PATH]; - - // Prepare the search mask - CombinePath(szSearchMask, _countof(szSearchMask), szIndexPath, _T("*"), NULL); // Prepare directory search - hFind = FindFirstFile(szSearchMask, &wf); + hFind = FindFirstFile(SearchMask, &wf); if(hFind != INVALID_HANDLE_VALUE) { // Skip the first file as it's always just "." or ".." while(FindNextFile(hFind, &wf)) { - // If the found object is a file, pass it to the handler - if(!(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + // If we found a folder, we call the directory callback + if(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if(PfnFolderCallback != NULL) + { + if(!PfnFolderCallback(wf.cFileName, pvContext)) + break; + } + } + else { - // Let the callback scan the file name - pfnOnFileFound(wf.cFileName, pvContext); + if(PfnFileCallback != NULL) + { + if(!PfnFileCallback(wf.cFileName, pvContext)) + break; + } } } // Close the search handle FindClose(hFind); + return ERROR_SUCCESS; } #else // CASCLIB_PLATFORM_WINDOWS @@ -90,21 +100,39 @@ int ScanIndexDirectory( struct dirent * dir_entry; DIR * dir; - dir = opendir(szIndexPath); - if(dir != NULL) + // Prepare directory search + if((dir = opendir(szDirectory)) != NULL) { + // Read (the next) directory entry while((dir_entry = readdir(dir)) != NULL) { - if(dir_entry->d_type != DT_DIR) + if(dir_entry->d_type == DT_DIR) + { + if(PfnFolderCallback != NULL) + { + if(!PfnFolderCallback(dir_entry->d_name, pvContext)) + { + break; + } + } + } + else { - pfnOnFileFound(dir_entry->d_name, pvContext); + if(PfnFileCallback != NULL) + { + if(!PfnFileCallback(dir_entry->d_name, pvContext)) + { + break; + } + } } } closedir(dir); + return ERROR_SUCCESS; } #endif - return ERROR_SUCCESS; + return ERROR_PATH_NOT_FOUND; } diff --git a/dep/CascLib/src/common/Directory.h b/dep/CascLib/src/common/Directory.h index 30c7e384284..f2f0d5e4c65 100644 --- a/dep/CascLib/src/common/Directory.h +++ b/dep/CascLib/src/common/Directory.h @@ -14,13 +14,21 @@ //----------------------------------------------------------------------------- // Scanning a directory -bool DirectoryExists(LPCTSTR szDirectory); +// If the callback returns false, the directory enumeration stops +typedef bool (*DIRECTORY_CALLBACK)(LPCTSTR szPathName, void * pvContext); -bool MakeDirectory(LPCTSTR szDirectory); +bool DirectoryExists( + LPCTSTR szDirectory + ); + +bool MakeDirectory( + LPCTSTR szDirectory + ); -int ScanIndexDirectory( - LPCTSTR szIndexPath, - INDEX_FILE_FOUND pfnOnFileFound, +DWORD ScanDirectory( + LPCTSTR szDirectory, + DIRECTORY_CALLBACK PfnFolderCallback, // Can be NULL if the caller doesn't care about folders + DIRECTORY_CALLBACK PfnFileCallback, // Can be NULL if the caller doesn't care about files void * pvContext ); diff --git a/dep/CascLib/src/common/FileStream.cpp b/dep/CascLib/src/common/FileStream.cpp index 622af421a82..7151ca9f36d 100644 --- a/dep/CascLib/src/common/FileStream.cpp +++ b/dep/CascLib/src/common/FileStream.cpp @@ -672,20 +672,22 @@ static DWORD BaseHttp_ParseURL(TFileStream * pStream, LPCTSTR szFileName, int * static bool BaseHttp_Download(TFileStream * pStream) { + CASC_MIME_RESPONSE MimeResponse; + CASC_BLOB FileData; CASC_MIME Mime; const char * request_mask = "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: Keep-Alive\r\n\r\n"; char * server_response; char * fileName = pStream->Base.Socket.fileName; char request[0x100]; - size_t response_length = 0; size_t request_length = 0; - DWORD dwErrCode; + DWORD dwErrCode = ERROR_SUCCESS; // If we already have the data, it's success if(pStream->Base.Socket.fileData == NULL) { // Reset the file data length as well pStream->Base.Socket.fileDataLength = 0; + dwErrCode = ERROR_BAD_FORMAT; // Construct the request, either HTTP or Ribbit (https://wowdev.wiki/Ribbit). // Note that Ribbit requests don't start with slash @@ -698,31 +700,31 @@ static bool BaseHttp_Download(TFileStream * pStream) // Send the request and receive decoded response request_length = CascStrPrintf(request, _countof(request), request_mask, fileName, pStream->Base.Socket.hostName); - server_response = pStream->Base.Socket.pSocket->ReadResponse(request, request_length, &response_length); + server_response = pStream->Base.Socket.pSocket->ReadResponse(request, request_length, MimeResponse); if(server_response != NULL) { - // Check for non-zero data - if(response_length != 0) + // Decode the MIME document + if((dwErrCode = Mime.Load(server_response, MimeResponse)) == ERROR_SUCCESS) { - // Decode the MIME document - if((dwErrCode = Mime.Load(server_response, response_length)) == ERROR_SUCCESS) + // Move the data from MIME to HTTP stream + if((dwErrCode = Mime.GiveAway(FileData)) == ERROR_SUCCESS) { - // Move the data from MIME to HTTP stream - pStream->Base.Socket.fileData = Mime.GiveAway(&pStream->Base.Socket.fileDataLength); + pStream->Base.Socket.fileData = FileData.pbData; + pStream->Base.Socket.fileDataLength = FileData.cbData; + pStream->Base.Socket.fileDataPos = 0; + FileData.Reset(); } } - else - { - SetCascError(ERROR_BAD_FORMAT); - } // Free the buffer CASC_FREE(server_response); } } - // If we have data loaded, return true - return (pStream->Base.Socket.fileData != NULL); + // Process error codes + if(dwErrCode != ERROR_SUCCESS) + SetCascError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); } static bool BaseHttp_Open(TFileStream * pStream, LPCTSTR szFileName, DWORD dwStreamFlags) diff --git a/dep/CascLib/src/common/FileTree.cpp b/dep/CascLib/src/common/FileTree.cpp index 490698122e5..fa21863c5e9 100644 --- a/dep/CascLib/src/common/FileTree.cpp +++ b/dep/CascLib/src/common/FileTree.cpp @@ -177,7 +177,7 @@ bool CASC_FILE_TREE::RebuildNameMaps() if(NameMap.Create(nMaxItems, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_NODE, FileNameHash)) != ERROR_SUCCESS) return false; - // Reset the entire array, but keep the buffer allocated + // Reset the entire array, but buffers allocated FileDataIds.Reset(); // Parse all items and insert them to the map @@ -393,13 +393,22 @@ PCASC_FILE_NODE CASC_FILE_TREE::ItemAt(size_t nItemIndex) PCASC_FILE_NODE CASC_FILE_TREE::PathAt(char * szBuffer, size_t cchBuffer, size_t nItemIndex) { + PCASC_FILE_NODE * RefFileNode; PCASC_FILE_NODE pFileNode = NULL; // If we have FileDataId, then we need to enumerate the files by FileDataId if(FileDataIds.IsInitialized()) - pFileNode = *(PCASC_FILE_NODE *)FileDataIds.ItemAt(nItemIndex); + { + RefFileNode = (PCASC_FILE_NODE *)FileDataIds.ItemAt(nItemIndex); + if(RefFileNode != NULL) + { + pFileNode = *(PCASC_FILE_NODE *)FileDataIds.ItemAt(nItemIndex); + } + } else + { pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nItemIndex); + } // Construct the entire path PathAt(szBuffer, cchBuffer, pFileNode); diff --git a/dep/CascLib/src/common/FileTree.h b/dep/CascLib/src/common/FileTree.h index 38bc4e07e9f..69a2c15c496 100644 --- a/dep/CascLib/src/common/FileTree.h +++ b/dep/CascLib/src/common/FileTree.h @@ -82,6 +82,13 @@ class CASC_FILE_TREE // Retrieve the maximum FileDataId ever inserted DWORD GetNextFileDataId(); +#ifdef _DEBUG + void DumpFileDataIds(const char * szFileName) + { + FileDataIds.Dump(szFileName); + } +#endif + protected: PCASC_FILE_NODE InsertNew(PCASC_CKEY_ENTRY pCKeyEntry); @@ -95,7 +102,8 @@ class CASC_FILE_TREE CASC_ARRAY NodeTable; // Dynamic array that holds all CASC_FILE_NODEs CASC_ARRAY NameTable; // Dynamic array that holds all node names - CASC_ARRAY FileDataIds; // Dynamic array that maps FileDataId -> CASC_FILE_NODE + CASC_SPARSE_ARRAY FileDataIds; // Dynamic array that maps FileDataId -> CASC_FILE_NODE + //CASC_ARRAY FileDataIds; // Dynamic array that maps FileDataId -> CASC_FILE_NODE CASC_MAP NameMap; // Map of FileNameHash -> CASC_FILE_NODE size_t FileDataIdOffset; // If nonzero, this is the offset of the "FileDataId" field in the CASC_FILE_NODE diff --git a/dep/CascLib/src/common/ListFile.cpp b/dep/CascLib/src/common/ListFile.cpp index a91922cd9b1..4545ada62e9 100644 --- a/dep/CascLib/src/common/ListFile.cpp +++ b/dep/CascLib/src/common/ListFile.cpp @@ -303,14 +303,14 @@ LPBYTE ListFile_GetData(void * pvListFile, PDWORD PtrDataSize) DWORD cbData = 0; // Get data from the list file cache - if (pvListFile != NULL) + if(pvListFile != NULL) { pbData = (LPBYTE)pCache->pBegin; cbData = (DWORD)(pCache->pEnd - pCache->pBegin); } // Give the data to the caller - if (PtrDataSize != NULL) + if(PtrDataSize != NULL) PtrDataSize[0] = cbData; return pbData; } diff --git a/dep/CascLib/src/common/Mime.cpp b/dep/CascLib/src/common/Mime.cpp index faa3402d832..5aa7401ff24 100644 --- a/dep/CascLib/src/common/Mime.cpp +++ b/dep/CascLib/src/common/Mime.cpp @@ -37,45 +37,90 @@ static size_t DecodeValueInt32(const char * string, const char * string_end) return result; } -size_t CASC_MIME_HTTP::IsDataComplete(const char * response, size_t response_length, size_t * ptr_content_length) +static const char * GetContentLengthValue(const char * response, const char * end) { - const char * content_length_ptr; - const char * content_begin_ptr; + const char * ptr; - // Do not parse the HTTP response multiple times - if((http_flags & HTTP_HEADER_COMPLETE) == 0 && response_length > 8) + if((ptr = strstr(response, "Content-Length: ")) != NULL && ptr < end) + return ptr; + if((ptr = strstr(response, "content-length: ")) != NULL && ptr < end) + return ptr; + return NULL; +} + +bool CASC_MIME_RESPONSE::ParseResponse(const char * response, size_t length, bool final) +{ + const char * ptr; + + // Only parse the data if there was an increment + if(length > response_length) { - // Check the begin of the response - if(!strncmp(response, "HTTP/1.1", 8)) + // Set the header offset + header_offset = 0; + + // Check for the complete header + if(header_length == CASC_INVALID_SIZE_T) { - // Check if there's begin of the content - if((content_begin_ptr = strstr(response, "\r\n\r\n")) != NULL) + if((ptr = strstr(response, "\r\n\r\n")) != NULL) { - // HTTP responses contain "Content-Length: %u\n\r" - if((content_length_ptr = strstr(response, "Content-Length: ")) == NULL) - content_length_ptr = strstr(response, "content-length: "); - if(content_length_ptr != NULL) - { - // The content length info must be before the actual content - if(content_length_ptr < content_begin_ptr) - { - // Fill the HTTP info cache - content_offset = (content_begin_ptr + 4) - response; - content_length = DecodeValueInt32(content_length_ptr + 16, content_begin_ptr); - total_length = content_offset + content_length; - http_flags = HTTP_HEADER_COMPLETE; - } - } + header_length = (ptr - response) - header_offset; + content_offset = header_length + 4; } } + + // Determine the presence of the HTTP field + if(http_presence == FieldPresenceUnknown && header_length != CASC_INVALID_SIZE_T) + { + const char * http_ptr = (response + header_offset); + + if(!_strnicmp(http_ptr, "HTTP/1.1 ", 9)) + { + http_presence = FieldPresencePresent; + http_code = DecodeValueInt32(response + 9, response + 13); + } + else + { + http_presence = FieldPresenceNotPresent; + } + } + + // Determine the presence of content length + if(clength_presence == FieldPresenceUnknown && header_length != CASC_INVALID_SIZE_T) + { + const char * clength_ptr = GetContentLengthValue(response + header_offset, response + header_length); + + if(clength_ptr != NULL) + { + content_length = DecodeValueInt32(clength_ptr + 16, response + header_length); + clength_presence = FieldPresencePresent; + } + else + { + clength_presence = FieldPresenceNotPresent; + } + } + + // Update the length + response_length = length; + } + + // If this is a final response parsing we calculate the content length + if(content_length == CASC_INVALID_SIZE_T && final == true && (content_offset + 2) < length) + { + // The field must end with \r\n (0D 0A) + const char * end_ptr = (response + length - 2); + + // Is the MIME data terminated with "\r\n"? + if(end_ptr[0] == 0x0D && end_ptr[1] == 0x0A) + { + content_length = (response + length - 2) - (response + content_offset); + } } - // Update flags - if((http_flags & HTTP_HEADER_COMPLETE) && (ptr_content_length != NULL)) - ptr_content_length[0] = content_length; - if(total_length == response_length) - http_flags |= HTTP_DATA_COMPLETE; - return http_flags; + // Determine if we are finished or not + if(header_length != CASC_INVALID_SIZE_T && content_length != CASC_INVALID_SIZE_T) + return (length >= content_offset + content_length); + return false; } //----------------------------------------------------------------------------- @@ -141,33 +186,27 @@ CASC_MIME_ELEMENT::~CASC_MIME_ELEMENT() if(folder.pNext != NULL) delete folder.pNext; folder.pNext = NULL; - - // Free the data - if(data.begin != NULL) - CASC_FREE(data.begin); - data.begin = NULL; } -unsigned char * CASC_MIME_ELEMENT::GiveAway(size_t * ptr_data_length) +DWORD CASC_MIME_ELEMENT::GiveAway(CASC_BLOB & target) { - unsigned char * give_away_data = data.begin; - size_t give_away_length = data.length; - - // Clear the data (DO NOT FREE) - data.begin = NULL; - data.length = 0; + if(data.pbData && data.cbData) + { + target.MoveFrom(data); + return ERROR_SUCCESS; + } + return ERROR_HANDLE_EOF; +} - // Copy the data to local buffer - if(ptr_data_length != NULL) - ptr_data_length[0] = give_away_length; - return give_away_data; +DWORD CASC_MIME_ELEMENT::LoadSingle(char * data_ptr, size_t data_length) +{ + return data.SetData(data_ptr, data_length); } DWORD CASC_MIME_ELEMENT::Load(char * mime_data_begin, char * mime_data_end, const char * boundary_ptr) { CASC_MIME_ENCODING Encoding = MimeEncodingTextPlain; CASC_MIME_BLOB mime_data(mime_data_begin, mime_data_end); - CASC_MIME_HTTP HttpInfo; size_t length_begin; size_t length_end; char * mime_line; @@ -176,18 +215,6 @@ DWORD CASC_MIME_ELEMENT::Load(char * mime_data_begin, char * mime_data_end, cons DWORD dwErrCode = ERROR_SUCCESS; bool mime_version = false; - // Diversion for HTTP: No need to parse the entire headers and stuff. - // Just give the data right away - if(HttpInfo.IsDataComplete(mime_data_begin, (mime_data_end - mime_data_begin))) - { - if((data.begin = CASC_ALLOC<BYTE>(HttpInfo.content_length)) == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - - memcpy(data.begin, mime_data_begin + HttpInfo.content_offset, HttpInfo.content_length); - data.length = HttpInfo.content_length; - return ERROR_SUCCESS; - } - // Reset the boundary boundary[0] = 0; @@ -298,9 +325,7 @@ DWORD CASC_MIME_ELEMENT::Load(char * mime_data_begin, char * mime_data_end, cons else { CASC_MIME_BLOB content(mime_data.ptr, NULL); - unsigned char * data_buffer; - size_t data_length = 0; - size_t raw_length; + CASC_BLOB data_buffer; // If we have boundary pointer, we need to cut the data up to the boundary end. // Otherwise, we decode the data to the end of the document @@ -323,47 +348,26 @@ DWORD CASC_MIME_ELEMENT::Load(char * mime_data_begin, char * mime_data_end, cons return ERROR_BAD_FORMAT; } - // Allocate buffer for decoded data. - // Make it the same size like source data plus zero at the end - raw_length = (content.end - content.ptr); - data_buffer = CASC_ALLOC<unsigned char>(raw_length); - if(data_buffer != NULL) + // Decode the data + switch(Encoding) { - // Decode the data - switch(Encoding) - { - case MimeEncodingTextPlain: - dwErrCode = DecodeTextPlain(content.ptr, content.end, data_buffer, &data_length); - break; + case MimeEncodingTextPlain: + dwErrCode = DecodeTextPlain(content.ptr, content.end, data); + break; - case MimeEncodingQuotedPrintable: - dwErrCode = DecodeQuotedPrintable(content.ptr, content.end, data_buffer, &data_length); - break; + case MimeEncodingQuotedPrintable: + dwErrCode = DecodeQuotedPrintable(content.ptr, content.end, data); + break; - case MimeEncodingBase64: - dwErrCode = DecodeBase64(content.ptr, content.end, data_buffer, &data_length); - break; + case MimeEncodingBase64: + dwErrCode = DecodeBase64(content.ptr, content.end, data); + break; - default:; - // to remove warning - } - - // If failed, free the buffer back - if(dwErrCode != ERROR_SUCCESS) - { - CASC_FREE(data_buffer); - data_buffer = NULL; - data_length = 0; - } - } - else - { - dwErrCode = ERROR_NOT_ENOUGH_MEMORY; + default: + dwErrCode = ERROR_NOT_SUPPORTED; + assert(false); + break; } - - // Put the data there, even if they are invalid - data.begin = data_buffer; - data.length = data_length; } } else @@ -400,15 +404,15 @@ void CASC_MIME_ELEMENT::Print(size_t nLevel, size_t nIndex) { char data_printable[0x20] = {0}; - for(size_t i = 0; (i < data.length && i < _countof(data_printable) - 1); i++) + for(size_t i = 0; (i < data.cbData && i < _countof(data_printable) - 1); i++) { - if(0x20 <= data.begin[i] && data.begin[i] <= 0x7F) - data_printable[i] = data.begin[i]; + if(0x20 <= data.pbData[i] && data.pbData[i] <= 0x7F) + data_printable[i] = data.pbData[i]; else data_printable[i] = '.'; } - printf("Data item (%u bytes): \"%s\"\n", (int)data.length, data_printable); + printf("Data item (%u bytes): \"%s\"\n", (int)data.cbData, data_printable); } // Do we have a next element? @@ -481,127 +485,124 @@ bool CASC_MIME_ELEMENT::ExtractBoundary(const char * line) return false; } -DWORD CASC_MIME_ELEMENT::DecodeTextPlain(char * content_begin, char * content_end, unsigned char * data_buffer, size_t * ptr_length) +DWORD CASC_MIME_ELEMENT::DecodeTextPlain(char * content_begin, char * content_end, CASC_BLOB & output) { - size_t data_length = (size_t)(content_end - content_begin); - - // Sanity checks - assert(content_begin && content_end); - assert(content_end > content_begin); - - // Plain copy - memcpy(data_buffer, content_begin, data_length); - - // Give the result - if(ptr_length != NULL) - ptr_length[0] = data_length; - return ERROR_SUCCESS; + return output.SetData(content_begin, (content_end - content_begin)); } -DWORD CASC_MIME_ELEMENT::DecodeQuotedPrintable(char * content_begin, char * content_end, unsigned char * data_buffer, size_t * ptr_length) +DWORD CASC_MIME_ELEMENT::DecodeQuotedPrintable(char * content_begin, char * content_end, CASC_BLOB & output) { - unsigned char * save_data_buffer = data_buffer; - char * content_ptr; DWORD dwErrCode; // Sanity checks assert(content_begin && content_end); assert(content_end > content_begin); - // Decode the data - for(content_ptr = content_begin; content_ptr < content_end; ) + // Allocate space for the output + if((dwErrCode = output.SetSize(content_end - content_begin)) == ERROR_SUCCESS) { - // If the data begins with '=', there is either newline or 2-char hexa number - if(content_ptr[0] == '=') + unsigned char * output_ptr = output.pbData; + char * content_ptr; + + // Decode the data + for(content_ptr = content_begin; content_ptr < content_end; ) { - // Is there a newline after the equal sign? - if(content_ptr[1] == 0x0D && content_ptr[2] == 0x0A) + // If the data begins with '=', there is either newline or 2-char hexa number + if(content_ptr[0] == '=') { + // Is there a newline after the equal sign? + if(content_ptr[1] == 0x0D && content_ptr[2] == 0x0A) + { + content_ptr += 3; + continue; + } + + // Is there hexa number after the equal sign? + dwErrCode = BinaryFromString(content_ptr + 1, 2, output_ptr); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; + content_ptr += 3; + output_ptr++; continue; } - - // Is there hexa number after the equal sign? - dwErrCode = BinaryFromString(content_ptr + 1, 2, data_buffer); - if(dwErrCode != ERROR_SUCCESS) - return dwErrCode; - - content_ptr += 3; - data_buffer++; - continue; - } - else - { - *data_buffer++ = (unsigned char)(*content_ptr++); + else + { + *output_ptr++ = (unsigned char)(*content_ptr++); + } } - } - if(ptr_length != NULL) - ptr_length[0] = (size_t)(data_buffer - save_data_buffer); - return ERROR_SUCCESS; + // Set the real length + output.cbData = (size_t)(output_ptr - output.pbData); + } + return dwErrCode; } -DWORD CASC_MIME_ELEMENT::DecodeBase64(char * content_begin, char * content_end, unsigned char * data_buffer, size_t * ptr_length) +DWORD CASC_MIME_ELEMENT::DecodeBase64(char * content_begin, char * content_end, CASC_BLOB & output) { - unsigned char * save_data_buffer = data_buffer; + DWORD dwErrCode; DWORD BitBuffer = 0; DWORD BitCount = 0; BYTE OneByte; - // One time preparation of the conversion table - if(CascBase64ToBits[0] == 0) + if((dwErrCode = output.SetSize(content_end - content_begin)) == ERROR_SUCCESS) { - // Fill the entire table with 0xFF to mark invalid characters - memset(CascBase64ToBits, BASE64_INVALID_CHAR, sizeof(CascBase64ToBits)); - - // Set all whitespace characters - for(BYTE i = 1; i <= 0x20; i++) - CascBase64ToBits[i] = BASE64_WHITESPACE_CHAR; + unsigned char * output_ptr = output.pbData; - // Set all valid characters - for(BYTE i = 0; CascBase64Table[i] != 0; i++) + // One time preparation of the conversion table + if(CascBase64ToBits[0] == 0) { - OneByte = CascBase64Table[i]; - CascBase64ToBits[OneByte] = i; - } - } + // Fill the entire table with 0xFF to mark invalid characters + memset(CascBase64ToBits, BASE64_INVALID_CHAR, sizeof(CascBase64ToBits)); - // Do the decoding - while(content_begin < content_end && content_begin[0] != '=') - { - // Check for end of string - if(content_begin[0] > sizeof(CascBase64ToBits)) - return ERROR_BAD_FORMAT; - if((OneByte = CascBase64ToBits[*content_begin++]) == BASE64_INVALID_CHAR) - return ERROR_BAD_FORMAT; - if(OneByte == BASE64_WHITESPACE_CHAR) - continue; + // Set all whitespace characters + for(BYTE i = 1; i <= 0x20; i++) + CascBase64ToBits[i] = BASE64_WHITESPACE_CHAR; - // Put the 6 bits into the bit buffer - BitBuffer = (BitBuffer << 6) | OneByte; - BitCount += 6; + // Set all valid characters + for(BYTE i = 0; CascBase64Table[i] != 0; i++) + { + OneByte = CascBase64Table[i]; + CascBase64ToBits[OneByte] = i; + } + } - // Flush all values - while(BitCount >= 8) + // Do the decoding + while(content_begin < content_end && content_begin[0] != '=') { - // Decrement the bit count in the bit buffer - BitCount -= 8; + // Check for end of string + if(content_begin[0] > sizeof(CascBase64ToBits)) + return ERROR_BAD_FORMAT; + if((OneByte = CascBase64ToBits[*content_begin++]) == BASE64_INVALID_CHAR) + return ERROR_BAD_FORMAT; + if(OneByte == BASE64_WHITESPACE_CHAR) + continue; + + // Put the 6 bits into the bit buffer + BitBuffer = (BitBuffer << 6) | OneByte; + BitCount += 6; - // The byte is the upper 8 bits of the bit buffer - OneByte = (BYTE)(BitBuffer >> BitCount); - BitBuffer = BitBuffer % (1 << BitCount); + // Flush all values + while(BitCount >= 8) + { + // Decrement the bit count in the bit buffer + BitCount -= 8; + + // The byte is the upper 8 bits of the bit buffer + OneByte = (BYTE)(BitBuffer >> BitCount); + BitBuffer = BitBuffer % (1 << BitCount); - // Put the byte value. The buffer can not overflow, - // because it is guaranteed to be equal to the length of the base64 string - *data_buffer++ = OneByte; + // Put the byte value. The buffer can not overflow, + // because it is guaranteed to be equal to the length of the base64 string + *output_ptr++ = OneByte; + } } - } - // Return the decoded length - if(ptr_length != NULL) - ptr_length[0] = (data_buffer - save_data_buffer); - return ERROR_SUCCESS; + // Set the decoded length + output.cbData = (output_ptr - output.pbData); + } + return dwErrCode; } //----------------------------------------------------------------------------- @@ -613,62 +614,57 @@ CASC_MIME::CASC_MIME() CASC_MIME::~CASC_MIME() {} -unsigned char * CASC_MIME::GiveAway(size_t * ptr_data_length) +DWORD CASC_MIME::GiveAway(CASC_BLOB & target) { - CASC_MIME_ELEMENT * pElement = &root; - unsigned char * data; + CASC_MIME_ELEMENT * pElement; // 1) Give the data from the root - if((data = root.GiveAway(ptr_data_length)) != NULL) - return data; + if(root.GiveAway(target) == ERROR_SUCCESS) + return ERROR_SUCCESS; // 2) If we have children, then give away from the first child - pElement = root.GetChild(); - if(pElement && (data = pElement->GiveAway(ptr_data_length)) != NULL) - return data; - - // Return NULL - if(ptr_data_length != NULL) - ptr_data_length[0] = 0; - return NULL; -} + if((pElement = root.GetChild()) != NULL) + return pElement->GiveAway(target); -DWORD CASC_MIME::Load(char * data, size_t length) -{ - // Clear the root element - memset(&root, 0, sizeof(CASC_MIME_ELEMENT)); - - //FILE * fp = fopen("E:\\html_response.txt", "wb"); - //if(fp != NULL) - //{ - // fwrite(data, 1, length, fp); - // fclose(fp); - //} - - // Load the root element - return root.Load(data, data + length); + return ERROR_CAN_NOT_COMPLETE; } -DWORD CASC_MIME::Load(LPCTSTR szFileName) +DWORD CASC_MIME::Load(char * data, CASC_MIME_RESPONSE & MimeResponse) { - char * szFileData; - DWORD cbFileData = 0; - DWORD dwErrCode = ERROR_SUCCESS; + // Avoid parsing empty responses + if(MimeResponse.response_length == 0) + return ERROR_BAD_FORMAT; + if(MimeResponse.header_offset == CASC_INVALID_SIZE_T || MimeResponse.header_length == CASC_INVALID_SIZE_T) + return ERROR_BAD_FORMAT; + if(MimeResponse.content_offset == CASC_INVALID_SIZE_T || MimeResponse.content_offset == 0) + return ERROR_BAD_FORMAT; + if(MimeResponse.content_length == CASC_INVALID_SIZE_T || MimeResponse.content_length == 0) + return ERROR_BAD_FORMAT; + + // Avoid parsing responses where the data are incomplete + // Example: http://level3.blizzard.com/tpr/wow/data/c6/50/c650c203d52b9e5bdcf1d4b2b8b5bd16.index + if(MimeResponse.response_length < (MimeResponse.content_offset + MimeResponse.content_length)) + return ERROR_BAD_FORMAT; + + // Debug: dump the MIME data to file +#ifdef _DEBUG + //CascDumpData("E:\\mime_raw_data.txt", data, MimeResponse.response_length); +#endif - // Note that LoadFileToMemory allocated one byte more and puts zero at the end - // Thus, we can treat it as zero-terminated string - szFileData = (char *)LoadFileToMemory(szFileName, &cbFileData); - if(szFileData != NULL) + // Special handling of HTTP responses + if(MimeResponse.http_presence == FieldPresencePresent) { - dwErrCode = Load(szFileData, cbFileData); - CASC_FREE(szFileData); - } - else - { - dwErrCode = GetCascError(); + // Avoid parsing of failed HTTP requests + if(MimeResponse.http_code != 200) + return ERROR_FILE_NOT_FOUND; + + // Directly setup the root item + return root.LoadSingle(data + MimeResponse.content_offset, MimeResponse.content_length); } - return dwErrCode; + // Load the root element + memset(&root, 0, sizeof(CASC_MIME_ELEMENT)); + return root.Load(data, data + MimeResponse.response_length); } #ifdef _DEBUG diff --git a/dep/CascLib/src/common/Mime.h b/dep/CascLib/src/common/Mime.h index dc7db517179..9142c33bfd3 100644 --- a/dep/CascLib/src/common/Mime.h +++ b/dep/CascLib/src/common/Mime.h @@ -16,7 +16,7 @@ #define MAX_LENGTH_BOUNDARY 128 -// Flags returned by CASC_MIME_HTTP::IsDataComplete() +// Flags returned by CASC_MIME_HTTP::GetHttpReplyFlags() #define HTTP_HEADER_COMPLETE 0x01 // HTML header is complete #define HTTP_DATA_COMPLETE 0x02 // HTML data is complete @@ -28,22 +28,37 @@ enum CASC_MIME_ENCODING MimeEncodingMax }; +enum CASC_PRESENCE +{ + FieldPresenceUnknown, + FieldPresencePresent, + FieldPresenceNotPresent +}; + //----------------------------------------------------------------------------- // Structure for caching parsed HTTP response information -struct CASC_MIME_HTTP +struct CASC_MIME_RESPONSE { - CASC_MIME_HTTP() + CASC_MIME_RESPONSE() { - total_length = content_offset = content_length = http_flags = 0; + header_offset = header_length = CASC_INVALID_SIZE_T; + content_offset = content_length = CASC_INVALID_SIZE_T; + http_code = CASC_INVALID_SIZE_T; + clength_presence = http_presence = FieldPresenceUnknown; + response_length = 0; } - size_t IsDataComplete(const char * response, size_t response_length, size_t * ptr_content_length = NULL); + bool ParseResponse(const char * response, size_t length, bool final = false); - size_t content_length; // Parsed value of "Content-Length" - size_t content_offset; // Offset of the HTTP data, relative to the begin of the response - size_t total_length; // Expected total length of the HTTP response (content_offset + content_size) - size_t http_flags; // Nonzero if this is an already parsed HTTP response + size_t response_length; // Previous length of the response + size_t header_offset; // Offset of the response header, usually 0 + size_t header_length; // Length of the header, if known + size_t content_offset; // Offset of the content + size_t content_length; // Length of the content, if known + size_t http_code; // HTTP code, if present + CASC_PRESENCE clength_presence; // State of the "content length" field + CASC_PRESENCE http_presence; // Presence of the "HTTP" field }; //----------------------------------------------------------------------------- @@ -70,8 +85,9 @@ class CASC_MIME_ELEMENT CASC_MIME_ELEMENT(); ~CASC_MIME_ELEMENT(); - unsigned char * GiveAway(size_t * ptr_data_length); + DWORD GiveAway(CASC_BLOB & target); + DWORD LoadSingle(char * data, size_t data_length); DWORD Load(char * mime_data_begin, char * mime_data_end, const char * boundary_ptr = NULL); CASC_MIME_ELEMENT * GetChild() { return folder.pChild; } @@ -86,9 +102,9 @@ class CASC_MIME_ELEMENT bool ExtractEncoding(const char * line, CASC_MIME_ENCODING & Encoding); bool ExtractBoundary(const char * line); - DWORD DecodeTextPlain(char * content_begin, char * content_end, unsigned char * data_ptr, size_t * ptr_length); - DWORD DecodeQuotedPrintable(char * content_begin, char * content_end, unsigned char * data_ptr, size_t * ptr_length); - DWORD DecodeBase64(char * content_begin, char * content_end, unsigned char * data_ptr, size_t * ptr_length); + DWORD DecodeTextPlain(char * content_begin, char * content_end, CASC_BLOB & output); + DWORD DecodeQuotedPrintable(char * content_begin, char * content_end, CASC_BLOB & output); + DWORD DecodeBase64(char * content_begin, char * content_end, CASC_BLOB & output); struct { @@ -96,12 +112,7 @@ class CASC_MIME_ELEMENT CASC_MIME_ELEMENT * pNext; // Pointer to the next-in-folder element } folder; - struct - { - unsigned char * begin; - size_t length; - } data; - + CASC_BLOB data; char boundary[MAX_LENGTH_BOUNDARY]; }; @@ -112,10 +123,9 @@ class CASC_MIME CASC_MIME(); ~CASC_MIME(); - unsigned char * GiveAway(size_t * ptr_data_length); + DWORD GiveAway(CASC_BLOB & target); - DWORD Load(char * data, size_t length); - DWORD Load(LPCTSTR fileName); + DWORD Load(char * data, CASC_MIME_RESPONSE & MimeResponse); #ifdef _DEBUG void Print(); diff --git a/dep/CascLib/src/common/Path.h b/dep/CascLib/src/common/Path.h index 56489c5f4d9..082ce6ea61c 100644 --- a/dep/CascLib/src/common/Path.h +++ b/dep/CascLib/src/common/Path.h @@ -17,13 +17,20 @@ template <typename xchar> struct CASC_PATH { - CASC_PATH(int chSeparator = PATH_SEP_CHAR) + CASC_PATH(const xchar * szRoot, ...) { - m_szBufferBegin = m_szBufferPtr = m_Buffer; - m_szBufferEnd = m_szBufferBegin + _countof(m_Buffer); - m_chSeparator = (xchar)chSeparator; - m_bLocalCache = 0; - m_Buffer[0] = 0; + va_list argList; + + Initialize(PATH_SEP_CHAR); + + va_start(argList, szRoot); + Create(szRoot, argList); + va_end(argList); + } + + CASC_PATH(xchar chSeparator = PATH_SEP_CHAR) + { + Initialize(chSeparator); } ~CASC_PATH() @@ -34,10 +41,53 @@ struct CASC_PATH } } - // LPCTSTR szPath = Path; - operator const xchar *() const + void Create(const xchar * szRoot, ...) { - return m_szBufferBegin; + va_list argList; + + va_start(argList, szRoot); + Create(szRoot, argList); + va_end(argList); + } + + void Create(const xchar * szRoot, va_list argList) + { + const xchar * szPathPart; + + // Fill-in the root path + SetPathRoot(szRoot); + + // Append all parts until there is NULL + while((szPathPart = va_arg(argList, const xchar *)) != NULL) + { + AppendString(szPathPart, true); + } + } + + bool CutLastPart() + { + xchar * szBufferPtr; + + // Cut ending (back)slashes, if any + while((m_szBufferPtr > m_szBufferBegin) && (m_szBufferPtr[-1] == _T('\\') || m_szBufferPtr[-1] == _T('/'))) + m_szBufferPtr--; + szBufferPtr = m_szBufferPtr - 1; + + // Cut the last path part + while(szBufferPtr > m_szBufferBegin) + { + // End of path? + if(szBufferPtr[0] == _T('\\') || szBufferPtr[0] == _T('/')) + { + m_szBufferPtr = szBufferPtr; + m_szBufferPtr[0] = 0; + return true; + } + + // Go one character back + szBufferPtr--; + } + return false; } void SetLocalCaching(int bLocalCache) @@ -50,11 +100,22 @@ struct CASC_PATH return (m_bLocalCache != 0); } + // LPCTSTR szPath = Path; + operator const xchar * () const + { + return m_szBufferBegin; + } + // LPTSTR szPath = Path.New(); - xchar * New() + xchar * New(bool bCutLastPart = false) { xchar * szNewStr; + if(bCutLastPart) + { + CutLastPart(); + } + if((szNewStr = CASC_ALLOC<xchar>(Length() + 1)) != NULL) { memcpy(szNewStr, m_szBufferBegin, Length() * sizeof(xchar)); @@ -77,12 +138,11 @@ struct CASC_PATH m_szBufferPtr[0] = 0; return true; } - return false; } - // Path.Copy(szBuffer, _countof(szBuffer)); - bool Copy(xchar * szBuffer, size_t cchBuffer) + // Path.CopyTo(szBuffer, _countof(szBuffer)); + bool CopyTo(xchar * szBuffer, size_t cchBuffer) { if((Length() + 1) > cchBuffer) return false; @@ -183,6 +243,15 @@ struct CASC_PATH protected: + void Initialize(xchar chSeparator) + { + m_szBufferBegin = m_szBufferPtr = m_Buffer; + m_szBufferEnd = m_szBufferBegin + _countof(m_Buffer); + m_chSeparator = chSeparator; + m_bLocalCache = 0; + m_Buffer[0] = 0; + } + xchar * m_szBufferBegin; xchar * m_szBufferPtr; xchar * m_szBufferEnd; diff --git a/dep/CascLib/src/common/RootHandler.cpp b/dep/CascLib/src/common/RootHandler.cpp index fa34346b9ce..95fa106e1bd 100644 --- a/dep/CascLib/src/common/RootHandler.cpp +++ b/dep/CascLib/src/common/RootHandler.cpp @@ -87,7 +87,7 @@ PCASC_CKEY_ENTRY TFileTreeRoot::Search(TCascSearch * pSearch, PCASC_FIND_DATA pF if(!(pFileNode->Flags & CFN_FLAG_FOLDER)) { // Check the wildcard - if (CascCheckWildCard(pFindData->szFileName, pSearch->szMask)) + if(CascCheckWildCard(pFindData->szFileName, pSearch->szMask)) { // Retrieve the extra values (FileDataId, file size and locale flags) FileTree.GetExtras(pFileNode, &pFindData->dwFileDataId, &pFindData->dwLocaleFlags, &pFindData->dwContentFlags); diff --git a/dep/CascLib/src/common/Sockets.cpp b/dep/CascLib/src/common/Sockets.cpp index cf3ffe9d0b1..bb490505b15 100644 --- a/dep/CascLib/src/common/Sockets.cpp +++ b/dep/CascLib/src/common/Sockets.cpp @@ -24,21 +24,16 @@ CASC_SOCKET_CACHE SocketCache; // CASC_SOCKET functions // Guarantees that there is zero terminator after the response -char * CASC_SOCKET::ReadResponse(const char * request, size_t request_length, size_t * PtrLength) +char * CASC_SOCKET::ReadResponse(const char * request, size_t request_length, CASC_MIME_RESPONSE & MimeResponse) { - CASC_MIME_HTTP HttpInfo; char * server_response = NULL; - size_t content_length = 0; size_t total_received = 0; size_t buffer_length = BUFFER_INITIAL_SIZE; size_t buffer_delta = BUFFER_INITIAL_SIZE; - size_t http_flags = 0; DWORD dwErrCode = ERROR_SUCCESS; int bytes_received = 0; // Pre-set the result length - if(PtrLength != NULL) - PtrLength[0] = 0; if(request_length == 0) request_length = strlen(request); @@ -59,9 +54,10 @@ char * CASC_SOCKET::ReadResponse(const char * request, size_t request_length, si } // Allocate buffer for server response. Allocate one extra byte for zero terminator - if((server_response = CASC_ALLOC<char>(buffer_length + 1)) != NULL) + if((server_response = CASC_ALLOC_ZERO<char>(buffer_length + 1)) != NULL) { - while((http_flags & HTTP_DATA_COMPLETE) == 0) + // Keep working until the response parser says it's finished + for(;;) { // Reallocate the buffer size, if needed if(total_received == buffer_length) @@ -80,7 +76,10 @@ char * CASC_SOCKET::ReadResponse(const char * request, size_t request_length, si // Return value 0 means "connection closed", -1 means an error bytes_received = recv(sock, server_response + total_received, (int)(buffer_length - total_received), 0); if(bytes_received <= 0) + { + MimeResponse.ParseResponse(server_response, total_received, true); break; + } // Verify buffer overflow if((total_received + bytes_received) < total_received) @@ -93,23 +92,29 @@ char * CASC_SOCKET::ReadResponse(const char * request, size_t request_length, si total_received += bytes_received; server_response[total_received] = 0; - // On a HTTP protocol, we need to check whether we received all data - http_flags = HttpInfo.IsDataComplete(server_response, total_received, &content_length); - if(http_flags & HTTP_HEADER_COMPLETE) + // Parse the MIME response + if(MimeResponse.ParseResponse(server_response, total_received, false)) + break; + + // If we know the content length (HTTP only), we temporarily increment + // the buffer delta. This will make next reallocation to make buffer + // large enough to prevent abundant reallocations and memory memcpy's + if(MimeResponse.clength_presence == FieldPresencePresent && MimeResponse.content_length != CASC_INVALID_SIZE_T) { + // Calculate the final length of the buffer, including the terminating EOLs + size_t content_end = MimeResponse.content_offset + MimeResponse.content_length + 2; + // Check for maximum file size - if(content_length > CASC_MAX_ONLINE_FILE_SIZE) + if(content_end > CASC_MAX_ONLINE_FILE_SIZE) { dwErrCode = ERROR_NOT_ENOUGH_MEMORY; break; } - // If we just retrieved the content length, we temporarily increment - // the buffer delta. This will make next reallocation to make buffer - // large enough to prevent abundant reallocations and memory memcpy's - if(content_length > buffer_length) + // Estimate the total buffer size + if(content_end > buffer_length) { - buffer_delta = (HttpInfo.content_offset + content_length) - buffer_length; + buffer_delta = content_end - buffer_length; } } } @@ -127,8 +132,6 @@ char * CASC_SOCKET::ReadResponse(const char * request, size_t request_length, si } // Give the result to the caller - if(PtrLength != NULL) - PtrLength[0] = total_received; return server_response; } diff --git a/dep/CascLib/src/common/Sockets.h b/dep/CascLib/src/common/Sockets.h index 5d1aa677f85..c821748802b 100644 --- a/dep/CascLib/src/common/Sockets.h +++ b/dep/CascLib/src/common/Sockets.h @@ -48,7 +48,7 @@ class CASC_SOCKET { public: - char * ReadResponse(const char * request, size_t request_length = 0, size_t * PtrLength = NULL); + char * ReadResponse(const char * request, size_t request_length, CASC_MIME_RESPONSE & MimeResponse); DWORD AddRef(); void Release(); diff --git a/dep/CascLib/src/jenkins/lookup3.c b/dep/CascLib/src/jenkins/lookup3.c index 8035dc4655a..cb217e738e2 100644 --- a/dep/CascLib/src/jenkins/lookup3.c +++ b/dep/CascLib/src/jenkins/lookup3.c @@ -69,6 +69,12 @@ on 1 byte), but shoehorning those bytes into integers efficiently is messy. #define hashmask(n) (hashsize(n)-1) #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) +/* Fixup some warnings in MS Visual C++ */ +#ifdef _MSC_VER +#pragma warning(disable: 4127) // warning C4127: conditional expression is constant +#pragma warning(disable: 4101) // warning C4101: 'k8': unreferenced local variable +#endif + /* ------------------------------------------------------------------------------- mix -- mix 3 32-bit values reversibly. diff --git a/dep/PackageList.txt b/dep/PackageList.txt index eef3d00f8a0..393ff763644 100644 --- a/dep/PackageList.txt +++ b/dep/PackageList.txt @@ -63,7 +63,7 @@ catch2 CascLib (An open-source implementation of library for reading CASC storage from Blizzard games since 2014) https://github.com/ladislav-zezula/CascLib - Version: 136c6e05537bd7123620ddb28671d1f2cf060e0b + Version: a5080b5794027a25d98aa6024b2bef17d06fe0ea rapidjson (A fast JSON parser/generator for C++ with both SAX/DOM style API http://rapidjson.org/) https://github.com/Tencent/rapidjson |