This commit is contained in:
Shauren
2023-02-06 20:08:39 +01:00
parent 9932046499
commit fd154940ed
39 changed files with 2116 additions and 1675 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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)

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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];

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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

View 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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View 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__

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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];
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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
);

View File

@@ -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)

View File

@@ -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);

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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.

View File

@@ -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