aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dep/CascLib/CMakeLists.txt1
-rw-r--r--dep/CascLib/src/CascCommon.h123
-rw-r--r--dep/CascLib/src/CascDecompress.cpp2
-rw-r--r--dep/CascLib/src/CascDecrypt.cpp6
-rw-r--r--dep/CascLib/src/CascDumpData.cpp16
-rw-r--r--dep/CascLib/src/CascFiles.cpp1109
-rw-r--r--dep/CascLib/src/CascIndexFiles.cpp243
-rw-r--r--dep/CascLib/src/CascLib.h21
-rw-r--r--dep/CascLib/src/CascOpenFile.cpp4
-rw-r--r--dep/CascLib/src/CascOpenStorage.cpp268
-rw-r--r--dep/CascLib/src/CascPort.h2
-rw-r--r--dep/CascLib/src/CascReadFile.cpp44
-rw-r--r--dep/CascLib/src/CascRootFile_Diablo3.cpp151
-rw-r--r--dep/CascLib/src/CascRootFile_Install.cpp18
-rw-r--r--dep/CascLib/src/CascRootFile_MNDX.cpp217
-rw-r--r--dep/CascLib/src/CascRootFile_OW.cpp10
-rw-r--r--dep/CascLib/src/CascRootFile_TVFS.cpp64
-rw-r--r--dep/CascLib/src/CascRootFile_Text.cpp12
-rw-r--r--dep/CascLib/src/CascRootFile_WoW.cpp13
-rw-r--r--dep/CascLib/src/common/Array.h33
-rw-r--r--dep/CascLib/src/common/ArraySparse.h286
-rw-r--r--dep/CascLib/src/common/Common.cpp127
-rw-r--r--dep/CascLib/src/common/Common.h87
-rw-r--r--dep/CascLib/src/common/Csv.cpp37
-rw-r--r--dep/CascLib/src/common/Csv.h2
-rw-r--r--dep/CascLib/src/common/Directory.cpp62
-rw-r--r--dep/CascLib/src/common/Directory.h18
-rw-r--r--dep/CascLib/src/common/FileStream.cpp32
-rw-r--r--dep/CascLib/src/common/FileTree.cpp13
-rw-r--r--dep/CascLib/src/common/FileTree.h10
-rw-r--r--dep/CascLib/src/common/ListFile.cpp4
-rw-r--r--dep/CascLib/src/common/Mime.cpp454
-rw-r--r--dep/CascLib/src/common/Mime.h54
-rw-r--r--dep/CascLib/src/common/Path.h95
-rw-r--r--dep/CascLib/src/common/RootHandler.cpp2
-rw-r--r--dep/CascLib/src/common/Sockets.cpp41
-rw-r--r--dep/CascLib/src/common/Sockets.h2
-rw-r--r--dep/CascLib/src/jenkins/lookup3.c6
-rw-r--r--dep/PackageList.txt2
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