This commit is contained in:
Shauren
2019-08-10 19:01:24 +02:00
parent 0d6320dfd3
commit cd720efbfa
37 changed files with 3325 additions and 2303 deletions

View File

@@ -9,8 +9,10 @@ set(HEADER_FILES
src/common/Directory.h
src/common/FileStream.h
src/common/FileTree.h
src/common/IndexMap.h
src/common/ListFile.h
src/common/Map.h
src/common/Path.h
src/common/RootHandler.h
src/jenkins/lookup.h
)
@@ -24,7 +26,6 @@ set(SRC_FILES
src/common/ListFile.cpp
src/common/RootHandler.cpp
src/jenkins/lookup3.c
src/CascCommon.cpp
src/CascDecompress.cpp
src/CascDecrypt.cpp
src/CascDumpData.cpp

View File

@@ -1,144 +0,0 @@
/*****************************************************************************/
/* CascCommon.cpp Copyright (c) Ladislav Zezula 2014 */
/*---------------------------------------------------------------------------*/
/* Common functions for CascLib */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 29.04.14 1.00 Lad The first version of CascCommon.cpp */
/*****************************************************************************/
#define __CASCLIB_SELF__
#include "CascLib.h"
#include "CascCommon.h"
//-----------------------------------------------------------------------------
// Functions
LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD * pcbFileData)
{
LPBYTE pbFileData = NULL;
HANDLE hFile = NULL;
DWORD cbFileData = pcbFileData[0];
DWORD dwBytesRead = 0;
int nError = ERROR_SUCCESS;
// Open the file either by CKey or by EKey
if(OpenFileByCKeyEntry(hs, pCKeyEntry, CASC_STRICT_DATA_CHECK, &hFile))
{
// Retrieve the size of the file. Note that the caller might specify
// the real size of the file, in case the file size is not retrievable
// or if the size is wrong. Example: ENCODING file has size specified in BUILD
if(cbFileData == 0 || cbFileData == CASC_INVALID_SIZE)
{
cbFileData = CascGetFileSize(hFile, NULL);
if(cbFileData == 0 || cbFileData == CASC_INVALID_SIZE)
nError = ERROR_FILE_CORRUPT;
}
// Retrieve the size of the ENCODING file
if(nError == ERROR_SUCCESS)
{
// Allocate space for the ENCODING file
pbFileData = CASC_ALLOC(BYTE, cbFileData);
if(pbFileData != NULL)
{
// Read the entire file to memory
CascReadFile(hFile, pbFileData, cbFileData, &dwBytesRead);
if(dwBytesRead != cbFileData)
{
nError = ERROR_FILE_CORRUPT;
}
}
else
{
nError = ERROR_NOT_ENOUGH_MEMORY;
}
}
// Close the file
CascCloseFile(hFile);
}
else
{
nError = GetLastError();
}
// Handle errors
if(nError != ERROR_SUCCESS)
{
// Free the file data
CASC_FREE(pbFileData);
cbFileData = 0;
// Set the last error
SetLastError(nError);
}
// Give the loaded file length
if(pcbFileData != NULL)
*pcbFileData = cbFileData;
return pbFileData;
}
LPBYTE LoadFileToMemory(const TCHAR * szFileName, DWORD * pcbFileData)
{
TFileStream * pStream;
ULONGLONG FileSize = 0;
LPBYTE pbFileData = NULL;
DWORD cbFileData = 0;
// Open the stream for read-only access and read the file
// Note that this fails when the game is running (sharing violation).
pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
if(pStream != NULL)
{
// Retrieve the file size
FileStream_GetSize(pStream, &FileSize);
cbFileData = (DWORD)FileSize;
// Do not load zero files or too larget files
if(0 < FileSize && FileSize <= 0x2000000)
{
// Allocate file data buffer. Make it 1 byte longer
// so string functions can put '\0' there
pbFileData = CASC_ALLOC(BYTE, cbFileData + 1);
if(pbFileData != NULL)
{
if(!FileStream_Read(pStream, NULL, pbFileData, cbFileData))
{
CASC_FREE(pbFileData);
cbFileData = 0;
}
}
else
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
cbFileData = 0;
}
}
else
{
SetLastError(ERROR_BAD_FORMAT);
cbFileData = 0;
assert(false);
}
// Close the file stream
FileStream_Close(pStream);
}
// Give out values
if(pcbFileData != NULL)
pcbFileData[0] = cbFileData;
return pbFileData;
}
void FreeCascBlob(PQUERY_KEY pBlob)
{
if(pBlob != NULL)
{
CASC_FREE(pBlob->pbData);
pBlob->cbData = 0;
}
}

View File

@@ -30,6 +30,7 @@
#include "common/Directory.h"
#include "common/ListFile.h"
#include "common/Csv.h"
#include "common/Path.h"
#include "common/RootHandler.h"
// Headers from Alexander Peslyak's MD5 implementation
@@ -52,6 +53,14 @@
#define BREAKIF(condition) { /* NOTHING */ }
#endif
#define CASC_MAGIC_STORAGE 0x524F545343534143 // 'CASCSTOR'
#define CASC_MAGIC_FILE 0x454C494643534143 // 'CASCFILE'
#define CASC_MAGIC_FIND 0x444E494643534143 // 'CASCFIND'
// For CASC_CDN_DOWNLOAD::Flags
#define CASC_CDN_FLAG_PORT1119 0x0001 // Use port 1119
#define CASC_CDN_FORCE_DOWNLOAD 0x0002 // Force downloading the file even if in the cache
//-----------------------------------------------------------------------------
// In-memory structures
@@ -63,6 +72,13 @@ typedef enum _CBLD_TYPE
CascVersionsDb // versions (downloaded online)
} CBLD_TYPE, *PCBLD_TYPE;
typedef enum _CSTRTG
{
CascCacheInvalid, // Do not cache anything. Used as invalid value
CascCacheNothing, // Do not cache anything. Used on internal files, where the content is loaded directly to the user buffer
CascCacheLastFrame, // Only cache one file frame
} CSTRTG, *PCSTRTG;
// Tag file entry, loaded from the DOWNLOAD file
typedef struct _CASC_TAG_ENTRY
{
@@ -97,7 +113,7 @@ typedef struct _CASC_ARCINDEX_FOOTER
BYTE Version; // Version of the index footer
BYTE OffsetBytes; // Length, in bytes, of the file offset field
BYTE SizeBytes; // Length, in bytes, of the file size field
BYTE EKeyBytes; // Length, in bytes, of the EKey field
BYTE EKeyLength; // Length, in bytes, of the EKey field
BYTE FooterHashBytes; // Length, in bytes, of the hash and checksum
BYTE Alignment[3];
size_t ElementCount; // total number of elements in the index file
@@ -107,15 +123,6 @@ typedef struct _CASC_ARCINDEX_FOOTER
} CASC_ARCINDEX_FOOTER, *PCASC_ARCINDEX_FOOTER;
// Normalized structure for archive index entry
typedef struct _CASC_ARCINDEX_ENTRY
{
BYTE IndexHash[MD5_HASH_SIZE];
BYTE EKey[MD5_HASH_SIZE];
DWORD ArchiveOffset;
DWORD EncodedSize;
} CASC_ARCINDEX_ENTRY, *PCASC_ARCINDEX_ENTRY;
// Normalized header of the ENCODING file
typedef struct _CASC_ENCODING_HEADER
{
@@ -188,12 +195,51 @@ typedef struct _CASC_INSTALL_HEADER
typedef struct _CASC_FILE_FRAME
{
CONTENT_KEY FrameHash; // MD5 hash of the file frame
ULONGLONG StartOffset; // Starting offset of the file span
ULONGLONG EndOffset; // Ending offset of the file span
DWORD DataFileOffset; // Offset in the data file (data.###)
DWORD FileOffset; // File offset of this frame
DWORD EncodedSize; // Encoded size of the frame
DWORD ContentSize; // Content size of the frame
} CASC_FILE_FRAME, *PCASC_FILE_FRAME;
typedef struct _CASC_FILE_SPAN
{
ULONGLONG StartOffset; // Starting offset of the file span
ULONGLONG EndOffset; // Ending offset of the file span
PCASC_FILE_FRAME pFrames;
TFileStream * pStream; // [Opened] stream for the file span
DWORD ArchiveIndex; // Index of the archive
DWORD ArchiveOffs; // Offset in the archive
DWORD HeaderSize; // Size of encoded frame headers
DWORD FrameCount; // Number of frames in this 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
{
ULONGLONG ArchiveOffs; // Archive offset (if pbArchiveKey != NULL)
LPCTSTR szCdnsHost; // Address of the remote CDN server. ("level3.blizzard.com")
// If NULL, the downloader will try all CDN servers from the storage
LPCTSTR szCdnsPath; // Remote CDN path ("tpr/bnt001")
LPCTSTR szPathType; // Path type ("config", "data", "patch")
LPCTSTR szLoPaType; // Local path type ("config", "data", "patch"). If NULL, it's gonna be the same like szPathType, If "", then it's not used
LPCTSTR szFileName; // Plain file name, without path and extension
LPBYTE pbArchiveKey; // If non-NULL, then the file is present in the archive.
LPBYTE pbEKey; // 16-byte EKey of the file of of the archive
LPCTSTR szExtension; // Extension for the file. Can be NULL.
LPTSTR szLocalPath; // Pointer to the variable that, upon success, reveives the local path where the file was downloaded
size_t ccLocalPath; // Maximum length of szLocalPath, in TCHARs
DWORD ArchiveIndex; // Index of the archive (if pbArchiveKey != NULL)
DWORD EncodedSize; // Encoded length (if pbArchiveKey != NULL)
DWORD Flags; // See CASC_CDN_FLAG_XXX
} CASC_CDN_DOWNLOAD, *PCASC_CDN_DOWNLOAD;
//-----------------------------------------------------------------------------
// Structures for CASC storage and CASC file
@@ -201,16 +247,26 @@ struct TCascStorage
{
TCascStorage();
~TCascStorage();
TCascStorage * AddRef();
TCascStorage * Release();
static TCascStorage * IsValid(HANDLE hStorage);
static TCascStorage * IsValid(HANDLE hStorage)
{
TCascStorage * hs = (TCascStorage *)hStorage;
return (hs != INVALID_HANDLE_VALUE &&
hs != NULL &&
hs->ClassName == CASC_MAGIC_STORAGE) ? hs : NULL;
}
// Class recognizer. Has constant value of 'CASCSTOR' (CASC_MAGIC_STORAGE)
ULONGLONG ClassName;
// Class members
PCASC_OPEN_STORAGE_ARGS pArgs; // Open storage arguments. Only valid during opening the storage
LPCTSTR szIndexFormat; // Format of the index file name
LPCSTR szClassName; // "TCascStorage"
LPCSTR szProductName; // String representation of the product name
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
@@ -219,12 +275,10 @@ struct TCascStorage
LPTSTR szCdnServers; // Multi-SZ list of CDN servers
LPTSTR szCdnPath; // Remote CDN sub path for the product
LPSTR szRegion; // Product region. Only when "versions" is used as storage root file
CASC_PRODUCT Product; // Product enum value (see CASC_PRODUCT)
DWORD dwDefaultLocale; // Default locale, read from ".build.info"
DWORD dwBuildNumber; // Product build number
DWORD dwRefCount; // Number of references
DWORD dwDefaultLocale; // Default locale, read from ".build.info"
DWORD dwFeatures; // List of CASC features. See CASC_FEATURE_XXX
bool bAllowOrphans; // If TRUE, then orphaned items are allowed
CBLD_TYPE BuildFileType; // Type of the build file
@@ -249,20 +303,17 @@ struct TCascStorage
CASC_ARRAY VfsRootList; // List of CASC_EKEY_ENTRY for each TVFS sub-root
TRootHandler * pRootHandler; // Common handler for various ROOT file formats
CASC_ARRAY ArcIndexArray; // Array of CASC_ARCINDEX_ENTRY, loaded from archive indexes
CASC_ARRAY IndexArray; // Array of CASC_CKEY_ENTRY loaded from local index files
CASC_ARRAY CKeyArray; // Array of CASC_CKEY_ENTRY, one entry for each physical file
CASC_ARRAY IndexArray; // Array of CASC_EKEY_ENTRY, loaded from online indexes
CASC_ARRAY CKeyArray; // Array of CASC_CKEY_ENTRY, loaded from ENCODING file
CASC_ARRAY TagsArray; // Array of CASC_DOWNLOAD_TAG2
CASC_MAP ArcIndexMap; // Map of EKey -> CASC_ARCINDEX_ENTRY
CASC_MAP CKeyMap; // Map of CKey -> CASC_CKEY_ENTRY
CASC_MAP EKeyMap; // Map of EKey -> CASC_EKEY_ENTRY
CASC_MAP IndexMap; // Map of EKey -> IndexArray (for online archives)
CASC_MAP CKeyMap; // Map of CKey -> CKeyArray
CASC_MAP EKeyMap; // Map of EKey -> CKeyArray
size_t LocalFiles; // Number of files that are present locally
size_t TotalFiles; // Total number of files in the storage, some may not be present locally
size_t EKeyEntries; // Number of CKeyEntry-ies loaded from text build file
size_t OrphanItems; // Number of EKey entries in indexes that do not have their counterpart in ENCODING
size_t SkippedItems; // Number of EKey entries in indexes that were ignored due to insufficient capacity of CKeyArray
size_t EKeyLength; // EKey length from the index files
DWORD FileOffsetBits; // Nimber of bits in the storage offset which mean data segent offset
DWORD FileOffsetBits; // Number of bits in the storage offset which mean data segent offset
CASC_ARRAY ExtraKeysList; // List additional encryption keys
CASC_MAP EncryptionKeys; // Map of encryption keys
@@ -271,49 +322,12 @@ struct TCascStorage
struct TCascFile
{
TCascFile(TCascStorage * ahs, PCASC_CKEY_ENTRY apCKeyEntry)
{
ULONGLONG StorageOffset;
ULONGLONG FileOffsMask;
TCascFile(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry);
~TCascFile();
// Reference the storage handle
hs = ahs->AddRef();
szClassName = "TCascFile";
// Init class variables
pCKeyEntry = apCKeyEntry;
pFrames = NULL;
pbFileCache = NULL;
cbFileCache = 0;
// Supply the sizes
StorageOffset = pCKeyEntry->StorageOffset;
FileOffsMask = ((ULONGLONG)1 << hs->FileOffsetBits) - 1;
ArchiveIndex = (DWORD)(StorageOffset >> hs->FileOffsetBits);
ArchiveOffset = (DWORD)(StorageOffset & FileOffsMask);
EncodedSize = pCKeyEntry->EncodedSize;
ContentSize = pCKeyEntry->ContentSize;
FilePointer = 0;
FrameCount = 0;
bVerifyIntegrity = false;
bDownloadFileIf = false;
bLocalFileStream = false;
}
~TCascFile()
{
// Close (dereference) the archive handle
hs = hs->Release();
szClassName = NULL;
// Free the file cache and frame array
CASC_FREE(pbFileCache);
CASC_FREE(pFrames);
// If we are supposed to close the file stream, do it
if (pStream && bLocalFileStream)
FileStream_Close(pStream);
}
DWORD OpenFileSpans(LPCTSTR szSpanList);
void InitFileSpans(PCASC_FILE_SPAN pSpans, DWORD dwSpanCount);
void InitCacheStrategy();
static TCascFile * IsValid(HANDLE hFile)
{
@@ -321,31 +335,32 @@ struct TCascFile
return (hf != INVALID_HANDLE_VALUE &&
hf != NULL &&
hf->hs != NULL &&
hf->szClassName != NULL &&
hf->ClassName == CASC_MAGIC_FILE &&
hf->pCKeyEntry != NULL) ? hf : NULL;
}
TCascStorage * hs; // Pointer to storage structure
TFileStream * pStream; // An open data stream
const char * szClassName; // "TCascFile"
// Class recognizer. Has constant value of 'CASCFILE' (CASC_MAGIC_FILE)
ULONGLONG ClassName;
PCASC_CKEY_ENTRY pCKeyEntry;
PCASC_FILE_FRAME pFrames; // Array of file frames
DWORD ArchiveIndex; // Index of the archive (data.###)
DWORD ArchiveOffset; // Offset in the archive (data.###)
DWORD FilePointer; // Current file pointer
DWORD EncodedSize; // Encoded size. This is the size of encoded header, all file frame headers and all file frames
DWORD ContentSize; // Content size. This is the size of the file content, aka the file size
DWORD FrameCount; // Number of the file frames
// Class members
TCascStorage * hs; // Pointer to storage structure
PCASC_CKEY_ENTRY pCKeyEntry; // Pointer to the first CKey entry. Each entry describes one file span
PCASC_FILE_SPAN pFileSpan; // Pointer to the first file span entry
ULONGLONG ContentSize; // Content size. This is the summed content size of all file spans
ULONGLONG EncodedSize; // Encoded size. This is the summed encoded size of all file spans
ULONGLONG FilePointer; // Current file pointer
DWORD SpanCount; // Number of file spans. There is one CKey entry for each file span
DWORD bVerifyIntegrity:1; // If true, then the data are validated more strictly when read
DWORD bDownloadFileIf:1; // If true, then the data will be downloaded from the online storage if missing
DWORD bLocalFileStream:1; // If true, then the file stream is a local file
DWORD bCloseFileStream:1; // If true, file stream needs to be closed during CascCloseFile
DWORD bOvercomeEncrypted:1; // If true, then CascReadFile will fill the part that is encrypted (and key was not found) with zeros
DWORD bFreeCKeyEntries:1; // If true, dectructor will free the array of CKey entries
LPBYTE pbFileCache; // Pointer to file cache
DWORD cbFileCache; // Size of the file cache
ULONGLONG FileCacheStart; // Starting offset of the file cached area
ULONGLONG FileCacheEnd; // Ending offset of the file cached area
LPBYTE pbFileCache; // Pointer to file cached area
CSTRTG CacheStrategy; // Caching strategy. See CSTRTG enum for more info
};
struct TCascSearch
@@ -353,7 +368,7 @@ struct TCascSearch
TCascSearch(TCascStorage * ahs, LPCTSTR aszListFile, const char * aszMask)
{
// Init the class
szClassName = "TCascSearch";
ClassName = CASC_MAGIC_FIND;
hs = ahs->AddRef();
// Init provider-specific data
@@ -371,7 +386,7 @@ struct TCascSearch
{
// Dereference the CASC storage
hs = hs->Release();
szClassName = NULL;
ClassName = 0;
// Free the rest of the members
CASC_FREE(szMask);
@@ -385,14 +400,14 @@ struct TCascSearch
return (hFind != INVALID_HANDLE_VALUE &&
hFind != NULL &&
pSearch->szClassName != NULL &&
!strcmp(pSearch->szClassName, "TCascSearch") &&
pSearch->ClassName == CASC_MAGIC_FIND &&
pSearch->szMask != NULL) ? pSearch : NULL;
}
ULONGLONG ClassName; // Contains 'CASCFIND'
TCascStorage * hs; // Pointer to the storage handle
const char * szClassName; // Contains "TCascSearch"
TCHAR * szListFile; // Name of the listfile
LPTSTR szListFile; // Name of the listfile
void * pCache; // Listfile cache
char * szMask; // Search mask
@@ -405,22 +420,32 @@ struct TCascSearch
//-----------------------------------------------------------------------------
// Common functions (CascCommon.cpp)
bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD dwOpenFlags, HANDLE * PtrFileHandle);
LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD * pcbFileData);
LPBYTE LoadFileToMemory(const TCHAR * szFileName, DWORD * pcbFileData);
void FreeCascBlob(PQUERY_KEY pQueryKey);
inline void FreeCascBlob(PQUERY_KEY pBlob)
{
if(pBlob != NULL)
{
CASC_FREE(pBlob->pbData);
pBlob->cbData = 0;
}
}
//-----------------------------------------------------------------------------
// Text file parsing (CascFiles.cpp)
bool InvokeProgressCallback(TCascStorage * hs, LPCSTR szMessage, LPCSTR szObject, DWORD CurrentValue, DWORD TotalValue);
DWORD DownloadFileFromCDN(TCascStorage * hs, const TCHAR * szSubDir, LPBYTE pbEKey, const TCHAR * szExtension, TCHAR * szOutLocalPath, size_t cchOutLocalPath);
DWORD CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory);
DWORD LoadCdnsInfo(TCascStorage * hs);
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 LoadCdnConfigFile(TCascStorage * hs);
DWORD LoadCdnBuildFile(TCascStorage * hs);
LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD * pcbFileData);
LPBYTE LoadFileToMemory(LPCTSTR szFileName, DWORD * pcbFileData);
bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD dwOpenFlags, HANDLE * PtrFileHandle);
bool SetCacheStrategy(HANDLE hFile, CSTRTG CacheStrategy);
//-----------------------------------------------------------------------------
// Internal file functions
@@ -431,27 +456,27 @@ PCASC_CKEY_ENTRY FindCKeyEntry_EKey(TCascStorage * hs, LPBYTE pbEKey, PDWORD Ptr
size_t GetTagBitmapLength(LPBYTE pbFilePtr, LPBYTE pbFileEnd, DWORD EntryCount);
int CascDecompress(LPBYTE pvOutBuffer, PDWORD pcbOutBuffer, LPBYTE pvInBuffer, DWORD cbInBuffer);
int CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer);
DWORD CascDecompress(LPBYTE pvOutBuffer, PDWORD pcbOutBuffer, LPBYTE pvInBuffer, DWORD cbInBuffer);
DWORD CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer);
int CascLoadEncryptionKeys(TCascStorage * hs);
int CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex);
DWORD CascLoadEncryptionKeys(TCascStorage * hs);
DWORD CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex);
//-----------------------------------------------------------------------------
// Support for index files
int LoadIndexFiles(TCascStorage * hs);
DWORD LoadIndexFiles(TCascStorage * hs);
//-----------------------------------------------------------------------------
// Support for ROOT file
int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
int RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
int RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask);
int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
int RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
int RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
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);
//-----------------------------------------------------------------------------
// Dumpers (CascDumpData.cpp)

View File

