mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-24 19:06:49 +01:00
418 lines
19 KiB
C++
418 lines
19 KiB
C++
/*****************************************************************************/
|
|
/* CascCommon.h Copyright (c) Ladislav Zezula 2014 */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Common functions for CascLib */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Date Ver Who Comment */
|
|
/* -------- ---- --- ------- */
|
|
/* 29.04.14 1.00 Lad The first version of CascCommon.h */
|
|
/*****************************************************************************/
|
|
|
|
#ifndef __CASCCOMMON_H__
|
|
#define __CASCCOMMON_H__
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Compression support
|
|
|
|
// Include functions from zlib
|
|
#ifndef __SYS_ZLIB
|
|
#include "zlib/zlib.h"
|
|
#else
|
|
#include <zlib.h>
|
|
#endif
|
|
|
|
#include "CascPort.h"
|
|
#include "common/Common.h"
|
|
#include "common/Map.h"
|
|
#include "common/FileStream.h"
|
|
#include "common/ListFile.h"
|
|
|
|
// Headers from LibTomCrypt
|
|
#include "libtomcrypt/src/headers/tomcrypt.h"
|
|
|
|
// For HashStringJenkins
|
|
#include "jenkins/lookup.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CascLib private defines
|
|
|
|
#define CASC_GAME_HOTS 0x00010000 // Heroes of the Storm
|
|
#define CASC_GAME_WOW6 0x00020000 // World of Warcraft - Warlords of Draenor
|
|
#define CASC_GAME_DIABLO3 0x00030000 // Diablo 3 Since PTR 2.2.0
|
|
#define CASC_GAME_MASK 0xFFFF0000 // Mask for getting game ID
|
|
|
|
#define CASC_INDEX_COUNT 0x10
|
|
#define CASC_FILE_KEY_SIZE 0x09 // Size of the file key
|
|
#define CASC_MAX_DATA_FILES 0x100
|
|
#define CASC_MAX_MAR_FILES 3 // Maximum of 3 MAR files are supported
|
|
#define CASC_MNDX_SIGNATURE 0x58444E4D // 'MNDX'
|
|
|
|
#define CASC_SEARCH_HAVE_NAME 0x0001 // Indicated that previous search found a name
|
|
|
|
#define BLTE_HEADER_SIGNATURE 0x45544C42 // 'BLTE' header in the data files
|
|
#define BLTE_HEADER_DELTA 0x1E // Distance of BLTE header from begin of the header area
|
|
#define MAX_HEADER_AREA_SIZE 0x2A // Length of the file header area
|
|
|
|
// File header area in the data.xxx:
|
|
// BYTE HeaderHash[MD5_HASH_SIZE]; // MD5 of the frame array
|
|
// DWORD dwFileSize; // Size of the file (see comment before CascGetFileSize for details)
|
|
// BYTE SomeSize[4]; // Some size (big endian)
|
|
// BYTE Padding[6]; // Padding (?)
|
|
// DWORD dwSignature; // Must be "BLTE"
|
|
// BYTE HeaderSizeAsBytes[4]; // Header size in bytes (big endian)
|
|
// BYTE MustBe0F; // Must be 0x0F. Optional, only if HeaderSizeAsBytes != 0
|
|
// BYTE FrameCount[3]; // Frame count (big endian). Optional, only if HeaderSizeAsBytes != 0
|
|
|
|
// Prevent problems with CRT "min" and "max" functions,
|
|
// as they are not defined on all platforms
|
|
#define CASCLIB_MIN(a, b) ((a < b) ? a : b)
|
|
#define CASCLIB_MAX(a, b) ((a > b) ? a : b)
|
|
#define CASCLIB_UNUSED(p) ((void)(p))
|
|
|
|
#define CASC_PACKAGE_BUFFER 0x1000
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// On-disk structures
|
|
|
|
typedef struct _FILE_LOCALE_BLOCK
|
|
{
|
|
DWORD NumberOfFiles; // Number of entries
|
|
DWORD Flags;
|
|
DWORD Locales; // File locale mask (CASC_LOCALE_XXX)
|
|
|
|
// Followed by a block of 32-bit integers (count: NumberOfFiles)
|
|
// Followed by the MD5 and file name hash (count: NumberOfFiles)
|
|
|
|
} FILE_LOCALE_BLOCK, *PFILE_LOCALE_BLOCK;
|
|
|
|
typedef struct _FILE_ROOT_ENTRY
|
|
{
|
|
DWORD EncodingKey[4]; // MD5 of the file
|
|
ULONGLONG FileNameHash; // Jenkins hash of the file name
|
|
|
|
} FILE_ROOT_ENTRY, *PFILE_ROOT_ENTRY;
|
|
|
|
typedef struct _ROOT_BLOCK_INFO
|
|
{
|
|
PFILE_LOCALE_BLOCK pLocaleBlockHdr; // Pointer to the locale block
|
|
PDWORD pInt32Array; // Pointer to the array of 32-bit integers
|
|
PFILE_ROOT_ENTRY pRootEntries;
|
|
|
|
} ROOT_BLOCK_INFO, *PROOT_BLOCK_INFO;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// In-memory structures
|
|
|
|
class TMndxFindResult;
|
|
struct TFileStream;
|
|
struct _MAR_FILE;
|
|
|
|
typedef struct _CASC_INDEX_ENTRY
|
|
{
|
|
BYTE IndexKey[CASC_FILE_KEY_SIZE]; // The first 9 bytes of the encoding key
|
|
BYTE FileOffsetBE[5]; // Index of data file and offset within (big endian).
|
|
BYTE FileSizeLE[4]; // Size occupied in the storage file (data.###). See comment before CascGetFileSize for details
|
|
} CASC_INDEX_ENTRY, *PCASC_INDEX_ENTRY;
|
|
|
|
typedef struct _CASC_MAPPING_TABLE
|
|
{
|
|
TCHAR * szFileName; // Name of the key mapping file
|
|
LPBYTE pbFileData; // Pointer to the file data
|
|
DWORD cbFileData; // Length of the file data
|
|
BYTE ExtraBytes; // (?) Extra bytes in the key record
|
|
BYTE SpanSizeBytes; // Size of field with file size
|
|
BYTE SpanOffsBytes; // Size of field with file offset
|
|
BYTE KeyBytes; // Size of the file key
|
|
BYTE SegmentBits; // Number of bits for the file offset (rest is archive index)
|
|
ULONGLONG MaxFileOffset;
|
|
|
|
PCASC_INDEX_ENTRY pIndexEntries; // Sorted array of index entries
|
|
DWORD nIndexEntries; // Number of index entries
|
|
|
|
} CASC_MAPPING_TABLE, *PCASC_MAPPING_TABLE;
|
|
|
|
typedef struct _CASC_FILE_FRAME
|
|
{
|
|
DWORD FrameArchiveOffset; // Archive file pointer corresponding to the begin of the frame
|
|
DWORD FrameFileOffset; // File pointer corresponding to the begin of the frame
|
|
DWORD CompressedSize; // Compressed size of the file
|
|
DWORD FrameSize; // Size of the frame
|
|
BYTE md5[MD5_HASH_SIZE]; // MD5 hash of the file sector
|
|
} CASC_FILE_FRAME, *PCASC_FILE_FRAME;
|
|
|
|
typedef struct _CASC_ENCODING_HEADER
|
|
{
|
|
BYTE Magic[2]; // "EN"
|
|
BYTE field_2;
|
|
BYTE field_3;
|
|
BYTE field_4;
|
|
BYTE field_5[2];
|
|
BYTE field_7[2];
|
|
BYTE NumSegments[4]; // Number of entries (big endian)
|
|
BYTE field_D[4];
|
|
BYTE field_11;
|
|
BYTE SegmentsPos[4]; // Offset of encoding segments
|
|
|
|
} CASC_ENCODING_HEADER, *PCASC_ENCODING_HEADER;
|
|
|
|
typedef struct _CASC_ENCODING_ENTRY
|
|
{
|
|
USHORT KeyCount; // Number of subitems
|
|
BYTE FileSizeBE[4]; // Compressed file size (header area + frame headers + compressed frames), in bytes
|
|
BYTE EncodingKey[MD5_HASH_SIZE]; // File encoding key
|
|
|
|
// Followed by the index keys
|
|
// (number of items = KeyCount)
|
|
// Followed by the index keys (number of items = KeyCount)
|
|
} CASC_ENCODING_ENTRY, *PCASC_ENCODING_ENTRY;
|
|
|
|
typedef struct _CASC_ROOT_LOCALE_BLOCK
|
|
{
|
|
DWORD NumberOfFiles; // Number of entries
|
|
DWORD Flags;
|
|
DWORD FileLocales; // File locales (CASC_LOCALE_XXX)
|
|
|
|
// Followed by a block of 32-bit integers (count: NumberOfFiles)
|
|
// Followed by the MD5 and file name hash (count: NumberOfFiles)
|
|
|
|
} CASC_ROOT_LOCALE_BLOCK, *PCASC_ROOT_LOCALE_BLOCK;
|
|
|
|
// Root file entry for CASC storages with MNDX root file (Heroes of the Storm)
|
|
// Corresponds to the in-file structure
|
|
typedef struct _CASC_ROOT_ENTRY_MNDX
|
|
{
|
|
DWORD Flags; // High 8 bits: Flags, low 24 bits: package index
|
|
BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key for the file
|
|
DWORD FileSize; // Uncompressed file size, in bytes
|
|
|
|
} CASC_ROOT_ENTRY_MNDX, *PCASC_ROOT_ENTRY_MNDX;
|
|
|
|
// Root file entry for CASC storages without MNDX root file (World of Warcraft 6.0+)
|
|
// Does not match to the in-file structure of the root entry
|
|
typedef struct _CASC_ROOT_ENTRY
|
|
{
|
|
ULONGLONG FileNameHash; // Jenkins hash of the file name
|
|
DWORD SumValue; // Sum value
|
|
DWORD Locales; // Locale flags of the file
|
|
DWORD EncodingKey[4]; // File encoding key (MD5)
|
|
|
|
} CASC_ROOT_ENTRY, *PCASC_ROOT_ENTRY;
|
|
|
|
// Definition of the hash table for CASC root items
|
|
typedef struct _CASC_ROOT_HASH_TABLE
|
|
{
|
|
PCASC_ROOT_ENTRY TablePtr; // Pointer to the CASC root table
|
|
DWORD TableSize; // Total size of the root table
|
|
DWORD ItemCount; // Number of items currently in the table
|
|
|
|
} CASC_ROOT_HASH_TABLE, *PCASC_ROOT_HASH_TABLE;
|
|
|
|
typedef struct _CASC_MNDX_INFO
|
|
{
|
|
bool bRootFileLoaded; // true if the root info file was properly loaded
|
|
BYTE RootFileName[MD5_HASH_SIZE]; // Name (aka MD5) of the root file
|
|
DWORD HeaderVersion; // Must be <= 2
|
|
DWORD FormatVersion;
|
|
DWORD field_1C;
|
|
DWORD field_20;
|
|
DWORD MarInfoOffset; // Offset of the first MAR entry info
|
|
DWORD MarInfoCount; // Number of the MAR info entries
|
|
DWORD MarInfoSize; // Size of the MAR info entry
|
|
DWORD MndxEntriesOffset;
|
|
DWORD MndxEntriesTotal; // Total number of MNDX root entries
|
|
DWORD MndxEntriesValid; // Number of valid MNDX root entries
|
|
DWORD MndxEntrySize; // Size of one MNDX root entry
|
|
struct _MAR_FILE * pMarFile1; // File name list for the packages
|
|
struct _MAR_FILE * pMarFile2; // File name list for names stripped of package names
|
|
struct _MAR_FILE * pMarFile3; // File name list for complete names
|
|
PCASC_ROOT_ENTRY_MNDX pMndxEntries;
|
|
PCASC_ROOT_ENTRY_MNDX * ppValidEntries;
|
|
|
|
} CASC_MNDX_INFO, *PCASC_MNDX_INFO;
|
|
|
|
typedef struct _CASC_PACKAGE
|
|
{
|
|
char * szFileName; // Pointer to file name
|
|
size_t nLength; // Length of the file name
|
|
|
|
} CASC_PACKAGE, *PCASC_PACKAGE;
|
|
|
|
typedef struct _CASC_PACKAGES
|
|
{
|
|
char * szNameBuffer; // Pointer to the buffer for file names
|
|
size_t NameEntries; // Number of name entries in Names
|
|
size_t NameBufferUsed; // Number of bytes used in the name buffer
|
|
size_t NameBufferMax; // Total size of the name buffer
|
|
|
|
CASC_PACKAGE Packages[1]; // List of packages
|
|
|
|
} CASC_PACKAGES, *PCASC_PACKAGES;
|
|
|
|
typedef struct _TCascStorage
|
|
{
|
|
const char * szClassName; // "TCascStorage"
|
|
const TCHAR * szIndexFormat; // Format of the index file name
|
|
TCHAR * szRootPath; // This is the game directory
|
|
TCHAR * szDataPath; // This is the directory where data files are
|
|
TCHAR * szIndexPath; // This is the directory where index files are
|
|
TCHAR * szUrlPath; // URL to the Blizzard servers
|
|
DWORD dwRefCount; // Number of references
|
|
DWORD dwGameInfo; // Game type
|
|
DWORD dwBuildNumber; // Game build number
|
|
DWORD dwFileBeginDelta; // This is number of bytes to shift back from archive offset (from index entry) to actual begin of file data
|
|
DWORD dwDefaultLocale; // Default locale, read from ".build.info"
|
|
|
|
QUERY_KEY CdnConfigKey;
|
|
QUERY_KEY CdnBuildKey;
|
|
|
|
PQUERY_KEY pArchiveArray; // Array of the archives
|
|
QUERY_KEY ArchiveGroup; // Name of the group archive file
|
|
DWORD ArchiveCount; // Number of archives in the array
|
|
|
|
PQUERY_KEY pPatchArchiveArray; // Array of the patch archives
|
|
QUERY_KEY PatchArchiveGroup; // Name of the patch group archive file
|
|
DWORD PatchArchiveCount; // Number of patch archives in the array
|
|
|
|
QUERY_KEY RootKey;
|
|
QUERY_KEY PatchKey;
|
|
QUERY_KEY DownloadKey;
|
|
QUERY_KEY InstallKey;
|
|
|
|
PQUERY_KEY pEncodingKeys;
|
|
QUERY_KEY EncodingKey;
|
|
QUERY_KEY EncodingEKey;
|
|
DWORD EncodingKeys;
|
|
|
|
TFileStream * DataFileArray[CASC_MAX_DATA_FILES]; // Data file handles
|
|
|
|
CASC_MAPPING_TABLE KeyMapping[CASC_INDEX_COUNT]; // Key mapping
|
|
PCASC_MAP pIndexEntryMap; // Map of index entries
|
|
|
|
PCASC_ENCODING_HEADER pEncodingHeader; // The encoding file
|
|
PCASC_ENCODING_ENTRY * ppEncodingEntries; // Map of encoding entries
|
|
size_t nEncodingEntries;
|
|
|
|
CASC_ROOT_HASH_TABLE RootTable; // Hash table for the root entries
|
|
|
|
PCASC_MNDX_INFO pMndxInfo; // Used for storages which have MNDX/MAR file
|
|
PCASC_PACKAGES pPackages; // Linear list of present packages
|
|
|
|
} TCascStorage;
|
|
|
|
typedef struct _TCascFile
|
|
{
|
|
TCascStorage * hs; // Pointer to storage structure
|
|
TFileStream * pStream; // An open data stream
|
|
const char * szClassName; // "TCascFile"
|
|
|
|
DWORD FilePointer; // Current file pointer
|
|
|
|
DWORD ArchiveIndex; // Index of the archive (data.###)
|
|
DWORD HeaderOffset; // Offset of the BLTE header, relative to the begin of the archive
|
|
DWORD HeaderSize; // Length of the BLTE header
|
|
DWORD FramesOffset; // Offset of the frame data, relative to the begin of the archive
|
|
DWORD CompressedSize; // Compressed size of the file (in bytes)
|
|
DWORD FileSize; // Size of file, in bytes
|
|
BYTE FrameArrayHash[MD5_HASH_SIZE]; // MD5 hash of the frame array
|
|
|
|
PCASC_FILE_FRAME pFrames; // Array of file frames
|
|
DWORD FrameCount; // Number of the file frames
|
|
|
|
LPBYTE pbFileCache; // Pointer to file cache
|
|
DWORD cbFileCache; // Size of the file cache
|
|
DWORD CacheStart; // Starting offset in the cache
|
|
DWORD CacheEnd; // Ending offset in the cache
|
|
|
|
#ifdef CASCLIB_TEST // Extra fields for analyzing the file size problem
|
|
DWORD FileSize_RootEntry; // File size, from the root entry
|
|
DWORD FileSize_EncEntry; // File size, from the encoding entry
|
|
DWORD FileSize_IdxEntry; // File size, from the index entry
|
|
DWORD FileSize_HdrArea; // File size, as stated in the file header area
|
|
DWORD FileSize_FrameSum; // File size as sum of frame sizes
|
|
#endif
|
|
|
|
} TCascFile;
|
|
|
|
typedef struct _TCascSearch
|
|
{
|
|
TCascStorage * hs; // Pointer to the storage handle
|
|
const char * szClassName;
|
|
TCHAR * szListFile;
|
|
void * pCache; // Listfile cache
|
|
TMndxFindResult * pStruct1C; // Search structure for MNDX info
|
|
char * szMask;
|
|
char szFileName[MAX_PATH]; // Buffer for the file name
|
|
char szNormName[MAX_PATH]; // Buffer for normalized file name
|
|
DWORD RootIndex; // Root index of the previously found item
|
|
DWORD dwState; // Pointer to the state (0 = listfile, 1 = nameless, 2 = done)
|
|
BYTE BitArray[1]; // Bit array of already-reported files
|
|
|
|
} TCascSearch;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Memory management
|
|
//
|
|
// We use our own macros for allocating/freeing memory. If you want
|
|
// to redefine them, please keep the following rules:
|
|
//
|
|
// - The memory allocation must return NULL if not enough memory
|
|
// (i.e not to throw exception)
|
|
// - The allocating function does not need to fill the allocated buffer with zeros
|
|
// - The reallocating function must support NULL as the previous block
|
|
// - Memory freeing function doesn't have to test the pointer to NULL
|
|
//
|
|
|
|
#if defined(_MSC_VER) && defined(_DEBUG)
|
|
|
|
#define CASC_REALLOC(type, ptr, count) (type *)HeapReAlloc(GetProcessHeap(), 0, ptr, ((count) * sizeof(type)))
|
|
#define CASC_ALLOC(type, count) (type *)HeapAlloc(GetProcessHeap(), 0, ((count) * sizeof(type)))
|
|
#define CASC_FREE(ptr) HeapFree(GetProcessHeap(), 0, ptr)
|
|
|
|
#else
|
|
|
|
#define CASC_REALLOC(type, ptr, count) (type *)realloc(ptr, (count) * sizeof(type))
|
|
#define CASC_ALLOC(type, count) (type *)malloc((count) * sizeof(type))
|
|
#define CASC_FREE(ptr) free(ptr)
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Big endian number manipulation
|
|
|
|
DWORD ConvertBytesToInteger_3(LPBYTE ValueAsBytes);
|
|
DWORD ConvertBytesToInteger_4(LPBYTE ValueAsBytes);
|
|
DWORD ConvertBytesToInteger_4_LE(LPBYTE ValueAsBytes);
|
|
ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Build configuration reading
|
|
|
|
int LoadBuildInfo(TCascStorage * hs);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Internal file functions
|
|
|
|
TCascStorage * IsValidStorageHandle(HANDLE hStorage);
|
|
TCascFile * IsValidFileHandle(HANDLE hFile);
|
|
|
|
PCASC_ROOT_ENTRY FindRootEntry(TCascStorage * hs, const char * szFileName, DWORD * PtrTableIndex);
|
|
PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, size_t * PtrIndex);
|
|
PCASC_INDEX_ENTRY FindIndexEntry(TCascStorage * hs, PQUERY_KEY pIndexKey);
|
|
|
|
int CascDecompress(void * pvOutBuffer, PDWORD pcbOutBuffer, void * pvInBuffer, DWORD cbInBuffer);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Dumping CASC data structures
|
|
|
|
#ifdef _DEBUG
|
|
void CascDumpSparseArray(const char * szFileName, void * pvSparseArray);
|
|
void CascDumpNameFragTable(const char * szFileName, void * pvMarFile);
|
|
void CascDumpFileNames(const char * szFileName, void * pvMarFile);
|
|
void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs);
|
|
void CascDumpMndxRoot(const char * szFileName, PCASC_MNDX_INFO pMndxInfo);
|
|
void CascDumpRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, const char * szFormat, const TCHAR * szListFile, int nDumpLevel);
|
|
void CascDumpFile(const char * szFileName, HANDLE hFile);
|
|
#endif // _DEBUG
|
|
|
|
#endif // __CASCCOMMON_H__
|