mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Dep/CascLib: Update to ladislav-zezula/CascLib@a5080b5794
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 index file
|
||||
typedef struct _CASC_INDEX
|
||||
// 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
|
||||
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.
|
||||
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
|
||||
|
||||
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;
|
||||
} 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,21 +27,40 @@ typedef bool (*EKEY_ENTRY_CALLBACK)(TCascStorage * hs, CASC_INDEX_HEADER & InHea
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
// "data.iXY"
|
||||
static bool IsIndexFileName_V1(LPCTSTR szFileName)
|
||||
// Check a file name with mask
|
||||
static bool IsFileNameMask(LPCTSTR szFileName, LPCTSTR szMask)
|
||||
{
|
||||
// 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);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
// For online storages, we need to load CDN servers
|
||||
dwErrCode = LoadCdnsFile(hs);
|
||||
// Copy the name of the build file
|
||||
hs->szMainFile = CascNewStr(szMainFile);
|
||||
|
||||
// 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)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Search the folder and upper folders for the build file
|
||||
else if((dwErrCode = CheckCascBuildFileDirs(BuildFile, pArgs->szLocalPath)) == ERROR_SUCCESS)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
CASC_ARCHIVE_INFO ArchiveInfo = {0};
|
||||
CASC_PATH<TCHAR> LocalPath;
|
||||
CPATH_TYPE PathType = (pCKeyEntry->Flags & CASC_CE_FILE_PATCH) ? PathTypePatch : PathTypeData;
|
||||
|
||||
// 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);
|
||||
// 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];
|
||||
|
||||
|
||||
@@ -64,20 +64,6 @@ typedef struct _DIABLO3_CORE_TOC_ENTRY
|
||||
|
||||
} DIABLO3_CORE_TOC_ENTRY, *PDIABLO3_CORE_TOC_ENTRY;
|
||||
|
||||
// In-memory structure of parsed directory data
|
||||
typedef struct _DIABLO3_DIRECTORY
|
||||
{
|
||||
LPBYTE pbDirectoryData; // The begin of the directory data block
|
||||
LPBYTE pbDirectoryEnd; // The end of the directory data block
|
||||
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
|
||||
DWORD dwAssetEntries; // Number of asset entries without subitem number
|
||||
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
|
||||
{
|
||||
@@ -87,6 +73,26 @@ typedef struct _DIABLO3_ASSET_INFO
|
||||
} DIABLO3_ASSET_INFO;
|
||||
typedef const DIABLO3_ASSET_INFO * PDIABLO3_ASSET_INFO;
|
||||
|
||||
// In-memory structure of parsed directory data
|
||||
struct DIABLO3_DIRECTORY
|
||||
{
|
||||
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
|
||||
DWORD dwAssetEntries; // Number of asset entries without subitem number
|
||||
DWORD dwAssetIdxEntries;
|
||||
DWORD dwNamedEntries;
|
||||
DWORD dwNodeIndex; // Index of file node for this folder
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
286
dep/CascLib/src/common/ArraySparse.h
Normal file
286
dep/CascLib/src/common/ArraySparse.h
Normal file
@@ -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__
|
||||
@@ -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,44 +293,56 @@ 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
|
||||
|
||||
return (buffend - buffer);
|
||||
}
|
||||
|
||||
size_t CascStrPrintf(char * buffer, size_t nCount, const char * 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;
|
||||
}
|
||||
|
||||
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);
|
||||
#else
|
||||
buffend = buffer + vswprintf(buffer, nCount, format, argList);
|
||||
#endif
|
||||
|
||||
return (buffend - buffer);
|
||||
}
|
||||
|
||||
size_t CascStrPrintf(wchar_t * buffer, size_t nCount, const wchar_t * format, ...)
|
||||
{
|
||||
wchar_t * buffend;
|
||||
va_list argList;
|
||||
size_t length;
|
||||
|
||||
// Start the argument list
|
||||
va_start(argList, format);
|
||||
|
||||
#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
|
||||
length = CascStrPrintfV(buffer, nCount, format, argList);
|
||||
va_end(argList);
|
||||
return (buffend - buffer);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
~CASC_BLOB()
|
||||
{
|
||||
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(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;
|
||||
}
|
||||
|
||||
~QUERY_KEY()
|
||||
void Free()
|
||||
{
|
||||
CASC_FREE(pbData);
|
||||
if(pbData != NULL)
|
||||
CASC_FREE(pbData);
|
||||
pbData = NULL;
|
||||
cbData = 0;
|
||||
}
|
||||
|
||||
DWORD SetData(const void * pv, size_t cb)
|
||||
LPBYTE End() const
|
||||
{
|
||||
if((pbData = CASC_ALLOC<BYTE>(cb)) == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
memcpy(pbData, pv, cb);
|
||||
cbData = cb;
|
||||
return ERROR_SUCCESS;
|
||||
return pbData + cbData;
|
||||
}
|
||||
|
||||
LPBYTE pbData;
|
||||
size_t cbData;
|
||||
};
|
||||
typedef QUERY_KEY *PQUERY_KEY;
|
||||
typedef CASC_BLOB *PCASC_BLOB;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File name utilities
|
||||
@@ -526,21 +570,10 @@ 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
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
// Let the callback scan the file name
|
||||
pfnOnFileFound(wf.cFileName, pvContext);
|
||||
if(PfnFolderCallback != NULL)
|
||||
{
|
||||
if(!PfnFolderCallback(wf.cFileName, pvContext))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
pfnOnFileFound(dir_entry->d_name, pvContext);
|
||||
if(PfnFolderCallback != NULL)
|
||||
{
|
||||
if(!PfnFolderCallback(dir_entry->d_name, pvContext))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
int ScanIndexDirectory(
|
||||
LPCTSTR szIndexPath,
|
||||
INDEX_FILE_FOUND pfnOnFileFound,
|
||||
bool MakeDirectory(
|
||||
LPCTSTR szDirectory
|
||||
);
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
if(data.pbData && data.cbData)
|
||||
{
|
||||
target.MoveFrom(data);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
return ERROR_HANDLE_EOF;
|
||||
}
|
||||
|
||||
// Clear the data (DO NOT FREE)
|
||||
data.begin = NULL;
|
||||
data.length = 0;
|
||||
|
||||
// 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;
|
||||
}
|
||||
default:
|
||||
dwErrCode = ERROR_NOT_SUPPORTED;
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// 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++);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the real length
|
||||
output.cbData = (size_t)(output_ptr - output.pbData);
|
||||
}
|
||||
|
||||
if(ptr_length != NULL)
|
||||
ptr_length[0] = (size_t)(data_buffer - save_data_buffer);
|
||||
return ERROR_SUCCESS;
|
||||
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));
|
||||
unsigned char * output_ptr = output.pbData;
|
||||
|
||||
// Set all whitespace characters
|
||||
for(BYTE i = 1; i <= 0x20; i++)
|
||||
CascBase64ToBits[i] = BASE64_WHITESPACE_CHAR;
|
||||
|
||||
// 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));
|
||||
|
||||
// Set all whitespace characters
|
||||
for(BYTE i = 1; i <= 0x20; i++)
|
||||
CascBase64ToBits[i] = BASE64_WHITESPACE_CHAR;
|
||||
|
||||
// Set all valid characters
|
||||
for(BYTE i = 0; CascBase64Table[i] != 0; i++)
|
||||
{
|
||||
OneByte = CascBase64Table[i];
|
||||
CascBase64ToBits[OneByte] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// Put the 6 bits into the bit buffer
|
||||
BitBuffer = (BitBuffer << 6) | OneByte;
|
||||
BitCount += 6;
|
||||
|
||||
// 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;
|
||||
|
||||
// The byte is the upper 8 bits of the bit buffer
|
||||
OneByte = (BYTE)(BitBuffer >> BitCount);
|
||||
BitBuffer = BitBuffer % (1 << BitCount);
|
||||
// Put the 6 bits into the bit buffer
|
||||
BitBuffer = (BitBuffer << 6) | OneByte;
|
||||
BitCount += 6;
|
||||
|
||||
// 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;
|
||||
// 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
|
||||
*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;
|
||||
if((pElement = root.GetChild()) != NULL)
|
||||
return pElement->GiveAway(target);
|
||||
|
||||
// Return NULL
|
||||
if(ptr_data_length != NULL)
|
||||
ptr_data_length[0] = 0;
|
||||
return NULL;
|
||||
return ERROR_CAN_NOT_COMPLETE;
|
||||
}
|
||||
|
||||
DWORD CASC_MIME::Load(char * data, size_t length)
|
||||
DWORD CASC_MIME::Load(char * data, CASC_MIME_RESPONSE & MimeResponse)
|
||||
{
|
||||
// Clear the root element
|
||||
memset(&root, 0, sizeof(CASC_MIME_ELEMENT));
|
||||
// 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;
|
||||
|
||||
//FILE * fp = fopen("E:\\html_response.txt", "wb");
|
||||
//if(fp != NULL)
|
||||
//{
|
||||
// fwrite(data, 1, length, fp);
|
||||
// fclose(fp);
|
||||
//}
|
||||
// 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
|
||||
|
||||
// Special handling of HTTP responses
|
||||
if(MimeResponse.http_presence == FieldPresencePresent)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Load the root element
|
||||
return root.Load(data, data + length);
|
||||
}
|
||||
|
||||
DWORD CASC_MIME::Load(LPCTSTR szFileName)
|
||||
{
|
||||
char * szFileData;
|
||||
DWORD cbFileData = 0;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// 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)
|
||||
{
|
||||
dwErrCode = Load(szFileData, cbFileData);
|
||||
CASC_FREE(szFileData);
|
||||
}
|
||||
else
|
||||
{
|
||||
dwErrCode = GetCascError();
|
||||
}
|
||||
|
||||
return dwErrCode;
|
||||
memset(&root, 0, sizeof(CASC_MIME_ELEMENT));
|
||||
return root.Load(data, data + MimeResponse.response_length);
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user