@@ -15,12 +15,12 @@
//-----------------------------------------------------------------------------
// Public functions
int CascDecompress(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer)
DWORD CascDecompress(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer)
{
z_stream z; // Stream information for zlib
DWORD dwErrCode = ERROR_FILE_CORRUPT;
uInt cbOutBuffer = *pcbOutBuffer;
int nResult;
int nError = ERROR_FILE_CORRUPT;
// Fill the stream structure for zlib
z.next_in = pbInBuffer;
@@ -44,7 +44,7 @@ int CascDecompress(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, D
{
// Give the size of the uncompressed data
cbOutBuffer = z.total_out;
nError = ERROR_SUCCESS;
dwErrCode = ERROR_SUCCESS;
}
inflateEnd(&z);
@@ -52,5 +52,5 @@ int CascDecompress(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, D
// Give the caller the number of bytes needed
pcbOutBuffer[0] = cbOutBuffer;
return nError;
return dwErrCode;
}

View File

@@ -16,16 +16,17 @@
// Local structures
#define CASC_EXTRA_KEYS 0x80
#define CASC_KEY_LENGTH 0x10
typedef struct _CASC_ENCRYPTION_KEY
{
ULONGLONG KeyName; // "Name" of the key
BYTE Key[0x10]; // The key itself
BYTE Key[CASC_KEY_LENGTH]; // The key itself
} CASC_ENCRYPTION_KEY, *PCASC_ENCRYPTION_KEY;
typedef struct _CASC_SALSA20
{
DWORD Key[0x10];
DWORD Key[CASC_KEY_LENGTH];
DWORD dwRounds;
} CASC_SALSA20, *PCASC_SALSA20;
@@ -57,15 +58,15 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
{ 0xDEE3A0521EFF6F03ULL, { 0xAD, 0x74, 0x0C, 0xE3, 0xFF, 0xFF, 0x92, 0x31, 0x46, 0x81, 0x26, 0x98, 0x57, 0x08, 0xE1, 0xB9 } }, // 0.8.0.24919_retailx64 (streamed from server)
{ 0x8C9106108AA84F07ULL, { 0x53, 0xD8, 0x59, 0xDD, 0xA2, 0x63, 0x5A, 0x38, 0xDC, 0x32, 0xE7, 0x2B, 0x11, 0xB3, 0x2F, 0x29 } }, // 0.8.0.24919_retailx64 (streamed from server)
{ 0x49166D358A34D815ULL, { 0x66, 0x78, 0x68, 0xCD, 0x94, 0xEA, 0x01, 0x35, 0xB9, 0xB1, 0x6C, 0x93, 0xB1, 0x12, 0x4A, 0xBA } }, // 0.8.0.24919_retailx64 (streamed from server)
{ 0x1463A87356778D14ULL, { 0x69, 0xBD, 0x2A, 0x78, 0xD0, 0x5C, 0x50, 0x3E, 0x93, 0x99, 0x49, 0x59, 0xB3, 0x0E, 0x5A, 0xEC } }, // ? 1.0.3.0 (streamed from server)
{ 0x5E152DE44DFBEE01ULL, { 0xE4, 0x5A, 0x17, 0x93, 0xB3, 0x7E, 0xE3, 0x1A, 0x8E, 0xB8, 0x5C, 0xEE, 0x0E, 0xEE, 0x1B, 0x68 } }, // ? 1.0.3.0 (streamed from server)
{ 0x1463A87356778D14ULL, { 0x69, 0xBD, 0x2A, 0x78, 0xD0, 0x5C, 0x50, 0x3E, 0x93, 0x99, 0x49, 0x59, 0xB3, 0x0E, 0x5A, 0xEC } }, // ? 1.0.3.0 (streamed from server)
{ 0x5E152DE44DFBEE01ULL, { 0xE4, 0x5A, 0x17, 0x93, 0xB3, 0x7E, 0xE3, 0x1A, 0x8E, 0xB8, 0x5C, 0xEE, 0x0E, 0xEE, 0x1B, 0x68 } }, // ? 1.0.3.0 (streamed from server)
{ 0x9B1F39EE592CA415ULL, { 0x54, 0xA9, 0x9F, 0x08, 0x1C, 0xAD, 0x0D, 0x08, 0xF7, 0xE3, 0x36, 0xF4, 0x36, 0x8E, 0x89, 0x4C } }, // ? 1.0.3.0 (streamed from server)
{ 0x24C8B75890AD5917ULL, { 0x31, 0x10, 0x0C, 0x00, 0xFD, 0xE0, 0xCE, 0x18, 0xBB, 0xB3, 0x3F, 0x3A, 0xC1, 0x5B, 0x30, 0x9F } }, // ? 1.0.3.0 (included in game)
{ 0xEA658B75FDD4890FULL, { 0xDE, 0xC7, 0xA4, 0xE7, 0x21, 0xF4, 0x25, 0xD1, 0x33, 0x03, 0x98, 0x95, 0xC3, 0x60, 0x36, 0xF8 } }, // ? 1.0.3.0 (included in game)
{ 0x026FDCDF8C5C7105ULL, { 0x8F, 0x41, 0x80, 0x9D, 0xA5, 0x53, 0x66, 0xAD, 0x41, 0x6D, 0x3C, 0x33, 0x74, 0x59, 0xEE, 0xE3 } }, // (included in game)
{ 0xCAE3FAC925F20402ULL, { 0x98, 0xB7, 0x8E, 0x87, 0x74, 0xBF, 0x27, 0x50, 0x93, 0xCB, 0x1B, 0x5F, 0xC7, 0x14, 0x51, 0x1B } }, // (included in game)
{ 0x061581CA8496C80CULL, { 0xDA, 0x2E, 0xF5, 0x05, 0x2D, 0xB9, 0x17, 0x38, 0x0B, 0x8A, 0xA6, 0xEF, 0x7A, 0x5F, 0x8E, 0x6A } }, //
{ 0xBE2CB0FAD3698123ULL, { 0x90, 0x2A, 0x12, 0x85, 0x83, 0x6C, 0xE6, 0xDA, 0x58, 0x95, 0x02, 0x0D, 0xD6, 0x03, 0xB0, 0x65 } }, //
{ 0x061581CA8496C80CULL, { 0xDA, 0x2E, 0xF5, 0x05, 0x2D, 0xB9, 0x17, 0x38, 0x0B, 0x8A, 0xA6, 0xEF, 0x7A, 0x5F, 0x8E, 0x6A } }, //
{ 0xBE2CB0FAD3698123ULL, { 0x90, 0x2A, 0x12, 0x85, 0x83, 0x6C, 0xE6, 0xDA, 0x58, 0x95, 0x02, 0x0D, 0xD6, 0x03, 0xB0, 0x65 } }, //
{ 0x57A5A33B226B8E0AULL, { 0xFD, 0xFC, 0x35, 0xC9, 0x9B, 0x9D, 0xB1, 0x1A, 0x32, 0x62, 0x60, 0xCA, 0x24, 0x6A, 0xCB, 0x41 } }, // 1.1.0.0.30200 Ana
{ 0x42B9AB1AF5015920ULL, { 0xC6, 0x87, 0x78, 0x82, 0x3C, 0x96, 0x4C, 0x6F, 0x24, 0x7A, 0xCC, 0x0F, 0x4A, 0x25, 0x84, 0xF8 } }, // 1.2.0.1.30684 Summer Games
{ 0x4F0FE18E9FA1AC1AULL, { 0x89, 0x38, 0x1C, 0x74, 0x8F, 0x65, 0x31, 0xBB, 0xFC, 0xD9, 0x77, 0x53, 0xD0, 0x6C, 0xC3, 0xCD } }, // 1.2.0.1.30684
@@ -78,7 +79,7 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
{ 0x90CA73B2CDE3164BULL, { 0x5C, 0xBF, 0xF1, 0x1F, 0x22, 0x72, 0x0B, 0xAC, 0xC2, 0xAE, 0x6A, 0xAD, 0x8F, 0xE5, 0x33, 0x17 } }, // 1.6.1.0.33236 Oasis map
{ 0x6DD3212FB942714AULL, { 0xE0, 0x2C, 0x16, 0x43, 0x60, 0x2E, 0xC1, 0x6C, 0x3A, 0xE2, 0xA4, 0xD2, 0x54, 0xA0, 0x8F, 0xD9 } }, // 1.6.1.0.33236
{ 0x11DDB470ABCBA130ULL, { 0x66, 0x19, 0x87, 0x66, 0xB1, 0xC4, 0xAF, 0x75, 0x89, 0xEF, 0xD1, 0x3A, 0xD4, 0xDD, 0x66, 0x7A } }, // 1.6.1.0.33236 Winter Wonderland
{ 0x5BEF27EEE95E0B4BULL, { 0x36, 0xBC, 0xD2, 0xB5, 0x51, 0xFF, 0x1C, 0x84, 0xAA, 0x3A, 0x39, 0x94, 0xCC, 0xEB, 0x03, 0x3E } }, //
{ 0x5BEF27EEE95E0B4BULL, { 0x36, 0xBC, 0xD2, 0xB5, 0x51, 0xFF, 0x1C, 0x84, 0xAA, 0x3A, 0x39, 0x94, 0xCC, 0xEB, 0x03, 0x3E } }, //
{ 0x9359B46E49D2DA42ULL, { 0x17, 0x3D, 0x65, 0xE7, 0xFC, 0xAE, 0x29, 0x8A, 0x93, 0x63, 0xBD, 0x6A, 0xA1, 0x89, 0xF2, 0x00 } }, // Diablo's 20th anniversary
{ 0x1A46302EF8896F34ULL, { 0x80, 0x29, 0xAD, 0x54, 0x51, 0xD4, 0xBC, 0x18, 0xE9, 0xD0, 0xF5, 0xAC, 0x44, 0x9D, 0xC0, 0x55 } }, // 1.7.0.2.34156 Year of the Rooster
{ 0x693529F7D40A064CULL, { 0xCE, 0x54, 0x87, 0x3C, 0x62, 0xDA, 0xA4, 0x8E, 0xFF, 0x27, 0xFC, 0xC0, 0x32, 0xBD, 0x07, 0xE3 } }, // 1.8.0.0.34470 CTF Maps
@@ -86,8 +87,8 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
{ 0xE218F69AAC6C104DULL, { 0xF4, 0x3D, 0x12, 0xC9, 0x4A, 0x9A, 0x52, 0x84, 0x97, 0x97, 0x1F, 0x1C, 0xBE, 0x41, 0xAD, 0x4D } }, // 1.9.0.0.34986 Orisa
{ 0xF432F0425363F250ULL, { 0xBA, 0x69, 0xF2, 0xB3, 0x3C, 0x27, 0x68, 0xF5, 0xF2, 0x9B, 0xFE, 0x78, 0xA5, 0xA1, 0xFA, 0xD5 } }, // 1.10.0.0.35455 Uprising
{ 0x061D52F86830B35DULL, { 0xD7, 0x79, 0xF9, 0xC6, 0xCC, 0x9A, 0x4B, 0xE1, 0x03, 0xA4, 0xE9, 0x0A, 0x73, 0x38, 0xF7, 0x93 } }, // 1.10.0.0.35455 D.Va Officer Skin (HotS Nexus Challenge 2)
{ 0x1275C84CF113EF65ULL, { 0xCF, 0x58, 0xB6, 0x93, 0x3E, 0xAF, 0x98, 0xAF, 0x53, 0xE7, 0x6F, 0x84, 0x26, 0xCC, 0x7E, 0x6C } }, //
{ 0xD9C7C7AC0F14C868ULL, { 0x3A, 0xFD, 0xF6, 0x8E, 0x3A, 0x5D, 0x63, 0xBA, 0xBA, 0x1E, 0x68, 0x21, 0x88, 0x3F, 0x06, 0x7D } }, //
{ 0x1275C84CF113EF65ULL, { 0xCF, 0x58, 0xB6, 0x93, 0x3E, 0xAF, 0x98, 0xAF, 0x53, 0xE7, 0x6F, 0x84, 0x26, 0xCC, 0x7E, 0x6C } }, //
{ 0xD9C7C7AC0F14C868ULL, { 0x3A, 0xFD, 0xF6, 0x8E, 0x3A, 0x5D, 0x63, 0xBA, 0xBA, 0x1E, 0x68, 0x21, 0x88, 0x3F, 0x06, 0x7D } }, //
{ 0xBD4E42661A432951ULL, { 0x6D, 0xE8, 0xE2, 0x8C, 0x85, 0x11, 0x64, 0x4D, 0x55, 0x95, 0xFC, 0x45, 0xE5, 0x35, 0x14, 0x72 } }, // 1.11.0.0.36376 Anniversary event
{ 0xC43CB14355249451ULL, { 0x0E, 0xA2, 0xB4, 0x4F, 0x96, 0xA2, 0x69, 0xA3, 0x86, 0x85, 0x6D, 0x04, 0x9A, 0x3D, 0xEC, 0x86 } }, // 1.12.0.0.37104 Horizon Lunar Colony
{ 0xE6D914F8E4744953ULL, { 0xC8, 0x47, 0x7C, 0x28, 0x9D, 0xCE, 0x66, 0xD9, 0x13, 0x65, 0x07, 0xA3, 0x3A, 0xA3, 0x33, 0x01 } }, // 1.13.0.0.37646 Doomfist
@@ -132,12 +133,12 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
{ 0xFEBBF66DAEF6C9BEULL, { 0x4A, 0x22, 0x0A, 0xE3, 0xA6, 0x80, 0x8E, 0xD2, 0x69, 0x74, 0x10, 0xC9, 0x4C, 0x1C, 0xE9, 0x70 } }, // 1.30.0.1.????? Ashe
{ 0x2AD4834DAB3986ABULL, { 0xEA, 0x69, 0x71, 0xF7, 0x8A, 0xBE, 0xBD, 0x2A, 0xF8, 0x83, 0xD4, 0x6A, 0x54, 0x86, 0xF3, 0x9F } }, // 1.31.?.?.????? Winter 2018
{ 0xD89B24D62F00A04EULL, { 0xC3, 0xA4, 0xD0, 0x10, 0xAA, 0xA7, 0x28, 0x7A, 0x3E, 0xAC, 0xCD, 0x45, 0xED, 0x47, 0x13, 0x45 } }, // 1.31.?.?.????? Bastet Challenge
{ 0x32DDC40236DAEA7BULL, { 0x2D, 0xBD, 0xE4, 0xFB, 0x9F, 0xDA, 0x77, 0x6A, 0xA2, 0x94, 0x87, 0x08, 0x54, 0xFE, 0x9B, 0x02 } }, // 1.31.?.?.????? Lunar New Year 2019 (Pig)
{ 0xF481EFC2302EE415ULL, { 0x37, 0xA7, 0xF2, 0xB8, 0x7D, 0x0B, 0x8B, 0x70, 0x04, 0x56, 0xC6, 0xA9, 0x2C, 0x71, 0xD6, 0xDD } }, // 1.33.?.?.????? Paris Map
{ 0xD1AC8C1903524D9AULL, { 0xD7, 0x81, 0xD0, 0xAA, 0x35, 0xE5, 0xC1, 0x06, 0xBC, 0xA7, 0xCF, 0x01, 0xDE, 0xBD, 0x14, 0x94 } }, // 1.34.0.1.55918 Baptiste
{ 0x32DDC40236DAEA7BULL, { 0x2D, 0xBD, 0xE4, 0xFB, 0x9F, 0xDA, 0x77, 0x6A, 0xA2, 0x94, 0x87, 0x08, 0x54, 0xFE, 0x9B, 0x02 } }, // 1.31.?.?.????? Lunar New Year 2019 (Pig)
{ 0xF481EFC2302EE415ULL, { 0x37, 0xA7, 0xF2, 0xB8, 0x7D, 0x0B, 0x8B, 0x70, 0x04, 0x56, 0xC6, 0xA9, 0x2C, 0x71, 0xD6, 0xDD } }, // 1.33.?.?.????? Paris Map
{ 0xD1AC8C1903524D9AULL, { 0xD7, 0x81, 0xD0, 0xAA, 0x35, 0xE5, 0xC1, 0x06, 0xBC, 0xA7, 0xCF, 0x01, 0xDE, 0xBD, 0x14, 0x94 } }, // 1.34.0.1.55918 Baptiste
{ 0x71EEBE93590AA903ULL, { 0x0C, 0xCD, 0x10, 0xD4, 0x55, 0x3E, 0xEC, 0x7E, 0x97, 0xFD, 0x36, 0xA9, 0xE8, 0xAD, 0xF0, 0xFF } }, // 1.34.0.1.55918 Baptiste Cosmetics
// Streamed WoW keys
// Streamed WoW keys
{ 0xFA505078126ACB3EULL, { 0xBD, 0xC5, 0x18, 0x62, 0xAB, 0xED, 0x79, 0xB2, 0xDE, 0x48, 0xC8, 0xE7, 0xE6, 0x6C, 0x62, 0x00 } }, // 15 WOW-20740patch7.0.1_Beta <not used between 7.0 and 7.3>
{ 0xFF813F7D062AC0BCULL, { 0xAA, 0x0B, 0x5C, 0x77, 0xF0, 0x88, 0xCC, 0xC2, 0xD3, 0x90, 0x49, 0xBD, 0x26, 0x7F, 0x06, 0x6D } }, // 25 WOW-20740patch7.0.1_Beta <not used between 7.0 and 7.3>
{ 0xD1E9B5EDF9283668ULL, { 0x8E, 0x4A, 0x25, 0x79, 0x89, 0x4E, 0x38, 0xB4, 0xAB, 0x90, 0x58, 0xBA, 0x5C, 0x73, 0x28, 0xEE } }, // 39 WOW-20740patch7.0.1_Beta Enchanted Torch pet
@@ -217,7 +218,7 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
{ 0xE2F6BD41298A2AB9ULL, { 0xC5, 0xDC, 0x1B, 0xB4, 0x3B, 0x8C, 0xF3, 0xF0, 0x85, 0xD6, 0x98, 0x68, 0x26, 0xB9, 0x28, 0xEC } }, // 241 WOW-28151patch8.1.0_PTR Horde fireworks
{ 0x14C4257E557B49A1ULL, { 0x06, 0x4A, 0x97, 0x09, 0xF4, 0x2D, 0x50, 0xCB, 0x5F, 0x8B, 0x94, 0xBC, 0x1A, 0xCF, 0xDD, 0x5D } }, // 242 WOW-28440patch8.1.0_PTR dor cinematic
{ 0x1254E65319C6EEFFULL, { 0x79, 0xD2, 0xB3, 0xD1, 0xCC, 0xB0, 0x15, 0x47, 0x4E, 0x71, 0x58, 0x81, 0x38, 0x64, 0xB8, 0xE6 } }, // 243 WOW-28440patch8.1.0_PTR akt cinematic
// { 0xC8753773ADF1174CULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 244 WOW-28938patch8.1.5_PTR starts at fdid 2615771, total of 15 fdids
{ 0xC8753773ADF1174CULL, { 0x1E, 0x0E, 0x37, 0xD4, 0x2E, 0xE5, 0xCE, 0x5E, 0x80, 0x67, 0xF0, 0x39, 0x4B, 0x09, 0x05, 0xF2 } }, // 244 WOW-28938patch8.1.5_PTR starts at fdid 2615771, total of 15 fdids
// { 0x2170BCAA9FA96E22ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 245 WOW-28938patch8.1.5_PTR alpaca mount
// { 0x75485627AA225F4DULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 246 WOW-28938patch8.1.5_PTR fdid 2741546, 2741548, 2741549
{ 0x08717B15BF3C7955ULL, { 0x4B, 0x06, 0xBF, 0x9D, 0x17, 0x66, 0x3C, 0xEB, 0x33, 0x12, 0xEA, 0x3C, 0x69, 0xFB, 0xC5, 0xDD } }, // 248 WOW-29220patch8.1.5_PTR inv_encrypted20.blp (fdid 2823166)
@@ -225,13 +226,23 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
{ 0x9FD609902B4B2E07ULL, { 0xAB, 0xE0, 0xC5, 0xF9, 0xC1, 0x23, 0xE6, 0xE2, 0x4E, 0x7B, 0xEA, 0x43, 0xC2, 0xBF, 0x00, 0xAC } }, // 250 WOW-29418patch8.1.5_PTR Derek Proudmoore cinematic (dpr, 5 files)
// { 0xCB26B441FAE4C8CDULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 251 WOW-30080patch8.2.0_PTR fdid 2888623, 2892270, 2892271, 2892272, 2892274, 2892275
// { 0xA98C7594F55C02F0ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 252 WOW-30080patch8.2.0_PTR starts at fdid 2921871, total of 24 fdids
// { 0x259EE68CD9E76DBAULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 253 WOW-30080patch8.2.0_PTR starts at fdid 2957406, total of 30 fdids
{ 0x259EE68CD9E76DBAULL, { 0x46, 0x5D, 0x78, 0x4F, 0x10, 0x19, 0x66, 0x1C, 0xCF, 0x41, 0x7F, 0xE4, 0x66, 0x80, 0x12, 0x83 } }, // 253 WOW-30080patch8.2.0_PTR starts at fdid 2957406, total of 30 fdids
// { 0x6A026290FBDB3754ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 255 WOW-30080patch8.2.0_PTR starts at fdid 2976294, total of 201 fdids
{ 0xCF72FD04608D36EDULL, { 0xA0, 0xA8, 0x89, 0x97, 0x6D, 0x02, 0xFA, 0x8D, 0x00, 0xF7, 0xAF, 0x00, 0x17, 0xAD, 0x72, 0x1F } }, // 257 WOW-30262patch8.2.0_PTR Azshara Warbringer cinematic (5 files)
// { 0x17F07C2E3A45DB3DULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 258 WOW-30262patch8.2.0_PTR starts at fdid 982457, total of 17 fdids
{ 0x17F07C2E3A45DB3DULL, { 0x6D, 0x38, 0x86, 0xBD, 0xB9, 0x1E, 0x71, 0x5A, 0xE7, 0x18, 0x2D, 0x9F, 0x3A, 0x08, 0xF2, 0xC9 } }, // 258 WOW-30262patch8.2.0_PTR Solesa Naksu Nazjatar phase (17 files)
// { 0xDFAB5841B87802B5ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 259 WOW-31337patch8.2.5_PTR starts at fdid 3016206, total of 8 fdids
{ 0xC050FA06BB0538F6ULL, { 0xC5, 0x52, 0xF5, 0xD0, 0xB7, 0x22, 0x31, 0x50, 0x2D, 0x25, 0x47, 0x31, 0x4E, 0x60, 0x15, 0xF7 } }, // 260 WOW-30495patch8.2.0_PTR Crossroads cinematic (5 files)
// { 0xAB5CDD3FC321831FULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 261 WOW-30495patch8.2.0_PTR Unknown cinematic fdid 3022943-3022947 (5 files)
{ 0xAB5CDD3FC321831FULL, { 0xE1, 0x38, 0x4F, 0x5B, 0x06, 0xEB, 0xBC, 0xD3, 0x33, 0x69, 0x5A, 0xA6, 0xFF, 0xC6, 0x83, 0x18 } }, // 261 WOW-30495patch8.2.0_PTR Azshara kill cinematic (5 files)
{ 0xA7B7D1F12395040EULL, { 0x36, 0xAD, 0x3B, 0x31, 0x27, 0x3F, 0x1E, 0xBC, 0xEE, 0x85, 0x20, 0xAA, 0xA7, 0x4B, 0x12, 0xF2 } }, // 262 WOW-30495patch8.2.0_PTR Nazjatar intro cinematics (9 files)
// { 0x83A2AB72DD8AE992ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 263 WOW-31337patch8.2.5_PTR starts at fdid 801707, total of 372 fdids
// { 0xBEAF567CC45362F0ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 264 WOW-31337patch8.2.5_PTR starts at fdid 1002186, total of 71 fdids
// { 0x7BB3A77FD8D14783ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 265 WOW-31337patch8.2.5_PTR fdid 1251882, 1251883
// { 0x8F4098E2470FE0C8ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 266 WOW-31337patch8.2.5_PTR starts at fdid 841604, total of 58 fdids
// { 0x6AC5C837A2027A6BULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 267 WOW-31337patch8.2.5_PTR starts at fdid 3037834, total of 263 fdids
// { 0x302AAD8B1F441D95ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 271 WOW-31337patch8.2.5_PTR starts at fdid 1376212, total of 300 fdids
// { 0x5C909F00088734B9ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 272 WOW-31337patch8.2.5_PTR
// { 0xF785977C76DE9C77ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 273 WOW-31337patch8.2.5_PTR starts at fdid 3071600, total of 295 fdids
// { 0x1CDAF3931871BEC3ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 275 WOW-31337patch8.2.5_PTR fdid 988200, 1140079, 1263818, 1347275
};
//-----------------------------------------------------------------------------
@@ -374,27 +385,27 @@ static LPBYTE CascFindKey(TCascStorage * hs, ULONGLONG KeyName)
//-----------------------------------------------------------------------------
// Public functions
int CascLoadEncryptionKeys(TCascStorage * hs)
DWORD CascLoadEncryptionKeys(TCascStorage * hs)
{
size_t nKeyCount = (sizeof(CascKeys) / sizeof(CASC_ENCRYPTION_KEY));
size_t nMaxItems = nKeyCount + CASC_EXTRA_KEYS;
int nError;
DWORD dwErrCode;
// Create fast map of KeyName -> Key
nError = hs->EncryptionKeys.Create(nMaxItems, sizeof(ULONGLONG), FIELD_OFFSET(CASC_ENCRYPTION_KEY, KeyName));
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = hs->EncryptionKeys.Create(nMaxItems, sizeof(ULONGLONG), FIELD_OFFSET(CASC_ENCRYPTION_KEY, KeyName));
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Insert all static keys
for (size_t i = 0; i < nKeyCount; i++)
hs->EncryptionKeys.InsertObject(&CascKeys[i], &CascKeys[i].KeyName);
// Create array for extra keys
nError = hs->ExtraKeysList.Create<CASC_ENCRYPTION_KEY>(CASC_EXTRA_KEYS);
return nError;
dwErrCode = hs->ExtraKeysList.Create<CASC_ENCRYPTION_KEY>(CASC_EXTRA_KEYS);
return dwErrCode;
}
int CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex)
DWORD CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex)
{
ULONGLONG KeyName = 0;
LPBYTE pbBufferEnd = pbInBuffer + cbInBuffer;
@@ -404,7 +415,7 @@ int CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBY
DWORD IVSize;
BYTE Vector[0x08];
BYTE EncryptionType;
int nError;
DWORD dwErrCode;
// Verify and retrieve the key name size
if(pbInBuffer >= pbBufferEnd)
@@ -462,15 +473,15 @@ int CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBY
switch(EncryptionType)
{
case 'S': // Salsa20
nError = Decrypt_Salsa20(pbOutBuffer, pbInBuffer, (pbBufferEnd - pbInBuffer), pbKey, 0x10, Vector);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = Decrypt_Salsa20(pbOutBuffer, pbInBuffer, (pbBufferEnd - pbInBuffer), pbKey, 0x10, Vector);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Supply the size of the output buffer
pcbOutBuffer[0] = (DWORD)(pbBufferEnd - pbInBuffer);
return ERROR_SUCCESS;
// case 'A':
// case 'A':
// return ERROR_NOT_SUPPORTED;
}
@@ -478,7 +489,7 @@ int CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBY
return ERROR_NOT_SUPPORTED;
}
int CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer)
DWORD CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer)
{
// Check the buffer size
if((cbInBuffer - 1) > pcbOutBuffer[0])
@@ -526,7 +537,34 @@ bool WINAPI CascAddEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPBYTE Key)
pEncKey->KeyName = KeyName;
// Also insert the key to the map
return hs->EncryptionKeys.InsertObject(pEncKey, &pEncKey->KeyName);
if (!hs->EncryptionKeys.InsertObject(pEncKey, &pEncKey->KeyName))
{
SetLastError(ERROR_ALREADY_EXISTS);
return false;
}
return true;
}
bool WINAPI CascAddStringEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPCSTR szKey)
{
BYTE Key[CASC_KEY_LENGTH];
// Check the length of the string key
if(strlen(szKey) != CASC_KEY_LENGTH * 2)
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
// Convert the string key to the binary array
if(ConvertStringToBinary(szKey, CASC_KEY_LENGTH * 2, Key) != ERROR_SUCCESS)
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
return CascAddEncryptionKey(hStorage, KeyName, Key);
}
LPBYTE WINAPI CascFindEncryptionKey(HANDLE hStorage, ULONGLONG KeyName)

File diff suppressed because it is too large Load Diff

View File

@@ -24,49 +24,52 @@ static void ResetFindData(PCASC_FIND_DATA pFindData)
pFindData->szFileName[0] = 0;
pFindData->szPlainName = pFindData->szFileName;
pFindData->TagBitMask = 0;
pFindData->FileSize = CASC_INVALID_SIZE64;
pFindData->dwFileDataId = CASC_INVALID_ID;
pFindData->dwFileSize = CASC_INVALID_SIZE;
pFindData->dwLocaleFlags = CASC_INVALID_ID;
pFindData->dwContentFlags = CASC_INVALID_ID;
pFindData->dwSpanCount = 1;
pFindData->NameType = CascNameFull;
pFindData->bFileAvailable = false;
pFindData->bCanOpenByName = false;
pFindData->bCanOpenByDataId = false;
pFindData->bCanOpenByCKey = false;
pFindData->bCanOpenByEKey = false;
}
static void SupplyFakeFileName(PCASC_FIND_DATA pFindData)
static void SupplyFakeFileName(PCASC_FIND_DATA pFindData, PCASC_CKEY_ENTRY pCKeyEntry)
{
// If the file can be open by file data id, create fake file name
if(pFindData->bCanOpenByDataId)
// If there is a file data ID, create fake file name
if(pFindData->dwFileDataId != CASC_INVALID_ID)
{
CascStrPrintf(pFindData->szFileName, _countof(pFindData->szFileName), "FILE%08X.dat", pFindData->dwFileDataId);
pFindData->NameType = CascNameDataId;
return;
}
// If the file can be open by CKey, convert the CKey to file name
if(pFindData->bCanOpenByCKey)
// If there is a CKey, convert the CKey to file name
if(pCKeyEntry->Flags & CASC_CE_HAS_CKEY)
{
StringFromBinary(pFindData->CKey, MD5_HASH_SIZE, pFindData->szFileName);
pFindData->NameType = CascNameCKey;
return;
}
// CKey should be always present
StringFromBinary(pFindData->EKey, MD5_HASH_SIZE, pFindData->szFileName);
pFindData->NameType = CascNameEKey;
assert(pFindData->bCanOpenByEKey != false);
// EKey should be always present
if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY)
{
StringFromBinary(pFindData->EKey, MD5_HASH_SIZE, pFindData->szFileName);
pFindData->NameType = CascNameEKey;
return;
}
assert(false);
}
static bool CopyCKeyEntryToFindData(PCASC_FIND_DATA pFindData, PCASC_CKEY_ENTRY pCKeyEntry)
{
ULONGLONG ContentSize = 0;
ULONGLONG EncodedSize = 0;
// Supply both keys
CopyMemory16(pFindData->CKey, pCKeyEntry->CKey);
CopyMemory16(pFindData->EKey, pCKeyEntry->EKey);
pFindData->bCanOpenByCKey = (pCKeyEntry->Flags & CASC_CE_HAS_CKEY) ? true : false;
pFindData->bCanOpenByEKey = (pCKeyEntry->Flags & CASC_CE_HAS_EKEY) ? true : false;
// Supply the tag mask
pFindData->TagBitMask = pCKeyEntry->TagBitMask;
@@ -77,15 +80,21 @@ static bool CopyCKeyEntryToFindData(PCASC_FIND_DATA pFindData, PCASC_CKEY_ENTRY
// If we retrieved the file size directly from the root provider, use it
// Otherwise, supply EncodedSize or ContentSize, whichever is available (but ContentSize > EncodedSize)
if(pFindData->dwFileSize == CASC_INVALID_SIZE)
pFindData->dwFileSize = pCKeyEntry->ContentSize;
pFindData->dwSpanCount = GetFileSpanInfo(pCKeyEntry, &ContentSize, &EncodedSize);
if(pFindData->FileSize == CASC_INVALID_SIZE64)
{
if(ContentSize != CASC_INVALID_SIZE64)
pFindData->FileSize = ContentSize;
else
pFindData->FileSize = EncodedSize;
}
// Set flag indicating that the file is locally available
pFindData->bFileAvailable = (pCKeyEntry->Flags & CASC_CE_FILE_IS_LOCAL);
// Supply a fake file name, if there is none supplied by the root handler
if(pFindData->szFileName[0] == 0)
SupplyFakeFileName(pFindData);
SupplyFakeFileName(pFindData, pCKeyEntry);
return true;
}
@@ -127,9 +136,12 @@ static bool DoStorageSearch_CKey(TCascSearch * pSearch, PCASC_FIND_DATA pFindDat
// Check for CKeys that haven't been found yet
while(pSearch->nFileIndex < nTotalItems)
{
// Locate the n-th CKey entry. If this entry is not referenced by the root handler, we include it in the search result
// Locate the n-th CKey entry.
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.ItemAt(pSearch->nFileIndex++);
if((pCKeyEntry->Flags & CASC_CE_FOLDER_ENTRY) == 0 && pCKeyEntry->RefCount == 0)
// BREAK_ON_XKEY3(pCKeyEntry->CKey, 0x2B, 0xfc, 0xe4);
// Only report files that are unreferenced by the ROOT handler
if(pCKeyEntry->IsFile() && pCKeyEntry->RefCount == 0)
{
return CopyCKeyEntryToFindData(pFindData, pCKeyEntry);
}
@@ -189,31 +201,31 @@ HANDLE WINAPI CascFindFirstFile(
{
TCascStorage * hs;
TCascSearch * pSearch = NULL;
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
// Check parameters
if((hs = TCascStorage::IsValid(hStorage)) == NULL)
nError = ERROR_INVALID_HANDLE;
dwErrCode = ERROR_INVALID_HANDLE;
if(szMask == NULL || pFindData == NULL)
nError = ERROR_INVALID_PARAMETER;
dwErrCode = ERROR_INVALID_PARAMETER;
// Init the search structure and search handle
if(nError == ERROR_SUCCESS)
if(dwErrCode == ERROR_SUCCESS)
{
// Allocate the search handle
pSearch = new TCascSearch(hs, szListFile, szMask);
if(pSearch == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
// Perform search
if(nError == ERROR_SUCCESS)
if(dwErrCode == ERROR_SUCCESS)
{
if(!DoStorageSearch(pSearch, pFindData))
nError = ERROR_NO_MORE_FILES;
dwErrCode = ERROR_NO_MORE_FILES;
}
if(nError != ERROR_SUCCESS)
if(dwErrCode != ERROR_SUCCESS)
{
delete pSearch;
pSearch = (TCascSearch *)INVALID_HANDLE_VALUE;

View File

@@ -129,6 +129,17 @@ static TCHAR * CreateIndexFileName(TCascStorage * hs, DWORD IndexValue, DWORD In
return CombinePath(hs->szIndexPath, szPlainName);
}
static void SaveFileOffsetBitsAndEKeyLength(TCascStorage * hs, BYTE FileOffsetBits, BYTE EKeyLength)
{
if(hs->FileOffsetBits == 0)
hs->FileOffsetBits = FileOffsetBits;
assert(hs->FileOffsetBits == FileOffsetBits);
if(hs->EKeyLength == 0)
hs->EKeyLength = EKeyLength;
assert(hs->EKeyLength == EKeyLength);
}
// Verifies a guarded block - data availability and checksum match
static LPBYTE CaptureGuardedBlock1(LPBYTE pbFileData, LPBYTE pbFileEnd)
{
@@ -206,74 +217,84 @@ static LPBYTE CaptureGuardedBlock3(LPBYTE pbFileData, LPBYTE pbFileEnd, size_t E
return (LPBYTE)(PtrEntryHash + 1);
}
static bool CaptureIndexEntry(CASC_INDEX_HEADER & InHeader, PCASC_CKEY_ENTRY pCKeyEntry, LPBYTE pbEKeyEntry)
static bool CaptureIndexEntry(CASC_INDEX_HEADER & InHeader, PCASC_EKEY_ENTRY pEKeyEntry, LPBYTE pbEKeyEntry)
{
// Zero both CKey and EKey
ZeroMemory16(pCKeyEntry->CKey);
ZeroMemory16(pCKeyEntry->EKey);
// Copy the EKey of the variable length
pbEKeyEntry = CaptureEncodedKey(pEKeyEntry->EKey, pbEKeyEntry, InHeader.EKeyLength);
// Copy the EKey. We assume 9 bytes
pCKeyEntry->EKey[0x00] = pbEKeyEntry[0];
pCKeyEntry->EKey[0x01] = pbEKeyEntry[1];
pCKeyEntry->EKey[0x02] = pbEKeyEntry[2];
pCKeyEntry->EKey[0x03] = pbEKeyEntry[3];
pCKeyEntry->EKey[0x04] = pbEKeyEntry[4];
pCKeyEntry->EKey[0x05] = pbEKeyEntry[5];
pCKeyEntry->EKey[0x06] = pbEKeyEntry[6];
pCKeyEntry->EKey[0x07] = pbEKeyEntry[7];
pCKeyEntry->EKey[0x08] = pbEKeyEntry[8];
pCKeyEntry->EKey[0x09] = pbEKeyEntry[9];
pbEKeyEntry += InHeader.EKeyLength;
// Copy the storage offset
pCKeyEntry->StorageOffset = ConvertBytesToInteger_5(pbEKeyEntry);
pbEKeyEntry += InHeader.StorageOffsetLength;
// Clear the tag bit mask
pCKeyEntry->TagBitMask = 0;
// Copy the encoded length
pCKeyEntry->EncodedSize = ConvertBytesToInteger_4_LE(pbEKeyEntry);
pCKeyEntry->ContentSize = CASC_INVALID_SIZE;
pCKeyEntry->RefCount = 0;
pCKeyEntry->Priority = 0;
pCKeyEntry->Flags = CASC_CE_FILE_IS_LOCAL | CASC_CE_HAS_EKEY | CASC_CE_HAS_EKEY_PARTIAL;
// 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 (pCKeyEntry->EncodedSize > FIELD_OFFSET(BLTE_ENCODED_HEADER, Signature));
return (pEKeyEntry->EncodedSize > FIELD_OFFSET(BLTE_ENCODED_HEADER, Signature));
}
static void CheckForEncodingManifestCKey(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry)
static void InsertCKeyEntry(TCascStorage * hs, CASC_EKEY_ENTRY & EKeyEntry, DWORD Flags)
{
// If the encoding file was not found yet
if(hs->EncodingCKey.StorageOffset == CASC_INVALID_OFFS64)
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)
{
if(!memcmp(pCKeyEntry->EKey, hs->EncodingCKey.EKey, hs->EKeyLength))
// 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)
{
hs->EncodingCKey.StorageOffset = pCKeyEntry->StorageOffset;
hs->EncodingCKey.EncodedSize = pCKeyEntry->EncodedSize;
hs->EncodingCKey.Flags |= CASC_CE_FILE_IS_LOCAL;
pCKeyEntry->StorageOffset = EKeyEntry.StorageOffset;
pCKeyEntry->EncodedSize = EKeyEntry.EncodedSize;
}
}
// Add the extra flag
pCKeyEntry->Flags |= Flags;
}
static int LoadIndexItems(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbEKeyEntry, LPBYTE pbEKeyEnd)
static DWORD LoadIndexItems(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbEKeyEntry, LPBYTE pbEKeyEnd)
{
size_t EntryLength = InHeader.EntryLength;
while((pbEKeyEntry + EntryLength) <= pbEKeyEnd)
{
CASC_CKEY_ENTRY CKeyEntry;
CASC_EKEY_ENTRY EKeyEntry;
// Capture the index entry and verify it.
if(CaptureIndexEntry(InHeader, &CKeyEntry, pbEKeyEntry))
if(CaptureIndexEntry(InHeader, &EKeyEntry, pbEKeyEntry))
{
// Insert new entry to the array of CKey entries
if(hs->IndexArray.Insert(&CKeyEntry, 1) == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// DOWNLOAD in HOTS
//BREAK_ON_XKEY3(EKeyEntry.EKey, 0x09, 0xF3, 0xCD);
// Verify whether the key is not a CKEy entry for ENCODING file
CheckForEncodingManifestCKey(hs, &CKeyEntry);
// Insert the index entry to the central table
InsertCKeyEntry(hs, EKeyEntry, CASC_CE_FILE_IS_LOCAL);
}
pbEKeyEntry += EntryLength;
@@ -282,7 +303,7 @@ static int LoadIndexItems(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYT
return ERROR_SUCCESS;
}
static int CaptureIndexHeader_V1(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
static DWORD CaptureIndexHeader_V1(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
{
PFILE_INDEX_HEADER_V1 pIndexHeader = (PFILE_INDEX_HEADER_V1)pbFileData;
LPBYTE pbKeyEntries;
@@ -342,7 +363,7 @@ static int CaptureIndexHeader_V1(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData
return ERROR_SUCCESS;
}
static int CaptureIndexHeader_V2(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
static DWORD CaptureIndexHeader_V2(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
{
PFILE_INDEX_HEADER_V2 pIndexHeader;
LPBYTE pbFileEnd = pbFileData + cbFileData;
@@ -376,30 +397,28 @@ static int CaptureIndexHeader_V2(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData
return ERROR_SUCCESS;
}
static int LoadIndexFile_V1(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData)
static DWORD LoadIndexFile_V1(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData)
{
LPBYTE pbEKeyEntries = pbFileData + InHeader.HeaderLength + InHeader.HeaderPadding;
// Remember the values from the index header
hs->FileOffsetBits = InHeader.FileOffsetBits;
hs->EKeyLength = InHeader.EKeyLength;
SaveFileOffsetBitsAndEKeyLength(hs, InHeader.FileOffsetBits, InHeader.EKeyLength);
// Load the entries from a continuous array
return LoadIndexItems(hs, InHeader, pbEKeyEntries, pbFileData + cbFileData);
}
static int LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData)
static DWORD LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData)
{
LPBYTE pbEKeyEntry;
LPBYTE pbFileEnd = pbFileData + cbFileData;
LPBYTE pbFilePtr = pbFileData + InHeader.HeaderLength + InHeader.HeaderPadding;
size_t EKeyEntriesLength;
DWORD BlockSize = 0;
int nError = ERROR_NOT_SUPPORTED;
DWORD dwErrCode = ERROR_NOT_SUPPORTED;
// Remember the values from the index header
hs->FileOffsetBits = InHeader.FileOffsetBits;
hs->EKeyLength = InHeader.EKeyLength;
SaveFileOffsetBitsAndEKeyLength(hs, InHeader.FileOffsetBits, InHeader.EKeyLength);
// Get the pointer to the first block of EKey entries
if((pbEKeyEntry = CaptureGuardedBlock2(pbFilePtr, pbFileEnd, InHeader.EntryLength, &BlockSize)) != NULL)
@@ -427,7 +446,7 @@ static int LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPB
while(pbEKeyEntry < pbEndPage)
{
CASC_CKEY_ENTRY CKeyEntry;
CASC_EKEY_ENTRY EKeyEntry;
// Check the EKey entry protected by 32-bit hash
if((pbEKeyEntry = CaptureGuardedBlock3(pbEKeyEntry, pbEndPage, InHeader.EntryLength)) == NULL)
@@ -437,14 +456,9 @@ static int LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPB
//BREAK_ON_XKEY3(pbEKeyEntry, 0xbc, 0xe8, 0x23);
// Capture the index entry and verify it.
if(CaptureIndexEntry(InHeader, &CKeyEntry, pbEKeyEntry))
if(CaptureIndexEntry(InHeader, &EKeyEntry, pbEKeyEntry))
{
// Insert the EKey entry to the array
if(hs->IndexArray.Insert(&CKeyEntry, 1) == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// Check whether the CKey entry is an encoding entry
CheckForEncodingManifestCKey(hs, &CKeyEntry);
InsertCKeyEntry(hs, EKeyEntry, CASC_CE_FILE_IS_LOCAL);
}
// Move to the next entry
@@ -454,13 +468,13 @@ static int LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPB
// Move to the next chunk
pbStartPage += FILE_INDEX_PAGE_SIZE;
}
nError = ERROR_SUCCESS;
dwErrCode = ERROR_SUCCESS;
}
return nError;
return dwErrCode;
}
static int LoadIndexFile(TCascStorage * hs, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
static DWORD LoadIndexFile(TCascStorage * hs, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
{
CASC_INDEX_HEADER InHeader;
@@ -477,76 +491,73 @@ static int LoadIndexFile(TCascStorage * hs, LPBYTE pbFileData, DWORD cbFileData,
return ERROR_BAD_FORMAT;
}
static int LoadIndexFile(TCascStorage * hs, const TCHAR * szFileName, DWORD BucketIndex)
static DWORD LoadIndexFile(TCascStorage * hs, const TCHAR * szFileName, DWORD BucketIndex)
{
LPBYTE pbFileData;
DWORD cbFileData;
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
// WoW6 actually reads THE ENTIRE file to memory. Verified on Mac build (x64).
pbFileData = LoadFileToMemory(szFileName, &cbFileData);
if(pbFileData && cbFileData)
{
// Parse and load the index file
nError = LoadIndexFile(hs, pbFileData, cbFileData, BucketIndex);
dwErrCode = LoadIndexFile(hs, pbFileData, cbFileData, BucketIndex);
CASC_FREE(pbFileData);
}
else
{
nError = GetLastError();
dwErrCode = GetLastError();
}
return nError;
return dwErrCode;
}
static int LoadLocalIndexFiles(TCascStorage * hs)
static DWORD LoadLocalIndexFiles(TCascStorage * hs)
{
TCHAR * szFileName;
DWORD OldIndexArray[CASC_INDEX_COUNT];
DWORD IndexArray[CASC_INDEX_COUNT];
int nError;
DWORD dwErrCode;
// Scan all index files and load contained EKEY entries
memset(OldIndexArray, 0, sizeof(OldIndexArray));
memset(IndexArray, 0, sizeof(IndexArray));
nError = ScanIndexDirectory(hs->szIndexPath, IndexDirectory_OnFileFound, IndexArray, OldIndexArray, hs);
if(nError == ERROR_SUCCESS)
dwErrCode = ScanIndexDirectory(hs->szIndexPath, IndexDirectory_OnFileFound, IndexArray, OldIndexArray, hs);
if(dwErrCode == ERROR_SUCCESS)
{
// Initialize the array of index files
if((nError = hs->IndexArray.Create(sizeof(CASC_CKEY_ENTRY), 0x200000)) == ERROR_SUCCESS)
// Load each index file
for(DWORD i = 0; i < CASC_INDEX_COUNT; i++)
{
// Load each index file
for(DWORD i = 0; i < CASC_INDEX_COUNT; i++)
// Create the name of the index file
if((szFileName = CreateIndexFileName(hs, i, IndexArray[i])) != NULL)
{
// Create the name of the index file
if((szFileName = CreateIndexFileName(hs, i, IndexArray[i])) != NULL)
// Inform the user about what we are doing
if(InvokeProgressCallback(hs, "Loading index files", NULL, i, CASC_INDEX_COUNT))
{
// Inform the user about what we are doing
if(InvokeProgressCallback(hs, "Loading index files", NULL, i, CASC_INDEX_COUNT))
{
nError = ERROR_CANCELLED;
break;
}
// Load the index file
if((nError = LoadIndexFile(hs, szFileName, i)) != ERROR_SUCCESS)
break;
CASC_FREE(szFileName);
dwErrCode = ERROR_CANCELLED;
break;
}
}
// Remember the number of files that are present locally
hs->LocalFiles = hs->IndexArray.ItemCount();
// Load the index file
if((dwErrCode = LoadIndexFile(hs, szFileName, i)) != ERROR_SUCCESS)
break;
CASC_FREE(szFileName);
}
}
// Remember the number of files that are present locally
hs->LocalFiles = hs->CKeyArray.ItemCount();
}
return nError;
return dwErrCode;
}
//-----------------------------------------------------------------------------
// Online index files
// https://wowdev.wiki/TACT#CDN_File_Organization
static int CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, DWORD cbIndexFile)
static DWORD CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, DWORD cbIndexFile)
{
FILE_INDEX_FOOTER<0x08> * pFooter08;
BYTE checksum_data[0x40] = { 0 };
@@ -566,10 +577,10 @@ static int CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndex
InFooter.Version = pFooter08->Version;
InFooter.OffsetBytes = pFooter08->OffsetBytes;
InFooter.SizeBytes = pFooter08->SizeBytes;
InFooter.EKeyBytes = pFooter08->EKeySizeBytes;
InFooter.EKeyLength = pFooter08->EKeyLength;
InFooter.FooterHashBytes = pFooter08->FooterHashBytes;
InFooter.PageLength = pFooter08->PageSizeKB << 10;
InFooter.ItemLength = pFooter08->EKeySizeBytes + pFooter08->OffsetBytes + pFooter08->SizeBytes;
InFooter.ItemLength = pFooter08->EKeyLength + pFooter08->OffsetBytes + pFooter08->SizeBytes;
InFooter.FooterLength = sizeof(FILE_INDEX_FOOTER<0x08>);
InFooter.ElementCount = ConvertBytesToInteger_4_LE(pFooter08->ElementCount);
@@ -585,30 +596,33 @@ static int CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndex
return ERROR_BAD_FORMAT;
}
static int CaptureArcIndexEntry(CASC_ARCINDEX_FOOTER & InFooter, CASC_ARCINDEX_ENTRY & InEntry, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd)
static DWORD CaptureIndexEntry(CASC_ARCINDEX_FOOTER & InFooter, CASC_EKEY_ENTRY & EKeyEntry, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd, size_t nArchive)
{
ULONGLONG StorageOffset = nArchive;
ULONGLONG ArchiveOffset;
// If there enough bytes for one entry/
if ((pbIndexPage + InFooter.ItemLength) > pbIndexPageEnd)
return ERROR_BAD_FORMAT;
// Copy the item
memcpy(InEntry.EKey, pbIndexPage, InFooter.EKeyBytes);
pbIndexPage += InFooter.EKeyBytes;
// Capture the EKey (variable length)
pbIndexPage = CaptureEncodedKey(EKeyEntry.EKey, pbIndexPage, InFooter.EKeyLength);
// Copy the archive offset
InEntry.EncodedSize = ConvertBytesToInteger_X(pbIndexPage, InFooter.OffsetBytes);
pbIndexPage += InFooter.OffsetBytes;
// Copy thefile encoded size
InEntry.ArchiveOffset = ConvertBytesToInteger_X(pbIndexPage, InFooter.SizeBytes);
if (InEntry.ArchiveOffset >= 0x10000000)
ArchiveOffset = ConvertBytesToInteger_X(pbIndexPage + InFooter.SizeBytes, InFooter.OffsetBytes);
if (ArchiveOffset >= 0x10000000)
return ERROR_BAD_FORMAT;
// Capture the storage offset and encoded size
EKeyEntry.StorageOffset = (StorageOffset << (InFooter.OffsetBytes * 8)) | ArchiveOffset;
EKeyEntry.EncodedSize = ConvertBytesToInteger_X(pbIndexPage, InFooter.SizeBytes);
EKeyEntry.Alignment = 0;
// Is there a valid hash?
return CascIsValidMD5(InEntry.EKey) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
return CascIsValidMD5(EKeyEntry.EKey) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
}
static int VerifyIndexSize(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, size_t cbIndexFile, LPBYTE * PtrIndexEnd)
static DWORD VerifyIndexSize(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, size_t cbIndexFile, LPBYTE * PtrIndexEnd)
{
size_t nPageCount;
@@ -626,21 +640,20 @@ static int VerifyIndexSize(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile,
return ERROR_SUCCESS;
}
static int LoadArchiveIndexPage(TCascStorage * hs, CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexHash, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd)
static DWORD LoadArchiveIndexPage(TCascStorage * hs, CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd, size_t nArchive)
{
CASC_ARCINDEX_ENTRY InEntry;
int nError;
CASC_EKEY_ENTRY EKeyEntry;
DWORD dwErrCode;
while (pbIndexPage < pbIndexPageEnd)
while (pbIndexPage <= pbIndexPageEnd)
{
// Capture the index entry
nError = CaptureArcIndexEntry(InFooter, InEntry, pbIndexPage, pbIndexPageEnd);
if (nError != ERROR_SUCCESS)
dwErrCode = CaptureIndexEntry(InFooter, EKeyEntry, pbIndexPage, pbIndexPageEnd, nArchive);
if (dwErrCode != ERROR_SUCCESS)
break;
// Insert the index entry to the array
memcpy(InEntry.IndexHash, pbIndexHash, MD5_HASH_SIZE);
if (hs->ArcIndexArray.Insert(&InEntry, 1) == NULL)
// Insert a new entry to the index array
if((hs->IndexArray.Insert(&EKeyEntry, 1)) == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// Move to the next entry
@@ -650,28 +663,31 @@ static int LoadArchiveIndexPage(TCascStorage * hs, CASC_ARCINDEX_FOOTER & InFoot
return ERROR_SUCCESS;
}
static int LoadArchiveIndexFile(TCascStorage * hs, LPBYTE pbIndexHash, LPBYTE pbIndexFile, DWORD cbIndexFile)
static DWORD LoadArchiveIndexFile(TCascStorage * hs, LPBYTE pbIndexFile, DWORD cbIndexFile, size_t nArchive)
{
CASC_ARCINDEX_FOOTER InFooter;
LPBYTE pbIndexEnd = NULL;
int nError;
DWORD dwErrCode;
// Validate and capture the footer
nError = CaptureArcIndexFooter(InFooter, pbIndexFile, cbIndexFile);
if (nError != ERROR_SUCCESS)
return nError;
dwErrCode = CaptureArcIndexFooter(InFooter, pbIndexFile, cbIndexFile);
if (dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Remember the file offset and EKey length
SaveFileOffsetBitsAndEKeyLength(hs, InFooter.OffsetBytes * 8, InFooter.EKeyLength);
// Verify the size of the index file
nError = VerifyIndexSize(InFooter, pbIndexFile, cbIndexFile, &pbIndexEnd);
if (nError != ERROR_SUCCESS)
return nError;
dwErrCode = VerifyIndexSize(InFooter, pbIndexFile, cbIndexFile, &pbIndexEnd);
if (dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Parse all pages
while (pbIndexFile < pbIndexEnd)
{
// Load the entire page
nError = LoadArchiveIndexPage(hs, InFooter, pbIndexHash, pbIndexFile, pbIndexFile + InFooter.PageLength);
if (nError != ERROR_SUCCESS)
dwErrCode = LoadArchiveIndexPage(hs, InFooter, pbIndexFile, pbIndexFile + InFooter.PageLength, nArchive);
if (dwErrCode != ERROR_SUCCESS)
break;
// Move to the next page
@@ -681,87 +697,95 @@ static int LoadArchiveIndexFile(TCascStorage * hs, LPBYTE pbIndexHash, LPBYTE pb
return ERROR_SUCCESS;
}
static int BuildMapOfArcIndices(TCascStorage * hs)
static DWORD BuildMapOfArchiveIndices(TCascStorage * hs)
{
PCASC_ARCINDEX_ENTRY pEntry;
size_t nItemCount = hs->ArcIndexArray.ItemCount();
int nError;
PCASC_EKEY_ENTRY pEKeyEntry;
size_t nItemCount = hs->IndexArray.ItemCount();
DWORD dwErrCode;
// Create the map
nError = hs->ArcIndexMap.Create(nItemCount, MD5_HASH_SIZE, FIELD_OFFSET(CASC_ARCINDEX_ENTRY, EKey));
if (nError != ERROR_SUCCESS)
return nError;
dwErrCode = hs->IndexMap.Create(nItemCount, MD5_HASH_SIZE, FIELD_OFFSET(CASC_EKEY_ENTRY, EKey));
if (dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Insert all items
for(size_t i = 0; i < nItemCount; i++)
{
pEntry = (PCASC_ARCINDEX_ENTRY)hs->ArcIndexArray.ItemAt(i);
if (pEntry != NULL)
pEKeyEntry = (PCASC_EKEY_ENTRY)hs->IndexArray.ItemAt(i);
if (pEKeyEntry != NULL)
{
if (!hs->ArcIndexMap.InsertObject(pEntry, pEntry->EKey))
if (!hs->IndexMap.InsertObject(pEKeyEntry, pEKeyEntry->EKey))
{
return ERROR_NOT_ENOUGH_MEMORY;
}
}
}
return nError;
return dwErrCode;
}
static int LoadArchiveIndexFiles(TCascStorage * hs)
static DWORD LoadArchiveIndexFiles(TCascStorage * hs)
{
LPBYTE pbFileData;
TCHAR szLocalPath[MAX_PATH];
DWORD cbFileData = 0;
size_t nArchiveCount = (hs->ArchivesKey.cbData / MD5_HASH_SIZE);
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
// Create the array object for the indices
nError = hs->ArcIndexArray.Create(sizeof(CASC_ARCINDEX_ENTRY), 10000);
if (nError != ERROR_SUCCESS)
return nError;
dwErrCode = hs->IndexArray.Create(sizeof(CASC_EKEY_ENTRY), 0x10000);
if (dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Load all the indices
for (size_t i = 0; i < nArchiveCount; i++)
{
CASC_CDN_DOWNLOAD CdnsInfo = {0};
LPBYTE pbIndexHash = hs->ArchivesKey.pbData + (i * MD5_HASH_SIZE);
// Inform the user about what we are doing
if(InvokeProgressCallback(hs, "Downloading archive indexes", NULL, (DWORD)(i), (DWORD)(nArchiveCount)))
{
nError = ERROR_CANCELLED;
dwErrCode = ERROR_CANCELLED;
break;
}
// Make sure that we have local copy of the file
nError = DownloadFileFromCDN(hs, _T("data"), pbIndexHash, _T(".index"), szLocalPath, _countof(szLocalPath));
if (nError == ERROR_SUCCESS)
// 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)
{
// Load the index file to memory
pbFileData = LoadFileToMemory(szLocalPath, &cbFileData);
if (pbFileData && cbFileData)
{
nError = LoadArchiveIndexFile(hs, pbIndexHash, pbFileData, cbFileData);
dwErrCode = LoadArchiveIndexFile(hs, pbFileData, cbFileData, i);
CASC_FREE(pbFileData);
}
}
// Break if an error
if (nError != ERROR_SUCCESS)
if (dwErrCode != ERROR_SUCCESS)
break;
}
// Build map of EKey -> CASC_ARCINDEX_ENTRY
if (nError == ERROR_SUCCESS)
nError = BuildMapOfArcIndices(hs);
return nError;
// Build map of EKey -> CASC_EKEY_ENTRY
if (dwErrCode == ERROR_SUCCESS)
dwErrCode = BuildMapOfArchiveIndices(hs);
return dwErrCode;
}
//-----------------------------------------------------------------------------
// Public functions
int LoadIndexFiles(TCascStorage * hs)
DWORD LoadIndexFiles(TCascStorage * hs)
{
if (hs->dwFeatures & CASC_FEATURE_ONLINE)
{

View File

@@ -13,13 +13,17 @@ EXPORTS
CascOpenOnlineStorage
CascGetStorageInfo
CascAddEncryptionKey
CascAddStringEncryptionKey
CascFindEncryptionKey
CascCloseStorage
CascOpenFile
CascOpenLocalFile
CascGetFileInfo
CascGetFileSize
CascGetFileSize64
CascSetFilePointer
CascSetFilePointer64
CascReadFile
CascCloseFile
@@ -29,4 +33,3 @@ EXPORTS
GetLastError=Kernel32.GetLastError
SetLastError=Kernel32.SetLastError

View File

@@ -16,7 +16,7 @@
#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
@@ -41,29 +41,29 @@ extern "C" {
#ifdef _DEBUG // DEBUG VERSIONS
#ifndef _UNICODE
#ifdef _DLL
#pragma comment(lib, "CascLibDAD.lib") // Debug Ansi CRT-DLL version
#pragma comment(lib, "CascLibDAD.lib") // Debug Ansi CRT-DLL version
#else
#pragma comment(lib, "CascLibDAS.lib") // Debug Ansi CRT-LIB version
#pragma comment(lib, "CascLibDAS.lib") // Debug Ansi CRT-LIB version
#endif
#else
#ifdef _DLL
#pragma comment(lib, "CascLibDUD.lib") // Debug Unicode CRT-DLL version
#pragma comment(lib, "CascLibDUD.lib") // Debug Unicode CRT-DLL version
#else
#pragma comment(lib, "CascLibDUS.lib") // Debug Unicode CRT-LIB version
#pragma comment(lib, "CascLibDUS.lib") // Debug Unicode CRT-LIB version
#endif
#endif
#else // RELEASE VERSIONS
#ifndef _UNICODE
#ifdef _DLL
#pragma comment(lib, "CascLibRAD.lib") // Release Ansi CRT-DLL version
#pragma comment(lib, "CascLibRAD.lib") // Release Ansi CRT-DLL version
#else
#pragma comment(lib, "CascLibRAS.lib") // Release Ansi CRT-LIB version
#pragma comment(lib, "CascLibRAS.lib") // Release Ansi CRT-LIB version
#endif
#else
#ifdef _DLL
#pragma comment(lib, "CascLibRUD.lib") // Release Unicode CRT-DLL version
#pragma comment(lib, "CascLibRUD.lib") // Release Unicode CRT-DLL version
#else
#pragma comment(lib, "CascLibRUS.lib") // Release Unicode CRT-LIB version
#pragma comment(lib, "CascLibRUS.lib") // Release Unicode CRT-LIB version
#endif
#endif
#endif
@@ -72,14 +72,14 @@ extern "C" {
//-----------------------------------------------------------------------------
// Defines
#define CASCLIB_VERSION 0x0132 // Current version of CascLib (1.50)
#define CASCLIB_VERSION_STRING "1.50" // String version of CascLib version
#define CASCLIB_VERSION 0x0200 // CascLib version - integral (1.50)
#define CASCLIB_VERSION_STRING "2.0" // CascLib version - string
// Values for CascOpenFile
#define CASC_OPEN_BY_NAME 0x00000000 // Open the file by name. This is the default value
#define CASC_OPEN_BY_CKEY 0x00000001 // The name is just the content key; skip ROOT file processing
#define CASC_OPEN_BY_EKEY 0x00000002 // The name is just the encoded key; skip ROOT file processing
#define CASC_OPEN_BY_FILEID 0x00000003 // The name is CASC_IDTONAME(FileDataId)
#define CASC_OPEN_BY_FILEID 0x00000003 // The name is CASC_FILE_DATA_ID(FileDataId)
#define CASC_OPEN_TYPE_MASK 0x0000000F // The mask which gets open type from the dwFlags
#define CASC_OPEN_FLAGS_MASK 0xFFFFFFF0 // The mask which gets open type from the dwFlags
#define CASC_STRICT_DATA_CHECK 0x00000010 // Verify all data read from a file
@@ -119,16 +119,13 @@ extern "C" {
#define MD5_STRING_SIZE 0x20
#endif
#ifndef SHA1_DIGEST_SIZE
#define SHA1_DIGEST_SIZE 0x14 // 160 bits
#endif
// Return value for CascGetFileSize and CascSetFilePointer
#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
// Flags for CASC_STORAGE_FEATURES::dwFeatures
#define CASC_FEATURE_FILE_NAMES 0x00000001 // File names are supported by the storage
@@ -160,7 +157,7 @@ typedef enum _CASC_STORAGE_INFO_CLASS
CascStorageFeatures, // Returns the features flag
CascStorageInstalledLocales,
CascStorageInstalledLocales, // Not supported
CascStorageProduct, // Gives CASC_STORAGE_PRODUCT
CascStorageTags, // Gives CASC_STORAGE_TAGS structure
CascStoragePathProduct, // Gives Path:Product into a LPTSTR buffer
@@ -173,27 +170,10 @@ typedef enum _CASC_FILE_INFO_CLASS
CascFileContentKey,
CascFileEncodedKey,
CascFileFullInfo, // Gives CASC_FILE_FULL_INFO structure
CascFileSpanInfo, // Gives CASC_FILE_SPAN_INFO structure for each file span
CascFileInfoClassMax
} CASC_FILE_INFO_CLASS, *PCASC_FILE_INFO_CLASS;
// See https://wowdev.wiki/TACT#Products
typedef enum _CASC_PRODUCT
{
UnknownProduct,
HeroesOfTheStorm,
Diablo3,
Overwatch,
StarCraft1,
StarCraft2,
WorldOfWarcraft,
WarCraft3,
Destiny2,
CallOfDutyBlackOps4,
Odin,
MaxProductValue
} CASC_PRODUCT, *PCASC_PRODUCT;
// CascLib may provide a fake name, constructed from file data id, CKey or EKey.
// This enum helps to see what name was actually returned
// Note that any of these names can be passed to CascOpenFile with no extra flags
@@ -221,27 +201,29 @@ typedef struct _CASC_FIND_DATA
// Tag mask. Only valid if the storage supports tags, otherwise 0
ULONGLONG TagBitMask;
// Size of the file, as retrieved from CKey entry
ULONGLONG FileSize;
// Plain name of the found file. Pointing inside the 'szFileName' array
char * szPlainName;
// File data ID. Only valid if the storage supports file data IDs, otherwise CASC_INVALID_ID
DWORD dwFileDataId;
// Size of the file, as retrieved from CKey entry or EKey entry
DWORD dwFileSize;
// Locale flags. Only valid if the storage supports locale flags, otherwise CASC_INVALID_ID
DWORD dwLocaleFlags;
// Content flags. Only valid if the storage supports content flags, otherwise CASC_INVALID_ID
DWORD dwContentFlags;
// Hints as for which open method is suitable
DWORD bFileAvailable:1; // If true the file is available locally
DWORD bCanOpenByName:1;
DWORD bCanOpenByDataId:1;
DWORD bCanOpenByCKey:1;
DWORD bCanOpenByEKey:1;
// Span count
DWORD dwSpanCount;
// If true the file is available locally
DWORD bFileAvailable:1;
// Name type in 'szFileName'. In case the file name is not known,
// CascLib can put FileDataId-like name or a string representation of CKey/EKey
CASC_NAME_TYPE NameType;
} CASC_FIND_DATA, *PCASC_FIND_DATA;
@@ -264,9 +246,8 @@ typedef struct _CASC_STORAGE_TAGS
typedef struct _CASC_STORAGE_PRODUCT
{
LPCSTR szProductName;
DWORD dwBuildNumber;
CASC_PRODUCT Product;
char szCodeName[0x1C]; // Code name of the product ("wowt" = "World of Warcraft PTR")
DWORD BuildNumber; // Build number. If zero, then CascLib didn't recognize build number
} CASC_STORAGE_PRODUCT, *PCASC_STORAGE_PRODUCT;
@@ -279,15 +260,29 @@ typedef struct _CASC_FILE_FULL_INFO
ULONGLONG SegmentOffset; // Offset of the file in the segment file ("data.###")
ULONGLONG TagBitMask; // Bitmask of tags. Zero if not supported
ULONGLONG FileNameHash; // Hash of the file name. Zero if not supported
ULONGLONG ContentSize; // Content size of all spans
ULONGLONG EncodedSize; // Encoded size of all spans
DWORD SegmentIndex; // Index of the segment file (aka 0 = "data.000")
DWORD SpanCount; // Number of spans forming the file
DWORD FileDataId; // File data ID. CASC_INVALID_ID if not supported.
DWORD ContentSize; // Content size of the file
DWORD EncodedSize; // Encoded size of the file
DWORD LocaleFlags; // Locale flags. CASC_INVALID_ID if not supported.
DWORD ContentFlags; // Locale flags. CASC_INVALID_ID if not supported
} CASC_FILE_FULL_INFO, *PCASC_FILE_FULL_INFO;
typedef struct _CASC_FILE_SPAN_INFO
{
BYTE CKey[MD5_HASH_SIZE]; // Content key of the file span
BYTE EKey[MD5_HASH_SIZE]; // Encoded key of the file span
ULONGLONG StartOffset; // Starting offset of the file span
ULONGLONG EndOffset; // Ending offset of the file span
DWORD ArchiveIndex; // Index of the archive
DWORD ArchiveOffs; // Offset in the archive
DWORD HeaderSize; // Size of encoded frame headers
DWORD FrameCount; // Number of frames in this span
} CASC_FILE_SPAN_INFO, *PCASC_FILE_SPAN_INFO;
//-----------------------------------------------------------------------------
// Extended version of CascOpenStorage
@@ -348,16 +343,21 @@ bool WINAPI CascOpenStorage(LPCTSTR szParams, DWORD dwLocaleMask, HANDLE * phSt
bool WINAPI CascOpenOnlineStorage(LPCTSTR szParams, DWORD dwLocaleMask, HANDLE * phStorage);
bool WINAPI CascGetStorageInfo(HANDLE hStorage, CASC_STORAGE_INFO_CLASS InfoClass, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded);
bool WINAPI CascAddEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPBYTE Key);
bool WINAPI CascAddStringEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPCSTR szKey);
LPBYTE WINAPI CascFindEncryptionKey(HANDLE hStorage, ULONGLONG KeyName);
bool WINAPI CascCloseStorage(HANDLE hStorage);
bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocaleFlags, DWORD dwOpenFlags, HANDLE * phFile);
bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocaleFlags, DWORD dwOpenFlags, HANDLE * PtrFileHandle);
bool WINAPI CascOpenLocalFile(LPCTSTR szFileName, DWORD dwOpenFlags, HANDLE * PtrFileHandle);
bool WINAPI CascGetFileInfo(HANDLE hFile, CASC_FILE_INFO_CLASS InfoClass, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded);
DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh);
DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod);
bool WINAPI CascGetFileSize64(HANDLE hFile, PULONGLONG PtrFileSize);
bool WINAPI CascSetFilePointer64(HANDLE hFile, LONGLONG DistanceToMove, PULONGLONG PtrNewPos, DWORD dwMoveMethod);
bool WINAPI CascReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, PDWORD pdwRead);
bool WINAPI CascCloseFile(HANDLE hFile);
DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh);
DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * PtrFilePosHigh, DWORD dwMoveMethod);
HANDLE WINAPI CascFindFirstFile(HANDLE hStorage, LPCSTR szMask, PCASC_FIND_DATA pFindData, LPCTSTR szListFile);
bool WINAPI CascFindNextFile(HANDLE hFind, PCASC_FIND_DATA pFindData);
bool WINAPI CascFindClose(HANDLE hFind);

View File

@@ -12,9 +12,172 @@
#include "CascLib.h"
#include "CascCommon.h"
//-----------------------------------------------------------------------------
// TCascFile class functions
TCascFile::TCascFile(TCascStorage * ahs, PCASC_CKEY_ENTRY apCKeyEntry)
{
// Reference the storage handle
if((hs = ahs) != NULL)
hs->AddRef();
ClassName = CASC_MAGIC_FILE;
FilePointer = 0;
pCKeyEntry = apCKeyEntry;
SpanCount = (pCKeyEntry->SpanCount != 0) ? pCKeyEntry->SpanCount : 1;
bVerifyIntegrity = false;
bDownloadFileIf = false;
bCloseFileStream = false;
bFreeCKeyEntries = false;
// Allocate the array of file spans
if((pFileSpan = CASC_ALLOC_ZERO<CASC_FILE_SPAN>(SpanCount)) != NULL)
{
InitFileSpans(pFileSpan, SpanCount);
InitCacheStrategy();
}
}
TCascFile::~TCascFile()
{
// Free all stuff related to file spans
if (pFileSpan != NULL)
{
PCASC_FILE_SPAN pSpanPtr = pFileSpan;
for(DWORD i = 0; i < SpanCount; i++, pSpanPtr++)
{
// Close the span file stream if this is a local file
if(bCloseFileStream)
FileStream_Close(pSpanPtr->pStream);
pSpanPtr->pStream = NULL;
// Free the span frames
CASC_FREE(pSpanPtr->pFrames);
}
CASC_FREE(pFileSpan);
}
// Free the CKey entries, if needed
if(pCKeyEntry && bFreeCKeyEntries)
delete [] pCKeyEntry;
pCKeyEntry = NULL;
// Free the file cache
CASC_FREE(pbFileCache);
// Close (dereference) the archive handle
if(hs != NULL)
hs = hs->Release();
ClassName = 0;
}
DWORD TCascFile::OpenFileSpans(LPCTSTR szSpanList)
{
TFileStream * pStream;
ULONGLONG FileSize = 0;
DWORD dwErrCode = ERROR_SUCCESS;
for(DWORD i = 0; i < SpanCount; i++)
{
// Open the file span
pFileSpan[i].pStream = pStream = FileStream_OpenFile(szSpanList, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT);
if(pFileSpan[i].pStream == NULL)
{
dwErrCode = GetLastError();
break;
}
// If succeeded, we assign the span to the
FileStream_GetSize(pStream, &FileSize);
if((FileSize >> 0x1E) != 0)
{
dwErrCode = ERROR_NOT_SUPPORTED;
break;
}
pCKeyEntry[i].EncodedSize = (DWORD)FileSize;
}
// Free the so-far-opened files
if(dwErrCode != ERROR_SUCCESS)
{
for(DWORD i = 0; i < SpanCount; i++)
{
if(pFileSpan[i].pStream != NULL)
FileStream_Close(pFileSpan[i].pStream);
pFileSpan[i].pStream = NULL;
}
}
return dwErrCode;
}
void TCascFile::InitFileSpans(PCASC_FILE_SPAN pSpans, DWORD dwSpanCount)
{
ULONGLONG FileOffsetBits = 30;
ULONGLONG FileOffsetMask = 0;
ULONGLONG FileOffset = 0;
// Initialize the file sizes. Note that if any of the spans has invalid size,
// the entire file size will be set to CASC_INVALID_SIZE64.
GetFileSpanInfo(pCKeyEntry, &ContentSize, &EncodedSize);
// Resolve the file offset bits and file offset mask
if(hs != NULL)
FileOffsetBits = hs->FileOffsetBits;
FileOffsetMask = ((ULONGLONG)1 << FileOffsetBits) - 1;
// Add all span sizes
for(DWORD i = 0; i < dwSpanCount; i++, pSpans++)
{
// Put the archive index and archive offset
pSpans->ArchiveIndex = (DWORD)(pCKeyEntry[i].StorageOffset >> FileOffsetBits);
pSpans->ArchiveOffs = (DWORD)(pCKeyEntry[i].StorageOffset & FileOffsetMask);
// Add to the total encoded size
if(ContentSize != CASC_INVALID_SIZE64)
{
pSpans->StartOffset = FileOffset;
FileOffset = FileOffset + pCKeyEntry[i].ContentSize;
pSpans->EndOffset = FileOffset;
}
}
}
void TCascFile::InitCacheStrategy()
{
CacheStrategy = CascCacheLastFrame;
FileCacheStart = FileCacheEnd = 0;
pbFileCache = NULL;
}
//-----------------------------------------------------------------------------
// Local functions
static size_t GetSpanFileCount(LPTSTR szSpanList)
{
LPTSTR szSpanPtr = szSpanList;
size_t nSpanCount = 1;
while(szSpanPtr[0] != 0)
{
// End of a file?
if(szSpanPtr[0] == ';' && szSpanPtr[1] != 0)
{
szSpanPtr[0] = 0;
nSpanCount++;
}
szSpanPtr++;
}
// Place an additional zero to make the list terminated by double EOS
szSpanPtr[1] = 0;
return nSpanCount;
}
PCASC_CKEY_ENTRY FindCKeyEntry_CKey(TCascStorage * hs, LPBYTE pbCKey, PDWORD PtrIndex)
{
return (PCASC_CKEY_ENTRY)hs->CKeyMap.FindObject(pbCKey, PtrIndex);
@@ -28,7 +191,7 @@ PCASC_CKEY_ENTRY FindCKeyEntry_EKey(TCascStorage * hs, LPBYTE pbEKey, PDWORD Ptr
bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD dwOpenFlags, HANDLE * PtrFileHandle)
{
TCascFile * hf = NULL;
int nError = ERROR_FILE_NOT_FOUND;
DWORD dwErrCode = ERROR_FILE_NOT_FOUND;
// If the CKey entry is NULL, we consider the file non-existant
if(pCKeyEntry != NULL)
@@ -39,11 +202,11 @@ bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD d
hf->bVerifyIntegrity = (dwOpenFlags & CASC_STRICT_DATA_CHECK) ? true : false;
hf->bDownloadFileIf = (hs->dwFeatures & CASC_FEATURE_ONLINE) ? true : false;
hf->bOvercomeEncrypted = (dwOpenFlags & CASC_OVERCOME_ENCRYPTED) ? true : false;
nError = ERROR_SUCCESS;
dwErrCode = ERROR_SUCCESS;
}
else
{
nError = ERROR_NOT_ENOUGH_MEMORY;
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
}
@@ -51,9 +214,89 @@ bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD d
PtrFileHandle[0] = (HANDLE)hf;
// Handle last error
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return (nError == ERROR_SUCCESS);
if(dwErrCode != ERROR_SUCCESS)
SetLastError(dwErrCode);
return (dwErrCode == ERROR_SUCCESS);
}
bool OpenLocalFile(LPCTSTR szFileName, DWORD dwOpenFlags, HANDLE * PtrFileHandle)
{
PCASC_CKEY_ENTRY pCKeyEntry;
TCascFile * hf = NULL;
LPTSTR szSpanList;
size_t nSpanCount;
DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
// Create a copy of the file name. It is actually a file name list,
// separated by comma (for supporting multi-span files)
if((szSpanList = CascNewStr(szFileName, 1)) != NULL)
{
// Calculate the span count
if((nSpanCount = GetSpanFileCount(szSpanList)) != 0 || nSpanCount > 0xFF)
{
// Allocate CKey array for the file. Each entry describes one file span
if((pCKeyEntry = new CASC_CKEY_ENTRY[nSpanCount]) != NULL)
{
// Prepare the span count to the first item
pCKeyEntry->SpanCount = (BYTE)nSpanCount;
// Prepare the archive offset in each CKey entry
for(size_t i = 0; i < nSpanCount; i++)
pCKeyEntry[i].StorageOffset = 0;
// Create an instance of the TCascFile
if((hf = new TCascFile(NULL, pCKeyEntry)) != NULL)
{
// Prepare the structure
hf->bVerifyIntegrity = (dwOpenFlags & CASC_STRICT_DATA_CHECK) ? true : false;
hf->bOvercomeEncrypted = (dwOpenFlags & CASC_OVERCOME_ENCRYPTED) ? true : false;
hf->bCloseFileStream = true;
// Open all local file spans
dwErrCode = hf->OpenFileSpans(szSpanList);
if(dwErrCode != ERROR_SUCCESS)
{
delete hf;
hf = NULL;
}
}
}
}
else
{
dwErrCode = ERROR_INVALID_PARAMETER;
}
delete [] szSpanList;
}
// Give the output parameter, no matter what
PtrFileHandle[0] = (HANDLE)hf;
// Handle last error
if(dwErrCode != ERROR_SUCCESS)
SetLastError(dwErrCode);
return (dwErrCode == ERROR_SUCCESS);
}
bool SetCacheStrategy(HANDLE hFile, CSTRTG CacheStrategy)
{
TCascFile * hf;
// Validate the file handle
if((hf = TCascFile::IsValid(hFile)) != NULL)
{
// The cache must not be initialized yet
if(hf->pbFileCache == NULL)
{
hf->CacheStrategy = CacheStrategy;
return true;
}
}
// Failed. This should never happen
assert(false);
return false;
}
//-----------------------------------------------------------------------------
@@ -66,7 +309,7 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocal
const char * szFileName;
DWORD FileDataId = CASC_INVALID_ID;
BYTE CKeyEKeyBuffer[MD5_HASH_SIZE];
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
// This parameter is not used
CASCLIB_UNUSED(dwLocaleFlags);
@@ -162,7 +405,7 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocal
default:
// Unknown open mode
nError = ERROR_INVALID_PARAMETER;
dwErrCode = ERROR_INVALID_PARAMETER;
break;
}
@@ -170,6 +413,18 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocal
return OpenFileByCKeyEntry(hs, pCKeyEntry, dwOpenFlags, PtrFileHandle);
}
bool WINAPI CascOpenLocalFile(LPCTSTR szFileName, DWORD dwOpenFlags, HANDLE * PtrFileHandle)
{
// Verify parameters
if(szFileName == NULL || szFileName[0] == 0 || PtrFileHandle == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
return OpenLocalFile(szFileName, dwOpenFlags, PtrFileHandle);
}
bool WINAPI CascCloseFile(HANDLE hFile)
{
TCascFile * hf;

View File

@@ -17,33 +17,29 @@
//-----------------------------------------------------------------------------
// Local defines
// Limit for "orphaned" items - those that are in index files, but are not in ENCODING manifest
#define CASC_MAX_ORPHANED_ITEMS 0x100
// Limit for "additional" items in CKey table
#define CASC_MAX_EXTRA_ITEMS 0x40
//-----------------------------------------------------------------------------
// TCascStorage service functions
// TCascStorage class functions
TCascStorage::TCascStorage()
{
// Prepare the base storage parameters
szClassName = "TCascStorage";
ClassName = CASC_MAGIC_STORAGE;
pRootHandler = NULL;
dwDefaultLocale = CASC_LOCALE_ENUS | CASC_LOCALE_ENGB;
dwRefCount = 1;
szRootPath = szDataPath = szIndexPath = szBuildFile = szCdnServers = szCdnPath = szCodeName = NULL;
szProductName = NULL;
szIndexFormat = NULL;
szRegion = NULL;
memset(DataFiles, 0, sizeof(DataFiles));
Product = UnknownProduct;
dwBuildNumber = 0;
dwFeatures = 0;
bAllowOrphans = false;
BuildFileType = CascBuildNone;
LocalFiles = TotalFiles = EKeyEntries = OrphanItems = SkippedItems = EKeyLength = FileOffsetBits = 0;
LocalFiles = TotalFiles = EKeyEntries = EKeyLength = FileOffsetBits = 0;
}
TCascStorage::~TCascStorage()
@@ -79,32 +75,24 @@ TCascStorage::~TCascStorage()
FreeCascBlob(&PatchArchivesKey);
FreeCascBlob(&PatchArchivesGroup);
FreeCascBlob(&BuildFiles);
szClassName = NULL;
ClassName = 0;
}
TCascStorage * TCascStorage::AddRef()
{
dwRefCount++;
CascInterlockedIncrement(&dwRefCount);
return this;
}
TCascStorage * TCascStorage::Release()
{
if (dwRefCount == 1)
if(CascInterlockedDecrement(&dwRefCount) == 0)
{
delete this;
return NULL;
}
dwRefCount--;
return NULL;
}
TCascStorage * TCascStorage::IsValid(HANDLE hStorage)
{
TCascStorage * hs = (TCascStorage *)hStorage;
return (hs != NULL && hs->szClassName != NULL && !strcmp(hs->szClassName, "TCascStorage")) ? hs : NULL;
return this;
}
//-----------------------------------------------------------------------------
@@ -139,38 +127,205 @@ static TCHAR * CheckForIndexDirectory(TCascStorage * hs, const TCHAR * szSubDir)
return szIndexPath;
}
static int CreateCKeyMaps(TCascStorage * hs, CASC_ENCODING_HEADER & EnHeader)
// Inserts an entry from the text build file
static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, CASC_CKEY_ENTRY & CKeyEntry)
{
size_t nEstimatedEntries = (EnHeader.CKeyPageCount * EnHeader.CKeyPageSize) / sizeof(FILE_CKEY_ENTRY);
size_t nIxEntries = hs->IndexArray.ItemCount();
int nError;
PCASC_CKEY_ENTRY pCKeyEntry = NULL;
// Orphaned items: These are present in INDEX files (by EKey), but missing in the ENCODING manifest.
// Probably a bug in generator of "2018 - New CASC\00001", but we want to open the storage nontheless.
if(nEstimatedEntries < 0x100)
// Skip entries without any key
if(CKeyEntry.Flags & (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY))
{
nEstimatedEntries = nEstimatedEntries + nIxEntries;
hs->bAllowOrphans = true;
// Check if there is an existing entry
if((pCKeyEntry = FindCKeyEntry_CKey(hs, CKeyEntry.CKey)) == NULL)
{
// Insert a new entry to the array. DO NOT ALLOW enlarge array here
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false);
if(pCKeyEntry == NULL)
return NULL;
// Fill in the item
memcpy(pCKeyEntry, &CKeyEntry, sizeof(CASC_CKEY_ENTRY));
// If we have CKey present, insert it to the CKey map
if(CKeyEntry.Flags & CASC_CE_HAS_CKEY)
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
// If we have EKey present, insert it to the EKey map
if(CKeyEntry.Flags & CASC_CE_HAS_EKEY)
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
}
else
{
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
pCKeyEntry->ContentSize = CKeyEntry.ContentSize;
if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE)
pCKeyEntry->EncodedSize = CKeyEntry.EncodedSize;
}
}
// Allow some room for extra entries
nEstimatedEntries += CASC_MAX_ORPHANED_ITEMS;
return pCKeyEntry;
}
// Inserts an entry from ENCODING
static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, PFILE_CKEY_ENTRY pFileEntry)
{
PCASC_CKEY_ENTRY pCKeyEntry;
// Check whether the entry is already there
if((pCKeyEntry = FindCKeyEntry_EKey(hs, pFileEntry->EKey)) == NULL)
{
// Insert a new entry to the array. DO NOT ALLOW enlarge array here
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false);
if(pCKeyEntry == NULL)
return NULL;
CopyMemory16(pCKeyEntry->CKey, pFileEntry->CKey);
CopyMemory16(pCKeyEntry->EKey, pFileEntry->EKey);
pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64;
pCKeyEntry->TagBitMask = 0;
pCKeyEntry->ContentSize = ConvertBytesToInteger_4(pFileEntry->ContentSize);
pCKeyEntry->EncodedSize = CASC_INVALID_SIZE;
pCKeyEntry->Flags = CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_IN_ENCODING;
pCKeyEntry->RefCount = 0;
pCKeyEntry->SpanCount = 1;
pCKeyEntry->Priority = 0;
// Insert the item into both maps
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
}
else
{
// Supply both CKey and EKey. Rewrite EKey regardless, because ENCODING manifest contains a full one
CopyMemory16(pCKeyEntry->CKey, pFileEntry->CKey);
CopyMemory16(pCKeyEntry->EKey, pFileEntry->EKey);
// Supply the content size
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
pCKeyEntry->ContentSize = ConvertBytesToInteger_4(pFileEntry->ContentSize);
pCKeyEntry->Flags |= CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_IN_ENCODING;
pCKeyEntry->Flags &= ~CASC_CE_HAS_EKEY_PARTIAL;
// Insert the item into CKey map
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
}
return pCKeyEntry;
}
// Inserts an entry from DOWNLOAD
static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, CASC_DOWNLOAD_ENTRY & DlEntry)
{
PCASC_CKEY_ENTRY pCKeyEntry;
// Check whether the entry is already there
if((pCKeyEntry = FindCKeyEntry_EKey(hs, DlEntry.EKey)) == NULL)
{
// Insert dummy CKey entry to the array. DO NOT allow to enlarge the array
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false);
if(pCKeyEntry == NULL)
return NULL;
// Copy the entry
ZeroMemory16(pCKeyEntry->CKey);
CopyMemory16(pCKeyEntry->EKey, DlEntry.EKey);
pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64;
pCKeyEntry->TagBitMask = 0;
pCKeyEntry->ContentSize = CASC_INVALID_SIZE;
pCKeyEntry->EncodedSize = (DWORD)DlEntry.EncodedSize;
pCKeyEntry->Flags = CASC_CE_HAS_EKEY | CASC_CE_IN_DOWNLOAD;
pCKeyEntry->RefCount = 0;
pCKeyEntry->SpanCount = 1;
// Insert the entry to the map. Only insert it to the EKey map, as there is no CKey present
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
}
else
{
// Copy the EKey if we only have partial one
if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY_PARTIAL)
CopyMemory16(pCKeyEntry->EKey, DlEntry.EKey);
// Supply the encoded size, if unknown yet
if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE)
pCKeyEntry->EncodedSize = (DWORD)DlEntry.EncodedSize;
pCKeyEntry->Flags = (pCKeyEntry->Flags & ~CASC_CE_HAS_EKEY_PARTIAL) | CASC_CE_IN_DOWNLOAD;
}
// Supply the rest
pCKeyEntry->Priority = DlEntry.Priority;
return pCKeyEntry;
}
static DWORD CopyBuildFileItemsToCKeyArray(TCascStorage * hs)
{
// Insert the well-known files
// InsertCKeyEntry(hs, hs->EncodingCKey);
InsertCKeyEntry(hs, hs->DownloadCKey);
InsertCKeyEntry(hs, hs->InstallCKey);
InsertCKeyEntry(hs, hs->PatchFile);
InsertCKeyEntry(hs, hs->RootFile);
InsertCKeyEntry(hs, hs->SizeFile);
InsertCKeyEntry(hs, hs->VfsRoot);
// Insert all VFS roots
for(size_t i = 0; i < hs->VfsRootList.ItemCount(); i++)
{
PCASC_CKEY_ENTRY pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i);
InsertCKeyEntry(hs, *pCKeyEntry);
}
return ERROR_SUCCESS;
}
// Estimate the total number of files, so we won't have to re-allocate the array
static size_t GetEstimatedNumberOfFiles(TCascStorage * hs)
{
// If we know the size of DOWNLOAD at this point, we estimate number of files from it.
// Size of one entry in DOWNLOAD is at least 26 bytes. This is the most reliable method.
// However, for some online storages ("agent"), this is a very small value
if(hs->DownloadCKey.ContentSize != CASC_INVALID_SIZE)
return (hs->DownloadCKey.ContentSize / 26) + CASC_MAX_EXTRA_ITEMS;
// If we know the size of ENCODING at this point, we estimate number of files from it.
// Size of one entry in ENCODING is at least 38 bytes. This method fails on storages
// with TVFS file system, as ENCODING only contains a small subset of file.
// Fortunately, all known TVFS-based storages have "download-size" present
if(hs->EncodingCKey.ContentSize != CASC_INVALID_SIZE)
return (hs->EncodingCKey.ContentSize / 26) + CASC_MAX_EXTRA_ITEMS;
// By default, it's gonna be half a million, which is the maximum observed number of files
// for all older storages (HOTS before 39445, WoW before 19116)
return 500000;
}
static DWORD InitCKeyArray(TCascStorage * hs)
{
size_t nNumberOfFiles = GetEstimatedNumberOfFiles(hs);
DWORD dwErrCode;
//
// Allocate array and map of CKey entries
//
// Create the array of CKey items
nError = hs->CKeyArray.Create(sizeof(CASC_CKEY_ENTRY), nEstimatedEntries);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = hs->CKeyArray.Create(sizeof(CASC_CKEY_ENTRY), nNumberOfFiles);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Create the map CKey -> CASC_CKEY_ENTRY
nError = hs->CKeyMap.Create(nEstimatedEntries, EnHeader.CKeyLength, FIELD_OFFSET(CASC_CKEY_ENTRY, CKey));
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = hs->CKeyMap.Create(nNumberOfFiles, MD5_HASH_SIZE, FIELD_OFFSET(CASC_CKEY_ENTRY, CKey));
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Create the map EKey -> CASC_CKEY_ENTRY
nError = hs->EKeyMap.Create(nEstimatedEntries, hs->EKeyLength, FIELD_OFFSET(CASC_CKEY_ENTRY, EKey));
if(nError != ERROR_SUCCESS)
return nError;
// Create the map CKey -> CASC_CKEY_ENTRY. Note that TVFS root references files
// using 9-byte EKey, so cut the search EKey length to 9 bytes
dwErrCode = hs->EKeyMap.Create(nNumberOfFiles, CASC_EKEY_SIZE, FIELD_OFFSET(CASC_CKEY_ENTRY, EKey));
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Insert the entry of ENCODING file. This is vital for its opening and loading
InsertCKeyEntry(hs, hs->EncodingCKey);
return ERROR_SUCCESS;
}
@@ -200,7 +355,6 @@ int CaptureEncodingHeader(CASC_ENCODING_HEADER & EnHeader, LPBYTE pbFileData, si
static int LoadEncodingCKeyPage(TCascStorage * hs, CASC_ENCODING_HEADER & EnHeader, LPBYTE pbPageBegin, LPBYTE pbEndOfPage)
{
PCASC_CKEY_ENTRY pCKeyEntry;
PFILE_CKEY_ENTRY pFileEntry;
LPBYTE pbFileEntry = pbPageBegin;
@@ -219,26 +373,10 @@ static int LoadEncodingCKeyPage(TCascStorage * hs, CASC_ENCODING_HEADER & EnHead
// Example of a file entry with multiple EKeys:
// Overwatch build 24919, CKey: 0e 90 94 fa d2 cb 85 ac d0 7c ea 09 f9 c5 ba 00
// BREAKIF(pFileEntry->EKeyCount > 1);
// BREAK_ON_XKEY3(pFileEntry->EKey, 0x09, 0xF3, 0xCD);
// Insert the CKey entry into the array
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1);
if(pCKeyEntry != NULL)
{
// Supply both CKey and EKey. Rewrite EKey regardless, because ENCODING manifest contains a full one
CopyMemory16(pCKeyEntry->CKey, pFileEntry->CKey);
CopyMemory16(pCKeyEntry->EKey, pFileEntry->EKey);
pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64;
pCKeyEntry->TagBitMask = 0;
pCKeyEntry->EncodedSize = CASC_INVALID_SIZE;
pCKeyEntry->ContentSize = ConvertBytesToInteger_4(pFileEntry->ContentSize);
pCKeyEntry->RefCount = 0;
pCKeyEntry->Priority = 0;
pCKeyEntry->Flags = (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_IN_ENCODING);
// Insert the item into both maps
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
}
// Insert the entry to the central CKey table
InsertCKeyEntry(hs, pFileEntry);
// Move to the next encoding entry
pbFileEntry = pbFileEntry + 2 + 4 + EnHeader.CKeyLength + (pFileEntry->EKeyCount * EnHeader.EKeyLength);
@@ -246,245 +384,88 @@ static int LoadEncodingCKeyPage(TCascStorage * hs, CASC_ENCODING_HEADER & EnHead
return ERROR_SUCCESS;
}
static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pSourceEntry, bool bAllowOrphans, bool * pbAllocatedNewEntry)
{
PCASC_CKEY_ENTRY pCKeyEntry = NULL;
bool bAllocatedNewEntry = false;
if(pSourceEntry->Flags & CASC_CE_HAS_EKEY)
{
// If there is that item already, reuse it
pCKeyEntry = FindCKeyEntry_EKey(hs, pSourceEntry->EKey);
if(pCKeyEntry == NULL)
{
// Increment number of orphaned index entries
hs->OrphanItems++;
// Insert the orphan item only of they are allowed and if we won't overflow the array
if(bAllowOrphans && (hs->CKeyArray.ItemCount() + 1) < hs->CKeyArray.ItemCountMax())
{
// Insert a new entry to the array
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1);
if(pCKeyEntry != NULL)
{
// Copy CKey, EKey and some flags
if(pSourceEntry->Flags & CASC_CE_HAS_CKEY)
CopyMemory16(pCKeyEntry->CKey, pSourceEntry->CKey);
if(pSourceEntry->Flags & CASC_CE_HAS_EKEY)
CopyMemory16(pCKeyEntry->EKey, pSourceEntry->EKey);
pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64;
pCKeyEntry->TagBitMask = 0;
pCKeyEntry->RefCount = 0;
pCKeyEntry->Priority = 0;
pCKeyEntry->EncodedSize = CASC_INVALID_SIZE;
pCKeyEntry->ContentSize = CASC_INVALID_SIZE;
pCKeyEntry->Flags = (pSourceEntry->Flags & (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_HAS_EKEY_PARTIAL));
bAllocatedNewEntry = true;
}
}
else
{
hs->SkippedItems++;
}
}
}
if(pbAllocatedNewEntry != NULL)
pbAllocatedNewEntry[0] = bAllocatedNewEntry;
return pCKeyEntry;
}
static PCASC_CKEY_ENTRY CopyBuildFileItemToCKeyArray(TCascStorage * hs, PCASC_CKEY_ENTRY pSourceEntry)
{
PCASC_CKEY_ENTRY pCKeyEntry = NULL;
bool bAllocatedNewEntry = false;
pCKeyEntry = InsertCKeyEntry(hs, pSourceEntry, true, &bAllocatedNewEntry);
if(pCKeyEntry != NULL)
{
// Fill the values that might be known
if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE)
pCKeyEntry->EncodedSize = pSourceEntry->EncodedSize;
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
pCKeyEntry->ContentSize = pSourceEntry->ContentSize;
// If this is a new entry, we need to insert it to the maps
if(bAllocatedNewEntry)
{
if(pCKeyEntry->Flags & CASC_CE_HAS_CKEY)
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY)
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
}
}
return pCKeyEntry;
}
static int CopyBuildFileItemsToCKeyArray(TCascStorage * hs)
{
// Insert the well-known files
CopyBuildFileItemToCKeyArray(hs, &hs->EncodingCKey);
CopyBuildFileItemToCKeyArray(hs, &hs->DownloadCKey);
CopyBuildFileItemToCKeyArray(hs, &hs->InstallCKey);
CopyBuildFileItemToCKeyArray(hs, &hs->PatchFile);
CopyBuildFileItemToCKeyArray(hs, &hs->RootFile);
CopyBuildFileItemToCKeyArray(hs, &hs->SizeFile);
CopyBuildFileItemToCKeyArray(hs, &hs->VfsRoot);
// Insert all VFS roots
for(size_t i = 0; i < hs->VfsRootList.ItemCount(); i++)
{
PCASC_CKEY_ENTRY pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i);
CopyBuildFileItemToCKeyArray(hs, pCKeyEntry);
}
return ERROR_SUCCESS;
}
static int CopyIndexItemsToCKeyArray(TCascStorage * hs)
{
PCASC_CKEY_ENTRY pIndexEntry;
PCASC_CKEY_ENTRY pCKeyEntry;
size_t nItemCount = hs->IndexArray.ItemCount();
bool bAllocatedNewEntry = false;
// Iterate over all index items
for(size_t i = 0; i < nItemCount; i++)
{
// Get the n-th index entry
pIndexEntry = (PCASC_CKEY_ENTRY)hs->IndexArray.ItemAt(i);
// Sometimes, there are multiple items with the same EKey in the index files
// Example: "2018 - New CASC\00001", EKey 37 89 16 5b 2d cc 71 c1 25 00 00 00 00 00 00 00
// Positions: 0x2D, 0x2E, 0x2F
//BREAK_ON_XKEY3(pIndexEntry->EKey, 0x37, 0x89, 0x16);
// Copy the index entry to the central storage
if((pCKeyEntry = InsertCKeyEntry(hs, pIndexEntry, hs->bAllowOrphans, &bAllocatedNewEntry)) != NULL)
{
// Make sure that the CKey is zeroed when not present
if((pCKeyEntry->Flags & CASC_CE_HAS_CKEY) == 0)
ZeroMemory16(pCKeyEntry->CKey);
// Only copy the storage offset and sizes if not available yet
if(pCKeyEntry->StorageOffset == CASC_INVALID_OFFS64)
{
pCKeyEntry->StorageOffset = pIndexEntry->StorageOffset;
pCKeyEntry->EncodedSize = pIndexEntry->EncodedSize;
}
if(bAllocatedNewEntry)
{
if(pCKeyEntry->Flags & CASC_CE_HAS_CKEY)
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY)
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
}
// Mark the file as available locally
pCKeyEntry->Flags |= CASC_CE_FILE_IS_LOCAL;
}
}
// We free the index array at this point
hs->IndexArray.Free();
return ERROR_SUCCESS;
}
static int LoadEncodingManifest(TCascStorage * hs)
{
PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->EncodingCKey.CKey);
LPBYTE pbEncodingFile;
DWORD cbEncodingFile = 0;
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
// Inform the user about what we are doing
if(InvokeProgressCallback(hs, "Loading ENCODING manifest", NULL, 0, 0))
return ERROR_CANCELLED;
// Load the entire encoding file to memory
pbEncodingFile = LoadInternalFileToMemory(hs, &hs->EncodingCKey, &cbEncodingFile);
pbEncodingFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbEncodingFile);
if(pbEncodingFile != NULL && cbEncodingFile != 0)
{
CASC_ENCODING_HEADER EnHeader;
// Capture the header of the ENCODING file
nError = CaptureEncodingHeader(EnHeader, pbEncodingFile, cbEncodingFile);
if(nError == ERROR_SUCCESS)
dwErrCode = CaptureEncodingHeader(EnHeader, pbEncodingFile, cbEncodingFile);
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);
LPBYTE pbCKeyPage = (LPBYTE)(pPageHeader + EnHeader.CKeyPageCount);
// Since ENCODING contains the full list of all files (even those not downloaded),
// we can now make a fair estimate about how large maps shall we create.
// So, we can build the maps CKey and EKey map.
if((nError = CreateCKeyMaps(hs, EnHeader)) == ERROR_SUCCESS)
// Go through all CKey pages and verify them
for(DWORD i = 0; i < EnHeader.CKeyPageCount; i++)
{
// 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))
{
PFILE_CKEY_ENTRY pCKeyEntry = (PFILE_CKEY_ENTRY)pbCKeyPage;
// Check if there is enough space in the buffer
if((pbCKeyPage + EnHeader.CKeyPageSize) > (pbEncodingFile + cbEncodingFile))
{
nError = ERROR_FILE_CORRUPT;
break;
}
// Check the hash of the entire segment
// Note that verifying takes considerable time of the storage loading
// if(!VerifyDataBlockHash(pbCKeyPage, EnHeader.CKeyPageSize, pEncodingSegment->SegmentHash))
// {
// nError = ERROR_FILE_CORRUPT;
// break;
// }
// Check if the CKey matches with the expected first value
if(memcmp(pCKeyEntry->CKey, pPageHeader[i].FirstKey, CASC_CKEY_SIZE))
{
nError = ERROR_FILE_CORRUPT;
break;
}
// Load the entire page of CKey entries.
// This operation will never fail, because all memory is already pre-allocated
nError = LoadEncodingCKeyPage(hs, EnHeader, pbCKeyPage, pbCKeyPage + EnHeader.CKeyPageSize);
if(nError != ERROR_SUCCESS)
break;
// Move to the next CKey page
pbCKeyPage += EnHeader.CKeyPageSize;
dwErrCode = ERROR_FILE_CORRUPT;
break;
}
// Check the hash of the entire segment
// Note that verifying takes considerable time of the storage loading
// if(!VerifyDataBlockHash(pbCKeyPage, EnHeader.CKeyPageSize, pEncodingSegment->SegmentHash))
// {
// dwErrCode = ERROR_FILE_CORRUPT;
// break;
// }
// Check if the CKey matches with the expected first value
if(memcmp(((PFILE_CKEY_ENTRY)pbCKeyPage)->CKey, pPageHeader[i].FirstKey, MD5_HASH_SIZE))
{
dwErrCode = ERROR_FILE_CORRUPT;
break;
}
// Load the entire page of CKey entries.
// This operation will never fail, because all memory is already pre-allocated
dwErrCode = LoadEncodingCKeyPage(hs, EnHeader, pbCKeyPage, pbCKeyPage + EnHeader.CKeyPageSize);
if(dwErrCode != ERROR_SUCCESS)
break;
// Move to the next CKey page
pbCKeyPage += EnHeader.CKeyPageSize;
}
}
// All CKey->EKey entries from the text build files need to be copied to the CKey array
// This also includes the ENCODING file itself, which is vital for later loading
if(nError == ERROR_SUCCESS)
if(dwErrCode == ERROR_SUCCESS)
{
nError = CopyBuildFileItemsToCKeyArray(hs);
dwErrCode = CopyBuildFileItemsToCKeyArray(hs);
}
// Now supply all the entries from the index files
if(nError == ERROR_SUCCESS)
{
nError = CopyIndexItemsToCKeyArray(hs);
}
//if(dwErrCode == ERROR_SUCCESS)
//{
// dwErrCode = CopyIndexItemsToCKeyArray(hs);
//}
// Free the loaded ENCODING file
CASC_FREE(pbEncodingFile);
}
else
{
nError = GetLastError();
dwErrCode = GetLastError();
}
return nError;
return dwErrCode;
}
size_t GetTagBitmapLength(LPBYTE pbFilePtr, LPBYTE pbFileEnd, DWORD EntryCount)
@@ -615,7 +596,7 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
LPBYTE pbTag = pbTags;
size_t nMaxNameLength = 0;
size_t nTagEntryLengh = 0;
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
// Does the storage support tags?
if(DlHeader.TagCount != 0)
@@ -624,7 +605,7 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
hs->dwFeatures |= CASC_FEATURE_TAGS;
// Allocate space for the tag array
TagArray = CASC_ALLOC(CASC_TAG_ENTRY1, DlHeader.TagCount);
TagArray = CASC_ALLOC<CASC_TAG_ENTRY1>(DlHeader.TagCount);
if(TagArray != NULL)
{
// Get the longest tag name
@@ -640,8 +621,8 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
nTagEntryLengh = ALIGN_TO_SIZE(nTagEntryLengh, 8);
// Load the tags into array in the storage structure
nError = hs->TagsArray.Create(nTagEntryLengh, DlHeader.TagCount);
if(nError == ERROR_SUCCESS)
dwErrCode = hs->TagsArray.Create(nTagEntryLengh, DlHeader.TagCount);
if(dwErrCode == ERROR_SUCCESS)
{
// Convert the array of CASC_DOWNLOAD_TAG1 to array of CASC_DOWNLOAD_TAG2
for(DWORD i = 0; i < DlHeader.TagCount; i++)
@@ -653,7 +634,7 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
pTargetTag = (PCASC_TAG_ENTRY2)hs->TagsArray.Insert(1);
if(pTargetTag == NULL)
{
nError = ERROR_NOT_ENOUGH_MEMORY;
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
break;
}
@@ -667,7 +648,7 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
}
else
{
nError = ERROR_NOT_ENOUGH_MEMORY;
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
}
@@ -676,20 +657,21 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
{
CASC_DOWNLOAD_ENTRY DlEntry;
PCASC_CKEY_ENTRY pCKeyEntry;
ULONGLONG TagBit = 1;
size_t BitMaskOffset = (i / 8);
size_t TagItemCount = hs->TagsArray.ItemCount();
BYTE BitMaskBit = 0x80 >> (i % 8);
// Capture the download entry
if(CaptureDownloadEntry(DlHeader, DlEntry, pbEntry, pbFileEnd) != ERROR_SUCCESS)
break;
// Make sure we have the entry in CKey table
pCKeyEntry = FindCKeyEntry_EKey(hs, DlEntry.EKey);
if(pCKeyEntry != NULL)
{
ULONGLONG TagBit = 1;
size_t TagItemCount = hs->TagsArray.ItemCount();
// COD4: zone/base.xpak
//BREAK_ON_XKEY3(DlEntry.EKey, 0xa5, 0x00, 0x16);
// 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++)
{
@@ -700,17 +682,6 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
// Move to the next bit
TagBit <<= 1;
}
// If the EKey has partial EKey only, fix that
if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY_PARTIAL)
{
CopyMemory16(pCKeyEntry->EKey, DlEntry.EKey);
pCKeyEntry->Flags &= ~CASC_CE_HAS_EKEY_PARTIAL;
}
// Supply the priority
pCKeyEntry->Priority = DlEntry.Priority;
pCKeyEntry->Flags |= CASC_CE_IN_DOWNLOAD;
}
// Move to the next entry
@@ -722,7 +693,7 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
// Remember the total file count
hs->TotalFiles = hs->CKeyArray.ItemCount();
return nError;
return dwErrCode;
}
static int LoadDownloadManifest(TCascStorage * hs)
@@ -730,7 +701,7 @@ static int LoadDownloadManifest(TCascStorage * hs)
PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->DownloadCKey.CKey);
LPBYTE pbDownloadFile = NULL;
DWORD cbDownloadFile = 0;
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
// Inform the user about what we are doing
if(InvokeProgressCallback(hs, "Loading DOWNLOAD manifest", NULL, 0, 0))
@@ -743,11 +714,11 @@ static int LoadDownloadManifest(TCascStorage * hs)
CASC_DOWNLOAD_HEADER DlHeader;
// Capture the header of the DOWNLOAD file
nError = CaptureDownloadHeader(DlHeader, pbDownloadFile, cbDownloadFile);
if(nError == ERROR_SUCCESS)
dwErrCode = CaptureDownloadHeader(DlHeader, pbDownloadFile, cbDownloadFile);
if(dwErrCode == ERROR_SUCCESS)
{
// Parse the entire download manifest
nError = LoadDownloadManifest(hs, DlHeader, pbDownloadFile, pbDownloadFile + cbDownloadFile);
dwErrCode = LoadDownloadManifest(hs, DlHeader, pbDownloadFile, pbDownloadFile + cbDownloadFile);
}
// Free the loaded manifest
@@ -755,7 +726,7 @@ static int LoadDownloadManifest(TCascStorage * hs)
}
// If the DOWNLOAD manifest is not present, we won't abort the downloading process.
return nError;
return dwErrCode;
}
//-----------------------------------------------------------------------------
@@ -767,7 +738,7 @@ static int LoadInstallManifest(TCascStorage * hs)
PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->InstallCKey.CKey);
LPBYTE pbInstallFile = NULL;
DWORD cbInstallFile = 0;
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
// Inform the user about what we are doing
if(InvokeProgressCallback(hs, "Loading INSTALL manifest", NULL, 0, 0))
@@ -777,18 +748,18 @@ static int LoadInstallManifest(TCascStorage * hs)
pbInstallFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbInstallFile);
if (pbInstallFile != NULL && cbInstallFile != 0)
{
nError = RootHandler_CreateInstall(hs, pbInstallFile, cbInstallFile);
dwErrCode = RootHandler_CreateInstall(hs, pbInstallFile, cbInstallFile);
CASC_FREE(pbInstallFile);
}
else
{
nError = GetLastError();
dwErrCode = GetLastError();
}
return nError;
return dwErrCode;
}
static bool InsertWellKnownFile(TCascStorage * hs, const char * szFileName, CASC_CKEY_ENTRY & FakeCKeyEntry)
static bool InsertWellKnownFile(TCascStorage * hs, const char * szFileName, CASC_CKEY_ENTRY & FakeCKeyEntry, DWORD dwFlags = 0)
{
PCASC_CKEY_ENTRY pCKeyEntry = NULL;
@@ -799,9 +770,24 @@ static bool InsertWellKnownFile(TCascStorage * hs, const char * szFileName, CASC
pCKeyEntry = FindCKeyEntry_CKey(hs, FakeCKeyEntry.CKey);
if(pCKeyEntry != NULL)
{
// Insert the key to the root handler, unless it's already referenced by a name
if(pCKeyEntry->RefCount == 0)
hs->pRootHandler->Insert(szFileName, pCKeyEntry);
// Insert the key to the root handler. Note that the file can already be referenced
// ("index" vs "vfs-root" in Warcraft III storages)
hs->pRootHandler->Insert(szFileName, pCKeyEntry);
pCKeyEntry->Flags |= (CASC_CE_IN_BUILD | dwFlags);
return true;
}
}
// Special case: the PATCH file is usually not in any indices.
// It's also never locally available
if((dwFlags & CASC_CE_FILE_PATCH) && (hs->dwFeatures & CASC_FEATURE_ONLINE))
{
// Get or insert the PATCH entry
pCKeyEntry = InsertCKeyEntry(hs, FakeCKeyEntry);
if(pCKeyEntry != NULL)
{
hs->pRootHandler->Insert(szFileName, pCKeyEntry);
pCKeyEntry->Flags |= (CASC_CE_IN_BUILD | dwFlags);
return true;
}
}
@@ -815,16 +801,14 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
PDWORD FileSignature;
LPBYTE pbRootFile = NULL;
DWORD cbRootFile = 0;
int nError = ERROR_BAD_FORMAT;
DWORD dwErrCode = ERROR_BAD_FORMAT;
// Sanity checks
assert(hs->CKeyMap.IsInitialized() == true);
assert(hs->pRootHandler == NULL);
// Locale: The default parameter is 0 - in that case,
// we assign the default locale, loaded from the .build.info file
if(dwLocaleMask == 0)
dwLocaleMask = hs->dwDefaultLocale;
// Locale: The default parameter is 0 - in that case, we load all locales
dwLocaleMask = (dwLocaleMask != 0) ? dwLocaleMask : 0xFFFFFFFF;
// Prioritize the VFS root over legacy ROOT file
pCKeyEntry = (hs->VfsRoot.ContentSize != CASC_INVALID_SIZE) ? &hs->VfsRoot : &hs->RootFile;
@@ -846,19 +830,19 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
switch(FileSignature[0])
{
case CASC_MNDX_ROOT_SIGNATURE:
nError = RootHandler_CreateMNDX(hs, pbRootFile, cbRootFile);
dwErrCode = RootHandler_CreateMNDX(hs, pbRootFile, cbRootFile);
break;
case CASC_DIABLO3_ROOT_SIGNATURE:
nError = RootHandler_CreateDiablo3(hs, pbRootFile, cbRootFile);
dwErrCode = RootHandler_CreateDiablo3(hs, pbRootFile, cbRootFile);
break;
case CASC_TVFS_ROOT_SIGNATURE:
nError = RootHandler_CreateTVFS(hs, pbRootFile, cbRootFile);
dwErrCode = RootHandler_CreateTVFS(hs, pbRootFile, cbRootFile);
break;
case CASC_WOW82_ROOT_SIGNATURE:
nError = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask);
dwErrCode = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask);
break;
default:
@@ -868,13 +852,13 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
// If the format was not recognized, they need to return ERROR_BAD_FORMAT
//
nError = RootHandler_CreateOverwatch(hs, pbRootFile, cbRootFile);
if(nError == ERROR_BAD_FORMAT)
dwErrCode = RootHandler_CreateOverwatch(hs, pbRootFile, cbRootFile);
if(dwErrCode == ERROR_BAD_FORMAT)
{
nError = RootHandler_CreateStarcraft1(hs, pbRootFile, cbRootFile);
if(nError == ERROR_BAD_FORMAT)
dwErrCode = RootHandler_CreateStarcraft1(hs, pbRootFile, cbRootFile);
if(dwErrCode == ERROR_BAD_FORMAT)
{
nError = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask);
dwErrCode = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask);
}
}
break;
@@ -886,10 +870,10 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
}
else
{
nError = GetLastError();
dwErrCode = GetLastError();
}
return nError;
return dwErrCode;
}
static DWORD GetStorageTotalFileCount(TCascStorage * hs)
@@ -902,7 +886,7 @@ static DWORD GetStorageTotalFileCount(TCascStorage * hs)
{
if((pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.ItemAt(i)) != NULL)
{
if((pCKeyEntry->Flags & CASC_CE_FOLDER_ENTRY) == 0)
if(pCKeyEntry->IsFile())
{
// If there is zero or one file name reference, we count the item as one file.
// If there is more than 1 name reference, we count the file as many times as number of references
@@ -925,9 +909,13 @@ static bool GetStorageProduct(TCascStorage * hs, void * pvStorageInfo, size_t cb
pProductInfo = (PCASC_STORAGE_PRODUCT)ProbeOutputBuffer(pvStorageInfo, cbStorageInfo, sizeof(CASC_STORAGE_PRODUCT), pcbLengthNeeded);
if(pProductInfo != NULL)
{
pProductInfo->szProductName = hs->szProductName;
pProductInfo->dwBuildNumber = hs->dwBuildNumber;
pProductInfo->Product = hs->Product;
// Clear the entire structure
memset(pProductInfo, 0, sizeof(CASC_STORAGE_PRODUCT));
// Copy the product code name and build number
if(hs->szCodeName != NULL)
CascStrCopy(pProductInfo->szCodeName, _countof(pProductInfo->szCodeName), hs->szCodeName);
pProductInfo->BuildNumber = hs->dwBuildNumber;
}
return (pProductInfo != NULL);
@@ -1083,21 +1071,13 @@ static DWORD InitializeLocalDirectories(TCascStorage * hs, PCASC_OPEN_STORAGE_AR
static DWORD InitializeOnlineDirectories(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
{
LPCTSTR szLocalCache = pArgs->szLocalPath;
LPCTSTR szCodeName = pArgs->szCodeName;
// Create the root path
hs->szRootPath = CombinePath(szLocalCache, szCodeName);
hs->szRootPath = CascNewStr(pArgs->szLocalPath);
if (hs->szRootPath != NULL)
{
// Create the name of the build file
hs->szBuildFile = CombinePath(hs->szRootPath, _T("versions"));
if(hs->szBuildFile != NULL)
{
hs->BuildFileType = CascVersionsDb;
hs->dwFeatures |= CASC_FEATURE_ONLINE;
return ERROR_SUCCESS;
}
hs->BuildFileType = CascVersionsDb;
hs->dwFeatures |= CASC_FEATURE_ONLINE;
return ERROR_SUCCESS;
}
return ERROR_NOT_ENOUGH_MEMORY;
@@ -1131,7 +1111,7 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
// For online storages, we need to load CDN servers
if ((dwErrCode == ERROR_SUCCESS) && (hs->dwFeatures & CASC_FEATURE_ONLINE))
{
dwErrCode = LoadCdnsInfo(hs);
dwErrCode = LoadCdnsFile(hs);
}
// Now, load the main storage file ".build.info" (or ".build.db" in old storages)
@@ -1145,6 +1125,8 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
if (dwErrCode == ERROR_SUCCESS)
{
dwErrCode = LoadCdnConfigFile(hs);
if(dwErrCode != ERROR_SUCCESS && (hs->dwFeatures & CASC_FEATURE_ONLINE) == 0)
dwErrCode = ERROR_SUCCESS;
}
// Proceed with loading the CDN build file
@@ -1153,6 +1135,12 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
dwErrCode = LoadCdnBuildFile(hs);
}
// Create the central file array
if(dwErrCode == ERROR_SUCCESS)
{
dwErrCode = InitCKeyArray(hs);
}
// Load the index files. Store information from the index files to the CKeyArray.
if(dwErrCode == ERROR_SUCCESS)
{
@@ -1190,7 +1178,7 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
InsertWellKnownFile(hs, "ENCODING", hs->EncodingCKey);
InsertWellKnownFile(hs, "DOWNLOAD", hs->DownloadCKey);
InsertWellKnownFile(hs, "INSTALL", hs->InstallCKey);
InsertWellKnownFile(hs, "PATCH", hs->PatchFile);
InsertWellKnownFile(hs, "PATCH", hs->PatchFile, CASC_CE_FILE_PATCH);
InsertWellKnownFile(hs, "ROOT", hs->RootFile);
InsertWellKnownFile(hs, "SIZE", hs->SizeFile);
@@ -1267,7 +1255,6 @@ static LPTSTR ParseOpenParams(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs)
bool WINAPI CascOpenStorageEx(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs, bool bOnlineStorage, HANDLE * phStorage)
{
CASC_OPEN_STORAGE_ARGS LocalArgs = {sizeof(CASC_OPEN_STORAGE_ARGS)};
DWORD (*PfnInitDirs)(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs);
TCascStorage * hs;
LPTSTR szParamsCopy = NULL;
DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
@@ -1297,8 +1284,7 @@ bool WINAPI CascOpenStorageEx(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs, b
if((hs = new TCascStorage()) != NULL)
{
// Setup the directories
PfnInitDirs = (bOnlineStorage) ? InitializeOnlineDirectories : InitializeLocalDirectories;
dwErrCode = PfnInitDirs(hs, pArgs);
dwErrCode = (bOnlineStorage) ? InitializeOnlineDirectories(hs, pArgs) : InitializeLocalDirectories(hs, pArgs);
if(dwErrCode == ERROR_SUCCESS)
{
// Perform the entire storage loading

View File

@@ -87,6 +87,7 @@
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <cassert>
// Support for PowerPC on Max OS X
@@ -161,6 +162,8 @@
typedef int LONG;
typedef unsigned int DWORD;
typedef long long LONGLONG;
typedef signed long long LONGLONG;
typedef signed long long *PLONGLONG;
typedef unsigned long long ULONGLONG;
typedef unsigned long long *PULONGLONG;
typedef void * HANDLE;
@@ -303,6 +306,27 @@
#define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b))
#endif
//-----------------------------------------------------------------------------
// Interlocked operations
inline DWORD CascInterlockedIncrement(PDWORD PtrValue)
{
#ifdef PLATFORM_WINDOWS
return (DWORD)InterlockedIncrement((LONG *)(PtrValue));
#else
return ++PtrValue[0];
#endif
}
inline DWORD CascInterlockedDecrement(PDWORD PtrValue)
{
#ifdef PLATFORM_WINDOWS
return (DWORD)InterlockedIncrement((LONG *)(PtrValue));
#else
return --PtrValue[0];
#endif
}
//-----------------------------------------------------------------------------
// Forbidden functions, do not use

File diff suppressed because it is too large Load Diff

View File

@@ -195,33 +195,6 @@ struct TDiabloRoot : public TFileTreeRoot
FreeLoadingStuff();
}
void AppendBackslashToTotalPath(PATH_BUFFER & PathBuffer)
{
if(PathBuffer.szPtr < PathBuffer.szEnd)
{
PathBuffer.szPtr[0] = '\\';
PathBuffer.szPtr++;
}
}
void AppendPathToTotalPath(PATH_BUFFER & PathBuffer, const char * szFileName, const char * szFileEnd)
{
char * szPathPtr = PathBuffer.szPtr;
size_t nLength = (szFileEnd - szFileName);
// Append the name
if((szPathPtr + nLength) < PathBuffer.szEnd)
{
memcpy(szPathPtr, szFileName, nLength);
szPathPtr += nLength;
}
// Append zero terminator
if(szPathPtr < PathBuffer.szEnd)
szPathPtr[0] = 0;
PathBuffer.szPtr = szPathPtr;
}
PDIABLO3_ASSET_INFO GetAssetInfo(DWORD dwAssetIndex)
{
if(dwAssetIndex < DIABLO3_ASSET_COUNT && Assets[dwAssetIndex].szDirectoryName != NULL)
@@ -404,52 +377,28 @@ struct TDiabloRoot : public TFileTreeRoot
}
bool CreateAssetFileName(
PATH_BUFFER & PathBuffer,
CASC_PATH<char> & PathBuffer,
DWORD FileIndex,
DWORD SubIndex)
{
PDIABLO3_CORE_TOC_ENTRY pTocEntry;
PDIABLO3_ASSET_INFO pAssetInfo;
const char * szPackageName = NULL;
const char * szPlainName;
const char * szFormat;
char * szPathEnd = PathBuffer.szEnd;
char * szPathPtr = PathBuffer.szPtr;
size_t nLength = 0;
LPCSTR szPackageName = NULL;
LPCSTR szPlainName;
LPCSTR szFormat;
char szBuffer[MAX_PATH];
// Find and check the entry
pTocEntry = pFileIndices + FileIndex;
if(pTocEntry->FileIndex == FileIndex)
{
// Retrieve the asset information
szPlainName = (LPCSTR)(pbCoreTocData + pTocEntry->NameOffset);
pAssetInfo = GetAssetInfo(pTocEntry->AssetIndex);
// Either use the asset info for getting the folder name or supply "Asset##"
if(pAssetInfo != NULL)
{
CascStrCopy(szPathPtr, (szPathEnd - szPathPtr), pAssetInfo->szDirectoryName);
szPathPtr += strlen(szPathPtr);
}
else
{
szPathPtr[0] = 'A';
szPathPtr[1] = 's';
szPathPtr[2] = 's';
szPathPtr[3] = 'e';
szPathPtr[4] = 't';
szPathPtr[5] = (char)('0' + (pTocEntry->AssetIndex / 10));
szPathPtr[6] = (char)('0' + (pTocEntry->AssetIndex % 10));
szPathPtr += 7;
}
// Put the backslash
if(szPathPtr < PathBuffer.szEnd)
*szPathPtr++ = '\\';
// Construct the file name, up to the extension. Don't include the '.'
szPlainName = (const char *)(pbCoreTocData + pTocEntry->NameOffset);
szFormat = (SubIndex != CASC_INVALID_INDEX) ? "%s\\%04u" : "%s";
nLength = CascStrPrintf(szPathPtr, (szPathEnd - szPathPtr), szFormat, szPlainName, SubIndex);
CascStrPrintf(szBuffer, _countof(szBuffer), szFormat, szPlainName, SubIndex);
// Try to fixup the file extension from the package name.
// File extensions are not predictable because for subitems,
@@ -466,27 +415,41 @@ struct TDiabloRoot : public TFileTreeRoot
// We use the Base\Data_D3\PC\Misc\Packages.dat for real file extensions, where possible
//
if(PackagesMap.IsInitialized() && pAssetInfo != NULL)
if(pAssetInfo != NULL)
{
// Retrieve the asset name
szPackageName = FindPackageName(pAssetInfo->szDirectoryName, szPathPtr);
szPackageName = FindPackageName(pAssetInfo->szDirectoryName, szBuffer);
if(szPackageName != NULL)
{
CascStrCopy(PathBuffer.szPtr, (PathBuffer.szEnd - PathBuffer.szPtr), szPackageName);
PathBuffer.AppendString(szPackageName, false);
return true;
}
// Append the directory name
PathBuffer.AppendString(pAssetInfo->szDirectoryName, false);
}
else
{
// Append generic name "Asset##" and continue
PathBuffer.AppendString("Asset", false);
PathBuffer.AppendChar((char)('0' + (pTocEntry->AssetIndex / 10)));
PathBuffer.AppendChar((char)('0' + (pTocEntry->AssetIndex % 10)));
}
// Use the extension from the AssetInfo, if we have any
// Append the content of the buffer
PathBuffer.AppendString(szBuffer, true);
// If we have an extension, use it. Otherwise, supply "a##"
if(pAssetInfo != NULL && pAssetInfo->szExtension != NULL)
{
szPathPtr[nLength++] = '.';
CascStrCopy(szPathPtr + nLength, (szPathEnd - (szPathPtr + nLength)), pAssetInfo->szExtension);
return true;
PathBuffer.AppendChar('.');
PathBuffer.AppendString(pAssetInfo->szExtension, false);
}
else
{
CascStrPrintf(szBuffer, _countof(szBuffer), ".a%02u", pTocEntry->AssetIndex);
PathBuffer.AppendString(szBuffer, false);
}
// Otherwise, supply "a##"
CascStrPrintf(szPathPtr + nLength, (szPathEnd - (szPathPtr + nLength)), ".a%02u", pTocEntry->AssetIndex);
return true;
}
@@ -494,13 +457,14 @@ struct TDiabloRoot : public TFileTreeRoot
}
// Parse the asset entries
int ParseAssetEntries(
DWORD ParseAssetEntries(
TCascStorage * hs,
DIABLO3_DIRECTORY & Directory,
PATH_BUFFER & PathBuffer)
CASC_PATH<char> & PathBuffer)
{
PDIABLO3_ASSET_ENTRY pEntry = (PDIABLO3_ASSET_ENTRY)Directory.pbAssetEntries;
PCASC_CKEY_ENTRY pCKeyEntry;
size_t nSavePos = PathBuffer.Save();
DWORD dwEntries = Directory.dwAssetEntries;
// Do nothing if there is no entries
@@ -516,8 +480,11 @@ struct TDiabloRoot : public TFileTreeRoot
if(CreateAssetFileName(PathBuffer, pEntry->FileIndex, CASC_INVALID_INDEX))
{
// Insert the entry to the file tree
FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
FileTree.InsertByName(pCKeyEntry, PathBuffer);
}
// Restore the path buffer position
PathBuffer.Restore(nSavePos);
}
}
}
@@ -525,13 +492,14 @@ struct TDiabloRoot : public TFileTreeRoot
return ERROR_SUCCESS;
}
int ParseAssetAndIdxEntries(
DWORD ParseAssetAndIdxEntries(
TCascStorage * hs,
DIABLO3_DIRECTORY & Directory,
PATH_BUFFER & PathBuffer)
CASC_PATH<char> & PathBuffer)
{
PDIABLO3_ASSETIDX_ENTRY pEntry = (PDIABLO3_ASSETIDX_ENTRY)Directory.pbAssetIdxEntries;
PCASC_CKEY_ENTRY pCKeyEntry;
size_t nSavePos = PathBuffer.Save();
DWORD dwEntries = Directory.dwAssetIdxEntries;
// Do nothing if there is no entries
@@ -548,8 +516,11 @@ struct TDiabloRoot : public TFileTreeRoot
{
// Insert the entry to the file tree
// fprintf(fp, "%08u %04u %s\n", pEntry->FileIndex, pEntry->SubIndex, PathBuffer.szBegin);
FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
FileTree.InsertByName(pCKeyEntry, PathBuffer);
}
// Restore the path buffer position
PathBuffer.Restore(nSavePos);
}
}
}
@@ -558,16 +529,16 @@ struct TDiabloRoot : public TFileTreeRoot
}
// Parse the named entries of all folders
int ParseDirectory_Phase1(
DWORD ParseDirectory_Phase1(
TCascStorage * hs,
DIABLO3_DIRECTORY & Directory,
PATH_BUFFER & PathBuffer,
CASC_PATH<char> & PathBuffer,
bool bIsRootDirectory)
{
DIABLO3_NAMED_ENTRY NamedEntry;
char * szSavePtr = PathBuffer.szPtr;
size_t nFolderIndex = 0;
int nError = ERROR_SUCCESS;
size_t nSavePos = PathBuffer.Save();
DWORD dwErrCode = ERROR_SUCCESS;
// Do nothing if there is no named headers
if(Directory.pbNamedEntries && Directory.dwNamedEntries)
@@ -587,33 +558,32 @@ struct TDiabloRoot : public TFileTreeRoot
return ERROR_BAD_FORMAT;
// Append the path fragment to the total path
AppendPathToTotalPath(PathBuffer, NamedEntry.szFileName, NamedEntry.szFileEnd);
PathBuffer.AppendStringN(NamedEntry.szFileName, (NamedEntry.szFileEnd - NamedEntry.szFileName), true);
// Check whether the file exists in the storage
pCKeyEntry = FindCKeyEntry_CKey(hs, NamedEntry.pCKey->Value);
if(pCKeyEntry != NULL)
{
// Create file node belonging to this folder
pFileNode = FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
pFileNode = FileTree.InsertByName(pCKeyEntry, PathBuffer);
dwNodeIndex = (DWORD)FileTree.IndexOf(pFileNode);
// If we are parsing root folder, we also need to load the data of the sub-folder file
if(bIsRootDirectory)
{
// Mark the node as directory
AppendBackslashToTotalPath(PathBuffer);
pCKeyEntry->Flags |= CASC_CE_FOLDER_ENTRY;
pFileNode->Flags |= CFN_FLAG_FOLDER;
// Load the sub-directory file
nError = LoadDirectoryFile(hs, RootFolders[nFolderIndex], pCKeyEntry);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = LoadDirectoryFile(hs, RootFolders[nFolderIndex], pCKeyEntry);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Parse the sub-directory file
nError = ParseDirectory_Phase1(hs, RootFolders[nFolderIndex], PathBuffer, false);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = ParseDirectory_Phase1(hs, RootFolders[nFolderIndex], PathBuffer, false);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Also save the item pointer and increment the folder index
RootFolders[nFolderIndex].dwNodeIndex = dwNodeIndex;
@@ -621,20 +591,19 @@ struct TDiabloRoot : public TFileTreeRoot
}
// Restore the path pointer
PathBuffer.szPtr = szSavePtr;
szSavePtr[0] = 0;
PathBuffer.Restore(nSavePos);
}
}
}
return nError;
return dwErrCode;
}
// Parse the nameless entries of all folders
int ParseDirectory_Phase2(TCascStorage * hs)
{
PATH_BUFFER PathBuffer;
char szPathBuffer[MAX_PATH];
CASC_PATH<char> PathBuffer;
char szBuffer[MAX_PATH];
// Parse each root subdirectory
for(size_t i = 0; i < DIABLO3_MAX_ROOT_FOLDERS; i++)
@@ -642,16 +611,11 @@ struct TDiabloRoot : public TFileTreeRoot
// Is this root folder loaded?
if(RootFolders[i].pbDirectoryData != NULL)
{
PathBuffer.szBegin = szPathBuffer;
PathBuffer.szPtr = szPathBuffer;
PathBuffer.szEnd = szPathBuffer + MAX_PATH - 1;
szPathBuffer[0] = 0;
// Retrieve the parent name
if(RootFolders[i].dwNodeIndex != 0)
{
FileTree.PathAt(szPathBuffer, MAX_PATH, RootFolders[i].dwNodeIndex);
PathBuffer.szPtr = PathBuffer.szBegin + strlen(szPathBuffer);
FileTree.PathAt(szBuffer, _countof(szBuffer), RootFolders[i].dwNodeIndex);
PathBuffer.SetPathRoot(szBuffer);
}
// Array of DIABLO3_ASSET_ENTRY entries.
@@ -671,12 +635,12 @@ struct TDiabloRoot : public TFileTreeRoot
// Creates an array of DIABLO3_CORE_TOC_ENTRY entries indexed by FileIndex
// Used as lookup table when we have FileIndex and need Asset+PlainName
int CreateMapOfFileIndices(TCascStorage * hs, const char * szFileName)
DWORD CreateMapOfFileIndices(TCascStorage * hs, const char * szFileName)
{
PDIABLO3_CORE_TOC_HEADER pTocHeader = NULL;
LPBYTE pbCoreTocPtr = pbCoreTocFile;
DWORD dwMaxFileIndex = 0;
int nError = ERROR_CAN_NOT_COMPLETE;
DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE;
// Load the entire file to memory
pbCoreTocFile = pbCoreTocPtr = LoadFileToMemory(hs, szFileName, &cbCoreTocFile);
@@ -693,7 +657,7 @@ struct TDiabloRoot : public TFileTreeRoot
return ERROR_SUCCESS;
// Allocate and populate the array of DIABLO3_CORE_TOC_ENTRYs
pFileIndices = CASC_ALLOC(DIABLO3_CORE_TOC_ENTRY, dwMaxFileIndex + 1);
pFileIndices = CASC_ALLOC<DIABLO3_CORE_TOC_ENTRY>(dwMaxFileIndex + 1);
if(pFileIndices != NULL)
{
// Initialize all entries to invalid
@@ -720,10 +684,10 @@ struct TDiabloRoot : public TFileTreeRoot
// Save the file to the root handler
pbCoreTocData = pbCoreTocPtr;
nFileIndices = dwMaxFileIndex;
nError = ERROR_SUCCESS;
dwErrCode = ERROR_SUCCESS;
}
}
return nError;
return dwErrCode;
}
// Packages.dat contains a list of full file names (without locale prefix).
@@ -740,19 +704,7 @@ struct TDiabloRoot : public TFileTreeRoot
{
LPBYTE pbPackagesPtr = pbPackagesDat;
LPBYTE pbPackagesEnd = pbPackagesDat + cbPackagesDat;
/*
LPBYTE pbPackagesPtr = pbPackagesDat + 8;
FILE * fp = fopen("E:\\Packages.dat", "wt");
if(fp != NULL)
{
while(pbPackagesPtr < pbPackagesEnd)
{
fprintf(fp, "%s\n", pbPackagesPtr);
pbPackagesPtr = pbPackagesPtr + strlen((char *)pbPackagesPtr) + 1;
}
fclose(fp);
}
*/
// Get the header. There is just Signature + NumberOfNames
if((pbPackagesPtr = CaptureInteger32(pbPackagesPtr, pbPackagesEnd, &Signature)) == NULL)
return ERROR_BAD_FORMAT;
@@ -783,27 +735,20 @@ struct TDiabloRoot : public TFileTreeRoot
return ERROR_SUCCESS;
}
int Load(TCascStorage * hs, DIABLO3_DIRECTORY & RootDirectory)
DWORD Load(TCascStorage * hs, DIABLO3_DIRECTORY & RootDirectory)
{
PATH_BUFFER PathBuffer;
char szPathBuffer[MAX_PATH];
int nError;
// Initialize path buffer and go parse the directory
PathBuffer.szBegin = szPathBuffer;
PathBuffer.szPtr = szPathBuffer;
PathBuffer.szEnd = szPathBuffer + MAX_PATH;
szPathBuffer[0] = 0;
CASC_PATH<char> PathBuffer;
DWORD dwErrCode;
// Always parse the named entries first. They always point to a file.
// These are entries with arbitrary names, and they do not belong to an asset
nError = ParseDirectory_Phase1(hs, RootDirectory, PathBuffer, true);
if(nError == ERROR_SUCCESS)
dwErrCode = ParseDirectory_Phase1(hs, RootDirectory, PathBuffer, true);
if(dwErrCode == ERROR_SUCCESS)
{
// The asset entries in the ROOT file don't contain file names, but indices.
// To convert a file index to a file name, we need to load and parse the "Base\\CoreTOC.dat" file.
nError = CreateMapOfFileIndices(hs, "Base\\CoreTOC.dat");
if(nError == ERROR_SUCCESS)
dwErrCode = CreateMapOfFileIndices(hs, "Base\\CoreTOC.dat");
if(dwErrCode == ERROR_SUCCESS)
{
// The file "Base\Data_D3\PC\Misc\Packages.dat" contains the file names
// (without level-0 and level-1 directory).
@@ -818,7 +763,7 @@ struct TDiabloRoot : public TFileTreeRoot
FreeLoadingStuff();
}
return nError;
return dwErrCode;
}
void FreeLoadingStuff()
@@ -860,11 +805,11 @@ struct TDiabloRoot : public TFileTreeRoot
//-----------------------------------------------------------------------------
// Public functions
int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
DWORD RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
{
TDiabloRoot * pRootHandler = NULL;
DIABLO3_DIRECTORY RootDirectory;
int nError = ERROR_BAD_FORMAT;
DWORD dwErrCode = ERROR_BAD_FORMAT;
// Verify the header of the ROOT file
if(TDiabloRoot::CaptureDirectoryData(RootDirectory, pbRootFile, cbRootFile) != NULL)
@@ -874,8 +819,8 @@ int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRoot
if(pRootHandler != NULL)
{
// Load the root directory. If load failed, we free the object
nError = pRootHandler->Load(hs, RootDirectory);
if(nError != ERROR_SUCCESS)
dwErrCode = pRootHandler->Load(hs, RootDirectory);
if(dwErrCode != ERROR_SUCCESS)
{
delete pRootHandler;
pRootHandler = NULL;
@@ -885,5 +830,5 @@ int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRoot
// Assign the root directory (or NULL) and return error
hs->pRootHandler = pRootHandler;
return nError;
return dwErrCode;
}

View File

@@ -25,30 +25,7 @@ struct TRootHandler_Install : public TFileTreeRoot
dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY);
}
static int CaptureInstallHeader(CASC_INSTALL_HEADER & InHeader, LPBYTE pbFileData, size_t cbFileData)
{
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)
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)
return ERROR_BAD_FORMAT;
// Capture the header version 1
memset(&InHeader, 0, sizeof(CASC_INSTALL_HEADER));
InHeader.Magic = pFileHeader->Magic;
InHeader.Version = pFileHeader->Version;
InHeader.EKeyLength = pFileHeader->EKeyLength;
InHeader.TagCount = ConvertBytesToInteger_2(pFileHeader->TagCount);
InHeader.EntryCount = ConvertBytesToInteger_4(pFileHeader->EntryCount);
InHeader.HeaderLength = sizeof(FILE_INSTALL_HEADER);
return ERROR_SUCCESS;
}
int Load(TCascStorage * hs, CASC_INSTALL_HEADER InHeader, LPBYTE pbInstallFile, LPBYTE pbInstallEnd)
DWORD Load(TCascStorage * hs, CASC_INSTALL_HEADER InHeader, LPBYTE pbInstallFile, LPBYTE pbInstallEnd)
{
PCASC_CKEY_ENTRY pCKeyEntry;
const char * szString;
@@ -90,32 +67,48 @@ struct TRootHandler_Install : public TFileTreeRoot
//-----------------------------------------------------------------------------
// Public functions
//
// Starcraft ROOT file is a text file with the following format:
// HD2/portraits/NBluCrit/NLCFID01.webm|c2795b120592355d45eba9cdc37f691e
// locales/enUS/Assets/campaign/EXPZerg/Zerg08/staredit/wav/zovtra01.ogg|316b0274bf2dabaa8db60c3ff1270c85
// locales/zhCN/Assets/sound/terran/ghost/tghdth01.wav|6637ed776bd22089e083b8b0b2c0374c
//
DWORD CaptureInstallHeader(CASC_INSTALL_HEADER & InHeader, LPBYTE pbFileData, size_t cbFileData)
{
PFILE_INSTALL_HEADER pFileHeader = (PFILE_INSTALL_HEADER)pbFileData;
int RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbInstallFile, DWORD cbInstallFile)
// Check the signature ('DL') and version
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)
return ERROR_BAD_FORMAT;
// Capture the header version 1
memset(&InHeader, 0, sizeof(CASC_INSTALL_HEADER));
InHeader.Magic = pFileHeader->Magic;
InHeader.Version = pFileHeader->Version;
InHeader.EKeyLength = pFileHeader->EKeyLength;
InHeader.TagCount = ConvertBytesToInteger_2(pFileHeader->TagCount);
InHeader.EntryCount = ConvertBytesToInteger_4(pFileHeader->EntryCount);
InHeader.HeaderLength = sizeof(FILE_INSTALL_HEADER);
return ERROR_SUCCESS;
}
DWORD RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbInstallFile, DWORD cbInstallFile)
{
CASC_INSTALL_HEADER InHeader;
TRootHandler_Install * pRootHandler = NULL;
int nError = ERROR_BAD_FORMAT;
DWORD dwErrCode = ERROR_BAD_FORMAT;
// Capture the header of the DOWNLOAD file
nError = TRootHandler_Install::CaptureInstallHeader(InHeader, pbInstallFile, cbInstallFile);
if (nError == ERROR_SUCCESS)
dwErrCode = CaptureInstallHeader(InHeader, pbInstallFile, cbInstallFile);
if (dwErrCode == ERROR_SUCCESS)
{
// Allocate the root handler object
pRootHandler = new TRootHandler_Install();
if (pRootHandler != NULL)
{
// Parse the entire install manifest
nError = pRootHandler->Load(hs, InHeader, pbInstallFile, pbInstallFile + cbInstallFile);
dwErrCode = pRootHandler->Load(hs, InHeader, pbInstallFile, pbInstallFile + cbInstallFile);
hs->pRootHandler = pRootHandler;
}
}
return nError;
return dwErrCode;
}

View File

@@ -368,14 +368,14 @@ class TByteStream
// HOTS: 1957160 <DWORD>
template <typename T>
int GetValue(T & Value)
DWORD GetValue(T & Value)
{
T * Pointer;
int nError;
DWORD dwErrCode;
nError = GetBytes(sizeof(T), (LPBYTE *)(&Pointer));
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = GetBytes(sizeof(T), (LPBYTE *)(&Pointer));
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
Value = Pointer[0];
return ERROR_SUCCESS;
@@ -383,15 +383,15 @@ class TByteStream
// Retrieves the item count in the array
template <typename T>
int GetArrayItemCount(DWORD & ArraySize, DWORD & ItemCount)
DWORD GetArrayItemCount(DWORD & ArraySize, DWORD & ItemCount)
{
ULONGLONG ByteCount;
int nError;
DWORD dwErrCode;
// The first 8 bytes is the byte size of the array
nError = GetValue<ULONGLONG>(ByteCount);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = GetValue<ULONGLONG>(ByteCount);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Extract the number of bytes
if(ByteCount > 0xFFFFFFFF || (ByteCount % sizeof(T)) != 0)
@@ -408,9 +408,9 @@ class TByteStream
// HOTS: 1957230: <BYTE>
// HOTS: 1957280: <HASH_ENTRY>
template <typename T>
int GetArray(T ** Pointer, size_t ItemCount)
DWORD GetArray(T ** Pointer, size_t ItemCount)
{
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
// Verify parameters
if(Pointer == NULL && ItemCount != 0)
@@ -421,15 +421,15 @@ class TByteStream
// Allocate bytes for the array
if (Pointer != NULL)
{
Pointer[0] = CASC_ALLOC(T, ItemCount);
Pointer[0] = CASC_ALLOC<T>(ItemCount);
if (Pointer[0] == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// Get the pointer to the array
nError = CopyBytes(Pointer[0], sizeof(T) * ItemCount);
dwErrCode = CopyBytes(Pointer[0], sizeof(T) * ItemCount);
}
return nError;
return dwErrCode;
}
LPBYTE pbByteData;
@@ -488,7 +488,7 @@ class TGenericArray
T * NewArray;
// Allocate new data buffer
NewArray = CASC_ALLOC(T, NewMaxItemCount);
NewArray = CASC_ALLOC<T>(NewMaxItemCount);
if(NewArray != NULL)
{
// Copy the old items to the buffer
@@ -555,24 +555,24 @@ class TGenericArray
// HOTS: 1957700 <HASH_ENTRY>
// HOTS: 195A220 <char>
// HOTS: 1958580 <TBitStream, DWORD>
int LoadFromStream(TByteStream & InStream)
DWORD LoadFromStream(TByteStream & InStream)
{
DWORD NumberOfBytes;
int nError;
DWORD dwErrCode;
// Get and verify the number of items
nError = InStream.GetArrayItemCount<T>(NumberOfBytes, ItemCount);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = InStream.GetArrayItemCount<T>(NumberOfBytes, ItemCount);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Get the pointer to the array
nError = InStream.GetArray<T>(&ItemArray, ItemCount);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = InStream.GetArray<T>(&ItemArray, ItemCount);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
return SetArrayValid();
}
@@ -619,28 +619,28 @@ class TBitEntryArray : public TGenericArray<DWORD>
return dwResult & EntryBitMask;
}
int LoadBitsFromStream(TByteStream & InStream)
DWORD LoadBitsFromStream(TByteStream & InStream)
{
ULONGLONG Value64 = 0;
int nError;
DWORD dwErrCode;
nError = LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = LoadFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
nError = InStream.GetValue<DWORD>(BitsPerEntry);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = InStream.GetValue<DWORD>(BitsPerEntry);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
if(BitsPerEntry > 0x20)
return ERROR_BAD_FORMAT;
nError = InStream.GetValue<DWORD>(EntryBitMask);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = InStream.GetValue<DWORD>(EntryBitMask);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
nError = InStream.GetValue<ULONGLONG>(Value64);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = InStream.GetValue<ULONGLONG>(Value64);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
if(Value64 > 0xFFFFFFFF)
return ERROR_BAD_FORMAT;
TotalEntries = (DWORD)Value64;
@@ -687,39 +687,39 @@ class TSparseArray
}
// HOTS: 1958630
int LoadFromStream(TByteStream & InStream)
DWORD LoadFromStream(TByteStream & InStream)
{
DWORD total_count = 0;
DWORD valid_count = 0;
int nError;
DWORD dwErrCode;
nError = ItemBits.LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = ItemBits.LoadFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
nError = InStream.GetValue<DWORD>(total_count);
if(nError != ERROR_SUCCESS)
return nError;
nError = InStream.GetValue<DWORD>(valid_count);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = InStream.GetValue<DWORD>(total_count);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
dwErrCode = InStream.GetValue<DWORD>(valid_count);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
if(valid_count > total_count)
return ERROR_FILE_CORRUPT;
TotalItemCount = total_count;
ValidItemCount = valid_count;
nError = BaseVals.LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = BaseVals.LoadFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
nError = IndexToItem0.LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = IndexToItem0.LoadFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
nError = IndexToItem1.LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = IndexToItem1.LoadFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
return ERROR_SUCCESS;
}
@@ -1521,13 +1521,13 @@ class TPathFragmentTable
}
// HOTS: 0195A820
int LoadFromStream(TByteStream & InStream)
DWORD LoadFromStream(TByteStream & InStream)
{
int nError;
DWORD dwErrCode;
nError = PathFragments.LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = PathFragments.LoadFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
return PathMarks.LoadFromStream(InStream);
}
@@ -1606,10 +1606,10 @@ class TStruct10
}
// HOTS: 19572E0
int sub_19572E0(DWORD dwBitMask)
DWORD sub_19572E0(DWORD dwBitMask)
{
DWORD dwSubMask;
int nError;
DWORD dwErrCode;
if(dwBitMask & 0xFFF00000)
return ERROR_INVALID_PARAMETER;
@@ -1618,9 +1618,9 @@ class TStruct10
if(dwSubMask)
field_0 = dwSubMask;
nError = sub_1956FD0(dwBitMask);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = sub_1956FD0(dwBitMask);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
dwSubMask = dwBitMask & 0xF000;
if(dwSubMask == 0 || dwSubMask == 0x1000)
@@ -1718,32 +1718,32 @@ class TFileNameDatabase
}
// HOTS: 1956DA0
int Load(LPBYTE pbMarData, size_t cbMarData)
DWORD Load(LPBYTE pbMarData, size_t cbMarData)
{
TByteStream ByteStream;
DWORD dwSignature;
int nError;
DWORD dwErrCode;
if(pbMarData == NULL && cbMarData != 0)
return ERROR_INVALID_PARAMETER;
nError = ByteStream.SetByteBuffer(pbMarData, cbMarData);
if(nError == ERROR_SUCCESS)
dwErrCode = ByteStream.SetByteBuffer(pbMarData, cbMarData);
if(dwErrCode == ERROR_SUCCESS)
{
// Get pointer to MAR signature
nError = ByteStream.GetValue<DWORD>(dwSignature);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = ByteStream.GetValue<DWORD>(dwSignature);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Verify the signature
if(dwSignature != MNDX_MAR_SIGNATURE)
return ERROR_BAD_FORMAT;
// HOTS: 1956E11
nError = LoadFromStream(ByteStream);
dwErrCode = LoadFromStream(ByteStream);
}
return nError;
return dwErrCode;
}
// HOTS: 19584B0
@@ -2398,36 +2398,36 @@ class TFileNameDatabase
}
// HOTS: 1959790
int LoadFromStream(TByteStream & InStream)
DWORD LoadFromStream(TByteStream & InStream)
{
DWORD dwBitMask;
int nError;
DWORD dwErrCode;
nError = CollisionTable.LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = CollisionTable.LoadFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
nError = FileNameIndexes.LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = FileNameIndexes.LoadFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
nError = CollisionHiBitsIndexes.LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = CollisionHiBitsIndexes.LoadFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// HOTS: 019597CD
nError = LoBitsTable.LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = LoBitsTable.LoadFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
nError = HiBitsTable.LoadBitsFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = HiBitsTable.LoadBitsFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// HOTS: 019597F5
nError = PathFragmentTable.LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = PathFragmentTable.LoadFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// HOTS: 0195980A
if(CollisionHiBitsIndexes.ValidItemCount != 0 && PathFragmentTable.PathFragments.ItemCount == 0)
@@ -2438,29 +2438,29 @@ class TFileNameDatabase
if (pNewDB == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
nError = SetChildDatabase(pNewDB);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = SetChildDatabase(pNewDB);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
nError = pChildDB->LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = pChildDB->LoadFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
}
// HOTS: 0195986B
nError = HashTable.LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = HashTable.LoadFromStream(InStream);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
HashTableMask = HashTable.ItemCount - 1;
nError = InStream.GetValue<DWORD>(field_214);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = InStream.GetValue<DWORD>(field_214);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
nError = InStream.GetValue<DWORD>(dwBitMask);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = InStream.GetValue<DWORD>(dwBitMask);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
return Struct10.sub_1957800(dwBitMask);
}
@@ -2508,7 +2508,7 @@ class TMndxMarFile
int LoadRootData(FILE_MAR_INFO & MarInfo, LPBYTE pbRootFile, LPBYTE pbRootEnd)
{
// Allocate the MAR data
pbMarData = CASC_ALLOC(BYTE, MarInfo.MarDataSize);
pbMarData = CASC_ALLOC<BYTE>(MarInfo.MarDataSize);
cbMarData = MarInfo.MarDataSize;
if(pbMarData == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
@@ -2526,29 +2526,29 @@ class TMndxMarFile
}
// HOTS: 1956C60
int SearchFile(TMndxSearch * pSearch)
DWORD SearchFile(TMndxSearch * pSearch)
{
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
if(pDatabase == NULL)
return ERROR_INVALID_PARAMETER;
if(!pDatabase->FindFileInDatabase(pSearch))
nError = ERROR_FILE_NOT_FOUND;
dwErrCode = ERROR_FILE_NOT_FOUND;
return nError;
return dwErrCode;
}
// HOTS: 1956CE0
int DoSearch(TMndxSearch * pSearch, bool * pbFindResult)
DWORD DoSearch(TMndxSearch * pSearch, bool * pbFindResult)
{
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
if(pDatabase == NULL)
return ERROR_INVALID_PARAMETER;
*pbFindResult = pDatabase->DoSearch(pSearch);
return nError;
return dwErrCode;
}
// HOTS: 1956D20
@@ -2638,23 +2638,23 @@ struct TMndxHandler
return pbRootPtr + sizeof(FILE_MNDX_HEADER);
}
int LoadPackageNames()
DWORD LoadPackageNames()
{
TMndxMarFile * pMarFile = MndxInfo.MarFiles[MAR_PACKAGE_NAMES];
TMndxSearch Search;
PMNDX_PACKAGE pPackage;
size_t nPackageCount = 0x40;
bool bFindResult = false;
int nError;
DWORD dwErrCode;
// Prepare the file name search in the top level directory
Search.SetSearchMask("", 0);
// Allocate initial name list structure
pMarFile->GetFileNameCount(&nPackageCount);
nError = Packages.Create<MNDX_PACKAGE>(nPackageCount);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = Packages.Create<MNDX_PACKAGE>(nPackageCount);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Reset the package array
Packages.Reset();
@@ -2671,7 +2671,7 @@ struct TMndxHandler
assert(pPackage->szFileName == NULL);
// Allocate space for the file name
pPackage->szFileName = CASC_ALLOC(char, Search.cchFoundPath + 1);
pPackage->szFileName = CASC_ALLOC<char>(Search.cchFoundPath + 1);
if (pPackage->szFileName == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
@@ -2687,13 +2687,13 @@ struct TMndxHandler
return ERROR_SUCCESS;
}
int Load(const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd)
DWORD Load(const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd)
{
TMndxMarFile * pMarFile;
FILE_MAR_INFO MarInfo;
size_t nFilePointer = 0;
DWORD i;
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
// Copy the header into the MNDX info
MndxInfo.HeaderVersion = MndxHeader.HeaderVersion;
@@ -2728,13 +2728,13 @@ struct TMndxHandler
pMarFile = new TMndxMarFile();
if(pMarFile == NULL)
{
nError = ERROR_NOT_ENOUGH_MEMORY;
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Create the database from the MAR data
nError = pMarFile->LoadRootData(MarInfo, pbRootFile, pbRootEnd);
if(nError != ERROR_SUCCESS)
dwErrCode = pMarFile->LoadRootData(MarInfo, pbRootFile, pbRootEnd);
if(dwErrCode != ERROR_SUCCESS)
break;
// Assign the MAR file to the MNDX info structure
@@ -2743,23 +2743,23 @@ struct TMndxHandler
// All three MAR files must be loaded
// HOTS: 00E9503B
if(nError == ERROR_SUCCESS)
if(dwErrCode == ERROR_SUCCESS)
{
if(MndxInfo.MarFiles[MAR_PACKAGE_NAMES] == NULL || MndxInfo.MarFiles[MAR_STRIPPED_NAMES] == NULL || MndxInfo.MarFiles[MAR_FULL_NAMES] == NULL)
nError = ERROR_BAD_FORMAT;
dwErrCode = ERROR_BAD_FORMAT;
if(MndxInfo.CKeyEntrySize != sizeof(MNDX_CKEY_ENTRY))
nError = ERROR_BAD_FORMAT;
dwErrCode = ERROR_BAD_FORMAT;
}
// Load the array of Ckey entries. All present files are in the array,
// the same names (differentiated by package ID) are groupped together
if(nError == ERROR_SUCCESS)
if(dwErrCode == ERROR_SUCCESS)
{
size_t CKeyEntriesSize;
size_t FileNameCount = 0;
pMarFile = MndxInfo.MarFiles[MAR_STRIPPED_NAMES];
nError = ERROR_FILE_CORRUPT;
dwErrCode = ERROR_FILE_CORRUPT;
// Capture the array of CKey entries
if(pMarFile->GetFileNameCount(&FileNameCount) == ERROR_SUCCESS && FileNameCount == MndxInfo.FileNameCount)
@@ -2768,16 +2768,16 @@ struct TMndxHandler
if ((pbRootFile + MndxInfo.CKeyEntriesOffset + CKeyEntriesSize) <= pbRootEnd)
{
pCKeyEntries = (PMNDX_CKEY_ENTRY)(pbRootFile + MndxInfo.CKeyEntriesOffset);
nError = ERROR_SUCCESS;
dwErrCode = ERROR_SUCCESS;
}
}
}
// Pick the CKey entries that are the first with a given name
if(nError == ERROR_SUCCESS)
if(dwErrCode == ERROR_SUCCESS)
{
assert(MndxInfo.FileNameCount <= MndxInfo.CKeyEntriesCount);
FileNameIndexToCKeyIndex = CASC_ALLOC(PMNDX_CKEY_ENTRY, MndxInfo.FileNameCount + 1);
FileNameIndexToCKeyIndex = CASC_ALLOC<PMNDX_CKEY_ENTRY>(MndxInfo.FileNameCount + 1);
if(FileNameIndexToCKeyIndex != NULL)
{
PMNDX_CKEY_ENTRY pRootEntry = pCKeyEntries;
@@ -2800,22 +2800,22 @@ struct TMndxHandler
// Verify the final number of file names
if ((nFileNameIndex - 1) != MndxInfo.FileNameCount)
nError = ERROR_BAD_FORMAT;
dwErrCode = ERROR_BAD_FORMAT;
}
else
nError = ERROR_NOT_ENOUGH_MEMORY;
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
// Load the package names from the 0-th MAR file
if(nError == ERROR_SUCCESS)
if(dwErrCode == ERROR_SUCCESS)
{
nError = LoadPackageNames();
dwErrCode = LoadPackageNames();
}
return nError;
return dwErrCode;
}
int LoadFileNames(TCascStorage * hs, CASC_FILE_TREE & FileTree)
DWORD LoadFileNames(TCascStorage * hs, CASC_FILE_TREE & FileTree)
{
PCASC_CKEY_ENTRY pCKeyEntry;
PMNDX_CKEY_ENTRY pRootEntry;
@@ -2825,13 +2825,13 @@ struct TMndxHandler
TMndxSearch Search;
char szFileName[MAX_PATH];
bool bFindResult = false;
int nError;
DWORD dwErrCode;
// Setup the search mask
Search.SetSearchMask("", 0);
// Keep searching ad long as we found something
while ((nError = pMarFile->DoSearch(&Search, &bFindResult)) == ERROR_SUCCESS && bFindResult)
while ((dwErrCode = pMarFile->DoSearch(&Search, &bFindResult)) == ERROR_SUCCESS && bFindResult)
{
// Sanity check
assert(Search.cchFoundPath < MAX_PATH);
@@ -2874,7 +2874,7 @@ struct TMndxHandler
}
}
return nError;
return dwErrCode;
}
//
@@ -2931,32 +2931,32 @@ struct TRootHandler_MNDX : public TFileTreeRoot
dwFeatures |= CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY;
}
int Load(TCascStorage * hs, const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd)
DWORD Load(TCascStorage * hs, const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd)
{
TMndxHandler Handler;
int nError;
DWORD dwErrCode;
// Load and parse the entire MNDX structure
nError = Handler.Load(MndxHeader, pbRootFile, pbRootEnd);
if (nError == ERROR_SUCCESS)
dwErrCode = Handler.Load(MndxHeader, pbRootFile, pbRootEnd);
if (dwErrCode == ERROR_SUCCESS)
{
// Search all file names and insert them into the file tree
nError = Handler.LoadFileNames(hs, FileTree);
dwErrCode = Handler.LoadFileNames(hs, FileTree);
}
return nError;
return dwErrCode;
}
};
//-----------------------------------------------------------------------------
// Public functions - MNDX info
int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
DWORD RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
{
TRootHandler_MNDX * pRootHandler = NULL;
FILE_MNDX_HEADER MndxHeader;
LPBYTE pbRootEnd = pbRootFile + cbRootFile;
int nError = ERROR_BAD_FORMAT;
DWORD dwErrCode = ERROR_BAD_FORMAT;
// Verify the header of the ROOT file
if(TMndxHandler::CaptureRootHeader(MndxHeader, pbRootFile, pbRootEnd) != NULL)
@@ -2966,8 +2966,8 @@ int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFil
if(pRootHandler != NULL)
{
// Load the root directory. If load failed, we free the object
nError = pRootHandler->Load(hs, MndxHeader, pbRootFile, pbRootEnd);
if(nError != ERROR_SUCCESS)
dwErrCode = pRootHandler->Load(hs, MndxHeader, pbRootFile, pbRootEnd);
if(dwErrCode != ERROR_SUCCESS)
{
delete pRootHandler;
pRootHandler = NULL;
@@ -2977,5 +2977,5 @@ int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFil
// Assign the root directory (or NULL) and return error
hs->pRootHandler = pRootHandler;
return nError;
return dwErrCode;
}

View File

@@ -264,7 +264,7 @@ struct TApmFile
LPBYTE CaptureArrayOfEntries(LPBYTE pbArrayOfEntries, LPBYTE pbApmEnd)
{
// Allocate array of entries
pApmEntries = CASC_ALLOC(APM_ENTRY, EntryCount);
pApmEntries = CASC_ALLOC<APM_ENTRY>(EntryCount);
if(pApmEntries != NULL)
{
// The newest format
@@ -310,12 +310,9 @@ struct TApmFile
LPBYTE CapturePackageEntries(LPBYTE pbArrayOfEntries, LPBYTE pbApmEnd)
{
// Allocate array of entries
pApmPackages = CASC_ALLOC(APM_PACKAGE_ENTRY, PackageCount);
pApmPackages = CASC_ALLOC_ZERO<APM_PACKAGE_ENTRY>(PackageCount);
if(pApmPackages != NULL)
{
// Zero the entire array
memset(pApmPackages, 0, PackageCount * sizeof(APM_PACKAGE_ENTRY));
// The newest format
if(BuildNumber > 45104 && BuildNumber != 45214)
{
@@ -426,12 +423,12 @@ struct TRootHandler_OW : public TFileTreeRoot
return false;
}
int LoadApmFile(TCascStorage * hs, CONTENT_KEY & CKey, const char * szFileName)
DWORD LoadApmFile(TCascStorage * hs, CONTENT_KEY & CKey, const char * szFileName)
{
TApmFile ApmFile;
LPBYTE pbApmData;
DWORD cbApmData = 0;
int nError = ERROR_BAD_FORMAT;
DWORD dwErrCode = ERROR_BAD_FORMAT;
pbApmData = LoadInternalFileToMemory(hs, CKey.Value, CASC_OPEN_BY_CKEY, &cbApmData);
if(pbApmData != NULL)
@@ -456,16 +453,15 @@ struct TRootHandler_OW : public TFileTreeRoot
CASC_FREE(pbApmData);
}
return nError;
return dwErrCode;
}
static int LoadCmfFile(TCascStorage * hs, CONTENT_KEY & CKey, const char * szFileName)
static DWORD LoadCmfFile(TCascStorage * hs, CONTENT_KEY & CKey, const char * szFileName)
{
TCmfFile CmfFile;
LPBYTE pbCmfData;
DWORD cbCmfData = 0;
int nError = ERROR_BAD_FORMAT;
DWORD dwErrCode = ERROR_BAD_FORMAT;
pbCmfData = LoadInternalFileToMemory(hs, CKey.Value, CASC_OPEN_BY_CKEY, &cbCmfData);
if(pbCmfData != NULL)
@@ -484,7 +480,7 @@ struct TRootHandler_OW : public TFileTreeRoot
CASC_FREE(pbCmfData);
}
return nError;
return dwErrCode;
}
*/
int Load(TCascStorage * hs, CASC_CSV & Csv, size_t nFileNameIndex, size_t nCKeyIndex)
@@ -525,7 +521,7 @@ struct TRootHandler_OW : public TFileTreeRoot
}
/*
// Load all CMF+APM files
if(nError == ERROR_SUCCESS)
if(dwErrCode == ERROR_SUCCESS)
{
for(size_t i = 0; i < nApmFiles; i++)
{
@@ -548,8 +544,8 @@ struct TRootHandler_OW : public TFileTreeRoot
break;
// Create the map of CMF entries
nError = LoadCmfFile(hs, pFileNode2->CKey, szCmfFile);
if(nError != ERROR_SUCCESS)
dwErrCode = LoadCmfFile(hs, pFileNode2->CKey, szCmfFile);
if(dwErrCode != ERROR_SUCCESS)
break;
}
@@ -563,16 +559,16 @@ struct TRootHandler_OW : public TFileTreeRoot
// Public functions
// TODO: There is way more files in the Overwatch CASC storage than present in the ROOT file.
int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
DWORD RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
{
TRootHandler_OW * pRootHandler = NULL;
CASC_CSV Csv(0, true);
size_t Indices[2];
int nError;
DWORD dwErrCode;
// Load the ROOT file
nError = Csv.Load(pbRootFile, cbRootFile);
if(nError == ERROR_SUCCESS)
dwErrCode = Csv.Load(pbRootFile, cbRootFile);
if(dwErrCode == ERROR_SUCCESS)
{
// Retrieve the indices of the file name and MD5 columns
Indices[0] = Csv.GetColumnIndex("FILENAME");
@@ -585,8 +581,8 @@ int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRo
if (pRootHandler != NULL)
{
// Load the root directory. If load failed, we free the object
nError = pRootHandler->Load(hs, Csv, Indices[0], Indices[1]);
if (nError != ERROR_SUCCESS)
dwErrCode = pRootHandler->Load(hs, Csv, Indices[0], Indices[1]);
if (dwErrCode != ERROR_SUCCESS)
{
delete pRootHandler;
pRootHandler = NULL;
@@ -595,11 +591,11 @@ int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRo
}
else
{
nError = ERROR_BAD_FORMAT;
dwErrCode = ERROR_BAD_FORMAT;
}
}
// Assign the root directory (or NULL) and return error
hs->pRootHandler = pRootHandler;
return nError;
return dwErrCode;
}

View File

@@ -98,19 +98,6 @@ typedef struct _TVFS_PATH_TABLE_ENTRY
DWORD NodeValue; // Node value
} TVFS_PATH_TABLE_ENTRY, *PTVFS_PATH_TABLE_ENTRY;
// In-memory layout of VFS span entry
typedef struct _TVFS_SPAN_ENTRY
{
LPBYTE pbCftFileTable;
LPBYTE pbCftFileEntry;
LPBYTE pbCftFileEnd;
DWORD dwFileOffset; // Offset into the referenced file
DWORD dwSpanSize; // Size of the span
DWORD dwCftOffset; // Offset relative to the beginning of the container file table
} TVFS_SPAN_ENTRY, *PTVFS_SPAN_ENTRY;
//-----------------------------------------------------------------------------
// Handler definition for TVFS root file
@@ -123,10 +110,9 @@ struct TRootHandler_TVFS : public TFileTreeRoot
{
// TVFS supports file names, but DOESN'T support CKeys.
dwFeatures |= CASC_FEATURE_FILE_NAMES;
dwNestLevel = 0;
}
// Returns size of "container file table offset" fiels in the VFS.
// Returns size of "container file table offset" field in the VFS.
// - If the container file table is larger than 0xffffff bytes, it's 4 bytes
// - If the container file table is larger than 0xffff bytes, it's 3 bytes
// - If the container file table is larger than 0xff bytes, it's 2 bytes
@@ -142,50 +128,24 @@ struct TRootHandler_TVFS : public TFileTreeRoot
return 1;
}
bool PathBuffer_AddChar(PATH_BUFFER & PathBuffer, char chOneChar)
{
if(PathBuffer.szPtr >= PathBuffer.szEnd)
return false;
*PathBuffer.szPtr++ = chOneChar;
*PathBuffer.szPtr = 0;
return true;
}
bool PathBuffer_AddString(PATH_BUFFER & PathBuffer, LPBYTE pbNamePtr, LPBYTE pbNameEnd)
{
size_t nLength = (pbNameEnd - pbNamePtr);
// Check whether we have enough space
if ((PathBuffer.szPtr + nLength) > PathBuffer.szEnd)
return false;
// Copy the node name
memcpy(PathBuffer.szPtr, pbNamePtr, nLength);
PathBuffer.szPtr += nLength;
return true;
}
bool PathBuffer_AppendNode(PATH_BUFFER & PathBuffer, TVFS_PATH_TABLE_ENTRY & PathEntry)
bool PathBuffer_AppendNode(CASC_PATH<char> & PathBuffer, TVFS_PATH_TABLE_ENTRY & PathEntry)
{
// Append the prefix separator, if needed
if (PathEntry.NodeFlags & TVFS_PTE_PATH_SEPARATOR_PRE)
PathBuffer_AddChar(PathBuffer, '/');
PathBuffer.AppendChar('/');
// Append the name fragment, if any
if (PathEntry.pbNameEnd > PathEntry.pbNamePtr)
PathBuffer_AddString(PathBuffer, PathEntry.pbNamePtr, PathEntry.pbNameEnd);
PathBuffer.AppendStringN((const char *)PathEntry.pbNamePtr, (PathEntry.pbNameEnd - PathEntry.pbNamePtr), false);
// Append the postfix separator, if needed
if (PathEntry.NodeFlags & TVFS_PTE_PATH_SEPARATOR_POST)
PathBuffer_AddChar(PathBuffer, '/');
PathBuffer.AppendChar('/');
// Always end the buffer with zero
PathBuffer.szPtr[0] = 0;
return true;
}
static int CaptureDirectoryHeader(TVFS_DIRECTORY_HEADER & DirHeader, LPBYTE pbDataPtr, LPBYTE pbDataEnd)
static DWORD CaptureDirectoryHeader(TVFS_DIRECTORY_HEADER & DirHeader, LPBYTE pbDataPtr, LPBYTE pbDataEnd)
{
// Fill the header structure with zeros
memset(&DirHeader, 0, sizeof(TVFS_DIRECTORY_HEADER));
@@ -262,32 +222,50 @@ struct TRootHandler_TVFS : public TFileTreeRoot
return (1 <= SpanCount && SpanCount <= 224) ? pbVfsFileEntry : NULL;
}
LPBYTE CaptureVfsSpanEntry(TVFS_DIRECTORY_HEADER & DirHeader, LPBYTE pbVfsSpanEntry, TVFS_SPAN_ENTRY & SpanEntry)
LPBYTE CaptureVfsSpanEntries(TVFS_DIRECTORY_HEADER & DirHeader, LPBYTE pbVfsSpanEntry, PCASC_CKEY_ENTRY PtrSpanEntry, size_t SpanCount)
{
LPBYTE pbCftFileTable;
LPBYTE pbCftFileEntry;
LPBYTE pbCftFileEnd;
LPBYTE pbVfsFileTable = DirHeader.pbDirectoryData + DirHeader.VfsTableOffset;
LPBYTE pbVfsFileEnd = pbVfsFileTable + DirHeader.VfsTableSize;
size_t ItemSize = sizeof(DWORD) + sizeof(DWORD) + DirHeader.CftOffsSize;
// Check the range being captured
if(pbVfsSpanEntry < pbVfsFileTable || (pbVfsSpanEntry + ItemSize) > pbVfsFileEnd)
// Check whether all spans are included in the valid range
if(pbVfsSpanEntry < pbVfsFileTable || (pbVfsSpanEntry + (ItemSize * SpanCount)) > pbVfsFileEnd)
return NULL;
//
// Structure of the span entry:
// (4bytes): Offset into the referenced file (big endian)
// (4bytes): Size of the span (big endian)
// (?bytes): Offset into Container File Table. Length depends on container file table size
//
// Convert all spans
for(size_t i = 0; i < SpanCount; i++)
{
DWORD dwCftOffset = ConvertBytesToInteger_X(pbVfsSpanEntry + sizeof(DWORD) + sizeof(DWORD), DirHeader.CftOffsSize);
SpanEntry.dwFileOffset = ConvertBytesToInteger_4(pbVfsSpanEntry);
SpanEntry.dwSpanSize = ConvertBytesToInteger_4(pbVfsSpanEntry + sizeof(DWORD));
SpanEntry.dwCftOffset = ConvertBytesToInteger_X(pbVfsSpanEntry + sizeof(DWORD) + sizeof(DWORD), DirHeader.CftOffsSize);
//
// Structure of the span entry:
// (4bytes): Offset into the referenced file (big endian)
// (4bytes): Size of the span (big endian)
// (?bytes): Offset into Container File Table. Length depends on container file table size
//
// Resolve the Container File Table entries
SpanEntry.pbCftFileTable = DirHeader.pbDirectoryData + DirHeader.CftTableOffset;
SpanEntry.pbCftFileEntry = SpanEntry.pbCftFileTable + SpanEntry.dwCftOffset;
SpanEntry.pbCftFileEnd = SpanEntry.pbCftFileTable + DirHeader.CftTableSize;
return pbVfsSpanEntry + ItemSize;
// Resolve the Container File Table entry
pbCftFileTable = DirHeader.pbDirectoryData + DirHeader.CftTableOffset;
pbCftFileEntry = pbCftFileTable + dwCftOffset;
pbCftFileEnd = pbCftFileTable + DirHeader.CftTableSize;
// Capture the EKey and the file size
if((pbCftFileEntry + DirHeader.EKeySize + sizeof(DWORD)) > pbCftFileEnd)
return NULL;
// Copy the EKey and content size
CaptureEncodedKey(PtrSpanEntry->EKey, pbCftFileEntry, DirHeader.EKeySize);
PtrSpanEntry->ContentSize = ConvertBytesToInteger_4(pbVfsSpanEntry + sizeof(DWORD));
// Move to the next entry
pbVfsSpanEntry += ItemSize;
PtrSpanEntry++;
}
return pbVfsSpanEntry;
}
//
@@ -365,7 +343,7 @@ struct TRootHandler_TVFS : public TFileTreeRoot
return pbPathTablePtr;
}
bool IsVfsFileEKey(TCascStorage * hs, ENCODED_KEY & EKey, size_t EKeyLength)
bool IsVfsFileEKey(TCascStorage * hs, LPBYTE EKey, size_t EKeyLength)
{
PCASC_CKEY_ENTRY pCKeyEntry;
size_t ItemCount = hs->VfsRootList.ItemCount();
@@ -376,7 +354,7 @@ struct TRootHandler_TVFS : public TFileTreeRoot
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i);
if (pCKeyEntry != NULL)
{
if (!memcmp(pCKeyEntry->EKey, EKey.Value, EKeyLength))
if (!memcmp(pCKeyEntry->EKey, EKey, EKeyLength))
return true;
}
}
@@ -387,27 +365,27 @@ struct TRootHandler_TVFS : public TFileTreeRoot
// This function verifies whether a file is actually a sub-directory.
// If yes, it contains just another "TVFS" virtual file system, just like the ROOT file.
int IsVfsSubDirectory(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, TVFS_DIRECTORY_HEADER & SubHeader, ENCODED_KEY & EKey, DWORD dwFileSize)
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;
int nError = ERROR_BAD_FORMAT;
DWORD dwErrCode = ERROR_BAD_FORMAT;
// Verify whether the EKey is in the list of VFS root files
if(IsVfsFileEKey(hs, EKey, DirHeader.EKeySize))
{
// Locate the CKey entry
if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey.Value)) != NULL)
if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey)) != NULL)
{
// Load the entire file into memory
pbVfsData = LoadInternalFileToMemory(hs, pCKeyEntry, &cbVfsData);
if (pbVfsData && cbVfsData)
{
// Capture the file folder. This also serves as test
nError = CaptureDirectoryHeader(SubHeader, pbVfsData, pbVfsData + cbVfsData);
if (nError == ERROR_SUCCESS)
return nError;
dwErrCode = CaptureDirectoryHeader(SubHeader, pbVfsData, pbVfsData + cbVfsData);
if (dwErrCode == ERROR_SUCCESS)
return dwErrCode;
// Clear the captured header
memset(&SubHeader, 0, sizeof(TVFS_DIRECTORY_HEADER));
@@ -416,7 +394,7 @@ struct TRootHandler_TVFS : public TFileTreeRoot
}
}
return nError;
return dwErrCode;
}
void InsertRootVfsEntry(TCascStorage * hs, LPBYTE pbCKey, const char * szFormat, size_t nIndex)
@@ -432,15 +410,18 @@ struct TRootHandler_TVFS : public TFileTreeRoot
}
}
DWORD ParsePathFileTable(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, PATH_BUFFER & PathBuffer, LPBYTE pbPathTablePtr, LPBYTE pbPathTableEnd)
DWORD ParsePathFileTable(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, CASC_PATH<char> & PathBuffer, LPBYTE pbPathTablePtr, LPBYTE pbPathTableEnd)
{
TVFS_DIRECTORY_HEADER SubHeader;
TVFS_PATH_TABLE_ENTRY PathEntry;
PCASC_CKEY_ENTRY pCKeyEntry;
LPBYTE pbVfsSpanEntry;
char * szSavePathPtr = PathBuffer.szPtr;
DWORD dwSpanCount = 0;
int nError;
size_t nSavePos = PathBuffer.Save();
DWORD dwSpanCount;
DWORD dwErrCode;
// Sanity check
assert(SpanArray.IsInitialized());
// Parse the file table
while(pbPathTablePtr < pbPathTableEnd)
@@ -467,9 +448,9 @@ struct TRootHandler_TVFS : public TFileTreeRoot
assert((PathEntry.NodeValue & TVFS_FOLDER_SIZE_MASK) >= sizeof(DWORD));
// Recursively call the folder parser on the same file
nError = ParsePathFileTable(hs, DirHeader, PathBuffer, pbPathTablePtr, pbDirectoryEnd);
if (nError != ERROR_SUCCESS)
return nError;
dwErrCode = ParsePathFileTable(hs, DirHeader, PathBuffer, pbPathTablePtr, pbDirectoryEnd);
if (dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Skip the directory data
pbPathTablePtr = pbDirectoryEnd;
@@ -481,63 +462,117 @@ struct TRootHandler_TVFS : public TFileTreeRoot
if(pbVfsSpanEntry == NULL)
return ERROR_BAD_FORMAT;
// TODO: Need to support multi-span files larger than 4 GB
// Example: CoD: Black Ops 4, file "zone/base.xpak" 0x16 spans, over 15 GB size
assert(dwSpanCount == 1);
dwSpanCount = 1;
// Parse all span entries
for(DWORD i = 0; i < dwSpanCount; i++)
// If it's one span, it's either a subdirectory or an entire file
if(dwSpanCount == 1)
{
TVFS_SPAN_ENTRY SpanEntry;
ENCODED_KEY EKey = {0};
CASC_CKEY_ENTRY SpanEntry;
// Capture the n-th span entry
pbVfsSpanEntry = CaptureVfsSpanEntry(DirHeader, pbVfsSpanEntry, SpanEntry);
// Capture the single span entry
pbVfsSpanEntry = CaptureVfsSpanEntries(DirHeader, pbVfsSpanEntry, &SpanEntry, 1);
if(pbVfsSpanEntry == NULL)
return ERROR_FILE_CORRUPT;
// Capture the encoded key
if((SpanEntry.pbCftFileEntry + DirHeader.EKeySize) > SpanEntry.pbCftFileEnd)
return ERROR_BAD_FORMAT;
// Copy the EKey
memcpy(EKey.Value, SpanEntry.pbCftFileEntry, DirHeader.EKeySize);
// We need to check whether this is another TVFS directory file
if (IsVfsSubDirectory(hs, DirHeader, SubHeader, EKey, SpanEntry.dwSpanSize) == ERROR_SUCCESS)
// Find the CKey entry
pCKeyEntry = FindCKeyEntry_EKey(hs, SpanEntry.EKey);
if(pCKeyEntry != NULL)
{
// Add colon (':')
PathBuffer_AddChar(PathBuffer, ':');
// Insert the file to the file tree
if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey.Value)) != NULL)
// We need to check whether this is another TVFS directory file
if (IsVfsSubDirectory(hs, DirHeader, SubHeader, SpanEntry.EKey, SpanEntry.ContentSize) == ERROR_SUCCESS)
{
// Add colon (':')
PathBuffer.AppendChar(':');
// The file content size should already be there
assert(pCKeyEntry->ContentSize == SpanEntry.dwSpanSize);
FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
assert(pCKeyEntry->ContentSize == SpanEntry.ContentSize);
FileTree.InsertByName(pCKeyEntry, PathBuffer);
// Parse the subdir
ParseDirectoryData(hs, SubHeader, PathBuffer);
CASC_FREE(SubHeader.pbDirectoryData);
}
else
{
// If the content content size is not there, supply it now
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
pCKeyEntry->ContentSize = SpanEntry.ContentSize;
FileTree.InsertByName(pCKeyEntry, PathBuffer);
}
}
}
else
{
PCASC_CKEY_ENTRY pSpanEntries;
PCASC_FILE_NODE pFileNode;
USHORT RefCount;
bool bFilePresent = true;
//
// Need to support multi-span files, possibly lager than 4 GB
// Example: CoD: Black Ops 4, file "zone/base.xpak" 0x16 spans, over 15 GB size
//
// Allocate buffer for all span entries
pSpanEntries = (PCASC_CKEY_ENTRY)SpanArray.Insert(dwSpanCount);
if(pSpanEntries == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// Capture all span entries
pbVfsSpanEntry = CaptureVfsSpanEntries(DirHeader, pbVfsSpanEntry, pSpanEntries, dwSpanCount);
if(pbVfsSpanEntry == NULL)
return ERROR_FILE_CORRUPT;
// Parse all span entries
for(DWORD dwSpanIndex = 0; dwSpanIndex < dwSpanCount; dwSpanIndex++)
{
PCASC_CKEY_ENTRY pSpanEntry = pSpanEntries + dwSpanIndex;
// Find the CKey entry
pCKeyEntry = FindCKeyEntry_EKey(hs, pSpanEntries[dwSpanIndex].EKey);
if(pCKeyEntry == NULL)
{
bFilePresent = false;
break;
}
ParseDirectoryData(hs, SubHeader, PathBuffer);
CASC_FREE(SubHeader.pbDirectoryData);
}
else
{
// Insert the file to the file tree
if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey.Value)) != NULL)
// Supply the content size
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
pCKeyEntry->ContentSize = pSpanEntry->ContentSize;
assert(pCKeyEntry->ContentSize == pSpanEntry->ContentSize);
// Fill-in the span entry
if(dwSpanIndex == 0)
{
// If the file content is not there, supply it now
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
pCKeyEntry->ContentSize = SpanEntry.dwSpanSize;
FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
pCKeyEntry->SpanCount = (BYTE)(dwSpanCount);
pCKeyEntry->RefCount++;
}
else
{
// Mark the CKey entry as a file span. Note that a CKey entry
// can actually be both a file span and a standalone file:
// * zone/zm_red.xpak - { zone/zm_red.xpak_1, zone/zm_red.xpak_2, ..., zone/zm_red.xpak_6 }
pCKeyEntry->Flags |= CASC_CE_FILE_SPAN;
}
// Copy all from the existing CKey entry
memcpy(pSpanEntry, pCKeyEntry, sizeof(CASC_CKEY_ENTRY));
}
// Do nothing if the file is not present locally
if(bFilePresent)
{
// Insert a new file node that will contain pointer to the span entries
RefCount = pSpanEntries->RefCount;
pFileNode = FileTree.InsertByName(pSpanEntries, PathBuffer);
pSpanEntries->RefCount = RefCount;
if(pFileNode == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
}
}
}
// Reset the position of the path buffer
PathBuffer.szPtr = szSavePathPtr;
PathBuffer.szPtr[0] = 0;
PathBuffer.Restore(nSavePos);
}
}
@@ -545,7 +580,7 @@ struct TRootHandler_TVFS : public TFileTreeRoot
return ERROR_SUCCESS;
}
int ParseDirectoryData(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, PATH_BUFFER & PathBuffer)
DWORD ParseDirectoryData(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, CASC_PATH<char> & PathBuffer)
{
LPBYTE pbRootDirectory = DirHeader.pbDirectoryData + DirHeader.PathTableOffset;
LPBYTE pbRootDirPtr = pbRootDirectory;
@@ -583,21 +618,19 @@ struct TRootHandler_TVFS : public TFileTreeRoot
return ParsePathFileTable(hs, DirHeader, PathBuffer, pbRootDirPtr, pbRootDirEnd);
}
int Load(TCascStorage * hs, TVFS_DIRECTORY_HEADER & RootHeader)
DWORD Load(TCascStorage * hs, TVFS_DIRECTORY_HEADER & RootHeader)
{
// PCASC_CKEY_ENTRY pCKeyEntry;
PATH_BUFFER PathBuffer;
char szPathBuffer[MAX_PATH];
// Initialize the path buffer
memset(szPathBuffer, 0, sizeof(szPathBuffer));
PathBuffer.szBegin =
PathBuffer.szPtr = szPathBuffer;
PathBuffer.szEnd = szPathBuffer + MAX_PATH;
CASC_PATH<char> PathBuffer;
DWORD dwErrCode;
// Save the length of the key
FileTree.SetKeyLength(RootHeader.EKeySize);
// Initialize the array of span entries
dwErrCode = SpanArray.Create(sizeof(CASC_CKEY_ENTRY), 0x100);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Insert the main VFS root file as named entry
InsertRootVfsEntry(hs, hs->VfsRoot.CKey, "vfs-root", 0);
@@ -612,29 +645,29 @@ struct TRootHandler_TVFS : public TFileTreeRoot
return ParseDirectoryData(hs, RootHeader, PathBuffer);
}
DWORD dwNestLevel;
CASC_ARRAY SpanArray; // Array of CASC_SPAN_ENTRY for all multi-span files
};
//-----------------------------------------------------------------------------
// Public functions - TVFS root
int RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
DWORD RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
{
TRootHandler_TVFS * pRootHandler = NULL;
TVFS_DIRECTORY_HEADER RootHeader;
int nError;
DWORD dwErrCode;
// Capture the entire root directory
nError = TRootHandler_TVFS::CaptureDirectoryHeader(RootHeader, pbRootFile, pbRootFile + cbRootFile);
if(nError == ERROR_SUCCESS)
dwErrCode = TRootHandler_TVFS::CaptureDirectoryHeader(RootHeader, pbRootFile, pbRootFile + cbRootFile);
if(dwErrCode == ERROR_SUCCESS)
{
// Allocate the root handler object
pRootHandler = new TRootHandler_TVFS();
if(pRootHandler != NULL)
{
// Load the root directory. If load failed, we free the object
nError = pRootHandler->Load(hs, RootHeader);
if(nError != ERROR_SUCCESS)
dwErrCode = pRootHandler->Load(hs, RootHeader);
if(dwErrCode != ERROR_SUCCESS)
{
delete pRootHandler;
pRootHandler = NULL;
@@ -644,5 +677,5 @@ int RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFil
// Assign the root directory (or NULL) and return error
hs->pRootHandler = pRootHandler;
return nError;
return dwErrCode;
}

View File

@@ -49,16 +49,16 @@ struct TRootHandler_SC1 : public TFileTreeRoot
return bResult;
}
int Load(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
DWORD Load(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
{
PCASC_CKEY_ENTRY pCKeyEntry;
CASC_CSV Csv(0, false);
BYTE CKey[MD5_HASH_SIZE];
int nError;
DWORD dwErrCode;
// Parse the ROOT file first in order to see whether we have the correct format
nError = Csv.Load(pbRootFile, cbRootFile);
if(nError == ERROR_SUCCESS)
dwErrCode = Csv.Load(pbRootFile, cbRootFile);
if(dwErrCode == ERROR_SUCCESS)
{
// Parse all lines
while(Csv.LoadNextLine())
@@ -79,7 +79,7 @@ struct TRootHandler_SC1 : public TFileTreeRoot
}
}
return nError;
return dwErrCode;
}
};
@@ -93,10 +93,10 @@ struct TRootHandler_SC1 : public TFileTreeRoot
// locales/zhCN/Assets/sound/terran/ghost/tghdth01.wav|6637ed776bd22089e083b8b0b2c0374c
//
int RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
DWORD RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
{
TRootHandler_SC1 * pRootHandler = NULL;
int nError = ERROR_BAD_FORMAT;
DWORD dwErrCode = ERROR_BAD_FORMAT;
// Verify whether this looks like a Starcraft I root file
if(TRootHandler_SC1::IsRootFile(pbRootFile, cbRootFile))
@@ -106,8 +106,8 @@ int RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbR
if(pRootHandler != NULL)
{
// Load the root directory. If load failed, we free the object
nError = pRootHandler->Load(hs, pbRootFile, cbRootFile);
if(nError != ERROR_SUCCESS)
dwErrCode = pRootHandler->Load(hs, pbRootFile, cbRootFile);
if(dwErrCode != ERROR_SUCCESS)
{
delete pRootHandler;
pRootHandler = NULL;
@@ -117,5 +117,5 @@ int RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbR
// Assign the root directory (or NULL) and return error
hs->pRootHandler = pRootHandler;
return nError;
return dwErrCode;
}

View File

@@ -178,7 +178,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
}
}
int ParseWowRootFile_AddFiles_6x(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup)
DWORD ParseWowRootFile_AddFiles_6x(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup)
{
PFILE_ROOT_ENTRY pRootEntry = RootGroup.pRootEntries;
PCASC_CKEY_ENTRY pCKeyEntry;
@@ -215,7 +215,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
return ERROR_SUCCESS;
}
int ParseWowRootFile_AddFiles_82(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup)
DWORD ParseWowRootFile_AddFiles_82(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup)
{
PCASC_CKEY_ENTRY pCKeyEntry;
PCONTENT_KEY pCKey = RootGroup.pCKeyEntries;
@@ -229,6 +229,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
{
// Set the file data ID
FileDataId = FileDataId + RootGroup.FileDataIds[i];
//printf("File Data ID: %u\n", FileDataId);
// Find the item in the central storage. Insert it to the tree
if((pCKeyEntry = FindCKeyEntry_CKey(hs, pCKey->Value)) != NULL)
@@ -253,7 +254,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
return ERROR_SUCCESS;
}
int ParseWowRootFile_Level2(
DWORD ParseWowRootFile_Level2(
TCascStorage * hs,
LPBYTE pbRootPtr,
LPBYTE pbRootEnd,
@@ -269,6 +270,10 @@ struct TRootHandler_WoW : public TFileTreeRoot
// Now parse the root file
while(pbRootPtr < pbRootEnd)
{
//char szMessage[0x100];
//StringCchPrintfA(szMessage, _countof(szMessage), "%p\n", (pbRootEnd - pbRootPtr));
//OutputDebugStringA(szMessage);
// Validate the file locale block
pbRootPtr = CaptureRootGroup(RootBlock, pbRootPtr, pbRootEnd);
if(pbRootPtr == NULL)
@@ -287,7 +292,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
continue;
// WoW.exe (build 19116): Locales other than defined mask are skipped too
if((RootBlock.Header.LocaleFlags & dwLocaleMask) == 0)
if(RootBlock.Header.LocaleFlags != 0 && (RootBlock.Header.LocaleFlags & dwLocaleMask) == 0)
continue;
// Now call the custom function
@@ -351,19 +356,19 @@ struct TRootHandler_WoW : public TFileTreeRoot
LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, (1 << dwLocale), bOverrideArchive, bAudioLocale);
*/
int ParseWowRootFile_Level1(
DWORD ParseWowRootFile_Level1(
TCascStorage * hs,
LPBYTE pbRootPtr,
LPBYTE pbRootEnd,
DWORD dwLocaleMask,
BYTE bAudioLocale)
{
int nError;
DWORD dwErrCode;
// Load the locale as-is
nError = ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, dwLocaleMask, false, bAudioLocale);
if (nError != ERROR_SUCCESS)
return nError;
dwErrCode = ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, dwLocaleMask, false, bAudioLocale);
if (dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// If we wanted enGB, we also load enUS for the missing files
if(dwLocaleMask == CASC_LOCALE_ENGB)
@@ -376,15 +381,15 @@ struct TRootHandler_WoW : public TFileTreeRoot
}
// WoW.exe: 004146C7 (BuildManifest::Load)
int Load(TCascStorage * hs, LPBYTE pbRootPtr, LPBYTE pbRootEnd, DWORD dwLocaleMask)
DWORD Load(TCascStorage * hs, LPBYTE pbRootPtr, LPBYTE pbRootEnd, DWORD dwLocaleMask)
{
int nError;
DWORD dwErrCode;
nError = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 0);
if (nError == ERROR_SUCCESS)
nError = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 1);
dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 0);
if (dwErrCode == ERROR_SUCCESS)
dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 1);
return nError;
return dwErrCode;
}
// Search for files
@@ -461,7 +466,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
//-----------------------------------------------------------------------------
// Public functions
int RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask)
DWORD RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask)
{
TRootHandler_WoW * pRootHandler = NULL;
FILE_ROOT_HEADER_82 RootHeader;
@@ -469,7 +474,7 @@ int RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile
LPBYTE pbRootEnd = pbRootFile + cbRootFile;
LPBYTE pbRootPtr;
DWORD FileCounterHashless = 0;
int nError = ERROR_BAD_FORMAT;
DWORD dwErrCode = ERROR_BAD_FORMAT;
// Check for the new format (World of Warcraft 8.2, build 30170)
pbRootPtr = TRootHandler_WoW::CaptureRootHeader(RootHeader, pbRootFile, pbRootEnd);
@@ -485,8 +490,8 @@ int RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile
if(pRootHandler != NULL)
{
// Load the root directory. If load failed, we free the object
nError = pRootHandler->Load(hs, pbRootFile, pbRootEnd, dwLocaleMask);
if(nError != ERROR_SUCCESS)
dwErrCode = pRootHandler->Load(hs, pbRootFile, pbRootEnd, dwLocaleMask);
if(dwErrCode != ERROR_SUCCESS)
{
delete pRootHandler;
pRootHandler = NULL;
@@ -495,6 +500,6 @@ int RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile
// Assign the root directory (or NULL) and return error
hs->pRootHandler = pRootHandler;
return nError;
return dwErrCode;
}

View File

@@ -94,7 +94,7 @@ struct FILE_INDEX_FOOTER
BYTE PageSizeKB; // Length, in kilobytes, of the index page
BYTE OffsetBytes; // Normally 4 for archive indices, 6 for group indices, and 0 for loose file indices
BYTE SizeBytes; // Normally 4
BYTE EKeySizeBytes; // Normally 16
BYTE EKeyLength; // Normally 16
BYTE FooterHashBytes; // Normally 8, <= 0x10
BYTE ElementCount[4]; // BigEndian in _old_ versions (e.g. 18179)
BYTE FooterHash[CHKSUM_LENGTH];

View File

@@ -25,8 +25,8 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,50,0,156
PRODUCTVERSION 1,50,0,156
FILEVERSION 1,50,0,158
PRODUCTVERSION 1,50,0,158
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -42,12 +42,12 @@ BEGIN
BLOCK "040504b0"
BEGIN
VALUE "FileDescription", "CascLib library for reading Blizzard CASC storages"
VALUE "FileVersion", "1, 50, 0, 156\0"
VALUE "FileVersion", "1, 50, 0, 158\0"
VALUE "InternalName", "CascLib"
VALUE "LegalCopyright", "Copyright (c) 2014 - 2019 Ladislav Zezula"
VALUE "OriginalFilename", "CascLib.dll"
VALUE "ProductName", "CascLib"
VALUE "ProductVersion", "1, 50, 0, 156\0"
VALUE "ProductVersion", "1, 50, 0, 158\0"
END
END
BLOCK "VarFileInfo"

View File

@@ -43,7 +43,7 @@ class CASC_ARRAY
int Create(size_t ItemSize, size_t ItemCountMax)
{
// 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;
@@ -53,12 +53,12 @@ class CASC_ARRAY
}
// Inserts one or more items; returns pointer to the first inserted item
void * Insert(size_t NewItemCount)
void * Insert(size_t NewItemCount, bool bEnlargeAllowed = true)
{
void * pNewItems;
// Try to enlarge the buffer, if needed
if (!EnlargeArray(m_ItemCount + NewItemCount))
if (!EnlargeArray(m_ItemCount + NewItemCount, bEnlargeAllowed))
return NULL;
pNewItems = m_pItemArray + (m_ItemCount * m_ItemSize);
@@ -70,9 +70,9 @@ class CASC_ARRAY
}
// Inserts one or more items; returns pointer to the first inserted item
void * Insert(const void * NewItems, size_t NewItemCount)
void * Insert(const void * NewItems, size_t NewItemCount, bool bEnlargeAllowed = true)
{
void * pNewItem = Insert(NewItemCount);
void * pNewItem = Insert(NewItemCount, bEnlargeAllowed);
// Copy the item(s) to the array, if any
if (pNewItem && NewItems)
@@ -99,7 +99,7 @@ class CASC_ARRAY
LPBYTE pbNewItem;
// Make sure we have array large enough
if(!EnlargeArray(ItemIndex + 1))
if(!EnlargeArray(ItemIndex + 1, true))
return NULL;
// Get the items range
@@ -169,7 +169,7 @@ class CASC_ARRAY
protected:
bool EnlargeArray(size_t NewItemCount)
bool EnlargeArray(size_t NewItemCount, bool bEnlargeAllowed)
{
LPBYTE NewItemArray;
size_t ItemCountMax;
@@ -181,6 +181,10 @@ class CASC_ARRAY
// Shall we enlarge the table?
if (NewItemCount > m_ItemCountMax)
{
// Deny enlarge if not allowed
if(bEnlargeAllowed == false)
return false;
// Calculate new table size
ItemCountMax = m_ItemCountMax;
while (ItemCountMax < NewItemCount)

View File

@@ -81,6 +81,19 @@ void SetLastError(DWORD dwErrCode)
//-----------------------------------------------------------------------------
// Linear data stream manipulation
LPBYTE CaptureInteger16_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue)
{
// Is there enough data?
if((pbDataPtr + sizeof(USHORT)) > pbDataEnd)
return NULL;
// Convert data from Little endian to
PtrValue[0] = ConvertBytesToInteger_2(pbDataPtr);
// Return the pointer to data following after the integer
return pbDataPtr + sizeof(USHORT);
}
LPBYTE CaptureInteger32(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue)
{
// Is there enough data?
@@ -133,6 +146,49 @@ LPBYTE CaptureContentKey(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PCONTENT_KEY * PtrC
return pbDataPtr + sizeof(CONTENT_KEY);
}
LPBYTE CaptureEncodedKey(LPBYTE pbEKey, LPBYTE pbData, BYTE EKeyLength)
{
// Two usual lengths of EKey
assert(EKeyLength == 0x09 || EKeyLength == 0x10);
// Copy the first 0x09 bytes
if(EKeyLength >= 0x09)
{
pbEKey[0x00] = pbData[0x00];
pbEKey[0x01] = pbData[0x01];
pbEKey[0x02] = pbData[0x02];
pbEKey[0x03] = pbData[0x03];
pbEKey[0x04] = pbData[0x04];
pbEKey[0x05] = pbData[0x05];
pbEKey[0x06] = pbData[0x06];
pbEKey[0x07] = pbData[0x07];
pbEKey[0x08] = pbData[0x08];
if(EKeyLength == 0x10)
{
pbEKey[0x09] = pbData[0x09];
pbEKey[0x0A] = pbData[0x0A];
pbEKey[0x0B] = pbData[0x0B];
pbEKey[0x0C] = pbData[0x0C];
pbEKey[0x0D] = pbData[0x0D];
pbEKey[0x0E] = pbData[0x0E];
pbEKey[0x0F] = pbData[0x0F];
}
else
{
pbEKey[0x09] = 0;
pbEKey[0x0A] = 0;
pbEKey[0x0B] = 0;
pbEKey[0x0C] = 0;
pbEKey[0x0D] = 0;
pbEKey[0x0E] = 0;
pbEKey[0x0F] = 0;
}
}
return pbData + EKeyLength;
}
LPBYTE CaptureArray_(LPBYTE pbDataPtr, LPBYTE pbDataEnd, LPBYTE * PtrArray, size_t ItemSize, size_t ItemCount)
{
size_t ArraySize = ItemSize * ItemCount;
@@ -229,6 +285,7 @@ size_t CascStrPrintf(char * buffer, size_t nCount, const char * format, ...)
#ifdef 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
@@ -248,6 +305,7 @@ size_t CascStrPrintf(wchar_t * buffer, size_t nCount, const wchar_t * format, ..
#ifdef 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
@@ -268,7 +326,7 @@ char * CascNewStr(const char * szString, size_t nCharsToReserve)
if(szString != NULL)
{
nLength = strlen(szString);
szNewString = CASC_ALLOC(char, nLength + nCharsToReserve + 1);
szNewString = CASC_ALLOC<char>(nLength + nCharsToReserve + 1);
if(szNewString != NULL)
{
memcpy(szNewString, szString, nLength);
@@ -287,7 +345,7 @@ wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve)
if(szString != NULL)
{
nLength = wcslen(szString);
szNewString = CASC_ALLOC(wchar_t, nLength + nCharsToReserve + 1);
szNewString = CASC_ALLOC<wchar_t>(nLength + nCharsToReserve + 1);
if(szNewString != NULL)
{
memcpy(szNewString, szString, nLength * sizeof(wchar_t));
@@ -298,43 +356,8 @@ wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve)
return szNewString;
}
template <typename XCHAR>
TCHAR * AppendPathFragment(TCHAR * szBuffer, TCHAR * szBufferEnd, const XCHAR * szPath, char chSeparator, bool bFirstFragment = false)
{
// The "Path" must not be empty
if(szPath && szPath[0])
{
// Append the path separator after the first fragment
if(szBuffer < szBufferEnd && bFirstFragment == false)
{
if(szBuffer[-1] != chSeparator)
{
*szBuffer++ = chSeparator;
}
}
// Copy the sub path
while(szBuffer < szBufferEnd && szPath[0] != 0)
{
// If there is a path separator, we skip it (all of them) and put single separator there
if(szPath[0] == '\\' || szPath[0] == '/')
{
while(szPath[0] == '\\' || szPath[0] == '/')
szPath++;
*szBuffer++ = chSeparator;
}
else
{
*szBuffer++ = *szPath++;
}
}
// Append end of string
szBuffer[0] = 0;
}
return szBuffer;
}
//-----------------------------------------------------------------------------
// String merging
LPTSTR GetLastPathPart(LPTSTR szWorkPath)
{
@@ -377,19 +400,18 @@ bool CutLastPathPart(TCHAR * szWorkPath)
size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, char chSeparator, va_list argList)
{
LPTSTR szSaveBuffer = szBuffer;
LPTSTR szBufferEnd = szBuffer + nMaxChars - 1;
LPTSTR szFragment;
bool bFirstFragment = true;
CASC_PATH<TCHAR> Path(chSeparator);
LPCTSTR szFragment;
bool bWithSeparator = false;
// Combine all parts of the path here
while((szFragment = va_arg(argList, LPTSTR)) != NULL)
{
szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szFragment, chSeparator, bFirstFragment);
bFirstFragment = false;
Path.AppendString(szFragment, bWithSeparator);
bWithSeparator = true;
}
return (szBuffer - szSaveBuffer);
return Path.Copy(szBuffer, nMaxChars);
}
size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, char chSeparator, ...)
@@ -406,40 +428,12 @@ size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, char chSeparator, ...)
LPTSTR CombinePath(LPCTSTR szDirectory, LPCTSTR szSubDir)
{
LPTSTR szFullPath;
size_t nLength = 0;
CASC_PATH<TCHAR> Path(PATH_SEP_CHAR);
// Calculate length
if(szDirectory != NULL)
nLength += (_tcslen(szDirectory) + 1);
if(szSubDir != NULL)
nLength += (_tcslen(szSubDir) + 1);
// Allocate buffer
if((szFullPath = CASC_ALLOC(TCHAR, nLength)) != NULL)
{
CombinePath(szFullPath, nLength, PATH_SEP_CHAR, szDirectory, szSubDir, NULL);
}
return szFullPath;
}
size_t CreateCascSubdirectoryName(LPTSTR szBuffer, size_t nMaxChars, LPCTSTR szSubDir, LPCTSTR szExtension, LPBYTE pbEKey)
{
TCHAR * szSaveBuffer = szBuffer;
TCHAR * szBufferEnd = szBuffer + nMaxChars - 1;
char szHashSubPath[0x80];
char szHashText[MD5_STRING_SIZE+1];
// Prepare the subpath
StringFromBinary(pbEKey, MD5_HASH_SIZE, szHashText);
CascStrPrintf(szHashSubPath, _countof(szHashSubPath), "%02x/%02x/%s", pbEKey[0], pbEKey[1], szHashText);
// Combine the path together
szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szSubDir, URL_SEP_CHAR, true);
szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szHashSubPath, URL_SEP_CHAR);
szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szExtension, URL_SEP_CHAR, true);
return (szBuffer - szSaveBuffer);
// Merge the path
Path.AppendString(szDirectory, false);
Path.AppendString(szSubDir, true);
return Path.New();
}
size_t NormalizeFileName(const unsigned char * NormTable, char * szNormName, const char * szFileName, size_t cchMaxChars)
@@ -585,32 +579,6 @@ int ConvertStringToBinary(
return ERROR_SUCCESS;
}
char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer)
{
char * szSaveBuffer = szBuffer;
// Verify the binary pointer
if(pbBinary && cbBinary)
{
// Convert the string to the array of MD5
// Copy the blob data as text
for(size_t i = 0; i < cbBinary; i++)
{
*szBuffer++ = IntToHexChar[pbBinary[i] >> 0x04];
*szBuffer++ = IntToHexChar[pbBinary[i] & 0x0F];
}
}
// Terminate the string
*szBuffer = 0;
return szSaveBuffer;
}
char * StringFromMD5(LPBYTE md5, char * szBuffer)
{
return StringFromBinary(md5, MD5_HASH_SIZE, szBuffer);
}
//-----------------------------------------------------------------------------
// File name utilities
@@ -693,10 +661,8 @@ bool CascCheckWildCard(const char * szString, const char * szWildCard)
{
if(szWildCardPtr[0] == '*')
{
szWildCardPtr++;
if(szWildCardPtr[0] == '*')
continue;
while(szWildCardPtr[0] == '*')
szWildCardPtr++;
if(szWildCardPtr[0] == 0)
return true;

View File

@@ -39,13 +39,19 @@ typedef struct _CONTENT_KEY
} CONTENT_KEY, *PCONTENT_KEY, ENCODED_KEY, *PENCODED_KEY;
// Helper structure for merging file paths
typedef struct _PATH_BUFFER
//-----------------------------------------------------------------------------
// EKey entry, captured from index files of all types. This structure
// is somewhat less memory consuming than CASC_CKEY_ENTRY
typedef struct _CASC_EKEY_ENTRY
{
char * szBegin;
char * szPtr;
char * szEnd;
} PATH_BUFFER, *PPATH_BUFFER;
BYTE EKey[MD5_HASH_SIZE]; // Encoded key. Length depends on TCascStorage::EKeyLength
ULONGLONG StorageOffset; // Offset of the encoded file in archive.
// Lower (TCascStorage::FileOffsetBits) bits are archive offset.
// Upper bits are archive index
DWORD EncodedSize; // Encoded size
DWORD Alignment; // Alignment to 8-byte boundary. Reserved for future use
} CASC_EKEY_ENTRY, *PCASC_EKEY_ENTRY;
//-----------------------------------------------------------------------------
// Basic structure used by all CascLib objects to describe a single entry
@@ -59,7 +65,12 @@ typedef struct _PATH_BUFFER
#define CASC_CE_HAS_EKEY_PARTIAL 0x00000008 // The EKey is only partial, padded by zeros. Always used with CASC_CE_HAS_EKEY
#define CASC_CE_IN_ENCODING 0x00000010 // Present in the ENCODING manifest
#define CASC_CE_IN_DOWNLOAD 0x00000020 // Present in the DOWNLOAD manifest
#define CASC_CE_FOLDER_ENTRY 0x00000040 // This CKey entry is a folder
#define CASC_CE_IN_BUILD 0x00000040 // Present in the BUILD (text) manifest
#define CASC_CE_IN_ARCHIVE 0x00000080 // File is stored in an archive (for online storages)
#define CASC_CE_FOLDER_ENTRY 0x00000100 // This CKey entry is a folder
#define CASC_CE_FILE_SPAN 0x00000200 // This CKey entry is a follow-up file span
#define CASC_CE_FILE_PATCH 0x00000400 // The file is in PATCH subfolder in remote storage
#define CASC_CE_PLAIN_DATA 0x00000800 // The file data is not BLTE encoded, but in plain format
// In-memory representation of a single entry.
struct CASC_CKEY_ENTRY
@@ -75,19 +86,36 @@ struct CASC_CKEY_ENTRY
StorageOffset = CASC_INVALID_OFFS64;
EncodedSize = CASC_INVALID_SIZE;
ContentSize = CASC_INVALID_SIZE;
SpanCount = 1;
}
bool IsFile()
{
// Must not be a folder entry
if((Flags & CASC_CE_FOLDER_ENTRY) == 0)
{
// There can be entries that are both file span or the standalone file
// * zone/zm_red.xpak - { zone/zm_red.xpak_1, zone/zm_red.xpak_2, ..., zone/zm_red.xpak_6 }
if(RefCount != 0)
return true;
// To include the file, it must either be present in ENCODING, DOWNLOAD or in BUILD file
if(((Flags & CASC_CE_FILE_SPAN) == 0) && (Flags & (CASC_CE_IN_ENCODING | CASC_CE_IN_DOWNLOAD | CASC_CE_IN_BUILD)))
return true;
}
return false;
}
BYTE CKey[MD5_HASH_SIZE]; // Content key of the full length
BYTE EKey[MD5_HASH_SIZE]; // Encoded key of the full length
ULONGLONG StorageOffset; // Linear offset over the entire storage. 0 if not present
ULONGLONG TagBitMask; // Bitmap for the tags. 0 ig tags are not supported
DWORD EncodedSize; // Encoded size of the file. 0 if not supported
DWORD ContentSize; // Content size of the file. 0 if not supported
DWORD ContentSize; // Content size of the file
DWORD EncodedSize; // Encoded size of the file
DWORD Flags; // See CASC_CE_XXX
USHORT RefCount; // This is the number of file names referencing this entry
BYTE SpanCount; // Number of spans for the file
BYTE Priority; // Download priority
BYTE Alignment;
};
typedef CASC_CKEY_ENTRY *PCASC_CKEY_ENTRY;
@@ -112,7 +140,22 @@ extern unsigned char IntToHexChar[];
//
#define CASC_REALLOC(type, ptr, count) (type *)realloc(ptr, (count) * sizeof(type))
#define CASC_ALLOC(type, count) (type *)malloc((count) * sizeof(type))
template <typename T>
T * CASC_ALLOC(size_t nCount)
{
return (T *)malloc(nCount * sizeof(T));
}
template <typename T>
T * CASC_ALLOC_ZERO(size_t nCount)
{
T * ptr = CASC_ALLOC<T>(nCount);
if(ptr != NULL)
memset(ptr, 0, sizeof(T) * nCount);
return ptr;
}
template <typename T>
void CASC_FREE(T *& ptr)
@@ -241,12 +284,14 @@ inline void CopyMemory16(void * Target, void * Source)
}
//-----------------------------------------------------------------------------
// Linear data stream manipulation
// Capturing various integral values
LPBYTE CaptureInteger16_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue);
LPBYTE CaptureInteger32(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue);
LPBYTE CaptureInteger32_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue);
LPBYTE CaptureByteArray(LPBYTE pbDataPtr, LPBYTE pbDataEnd, size_t nLength, LPBYTE pbOutput);
LPBYTE CaptureContentKey(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PCONTENT_KEY * PtrCKey);
LPBYTE CaptureEncodedKey(LPBYTE pbEKey, LPBYTE pbData, BYTE EKeyLength);
LPBYTE CaptureArray_(LPBYTE pbDataPtr, LPBYTE pbDataEnd, LPBYTE * PtrArray, size_t ItemSize, size_t ItemCount);
#define CaptureArray(pbDataPtr, pbDataEnd, PtrArray, type, count) CaptureArray_(pbDataPtr, pbDataEnd, PtrArray, sizeof(type), count)
@@ -277,7 +322,6 @@ LPTSTR CombinePath(LPCTSTR szPath, LPCTSTR szSubDir);
LPTSTR GetLastPathPart(LPTSTR szWorkPath);
bool CutLastPathPart(TCHAR * szWorkPath);
size_t CreateCascSubdirectoryName(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szSubDir, const TCHAR * szExtension, LPBYTE pbEKey);
size_t NormalizeFileName_UpperBkSlash(char * szNormName, const char * szFileName, size_t cchMaxChars);
size_t NormalizeFileName_LowerSlash(char * szNormName, const char * szFileName, size_t cchMaxChars);
@@ -288,8 +332,31 @@ int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue);
int ConvertStringToInt08(const char * szString, PDWORD PtrValue);
int ConvertStringToInt32(const TCHAR * szString, size_t nMaxDigits, PDWORD PtrValue);
int ConvertStringToBinary(const char * szString, size_t nMaxDigits, LPBYTE pbBinary);
char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer);
char * StringFromMD5(LPBYTE md5, char * szBuffer);
//-----------------------------------------------------------------------------
// Conversion from binary array to string. The caller must ensure that
// the buffer has at least ((cbBinary * 2) + 1) characters
template <typename xchar>
xchar * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, xchar * szBuffer)
{
xchar * szSaveBuffer = szBuffer;
// Verify the binary pointer
if(pbBinary && cbBinary)
{
// Convert the bytes to string array
for(size_t i = 0; i < cbBinary; i++)
{
*szBuffer++ = IntToHexChar[pbBinary[i] >> 0x04];
*szBuffer++ = IntToHexChar[pbBinary[i] & 0x0F];
}
}
// Terminate the string
*szBuffer = 0;
return szSaveBuffer;
}
//-----------------------------------------------------------------------------
// Structure query key

View File

@@ -185,10 +185,10 @@ CASC_CSV::~CASC_CSV()
m_szCsvFile = NULL;
}
int CASC_CSV::Load(const TCHAR * szFileName)
DWORD CASC_CSV::Load(const TCHAR * szFileName)
{
DWORD cbFileData = 0;
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
m_szCsvFile = (char *)LoadFileToMemory(szFileName, &cbFileData);
if (m_szCsvFile != NULL)
@@ -199,19 +199,19 @@ int CASC_CSV::Load(const TCHAR * szFileName)
m_nCsvFile = cbFileData;
// Parse the data
nError = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
dwErrCode = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
}
else
{
nError = GetLastError();
dwErrCode = GetLastError();
}
return nError;
return dwErrCode;
}
int CASC_CSV::Load(LPBYTE pbData, size_t cbData)
DWORD CASC_CSV::Load(LPBYTE pbData, size_t cbData)
{
int nError = ERROR_NOT_ENOUGH_MEMORY;
DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
m_szCsvFile = new char[cbData + 1];
if (m_szCsvFile != NULL)
@@ -223,10 +223,10 @@ int CASC_CSV::Load(LPBYTE pbData, size_t cbData)
m_nCsvFile = cbData;
// Parse the data
nError = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
dwErrCode = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
}
return nError;
return dwErrCode;
}
bool CASC_CSV::LoadNextLine()

View File

@@ -69,8 +69,8 @@ class CASC_CSV
CASC_CSV(size_t nLinesMax, bool bHasHeader);
~CASC_CSV();
int Load(LPBYTE pbData, size_t cbData);
int Load(const TCHAR * szFileName);
DWORD Load(LPBYTE pbData, size_t cbData);
DWORD Load(const TCHAR * szFileName);
bool LoadNextLine();
const CASC_CSV_COLUMN & operator[](const char * szColumnName) const;

View File

@@ -615,7 +615,7 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
HINTERNET hRequest;
DWORD dwTemp = 0;
bool bFileAvailable = false;
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
// Check alternate ports
if(dwStreamFlags & STREAM_FLAG_USE_PORT_1119)
@@ -625,10 +625,10 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
// Don't download if we are not connected to the internet
if(!InternetGetConnectedState(&dwTemp, 0))
nError = GetLastError();
dwErrCode = GetLastError();
// Initiate the connection to the internet
if(nError == ERROR_SUCCESS)
if(dwErrCode == ERROR_SUCCESS)
{
pStream->Base.Http.hInternet = InternetOpen(_T("agent/2.17.2.6700"),
INTERNET_OPEN_TYPE_PRECONFIG,
@@ -636,11 +636,11 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
NULL,
0);
if(pStream->Base.Http.hInternet == NULL)
nError = GetLastError();
dwErrCode = GetLastError();
}
// Connect to the server
if(nError == ERROR_SUCCESS)
if(dwErrCode == ERROR_SUCCESS)
{
TCHAR szServerName[MAX_PATH];
DWORD dwFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_CACHE_WRITE;
@@ -656,11 +656,11 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
dwFlags,
0);
if(pStream->Base.Http.hConnect == NULL)
nError = GetLastError();
dwErrCode = GetLastError();
}
// Now try to query the file size
if(nError == ERROR_SUCCESS)
if(dwErrCode == ERROR_SUCCESS)
{
// Open HTTP request to the file
hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0);
@@ -702,7 +702,7 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
}
else
{
nError = ERROR_FILE_NOT_FOUND;
dwErrCode = ERROR_FILE_NOT_FOUND;
}
}
InternetCloseHandle(hRequest);
@@ -714,7 +714,7 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
if(bFileAvailable == false)
{
pStream->BaseClose(pStream);
SetLastError(nError);
SetLastError(dwErrCode);
return false;
}
@@ -889,7 +889,7 @@ static bool BlockStream_Read(
BlockBufferOffset = (DWORD)(ByteOffset & (BlockSize - 1));
// Allocate buffer for reading blocks
TransferBuffer = BlockBuffer = CASC_ALLOC(BYTE, (BlockCount * BlockSize));
TransferBuffer = BlockBuffer = CASC_ALLOC<BYTE>(BlockCount * BlockSize);
if(TransferBuffer == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -1054,7 +1054,7 @@ static TFileStream * AllocateFileStream(
}
// Allocate the stream structure for the given stream type
pStream = (TFileStream *)CASC_ALLOC(BYTE, StreamSize + FileNameSize + sizeof(TCHAR));
pStream = (TFileStream *)CASC_ALLOC<BYTE>(StreamSize + FileNameSize + sizeof(TCHAR));
if(pStream != NULL)
{
// Zero the entire structure
@@ -1137,7 +1137,7 @@ static bool FlatStream_LoadBitmap(TBlockStream * pStream)
if(ByteOffset + BitmapSize + sizeof(FILE_BITMAP_FOOTER) == pStream->Base.File.FileSize)
{
// Allocate space for the bitmap
FileBitmap = CASC_ALLOC(BYTE, BitmapSize);
FileBitmap = CASC_ALLOC<BYTE>(BitmapSize);
if(FileBitmap != NULL)
{
// Load the bitmap bits
@@ -1349,12 +1349,11 @@ static bool FlatStream_CreateMirror(TBlockStream * pStream)
}
// Allocate the bitmap array
FileBitmap = CASC_ALLOC(BYTE, dwBitmapSize);
FileBitmap = CASC_ALLOC_ZERO<BYTE>(dwBitmapSize);
if(FileBitmap == NULL)
return false;
// Initialize the bitmap
memset(FileBitmap, 0, dwBitmapSize);
pStream->FileBitmap = FileBitmap;
pStream->BitmapSize = dwBitmapSize;
pStream->BlockSize = DEFAULT_BLOCK_SIZE;
@@ -1521,7 +1520,7 @@ static bool PartStream_LoadBitmap(TBlockStream * pStream)
if((ByteOffset + BitmapSize) < pStream->Base.File.FileSize)
{
// Allocate space for the array of PART_FILE_MAP_ENTRY
FileBitmap = CASC_ALLOC(PART_FILE_MAP_ENTRY, BlockCount);
FileBitmap = CASC_ALLOC<PART_FILE_MAP_ENTRY>(BlockCount);
if(FileBitmap != NULL)
{
// Load the block map
@@ -1771,12 +1770,11 @@ static bool PartStream_CreateMirror(TBlockStream * pStream)
}
// Allocate the bitmap array
FileBitmap = CASC_ALLOC(BYTE, dwBitmapSize);
FileBitmap = CASC_ALLOC_ZERO<BYTE>(dwBitmapSize);
if(FileBitmap == NULL)
return false;
// Initialize the bitmap
memset(FileBitmap, 0, dwBitmapSize);
pStream->FileBitmap = FileBitmap;
pStream->BitmapSize = dwBitmapSize;
pStream->BlockSize = DEFAULT_BLOCK_SIZE;
@@ -2268,7 +2266,7 @@ static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamF
pStream->BlockRead = (BLOCK_READ)Block4Stream_BlockRead;
// Allocate work space for numeric names
szNameBuff = CASC_ALLOC(TCHAR, nNameLength + 4);
szNameBuff = CASC_ALLOC<TCHAR>(nNameLength + 4);
if(szNameBuff != NULL)
{
// Set the base flags
@@ -2283,7 +2281,7 @@ static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamF
break;
// If the open succeeded, we re-allocate the base provider array
NewBaseArray = CASC_ALLOC(TBaseProviderData, dwBaseFiles + 1);
NewBaseArray = CASC_ALLOC<TBaseProviderData>(dwBaseFiles + 1);
if(NewBaseArray == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -2369,7 +2367,7 @@ static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamF
*/
TFileStream * FileStream_CreateFile(
const TCHAR * szFileName,
LPCTSTR szFileName,
DWORD dwStreamFlags)
{
TFileStream * pStream;
@@ -2424,7 +2422,7 @@ TFileStream * FileStream_CreateFile(
*/
TFileStream * FileStream_OpenFile(
const TCHAR * szFileName,
LPCTSTR szFileName,
DWORD dwStreamFlags)
{
DWORD dwProvider = dwStreamFlags & STREAM_PROVIDERS_MASK;
@@ -2473,7 +2471,7 @@ const TCHAR * FileStream_GetFileName(TFileStream * pStream)
* \a pdwStreamProvider Pointer to a DWORD variable that receives stream provider (STREAM_PROVIDER_XXX)
*/
size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider)
size_t FileStream_Prefix(LPCTSTR szFileName, DWORD * pdwProvider)
{
size_t nPrefixLength1 = 0;
size_t nPrefixLength2 = 0;
@@ -2592,14 +2590,6 @@ bool FileStream_SetCallback(TFileStream * pStream, STREAM_DOWNLOAD_CALLBACK pfnC
*/
bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead)
{
//FILE * fp = fopen("E:\\Loading.txt", "at");
//if(fp != NULL)
//{
// ULONGLONG ByteOffset = (pByteOffset != NULL) ? pByteOffset[0] : 0;
// fprintf(fp, "%-32ws\t%08X\t%08X\n", GetPlainFileName(pStream->szFileName), (ULONG)ByteOffset, dwBytesToRead);
// fclose(fp);
//}
assert(pStream->StreamRead != NULL);
return pStream->StreamRead(pStream, pByteOffset, pvBuffer, dwBytesToRead);
}

View File

@@ -247,10 +247,10 @@ struct TEncryptedStream : public TBlockStream
//-----------------------------------------------------------------------------
// Public functions for file stream
TFileStream * FileStream_CreateFile(const TCHAR * szFileName, DWORD dwStreamFlags);
TFileStream * FileStream_OpenFile(const TCHAR * szFileName, DWORD dwStreamFlags);
TFileStream * FileStream_CreateFile(LPCTSTR szFileName, DWORD dwStreamFlags);
TFileStream * FileStream_OpenFile(LPCTSTR szFileName, DWORD dwStreamFlags);
const TCHAR * FileStream_GetFileName(TFileStream * pStream);
size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider);
size_t FileStream_Prefix(LPCTSTR szFileName, DWORD * pdwProvider);
bool FileStream_SetCallback(TFileStream * pStream, STREAM_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData);

View File

@@ -30,28 +30,7 @@ inline void SET_NODE_INT32(void * node, size_t offset, DWORD value)
PtrValue[0] = value;
}
/*
static bool CompareFileNode(void * pvObject, void * pvUserData)
{
PCASC_COMPARE_CONTEXT pCtx = (PCASC_COMPARE_CONTEXT)pvUserData;
PCASC_FILE_TREE pFileTree = (PCASC_FILE_TREE)pCtx->pThis;
PCASC_FILE_NODE pFileNode = (PCASC_FILE_NODE)pvObject;
char szFullPath[MAX_PATH];
// First of all, the name hash must match
if(pFileNode->FileNameHash == pCtx->FileNameHash)
{
// Then also compare the full path name
pFileTree->PathAt(szFullPath, _countof(szFullPath), pFileNode);
if(!_stricmp(szFullPath, pCtx->szFileName))
{
return true;
}
}
return false;
}
*/
//-----------------------------------------------------------------------------
// Protected functions
@@ -224,11 +203,11 @@ bool CASC_FILE_TREE::RebuildNameMaps()
//-----------------------------------------------------------------------------
// Public functions
int CASC_FILE_TREE::Create(DWORD Flags)
DWORD CASC_FILE_TREE::Create(DWORD Flags)
{
PCASC_FILE_NODE pRootNode;
size_t FileNodeSize = FIELD_OFFSET(CASC_FILE_NODE, ExtraValues);
int nError;
DWORD dwErrCode;
// Initialize the file tree
memset(this, 0, sizeof(CASC_FILE_TREE));
@@ -242,9 +221,9 @@ int CASC_FILE_TREE::Create(DWORD Flags)
FileNodeSize += sizeof(DWORD);
// Create the array for FileDataId -> CASC_FILE_NODE
nError = FileDataIds.Create<PCASC_FILE_NODE>(START_ITEM_COUNT);
if(nError != ERROR_SUCCESS)
return nError;
dwErrCode = FileDataIds.Create<PCASC_FILE_NODE>(START_ITEM_COUNT);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
}
// Shall we use the locale ID in the tree node?
@@ -264,12 +243,12 @@ int CASC_FILE_TREE::Create(DWORD Flags)
FileNodeSize = ALIGN_TO_SIZE(FileNodeSize, 8);
// Initialize the dynamic array
nError = NodeTable.Create(FileNodeSize, START_ITEM_COUNT);
if(nError == ERROR_SUCCESS)
dwErrCode = NodeTable.Create(FileNodeSize, START_ITEM_COUNT);
if(dwErrCode == ERROR_SUCCESS)
{
// Create the dynamic array that will hold the node names
nError = NameTable.Create<char>(START_ITEM_COUNT);
if(nError == ERROR_SUCCESS)
dwErrCode = NameTable.Create<char>(START_ITEM_COUNT);
if(dwErrCode == ERROR_SUCCESS)
{
// Insert the first "root" node, without name
pRootNode = (PCASC_FILE_NODE)NodeTable.Insert(1);
@@ -287,8 +266,8 @@ int CASC_FILE_TREE::Create(DWORD Flags)
// Create both maps
if(!RebuildNameMaps())
nError = ERROR_NOT_ENOUGH_MEMORY;
return nError;
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
return dwErrCode;
}
void CASC_FILE_TREE::Free()
@@ -307,21 +286,26 @@ void CASC_FILE_TREE::Free()
PCASC_FILE_NODE CASC_FILE_TREE::InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const char * szFileName, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
{
CASC_COMPARE_CONTEXT CmpCtx;
PCASC_FILE_NODE pFileNode;
ULONGLONG FileNameHash;
// Sanity checks
assert(szFileName != NULL && szFileName[0] != 0);
assert(pCKeyEntry != NULL);
//char szCKey[MD5_STRING_SIZE+1];
//char szEKey[MD5_STRING_SIZE+1];
//StringFromBinary(pCKeyEntry->CKey, MD5_HASH_SIZE, szCKey);
//StringFromBinary(pCKeyEntry->EKey, MD5_HASH_SIZE, szEKey);
//printf("%s\t%s\t%s\n", szCKey, szEKey, szFileName);
//BREAK_ON_XKEY3(pCKeyEntry->EKey, 0x03, 0xDC, 0x7D);
// Calculate the file name hash
CmpCtx.FileNameHash = CalcFileNameHash(szFileName);
// CmpCtx.szFileName = szFileName;
// CmpCtx.pThis = this;
FileNameHash = CalcFileNameHash(szFileName);
// Do nothing if the file name is there already.
// pFileNode = (PCASC_FILE_NODE)NameMap.FindObjectEx(CompareFileNode, &CmpCtx);
pFileNode = (PCASC_FILE_NODE)NameMap.FindObject(&CmpCtx.FileNameHash);
pFileNode = (PCASC_FILE_NODE)NameMap.FindObject(&FileNameHash);
if(pFileNode == NULL)
{
// Insert new item
@@ -329,7 +313,7 @@ PCASC_FILE_NODE CASC_FILE_TREE::InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const
if(pFileNode != NULL)
{
// Supply the name hash
pFileNode->FileNameHash = CmpCtx.FileNameHash;
pFileNode->FileNameHash = FileNameHash;
// Set the file data id and the extra values
SetExtras(pFileNode, FileDataId, LocaleFlags, ContentFlags);
@@ -340,12 +324,13 @@ PCASC_FILE_NODE CASC_FILE_TREE::InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const
// Also make sure that it's in the file data id table, if the table is initialized
InsertToIdTable(pFileNode);
// Set the file name of the new file node. This also increments the number of references
// Set the file name of the new file node
SetNodeFileName(pFileNode, szFileName);
// If we created a new node, we need to increment the reference count
assert(pCKeyEntry->RefCount != 0xFFFF);
pCKeyEntry->RefCount++;
FileNodes++;
}
}
@@ -492,8 +477,6 @@ PCASC_FILE_NODE CASC_FILE_TREE::Find(const char * szFullPath, DWORD FileDataId,
if(pFileNode != NULL && pFindData != NULL)
{
GetExtras(pFileNode, &pFindData->dwFileDataId, &pFindData->dwLocaleFlags, &pFindData->dwContentFlags);
pFindData->bCanOpenByName = (pFileNode->FileNameHash != 0);
pFindData->bCanOpenByDataId = (FileDataIdOffset != 0);
}
return pFileNode;
@@ -543,7 +526,85 @@ bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szF
{
ULONGLONG FileNameHash = 0;
PCASC_FILE_NODE pFolderNode = NULL;
const char * szNodeBegin = szFileName;
CASC_PATH<char> PathBuffer;
LPCSTR szNodeBegin = szFileName;
size_t nFileNode = NodeTable.IndexOf(pFileNode);
size_t i;
DWORD Parent = 0;
// Sanity checks
assert(szFileName != NULL && szFileName[0] != 0);
// Traverse the entire path. For each subfolder, we insert an appropriate fake entry
for(i = 0; szFileName[i] != 0; i++)
{
char chOneChar = szFileName[i];
// Is there a path separator?
// Note: Warcraft III paths may contain "mount points".
// Example: "frFR-War3Local.mpq:Maps/FrozenThrone/Campaign/NightElfX06Interlude.w3x:war3map.j"
if(chOneChar == '\\' || chOneChar == '/' || chOneChar == ':')
{
// Calculate hash of the file name up to the end of the node name
FileNameHash = CalcNormNameHash(PathBuffer, i);
// If the entry is not there yet, create new one
if((pFolderNode = Find(FileNameHash)) == NULL)
{
// Insert new entry to the tree
pFolderNode = InsertNew();
if(pFolderNode == NULL)
return false;
// Populate the file entry
pFolderNode->FileNameHash = FileNameHash;
pFolderNode->Parent = Parent;
pFolderNode->Flags |= (chOneChar == ':') ? (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT) : CFN_FLAG_FOLDER;
FolderNodes++;
// Set the node sub name to the node
SetNodePlainName(pFolderNode, szNodeBegin, szFileName + i);
// Insert the entry to the name map
InsertToHashTable(pFolderNode);
}
// Move the parent to the current node
Parent = (DWORD)NodeTable.IndexOf(pFolderNode);
// Move the begin of the node after the separator
szNodeBegin = szFileName + i + 1;
}
// Copy the next character, even if it was slash/backslash before
PathBuffer.AppendChar(AsciiToUpperTable_BkSlash[chOneChar]);
}
// If anything left, this is gonna be our node name
if(szNodeBegin < szFileName + i)
{
// We need to reset the file node pointer, as the file node table might have changed
pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nFileNode);
// Write the plain file name to the node
SetNodePlainName(pFileNode, szNodeBegin, szFileName + i);
pFileNode->Parent = Parent;
// Also insert the node to the hash table so CascOpenFile can find it
if(pFileNode->FileNameHash == 0)
{
pFileNode->FileNameHash = CalcNormNameHash(PathBuffer, i);
InsertToHashTable(pFileNode);
}
}
return true;
}
/*
bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szFileName)
{
ULONGLONG FileNameHash = 0;
PCASC_FILE_NODE pFolderNode = NULL;
LPCSTR szNodeBegin = szFileName;
char szPathBuffer[MAX_PATH+1];
size_t nFileNode = NodeTable.IndexOf(pFileNode);
size_t i;
@@ -551,7 +612,6 @@ bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szF
// Sanity checks
assert(szFileName != NULL && szFileName[0] != 0);
assert(pFileNode->pCKeyEntry != NULL);
// Traverse the entire path. For each subfolder, we insert an appropriate fake entry
for(i = 0; szFileName[i] != 0; i++)
@@ -577,8 +637,8 @@ bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szF
// Populate the file entry
pFolderNode->FileNameHash = FileNameHash;
pFolderNode->Parent = Parent;
pFolderNode->Flags |= (chOneChar == ':') ? CFN_FLAG_MOUNT_POINT : 0;
pFolderNode->Flags |= CFN_FLAG_FOLDER;
pFolderNode->Flags |= (chOneChar == ':') ? (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT) : CFN_FLAG_FOLDER;
FolderNodes++;
// Set the node sub name to the node
SetNodePlainName(pFolderNode, szNodeBegin, szFileName + i);
@@ -604,12 +664,20 @@ bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szF
// We need to reset the file node pointer, as the file node table might have changed
pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nFileNode);
// Write the plain file name to the node
SetNodePlainName(pFileNode, szNodeBegin, szFileName + i);
pFileNode->Parent = Parent;
// Also insert the node to the hash table so CascOpenFile can find it
if(pFileNode->FileNameHash == 0)
{
pFileNode->FileNameHash = CalcNormNameHash(szPathBuffer, i);
InsertToHashTable(pFileNode);
}
}
return true;
}
*/
size_t CASC_FILE_TREE::GetMaxFileIndex()
{
if(FileDataIds.IsInitialized())

View File

@@ -25,32 +25,25 @@
typedef struct _CASC_FILE_NODE
{
ULONGLONG FileNameHash; // Jenkins hash of the normalized file name (uppercase, backslashes)
PCASC_CKEY_ENTRY pCKeyEntry; // Pointer to the CKey entry.
PCASC_CKEY_ENTRY pCKeyEntry; // Pointer to the CKey entry
DWORD Parent; // The index of a parent directory. If CASC_INVALID_INDEX, then this is the root item
DWORD NameIndex; // Index of the node name. If CASC_INVALID_INDEX, then this node has no name
USHORT NameLength; // Length of the node name (without the zero terminator)
USHORT Flags; // See CFE_FLAG_XXX
USHORT Flags; // See CFN_FLAG_XXX
DWORD ExtraValues[4]; // FileDataId: Only if FTREE_FLAG_USE_DATA_ID specified at create
// LocaleFlags: Only if FTREE_FLAG_USE_LOCALE_FLAGS specified at create
// ContentFlags: Only if FTREE_FLAG_USE_CONTENT_FLAGS specified at create
} CASC_FILE_NODE, *PCASC_FILE_NODE;
// Common structure for comparing a file node
typedef struct _CASC_COMPARE_CONTEXT
{
ULONGLONG FileNameHash;
const char * szFileName;
void * pThis;
} CASC_COMPARE_CONTEXT, *PCASC_COMPARE_CONTEXT;
// Main structure for the file tree
class CASC_FILE_TREE
{
public:
// Initializes/destroys the entire tree
int Create(DWORD Flags = 0);
DWORD Create(DWORD Flags = 0);
void Free();
// Inserts a new node to the tree; either with name or nameless
@@ -108,6 +101,8 @@ class CASC_FILE_TREE
size_t FileDataIdOffset; // If nonzero, this is the offset of the "FileDataId" field in the CASC_FILE_NODE
size_t LocaleFlagsOffset; // If nonzero, this is the offset of the "LocaleFlags" field in the CASC_FILE_NODE
size_t ContentFlagsOffset; // If nonzero, this is the offset of the "ContentFlags" field in the CASC_FILE_NODE
size_t FolderNodes; // Number of folder nodes
size_t FileNodes; // Number of file nodes
DWORD KeyLength; // Actual length of the key supported by the root handler
};

View File

@@ -36,7 +36,7 @@ static PLISTFILE_CACHE ListFile_CreateCache(DWORD dwFileSize)
PLISTFILE_CACHE pCache;
// Allocate cache for one file block
pCache = (PLISTFILE_CACHE)CASC_ALLOC(BYTE, sizeof(LISTFILE_CACHE) + dwFileSize);
pCache = (PLISTFILE_CACHE)CASC_ALLOC<BYTE>(sizeof(LISTFILE_CACHE) + dwFileSize);
if(pCache != NULL)
{
// Set the initial pointers
@@ -254,7 +254,7 @@ size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PD
PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile;
const char * szTemp;
size_t nLength = 0;
int nError = ERROR_SUCCESS;
DWORD dwErrCode = ERROR_SUCCESS;
// Check for parameters
for(;;)
@@ -266,12 +266,12 @@ size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PD
if(pCache->Flags & LISTFILE_FLAG_USES_FILEDATAID)
{
// Retrieve the data ID from the current position
nError = ListFile_GetFileDataId(pCache, &FileDataId);
if(nError == ERROR_NO_MORE_FILES)
dwErrCode = ListFile_GetFileDataId(pCache, &FileDataId);
if(dwErrCode == ERROR_NO_MORE_FILES)
break;
// If there was an error, skip the current line
if(nError != ERROR_SUCCESS || FileDataId == CASC_INVALID_ID)
if(dwErrCode != ERROR_SUCCESS || FileDataId == CASC_INVALID_ID)
{
ListFile_GetNextLine(pvListFile, &szTemp, &szTemp);
continue;
@@ -282,7 +282,7 @@ size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PD
nLength = ListFile_GetNextLine(pvListFile, szBuffer, nMaxChars);
if(nLength == 0)
{
nError = GetLastError();
dwErrCode = GetLastError();
break;
}
@@ -291,8 +291,8 @@ size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PD
return nLength;
}
if(nError != ERROR_SUCCESS)
SetLastError(nError);
if(dwErrCode != ERROR_SUCCESS)
SetLastError(dwErrCode);
return nLength;
}

View File

@@ -129,13 +129,8 @@ class CASC_MAP
return ERROR_NOT_ENOUGH_MEMORY;
// Allocate new map for the objects
m_HashTable = (void **)CASC_ALLOC(void *, m_HashTableSize);
if(m_HashTable == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// Initialize the map object
memset(m_HashTable, 0, m_HashTableSize * sizeof(void *));
return ERROR_SUCCESS;
m_HashTable = (void **)CASC_ALLOC_ZERO<void *>(m_HashTableSize);
return (m_HashTable != NULL) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
}
void * FindObject(void * pvKey, PDWORD PtrIndex = NULL)

View File

@@ -0,0 +1,182 @@
/*****************************************************************************/
/* Path.h Copyright (c) Ladislav Zezula 2019 */
/*---------------------------------------------------------------------------*/
/* Global path merger class */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 24.07.17 1.00 Lad Created */
/*****************************************************************************/
#ifndef __CASC_PATH_H__
#define __CASC_PATH_H__
//-----------------------------------------------------------------------------
// Structures
template <typename xchar>
struct CASC_PATH
{
CASC_PATH(xchar chSeparator = PATH_SEP_CHAR)
{
m_szBufferBegin = m_szBufferPtr = m_Buffer;
m_szBufferEnd = m_szBufferBegin + _countof(m_Buffer);
m_chSeparator = chSeparator;
m_Buffer[0] = 0;
}
~CASC_PATH()
{
if(m_szBufferBegin != m_Buffer)
{
CASC_FREE(m_szBufferBegin);
}
}
// LPCTSTR szPath = Path;
operator const xchar *()
{
return m_szBufferBegin;
}
// LPTSTR szPath = Path.New();
xchar * New()
{
xchar * szNewStr;
if((szNewStr = CASC_ALLOC<xchar>(Length() + 1)) != NULL)
{
memcpy(szNewStr, m_szBufferBegin, Length() * sizeof(xchar));
szNewStr[Length()] = 0;
}
return szNewStr;
}
size_t Save()
{
return (m_szBufferPtr - m_szBufferBegin);
}
bool Restore(size_t nSavePos)
{
if((m_szBufferBegin + nSavePos) < m_szBufferEnd)
{
m_szBufferPtr = m_szBufferBegin + nSavePos;
m_szBufferPtr[0] = 0;
return true;
}
return false;
}
// Path.Copy(szBuffer, _countof(szBuffer));
bool Copy(xchar * szBuffer, size_t cchBuffer)
{
if((Length() + 1) > cchBuffer)
return false;
memcpy(szBuffer, m_szBufferBegin, Length() * sizeof(xchar));
szBuffer[Length()] = 0;
return true;
}
size_t Length()
{
return m_szBufferPtr - m_szBufferBegin;
}
bool SetPathRoot(const xchar * szRoot)
{
// Make sure that there is no characters
m_szBufferPtr = m_szBufferBegin;
m_szBufferPtr[0] = 0;
// Append the root path
return AppendString(szRoot, false);
}
bool AppendStringN(const xchar * szString, size_t nMaxchars, bool bWithSeparator)
{
const xchar * szStringEnd = szString + nMaxchars;
xchar chOneChar;
if(szString && szString[0] && nMaxchars)
{
// Append separator, if required and not in begin of the string
if(m_szBufferPtr > m_szBufferBegin && bWithSeparator)
AppendChar(m_chSeparator);
// Append the characters from the string
while(szString[0] && szString < szStringEnd)
{
// Retrieve the single character
chOneChar = *szString++;
// Normalize the character
if(chOneChar == '/' || chOneChar == '\\')
chOneChar = m_chSeparator;
if(!AppendChar(chOneChar))
break;
}
}
return true;
}
bool AppendString(const xchar * szString, bool bWithSeparator)
{
return AppendStringN(szString, (0x10000 / sizeof(xchar)), bWithSeparator);
}
bool AppendEKey(LPBYTE pbEKey)
{
xchar szEKey[MD5_STRING_SIZE + 1];
StringFromBinary(pbEKey, MD5_HASH_SIZE, szEKey);
AppendStringN(szEKey, 2, true);
AppendStringN(szEKey+2, 2, true);
return AppendString(szEKey, true);
}
bool AppendChar(xchar chOneChar)
{
// Buffer our of space?
if((m_szBufferPtr + 2) >= m_szBufferEnd)
{
xchar * szOldBuffer = m_szBufferBegin;
xchar * szNewBuffer;
size_t nToAllocate = (m_szBufferEnd - m_szBufferBegin) * 2;
size_t nLength = (m_szBufferPtr - m_szBufferBegin);
if((szNewBuffer = CASC_ALLOC<xchar>(nToAllocate)) == NULL)
return false;
// Copy the chars
memcpy(szNewBuffer, m_szBufferBegin, (m_szBufferPtr - m_szBufferBegin) * sizeof(xchar));
m_szBufferBegin = szNewBuffer;
m_szBufferPtr = m_szBufferBegin + nLength;
m_szBufferEnd = m_szBufferBegin + nToAllocate;
// Free the old buffer
if(szOldBuffer != m_Buffer)
CASC_FREE(szOldBuffer);
}
// Append the character
*m_szBufferPtr++ = chOneChar;
m_szBufferPtr[0] = 0;
return true;
}
protected:
xchar * m_szBufferBegin;
xchar * m_szBufferPtr;
xchar * m_szBufferEnd;
xchar m_Buffer[MAX_PATH];
xchar m_chSeparator;
};
#endif // __CASC_PATH_H__

View File

@@ -81,10 +81,6 @@ PCASC_CKEY_ENTRY TFileTreeRoot::Search(TCascSearch * pSearch, PCASC_FIND_DATA pF
// Retrieve the extra values (FileDataId, file size and locale flags)
FileTree.GetExtras(pFileNode, &pFindData->dwFileDataId, &pFindData->dwLocaleFlags, &pFindData->dwContentFlags);
// Supply the bCanOpenByDataId variable
pFindData->bCanOpenByName = (pFileNode->FileNameHash != 0);
pFindData->bCanOpenByDataId = (pFindData->dwFileDataId != CASC_INVALID_ID);
// Return the found CKey entry
return pFileNode->pCKeyEntry;
}

View File

@@ -62,7 +62,7 @@ recastnavigation (Recast is state of the art navigation mesh construction toolse
CascLib (An open-source implementation of library for reading CASC storage from Blizzard games since 2014)
https://github.com/ladislav-zezula/CascLib
Version: b802391cfb9418d5550dc1671f18da1ca1c445d4
Version: b91f87c770c78340dcd96df970e55b5c0469e884
rapidjson (A fast JSON parser/generator for C++ with both SAX/DOM style API http://rapidjson.org/)
https://github.com/miloyip/rapidjson