mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Dep/CascLib: Update to ladislav-zezula/CascLib@919a2d670c
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
set(HEADER_FILES
|
||||
src/CascCommon.h
|
||||
src/CascLib.h
|
||||
src/CascMndxRoot.h
|
||||
src/CascMndx.h
|
||||
src/CascPort.h
|
||||
src/common/Common.h
|
||||
src/common/FileStream.h
|
||||
@@ -13,19 +13,26 @@ set(HEADER_FILES
|
||||
set(SRC_FILES
|
||||
src/common/Common.cpp
|
||||
src/common/Directory.cpp
|
||||
src/common/DumpContext.cpp
|
||||
src/common/DynamicArray.cpp
|
||||
src/common/FileStream.cpp
|
||||
src/common/ListFile.cpp
|
||||
src/common/Map.cpp
|
||||
src/common/RootHandler.cpp
|
||||
src/jenkins/lookup3.c
|
||||
src/CascBuildCfg.cpp
|
||||
src/CascCommon.cpp
|
||||
src/CascDecompress.cpp
|
||||
src/CascDecrypt.cpp
|
||||
src/CascDumpData.cpp
|
||||
src/CascFiles.cpp
|
||||
src/CascFindFile.cpp
|
||||
src/CascMndxRoot.cpp
|
||||
src/CascOpenFile.cpp
|
||||
src/CascOpenStorage.cpp
|
||||
src/CascReadFile.cpp
|
||||
src/CascRootFile_Diablo3.cpp
|
||||
src/CascRootFile_Mndx.cpp
|
||||
src/CascRootFile_Ovr.cpp
|
||||
src/CascRootFile_WoW6.cpp
|
||||
)
|
||||
|
||||
set(TOMCRYPT_FILES
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -65,3 +65,26 @@ ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes)
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
void ConvertIntegerToBytes_4(DWORD Value, LPBYTE ValueAsBytes)
|
||||
{
|
||||
ValueAsBytes[0] = (Value >> 0x18) & 0xFF;
|
||||
ValueAsBytes[1] = (Value >> 0x10) & 0xFF;
|
||||
ValueAsBytes[2] = (Value >> 0x08) & 0xFF;
|
||||
ValueAsBytes[3] = (Value >> 0x00) & 0xFF;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Common fre routine of a CASC blob
|
||||
|
||||
void FreeCascBlob(PQUERY_KEY pBlob)
|
||||
{
|
||||
if(pBlob != NULL)
|
||||
{
|
||||
if(pBlob->pbData != NULL)
|
||||
CASC_FREE(pBlob->pbData);
|
||||
|
||||
pBlob->pbData = NULL;
|
||||
pBlob->cbData = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,13 @@
|
||||
|
||||
#include "CascPort.h"
|
||||
#include "common/Common.h"
|
||||
#include "common/DynamicArray.h"
|
||||
#include "common/Map.h"
|
||||
#include "common/FileStream.h"
|
||||
#include "common/Directory.h"
|
||||
#include "common/ListFile.h"
|
||||
#include "common/DumpContext.h"
|
||||
#include "common/RootHandler.h"
|
||||
|
||||
// Headers from LibTomCrypt
|
||||
#include "libtomcrypt/src/headers/tomcrypt.h"
|
||||
@@ -36,16 +40,17 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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_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_OVERWATCH 0x00040000 // Overwatch since PTR 24919
|
||||
#define CASC_GAME_STARCRAFT2 0x00050000 // Starcraft II - Legacy of the Void, since build 38996
|
||||
#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_EXTRA_FILES 0x20 // Number of extra entries to be reserved for additionally inserted files
|
||||
|
||||
#define CASC_SEARCH_HAVE_NAME 0x0001 // Indicated that previous search found a name
|
||||
|
||||
@@ -71,41 +76,28 @@
|
||||
|
||||
#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;
|
||||
#ifndef _maxchars
|
||||
#define _maxchars(buff) ((sizeof(buff) / sizeof(buff[0])) - 1)
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// In-memory structures
|
||||
// See http://pxr.dk/wowdev/wiki/index.php?title=CASC for more information
|
||||
|
||||
class TMndxFindResult;
|
||||
struct TFileStream;
|
||||
struct _MAR_FILE;
|
||||
|
||||
typedef enum _CBLD_TYPE
|
||||
{
|
||||
CascBuildNone = 0, // No build type found
|
||||
CascBuildInfo, // .build.info
|
||||
CascBuildDb, // .build.db (older storages)
|
||||
} CBLD_TYPE, *PCBLD_TYPE;
|
||||
|
||||
typedef struct _ENCODING_KEY
|
||||
{
|
||||
BYTE Value[MD5_HASH_SIZE]; // MD5 of the file
|
||||
|
||||
} ENCODING_KEY, *PENCODING_KEY;
|
||||
|
||||
typedef struct _CASC_INDEX_ENTRY
|
||||
{
|
||||
@@ -140,25 +132,34 @@ typedef struct _CASC_FILE_FRAME
|
||||
BYTE md5[MD5_HASH_SIZE]; // MD5 hash of the file sector
|
||||
} CASC_FILE_FRAME, *PCASC_FILE_FRAME;
|
||||
|
||||
// The encoding file is in the form of:
|
||||
// * File header
|
||||
// * String block #1
|
||||
// * Table A header
|
||||
// * Table A entries
|
||||
// * Table B header
|
||||
// * Table B entries
|
||||
// * String block #2
|
||||
// http://pxr.dk/wowdev/wiki/index.php?title=CASC#Key_CASC_Files
|
||||
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 Version; // Expected to be 1 by CascLib
|
||||
BYTE ChecksumSizeA; // The length of the checksums in Encoding Table
|
||||
BYTE ChecksumSizeB; // The length of the checksums in Encoding Layout Table
|
||||
BYTE Flags_TableA[2]; // Flags for Encoding Table
|
||||
BYTE Flags_TableB[2]; // Flags for Encoding Layout Table
|
||||
BYTE Entries_TableA[4]; // Number of segments in Encoding Table (big endian)
|
||||
BYTE Entries_TableB[4]; // Number of segments in Encoding Layout Table (big endian)
|
||||
BYTE field_11;
|
||||
BYTE SegmentsPos[4]; // Offset of encoding segments
|
||||
BYTE Size_StringTable1[4]; // Size of the string block #1
|
||||
|
||||
} 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
|
||||
USHORT KeyCount; // Number of index keys
|
||||
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
|
||||
@@ -166,87 +167,21 @@ typedef struct _CASC_ENCODING_ENTRY
|
||||
// Followed by the index keys (number of items = KeyCount)
|
||||
} CASC_ENCODING_ENTRY, *PCASC_ENCODING_ENTRY;
|
||||
|
||||
typedef struct _CASC_ROOT_LOCALE_BLOCK
|
||||
// A version of CASC_ENCODING_ENTRY with one index key
|
||||
typedef struct _CASC_ENCODING_ENTRY_1
|
||||
{
|
||||
DWORD NumberOfFiles; // Number of entries
|
||||
DWORD Flags;
|
||||
DWORD FileLocales; // File locales (CASC_LOCALE_XXX)
|
||||
USHORT KeyCount; // Number of index keys
|
||||
BYTE FileSizeBE[4]; // Compressed file size (header area + frame headers + compressed frames), in bytes
|
||||
BYTE EncodingKey[MD5_HASH_SIZE]; // File encoding key
|
||||
BYTE IndexKey[MD5_HASH_SIZE]; // File index key
|
||||
|
||||
// Followed by a block of 32-bit integers (count: NumberOfFiles)
|
||||
// Followed by the MD5 and file name hash (count: NumberOfFiles)
|
||||
} CASC_ENCODING_ENTRY_1, *PCASC_ENCODING_ENTRY_1;
|
||||
|
||||
} CASC_ROOT_LOCALE_BLOCK, *PCASC_ROOT_LOCALE_BLOCK;
|
||||
#define GET_INDEX_KEY(pEncodingEntry) (pEncodingEntry->EncodingKey + MD5_HASH_SIZE)
|
||||
#define FAKE_ENCODING_ENTRY_SIZE (sizeof(CASC_ENCODING_ENTRY) + MD5_HASH_SIZE)
|
||||
|
||||
// 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;
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structures for CASC storage and CASC file
|
||||
|
||||
typedef struct _TCascStorage
|
||||
{
|
||||
@@ -254,6 +189,7 @@ typedef struct _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 * szBuildFile; // Build file name (.build.info or .build.db)
|
||||
TCHAR * szIndexPath; // This is the directory where index files are
|
||||
TCHAR * szUrlPath; // URL to the Blizzard servers
|
||||
DWORD dwRefCount; // Number of references
|
||||
@@ -262,40 +198,29 @@ typedef struct _TCascStorage
|
||||
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"
|
||||
|
||||
CBLD_TYPE BuildFileType; // Type of the build file
|
||||
|
||||
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 ArchivesGroup; // Key array of the "archive-group"
|
||||
QUERY_KEY ArchivesKey; // Key array of the "archives"
|
||||
QUERY_KEY PatchArchivesKey; // Key array of the "patch-archives"
|
||||
QUERY_KEY 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;
|
||||
QUERY_KEY EncodingFile; // Content of the ENCODING file
|
||||
PCASC_MAP pEncodingMap; // Map of encoding entries
|
||||
DYNAMIC_ARRAY ExtraEntries; // Extra encoding entries
|
||||
|
||||
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
|
||||
TRootHandler * pRootHandler; // Common handler for various ROOT file formats
|
||||
|
||||
} TCascStorage;
|
||||
|
||||
@@ -336,16 +261,19 @@ typedef struct _TCascFile
|
||||
typedef struct _TCascSearch
|
||||
{
|
||||
TCascStorage * hs; // Pointer to the storage handle
|
||||
const char * szClassName;
|
||||
TCHAR * szListFile;
|
||||
const char * szClassName; // Contains "TCascSearch"
|
||||
TCHAR * szListFile; // Name of the listfile
|
||||
void * pCache; // Listfile cache
|
||||
TMndxFindResult * pStruct1C; // Search structure for MNDX info
|
||||
char * szMask;
|
||||
char * szMask; // Search mask
|
||||
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
|
||||
|
||||
// Provider-specific data
|
||||
void * pRootContext; // Root-specific search context
|
||||
size_t IndexLevel1; // Root-specific search context
|
||||
size_t IndexLevel2; // Root-specific search context
|
||||
DWORD dwState; // Pointer to the search state (0 = listfile, 1 = nameless, 2 = done)
|
||||
|
||||
BYTE BitArray[1]; // Bit array of encoding keys. Set for each entry that has already been reported
|
||||
|
||||
} TCascSearch;
|
||||
|
||||
@@ -384,10 +312,15 @@ DWORD ConvertBytesToInteger_4(LPBYTE ValueAsBytes);
|
||||
DWORD ConvertBytesToInteger_4_LE(LPBYTE ValueAsBytes);
|
||||
ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes);
|
||||
|
||||
void ConvertIntegerToBytes_4(DWORD Value, LPBYTE ValueAsBytes);
|
||||
void FreeCascBlob(PQUERY_KEY pQueryKey);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Build configuration reading
|
||||
// Text file parsing (CascFiles.cpp)
|
||||
|
||||
int LoadBuildInfo(TCascStorage * hs);
|
||||
int CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory);
|
||||
int ParseRootFileLine(const char * szLinePtr, const char * szLineEnd, PQUERY_KEY pEncodingKey, char * szFileName, size_t nMaxChars);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Internal file functions
|
||||
@@ -395,11 +328,20 @@ int LoadBuildInfo(TCascStorage * hs);
|
||||
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_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, PDWORD PtrIndex);
|
||||
PCASC_INDEX_ENTRY FindIndexEntry(TCascStorage * hs, PQUERY_KEY pIndexKey);
|
||||
|
||||
int CascDecompress(void * pvOutBuffer, PDWORD pcbOutBuffer, void * pvInBuffer, DWORD cbInBuffer);
|
||||
int CascDecompress(LPBYTE pvOutBuffer, PDWORD pcbOutBuffer, LPBYTE pvInBuffer, DWORD cbInBuffer);
|
||||
int CascDecrypt (LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex);
|
||||
int CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Support for ROOT file
|
||||
|
||||
int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
int RootHandler_CreateWoW6(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Dumping CASC data structures
|
||||
@@ -409,8 +351,7 @@ 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 CascDumpEncodingEntry(TCascStorage * hs, TDumpContext * dc, PCASC_ENCODING_ENTRY pEncodingEntry, int nDumpLevel);
|
||||
void CascDumpFile(const char * szFileName, HANDLE hFile);
|
||||
#endif // _DEBUG
|
||||
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
#include "CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
// Public functions
|
||||
|
||||
static int Decompress_ZLIB(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer)
|
||||
int CascDecompress(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer)
|
||||
{
|
||||
z_stream z; // Stream information for zlib
|
||||
int nResult;
|
||||
@@ -44,40 +44,3 @@ static int Decompress_ZLIB(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInB
|
||||
// Return an error code
|
||||
return (nResult == Z_OK || nResult == Z_STREAM_END) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int CascDecompress(void * pvOutBuffer, PDWORD pcbOutBuffer, void * pvInBuffer, DWORD cbInBuffer)
|
||||
{
|
||||
LPBYTE pbOutBuffer = (LPBYTE)pvOutBuffer;
|
||||
LPBYTE pbInBuffer = (LPBYTE)pvInBuffer;
|
||||
DWORD cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer
|
||||
BYTE uCompression; // Decompressions applied to the data
|
||||
|
||||
// Verify buffer sizes
|
||||
if(cbInBuffer <= 1)
|
||||
return 0;
|
||||
|
||||
// Get applied compression types and decrement data length
|
||||
uCompression = *pbInBuffer++;
|
||||
cbInBuffer--;
|
||||
|
||||
// Perform the decompressions
|
||||
switch(uCompression)
|
||||
{
|
||||
case 'N': // Uncompressed
|
||||
|
||||
assert(cbOutBuffer == cbInBuffer);
|
||||
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
|
||||
*pcbOutBuffer = cbOutBuffer;
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case 'Z': // ZLIB
|
||||
|
||||
return Decompress_ZLIB(pbOutBuffer, pcbOutBuffer, pbInBuffer, cbInBuffer);
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
300
dep/CascLib/src/CascDecrypt.cpp
Normal file
300
dep/CascLib/src/CascDecrypt.cpp
Normal file
@@ -0,0 +1,300 @@
|
||||
/*****************************************************************************/
|
||||
/* CascDecrypt.cpp Copyright (c) Ladislav Zezula 2015 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Decryption functions for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 31.10.15 1.00 Lad The first version of CascDecrypt.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
|
||||
typedef struct _CASC_ENCRYPTION_KEY
|
||||
{
|
||||
ULONGLONG KeyName; // "Name" of the key
|
||||
BYTE Key[0x10]; // The key itself
|
||||
} CASC_ENCRYPTION_KEY, *PCASC_ENCRYPTION_KEY;
|
||||
|
||||
typedef struct _CASC_SALSA20
|
||||
{
|
||||
DWORD Key[0x10];
|
||||
DWORD dwRounds;
|
||||
|
||||
} CASC_SALSA20, *PCASC_SALSA20;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local variables
|
||||
|
||||
//keyName FB680CB6A8BF81F3 key 62D90EFA7F36D71C398AE2F1FE37BDB9 keyNameSize 8 keySize 16
|
||||
//keyName 402CD9D8D6BFED98 key AEB0EADEA47612FE6C041A03958DF241 keyNameSize 8 keySize 16
|
||||
//keyName 87AEBBC9C4E6B601 key 685E86C6063DFDA6C9E85298076B3D42 keyNameSize 8 keySize 16
|
||||
//keyName A19C4F859F6EFA54 key 0196CB6F5ECBAD7CB5283891B9712B4B keyNameSize 8 keySize 16
|
||||
//keyName 11A9203C9881710A key 2E2CB8C397C2F24ED0B5E452F18DC267 keyNameSize 8 keySize 16
|
||||
//keyName DBD3371554F60306 key 34E397ACE6DD30EEFDC98A2AB093CD3C keyNameSize 8 keySize 16
|
||||
//keyName DEE3A0521EFF6F03 key AD740CE3FFFF9231468126985708E1B9 keyNameSize 8 keySize 16
|
||||
//keyName 8C9106108AA84F07 key 53D859DDA2635A38DC32E72B11B32F29 keyNameSize 8 keySize 16
|
||||
|
||||
static CASC_ENCRYPTION_KEY CascKeys[] =
|
||||
{
|
||||
{0xFB680CB6A8BF81F3ULL, {0x62, 0xD9, 0x0E, 0xFA, 0x7F, 0x36, 0xD7, 0x1C, 0x39, 0x8A, 0xE2, 0xF1, 0xFE, 0x37, 0xBD, 0xB9}},
|
||||
{0x402CD9D8D6BFED98ULL, {0xAE, 0xB0, 0xEA, 0xDE, 0xA4, 0x76, 0x12, 0xFE, 0x6C, 0x04, 0x1A, 0x03, 0x95, 0x8D, 0xF2, 0x41}},
|
||||
{0x87AEBBC9C4E6B601ULL, {0x68, 0x5E, 0x86, 0xC6, 0x06, 0x3D, 0xFD, 0xA6, 0xC9, 0xE8, 0x52, 0x98, 0x07, 0x6B, 0x3D, 0x42}},
|
||||
{0xA19C4F859F6EFA54ULL, {0x01, 0x96, 0xCB, 0x6F, 0x5E, 0xCB, 0xAD, 0x7C, 0xB5, 0x28, 0x38, 0x91, 0xB9, 0x71, 0x2B, 0x4B}},
|
||||
{0x11A9203C9881710AULL, {0x2E, 0x2C, 0xB8, 0xC3, 0x97, 0xC2, 0xF2, 0x4E, 0xD0, 0xB5, 0xE4, 0x52, 0xF1, 0x8D, 0xC2, 0x67}},
|
||||
{0xDBD3371554F60306ULL, {0x34, 0xE3, 0x97, 0xAC, 0xE6, 0xDD, 0x30, 0xEE, 0xFD, 0xC9, 0x8A, 0x2A, 0xB0, 0x93, 0xCD, 0x3C}},
|
||||
{0xDEE3A0521EFF6F03ULL, {0xAD, 0x74, 0x0C, 0xE3, 0xFF, 0xFF, 0x92, 0x31, 0x46, 0x81, 0x26, 0x98, 0x57, 0x08, 0xE1, 0xB9}},
|
||||
{0x8C9106108AA84F07ULL, {0x53, 0xD8, 0x59, 0xDD, 0xA2, 0x63, 0x5A, 0x38, 0xDC, 0x32, 0xE7, 0x2B, 0x11, 0xB3, 0x2F, 0x29}},
|
||||
{0x49166D358A34D815ULL, {0x66, 0x78, 0x68, 0xCD, 0x94, 0xEA, 0x01, 0x35, 0xB9, 0xB1, 0x6C, 0x93, 0xB1, 0x12, 0x4A, 0xBA}},
|
||||
{0, {0}}
|
||||
};
|
||||
|
||||
static const char * szKeyConstant16 = "expand 16-byte k";
|
||||
static const char * szKeyConstant32 = "expand 32-byte k";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static DWORD Rol32(DWORD dwValue, DWORD dwRolCount)
|
||||
{
|
||||
return (dwValue << dwRolCount) | (dwValue >> (32 - dwRolCount));
|
||||
}
|
||||
|
||||
static LPBYTE FindCascKey(ULONGLONG KeyName)
|
||||
{
|
||||
// Search the known keys
|
||||
for(size_t i = 0; CascKeys[i].KeyName != 0; i++)
|
||||
{
|
||||
if(CascKeys[i].KeyName == KeyName)
|
||||
return CascKeys[i].Key;
|
||||
}
|
||||
|
||||
// Key not found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void Initialize(PCASC_SALSA20 pState, LPBYTE pbKey, DWORD cbKeyLength, LPBYTE pbVector)
|
||||
{
|
||||
const char * szConstants = (cbKeyLength == 32) ? szKeyConstant32 : szKeyConstant16;
|
||||
DWORD KeyIndex = cbKeyLength - 0x10;
|
||||
|
||||
memset(pState, 0, sizeof(CASC_SALSA20));
|
||||
pState->Key[0] = *(PDWORD)(szConstants + 0x00);
|
||||
pState->Key[1] = *(PDWORD)(pbKey + 0x00);
|
||||
pState->Key[2] = *(PDWORD)(pbKey + 0x04);
|
||||
pState->Key[3] = *(PDWORD)(pbKey + 0x08);
|
||||
pState->Key[4] = *(PDWORD)(pbKey + 0x0C);
|
||||
pState->Key[5] = *(PDWORD)(szConstants + 0x04);
|
||||
pState->Key[6] = *(PDWORD)(pbVector + 0x00);
|
||||
pState->Key[7] = *(PDWORD)(pbVector + 0x04);
|
||||
pState->Key[8] = 0;
|
||||
pState->Key[9] = 0;
|
||||
pState->Key[10] = *(PDWORD)(szConstants + 0x08);
|
||||
pState->Key[11] = *(PDWORD)(pbKey + KeyIndex + 0x00);
|
||||
pState->Key[12] = *(PDWORD)(pbKey + KeyIndex + 0x04);
|
||||
pState->Key[13] = *(PDWORD)(pbKey + KeyIndex + 0x08);
|
||||
pState->Key[14] = *(PDWORD)(pbKey + KeyIndex + 0x0C);
|
||||
pState->Key[15] = *(PDWORD)(szConstants + 0x0C);
|
||||
|
||||
pState->dwRounds = 20;
|
||||
}
|
||||
|
||||
static int Decrypt(PCASC_SALSA20 pState, LPBYTE pbOutBuffer, LPBYTE pbInBuffer, size_t cbInBuffer)
|
||||
{
|
||||
LPBYTE pbXorValue;
|
||||
DWORD KeyMirror[0x10];
|
||||
DWORD XorValue[0x10];
|
||||
DWORD BlockSize;
|
||||
DWORD i;
|
||||
|
||||
// Repeat until we have data to read
|
||||
while(cbInBuffer > 0)
|
||||
{
|
||||
// Create the copy of the key
|
||||
memcpy(KeyMirror, pState->Key, sizeof(KeyMirror));
|
||||
|
||||
// Shuffle the key
|
||||
for(i = 0; i < pState->dwRounds; i += 2)
|
||||
{
|
||||
KeyMirror[0x04] ^= Rol32((KeyMirror[0x00] + KeyMirror[0x0C]), 0x07);
|
||||
KeyMirror[0x08] ^= Rol32((KeyMirror[0x04] + KeyMirror[0x00]), 0x09);
|
||||
KeyMirror[0x0C] ^= Rol32((KeyMirror[0x08] + KeyMirror[0x04]), 0x0D);
|
||||
KeyMirror[0x00] ^= Rol32((KeyMirror[0x0C] + KeyMirror[0x08]), 0x12);
|
||||
|
||||
KeyMirror[0x09] ^= Rol32((KeyMirror[0x05] + KeyMirror[0x01]), 0x07);
|
||||
KeyMirror[0x0D] ^= Rol32((KeyMirror[0x09] + KeyMirror[0x05]), 0x09);
|
||||
KeyMirror[0x01] ^= Rol32((KeyMirror[0x0D] + KeyMirror[0x09]), 0x0D);
|
||||
KeyMirror[0x05] ^= Rol32((KeyMirror[0x01] + KeyMirror[0x0D]), 0x12);
|
||||
|
||||
KeyMirror[0x0E] ^= Rol32((KeyMirror[0x0A] + KeyMirror[0x06]), 0x07);
|
||||
KeyMirror[0x02] ^= Rol32((KeyMirror[0x0E] + KeyMirror[0x0A]), 0x09);
|
||||
KeyMirror[0x06] ^= Rol32((KeyMirror[0x02] + KeyMirror[0x0E]), 0x0D);
|
||||
KeyMirror[0x0A] ^= Rol32((KeyMirror[0x06] + KeyMirror[0x02]), 0x12);
|
||||
|
||||
KeyMirror[0x03] ^= Rol32((KeyMirror[0x0F] + KeyMirror[0x0B]), 0x07);
|
||||
KeyMirror[0x07] ^= Rol32((KeyMirror[0x03] + KeyMirror[0x0F]), 0x09);
|
||||
KeyMirror[0x0B] ^= Rol32((KeyMirror[0x07] + KeyMirror[0x03]), 0x0D);
|
||||
KeyMirror[0x0F] ^= Rol32((KeyMirror[0x0B] + KeyMirror[0x07]), 0x12);
|
||||
|
||||
KeyMirror[0x01] ^= Rol32((KeyMirror[0x00] + KeyMirror[0x03]), 0x07);
|
||||
KeyMirror[0x02] ^= Rol32((KeyMirror[0x01] + KeyMirror[0x00]), 0x09);
|
||||
KeyMirror[0x03] ^= Rol32((KeyMirror[0x02] + KeyMirror[0x01]), 0x0D);
|
||||
KeyMirror[0x00] ^= Rol32((KeyMirror[0x03] + KeyMirror[0x02]), 0x12);
|
||||
|
||||
KeyMirror[0x06] ^= Rol32((KeyMirror[0x05] + KeyMirror[0x04]), 0x07);
|
||||
KeyMirror[0x07] ^= Rol32((KeyMirror[0x06] + KeyMirror[0x05]), 0x09);
|
||||
KeyMirror[0x04] ^= Rol32((KeyMirror[0x07] + KeyMirror[0x06]), 0x0D);
|
||||
KeyMirror[0x05] ^= Rol32((KeyMirror[0x04] + KeyMirror[0x07]), 0x12);
|
||||
|
||||
KeyMirror[0x0B] ^= Rol32((KeyMirror[0x0A] + KeyMirror[0x09]), 0x07);
|
||||
KeyMirror[0x08] ^= Rol32((KeyMirror[0x0B] + KeyMirror[0x0A]), 0x09);
|
||||
KeyMirror[0x09] ^= Rol32((KeyMirror[0x08] + KeyMirror[0x0B]), 0x0D);
|
||||
KeyMirror[0x0A] ^= Rol32((KeyMirror[0x09] + KeyMirror[0x08]), 0x12);
|
||||
|
||||
KeyMirror[0x0C] ^= Rol32((KeyMirror[0x0F] + KeyMirror[0x0E]), 0x07);
|
||||
KeyMirror[0x0D] ^= Rol32((KeyMirror[0x0C] + KeyMirror[0x0F]), 0x09);
|
||||
KeyMirror[0x0E] ^= Rol32((KeyMirror[0x0D] + KeyMirror[0x0C]), 0x0D);
|
||||
KeyMirror[0x0F] ^= Rol32((KeyMirror[0x0E] + KeyMirror[0x0D]), 0x12);
|
||||
}
|
||||
|
||||
// Set the number of remaining bytes
|
||||
pbXorValue = (LPBYTE)XorValue;
|
||||
BlockSize = (DWORD)CASCLIB_MIN(cbInBuffer, 0x40);
|
||||
|
||||
// Prepare the XOR constants
|
||||
for(i = 0; i < 16; i++)
|
||||
{
|
||||
XorValue[i] = KeyMirror[i] + pState->Key[i];
|
||||
}
|
||||
|
||||
// Decrypt the block
|
||||
for(i = 0; i < BlockSize; i++)
|
||||
{
|
||||
pbOutBuffer[i] = pbInBuffer[i] ^ pbXorValue[i];
|
||||
}
|
||||
|
||||
pState->Key[8] = pState->Key[8] + 1;
|
||||
if(pState->Key[8] == 0)
|
||||
pState->Key[9] = pState->Key[9] + 1;
|
||||
|
||||
// Adjust buffers
|
||||
pbOutBuffer += BlockSize;
|
||||
pbInBuffer += BlockSize;
|
||||
cbInBuffer -= BlockSize;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int Decrypt_Salsa20(LPBYTE pbOutBuffer, LPBYTE pbInBuffer, size_t cbInBuffer, LPBYTE pbKey, DWORD cbKeySize, LPBYTE pbVector)
|
||||
{
|
||||
CASC_SALSA20 SalsaState;
|
||||
|
||||
Initialize(&SalsaState, pbKey, cbKeySize, pbVector);
|
||||
return Decrypt(&SalsaState, pbOutBuffer, pbInBuffer, cbInBuffer);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int CascDecrypt(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex)
|
||||
{
|
||||
ULONGLONG KeyName = 0;
|
||||
LPBYTE pbBufferEnd = pbInBuffer + cbInBuffer;
|
||||
LPBYTE pbKey;
|
||||
DWORD KeyNameSize;
|
||||
DWORD dwShift = 0;
|
||||
DWORD IVSize;
|
||||
BYTE Vector[0x08];
|
||||
BYTE EncryptionType;
|
||||
int nError;
|
||||
|
||||
// Verify and retrieve the key name size
|
||||
if(pbInBuffer >= pbBufferEnd)
|
||||
return ERROR_FILE_CORRUPT;
|
||||
if(pbInBuffer[0] != 0 && pbInBuffer[0] != 8)
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
KeyNameSize = *pbInBuffer++;
|
||||
|
||||
// Copy the key name
|
||||
if((pbInBuffer + KeyNameSize) >= pbBufferEnd)
|
||||
return ERROR_FILE_CORRUPT;
|
||||
memcpy(&KeyName, pbInBuffer, KeyNameSize);
|
||||
pbInBuffer += KeyNameSize;
|
||||
|
||||
// Verify and retrieve the Vector size
|
||||
if(pbInBuffer >= pbBufferEnd)
|
||||
return ERROR_FILE_CORRUPT;
|
||||
if(pbInBuffer[0] != 4 && pbInBuffer[0] != 8)
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
IVSize = *pbInBuffer++;
|
||||
|
||||
// Copy the initialization vector
|
||||
if((pbInBuffer + IVSize) >= pbBufferEnd)
|
||||
return ERROR_FILE_CORRUPT;
|
||||
memset(Vector, 0, sizeof(Vector));
|
||||
memcpy(Vector, pbInBuffer, IVSize);
|
||||
pbInBuffer += IVSize;
|
||||
|
||||
// Verify and retrieve the encryption type
|
||||
if(pbInBuffer >= pbBufferEnd)
|
||||
return ERROR_FILE_CORRUPT;
|
||||
if(pbInBuffer[0] != 'S' && pbInBuffer[0] != 'A')
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
EncryptionType = *pbInBuffer++;
|
||||
|
||||
// Do we have enough space in the output buffer?
|
||||
if((DWORD)(pbBufferEnd - pbInBuffer) > pcbOutBuffer[0])
|
||||
return ERROR_INSUFFICIENT_BUFFER;
|
||||
|
||||
// Check if we know the key
|
||||
pbKey = FindCascKey(KeyName);
|
||||
if(pbKey == NULL)
|
||||
return ERROR_UNKNOWN_FILE_KEY;
|
||||
|
||||
// Shuffle the Vector with the block index
|
||||
// Note that there's no point to go beyond 32 bits, unless the file has
|
||||
// more than 0xFFFFFFFF frames.
|
||||
for(int i = 0; i < sizeof(dwFrameIndex); i++)
|
||||
{
|
||||
Vector[i] = Vector[i] ^ (BYTE)((dwFrameIndex >> dwShift) & 0xFF);
|
||||
dwShift += 8;
|
||||
}
|
||||
|
||||
// Perform the decryption-specific action
|
||||
switch(EncryptionType)
|
||||
{
|
||||
case 'S': // Salsa20
|
||||
nError = Decrypt_Salsa20(pbOutBuffer, pbInBuffer, (pbBufferEnd - pbInBuffer), pbKey, 0x10, Vector);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
|
||||
// Supply the size of the output buffer
|
||||
pcbOutBuffer[0] = (DWORD)(pbBufferEnd - pbInBuffer);
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
// case 'A':
|
||||
// return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
int CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer)
|
||||
{
|
||||
// Check the buffer size
|
||||
if((cbInBuffer - 1) > pcbOutBuffer[0])
|
||||
return ERROR_INSUFFICIENT_BUFFER;
|
||||
|
||||
// Copy the data
|
||||
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
|
||||
pcbOutBuffer[0] = cbInBuffer;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
#include "CascMndxRoot.h"
|
||||
#include "CascMndx.h"
|
||||
|
||||
#ifdef _DEBUG // The entire file is only valid for debug purposes
|
||||
#ifdef _DEBUG // The entire feature is only valid for debug purposes
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forward definitions
|
||||
|
||||
LPBYTE VerifyLocaleBlock(PROOT_BLOCK_INFO pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd);
|
||||
//LPBYTE VerifyLocaleBlock(PROOT_BLOCK_INFO pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Sort compare functions
|
||||
@@ -49,160 +49,6 @@ static int CompareIndexEntries_FilePos(const void *, const void * pvIndexEntry1,
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static char * StringFromMD5(LPBYTE md5, char * szBuffer)
|
||||
{
|
||||
return StringFromBinary(md5, MD5_HASH_SIZE, szBuffer);
|
||||
}
|
||||
|
||||
static char * FormatFileName(const char * szFormat, TCascStorage * hs)
|
||||
{
|
||||
char * szFileName;
|
||||
char * szSrc;
|
||||
char * szTrg;
|
||||
|
||||
// Create copy of the file name
|
||||
szFileName = szSrc = szTrg = NewStr(szFormat, 0);
|
||||
if(szFileName != NULL)
|
||||
{
|
||||
// Format the file name
|
||||
while(szSrc[0] != 0)
|
||||
{
|
||||
if(szSrc[0] == '%')
|
||||
{
|
||||
// Replace "%build%" with a build number
|
||||
if(!strncmp(szSrc, "%build%", 7))
|
||||
{
|
||||
szTrg += sprintf(szTrg, "%u", hs->dwBuildNumber);
|
||||
szSrc += 7;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Just copy the character
|
||||
*szTrg++ = *szSrc++;
|
||||
}
|
||||
|
||||
// Terminate the target file name
|
||||
szTrg[0] = 0;
|
||||
}
|
||||
|
||||
return szFileName;
|
||||
}
|
||||
|
||||
FILE * CreateDumpFile(const char * szFormat, TCascStorage * hs)
|
||||
{
|
||||
FILE * fp = NULL;
|
||||
char * szFileName;
|
||||
|
||||
// Validate the storage handle
|
||||
if(hs != NULL)
|
||||
{
|
||||
// Format the real file name
|
||||
szFileName = FormatFileName(szFormat, hs);
|
||||
if(szFileName != NULL)
|
||||
{
|
||||
// Create the dump file
|
||||
fp = fopen(szFileName, "wt");
|
||||
CASC_FREE(szFileName);
|
||||
}
|
||||
}
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
static void DumpIndexKey(
|
||||
FILE * fp,
|
||||
TCascStorage * hs,
|
||||
LPBYTE pbIndexKey,
|
||||
int nDumpLevel)
|
||||
{
|
||||
PCASC_INDEX_ENTRY pIndexEntry;
|
||||
TCascFile * hf;
|
||||
QUERY_KEY QueryKey;
|
||||
HANDLE hFile;
|
||||
BYTE HeaderArea[MAX_HEADER_AREA_SIZE];
|
||||
char szBuffer[0x20];
|
||||
|
||||
QueryKey.pbData = pbIndexKey;
|
||||
QueryKey.cbData = MD5_HASH_SIZE;
|
||||
pIndexEntry = FindIndexEntry(hs, &QueryKey);
|
||||
if(pIndexEntry != NULL)
|
||||
{
|
||||
ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffsetBE);
|
||||
DWORD ArchIndex = (DWORD)(FileOffset >> 0x1E);
|
||||
DWORD FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE);
|
||||
|
||||
// Mask the file offset
|
||||
FileOffset &= 0x3FFFFFFF;
|
||||
fprintf(fp, " data.%03u at 0x%08x (0x%lx bytes)\n",
|
||||
ArchIndex,
|
||||
(DWORD)FileOffset,
|
||||
FileSize);
|
||||
|
||||
if(nDumpLevel > 2)
|
||||
{
|
||||
QueryKey.pbData = pIndexEntry->IndexKey;
|
||||
QueryKey.cbData = MD5_HASH_SIZE;
|
||||
if(CascOpenFileByIndexKey((HANDLE)hs, &QueryKey, 0, &hFile))
|
||||
{
|
||||
// Make sure that the data file is open and frame header loaded
|
||||
CascGetFileSize(hFile, NULL);
|
||||
hf = IsValidFileHandle(hFile);
|
||||
assert(hf->pStream != NULL);
|
||||
|
||||
// Read the header area
|
||||
FileOffset = hf->HeaderOffset - BLTE_HEADER_DELTA;
|
||||
FileStream_Read(hf->pStream, &FileOffset, HeaderArea, sizeof(HeaderArea));
|
||||
CascCloseFile(hFile);
|
||||
|
||||
// Dump the header area
|
||||
fprintf(fp, " FileSize: %X Rest: %s\n",
|
||||
ConvertBytesToInteger_4_LE(&HeaderArea[0x10]),
|
||||
StringFromBinary(&HeaderArea[0x14], 10, szBuffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(fp, " NO INDEX ENTRY\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void DumpEncodingEntry(
|
||||
FILE * fp,
|
||||
TCascStorage * hs,
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry,
|
||||
int nDumpLevel)
|
||||
{
|
||||
LPBYTE pbIndexKey;
|
||||
char szMd5[MD5_STRING_SIZE];
|
||||
|
||||
// If the encoding key exists
|
||||
if(pEncodingEntry != NULL)
|
||||
{
|
||||
fprintf(fp, " Size %lx Key Count: %u\n",
|
||||
ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE),
|
||||
pEncodingEntry->KeyCount);
|
||||
|
||||
// Dump all index keys
|
||||
pbIndexKey = pEncodingEntry->EncodingKey + MD5_HASH_SIZE;
|
||||
for(DWORD j = 0; j < pEncodingEntry->KeyCount; j++)
|
||||
{
|
||||
fprintf(fp, " %s\n", StringFromMD5(pbIndexKey, szMd5));
|
||||
DumpIndexKey(fp, hs, pbIndexKey, nDumpLevel);
|
||||
pbIndexKey += MD5_HASH_SIZE;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(fp, " NO ENCODING KEYS\n");
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
@@ -323,27 +169,92 @@ void CascDumpFileNames(const char * szFileName, void * pvMarFile)
|
||||
Struct1C.FreeStruct40();
|
||||
}
|
||||
|
||||
void CascDumpMndxRoot(const char * szFileName, PCASC_MNDX_INFO pMndxInfo)
|
||||
void CascDumpIndexEntry(
|
||||
TCascStorage * /* hs */,
|
||||
TDumpContext * dc,
|
||||
PCASC_INDEX_ENTRY pIndexEntry,
|
||||
int /* nDumpLevel */)
|
||||
{
|
||||
PCASC_ROOT_ENTRY_MNDX pRootEntry;
|
||||
FILE * fp;
|
||||
char szMd5[MD5_STRING_SIZE];
|
||||
|
||||
// Create the dump file
|
||||
fp = fopen(szFileName, "wt");
|
||||
if(fp != NULL)
|
||||
if(pIndexEntry != NULL)
|
||||
{
|
||||
fprintf(fp, "Indx Fl+Asset EncodingKey FileSize\n==== ======== ================================ ========\n");
|
||||
for(DWORD i = 0; i < pMndxInfo->MndxEntriesValid; i++)
|
||||
{
|
||||
pRootEntry = pMndxInfo->ppValidEntries[i];
|
||||
ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffsetBE);
|
||||
DWORD ArchIndex = (DWORD)(FileOffset >> 0x1E);
|
||||
DWORD FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE);
|
||||
|
||||
fprintf(fp, "%04X %08X %s %08X\n", i,
|
||||
pRootEntry->Flags,
|
||||
StringFromMD5(pRootEntry->EncodingKey, szMd5),
|
||||
pRootEntry->FileSize);
|
||||
// Mask the file offset
|
||||
FileOffset &= 0x3FFFFFFF;
|
||||
dump_print(dc, " data.%03u at 0x%08x (0x%lx bytes)\n",
|
||||
ArchIndex,
|
||||
(DWORD)FileOffset,
|
||||
FileSize);
|
||||
|
||||
//if(nDumpLevel > 2)
|
||||
//{
|
||||
// QueryKey.pbData = pIndexEntry->IndexKey;
|
||||
// QueryKey.cbData = MD5_HASH_SIZE;
|
||||
// if(CascOpenFileByIndexKey((HANDLE)hs, &QueryKey, 0, &hFile))
|
||||
// {
|
||||
// // Make sure that the data file is open and frame header loaded
|
||||
// CascGetFileSize(hFile, NULL);
|
||||
// hf = IsValidFileHandle(hFile);
|
||||
// assert(hf->pStream != NULL);
|
||||
|
||||
// // Read the header area
|
||||
// FileOffset = hf->HeaderOffset - BLTE_HEADER_DELTA;
|
||||
// FileStream_Read(hf->pStream, &FileOffset, HeaderArea, sizeof(HeaderArea));
|
||||
// CascCloseFile(hFile);
|
||||
|
||||
// // Dump the header area
|
||||
// dump_print(dc, " FileSize: %X Rest: %s\n",
|
||||
// ConvertBytesToInteger_4_LE(&HeaderArea[0x10]),
|
||||
// StringFromBinary(&HeaderArea[0x14], 10, szBuffer));
|
||||
// }
|
||||
//}
|
||||
}
|
||||
else
|
||||
{
|
||||
dump_print(dc, " NO INDEX ENTRY\n");
|
||||
}
|
||||
}
|
||||
|
||||
void CascDumpEncodingEntry(
|
||||
TCascStorage * hs,
|
||||
TDumpContext * dc,
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry,
|
||||
int nDumpLevel)
|
||||
{
|
||||
PCASC_INDEX_ENTRY pIndexEntry;
|
||||
QUERY_KEY QueryKey;
|
||||
LPBYTE pbIndexKey;
|
||||
char szMd5[MD5_STRING_SIZE+1];
|
||||
|
||||
// If the encoding key exists
|
||||
if(pEncodingEntry != NULL)
|
||||
{
|
||||
dump_print(dc, " Size %lx Key Count: %u\n",
|
||||
ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE),
|
||||
pEncodingEntry->KeyCount);
|
||||
|
||||
// Dump all index keys
|
||||
pbIndexKey = pEncodingEntry->EncodingKey + MD5_HASH_SIZE;
|
||||
for(DWORD j = 0; j < pEncodingEntry->KeyCount; j++, pbIndexKey += MD5_HASH_SIZE)
|
||||
{
|
||||
// Dump the index key
|
||||
dump_print(dc, " %s\n", StringFromMD5(pbIndexKey, szMd5));
|
||||
|
||||
// Dump the index entry as well
|
||||
if(nDumpLevel >= DUMP_LEVEL_INDEX_ENTRIES)
|
||||
{
|
||||
QueryKey.pbData = pbIndexKey;
|
||||
QueryKey.cbData = MD5_HASH_SIZE;
|
||||
pIndexEntry = FindIndexEntry(hs, &QueryKey);
|
||||
CascDumpIndexEntry(hs, dc, pIndexEntry, nDumpLevel);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
dump_print(dc, " NO ENCODING KEYS\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,7 +291,7 @@ void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs)
|
||||
FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE);
|
||||
ArchOffset &= 0x3FFFFFFF;
|
||||
|
||||
fprintf(fp, " %02X %08X %08X %s\n", ArchIndex, ArchOffset, FileSize, StringFromBinary(pIndexEntry->IndexKey, CASC_FILE_KEY_SIZE, szIndexKey));
|
||||
fprintf(fp, " %02X %08X %08X %s\n", ArchIndex, (DWORD)ArchOffset, FileSize, StringFromBinary(pIndexEntry->IndexKey, CASC_FILE_KEY_SIZE, szIndexKey));
|
||||
}
|
||||
|
||||
CASC_FREE(ppIndexEntries);
|
||||
@@ -390,81 +301,6 @@ void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs)
|
||||
}
|
||||
}
|
||||
|
||||
void CascDumpRootFile(
|
||||
TCascStorage * hs,
|
||||
LPBYTE pbRootFile,
|
||||
DWORD cbRootFile,
|
||||
const char * szFormat,
|
||||
const TCHAR * szListFile,
|
||||
int nDumpLevel)
|
||||
{
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry;
|
||||
ROOT_BLOCK_INFO BlockInfo;
|
||||
PLISTFILE_MAP pListMap;
|
||||
QUERY_KEY EncodingKey;
|
||||
LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
|
||||
LPBYTE pbFilePointer;
|
||||
FILE * fp;
|
||||
char szOneLine[0x100];
|
||||
DWORD i;
|
||||
|
||||
// This function only dumps WoW-style root file
|
||||
assert(*(PDWORD)pbRootFile != CASC_MNDX_SIGNATURE);
|
||||
|
||||
// Create the dump file
|
||||
fp = CreateDumpFile(szFormat, hs);
|
||||
if(fp != NULL)
|
||||
{
|
||||
// Create the listfile map
|
||||
// DWORD dwTickCount = GetTickCount();
|
||||
pListMap = ListFile_CreateMap(szListFile);
|
||||
// dwTickCount = GetTickCount() - dwTickCount;
|
||||
|
||||
// Dump the root entries as-is
|
||||
for(pbFilePointer = pbRootFile; pbFilePointer <= pbRootFileEnd; )
|
||||
{
|
||||
// Validate the root block
|
||||
pbFilePointer = VerifyLocaleBlock(&BlockInfo, pbFilePointer, pbRootFileEnd);
|
||||
if(pbFilePointer == NULL)
|
||||
break;
|
||||
|
||||
// Dump the locale block
|
||||
fprintf(fp, "Flags: %08X Locales: %08X NumberOfFiles: %u\n"
|
||||
"=========================================================\n",
|
||||
BlockInfo.pLocaleBlockHdr->Flags,
|
||||
BlockInfo.pLocaleBlockHdr->Locales,
|
||||
BlockInfo.pLocaleBlockHdr->NumberOfFiles);
|
||||
|
||||
// Dump the hashes and encoding keys
|
||||
for(i = 0; i < BlockInfo.pLocaleBlockHdr->NumberOfFiles; i++)
|
||||
{
|
||||
// Dump the entry
|
||||
fprintf(fp, "%08X %08X-%08X %s %s\n",
|
||||
(DWORD)(BlockInfo.pInt32Array[i]),
|
||||
(DWORD)(BlockInfo.pRootEntries[i].FileNameHash >> 0x20),
|
||||
(DWORD)(BlockInfo.pRootEntries[i].FileNameHash),
|
||||
StringFromMD5((LPBYTE)BlockInfo.pRootEntries[i].EncodingKey, szOneLine),
|
||||
ListFile_FindName(pListMap, BlockInfo.pRootEntries[i].FileNameHash));
|
||||
|
||||
// Find the encoding entry in the encoding table
|
||||
if(nDumpLevel > 1)
|
||||
{
|
||||
EncodingKey.pbData = (LPBYTE)BlockInfo.pRootEntries[i].EncodingKey;
|
||||
EncodingKey.cbData = MD5_HASH_SIZE;
|
||||
pEncodingEntry = FindEncodingEntry(hs, &EncodingKey, NULL);
|
||||
DumpEncodingEntry(fp, hs, pEncodingEntry, nDumpLevel);
|
||||
}
|
||||
}
|
||||
|
||||
// Put extra newline
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
|
||||
ListFile_FreeMap(pListMap);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void CascDumpFile(const char * szFileName, HANDLE hFile)
|
||||
{
|
||||
FILE * fp;
|
||||
|
||||
981
dep/CascLib/src/CascFiles.cpp
Normal file
981
dep/CascLib/src/CascFiles.cpp
Normal file
@@ -0,0 +1,981 @@
|
||||
/*****************************************************************************/
|
||||
/* CascFiles.cpp Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Various text file parsers */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 29.04.14 1.00 Lad The first version of CascBuildCfg.cpp */
|
||||
/* 30.10.15 1.00 Lad Renamed to CascFiles.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
typedef int (*PARSEINFOFILE)(TCascStorage * hs, void * pvListFile);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
|
||||
struct TBuildFileInfo
|
||||
{
|
||||
const TCHAR * szFileName;
|
||||
CBLD_TYPE BuildFileType;
|
||||
};
|
||||
|
||||
struct TGameIdString
|
||||
{
|
||||
const char * szGameInfo;
|
||||
size_t cchGameInfo;
|
||||
DWORD dwGameInfo;
|
||||
};
|
||||
|
||||
static const TBuildFileInfo BuildTypes[] =
|
||||
{
|
||||
{_T(".build.info"), CascBuildInfo}, // Since HOTS build 30027, the game uses .build.info file for storage info
|
||||
{_T(".build.db"), CascBuildDb}, // Older CASC storages
|
||||
{NULL, CascBuildNone}
|
||||
};
|
||||
|
||||
static const TCHAR * DataDirs[] =
|
||||
{
|
||||
_T("SC2Data"), // Starcraft II (Legacy of the Void) build 38749
|
||||
_T("Data\\Casc"), // Overwatch
|
||||
_T("Data"), // World of Warcraft, Diablo
|
||||
_T("HeroesData"), // Heroes of the Storm
|
||||
_T("BNTData"), // Heroes of the Storm, until build 30414
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const TGameIdString GameIds[] =
|
||||
{
|
||||
{"Hero", 0x04, CASC_GAME_HOTS}, // Alpha build of Heroes of the Storm
|
||||
{"WoW", 0x03, CASC_GAME_WOW6}, // Alpha build of World of Warcraft - Warlords of Draenor
|
||||
{"Diablo3", 0x07, CASC_GAME_DIABLO3}, // Diablo III BETA 2.2.0
|
||||
{"Prometheus", 0x0A, CASC_GAME_OVERWATCH}, // Overwatch BETA since build 24919
|
||||
{"SC2", 0x03, CASC_GAME_STARCRAFT2}, // Starcraft II - Legacy of the Void
|
||||
{NULL, 0, 0},
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static bool inline IsValueSeparator(const char * szVarValue)
|
||||
{
|
||||
return ((0 <= szVarValue[0] && szVarValue[0] <= 0x20) || (szVarValue[0] == '|'));
|
||||
}
|
||||
|
||||
static bool IsCharDigit(BYTE OneByte)
|
||||
{
|
||||
return ('0' <= OneByte && OneByte <= '9');
|
||||
}
|
||||
|
||||
static DWORD GetLocaleMask(const char * szTag)
|
||||
{
|
||||
if(!strcmp(szTag, "enUS"))
|
||||
return CASC_LOCALE_ENUS;
|
||||
|
||||
if(!strcmp(szTag, "koKR"))
|
||||
return CASC_LOCALE_KOKR;
|
||||
|
||||
if(!strcmp(szTag, "frFR"))
|
||||
return CASC_LOCALE_FRFR;
|
||||
|
||||
if(!strcmp(szTag, "deDE"))
|
||||
return CASC_LOCALE_DEDE;
|
||||
|
||||
if(!strcmp(szTag, "zhCN"))
|
||||
return CASC_LOCALE_ZHCN;
|
||||
|
||||
if(!strcmp(szTag, "esES"))
|
||||
return CASC_LOCALE_ESES;
|
||||
|
||||
if(!strcmp(szTag, "zhTW"))
|
||||
return CASC_LOCALE_ZHTW;
|
||||
|
||||
if(!strcmp(szTag, "enGB"))
|
||||
return CASC_LOCALE_ENGB;
|
||||
|
||||
if(!strcmp(szTag, "enCN"))
|
||||
return CASC_LOCALE_ENCN;
|
||||
|
||||
if(!strcmp(szTag, "enTW"))
|
||||
return CASC_LOCALE_ENTW;
|
||||
|
||||
if(!strcmp(szTag, "esMX"))
|
||||
return CASC_LOCALE_ESMX;
|
||||
|
||||
if(!strcmp(szTag, "ruRU"))
|
||||
return CASC_LOCALE_RURU;
|
||||
|
||||
if(!strcmp(szTag, "ptBR"))
|
||||
return CASC_LOCALE_PTBR;
|
||||
|
||||
if(!strcmp(szTag, "itIT"))
|
||||
return CASC_LOCALE_ITIT;
|
||||
|
||||
if(!strcmp(szTag, "ptPT"))
|
||||
return CASC_LOCALE_PTPT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool IsInfoVariable(const char * szLineBegin, const char * szLineEnd, const char * szVarName, const char * szVarType)
|
||||
{
|
||||
size_t nLength;
|
||||
|
||||
// Check the variable name
|
||||
nLength = strlen(szVarName);
|
||||
if((size_t)(szLineEnd - szLineBegin) > nLength)
|
||||
{
|
||||
// Check the variable name
|
||||
if(!_strnicmp(szLineBegin, szVarName, nLength))
|
||||
{
|
||||
// Skip variable name and the exclamation mark
|
||||
szLineBegin += nLength;
|
||||
if(szLineBegin < szLineEnd && szLineBegin[0] == '!')
|
||||
{
|
||||
// Skip the exclamation mark
|
||||
szLineBegin++;
|
||||
|
||||
// Check the variable type
|
||||
nLength = strlen(szVarType);
|
||||
if((size_t)(szLineEnd - szLineBegin) > nLength)
|
||||
{
|
||||
// Check the variable name
|
||||
if(!_strnicmp(szLineBegin, szVarType, nLength))
|
||||
{
|
||||
// Skip variable type and the doublecolon
|
||||
szLineBegin += nLength;
|
||||
return (szLineBegin < szLineEnd && szLineBegin[0] == ':');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char * SkipInfoVariable(const char * szLineBegin, const char * szLineEnd)
|
||||
{
|
||||
while(szLineBegin < szLineEnd)
|
||||
{
|
||||
if(szLineBegin[0] == '|')
|
||||
return szLineBegin + 1;
|
||||
|
||||
szLineBegin++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static TCHAR * CheckForIndexDirectory(TCascStorage * hs, const TCHAR * szSubDir)
|
||||
{
|
||||
TCHAR * szIndexPath;
|
||||
|
||||
// Cpmbine the index path
|
||||
szIndexPath = CombinePath(hs->szDataPath, szSubDir);
|
||||
if(DirectoryExists(szIndexPath))
|
||||
{
|
||||
hs->szIndexPath = szIndexPath;
|
||||
return hs->szIndexPath;
|
||||
}
|
||||
|
||||
CASC_FREE(szIndexPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TCHAR * AppendBlobText(TCHAR * szBuffer, LPBYTE pbData, DWORD cbData, TCHAR chSeparator)
|
||||
{
|
||||
// Put the separator, if any
|
||||
if(chSeparator != 0)
|
||||
*szBuffer++ = chSeparator;
|
||||
|
||||
// Copy the blob data as text
|
||||
for(DWORD i = 0; i < cbData; i++)
|
||||
{
|
||||
*szBuffer++ = IntToHexChar[pbData[0] >> 0x04];
|
||||
*szBuffer++ = IntToHexChar[pbData[0] & 0x0F];
|
||||
pbData++;
|
||||
}
|
||||
|
||||
// Terminate the string
|
||||
*szBuffer = 0;
|
||||
|
||||
// Return new buffer position
|
||||
return szBuffer;
|
||||
}
|
||||
|
||||
static const char * CheckLineVariable(const char * szLineBegin, const char * szLineEnd, const char * szVarName)
|
||||
{
|
||||
size_t nLineLength = (size_t)(szLineEnd - szLineBegin);
|
||||
size_t nNameLength = strlen(szVarName);
|
||||
|
||||
// If the line longer than the variable name?
|
||||
if(nLineLength > nNameLength)
|
||||
{
|
||||
if(!_strnicmp((const char *)szLineBegin, szVarName, nNameLength))
|
||||
{
|
||||
// Skip the variable name
|
||||
szLineBegin += nNameLength;
|
||||
|
||||
// Skip the separator(s)
|
||||
while(szLineBegin < szLineEnd && IsValueSeparator(szLineBegin))
|
||||
szLineBegin++;
|
||||
|
||||
// Check if there is "="
|
||||
if(szLineBegin >= szLineEnd || szLineBegin[0] != '=')
|
||||
return NULL;
|
||||
szLineBegin++;
|
||||
|
||||
// Skip the separator(s)
|
||||
while(szLineBegin < szLineEnd && IsValueSeparator(szLineBegin))
|
||||
szLineBegin++;
|
||||
|
||||
// Check if there is "="
|
||||
if(szLineBegin >= szLineEnd)
|
||||
return NULL;
|
||||
|
||||
// Return the begin of the variable
|
||||
return szLineBegin;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int LoadInfoVariable(PQUERY_KEY pVarBlob, const char * szLineBegin, const char * szLineEnd, bool bHexaValue)
|
||||
{
|
||||
const char * szLinePtr = szLineBegin;
|
||||
|
||||
// Sanity checks
|
||||
assert(pVarBlob->pbData == NULL);
|
||||
assert(pVarBlob->cbData == 0);
|
||||
|
||||
// Check length of the variable
|
||||
while(szLinePtr < szLineEnd && szLinePtr[0] != '|')
|
||||
szLinePtr++;
|
||||
|
||||
// Allocate space for the blob
|
||||
if(bHexaValue)
|
||||
{
|
||||
// Initialize the blob
|
||||
pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) / 2);
|
||||
pVarBlob->cbData = (DWORD)((szLinePtr - szLineBegin) / 2);
|
||||
return ConvertStringToBinary(szLineBegin, (size_t)(szLinePtr - szLineBegin), pVarBlob->pbData);
|
||||
}
|
||||
|
||||
// Initialize the blob
|
||||
pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) + 1);
|
||||
pVarBlob->cbData = (DWORD)(szLinePtr - szLineBegin);
|
||||
|
||||
// Check for success
|
||||
if(pVarBlob->pbData == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Copy the string
|
||||
memcpy(pVarBlob->pbData, szLineBegin, pVarBlob->cbData);
|
||||
pVarBlob->pbData[pVarBlob->cbData] = 0;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static void AppendConfigFilePath(TCHAR * szFileName, PQUERY_KEY pFileKey)
|
||||
{
|
||||
size_t nLength = _tcslen(szFileName);
|
||||
|
||||
// If there is no slash, append if
|
||||
if(nLength > 0 && szFileName[nLength - 1] != '\\' && szFileName[nLength - 1] != '/')
|
||||
szFileName[nLength++] = _T('/');
|
||||
|
||||
// Get to the end of the file name
|
||||
szFileName = szFileName + nLength;
|
||||
|
||||
// Append the "config" directory
|
||||
_tcscpy(szFileName, _T("config"));
|
||||
szFileName += 6;
|
||||
|
||||
// Append the first level directory
|
||||
szFileName = AppendBlobText(szFileName, pFileKey->pbData, 1, _T('/'));
|
||||
szFileName = AppendBlobText(szFileName, pFileKey->pbData + 1, 1, _T('/'));
|
||||
szFileName = AppendBlobText(szFileName, pFileKey->pbData, pFileKey->cbData, _T('/'));
|
||||
}
|
||||
|
||||
static DWORD GetBlobCount(const char * szLineBegin, const char * szLineEnd)
|
||||
{
|
||||
DWORD dwBlobCount = 0;
|
||||
|
||||
// Until we find an end of the line
|
||||
while(szLineBegin < szLineEnd)
|
||||
{
|
||||
// Skip the blob
|
||||
while(szLineBegin < szLineEnd && IsValueSeparator(szLineBegin) == false)
|
||||
szLineBegin++;
|
||||
|
||||
// Increment the number of blobs
|
||||
dwBlobCount++;
|
||||
|
||||
// Skip the separator
|
||||
while(szLineBegin < szLineEnd && IsValueSeparator(szLineBegin))
|
||||
szLineBegin++;
|
||||
}
|
||||
|
||||
return dwBlobCount;
|
||||
}
|
||||
|
||||
static int LoadBlobArray(
|
||||
PQUERY_KEY pBlob,
|
||||
const char * szLineBegin,
|
||||
const char * szLineEnd,
|
||||
DWORD dwMaxBlobs)
|
||||
{
|
||||
LPBYTE pbBufferEnd = pBlob->pbData + pBlob->cbData;
|
||||
LPBYTE pbBuffer = pBlob->pbData;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Sanity check
|
||||
assert(pBlob->pbData != NULL);
|
||||
assert(pBlob->cbData != 0);
|
||||
|
||||
// Until we find an end of the line
|
||||
while(szLineBegin < szLineEnd && dwMaxBlobs > 0)
|
||||
{
|
||||
const char * szBlobEnd = szLineBegin;
|
||||
|
||||
// Find the end of the text blob
|
||||
while(szBlobEnd < szLineEnd && IsValueSeparator(szBlobEnd) == false)
|
||||
szBlobEnd++;
|
||||
|
||||
// Verify the length of the found blob
|
||||
if((szBlobEnd - szLineBegin) != MD5_STRING_SIZE)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Verify if there is enough space in the buffer
|
||||
if((pbBufferEnd - pbBuffer) < MD5_HASH_SIZE)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Perform the conversion
|
||||
nError = ConvertStringToBinary(szLineBegin, MD5_STRING_SIZE, pbBuffer);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
|
||||
// Move pointers
|
||||
pbBuffer += MD5_HASH_SIZE;
|
||||
dwMaxBlobs--;
|
||||
|
||||
// Skip the separator
|
||||
while(szBlobEnd < szLineEnd && IsValueSeparator(szBlobEnd))
|
||||
szBlobEnd++;
|
||||
szLineBegin = szBlobEnd;
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int LoadMultipleBlobs(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd, DWORD dwBlobCount)
|
||||
{
|
||||
size_t nLength = (szLineEnd - szLineBegin);
|
||||
|
||||
// We expect each blob to have length of the encoding key and one space between
|
||||
if(nLength > (dwBlobCount * MD5_STRING_SIZE) + ((dwBlobCount - 1) * sizeof(char)))
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Allocate the blob buffer
|
||||
pBlob->pbData = CASC_ALLOC(BYTE, dwBlobCount * MD5_HASH_SIZE);
|
||||
if(pBlob->pbData == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Set the buffer size and load the blob array
|
||||
pBlob->cbData = dwBlobCount * MD5_HASH_SIZE;
|
||||
return LoadBlobArray(pBlob, szLineBegin, szLineEnd, dwBlobCount);
|
||||
}
|
||||
|
||||
static int LoadMultipleBlobs(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd)
|
||||
{
|
||||
return LoadMultipleBlobs(pBlob, szLineBegin, szLineEnd, GetBlobCount(szLineBegin, szLineEnd));
|
||||
}
|
||||
|
||||
static int LoadSingleBlob(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd)
|
||||
{
|
||||
return LoadMultipleBlobs(pBlob, szLineBegin, szLineEnd, 1);
|
||||
}
|
||||
|
||||
static int GetGameType(TCascStorage * hs, const char * szVarBegin, const char * szLineEnd)
|
||||
{
|
||||
// Go through all games that we support
|
||||
for(size_t i = 0; GameIds[i].szGameInfo != NULL; i++)
|
||||
{
|
||||
// Check the length of the variable
|
||||
if((size_t)(szLineEnd - szVarBegin) == GameIds[i].cchGameInfo)
|
||||
{
|
||||
// Check the string
|
||||
if(!_strnicmp(szVarBegin, GameIds[i].szGameInfo, GameIds[i].cchGameInfo))
|
||||
{
|
||||
hs->dwGameInfo = GameIds[i].dwGameInfo;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown/unsupported game
|
||||
assert(false);
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
// "B29049"
|
||||
// "WOW-18125patch6.0.1"
|
||||
// "30013_Win32_2_2_0_Ptr_ptr"
|
||||
// "prometheus-0_8_0_0-24919"
|
||||
static int GetBuildNumber(TCascStorage * hs, const char * szVarBegin, const char * szLineEnd)
|
||||
{
|
||||
DWORD dwBuildNumber = 0;
|
||||
|
||||
// Skip all non-digit characters
|
||||
while(szVarBegin < szLineEnd)
|
||||
{
|
||||
// There must be at least three digits (build 99 anyone?)
|
||||
if(IsCharDigit(szVarBegin[0]) && IsCharDigit(szVarBegin[1]) && IsCharDigit(szVarBegin[2]))
|
||||
{
|
||||
// Convert the build number string to value
|
||||
while(szVarBegin < szLineEnd && IsCharDigit(szVarBegin[0]))
|
||||
dwBuildNumber = (dwBuildNumber * 10) + (*szVarBegin++ - '0');
|
||||
break;
|
||||
}
|
||||
|
||||
// Move to the next
|
||||
szVarBegin++;
|
||||
}
|
||||
|
||||
assert(dwBuildNumber != 0);
|
||||
hs->dwBuildNumber = dwBuildNumber;
|
||||
return (dwBuildNumber != 0) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
static int GetDefaultLocaleMask(TCascStorage * hs, PQUERY_KEY pTagsString)
|
||||
{
|
||||
char * szTagEnd = (char *)pTagsString->pbData + pTagsString->cbData;
|
||||
char * szTagPtr = (char *)pTagsString->pbData;
|
||||
char * szNext;
|
||||
DWORD dwLocaleMask = 0;
|
||||
|
||||
while(szTagPtr < szTagEnd)
|
||||
{
|
||||
// Get the next part
|
||||
szNext = strchr(szTagPtr, ' ');
|
||||
if(szNext != NULL)
|
||||
*szNext++ = 0;
|
||||
|
||||
// Check whether the current tag is a language identifier
|
||||
dwLocaleMask = dwLocaleMask | GetLocaleMask(szTagPtr);
|
||||
|
||||
// Get the next part
|
||||
if(szNext == NULL)
|
||||
break;
|
||||
|
||||
// Skip spaces
|
||||
while(szNext < szTagEnd && szNext[0] == ' ')
|
||||
szNext++;
|
||||
szTagPtr = szNext;
|
||||
}
|
||||
|
||||
hs->dwDefaultLocale = dwLocaleMask;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static void * FetchAndVerifyConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey)
|
||||
{
|
||||
TCHAR * szFileName;
|
||||
void * pvListFile = NULL;
|
||||
|
||||
// Construct the local file name
|
||||
szFileName = CascNewStr(hs->szDataPath, 8 + 3 + 3 + 32);
|
||||
if(szFileName != NULL)
|
||||
{
|
||||
// Add the part where the config file path is
|
||||
AppendConfigFilePath(szFileName, pFileKey);
|
||||
|
||||
// Load and verify the external listfile
|
||||
pvListFile = ListFile_OpenExternal(szFileName);
|
||||
if(pvListFile != NULL)
|
||||
{
|
||||
if(!ListFile_VerifyMD5(pvListFile, pFileKey->pbData))
|
||||
{
|
||||
ListFile_Free(pvListFile);
|
||||
pvListFile = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Free the file name
|
||||
CASC_FREE(szFileName);
|
||||
}
|
||||
|
||||
return pvListFile;
|
||||
}
|
||||
|
||||
static int ParseFile_BuildInfo(TCascStorage * hs, void * pvListFile)
|
||||
{
|
||||
QUERY_KEY Active = {NULL, 0};
|
||||
QUERY_KEY TagString = {NULL, 0};
|
||||
QUERY_KEY CdnHost = {NULL, 0};
|
||||
QUERY_KEY CdnPath = {NULL, 0};
|
||||
char szOneLine1[0x200];
|
||||
char szOneLine2[0x200];
|
||||
size_t nLength1;
|
||||
size_t nLength2;
|
||||
int nError = ERROR_BAD_FORMAT;
|
||||
|
||||
// Extract the first line, cotaining the headers
|
||||
nLength1 = ListFile_GetNextLine(pvListFile, szOneLine1, _maxchars(szOneLine1));
|
||||
if(nLength1 == 0)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Now parse the second and the next lines. We are looking for line
|
||||
// with "Active" set to 1
|
||||
for(;;)
|
||||
{
|
||||
const char * szLinePtr1 = szOneLine1;
|
||||
const char * szLineEnd1 = szOneLine1 + nLength1;
|
||||
const char * szLinePtr2 = szOneLine2;
|
||||
const char * szLineEnd2;
|
||||
|
||||
// Read the next line
|
||||
nLength2 = ListFile_GetNextLine(pvListFile, szOneLine2, _maxchars(szOneLine2));
|
||||
if(nLength2 == 0)
|
||||
break;
|
||||
szLineEnd2 = szLinePtr2 + nLength2;
|
||||
|
||||
// Parse all variables
|
||||
while(szLinePtr1 < szLineEnd1)
|
||||
{
|
||||
// Check for variables we need
|
||||
if(IsInfoVariable(szLinePtr1, szLineEnd1, "Active", "DEC"))
|
||||
LoadInfoVariable(&Active, szLinePtr2, szLineEnd2, false);
|
||||
if(IsInfoVariable(szLinePtr1, szLineEnd1, "Build Key", "HEX"))
|
||||
LoadInfoVariable(&hs->CdnBuildKey, szLinePtr2, szLineEnd2, true);
|
||||
if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Key", "HEX"))
|
||||
LoadInfoVariable(&hs->CdnConfigKey, szLinePtr2, szLineEnd2, true);
|
||||
if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Hosts", "STRING"))
|
||||
LoadInfoVariable(&CdnHost, szLinePtr2, szLineEnd2, false);
|
||||
if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Path", "STRING"))
|
||||
LoadInfoVariable(&CdnPath, szLinePtr2, szLineEnd2, false);
|
||||
if(IsInfoVariable(szLinePtr1, szLineEnd1, "Tags", "STRING"))
|
||||
LoadInfoVariable(&TagString, szLinePtr2, szLineEnd2, false);
|
||||
|
||||
// Move both line pointers
|
||||
szLinePtr1 = SkipInfoVariable(szLinePtr1, szLineEnd1);
|
||||
if(szLinePtr1 == NULL)
|
||||
break;
|
||||
|
||||
szLinePtr2 = SkipInfoVariable(szLinePtr2, szLineEnd2);
|
||||
if(szLinePtr2 == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
// Stop parsing if found active config
|
||||
if(Active.pbData != NULL && *Active.pbData == '1')
|
||||
break;
|
||||
|
||||
// Free the blobs
|
||||
FreeCascBlob(&Active);
|
||||
FreeCascBlob(&hs->CdnBuildKey);
|
||||
FreeCascBlob(&hs->CdnConfigKey);
|
||||
FreeCascBlob(&CdnHost);
|
||||
FreeCascBlob(&CdnPath);
|
||||
FreeCascBlob(&TagString);
|
||||
}
|
||||
|
||||
// All four must be present
|
||||
if(hs->CdnBuildKey.pbData != NULL &&
|
||||
hs->CdnConfigKey.pbData != NULL &&
|
||||
CdnHost.pbData != NULL &&
|
||||
CdnPath.pbData != NULL)
|
||||
{
|
||||
// Merge the CDN host and CDN path
|
||||
hs->szUrlPath = CASC_ALLOC(TCHAR, CdnHost.cbData + CdnPath.cbData + 1);
|
||||
if(hs->szUrlPath != NULL)
|
||||
{
|
||||
CopyString(hs->szUrlPath, (char *)CdnHost.pbData, CdnHost.cbData);
|
||||
CopyString(hs->szUrlPath + CdnHost.cbData, (char *)CdnPath.pbData, CdnPath.cbData);
|
||||
nError = ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
// If we found tags, we can extract language build from it
|
||||
if(TagString.pbData != NULL)
|
||||
GetDefaultLocaleMask(hs, &TagString);
|
||||
|
||||
FreeCascBlob(&CdnHost);
|
||||
FreeCascBlob(&CdnPath);
|
||||
FreeCascBlob(&TagString);
|
||||
FreeCascBlob(&Active);
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int ParseFile_BuildDb(TCascStorage * hs, void * pvListFile)
|
||||
{
|
||||
const char * szLinePtr;
|
||||
const char * szLineEnd;
|
||||
char szOneLine[0x200];
|
||||
size_t nLength;
|
||||
int nError;
|
||||
|
||||
// Load the single line from the text file
|
||||
nLength = ListFile_GetNextLine(pvListFile, szOneLine, _maxchars(szOneLine));
|
||||
if(nLength == 0)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Set the line range
|
||||
szLinePtr = szOneLine;
|
||||
szLineEnd = szOneLine + nLength;
|
||||
|
||||
// Extract the CDN build key
|
||||
nError = LoadInfoVariable(&hs->CdnBuildKey, szLinePtr, szLineEnd, true);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Skip the variable
|
||||
szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd);
|
||||
|
||||
// Load the CDN config hash
|
||||
nError = LoadInfoVariable(&hs->CdnConfigKey, szLinePtr, szLineEnd, true);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Skip the variable
|
||||
szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd);
|
||||
|
||||
// Skip the Locale/OS/code variable
|
||||
szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd);
|
||||
|
||||
// Load the URL
|
||||
hs->szUrlPath = CascNewStrFromAnsi(szLinePtr, szLineEnd);
|
||||
if(hs->szUrlPath == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify all variables
|
||||
if(hs->CdnBuildKey.pbData == NULL || hs->CdnConfigKey.pbData == NULL || hs->szUrlPath == NULL)
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int LoadCdnConfigFile(TCascStorage * hs, void * pvListFile)
|
||||
{
|
||||
const char * szLineBegin;
|
||||
const char * szVarBegin;
|
||||
const char * szLineEnd;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Keep parsing the listfile while there is something in there
|
||||
for(;;)
|
||||
{
|
||||
// Get the next line
|
||||
if(!ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd))
|
||||
break;
|
||||
|
||||
// Archive group
|
||||
szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "archive-group");
|
||||
if(szVarBegin != NULL)
|
||||
{
|
||||
nError = LoadSingleBlob(&hs->ArchivesGroup, szVarBegin, szLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Archives
|
||||
szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "archives");
|
||||
if(szVarBegin != NULL)
|
||||
{
|
||||
nError = LoadMultipleBlobs(&hs->ArchivesKey, szVarBegin, szLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Patch archive group
|
||||
szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "patch-archive-group");
|
||||
if(szVarBegin != NULL)
|
||||
{
|
||||
LoadSingleBlob(&hs->PatchArchivesKey, szVarBegin, szLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Patch archives
|
||||
szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "patch-archives");
|
||||
if(szVarBegin != NULL)
|
||||
{
|
||||
nError = LoadMultipleBlobs(&hs->PatchArchivesKey, szVarBegin, szLineEnd);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all required fields are present
|
||||
if(hs->ArchivesKey.pbData == NULL || hs->ArchivesKey.cbData == 0)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int LoadCdnBuildFile(TCascStorage * hs, void * pvListFile)
|
||||
{
|
||||
const char * szLineBegin;
|
||||
const char * szVarBegin;
|
||||
const char * szLineEnd = NULL;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// Get the next line
|
||||
if(!ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd))
|
||||
break;
|
||||
|
||||
// Game name
|
||||
szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "build-product");
|
||||
if(szVarBegin != NULL)
|
||||
{
|
||||
GetGameType(hs, szVarBegin, szLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Game build number
|
||||
szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "build-name");
|
||||
if(szVarBegin != NULL)
|
||||
{
|
||||
GetBuildNumber(hs, szVarBegin, szLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Root
|
||||
szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "root");
|
||||
if(szVarBegin != NULL)
|
||||
{
|
||||
LoadSingleBlob(&hs->RootKey, szVarBegin, szLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Patch
|
||||
szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "patch");
|
||||
if(szVarBegin != NULL)
|
||||
{
|
||||
LoadSingleBlob(&hs->PatchKey, szVarBegin, szLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Download
|
||||
szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "download");
|
||||
if(szVarBegin != NULL)
|
||||
{
|
||||
LoadSingleBlob(&hs->DownloadKey, szVarBegin, szLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Install
|
||||
szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "install");
|
||||
if(szVarBegin != NULL)
|
||||
{
|
||||
LoadSingleBlob(&hs->InstallKey, szVarBegin, szLineEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Encoding keys
|
||||
szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "encoding");
|
||||
if(szVarBegin != NULL)
|
||||
{
|
||||
nError = LoadMultipleBlobs(&hs->EncodingKey, szVarBegin, szLineEnd, 2);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Check the encoding keys
|
||||
if(hs->EncodingKey.pbData == NULL || hs->EncodingKey.cbData != MD5_HASH_SIZE * 2)
|
||||
return ERROR_BAD_FORMAT;
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int CheckDataDirectory(TCascStorage * hs, TCHAR * szDirectory)
|
||||
{
|
||||
TCHAR * szDataPath;
|
||||
int nError = ERROR_FILE_NOT_FOUND;
|
||||
|
||||
// Try all known subdirectories
|
||||
for(size_t i = 0; DataDirs[i] != NULL; i++)
|
||||
{
|
||||
// Create the eventual data path
|
||||
szDataPath = CombinePath(szDirectory, DataDirs[i]);
|
||||
if(szDataPath != NULL)
|
||||
{
|
||||
// Does that directory exist?
|
||||
if(DirectoryExists(szDataPath))
|
||||
{
|
||||
hs->szDataPath = szDataPath;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// Free the data path
|
||||
CASC_FREE(szDataPath);
|
||||
}
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int LoadBuildInfo(TCascStorage * hs)
|
||||
{
|
||||
PARSEINFOFILE PfnParseProc = NULL;
|
||||
void * pvListFile;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
switch(hs->BuildFileType)
|
||||
{
|
||||
case CascBuildInfo:
|
||||
PfnParseProc = ParseFile_BuildInfo;
|
||||
break;
|
||||
|
||||
case CascBuildDb:
|
||||
PfnParseProc = ParseFile_BuildDb;
|
||||
break;
|
||||
|
||||
default:
|
||||
nError = ERROR_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse the appropriate build file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
pvListFile = ListFile_OpenExternal(hs->szBuildFile);
|
||||
if(pvListFile != NULL)
|
||||
{
|
||||
// Parse the info file
|
||||
nError = PfnParseProc(hs, pvListFile);
|
||||
ListFile_Free(pvListFile);
|
||||
}
|
||||
else
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// If the .build.info OR .build.db file has been loaded,
|
||||
// proceed with loading the CDN config file and CDN build file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Load the configuration file
|
||||
pvListFile = FetchAndVerifyConfigFile(hs, &hs->CdnConfigKey);
|
||||
if(pvListFile != NULL)
|
||||
{
|
||||
nError = LoadCdnConfigFile(hs, pvListFile);
|
||||
ListFile_Free(pvListFile);
|
||||
}
|
||||
else
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Load the build file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
pvListFile = FetchAndVerifyConfigFile(hs, &hs->CdnBuildKey);
|
||||
if(pvListFile != NULL)
|
||||
{
|
||||
nError = LoadCdnBuildFile(hs, pvListFile);
|
||||
ListFile_Free(pvListFile);
|
||||
}
|
||||
else
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Fill the index directory
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// First, check for more common "data" subdirectory
|
||||
if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("data"))) != NULL)
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
// Second, try the "darch" subdirectory (older builds of HOTS - Alpha)
|
||||
if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("darch"))) != NULL)
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
// Checks whether there is a ".agent.db". If yes, the function
|
||||
// sets "szRootPath" and "szDataPath" in the storage structure
|
||||
// and returns ERROR_SUCCESS
|
||||
int CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory)
|
||||
{
|
||||
TFileStream * pStream;
|
||||
TCHAR * szBuildFile;
|
||||
int nError = ERROR_FILE_NOT_FOUND;
|
||||
|
||||
// Try to find any of the root files used in the history
|
||||
for(size_t i = 0; BuildTypes[i].szFileName != NULL; i++)
|
||||
{
|
||||
// Create the full name of the .agent.db file
|
||||
szBuildFile = CombinePath(szDirectory, BuildTypes[i].szFileName);
|
||||
if(szBuildFile != NULL)
|
||||
{
|
||||
// Attempt to open the file
|
||||
pStream = FileStream_OpenFile(szBuildFile, 0);
|
||||
if(pStream != NULL)
|
||||
{
|
||||
// Free the stream
|
||||
FileStream_Close(pStream);
|
||||
|
||||
// Check for the data directory
|
||||
nError = CheckDataDirectory(hs, szDirectory);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
hs->szBuildFile = szBuildFile;
|
||||
hs->BuildFileType = BuildTypes[i].BuildFileType;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
CASC_FREE(szBuildFile);
|
||||
}
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
// Parses single line from Overwatch.
|
||||
// The line structure is: "#MD5|CHUNK_ID|FILENAME|INSTALLPATH"
|
||||
// The line has all preceding spaces removed
|
||||
int ParseRootFileLine(const char * szLinePtr, const char * szLineEnd, PQUERY_KEY PtrEncodingKey, char * szFileName, size_t nMaxChars)
|
||||
{
|
||||
size_t nLength;
|
||||
int nError;
|
||||
|
||||
// Check the MD5 (aka encoding key)
|
||||
if(szLinePtr[MD5_STRING_SIZE] != '|')
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Convert the encoding key to binary
|
||||
PtrEncodingKey->cbData = MD5_HASH_SIZE;
|
||||
nError = ConvertStringToBinary(szLinePtr, MD5_STRING_SIZE, PtrEncodingKey->pbData);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
|
||||
// Skip the MD5
|
||||
szLinePtr += MD5_STRING_SIZE+1;
|
||||
|
||||
// Skip the chunk ID
|
||||
szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd);
|
||||
|
||||
// Get the archived file name
|
||||
szLineEnd = SkipInfoVariable(szLinePtr, szLineEnd);
|
||||
nLength = (size_t)(szLineEnd - szLinePtr - 1);
|
||||
|
||||
// Get the file name
|
||||
if(nLength > nMaxChars)
|
||||
return ERROR_INSUFFICIENT_BUFFER;
|
||||
|
||||
memcpy(szFileName, szLinePtr, nLength);
|
||||
szFileName[nLength] = 0;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
@@ -11,7 +11,6 @@
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
#include "CascMndxRoot.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
@@ -30,16 +29,22 @@ static void FreeSearchHandle(TCascSearch * pSearch)
|
||||
|
||||
// Close (dereference) the archive handle
|
||||
if(pSearch->hs != NULL)
|
||||
{
|
||||
// Give root handler chance to free their stuff
|
||||
RootHandler_EndSearch(pSearch->hs->pRootHandler, pSearch);
|
||||
|
||||
// Dereference the storage handle
|
||||
CascCloseStorage((HANDLE)pSearch->hs);
|
||||
pSearch->hs = NULL;
|
||||
pSearch->hs = NULL;
|
||||
}
|
||||
|
||||
// Free the file cache and frame array
|
||||
if(pSearch->szMask != NULL)
|
||||
CASC_FREE(pSearch->szMask);
|
||||
if(pSearch->szListFile != NULL)
|
||||
CASC_FREE(pSearch->szListFile);
|
||||
if(pSearch->pStruct1C != NULL)
|
||||
delete pSearch->pStruct1C;
|
||||
// if(pSearch->pStruct1C != NULL)
|
||||
// delete pSearch->pStruct1C;
|
||||
if(pSearch->pCache != NULL)
|
||||
ListFile_Free(pSearch->pCache);
|
||||
|
||||
@@ -47,58 +52,6 @@ static void FreeSearchHandle(TCascSearch * pSearch)
|
||||
pSearch->szClassName = NULL;
|
||||
CASC_FREE(pSearch);
|
||||
}
|
||||
/*
|
||||
DWORD dwRootEntries = 0;
|
||||
DWORD dwEncoEntries = 0;
|
||||
DWORD dwIndexEntries = 0;
|
||||
*/
|
||||
static bool VerifyRootEntry(TCascSearch * pSearch, PCASC_ROOT_ENTRY pRootEntry, PCASC_FIND_DATA pFindData, size_t nRootIndex)
|
||||
{
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry;
|
||||
PCASC_INDEX_ENTRY pIndexEntry;
|
||||
TCascStorage * hs = pSearch->hs;
|
||||
QUERY_KEY QueryKey;
|
||||
DWORD dwByteIndex = (DWORD)(nRootIndex / 0x08);
|
||||
DWORD dwBitIndex = (DWORD)(nRootIndex & 0x07);
|
||||
|
||||
// First of all, check if that entry has been reported before
|
||||
// If the bit is set, then the file has already been reported
|
||||
// by a previous search iteration
|
||||
if(pSearch->BitArray[dwByteIndex] & (1 << dwBitIndex))
|
||||
return false;
|
||||
pSearch->BitArray[dwByteIndex] |= (1 << dwBitIndex);
|
||||
|
||||
// Increment the number of root entries
|
||||
// dwRootEntries++;
|
||||
|
||||
// Now try to find that encoding key in the array of encoding keys
|
||||
QueryKey.pbData = (LPBYTE)pRootEntry->EncodingKey;
|
||||
QueryKey.cbData = MD5_HASH_SIZE;
|
||||
pEncodingEntry = FindEncodingEntry(hs, &QueryKey, NULL);
|
||||
if(pEncodingEntry == NULL)
|
||||
return false;
|
||||
|
||||
// dwEncoEntries++;
|
||||
|
||||
// Now try to find the index entry. Note that we take the first key
|
||||
QueryKey.pbData = pEncodingEntry->EncodingKey + MD5_HASH_SIZE;
|
||||
QueryKey.cbData = MD5_HASH_SIZE;
|
||||
pIndexEntry = FindIndexEntry(hs, &QueryKey);
|
||||
if(pIndexEntry == NULL)
|
||||
return false;
|
||||
|
||||
// dwIndexEntries++;
|
||||
|
||||
// Fill the name hash and the MD5
|
||||
memcpy(pFindData->EncodingKey, pRootEntry->EncodingKey, MD5_HASH_SIZE);
|
||||
pFindData->FileNameHash = pRootEntry->FileNameHash;
|
||||
pFindData->dwPackageIndex = 0;
|
||||
pFindData->dwLocaleFlags = pRootEntry->Locales;
|
||||
|
||||
// Fill-in the file size
|
||||
pFindData->dwFileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE);
|
||||
return true;
|
||||
}
|
||||
|
||||
static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szListFile, const char * szMask)
|
||||
{
|
||||
@@ -106,7 +59,7 @@ static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szLis
|
||||
size_t cbToAllocate;
|
||||
|
||||
// When using the MNDX info, do not allocate the extra bit array
|
||||
cbToAllocate = sizeof(TCascSearch) + ((hs->pMndxInfo == NULL) ? (hs->RootTable.TableSize / 8) : 0);
|
||||
cbToAllocate = sizeof(TCascSearch) + ((hs->pEncodingMap->TableSize + 7) / 8);
|
||||
pSearch = (TCascSearch *)CASC_ALLOC(BYTE, cbToAllocate);
|
||||
if(pSearch != NULL)
|
||||
{
|
||||
@@ -125,7 +78,7 @@ static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szLis
|
||||
// Save the other variables
|
||||
if(szListFile != NULL)
|
||||
{
|
||||
pSearch->szListFile = NewStr(szListFile, 0);
|
||||
pSearch->szListFile = CascNewStr(szListFile, 0);
|
||||
if(pSearch->szListFile == NULL)
|
||||
{
|
||||
FreeSearchHandle(pSearch);
|
||||
@@ -134,7 +87,7 @@ static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szLis
|
||||
}
|
||||
|
||||
// Allocate the search mask
|
||||
pSearch->szMask = NewStr(szMask, 0);
|
||||
pSearch->szMask = CascNewStr(szMask, 0);
|
||||
if(pSearch->szMask == NULL)
|
||||
{
|
||||
FreeSearchHandle(pSearch);
|
||||
@@ -145,64 +98,106 @@ static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szLis
|
||||
return pSearch;
|
||||
}
|
||||
|
||||
static bool DoStorageSearch_ListFile(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
|
||||
// Perform searching using root-specific provider.
|
||||
// The provider may need the listfile
|
||||
static bool DoStorageSearch_RootFile(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
|
||||
{
|
||||
PCASC_ROOT_ENTRY pRootEntry;
|
||||
TCascStorage * hs = pSearch->hs;
|
||||
char szFileName2[MAX_PATH + 1];
|
||||
DWORD TableIndex = 0;
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry;
|
||||
PCASC_INDEX_ENTRY pIndexEntry;
|
||||
QUERY_KEY EncodingKey;
|
||||
QUERY_KEY IndexKey;
|
||||
LPBYTE pbEncodingKey;
|
||||
DWORD EncodingIndex = 0;
|
||||
DWORD LocaleFlags = 0;
|
||||
DWORD FileSize = CASC_INVALID_SIZE;
|
||||
DWORD ByteIndex;
|
||||
DWORD BitMask;
|
||||
|
||||
// Get next file from the listfile
|
||||
while(ListFile_GetNext(pSearch->pCache, pSearch->szMask, pSearch->szFileName, MAX_PATH))
|
||||
for(;;)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
// if(!_stricmp(pSearch->szFileName, "Character\\BloodElf\\Female\\DeathKnightEyeGlow.blp"))
|
||||
// DebugBreak();
|
||||
#endif
|
||||
// Attempt to find (the next) file from the root entry
|
||||
pbEncodingKey = RootHandler_Search(pSearch->hs->pRootHandler, pSearch, &FileSize, &LocaleFlags);
|
||||
if(pbEncodingKey == NULL)
|
||||
return false;
|
||||
|
||||
// Normalize the file name found in the list file
|
||||
NormalizeFileName_UpperBkSlash(szFileName2, pSearch->szFileName, MAX_PATH);
|
||||
|
||||
// Find the root entry
|
||||
pRootEntry = FindRootEntry(hs, szFileName2, &TableIndex);
|
||||
if(pRootEntry != NULL)
|
||||
// Verify whether the encoding key exists in the encoding table
|
||||
EncodingKey.pbData = pbEncodingKey;
|
||||
EncodingKey.cbData = MD5_HASH_SIZE;
|
||||
pEncodingEntry = FindEncodingEntry(pSearch->hs, &EncodingKey, &EncodingIndex);
|
||||
if(pEncodingEntry != NULL)
|
||||
{
|
||||
// Verify whether the file exists in the storage
|
||||
if(VerifyRootEntry(pSearch, pRootEntry, pFindData, TableIndex))
|
||||
{
|
||||
strcpy(pFindData->szFileName, pSearch->szFileName);
|
||||
pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Mark the item as already found
|
||||
// Note: Duplicate items are allowed while we are searching using file names
|
||||
// Do not exclude items from search if they were found before
|
||||
ByteIndex = (DWORD)(EncodingIndex / 8);
|
||||
BitMask = 1 << (EncodingIndex & 0x07);
|
||||
pSearch->BitArray[ByteIndex] |= BitMask;
|
||||
|
||||
// Locate the index entry
|
||||
IndexKey.pbData = GET_INDEX_KEY(pEncodingEntry);
|
||||
IndexKey.cbData = MD5_HASH_SIZE;
|
||||
pIndexEntry = FindIndexEntry(pSearch->hs, &IndexKey);
|
||||
if(pIndexEntry == NULL)
|
||||
continue;
|
||||
|
||||
// Listfile search ended
|
||||
return false;
|
||||
}
|
||||
// If we retrieved the file size directly from the root provider, use it
|
||||
// Otherwise, we need to retrieve it from the encoding entry
|
||||
if(FileSize == CASC_INVALID_SIZE)
|
||||
FileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE);
|
||||
|
||||
static bool DoStorageSearch_Hash(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
|
||||
{
|
||||
PCASC_ROOT_ENTRY pRootEntry;
|
||||
TCascStorage * hs = pSearch->hs;
|
||||
|
||||
// Check if there is more files with the same name hash
|
||||
while(pSearch->RootIndex < hs->RootTable.TableSize)
|
||||
{
|
||||
// Get the pointer to the root entry
|
||||
pRootEntry = hs->RootTable.TablePtr + pSearch->RootIndex;
|
||||
|
||||
// Verify if that root entry exists in the CASC storage
|
||||
// and was not found before
|
||||
if(VerifyRootEntry(pSearch, pRootEntry, pFindData, pSearch->RootIndex))
|
||||
{
|
||||
pFindData->szFileName[0] = 0;
|
||||
pFindData->szPlainName = NULL;
|
||||
// Fill-in the found file
|
||||
strcpy(pFindData->szFileName, pSearch->szFileName);
|
||||
memcpy(pFindData->EncodingKey, pEncodingEntry->EncodingKey, MD5_HASH_SIZE);
|
||||
pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName);
|
||||
pFindData->dwLocaleFlags = LocaleFlags;
|
||||
pFindData->dwFileSize = FileSize;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the next entry
|
||||
pSearch->RootIndex++;
|
||||
static bool DoStorageSearch_EncodingKey(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
|
||||
{
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry;
|
||||
PCASC_INDEX_ENTRY pIndexEntry;
|
||||
TCascStorage * hs = pSearch->hs;
|
||||
QUERY_KEY IndexKey;
|
||||
DWORD ByteIndex;
|
||||
DWORD BitMask;
|
||||
|
||||
// Check for encoding keys that haven't been found yet
|
||||
while(pSearch->IndexLevel1 < hs->pEncodingMap->TableSize)
|
||||
{
|
||||
// Check if that entry has been reported before
|
||||
ByteIndex = (DWORD)(pSearch->IndexLevel1 / 8);
|
||||
BitMask = 1 << (pSearch->IndexLevel1 & 0x07);
|
||||
if((pSearch->BitArray[ByteIndex] & BitMask) == 0)
|
||||
{
|
||||
// Locate the index entry
|
||||
pEncodingEntry = (PCASC_ENCODING_ENTRY)hs->pEncodingMap->HashTable[pSearch->IndexLevel1];
|
||||
if(pEncodingEntry != NULL)
|
||||
{
|
||||
IndexKey.pbData = GET_INDEX_KEY(pEncodingEntry);
|
||||
IndexKey.cbData = MD5_HASH_SIZE;
|
||||
pIndexEntry = FindIndexEntry(pSearch->hs, &IndexKey);
|
||||
if(pIndexEntry != NULL)
|
||||
{
|
||||
// Fill-in the found file
|
||||
memcpy(pFindData->EncodingKey, pEncodingEntry->EncodingKey, MD5_HASH_SIZE);
|
||||
pFindData->szFileName[0] = 0;
|
||||
pFindData->szPlainName = NULL;
|
||||
pFindData->dwLocaleFlags = CASC_LOCALE_NONE;
|
||||
pFindData->dwFileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE);
|
||||
|
||||
// Mark the entry as already-found
|
||||
pSearch->BitArray[ByteIndex] |= BitMask;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go to the next encoding entry
|
||||
pSearch->IndexLevel1++;
|
||||
}
|
||||
|
||||
// Nameless search ended
|
||||
@@ -211,10 +206,6 @@ static bool DoStorageSearch_Hash(TCascSearch * pSearch, PCASC_FIND_DATA pFindDat
|
||||
|
||||
static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
|
||||
{
|
||||
// Are we searching using the MNDX ?
|
||||
if(pSearch->hs->pMndxInfo != NULL)
|
||||
return DoStorageSearch_MNDX(pSearch, pFindData);
|
||||
|
||||
// State 0: No search done yet
|
||||
if(pSearch->dwState == 0)
|
||||
{
|
||||
@@ -223,30 +214,25 @@ static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
|
||||
pSearch->pCache = ListFile_OpenExternal(pSearch->szListFile);
|
||||
|
||||
// Move the search phase to the listfile searching
|
||||
pSearch->RootIndex = 0;
|
||||
pSearch->IndexLevel1 = 0;
|
||||
pSearch->dwState++;
|
||||
|
||||
// If either file stream or listfile cache are invalid,
|
||||
// move to the next phase
|
||||
if(pSearch->pCache == NULL)
|
||||
pSearch->dwState++;
|
||||
}
|
||||
|
||||
// State 1: Searching the list file
|
||||
if(pSearch->dwState == 1)
|
||||
{
|
||||
if(DoStorageSearch_ListFile(pSearch, pFindData))
|
||||
if(DoStorageSearch_RootFile(pSearch, pFindData))
|
||||
return true;
|
||||
|
||||
// Move to the nameless search state
|
||||
assert(pSearch->RootIndex == 0);
|
||||
pSearch->IndexLevel1 = 0;
|
||||
pSearch->dwState++;
|
||||
}
|
||||
|
||||
// State 2: Searching the remaining entries
|
||||
if(pSearch->dwState == 2)
|
||||
{
|
||||
if(DoStorageSearch_Hash(pSearch, pFindData))
|
||||
if(DoStorageSearch_EncodingKey(pSearch, pFindData))
|
||||
return true;
|
||||
|
||||
// Move to the final search state
|
||||
@@ -275,19 +261,12 @@ HANDLE WINAPI CascFindFirstFile(
|
||||
if(szMask == NULL || pFindData == NULL)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Allocate the structure for archive search
|
||||
// Init the search structure and search handle
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Clear the entire search structure
|
||||
memset(pFindData, 0, sizeof(CASC_FIND_DATA));
|
||||
|
||||
// We must have listfile for non-MNDX storages
|
||||
if(hs->pMndxInfo == NULL && szListFile == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Allocate the search handle
|
||||
pSearch = AllocateSearchHandle(hs, szListFile, szMask);
|
||||
if(pSearch == NULL)
|
||||
|
||||
29
dep/CascLib/src/CascLib.def
Normal file
29
dep/CascLib/src/CascLib.def
Normal file
@@ -0,0 +1,29 @@
|
||||
;
|
||||
; Export file for Windows
|
||||
; Copyright (c) 2015 Ladislav Zezula
|
||||
; ladik@zezula.net
|
||||
;
|
||||
|
||||
LIBRARY CascLib.dll
|
||||
|
||||
EXPORTS
|
||||
|
||||
CascOpenStorage
|
||||
CascGetStorageInfo
|
||||
CascCloseStorage
|
||||
|
||||
CascOpenFileByIndexKey
|
||||
CascOpenFileByEncodingKey
|
||||
CascOpenFile
|
||||
CascGetFileSize
|
||||
CascSetFilePointer
|
||||
CascReadFile
|
||||
CascCloseFile
|
||||
|
||||
CascFindFirstFile
|
||||
CascFindNextFile
|
||||
CascFindClose
|
||||
|
||||
GetLastError=Kernel32.GetLastError
|
||||
SetLastError=Kernel32.SetLastError
|
||||
|
||||
@@ -39,7 +39,7 @@ extern "C" {
|
||||
#define CASC_STOR_XXXXX 0x00000001 // Not used
|
||||
|
||||
// Values for CascOpenFile
|
||||
#define CASC_FILE_XXXXX 0x00000001 // Not used
|
||||
#define CASC_OPEN_BY_ENCODING_KEY 0x00000001 // The name is just the encoding key; skip ROOT file processing
|
||||
|
||||
// Flags for file stream
|
||||
#define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file
|
||||
@@ -103,7 +103,7 @@ extern "C" {
|
||||
|
||||
#ifndef MD5_HASH_SIZE
|
||||
#define MD5_HASH_SIZE 0x10
|
||||
#define MD5_STRING_SIZE 0x21
|
||||
#define MD5_STRING_SIZE 0x20
|
||||
#endif
|
||||
|
||||
#ifndef SHA1_DIGEST_SIZE
|
||||
@@ -146,9 +146,7 @@ typedef struct _CASC_FIND_DATA
|
||||
{
|
||||
char szFileName[MAX_PATH]; // Full name of the found file
|
||||
char * szPlainName; // Plain name of the found file
|
||||
ULONGLONG FileNameHash; // File name hash
|
||||
BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key
|
||||
DWORD dwPackageIndex; // File package index (HOTS only)
|
||||
DWORD dwLocaleFlags; // Locale flags (WoW only)
|
||||
DWORD dwFileSize; // Size of the file
|
||||
|
||||
@@ -184,6 +182,16 @@ HANDLE WINAPI CascFindFirstFile(HANDLE hStorage, const char * szMask, PCASC_FIND
|
||||
bool WINAPI CascFindNextFile(HANDLE hFind, PCASC_FIND_DATA pFindData);
|
||||
bool WINAPI CascFindClose(HANDLE hFind);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GetLastError/SetLastError support for non-Windows platform
|
||||
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
|
||||
int GetLastError();
|
||||
void SetLastError(int nError);
|
||||
|
||||
#endif // PLATFORM_WINDOWS
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
class TFileNameDatabase;
|
||||
|
||||
#define CASC_MAX_MAR_FILES 3 // Maximum of 3 MAR files are supported
|
||||
#define CASC_MNDX_SIGNATURE 0x58444E4D // 'MNDX'
|
||||
|
||||
#define CASC_MAX_ENTRIES(type) (0xFFFFFFFF / sizeof(type))
|
||||
|
||||
#define CASC_SEARCH_INITIALIZING 0
|
||||
@@ -353,13 +356,4 @@ inline bool IS_SINGLE_CHAR_MATCH(TGenericArray & Table, DWORD ItemIndex)
|
||||
return ((Table.NameFragArray[ItemIndex].FragOffs & 0xFFFFFF00) == 0xFFFFFF00);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CASC functions related to MNDX
|
||||
|
||||
int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName);
|
||||
int SearchMndxInfo(PCASC_MNDX_INFO pMndxInfo, const char * szFileName, DWORD dwPackage, PCASC_ROOT_ENTRY_MNDX * ppFoundInfo);
|
||||
bool DoStorageSearch_MNDX(TCascSearch * pSearch, PCASC_FIND_DATA pFindData);
|
||||
void FreeMndxInfo(PCASC_MNDX_INFO pMndxInfo);
|
||||
|
||||
#endif // __CASC_MNDX_ROOT__
|
||||
@@ -11,10 +11,6 @@
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
#include "CascMndxRoot.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
@@ -31,79 +27,19 @@ PCASC_INDEX_ENTRY FindIndexEntry(TCascStorage * hs, PQUERY_KEY pIndexKey)
|
||||
PCASC_INDEX_ENTRY pIndexEntry = NULL;
|
||||
|
||||
if(hs->pIndexEntryMap != NULL)
|
||||
pIndexEntry = (PCASC_INDEX_ENTRY)Map_FindObject(hs->pIndexEntryMap, pIndexKey->pbData);
|
||||
pIndexEntry = (PCASC_INDEX_ENTRY)Map_FindObject(hs->pIndexEntryMap, pIndexKey->pbData, NULL);
|
||||
|
||||
return pIndexEntry;
|
||||
}
|
||||
|
||||
PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, size_t * PtrIndex)
|
||||
PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, PDWORD PtrIndex)
|
||||
{
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry;
|
||||
size_t StartEntry = 0;
|
||||
size_t MidlEntry;
|
||||
size_t EndEntry = hs->nEncodingEntries;
|
||||
int nResult;
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry = NULL;
|
||||
|
||||
// Perform binary search
|
||||
while(StartEntry < EndEntry)
|
||||
{
|
||||
// Calculate the middle of the interval
|
||||
MidlEntry = StartEntry + ((EndEntry - StartEntry) / 2);
|
||||
pEncodingEntry = hs->ppEncodingEntries[MidlEntry];
|
||||
if(hs->pEncodingMap != NULL)
|
||||
pEncodingEntry = (PCASC_ENCODING_ENTRY)Map_FindObject(hs->pEncodingMap, pEncodingKey->pbData, PtrIndex);
|
||||
|
||||
// Did we find it?
|
||||
nResult = memcmp(pEncodingKey->pbData, pEncodingEntry->EncodingKey, MD5_HASH_SIZE);
|
||||
if(nResult == 0)
|
||||
{
|
||||
if(PtrIndex != NULL)
|
||||
PtrIndex[0] = MidlEntry;
|
||||
return pEncodingEntry;
|
||||
}
|
||||
|
||||
// Move the interval to the left or right
|
||||
(nResult < 0) ? EndEntry = MidlEntry : StartEntry = MidlEntry + 1;
|
||||
}
|
||||
|
||||
// Not found, sorry
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Also used in CascSearchFile
|
||||
PCASC_ROOT_ENTRY FindRootEntry(TCascStorage * hs, const char * szFileName, DWORD * PtrTableIndex)
|
||||
{
|
||||
PCASC_ROOT_ENTRY pRootEntry;
|
||||
ULONGLONG FileNameHash;
|
||||
DWORD TableIndex;
|
||||
uint32_t dwHashHigh = 0;
|
||||
uint32_t dwHashLow = 0;
|
||||
|
||||
// Calculate the HASH value of the normalized file name
|
||||
hashlittle2(szFileName, strlen(szFileName), &dwHashHigh, &dwHashLow);
|
||||
FileNameHash = ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow;
|
||||
|
||||
// Get the first table index
|
||||
TableIndex = (DWORD)(FileNameHash & (hs->RootTable.TableSize - 1));
|
||||
assert(hs->RootTable.ItemCount < hs->RootTable.TableSize);
|
||||
|
||||
// Search the proper entry
|
||||
for(;;)
|
||||
{
|
||||
// Does the has match?
|
||||
pRootEntry = hs->RootTable.TablePtr + TableIndex;
|
||||
if(pRootEntry->FileNameHash == FileNameHash)
|
||||
{
|
||||
if(PtrTableIndex != NULL)
|
||||
PtrTableIndex[0] = TableIndex;
|
||||
return pRootEntry;
|
||||
}
|
||||
|
||||
// If the entry is free, the file is not there
|
||||
if(pRootEntry->FileNameHash == 0 && pRootEntry->SumValue == 0)
|
||||
return NULL;
|
||||
|
||||
// Move to the next entry
|
||||
TableIndex = (DWORD)((TableIndex + 1) & (hs->RootTable.TableSize - 1));
|
||||
}
|
||||
return pEncodingEntry;
|
||||
}
|
||||
|
||||
static TCascFile * CreateFileHandle(TCascStorage * hs, PCASC_INDEX_ENTRY pIndexEntry)
|
||||
@@ -187,7 +123,7 @@ static bool OpenFileByEncodingKey(TCascStorage * hs, PQUERY_KEY pEncodingKey, DW
|
||||
// Prepare the file index and open the file by index
|
||||
// Note: We don't know what to do if there is more than just one index key
|
||||
// We always take the first file present. Is that correct?
|
||||
IndexKey.pbData = pEncodingEntry->EncodingKey + MD5_HASH_SIZE;
|
||||
IndexKey.pbData = GET_INDEX_KEY(pEncodingEntry);
|
||||
IndexKey.cbData = MD5_HASH_SIZE;
|
||||
if(OpenFileByIndexKey(hs, &IndexKey, dwFlags, ppCascFile))
|
||||
{
|
||||
@@ -259,13 +195,10 @@ bool WINAPI CascOpenFileByEncodingKey(HANDLE hStorage, PQUERY_KEY pEncodingKey,
|
||||
|
||||
bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocale, DWORD dwFlags, HANDLE * phFile)
|
||||
{
|
||||
PCASC_ROOT_ENTRY_MNDX pRootEntryMndx = NULL;
|
||||
PCASC_ROOT_ENTRY pRootEntry;
|
||||
PCASC_PACKAGE pPackage;
|
||||
TCascStorage * hs;
|
||||
QUERY_KEY EncodingKey;
|
||||
char * szStrippedName;
|
||||
char szFileName2[MAX_PATH+1];
|
||||
LPBYTE pbEncodingKey;
|
||||
BYTE KeyBuffer[MD5_HASH_SIZE];
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
CASCLIB_UNUSED(dwLocale);
|
||||
@@ -285,55 +218,37 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocal
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the storage has a MNDX root directory, use it to search the entry
|
||||
if(hs->pMndxInfo != NULL)
|
||||
// If the user is opening the file via encoding key, skip the ROOT file processing
|
||||
if((dwFlags & CASC_OPEN_BY_ENCODING_KEY) == 0)
|
||||
{
|
||||
// Convert the file name to lowercase + slashes
|
||||
NormalizeFileName_LowerSlash(szFileName2, szFileName, MAX_PATH);
|
||||
|
||||
// Find the package number
|
||||
pPackage = FindMndxPackage(hs, szFileName2);
|
||||
if(pPackage != NULL)
|
||||
// Let the root directory provider get us the encoding key
|
||||
pbEncodingKey = RootHandler_GetKey(hs->pRootHandler, szFileName);
|
||||
if(pbEncodingKey == NULL)
|
||||
{
|
||||
// Cut the package name off the full path
|
||||
szStrippedName = szFileName2 + pPackage->nLength;
|
||||
while(szStrippedName[0] == '/')
|
||||
szStrippedName++;
|
||||
SetLastError(ERROR_FILE_NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
|
||||
nError = SearchMndxInfo(hs->pMndxInfo, szStrippedName, (DWORD)(pPackage - hs->pPackages->Packages), &pRootEntryMndx);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Prepare the encoding key
|
||||
EncodingKey.pbData = pRootEntryMndx->EncodingKey;
|
||||
EncodingKey.cbData = MD5_HASH_SIZE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
// Setup the encoding key
|
||||
EncodingKey.pbData = pbEncodingKey;
|
||||
EncodingKey.cbData = MD5_HASH_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert the file name to lowercase + slashes
|
||||
NormalizeFileName_UpperBkSlash(szFileName2, szFileName, MAX_PATH);
|
||||
// Check the length of the file name
|
||||
if(strlen(szFileName) < MD5_STRING_SIZE)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the root directory for that hash
|
||||
pRootEntry = FindRootEntry(hs, szFileName2, NULL);
|
||||
if(pRootEntry != NULL)
|
||||
{
|
||||
// Prepare the root key
|
||||
EncodingKey.pbData = (LPBYTE)pRootEntry->EncodingKey;
|
||||
EncodingKey.cbData = MD5_HASH_SIZE;
|
||||
nError = ERROR_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
// Convert the file name to binary blob
|
||||
EncodingKey.pbData = KeyBuffer;
|
||||
EncodingKey.cbData = MD5_HASH_SIZE;
|
||||
nError = ConvertStringToBinary(szFileName, MD5_STRING_SIZE, KeyBuffer);
|
||||
}
|
||||
|
||||
// Use the root key to find the file in the encoding table entry
|
||||
// Use the encoding key to find the file in the encoding table entry
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(!OpenFileByEncodingKey(hs, &EncodingKey, dwFlags, (TCascFile **)phFile))
|
||||
@@ -344,10 +259,10 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocal
|
||||
}
|
||||
|
||||
#ifdef CASCLIB_TEST
|
||||
if(phFile[0] != NULL && pRootEntryMndx != NULL)
|
||||
{
|
||||
((TCascFile *)(phFile[0]))->FileSize_RootEntry = pRootEntryMndx->FileSize;
|
||||
}
|
||||
// if(phFile[0] != NULL && pRootEntryMndx != NULL)
|
||||
// {
|
||||
// ((TCascFile *)(phFile[0]))->FileSize_RootEntry = pRootEntryMndx->FileSize;
|
||||
// }
|
||||
#endif
|
||||
|
||||
if(nError != ERROR_SUCCESS)
|
||||
|
||||
@@ -13,19 +13,12 @@
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
#include "CascMndxRoot.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Dumping options
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define CASC_DUMP_ROOT_FILE 2 // The root file will be dumped (level 2)
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
|
||||
#define CASC_INITIAL_ROOT_TABLE_SIZE 0x00100000
|
||||
// Size of one segment in the ENCODING table
|
||||
// The segment is filled by entries of type
|
||||
#define CASC_ENCODING_SEGMENT_SIZE 0x1000
|
||||
|
||||
typedef struct _BLOCK_SIZE_AND_HASH
|
||||
@@ -67,25 +60,10 @@ typedef struct _FILE_INDEX_HEADER_V2
|
||||
|
||||
} FILE_INDEX_HEADER_V2, *PFILE_INDEX_HEADER_V2;
|
||||
|
||||
typedef struct _FILE_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
|
||||
|
||||
} FILE_ENCODING_HEADER, *PFILE_ENCODING_HEADER;
|
||||
|
||||
typedef struct _FILE_ENCODING_SEGMENT
|
||||
{
|
||||
BYTE FirstEncodingKey[MD5_HASH_SIZE]; // The first encoding key in the segment
|
||||
BYTE SegmentHash[MD5_HASH_SIZE]; // MD5 hash of the entire segment
|
||||
BYTE FirstEncodingKey[MD5_HASH_SIZE]; // The first encoding key in the segment
|
||||
BYTE SegmentHash[MD5_HASH_SIZE]; // MD5 hash of the entire segment
|
||||
|
||||
} FILE_ENCODING_SEGMENT, *PFILE_ENCODING_SEGMENT;
|
||||
|
||||
@@ -143,28 +121,6 @@ static bool IsIndexFileName_V2(const TCHAR * szFileName)
|
||||
_tcsicmp(szFileName + 0x0A, _T(".idx")) == 0);
|
||||
}
|
||||
|
||||
static void QUERY_KEY_Free(PQUERY_KEY pBlob)
|
||||
{
|
||||
if(pBlob != NULL)
|
||||
{
|
||||
if(pBlob->pbData != NULL)
|
||||
CASC_FREE(pBlob->pbData);
|
||||
|
||||
pBlob->pbData = NULL;
|
||||
pBlob->cbData = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void QUERY_KEY_FreeArray(PQUERY_KEY pBlobArray)
|
||||
{
|
||||
// Free the buffer in the first blob
|
||||
// (will also free all buffers in the array)
|
||||
QUERY_KEY_Free(pBlobArray);
|
||||
|
||||
// Free the array itself
|
||||
CASC_FREE(pBlobArray);
|
||||
}
|
||||
|
||||
static bool IsCascIndexHeader_V1(LPBYTE pbFileData, DWORD cbFileData)
|
||||
{
|
||||
PFILE_INDEX_HEADER_V1 pIndexHeader = (PFILE_INDEX_HEADER_V1)pbFileData;
|
||||
@@ -206,52 +162,99 @@ static bool IsCascIndexHeader_V2(LPBYTE pbFileData, DWORD cbFileData)
|
||||
return (HashHigh == pSizeAndHash->dwBlockHash);
|
||||
}
|
||||
|
||||
LPBYTE VerifyLocaleBlock(PROOT_BLOCK_INFO pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd)
|
||||
static bool CutLastPathPart(TCHAR * szWorkPath)
|
||||
{
|
||||
// Validate the file locale block
|
||||
pBlockInfo->pLocaleBlockHdr = (PFILE_LOCALE_BLOCK)pbFilePointer;
|
||||
pbFilePointer = (LPBYTE)(pBlockInfo->pLocaleBlockHdr + 1);
|
||||
if(pbFilePointer > pbFileEnd)
|
||||
return NULL;
|
||||
size_t nLength = _tcslen(szWorkPath);
|
||||
|
||||
// Validate the array of 32-bit integers
|
||||
pBlockInfo->pInt32Array = (PDWORD)pbFilePointer;
|
||||
pbFilePointer = (LPBYTE)(pBlockInfo->pInt32Array + pBlockInfo->pLocaleBlockHdr->NumberOfFiles);
|
||||
if(pbFilePointer > pbFileEnd)
|
||||
return NULL;
|
||||
for(nLength = _tcslen(szWorkPath); nLength > 0; nLength--)
|
||||
{
|
||||
if(szWorkPath[nLength] == '\\' || szWorkPath[nLength] == '/')
|
||||
{
|
||||
szWorkPath[nLength] = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the array of root entries
|
||||
pBlockInfo->pRootEntries = (PFILE_ROOT_ENTRY)pbFilePointer;
|
||||
pbFilePointer = (LPBYTE)(pBlockInfo->pRootEntries + pBlockInfo->pLocaleBlockHdr->NumberOfFiles);
|
||||
if(pbFilePointer > pbFileEnd)
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return the position of the next block
|
||||
return pbFilePointer;
|
||||
static int InsertExtraFile(
|
||||
TCascStorage * hs,
|
||||
const char * szFileName,
|
||||
PQUERY_KEY pQueryKey)
|
||||
{
|
||||
// If the given key is not encoding key (aka, it's an index key),
|
||||
// we need to create a fake encoding entry
|
||||
if(pQueryKey->cbData == MD5_HASH_SIZE * 2)
|
||||
{
|
||||
PCASC_ENCODING_ENTRY pNewEntry;
|
||||
PCASC_INDEX_ENTRY pIndexEntry;
|
||||
QUERY_KEY IndexKey;
|
||||
|
||||
// Find the entry in the index table in order to get the file size
|
||||
IndexKey.pbData = pQueryKey->pbData + MD5_HASH_SIZE;
|
||||
IndexKey.cbData = MD5_HASH_SIZE;
|
||||
pIndexEntry = FindIndexEntry(hs, &IndexKey);
|
||||
if(pIndexEntry == NULL)
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
|
||||
// Create a fake entry in the encoding map
|
||||
pNewEntry = (PCASC_ENCODING_ENTRY)Array_Insert(&hs->ExtraEntries, NULL, 1);
|
||||
if(pNewEntry == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Fill the encoding entry
|
||||
pNewEntry->KeyCount = 1;
|
||||
pNewEntry->FileSizeBE[0] = pIndexEntry->FileSizeLE[3];
|
||||
pNewEntry->FileSizeBE[1] = pIndexEntry->FileSizeLE[2];
|
||||
pNewEntry->FileSizeBE[2] = pIndexEntry->FileSizeLE[1];
|
||||
pNewEntry->FileSizeBE[3] = pIndexEntry->FileSizeLE[0];
|
||||
memcpy(pNewEntry->EncodingKey, pQueryKey->pbData, MD5_HASH_SIZE);
|
||||
memcpy(pNewEntry + 1, pQueryKey->pbData + MD5_HASH_SIZE, MD5_HASH_SIZE);
|
||||
|
||||
// Insert the entry to the map of encoding keys
|
||||
Map_InsertObject(hs->pEncodingMap, pNewEntry, pNewEntry->EncodingKey);
|
||||
}
|
||||
|
||||
// Now we need to insert the entry to the root handler in order
|
||||
// to be able to translate file name to encoding key
|
||||
return RootHandler_Insert(hs->pRootHandler, szFileName, pQueryKey->pbData);
|
||||
}
|
||||
|
||||
static int InitializeCascDirectories(TCascStorage * hs, const TCHAR * szDataPath)
|
||||
{
|
||||
TCHAR * szLastPathPart;
|
||||
TCHAR * szWorkPath;
|
||||
int nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Save the game data directory
|
||||
hs->szDataPath = NewStr(szDataPath, 0);
|
||||
|
||||
// Save the root game directory
|
||||
hs->szRootPath = NewStr(szDataPath, 0);
|
||||
|
||||
// Find the last part
|
||||
szLastPathPart = hs->szRootPath;
|
||||
for(size_t i = 0; hs->szRootPath[i] != 0; i++)
|
||||
// Find the root directory of the storage. The root directory
|
||||
// is the one where ".build.info" is.
|
||||
szWorkPath = CascNewStr(szDataPath, 0);
|
||||
if(szWorkPath != NULL)
|
||||
{
|
||||
if(hs->szRootPath[i] == '\\' || hs->szRootPath[i] == '/')
|
||||
szLastPathPart = hs->szRootPath + i;
|
||||
}
|
||||
|
||||
// Cut the last part
|
||||
if(szLastPathPart != NULL)
|
||||
szLastPathPart[0] = 0;
|
||||
return (hs->szRootPath && hs->szDataPath) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
|
||||
// Get the length and go up until we find the ".build.info" or ".build.db"
|
||||
for(;;)
|
||||
{
|
||||
// Is this a game directory?
|
||||
nError = CheckGameDirectory(hs, szWorkPath);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = ERROR_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
// Cut one path part
|
||||
if(!CutLastPathPart(szWorkPath))
|
||||
{
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Free the work path buffer
|
||||
CASC_FREE(szWorkPath);
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
static bool IndexDirectory_OnFileFound(
|
||||
@@ -566,7 +569,7 @@ static int CreateArrayOfIndexEntries(TCascStorage * hs)
|
||||
// 9e dc a7 8f e2 09 ad d8 b7 (encoding file)
|
||||
// f3 5e bb fb d1 2b 3f ef 8b
|
||||
// c8 69 9f 18 a2 5e df 7e 52
|
||||
Map_InsertObject(pMap, pIndexEntry->IndexKey);
|
||||
Map_InsertObject(pMap, pIndexEntry, pIndexEntry->IndexKey);
|
||||
|
||||
// Move to the next entry
|
||||
pIndexEntry++;
|
||||
@@ -581,28 +584,28 @@ static int CreateArrayOfIndexEntries(TCascStorage * hs)
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int CreateMapOfEncodingKeys(TCascStorage * hs, PFILE_ENCODING_SEGMENT pEncodingSegment, DWORD dwNumberOfSegments)
|
||||
static int CreateMapOfEncodingKeys(TCascStorage * hs, PFILE_ENCODING_SEGMENT pEncodingSegment, DWORD dwNumSegments)
|
||||
{
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry;
|
||||
size_t nMaxEntries;
|
||||
size_t nEntries = 0;
|
||||
DWORD dwMaxEntries;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Sanity check
|
||||
assert(hs->ppEncodingEntries == NULL);
|
||||
assert(hs->pIndexEntryMap != NULL);
|
||||
assert(hs->pEncodingMap == NULL);
|
||||
|
||||
// Calculate the largest eventual number of encodign entries
|
||||
nMaxEntries = (dwNumberOfSegments * CASC_ENCODING_SEGMENT_SIZE) / (sizeof(CASC_ENCODING_ENTRY) + MD5_HASH_SIZE);
|
||||
// Calculate the largest eventual number of encoding entries
|
||||
// Add space for extra entries
|
||||
dwMaxEntries = (dwNumSegments * CASC_ENCODING_SEGMENT_SIZE) / (sizeof(CASC_ENCODING_ENTRY) + MD5_HASH_SIZE);
|
||||
|
||||
// Allocate the array of pointers to encoding entries
|
||||
hs->ppEncodingEntries = CASC_ALLOC(PCASC_ENCODING_ENTRY, nMaxEntries);
|
||||
if(hs->ppEncodingEntries != NULL)
|
||||
// Create the map of the encoding entries
|
||||
hs->pEncodingMap = Map_Create(dwMaxEntries + CASC_EXTRA_FILES, MD5_HASH_SIZE, FIELD_OFFSET(CASC_ENCODING_ENTRY, EncodingKey));
|
||||
if(hs->pEncodingMap != NULL)
|
||||
{
|
||||
LPBYTE pbStartOfSegment = (LPBYTE)(pEncodingSegment + dwNumberOfSegments);
|
||||
LPBYTE pbStartOfSegment = (LPBYTE)(pEncodingSegment + dwNumSegments);
|
||||
|
||||
// Parse all segments
|
||||
for(DWORD i = 0; i < dwNumberOfSegments; i++)
|
||||
for(DWORD i = 0; i < dwNumSegments; i++)
|
||||
{
|
||||
LPBYTE pbEncodingEntry = pbStartOfSegment;
|
||||
LPBYTE pbEndOfSegment = pbStartOfSegment + CASC_ENCODING_SEGMENT_SIZE - sizeof(CASC_ENCODING_ENTRY) - MD5_HASH_SIZE;
|
||||
@@ -616,7 +619,7 @@ static int CreateMapOfEncodingKeys(TCascStorage * hs, PFILE_ENCODING_SEGMENT pEn
|
||||
break;
|
||||
|
||||
// Insert the pointer the array
|
||||
hs->ppEncodingEntries[nEntries++] = pEncodingEntry;
|
||||
Map_InsertObject(hs->pEncodingMap, pEncodingEntry, pEncodingEntry->EncodingKey);
|
||||
|
||||
// Move to the next encoding entry
|
||||
pbEncodingEntry += sizeof(CASC_ENCODING_ENTRY) + (pEncodingEntry->KeyCount * MD5_HASH_SIZE);
|
||||
@@ -625,9 +628,6 @@ static int CreateMapOfEncodingKeys(TCascStorage * hs, PFILE_ENCODING_SEGMENT pEn
|
||||
// Move to the next segment
|
||||
pbStartOfSegment += CASC_ENCODING_SEGMENT_SIZE;
|
||||
}
|
||||
|
||||
// Remember the total number of encoding entries
|
||||
hs->nEncodingEntries = nEntries;
|
||||
}
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
@@ -684,8 +684,16 @@ static LPBYTE LoadEncodingFileToMemory(HANDLE hFile, DWORD * pcbEncodingFile)
|
||||
CascReadFile(hFile, &EncodingHeader, sizeof(CASC_ENCODING_HEADER), &dwBytesRead);
|
||||
if(dwBytesRead == sizeof(CASC_ENCODING_HEADER))
|
||||
{
|
||||
dwNumSegments = ConvertBytesToInteger_4(EncodingHeader.NumSegments);
|
||||
dwSegmentPos = ConvertBytesToInteger_4(EncodingHeader.SegmentsPos);
|
||||
// Check the version and sizes
|
||||
if(EncodingHeader.Version != 0x01 || EncodingHeader.ChecksumSizeA != MD5_HASH_SIZE || EncodingHeader.ChecksumSizeB != MD5_HASH_SIZE)
|
||||
{
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the number of segments
|
||||
dwNumSegments = ConvertBytesToInteger_4(EncodingHeader.Entries_TableA);
|
||||
dwSegmentPos = ConvertBytesToInteger_4(EncodingHeader.Size_StringTable1);
|
||||
if(EncodingHeader.Magic[0] == 'E' && EncodingHeader.Magic[1] == 'N' && dwSegmentPos != 0 && dwNumSegments != 0)
|
||||
nError = ERROR_SUCCESS;
|
||||
}
|
||||
@@ -721,36 +729,16 @@ static LPBYTE LoadEncodingFileToMemory(HANDLE hFile, DWORD * pcbEncodingFile)
|
||||
|
||||
static LPBYTE LoadRootFileToMemory(HANDLE hFile, DWORD * pcbRootFile)
|
||||
{
|
||||
TCascFile * hf;
|
||||
LPBYTE pbRootFile = NULL;
|
||||
DWORD cbRootFile = 0;
|
||||
DWORD dwBytesRead = 0;
|
||||
BYTE StartOfFile[0x10];
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Dummy read the first 16 bytes
|
||||
CascReadFile(hFile, &StartOfFile, sizeof(StartOfFile), &dwBytesRead);
|
||||
if(dwBytesRead != sizeof(StartOfFile))
|
||||
// Retrieve the size of the ROOT file
|
||||
cbRootFile = CascGetFileSize(hFile, NULL);
|
||||
if(cbRootFile == 0)
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
|
||||
// Calculate and allocate space for the entire file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Convert the file handle to pointer to TCascFile
|
||||
hf = IsValidFileHandle(hFile);
|
||||
if(hf != NULL)
|
||||
{
|
||||
// Parse the frames to get the file size
|
||||
for(DWORD i = 0; i < hf->FrameCount; i++)
|
||||
{
|
||||
cbRootFile += hf->pFrames[i].FrameSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the error
|
||||
nError = (cbRootFile != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Allocate space for the entire file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
@@ -762,12 +750,9 @@ static LPBYTE LoadRootFileToMemory(HANDLE hFile, DWORD * pcbRootFile)
|
||||
// If all went OK, we load the entire file to memory
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Copy the header itself
|
||||
memcpy(pbRootFile, StartOfFile, sizeof(StartOfFile));
|
||||
|
||||
// Read the rest of the data
|
||||
CascReadFile(hFile, pbRootFile + sizeof(StartOfFile), cbRootFile - sizeof(StartOfFile), &dwBytesRead);
|
||||
if(dwBytesRead != (cbRootFile - sizeof(StartOfFile)))
|
||||
// Read the entire file to memory
|
||||
CascReadFile(hFile, pbRootFile, cbRootFile, &dwBytesRead);
|
||||
if(dwBytesRead != cbRootFile)
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
@@ -780,17 +765,19 @@ static LPBYTE LoadRootFileToMemory(HANDLE hFile, DWORD * pcbRootFile)
|
||||
static int LoadEncodingFile(TCascStorage * hs)
|
||||
{
|
||||
PFILE_ENCODING_SEGMENT pEncodingSegment;
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry;
|
||||
QUERY_KEY EncodingKey;
|
||||
LPBYTE pbStartOfSegment;
|
||||
LPBYTE pbEncodingFile = NULL;
|
||||
HANDLE hFile = NULL;
|
||||
DWORD cbEncodingFile = 0;
|
||||
DWORD dwNumberOfSegments = 0;
|
||||
DWORD dwNumSegments = 0;
|
||||
DWORD dwSegmentsPos = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Open the encoding file
|
||||
if(!CascOpenFileByIndexKey((HANDLE)hs, &hs->EncodingEKey, 0, &hFile))
|
||||
EncodingKey.pbData = hs->EncodingKey.pbData + MD5_HASH_SIZE;
|
||||
EncodingKey.cbData = MD5_HASH_SIZE;
|
||||
if(!CascOpenFileByIndexKey((HANDLE)hs, &EncodingKey, 0, &hFile))
|
||||
nError = GetLastError();
|
||||
|
||||
// Load the entire ENCODING file to memory
|
||||
@@ -808,20 +795,25 @@ static int LoadEncodingFile(TCascStorage * hs)
|
||||
// Verify all encoding segments
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Save the encoding header
|
||||
hs->pEncodingHeader = (PCASC_ENCODING_HEADER)pbEncodingFile;
|
||||
PCASC_ENCODING_HEADER pEncodingHeader = (PCASC_ENCODING_HEADER)pbEncodingFile;
|
||||
|
||||
// Convert size and offset
|
||||
dwNumberOfSegments = ConvertBytesToInteger_4(hs->pEncodingHeader->NumSegments);
|
||||
dwSegmentsPos = ConvertBytesToInteger_4(hs->pEncodingHeader->SegmentsPos);
|
||||
dwNumSegments = ConvertBytesToInteger_4(pEncodingHeader->Entries_TableA);
|
||||
dwSegmentsPos = ConvertBytesToInteger_4(pEncodingHeader->Size_StringTable1);
|
||||
|
||||
// Store the encoding file to the CASC storage
|
||||
hs->EncodingFile.pbData = pbEncodingFile;
|
||||
hs->EncodingFile.cbData = cbEncodingFile;
|
||||
|
||||
// Allocate the array of encoding segments
|
||||
pEncodingSegment = (PFILE_ENCODING_SEGMENT)(pbEncodingFile + sizeof(CASC_ENCODING_HEADER) + dwSegmentsPos);
|
||||
pbStartOfSegment = (LPBYTE)(pEncodingSegment + dwNumberOfSegments);
|
||||
pbStartOfSegment = (LPBYTE)(pEncodingSegment + dwNumSegments);
|
||||
|
||||
// Go through all encoding segments and verify them
|
||||
for(DWORD i = 0; i < dwNumberOfSegments; i++)
|
||||
for(DWORD i = 0; i < dwNumSegments; i++)
|
||||
{
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry = (PCASC_ENCODING_ENTRY)pbStartOfSegment;
|
||||
|
||||
// Check if there is enough space in the buffer
|
||||
if((pbStartOfSegment + CASC_ENCODING_SEGMENT_SIZE) > (pbEncodingFile + cbEncodingFile))
|
||||
{
|
||||
@@ -837,8 +829,7 @@ static int LoadEncodingFile(TCascStorage * hs)
|
||||
// break;
|
||||
// }
|
||||
|
||||
// Check if the encoding key matches
|
||||
pEncodingEntry = (PCASC_ENCODING_ENTRY)pbStartOfSegment;
|
||||
// Check if the encoding key matches with the expected first value
|
||||
if(memcmp(pEncodingEntry->EncodingKey, pEncodingSegment->FirstEncodingKey, MD5_HASH_SIZE))
|
||||
{
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
@@ -856,241 +847,12 @@ static int LoadEncodingFile(TCascStorage * hs)
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
pEncodingSegment = (PFILE_ENCODING_SEGMENT)(pbEncodingFile + sizeof(CASC_ENCODING_HEADER) + dwSegmentsPos);
|
||||
nError = CreateMapOfEncodingKeys(hs, pEncodingSegment, dwNumberOfSegments);
|
||||
nError = CreateMapOfEncodingKeys(hs, pEncodingSegment, dwNumSegments);
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
typedef struct _CHECK_ROOT_ENTRY_INPUT
|
||||
{
|
||||
ULONGLONG FileNameHash;
|
||||
DWORD SumValue;
|
||||
DWORD EncodingKey[4];
|
||||
|
||||
} CHECK_ROOT_ENTRY_INPUT, *PCHECK_ROOT_ENTRY_INPUT;
|
||||
|
||||
typedef struct _CHECK_ROOT_ENTRY_OUTPUT
|
||||
{
|
||||
DWORD field_0;
|
||||
DWORD field_4;
|
||||
DWORD field_8;
|
||||
bool field_C;
|
||||
|
||||
} CHECK_ROOT_ENTRY_OUTPUT, *PCHECK_ROOT_ENTRY_OUTPUT;
|
||||
|
||||
|
||||
// WoW6: 00413F61
|
||||
static bool EnlargeHashTableIfMoreThan75PercentUsed(PCASC_ROOT_HASH_TABLE pRootTable, DWORD NewItemCount)
|
||||
{
|
||||
// Don't relocate anything, just check
|
||||
assert((double)NewItemCount / (double)pRootTable->TableSize < .75);
|
||||
return true;
|
||||
}
|
||||
|
||||
// WOW6: 00414402
|
||||
// Finds an existing root table entry or a free one
|
||||
PCASC_ROOT_ENTRY CascRootTable_FindFreeEntryWithEnlarge(
|
||||
PCASC_ROOT_HASH_TABLE pRootTable,
|
||||
PCASC_ROOT_ENTRY pNewEntry)
|
||||
{
|
||||
PCASC_ROOT_ENTRY pEntry;
|
||||
DWORD TableIndex;
|
||||
|
||||
// The table size must be a power of two
|
||||
assert((pRootTable->TableSize & (pRootTable->TableSize - 1)) == 0);
|
||||
|
||||
// Make sure that number of occupied items is never bigger
|
||||
// than 75% of the table size
|
||||
if(!EnlargeHashTableIfMoreThan75PercentUsed(pRootTable, pRootTable->ItemCount + 1))
|
||||
return NULL;
|
||||
|
||||
// Get the start index of the table
|
||||
TableIndex = (DWORD)(pNewEntry->FileNameHash) & (pRootTable->TableSize - 1);
|
||||
|
||||
// If that entry is already occupied, move to a next entry
|
||||
for(;;)
|
||||
{
|
||||
// Check that entry if it's free or not
|
||||
pEntry = pRootTable->TablePtr + TableIndex;
|
||||
if(pEntry->SumValue == 0)
|
||||
break;
|
||||
|
||||
// Is the found entry equal to the existing one?
|
||||
if(pEntry->FileNameHash == pNewEntry->FileNameHash)
|
||||
break;
|
||||
|
||||
// Move to the next entry
|
||||
TableIndex = (TableIndex + 1) & (pRootTable->TableSize - 1);
|
||||
}
|
||||
|
||||
// Either return a free entry or an existing one
|
||||
return pEntry;
|
||||
}
|
||||
|
||||
// WOW6: 004145D1
|
||||
static void CascRootTable_InsertTableEntry(
|
||||
PCASC_ROOT_HASH_TABLE pRootTable,
|
||||
PCASC_ROOT_ENTRY pNewEntry)
|
||||
{
|
||||
PCASC_ROOT_ENTRY pEntry;
|
||||
|
||||
// Find an existing entry or an empty one
|
||||
pEntry = CascRootTable_FindFreeEntryWithEnlarge(pRootTable, pNewEntry);
|
||||
assert(pEntry != NULL);
|
||||
|
||||
// If that entry is not used yet, fill it in
|
||||
if(pEntry->FileNameHash == 0)
|
||||
{
|
||||
*pEntry = *pNewEntry;
|
||||
pRootTable->ItemCount++;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadWowRootFileLocales(
|
||||
TCascStorage * hs,
|
||||
LPBYTE pbRootFile,
|
||||
DWORD cbRootFile,
|
||||
DWORD dwLocaleMask,
|
||||
bool bLoadBlocksWithFlags80,
|
||||
BYTE HighestBitValue)
|
||||
{
|
||||
CASC_ROOT_ENTRY NewRootEntry;
|
||||
ROOT_BLOCK_INFO BlockInfo;
|
||||
LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
|
||||
LPBYTE pbFilePointer;
|
||||
|
||||
// Now parse the root file
|
||||
for(pbFilePointer = pbRootFile; pbFilePointer <= pbRootFileEnd; )
|
||||
{
|
||||
// Validate the file locale block
|
||||
pbFilePointer = VerifyLocaleBlock(&BlockInfo, pbFilePointer, pbRootFileEnd);
|
||||
if(pbFilePointer == NULL)
|
||||
break;
|
||||
|
||||
// WoW.exe (build 19116): Entries with flag 0x100 set are skipped
|
||||
if(BlockInfo.pLocaleBlockHdr->Flags & 0x100)
|
||||
continue;
|
||||
|
||||
// WoW.exe (build 19116): Entries with flag 0x80 set are skipped if arg_4 is set to FALSE (which is by default)
|
||||
if(bLoadBlocksWithFlags80 == 0 && (BlockInfo.pLocaleBlockHdr->Flags & 0x80))
|
||||
continue;
|
||||
|
||||
// WoW.exe (build 19116): Entries with (flags >> 0x1F) not equal to arg_8 are skipped
|
||||
if((BYTE)(BlockInfo.pLocaleBlockHdr->Flags >> 0x1F) != HighestBitValue)
|
||||
continue;
|
||||
|
||||
// WoW.exe (build 19116): Locales other than defined mask are skipped too
|
||||
if((BlockInfo.pLocaleBlockHdr->Locales & dwLocaleMask) == 0)
|
||||
continue;
|
||||
|
||||
// Reset the sum value
|
||||
NewRootEntry.SumValue = 0;
|
||||
|
||||
// WoW.exe (build 19116): Blocks with zero files are skipped
|
||||
for(DWORD i = 0; i < BlockInfo.pLocaleBlockHdr->NumberOfFiles; i++)
|
||||
{
|
||||
// (004147A3) Prepare the CASC_ROOT_ENTRY structure
|
||||
NewRootEntry.FileNameHash = BlockInfo.pRootEntries[i].FileNameHash;
|
||||
NewRootEntry.SumValue = NewRootEntry.SumValue + BlockInfo.pInt32Array[i];
|
||||
NewRootEntry.Locales = BlockInfo.pLocaleBlockHdr->Locales;
|
||||
NewRootEntry.EncodingKey[0] = BlockInfo.pRootEntries[i].EncodingKey[0];
|
||||
NewRootEntry.EncodingKey[1] = BlockInfo.pRootEntries[i].EncodingKey[1];
|
||||
NewRootEntry.EncodingKey[2] = BlockInfo.pRootEntries[i].EncodingKey[2];
|
||||
NewRootEntry.EncodingKey[3] = BlockInfo.pRootEntries[i].EncodingKey[3];
|
||||
|
||||
// Insert the root table item to the hash table
|
||||
CascRootTable_InsertTableEntry(&hs->RootTable, &NewRootEntry);
|
||||
NewRootEntry.SumValue++;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// WoW.exe: 004146C7 (BuildManifest::Load)
|
||||
static int LoadWowRootFileWithParams(
|
||||
TCascStorage * hs,
|
||||
LPBYTE pbRootFile,
|
||||
DWORD cbRootFile,
|
||||
DWORD dwLocaleBits,
|
||||
BYTE HighestBitValue)
|
||||
{
|
||||
// Load the locale as-is
|
||||
LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, dwLocaleBits, false, HighestBitValue);
|
||||
|
||||
// If we wanted enGB, we also load enUS for the missing files
|
||||
if(dwLocaleBits == CASC_LOCALE_ENGB)
|
||||
LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENUS, false, HighestBitValue);
|
||||
|
||||
if(dwLocaleBits == CASC_LOCALE_PTPT)
|
||||
LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTBR, false, HighestBitValue);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
// Code from WoW.exe
|
||||
if(dwLocaleBits == CASC_LOCALE_DUAL_LANG)
|
||||
{
|
||||
// Is this english version of WoW?
|
||||
if(arg_4 == CASC_LOCALE_BIT_ENUS)
|
||||
{
|
||||
LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENGB, false, HighestBitValue);
|
||||
LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENUS, false, HighestBitValue);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// Is this portuguese version of WoW?
|
||||
if(arg_4 == CASC_LOCALE_BIT_PTBR)
|
||||
{
|
||||
LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTPT, false, HighestBitValue);
|
||||
LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTBR, false, HighestBitValue);
|
||||
}
|
||||
}
|
||||
|
||||
LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, (1 << arg_4), false, HighestBitValue);
|
||||
*/
|
||||
|
||||
static int LoadWowRootFile(
|
||||
TCascStorage * hs,
|
||||
LPBYTE pbRootFile,
|
||||
DWORD cbRootFile,
|
||||
DWORD dwLocaleMask)
|
||||
{
|
||||
int nError;
|
||||
|
||||
// Dump the root file, if needed
|
||||
#ifdef CASC_DUMP_ROOT_FILE
|
||||
//CascDumpRootFile(hs,
|
||||
// pbRootFile,
|
||||
// cbRootFile,
|
||||
// "\\casc_root_%build%.txt",
|
||||
// _T("\\Ladik\\Appdir\\CascLib\\listfile\\listfile-wow6.txt"),
|
||||
// CASC_DUMP_ROOT_FILE);
|
||||
#endif
|
||||
|
||||
// Allocate root table entries. Note that the initial size
|
||||
// of the root table is set to 0x00200000 by World of Warcraft 6.x
|
||||
hs->RootTable.TablePtr = CASC_ALLOC(CASC_ROOT_ENTRY, CASC_INITIAL_ROOT_TABLE_SIZE);
|
||||
hs->RootTable.TableSize = CASC_INITIAL_ROOT_TABLE_SIZE;
|
||||
if(hs->RootTable.TablePtr == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Clear the entire table
|
||||
memset(hs->RootTable.TablePtr, 0, CASC_INITIAL_ROOT_TABLE_SIZE * sizeof(CASC_ROOT_ENTRY));
|
||||
|
||||
// Load the root file
|
||||
nError = LoadWowRootFileWithParams(hs, pbRootFile, cbRootFile, dwLocaleMask, 0);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
|
||||
nError = LoadWowRootFileWithParams(hs, pbRootFile, cbRootFile, dwLocaleMask, 1);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int LoadRootFile(TCascStorage * hs, DWORD dwLocaleMask)
|
||||
{
|
||||
PDWORD FileSignature;
|
||||
@@ -1100,49 +862,70 @@ static int LoadRootFile(TCascStorage * hs, DWORD dwLocaleMask)
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Sanity checks
|
||||
assert(hs->RootTable.TablePtr == NULL);
|
||||
assert(hs->RootTable.ItemCount == 0);
|
||||
assert(hs->ppEncodingEntries != NULL);
|
||||
assert(hs->pEncodingMap != NULL);
|
||||
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;
|
||||
|
||||
// The root file is either MNDX file (Heroes of the Storm)
|
||||
// or a file containing an array of root entries (World of Warcraft 6.0+)
|
||||
// Note: The "root" key file's MD5 hash is equal to its name
|
||||
// in the configuration
|
||||
// Load the entire ROOT file to memory
|
||||
if(!CascOpenFileByEncodingKey((HANDLE)hs, &hs->RootKey, 0, &hFile))
|
||||
nError = GetLastError();
|
||||
|
||||
// Load the entire ROOT file to memory
|
||||
// Load the entire file to memory
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Load the necessary part of the ENCODING file to memory
|
||||
pbRootFile = LoadRootFileToMemory(hFile, &cbRootFile);
|
||||
if(pbRootFile == NULL || cbRootFile <= sizeof(PFILE_LOCALE_BLOCK))
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
|
||||
// Close the encoding file
|
||||
CascCloseFile(hFile);
|
||||
}
|
||||
|
||||
// Check if the file is a MNDX file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
// Check if the version of the ROOT file
|
||||
if(nError == ERROR_SUCCESS && pbRootFile != NULL)
|
||||
{
|
||||
FileSignature = (PDWORD)pbRootFile;
|
||||
if(FileSignature[0] == CASC_MNDX_SIGNATURE)
|
||||
switch(FileSignature[0])
|
||||
{
|
||||
nError = LoadMndxRootFile(hs, pbRootFile, cbRootFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
// WOW6: 00415000
|
||||
nError = LoadWowRootFile(hs, pbRootFile, cbRootFile, dwLocaleMask);
|
||||
case CASC_MNDX_ROOT_SIGNATURE:
|
||||
nError = RootHandler_CreateMNDX(hs, pbRootFile, cbRootFile);
|
||||
break;
|
||||
|
||||
case CASC_DIABLO3_ROOT_SIGNATURE:
|
||||
nError = RootHandler_CreateDiablo3(hs, pbRootFile, cbRootFile);
|
||||
break;
|
||||
|
||||
case CASC_OVERWATCH_ROOT_SIGNATURE:
|
||||
nError = RootHandler_CreateOverwatch(hs, pbRootFile, cbRootFile);
|
||||
break;
|
||||
|
||||
default:
|
||||
nError = RootHandler_CreateWoW6(hs, pbRootFile, cbRootFile, dwLocaleMask);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert entry for the
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
InsertExtraFile(hs, "ENCODING", &hs->EncodingKey);
|
||||
InsertExtraFile(hs, "ROOT", &hs->RootKey);
|
||||
InsertExtraFile(hs, "DOWNLOAD", &hs->DownloadKey);
|
||||
InsertExtraFile(hs, "INSTALL", &hs->InstallKey);
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
//RootFile_Dump(hs,
|
||||
// pbRootFile,
|
||||
// cbRootFile,
|
||||
// _T("\\casc_root_%build%.txt"),
|
||||
// _T("\\Ladik\\Appdir\\CascLib\\listfile\\listfile-wow6.txt"),
|
||||
// DUMP_LEVEL_INDEX_ENTRIES);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Free the root file
|
||||
CASC_FREE(pbRootFile);
|
||||
return nError;
|
||||
@@ -1154,19 +937,19 @@ static TCascStorage * FreeCascStorage(TCascStorage * hs)
|
||||
|
||||
if(hs != NULL)
|
||||
{
|
||||
// Free the MNDX info
|
||||
if(hs->pPackages != NULL)
|
||||
CASC_FREE(hs->pPackages);
|
||||
if(hs->pMndxInfo != NULL)
|
||||
FreeMndxInfo(hs->pMndxInfo);
|
||||
// Free the root handler
|
||||
if(hs->pRootHandler != NULL)
|
||||
RootHandler_Close(hs->pRootHandler);
|
||||
hs->pRootHandler = NULL;
|
||||
|
||||
// Free the extra encoding entries
|
||||
Array_Free(&hs->ExtraEntries);
|
||||
|
||||
// Free the pointers to file entries
|
||||
if(hs->RootTable.TablePtr != NULL)
|
||||
CASC_FREE(hs->RootTable.TablePtr);
|
||||
if(hs->ppEncodingEntries != NULL)
|
||||
CASC_FREE(hs->ppEncodingEntries);
|
||||
if(hs->pEncodingHeader != NULL)
|
||||
CASC_FREE(hs->pEncodingHeader);
|
||||
if(hs->pEncodingMap != NULL)
|
||||
Map_Free(hs->pEncodingMap);
|
||||
if(hs->EncodingFile.pbData != NULL)
|
||||
CASC_FREE(hs->EncodingFile.pbData);
|
||||
if(hs->pIndexEntryMap != NULL)
|
||||
Map_Free(hs->pIndexEntryMap);
|
||||
|
||||
@@ -1195,25 +978,24 @@ static TCascStorage * FreeCascStorage(TCascStorage * hs)
|
||||
CASC_FREE(hs->szRootPath);
|
||||
if(hs->szDataPath != NULL)
|
||||
CASC_FREE(hs->szDataPath);
|
||||
if(hs->szBuildFile != NULL)
|
||||
CASC_FREE(hs->szBuildFile);
|
||||
if(hs->szIndexPath != NULL)
|
||||
CASC_FREE(hs->szIndexPath);
|
||||
if(hs->szUrlPath != NULL)
|
||||
CASC_FREE(hs->szUrlPath);
|
||||
|
||||
// Fre the blob arrays
|
||||
QUERY_KEY_FreeArray(hs->pArchiveArray);
|
||||
QUERY_KEY_FreeArray(hs->pPatchArchiveArray);
|
||||
QUERY_KEY_FreeArray(hs->pEncodingKeys);
|
||||
|
||||
// Free the blobs
|
||||
QUERY_KEY_Free(&hs->CdnConfigKey);
|
||||
QUERY_KEY_Free(&hs->CdnBuildKey);
|
||||
QUERY_KEY_Free(&hs->ArchiveGroup);
|
||||
QUERY_KEY_Free(&hs->PatchArchiveGroup);
|
||||
QUERY_KEY_Free(&hs->RootKey);
|
||||
QUERY_KEY_Free(&hs->PatchKey);
|
||||
QUERY_KEY_Free(&hs->DownloadKey);
|
||||
QUERY_KEY_Free(&hs->InstallKey);
|
||||
FreeCascBlob(&hs->CdnConfigKey);
|
||||
FreeCascBlob(&hs->CdnBuildKey);
|
||||
FreeCascBlob(&hs->ArchivesGroup);
|
||||
FreeCascBlob(&hs->ArchivesKey);
|
||||
FreeCascBlob(&hs->PatchArchivesKey);
|
||||
FreeCascBlob(&hs->RootKey);
|
||||
FreeCascBlob(&hs->PatchKey);
|
||||
FreeCascBlob(&hs->DownloadKey);
|
||||
FreeCascBlob(&hs->InstallKey);
|
||||
FreeCascBlob(&hs->EncodingKey);
|
||||
|
||||
// Free the storage structure
|
||||
hs->szClassName = NULL;
|
||||
@@ -1266,6 +1048,13 @@ bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwLocaleMask, HANDLE
|
||||
nError = LoadEncodingFile(hs);
|
||||
}
|
||||
|
||||
// Initialize the dynamic array for extra files
|
||||
// Reserve space for 0x20 encoding entries
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = Array_Create(&hs->ExtraEntries, CASC_ENCODING_ENTRY_1, CASC_EXTRA_FILES);
|
||||
}
|
||||
|
||||
// Load the index files
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
@@ -1309,8 +1098,7 @@ bool WINAPI CascGetStorageInfo(
|
||||
break;
|
||||
|
||||
case CascStorageFeatures:
|
||||
if(hs->pMndxInfo != NULL)
|
||||
dwInfoValue |= CASC_FEATURE_LISTFILE;
|
||||
dwInfoValue |= (hs->pRootHandler->dwRootFlags & ROOT_FLAG_HAS_NAMES) ? CASC_FEATURE_LISTFILE : 0;
|
||||
break;
|
||||
|
||||
case CascStorageGameInfo:
|
||||
@@ -1342,8 +1130,6 @@ bool WINAPI CascGetStorageInfo(
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool WINAPI CascCloseStorage(HANDLE hStorage)
|
||||
{
|
||||
TCascStorage * hs;
|
||||
|
||||
@@ -176,6 +176,7 @@
|
||||
#define _tcsrchr strrchr
|
||||
#define _tcsstr strstr
|
||||
#define _tcsspn strspn
|
||||
#define _tcsncmp strncmp
|
||||
#define _tprintf printf
|
||||
#define _stprintf sprintf
|
||||
#define _tremove remove
|
||||
|
||||
@@ -98,9 +98,10 @@ static int LoadFileFrames(TCascFile * hf)
|
||||
else
|
||||
nError = GetLastError();
|
||||
|
||||
// Note: Do not take the FileSize from the sum of frames.
|
||||
// This value is invalid when loading the ENCODING file.
|
||||
// hf->FileSize = FileSize;
|
||||
// Note: on ENCODING file, this value is almost always bigger
|
||||
// then the real size of ENCODING. We handle this problem
|
||||
// by calculating size of the ENCODIG file from its header.
|
||||
hf->FileSize = FileSize;
|
||||
|
||||
#ifdef CASCLIB_TEST
|
||||
hf->FileSize_FrameSum = FileSize;
|
||||
@@ -264,6 +265,85 @@ static PCASC_FILE_FRAME FindFileFrame(TCascFile * hf, DWORD FilePointer)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ProcessFileFrame(
|
||||
LPBYTE pbOutBuffer,
|
||||
DWORD cbOutBuffer,
|
||||
LPBYTE pbInBuffer,
|
||||
DWORD cbInBuffer,
|
||||
DWORD dwFrameIndex)
|
||||
{
|
||||
LPBYTE pbTempBuffer;
|
||||
LPBYTE pbWorkBuffer;
|
||||
DWORD cbTempBuffer = CASCLIB_MAX(cbInBuffer, cbOutBuffer);
|
||||
DWORD cbWorkBuffer = cbOutBuffer + 1;
|
||||
DWORD dwStepCount = 0;
|
||||
bool bWorkComplete = false;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Allocate the temporary buffer that will serve as output
|
||||
pbWorkBuffer = pbTempBuffer = CASC_ALLOC(BYTE, cbTempBuffer);
|
||||
if(pbWorkBuffer == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Perform the loop
|
||||
for(;;)
|
||||
{
|
||||
// Set the output buffer.
|
||||
// Even operations: extract to temporary buffer
|
||||
// Odd operations: extract to output buffer
|
||||
pbWorkBuffer = (dwStepCount & 0x01) ? pbOutBuffer : pbTempBuffer;
|
||||
cbWorkBuffer = (dwStepCount & 0x01) ? cbOutBuffer : cbTempBuffer;
|
||||
|
||||
// Perform the operation specific to the operation ID
|
||||
switch(pbInBuffer[0])
|
||||
{
|
||||
case 'E': // Encrypted files
|
||||
nError = CascDecrypt(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1, dwFrameIndex);
|
||||
bWorkComplete = (nError != ERROR_SUCCESS);
|
||||
break;
|
||||
|
||||
case 'Z': // ZLIB compressed files
|
||||
nError = CascDecompress(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1);
|
||||
bWorkComplete = true;
|
||||
break;
|
||||
|
||||
case 'N': // Normal stored files
|
||||
nError = CascDirectCopy(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1);
|
||||
bWorkComplete = true;
|
||||
break;
|
||||
|
||||
case 'F': // Recursive frames - not supported
|
||||
default: // Unrecognized - if we unpacked something, we consider it done
|
||||
nError = ERROR_NOT_SUPPORTED;
|
||||
bWorkComplete = true;
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// Are we done?
|
||||
if(bWorkComplete)
|
||||
break;
|
||||
|
||||
// Set the input buffer to the work buffer
|
||||
pbInBuffer = pbWorkBuffer;
|
||||
cbInBuffer = cbWorkBuffer;
|
||||
dwStepCount++;
|
||||
}
|
||||
|
||||
// If the data are currently in the temporary buffer,
|
||||
// we need to copy them to output buffer
|
||||
if(nError == ERROR_SUCCESS && pbWorkBuffer != pbOutBuffer)
|
||||
{
|
||||
if(cbWorkBuffer != cbOutBuffer)
|
||||
nError = ERROR_INSUFFICIENT_BUFFER;
|
||||
memcpy(pbOutBuffer, pbWorkBuffer, cbOutBuffer);
|
||||
}
|
||||
|
||||
// Free the temporary buffer
|
||||
CASC_FREE(pbTempBuffer);
|
||||
return nError;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
@@ -299,7 +379,7 @@ DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh)
|
||||
}
|
||||
|
||||
// Make sure that the file header area is loaded
|
||||
nError = EnsureHeaderAreaIsLoaded(hf);
|
||||
nError = EnsureFrameHeadersLoaded(hf);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
SetLastError(nError);
|
||||
@@ -387,7 +467,6 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW
|
||||
DWORD dwFilePointer = 0;
|
||||
DWORD dwEndPointer = 0;
|
||||
DWORD dwFrameSize;
|
||||
DWORD cbOutBuffer;
|
||||
bool bReadResult;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
@@ -423,7 +502,7 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW
|
||||
{
|
||||
// Get the frame
|
||||
pFrame = FindFileFrame(hf, hf->FilePointer);
|
||||
if(pFrame == NULL)
|
||||
if(pFrame == NULL || pFrame->CompressedSize < 1)
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
@@ -439,7 +518,7 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW
|
||||
// Perform block read from each file frame
|
||||
while(dwFilePointer < dwEndPointer)
|
||||
{
|
||||
LPBYTE pbRawData = NULL;
|
||||
LPBYTE pbFrameData = NULL;
|
||||
DWORD dwFrameStart = pFrame->FrameFileOffset;
|
||||
DWORD dwFrameEnd = pFrame->FrameFileOffset + pFrame->FrameSize;
|
||||
|
||||
@@ -457,8 +536,8 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW
|
||||
}
|
||||
|
||||
// We also need to allocate buffer for the raw data
|
||||
pbRawData = CASC_ALLOC(BYTE, pFrame->CompressedSize);
|
||||
if(pbRawData == NULL)
|
||||
pbFrameData = CASC_ALLOC(BYTE, pFrame->CompressedSize);
|
||||
if(pbFrameData == NULL)
|
||||
{
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
break;
|
||||
@@ -466,7 +545,7 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW
|
||||
|
||||
// Load the raw file data to memory
|
||||
FileOffset = pFrame->FrameArchiveOffset;
|
||||
bReadResult = FileStream_Read(hf->pStream, &FileOffset, pbRawData, pFrame->CompressedSize);
|
||||
bReadResult = FileStream_Read(hf->pStream, &FileOffset, pbFrameData, pFrame->CompressedSize);
|
||||
|
||||
// Note: The raw file data size could be less than expected
|
||||
// Happened in WoW build 19342 with the ROOT file. MD5 in the frame header
|
||||
@@ -484,43 +563,34 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW
|
||||
// If the frame offset is before EOF and frame end is beyond EOF, correct it
|
||||
if(FileOffset < StreamSize && dwFrameSize < pFrame->CompressedSize)
|
||||
{
|
||||
memset(pbRawData + dwFrameSize, 0, (pFrame->CompressedSize - dwFrameSize));
|
||||
memset(pbFrameData + dwFrameSize, 0, (pFrame->CompressedSize - dwFrameSize));
|
||||
bReadResult = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the read result failed, we cannot finish reading it
|
||||
if(bReadResult == false)
|
||||
if(bReadResult && VerifyDataBlockHash(pbFrameData, pFrame->CompressedSize, pFrame->md5))
|
||||
{
|
||||
CASC_FREE(pbRawData);
|
||||
nError = GetLastError();
|
||||
break;
|
||||
// Convert the source frame to the file cache
|
||||
nError = ProcessFileFrame(hf->pbFileCache,
|
||||
pFrame->FrameSize,
|
||||
pbFrameData,
|
||||
pFrame->CompressedSize,
|
||||
(DWORD)(pFrame - hf->pFrames));
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Set the start and end of the cache
|
||||
hf->CacheStart = dwFrameStart;
|
||||
hf->CacheEnd = dwFrameEnd;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the block MD5
|
||||
if(!VerifyDataBlockHash(pbRawData, pFrame->CompressedSize, pFrame->md5))
|
||||
else
|
||||
{
|
||||
CASC_FREE(pbRawData);
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Decompress the file frame
|
||||
cbOutBuffer = pFrame->FrameSize;
|
||||
nError = CascDecompress(hf->pbFileCache, &cbOutBuffer, pbRawData, pFrame->CompressedSize);
|
||||
if(nError != ERROR_SUCCESS || cbOutBuffer != pFrame->FrameSize)
|
||||
{
|
||||
CASC_FREE(pbRawData);
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the start and end of the cache
|
||||
hf->CacheStart = dwFrameStart;
|
||||
hf->CacheEnd = dwFrameEnd;
|
||||
|
||||
// Free the decompress buffer, if needed
|
||||
CASC_FREE(pbRawData);
|
||||
// Free the raw frame data
|
||||
CASC_FREE(pbFrameData);
|
||||
}
|
||||
|
||||
// Copy the decompressed data
|
||||
|
||||
1189
dep/CascLib/src/CascRootFile_Diablo3.cpp
Normal file
1189
dep/CascLib/src/CascRootFile_Diablo3.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,20 +12,20 @@
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
#include "CascMndxRoot.h"
|
||||
#include "CascMndx.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local defines
|
||||
|
||||
#define CASC_MAR_SIGNATURE 0x0052414d // 'MAR\0'
|
||||
#define CASC_MAR_SIGNATURE 0x0052414d // 'MAR\0'
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
|
||||
typedef struct _FILE_MNDX_HEADER
|
||||
{
|
||||
DWORD Signature; // 'MNDX'
|
||||
DWORD HeaderVersion; // Must be <= 2
|
||||
DWORD Signature; // 'MNDX'
|
||||
DWORD HeaderVersion; // Must be <= 2
|
||||
DWORD FormatVersion;
|
||||
|
||||
} FILE_MNDX_HEADER, *PFILE_MNDX_HEADER;
|
||||
@@ -39,6 +39,57 @@ typedef struct _FILE_MAR_INFO
|
||||
DWORD MarDataOffsetHi;
|
||||
} FILE_MAR_INFO, *PFILE_MAR_INFO;
|
||||
|
||||
typedef struct _CASC_MNDX_INFO
|
||||
{
|
||||
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;
|
||||
bool bRootFileLoaded; // true if the root info file was properly loaded
|
||||
|
||||
} CASC_MNDX_INFO, *PCASC_MNDX_INFO;
|
||||
|
||||
typedef struct _CASC_MNDX_PACKAGE
|
||||
{
|
||||
char * szFileName; // Pointer to file name
|
||||
size_t nLength; // Length of the file name
|
||||
|
||||
} CASC_MNDX_PACKAGE, *PCASC_MNDX_PACKAGE;
|
||||
|
||||
typedef struct _CASC_MNDX_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_MNDX_PACKAGE Packages[1]; // List of packages
|
||||
|
||||
} CASC_MNDX_PACKAGES, *PCASC_MNDX_PACKAGES;
|
||||
|
||||
// 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;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Testing functions prototypes
|
||||
|
||||
@@ -2734,14 +2785,14 @@ static void MAR_FILE_Destructor(PMAR_FILE pMarFile)
|
||||
#define CASC_PACKAGES_INIT 0x10
|
||||
#define CASC_PACKAGES_DELTA 0x10
|
||||
|
||||
static PCASC_PACKAGES AllocatePackages(size_t nNameEntries, size_t nNameBufferMax)
|
||||
static PCASC_MNDX_PACKAGES AllocatePackages(size_t nNameEntries, size_t nNameBufferMax)
|
||||
{
|
||||
PCASC_PACKAGES pPackages;
|
||||
PCASC_MNDX_PACKAGES pPackages;
|
||||
size_t cbToAllocate;
|
||||
|
||||
// Allocate space
|
||||
cbToAllocate = sizeof(CASC_PACKAGES) + (nNameEntries * sizeof(CASC_PACKAGE)) + nNameBufferMax;
|
||||
pPackages = (PCASC_PACKAGES)CASC_ALLOC(BYTE, cbToAllocate);
|
||||
cbToAllocate = sizeof(CASC_MNDX_PACKAGES) + (nNameEntries * sizeof(CASC_MNDX_PACKAGE)) + nNameBufferMax;
|
||||
pPackages = (PCASC_MNDX_PACKAGES)CASC_ALLOC(BYTE, cbToAllocate);
|
||||
if(pPackages != NULL)
|
||||
{
|
||||
// Fill the structure
|
||||
@@ -2757,8 +2808,8 @@ static PCASC_PACKAGES AllocatePackages(size_t nNameEntries, size_t nNameBufferMa
|
||||
return pPackages;
|
||||
}
|
||||
|
||||
static PCASC_PACKAGES InsertToPackageList(
|
||||
PCASC_PACKAGES pPackages,
|
||||
static PCASC_MNDX_PACKAGES InsertToPackageList(
|
||||
PCASC_MNDX_PACKAGES pPackages,
|
||||
const char * szFileName,
|
||||
size_t cchFileName,
|
||||
size_t nPackageIndex)
|
||||
@@ -2777,11 +2828,11 @@ static PCASC_PACKAGES InsertToPackageList(
|
||||
// If any of the two variables overflowed, we need to reallocate the name list
|
||||
if(nNewNameEntries > pPackages->NameEntries || nNewNameBufferMax > pPackages->NameBufferMax)
|
||||
{
|
||||
PCASC_PACKAGES pOldPackages = pPackages;
|
||||
PCASC_MNDX_PACKAGES pOldPackages = pPackages;
|
||||
|
||||
// Allocate new name list
|
||||
cbToAllocate = sizeof(CASC_PACKAGES) + (nNewNameEntries * sizeof(CASC_PACKAGE)) + nNewNameBufferMax;
|
||||
pPackages = (PCASC_PACKAGES)CASC_ALLOC(BYTE, cbToAllocate);
|
||||
cbToAllocate = sizeof(CASC_MNDX_PACKAGES) + (nNewNameEntries * sizeof(CASC_MNDX_PACKAGE)) + nNewNameBufferMax;
|
||||
pPackages = (PCASC_MNDX_PACKAGES)CASC_ALLOC(BYTE, cbToAllocate);
|
||||
if(pPackages == NULL)
|
||||
return NULL;
|
||||
|
||||
@@ -2822,17 +2873,17 @@ static PCASC_PACKAGES InsertToPackageList(
|
||||
return pPackages;
|
||||
}
|
||||
|
||||
static int LoadPackageNames(TCascStorage * hs)
|
||||
static int LoadPackageNames(PCASC_MNDX_INFO pMndxInfo, PCASC_MNDX_PACKAGES * ppPackages)
|
||||
{
|
||||
TMndxFindResult Struct1C;
|
||||
PCASC_PACKAGES pPackages = NULL;
|
||||
PCASC_MNDX_PACKAGES pPackages = NULL;
|
||||
PMAR_FILE pMarFile;
|
||||
|
||||
// Sanity checks
|
||||
assert(hs->pMndxInfo != NULL);
|
||||
assert(pMndxInfo != NULL);
|
||||
|
||||
// Prepare the file name search in the top level directory
|
||||
pMarFile = hs->pMndxInfo->pMarFile1;
|
||||
pMarFile = pMndxInfo->pMarFile1;
|
||||
Struct1C.SetSearchPath("", 0);
|
||||
|
||||
// Allocate initial name list structure
|
||||
@@ -2856,21 +2907,34 @@ static int LoadPackageNames(TCascStorage * hs)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Set the name list to the CASC storage structure
|
||||
hs->pPackages = pPackages;
|
||||
// Give the packages to the caller
|
||||
if(ppPackages != NULL)
|
||||
ppPackages[0] = pPackages;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName)
|
||||
//-----------------------------------------------------------------------------
|
||||
// Implementation of root file functions
|
||||
|
||||
struct TRootHandler_MNDX : public TRootHandler
|
||||
{
|
||||
PCASC_PACKAGE pMatching = NULL;
|
||||
PCASC_PACKAGE pPackage;
|
||||
CASC_MNDX_INFO MndxInfo;
|
||||
|
||||
PCASC_ROOT_ENTRY_MNDX * ppValidEntries;
|
||||
PCASC_ROOT_ENTRY_MNDX pMndxEntries;
|
||||
PCASC_MNDX_PACKAGES pPackages; // Linear list of present packages
|
||||
};
|
||||
|
||||
PCASC_MNDX_PACKAGE FindMndxPackage(TRootHandler_MNDX * pRootHandler, const char * szFileName)
|
||||
{
|
||||
PCASC_MNDX_PACKAGE pMatching = NULL;
|
||||
PCASC_MNDX_PACKAGE pPackage;
|
||||
size_t nMaxLength = 0;
|
||||
size_t nLength = strlen(szFileName);
|
||||
|
||||
// Packages must be loaded
|
||||
assert(hs->pPackages != NULL);
|
||||
pPackage = hs->pPackages->Packages;
|
||||
assert(pRootHandler->pPackages != NULL);
|
||||
pPackage = pRootHandler->pPackages->Packages;
|
||||
|
||||
//FILE * fp = fopen("E:\\packages.txt", "wt");
|
||||
//for(size_t i = 0; i < hs->pPackages->NameEntries; i++, pPackage++)
|
||||
@@ -2881,7 +2945,7 @@ PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName)
|
||||
//fclose(fp);
|
||||
|
||||
// Find the longest matching name
|
||||
for(size_t i = 0; i < hs->pPackages->NameEntries; i++, pPackage++)
|
||||
for(size_t i = 0; i < pRootHandler->pPackages->NameEntries; i++, pPackage++)
|
||||
{
|
||||
if(pPackage->szFileName != NULL && pPackage->nLength < nLength && pPackage->nLength > nMaxLength)
|
||||
{
|
||||
@@ -2898,11 +2962,49 @@ PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName)
|
||||
return pMatching;
|
||||
}
|
||||
|
||||
static bool FillFindData(TCascSearch * pSearch, PCASC_FIND_DATA pFindData, TMndxFindResult * pStruct1C)
|
||||
int SearchMndxInfo(TRootHandler_MNDX * pRootHandler, const char * szFileName, DWORD dwPackage, PCASC_ROOT_ENTRY_MNDX * ppRootEntry)
|
||||
{
|
||||
PCASC_ROOT_ENTRY_MNDX pRootEntry;
|
||||
PCASC_MNDX_INFO pMndxInfo = &pRootHandler->MndxInfo;
|
||||
TMndxFindResult Struct1C;
|
||||
|
||||
// Search the database for the file name
|
||||
if(pMndxInfo->bRootFileLoaded)
|
||||
{
|
||||
Struct1C.SetSearchPath(szFileName, strlen(szFileName));
|
||||
|
||||
// Search the file name in the second MAR info (the one with stripped package names)
|
||||
if(MAR_FILE_SearchFile(pMndxInfo->pMarFile2, &Struct1C) != ERROR_SUCCESS)
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
|
||||
// The found MNDX index must fall into range of valid MNDX entries
|
||||
if(Struct1C.FileNameIndex < pMndxInfo->MndxEntriesValid)
|
||||
{
|
||||
// HOTS: E945F4
|
||||
pRootEntry = pRootHandler->ppValidEntries[Struct1C.FileNameIndex];
|
||||
while((pRootEntry->Flags & 0x00FFFFFF) != dwPackage)
|
||||
{
|
||||
// The highest bit serves as a terminator if set
|
||||
if(pRootEntry->Flags & 0x80000000)
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
|
||||
pRootEntry++;
|
||||
}
|
||||
|
||||
// Give the root entry pointer to the caller
|
||||
if(ppRootEntry != NULL)
|
||||
ppRootEntry[0] = pRootEntry;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
static LPBYTE FillFindData(TRootHandler_MNDX * pRootHandler, TCascSearch * pSearch, TMndxFindResult * pStruct1C, PDWORD PtrFileSize)
|
||||
{
|
||||
PCASC_ROOT_ENTRY_MNDX pRootEntry = NULL;
|
||||
TCascStorage * hs = pSearch->hs;
|
||||
PCASC_PACKAGE pPackage;
|
||||
PCASC_MNDX_PACKAGE pPackage;
|
||||
char * szStrippedPtr;
|
||||
char szStrippedName[MAX_PATH+1];
|
||||
int nError;
|
||||
@@ -2911,40 +3013,134 @@ static bool FillFindData(TCascSearch * pSearch, PCASC_FIND_DATA pFindData, TMndx
|
||||
assert(pStruct1C->cchFoundPath < MAX_PATH);
|
||||
|
||||
// Fill the file name
|
||||
memcpy(pFindData->szFileName, pStruct1C->szFoundPath, pStruct1C->cchFoundPath);
|
||||
pFindData->szFileName[pStruct1C->cchFoundPath] = 0;
|
||||
pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName);
|
||||
pFindData->dwFileSize = CASC_INVALID_SIZE;
|
||||
memcpy(pSearch->szFileName, pStruct1C->szFoundPath, pStruct1C->cchFoundPath);
|
||||
pSearch->szFileName[pStruct1C->cchFoundPath] = 0;
|
||||
|
||||
// Fill the file size
|
||||
pPackage = FindMndxPackage(hs, pFindData->szFileName);
|
||||
if(pPackage != NULL)
|
||||
pPackage = FindMndxPackage(pRootHandler, pSearch->szFileName);
|
||||
if(pPackage == NULL)
|
||||
return NULL;
|
||||
|
||||
// Cut the package name off the full path
|
||||
szStrippedPtr = pSearch->szFileName + pPackage->nLength;
|
||||
while(szStrippedPtr[0] == '/')
|
||||
szStrippedPtr++;
|
||||
|
||||
// We need to convert the stripped name to lowercase, replacing backslashes with slashes
|
||||
NormalizeFileName_LowerSlash(szStrippedName, szStrippedPtr, MAX_PATH);
|
||||
|
||||
// Search the package
|
||||
nError = SearchMndxInfo(pRootHandler, szStrippedName, (DWORD)(pPackage - pRootHandler->pPackages->Packages), &pRootEntry);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return NULL;
|
||||
|
||||
// Give the file size
|
||||
if(PtrFileSize != NULL)
|
||||
PtrFileSize[0] = pRootEntry->FileSize;
|
||||
return pRootEntry->EncodingKey;
|
||||
}
|
||||
|
||||
static int MndxHandler_Insert(TRootHandler_MNDX *, const char *, LPBYTE)
|
||||
{
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static LPBYTE MndxHandler_Search(TRootHandler_MNDX * pRootHandler, TCascSearch * pSearch, PDWORD PtrFileSize, PDWORD /* PtrLocaleFlags */)
|
||||
{
|
||||
TMndxFindResult * pStruct1C = NULL;
|
||||
PCASC_MNDX_INFO pMndxInfo = &pRootHandler->MndxInfo;
|
||||
PMAR_FILE pMarFile = pMndxInfo->pMarFile3;
|
||||
bool bFindResult = false;
|
||||
|
||||
// If the first time, allocate the structure for the search result
|
||||
if(pSearch->pRootContext == NULL)
|
||||
{
|
||||
// Cut the package name off the full path
|
||||
szStrippedPtr = pFindData->szFileName + pPackage->nLength;
|
||||
while(szStrippedPtr[0] == '/')
|
||||
szStrippedPtr++;
|
||||
// Create the new search structure
|
||||
pStruct1C = new TMndxFindResult;
|
||||
if(pStruct1C == NULL)
|
||||
return NULL;
|
||||
|
||||
// We need to convert the stripped name to lowercase, replacing backslashes with slashes
|
||||
NormalizeFileName_LowerSlash(szStrippedName, szStrippedPtr, MAX_PATH);
|
||||
|
||||
// Search the package
|
||||
nError = SearchMndxInfo(hs->pMndxInfo, szStrippedName, (DWORD)(pPackage - hs->pPackages->Packages), &pRootEntry);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
pFindData->dwFileSize = pRootEntry->FileSize;
|
||||
}
|
||||
// Setup the search mask
|
||||
pStruct1C->SetSearchPath("", 0);
|
||||
pSearch->pRootContext = pStruct1C;
|
||||
}
|
||||
return true;
|
||||
|
||||
// Make shortcut for the search structure
|
||||
assert(pSearch->pRootContext != NULL);
|
||||
pStruct1C = (TMndxFindResult *)pSearch->pRootContext;
|
||||
|
||||
// Search the next file name (our code)
|
||||
pMarFile->pDatabasePtr->sub_1956CE0(pStruct1C, &bFindResult);
|
||||
if(bFindResult == false)
|
||||
return NULL;
|
||||
|
||||
// Give the file size and encoding key
|
||||
return FillFindData(pRootHandler, pSearch, pStruct1C, PtrFileSize);
|
||||
}
|
||||
|
||||
static void MndxHandler_EndSearch(TRootHandler_MNDX * /* pRootHandler */, TCascSearch * pSearch)
|
||||
{
|
||||
if(pSearch != NULL)
|
||||
delete (TMndxFindResult *)pSearch->pRootContext;
|
||||
pSearch->pRootContext = NULL;
|
||||
}
|
||||
|
||||
static LPBYTE MndxHandler_GetKey(TRootHandler_MNDX * pRootHandler, const char * szFileName)
|
||||
{
|
||||
PCASC_ROOT_ENTRY_MNDX pRootEntry = NULL;
|
||||
PCASC_MNDX_PACKAGE pPackage;
|
||||
char * szStrippedName;
|
||||
char szNormName[MAX_PATH+1];
|
||||
int nError;
|
||||
|
||||
// Convert the file name to lowercase + slashes
|
||||
NormalizeFileName_LowerSlash(szNormName, szFileName, MAX_PATH);
|
||||
|
||||
// Find the package number
|
||||
pPackage = FindMndxPackage(pRootHandler, szNormName);
|
||||
if(pPackage == NULL)
|
||||
return NULL;
|
||||
|
||||
// Cut the package name off the full path
|
||||
szStrippedName = szNormName + pPackage->nLength;
|
||||
while(szStrippedName[0] == '/')
|
||||
szStrippedName++;
|
||||
|
||||
// Find the root entry
|
||||
nError = SearchMndxInfo(pRootHandler, szStrippedName, (DWORD)(pPackage - pRootHandler->pPackages->Packages), &pRootEntry);
|
||||
if(nError != ERROR_SUCCESS || pRootEntry == NULL)
|
||||
return NULL;
|
||||
|
||||
// Return the encoding key
|
||||
return pRootEntry->EncodingKey;
|
||||
}
|
||||
|
||||
static void MndxHandler_Close(TRootHandler_MNDX * pRootHandler)
|
||||
{
|
||||
if(pRootHandler->MndxInfo.pMarFile1 != NULL)
|
||||
MAR_FILE_Destructor(pRootHandler->MndxInfo.pMarFile1);
|
||||
if(pRootHandler->MndxInfo.pMarFile2 != NULL)
|
||||
MAR_FILE_Destructor(pRootHandler->MndxInfo.pMarFile2);
|
||||
if(pRootHandler->MndxInfo.pMarFile3 != NULL)
|
||||
MAR_FILE_Destructor(pRootHandler->MndxInfo.pMarFile3);
|
||||
if(pRootHandler->ppValidEntries != NULL)
|
||||
CASC_FREE(pRootHandler->ppValidEntries);
|
||||
if(pRootHandler->pMndxEntries != NULL)
|
||||
CASC_FREE(pRootHandler->pMndxEntries);
|
||||
if(pRootHandler->pPackages != NULL)
|
||||
CASC_FREE(pRootHandler->pPackages);
|
||||
|
||||
CASC_FREE(pRootHandler);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions - MNDX info
|
||||
|
||||
int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
{
|
||||
PFILE_MNDX_HEADER pMndxHeader = (PFILE_MNDX_HEADER)pbRootFile;
|
||||
PCASC_MNDX_INFO pMndxInfo;
|
||||
TRootHandler_MNDX * pRootHandler;
|
||||
FILE_MAR_INFO MarInfo;
|
||||
PMAR_FILE pMarFile;
|
||||
LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
|
||||
@@ -2957,13 +3153,24 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
if(pMndxHeader->Signature != CASC_MNDX_SIGNATURE || pMndxHeader->FormatVersion > 2 || pMndxHeader->FormatVersion < 1)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Allocate space for the CASC_MNDX_INFO structure
|
||||
pMndxInfo = CASC_ALLOC(CASC_MNDX_INFO, 1);
|
||||
if(pMndxInfo == NULL)
|
||||
// Allocate the structure for the MNDX root file
|
||||
hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_MNDX, 1);
|
||||
if(pRootHandler == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Fill-in the handler functions
|
||||
memset(pRootHandler, 0, sizeof(TRootHandler_MNDX));
|
||||
pRootHandler->Insert = (ROOT_INSERT)MndxHandler_Insert;
|
||||
pRootHandler->Search = (ROOT_SEARCH)MndxHandler_Search;
|
||||
pRootHandler->EndSearch = (ROOT_ENDSEARCH)MndxHandler_EndSearch;
|
||||
pRootHandler->GetKey = (ROOT_GETKEY)MndxHandler_GetKey;
|
||||
pRootHandler->Close = (ROOT_CLOSE) MndxHandler_Close;
|
||||
pMndxInfo = &pRootHandler->MndxInfo;
|
||||
|
||||
// Fill-in the flags
|
||||
pRootHandler->dwRootFlags |= ROOT_FLAG_HAS_NAMES;
|
||||
|
||||
// Copy the header into the MNDX info
|
||||
memset(pMndxInfo, 0, sizeof(CASC_MNDX_INFO));
|
||||
pMndxInfo->HeaderVersion = pMndxHeader->HeaderVersion;
|
||||
pMndxInfo->FormatVersion = pMndxHeader->FormatVersion;
|
||||
dwFilePointer += sizeof(FILE_MNDX_HEADER);
|
||||
@@ -3047,10 +3254,10 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
if(nError == ERROR_SUCCESS && FileNameCount == pMndxInfo->MndxEntriesValid)
|
||||
{
|
||||
cbToAllocate = pMndxInfo->MndxEntriesTotal * pMndxInfo->MndxEntrySize;
|
||||
pMndxInfo->pMndxEntries = (PCASC_ROOT_ENTRY_MNDX)CASC_ALLOC(BYTE, cbToAllocate);
|
||||
if(pMndxInfo->pMndxEntries != NULL)
|
||||
pRootHandler->pMndxEntries = (PCASC_ROOT_ENTRY_MNDX)CASC_ALLOC(BYTE, cbToAllocate);
|
||||
if(pRootHandler->pMndxEntries != NULL)
|
||||
{
|
||||
if(!RootFileRead(pbRootFile + pMndxInfo->MndxEntriesOffset, pbRootFileEnd, pMndxInfo->pMndxEntries, cbToAllocate))
|
||||
if(!RootFileRead(pbRootFile + pMndxInfo->MndxEntriesOffset, pbRootFileEnd, pRootHandler->pMndxEntries, cbToAllocate))
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
else
|
||||
@@ -3064,15 +3271,15 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
assert(pMndxInfo->MndxEntriesValid <= pMndxInfo->MndxEntriesTotal);
|
||||
pMndxInfo->ppValidEntries = CASC_ALLOC(PCASC_ROOT_ENTRY_MNDX, pMndxInfo->MndxEntriesValid + 1);
|
||||
if(pMndxInfo->ppValidEntries != NULL)
|
||||
pRootHandler->ppValidEntries = CASC_ALLOC(PCASC_ROOT_ENTRY_MNDX, pMndxInfo->MndxEntriesValid + 1);
|
||||
if(pRootHandler->ppValidEntries != NULL)
|
||||
{
|
||||
PCASC_ROOT_ENTRY_MNDX pRootEntry = pMndxInfo->pMndxEntries;
|
||||
PCASC_ROOT_ENTRY_MNDX pRootEntry = pRootHandler->pMndxEntries;
|
||||
DWORD ValidEntryCount = 1; // edx
|
||||
DWORD nIndex1 = 0;
|
||||
|
||||
// The first entry is always valid
|
||||
pMndxInfo->ppValidEntries[nIndex1++] = pMndxInfo->pMndxEntries;
|
||||
pRootHandler->ppValidEntries[nIndex1++] = pRootHandler->pMndxEntries;
|
||||
|
||||
// Put the remaining entries
|
||||
for(i = 0; i < pMndxInfo->MndxEntriesTotal; i++, pRootEntry++)
|
||||
@@ -3082,7 +3289,7 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
|
||||
if(pRootEntry->Flags & 0x80000000)
|
||||
{
|
||||
pMndxInfo->ppValidEntries[nIndex1++] = pRootEntry + 1;
|
||||
pRootHandler->ppValidEntries[nIndex1++] = pRootEntry + 1;
|
||||
ValidEntryCount++;
|
||||
}
|
||||
}
|
||||
@@ -3090,131 +3297,28 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
// Verify the final number of valid entries
|
||||
if((ValidEntryCount - 1) != pMndxInfo->MndxEntriesValid)
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
|
||||
// Mark the MNDX info as fully loaded
|
||||
pMndxInfo->bRootFileLoaded = true;
|
||||
}
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Save the MNDX info to the archive storage
|
||||
// Load the MNDX packages
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Store the MNDX database into the archive
|
||||
hs->pMndxInfo = pMndxInfo;
|
||||
pMndxInfo = NULL;
|
||||
nError = LoadPackageNames(pMndxInfo, &pRootHandler->pPackages);
|
||||
pMndxInfo->bRootFileLoaded = (nError == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
#if defined(_DEBUG) && defined(_X86_) && defined(CASCLIB_TEST)
|
||||
// CascDumpNameFragTable("E:\\casc-name-fragment-table.txt", hs->pMndxInfo->pMarFile1);
|
||||
// CascDumpFileNames("E:\\casc-listfile.txt", hs->pMndxInfo->pMarFile1);
|
||||
TestMndxRootFile(hs->pMndxInfo);
|
||||
// CascDumpNameFragTable("E:\\casc-name-fragment-table.txt", pMndxInfo->pMarFile1);
|
||||
// CascDumpFileNames("E:\\casc-listfile.txt", pMndxInfo->pMarFile1);
|
||||
// TestMndxRootFile(pRootHandler);
|
||||
#endif
|
||||
// Load the top level entries
|
||||
nError = LoadPackageNames(hs);
|
||||
}
|
||||
|
||||
// If anything failed, free the memory remaining allocated
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
if(pMndxInfo != NULL)
|
||||
FreeMndxInfo(pMndxInfo);
|
||||
pMndxInfo = NULL;
|
||||
}
|
||||
|
||||
// Return the result
|
||||
return nError;
|
||||
}
|
||||
|
||||
int SearchMndxInfo(PCASC_MNDX_INFO pMndxInfo, const char * szFileName, DWORD dwPackage, PCASC_ROOT_ENTRY_MNDX * ppRootEntry)
|
||||
{
|
||||
PCASC_ROOT_ENTRY_MNDX pRootEntry;
|
||||
TMndxFindResult Struct1C;
|
||||
|
||||
// Search the database for the file name
|
||||
if(pMndxInfo->bRootFileLoaded)
|
||||
{
|
||||
Struct1C.SetSearchPath(szFileName, strlen(szFileName));
|
||||
|
||||
// Search the file name in the second MAR info (the one with stripped package names)
|
||||
if(MAR_FILE_SearchFile(pMndxInfo->pMarFile2, &Struct1C) != ERROR_SUCCESS)
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
|
||||
// The found MNDX index must fall into range of valid MNDX entries
|
||||
if(Struct1C.FileNameIndex < pMndxInfo->MndxEntriesValid)
|
||||
{
|
||||
// HOTS: E945F4
|
||||
pRootEntry = pMndxInfo->ppValidEntries[Struct1C.FileNameIndex];
|
||||
while((pRootEntry->Flags & 0x00FFFFFF) != dwPackage)
|
||||
{
|
||||
// The highest bit serves as a terminator if set
|
||||
if(pRootEntry->Flags & 0x80000000)
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
|
||||
pRootEntry++;
|
||||
}
|
||||
|
||||
// Give the root entry pointer to the caller
|
||||
if(ppRootEntry != NULL)
|
||||
ppRootEntry[0] = pRootEntry;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
bool DoStorageSearch_MNDX(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
|
||||
{
|
||||
TMndxFindResult * pStruct1C = NULL;
|
||||
PCASC_MNDX_INFO pMndxInfo = pSearch->hs->pMndxInfo;
|
||||
PMAR_FILE pMarFile = pMndxInfo->pMarFile3;
|
||||
bool bFindResult = false;
|
||||
|
||||
// Sanity checks
|
||||
assert(pMndxInfo != NULL);
|
||||
|
||||
// If the first time, allocate the structure for the search result
|
||||
if(pSearch->pStruct1C == NULL)
|
||||
{
|
||||
// Create the new search structure
|
||||
pSearch->pStruct1C = pStruct1C = new TMndxFindResult;
|
||||
if(pSearch->pStruct1C == NULL)
|
||||
return false;
|
||||
|
||||
// Setup the search mask
|
||||
pStruct1C->SetSearchPath("", 0);
|
||||
}
|
||||
|
||||
// Make shortcut for the search structure
|
||||
assert(pSearch->pStruct1C != NULL);
|
||||
pStruct1C = (TMndxFindResult *)pSearch->pStruct1C;
|
||||
|
||||
// Search the next file name (our code)
|
||||
pMarFile->pDatabasePtr->sub_1956CE0(pStruct1C, &bFindResult);
|
||||
if(bFindResult)
|
||||
return FillFindData(pSearch, pFindData, pStruct1C);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FreeMndxInfo(PCASC_MNDX_INFO pMndxInfo)
|
||||
{
|
||||
if(pMndxInfo != NULL)
|
||||
{
|
||||
if(pMndxInfo->pMarFile1 != NULL)
|
||||
MAR_FILE_Destructor(pMndxInfo->pMarFile1);
|
||||
if(pMndxInfo->pMarFile2 != NULL)
|
||||
MAR_FILE_Destructor(pMndxInfo->pMarFile2);
|
||||
if(pMndxInfo->pMarFile3 != NULL)
|
||||
MAR_FILE_Destructor(pMndxInfo->pMarFile3);
|
||||
if(pMndxInfo->ppValidEntries != NULL)
|
||||
CASC_FREE(pMndxInfo->ppValidEntries);
|
||||
if(pMndxInfo->pMndxEntries != NULL)
|
||||
CASC_FREE(pMndxInfo->pMndxEntries);
|
||||
CASC_FREE(pMndxInfo);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Unit tests
|
||||
|
||||
@@ -6,9 +6,9 @@ ASSUME FS: NOTHING
|
||||
|
||||
TMndxFindResult struc ; (sizeof=0x1C) ; XREF: GAME_OBJECT_03F7E848::sub_E94500_r
|
||||
szSearchMask dd ? ; XREF: TMndxFindResult__SetPath+2D_w
|
||||
; TMndxFindResult__Constructor+4_w ...
|
||||
; TMndxFindResult__Constructor+4_w
|
||||
cchSearchMask dd ? ; XREF: TMndxFindResult__SetPath:loc_1956E9A_w
|
||||
; TMndxFindResult__Constructor+6_w ...
|
||||
; TMndxFindResult__Constructor+6_w
|
||||
field_8 dd ? ; XREF: TMndxFindResult__Constructor+9_w
|
||||
szFoundPath dd ? ; XREF: TMndxFindResult__Constructor+C_w
|
||||
; TFileNameDatabase__FindFileInDatabase+55_w
|
||||
@@ -17,7 +17,7 @@ cchFoundPath dd ? ; XREF: TMndxFindResult__Constructor+F_w
|
||||
FileNameIndex dd ? ; XREF: TMndxFindResult__Constructor+12_w
|
||||
; TFileNameDatabase__FindFileInDatabase+6B_w
|
||||
pStruct40 dd ? ; XREF: MAR_FILE__FindFileInDatabase+19_r
|
||||
; TMndxFindResult__SetPath:loc_1956E8C_r ...
|
||||
; TMndxFindResult__SetPath:loc_1956E8C_r
|
||||
TMndxFindResult ends
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
@@ -25,9 +25,9 @@ TMndxFindResult ends
|
||||
TRIPLET struc ; (sizeof=0xC)
|
||||
BitIndex dd ? ; XREF: TFileNameDatabase__CheckNextPathFragment+39_r
|
||||
NextKey dd ? ; XREF: TFileNameDatabase__CheckNextPathFragment+8C_r
|
||||
; TSparseArray__GetItemValue:loc_1959B8F_r ...
|
||||
; TSparseArray__GetItemValue:loc_1959B8F_r
|
||||
Distance dd ? ; XREF: TFileNameDatabase__CheckNextPathFragment+3E_r
|
||||
; TSparseArray__GetItemValue:loc_1959BBE_r ...
|
||||
; TSparseArray__GetItemValue:loc_1959BBE_r
|
||||
TRIPLET ends
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
@@ -44,33 +44,33 @@ NAME_ENTRY ends
|
||||
|
||||
TStruct14 struc ; (sizeof=0x14) ; XREF: TFileNameDatabase::sub_1959460r
|
||||
HashValue dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+44r
|
||||
; TGenericArray__InsertItem_STRUCT14+46w ...
|
||||
; TGenericArray__InsertItem_STRUCT14+46w
|
||||
field_4 dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+48r
|
||||
; TGenericArray__InsertItem_STRUCT14+4Bw ...
|
||||
; TGenericArray__InsertItem_STRUCT14+4Bw
|
||||
field_8 dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+4Er
|
||||
; TGenericArray__InsertItem_STRUCT14+51w ...
|
||||
; TGenericArray__InsertItem_STRUCT14+51w
|
||||
field_C dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+54r
|
||||
; TGenericArray__InsertItem_STRUCT14+57w ...
|
||||
; TGenericArray__InsertItem_STRUCT14+57w
|
||||
field_10 dd ? ; XREF: TGenericArray__InsertItem_STRUCT14+5Ar
|
||||
; TGenericArray__InsertItem_STRUCT14+5Dw ...
|
||||
; TGenericArray__InsertItem_STRUCT14+5Dw
|
||||
TStruct14 ends
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
|
||||
TGenericArray struc ; (sizeof=0x15) ; XREF: TGenericArray::LoadDwordsArrayWithCopyr
|
||||
; TFileNameDatabase::LoadBytesr ...
|
||||
; TFileNameDatabase::LoadBytesr
|
||||
DataBuffer dd ? ; XREF: TMndxFindResult__CreateStruct40+24w
|
||||
; TMndxFindResult__CreateStruct40+35w ...
|
||||
; TMndxFindResult__CreateStruct40+35w
|
||||
field_4 dd ? ; XREF: TMndxFindResult__CreateStruct40+26w
|
||||
; TMndxFindResult__CreateStruct40+38w ...
|
||||
; TMndxFindResult__CreateStruct40+38w
|
||||
ItemArray dd ? ; XREF: TMndxFindResult__CreateStruct40+29w
|
||||
; TMndxFindResult__CreateStruct40+3Bw ...
|
||||
; TMndxFindResult__CreateStruct40+3Bw
|
||||
ItemCount dd ? ; XREF: TMndxFindResult__CreateStruct40+2Cw
|
||||
; TMndxFindResult__CreateStruct40+3Ew ...
|
||||
; TMndxFindResult__CreateStruct40+3Ew
|
||||
MaxItemCount dd ? ; XREF: TFileNameDatabasePtr__CreateDatabase+27o
|
||||
; TMndxFindResult__CreateStruct40+2Fw ...
|
||||
; TMndxFindResult__CreateStruct40+2Fw
|
||||
bIsValidArray db ? ; XREF: LoadAndVerifyIndexFileHeader+31w
|
||||
; TMndxFindResult__CreateStruct40+32w ...
|
||||
; TMndxFindResult__CreateStruct40+32w
|
||||
TGenericArray ends
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
@@ -81,7 +81,7 @@ DataBuffer dd ? ; XREF: TArchiveDatabase__Destructor+64
|
||||
; TArchiveDatabase__Constructor+1A3w
|
||||
field_4 dd ? ; XREF: TArchiveDatabase__Constructor+1A9w
|
||||
ItemArray dd ? ; XREF: sub_1957350+31r
|
||||
; sub_19573D0+1Fr ...
|
||||
; sub_19573D0+1Fr
|
||||
ItemCount dd ? ; XREF: TArchiveDatabase__Constructor+1B5w
|
||||
MaxItemCount dd ? ; XREF: TArchiveDatabase__Constructor+1BBw
|
||||
bIsValidArray db ? ; XREF: TArchiveDatabase__Constructor+1C1w
|
||||
@@ -89,9 +89,9 @@ bIsValidArray db ? ; XREF: TArchiveDatabase__Constructor+1C
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
BitsPerEntry dd ? ; XREF: sub_1957350+1Fr
|
||||
; sub_19573D0+6r ...
|
||||
; sub_19573D0+6r
|
||||
EntryBitMask dd ? ; XREF: sub_1957350:loc_19573B1r
|
||||
; sub_19573D0:loc_1957419r ...
|
||||
; sub_19573D0:loc_1957419r
|
||||
TotalEntries dd ? ; XREF: TGenericArrayEx__LoadFromStream:loc_195861Bw
|
||||
; TArchiveDatabase__Constructor+1D3w
|
||||
TBitEntryArray ends
|
||||
@@ -100,50 +100,50 @@ TBitEntryArray ends
|
||||
|
||||
TStruct40 struc ; (sizeof=0x40)
|
||||
array_00 TGenericArray <> ; XREF: TMndxFindResult__CreateStruct40+24w
|
||||
; TMndxFindResult__CreateStruct40+26w ...
|
||||
; TMndxFindResult__CreateStruct40+26w
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
array_18 TGenericArray <> ; XREF: TMndxFindResult__CreateStruct40+35w
|
||||
; TMndxFindResult__CreateStruct40+38w ...
|
||||
; TMndxFindResult__CreateStruct40+38w
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
HashValue dd ? ; XREF: TMndxFindResult__CreateStruct40+47w
|
||||
; TFileNameDatabase__CheckNextPathFragment+1Ar ...
|
||||
; TFileNameDatabase__CheckNextPathFragment+1Ar
|
||||
CharIndex dd ? ; XREF: TMndxFindResult__CreateStruct40+4Aw
|
||||
; TFileNameDatabase__CheckNextPathFragment+11r ...
|
||||
; TFileNameDatabase__CheckNextPathFragment+11r
|
||||
ItemCount dd ? ; XREF: TMndxFindResult__CreateStruct40+4Dw
|
||||
; TStruct40__InitSearchBuffers+6Bw ...
|
||||
; TStruct40__InitSearchBuffers+6Bw
|
||||
SearchPhase dd ? ; XREF: TMndxFindResult__CreateStruct40+50w
|
||||
; TFileNameDatabase__FindFileInDatabase+17w ...
|
||||
; TFileNameDatabase__FindFileInDatabase+17w
|
||||
TStruct40 ends
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
|
||||
TSparseArray struc ; (sizeof=0x65) ; XREF: TSparseArray::LoadFromStream_Exchange_r
|
||||
; TNameIndexStruct_r ...
|
||||
; TNameIndexStruct_r
|
||||
ItemIsPresent TGenericArray <> ; XREF: LoadAndVerifyIndexFileHeader+31_w
|
||||
; TFileNameDatabase__Destructor+4C_r ...
|
||||
; TFileNameDatabase__Destructor+4C_r
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
TotalItemCount dd ? ; XREF: TSparseArray__ExchangeWith+4D_r
|
||||
; TSparseArray__ExchangeWith+56_w ...
|
||||
; TSparseArray__ExchangeWith+56_w
|
||||
ValidItemCount dd ? ; XREF: TFileNameDatabasePtr__GetFileNameCount:loc_1956D32_r
|
||||
; TSparseArray__ExchangeWith+59_r ...
|
||||
; TSparseArray__ExchangeWith+59_r
|
||||
ArrayTriplets_20 TGenericArray <> ; XREF: TFileNameDatabase__Destructor+40_r
|
||||
; TFileNameDatabase__Destructor+94_r ...
|
||||
; TFileNameDatabase__Destructor+94_r
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
ArrayDwords_38 TGenericArray <> ; XREF: TFileNameDatabasePtr__CreateDatabase+27_o
|
||||
; TFileNameDatabase__Destructor+34_r ...
|
||||
; TFileNameDatabase__Destructor+34_r
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
ArrayDwords_50 TGenericArray <> ; XREF: TFileNameDatabase__Destructor+28_r
|
||||
; TFileNameDatabase__Destructor+7C_r ...
|
||||
; TFileNameDatabase__Destructor+7C_r
|
||||
TSparseArray ends
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
@@ -151,12 +151,12 @@ TSparseArray ends
|
||||
TNameIndexStruct struc ; (sizeof=0x7D) ; XREF: TNameIndexStruct::LoadFromStream_Exchange_r
|
||||
; TFileNameDatabaser
|
||||
NameFragments TGenericArray <> ; XREF: TFileNameDatabase__Destructor+58_r
|
||||
; TFileNameDatabase__LoadFromStream+82_r ...
|
||||
; TFileNameDatabase__LoadFromStream+82_r
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
Struct68 TSparseArray <> ; XREF: LoadAndVerifyIndexFileHeader+31_w
|
||||
; TFileNameDatabase__Destructor+28_r ...
|
||||
; TFileNameDatabase__Destructor+28_r
|
||||
TNameIndexStruct ends
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
@@ -164,11 +164,11 @@ TNameIndexStruct ends
|
||||
TByteStream struc ; (sizeof=0x18) ; XREF: TFileNameDatabasePtr::CreateDatabase_r
|
||||
; TFileNameDatabase_r
|
||||
pbData dd ? ; XREF: TByteStream__Constructor+4_w
|
||||
; TByteStream__IsMarDataValid+2_r ...
|
||||
; TByteStream__IsMarDataValid+2_r
|
||||
pvMappedFile dd ? ; XREF: TByteStream__Constructor+6_w
|
||||
; TByteStream__Destructor+3_r
|
||||
cbData dd ? ; XREF: TByteStream__Constructor+9_w
|
||||
; TByteStream__GetBytes:loc_1959A05_r ...
|
||||
; TByteStream__GetBytes:loc_1959A05_r
|
||||
field_C dd ? ; XREF: TByteStream__Constructor+C_w
|
||||
hFile dd ? ; XREF: TByteStream__Constructor+F_w
|
||||
; TByteStream__Destructor:loc_19599D2_r
|
||||
@@ -183,11 +183,11 @@ TStruct10 struc ; (sizeof=0x10) ; XREF: TStruct10::sub_1957800_r
|
||||
field_0 dd ? ; XREF: sub_19572E0+24_w
|
||||
; TFileNameDatabase__Constructor+21A_w
|
||||
field_4 dd ? ; XREF: sub_1956FD0+28_w
|
||||
; sub_1956FD0:loc_1957001_w ...
|
||||
; sub_1956FD0:loc_1957001_w
|
||||
field_8 dd ? ; XREF: sub_19572E0+4A_w
|
||||
; sub_19572E0+5B_w ...
|
||||
; sub_19572E0+5B_w
|
||||
field_C dd ? ; XREF: sub_1957050:loc_1957074_w
|
||||
; sub_1957050:loc_1957081_w ...
|
||||
; sub_1957050:loc_1957081_w
|
||||
TStruct10 ends
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
@@ -195,52 +195,52 @@ TStruct10 ends
|
||||
TFileNameDatabasePtr struc ; (sizeof=0x4) ; XREF: TFileNameDatabase_r
|
||||
; MAR_FILE_r
|
||||
pDatabase dd ? ; XREF: MAR_FILE__Constructor+1D_w
|
||||
; MAR_FILE__CreateDatabase+22_w ...
|
||||
; MAR_FILE__CreateDatabase+22_w
|
||||
TFileNameDatabasePtr ends
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
|
||||
TFileNameDatabase struc ; (sizeof=0x240) ; XREF: TFileNameDatabase::LoadFromStream_Exchange_r
|
||||
Struct68_00 TSparseArray <> ; XREF: TFileNameDatabasePtr__CreateDatabase+27_o
|
||||
; TFileNameDatabase__Destructor+D9_r ...
|
||||
; TFileNameDatabase__Destructor+D9_r
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
FileNameIndexes TSparseArray <> ; XREF: TFileNameDatabasePtr__GetFileNameCount:loc_1956D32_r
|
||||
; TFileNameDatabase__Destructor+AC_r ...
|
||||
; TFileNameDatabase__Destructor+AC_r
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
Struct68_D0 TSparseArray <> ; XREF: TFileNameDatabase__Destructor+7C_r
|
||||
; TFileNameDatabase__Destructor+88_r ...
|
||||
; TFileNameDatabase__Destructor+88_r
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
FrgmDist_LoBits TGenericArray <> ; XREF: TFileNameDatabase__Destructor+70_r
|
||||
; TFileNameDatabase__CheckNextPathFragment:loc_1957AC9_r ...
|
||||
; TFileNameDatabase__CheckNextPathFragment:loc_1957AC9_r
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
FrgmDist_HiBits TBitEntryArray <> ; XREF: TFileNameDatabase__Destructor+64_r
|
||||
; TFileNameDatabase__CheckNextPathFragment+119_r ...
|
||||
; TFileNameDatabase__CheckNextPathFragment+119_r
|
||||
IndexStruct_174 TNameIndexStruct <> ; XREF: TFileNameDatabase__Destructor+28_r
|
||||
; TFileNameDatabase__Destructor+34_r ...
|
||||
; TFileNameDatabase__Destructor+34_r
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
NextDB TFileNameDatabasePtr <> ; XREF: TFileNameDatabase__Destructor+1D_o
|
||||
; TFileNameDatabase__CheckNextPathFragment+52_r ...
|
||||
; TFileNameDatabase__CheckNextPathFragment+52_r
|
||||
NameFragTable TGenericArray <> ; XREF: TFileNameDatabase__Destructor+E_r
|
||||
; TFileNameDatabase__CheckNextPathFragment+2F_r ...
|
||||
; TFileNameDatabase__CheckNextPathFragment+2F_r
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
db ? ; undefined
|
||||
NameFragIndexMask dd ? ; XREF: TFileNameDatabase__CheckNextPathFragment+26_r
|
||||
; sub_1957B80:loc_1957B95_r ...
|
||||
; sub_1957B80:loc_1957B95_r
|
||||
field_214 dd ? ; XREF: TFileNameDatabase__Constructor+20E_w
|
||||
; TFileNameDatabase__LoadFromStream+110_w
|
||||
Struct10 TStruct10 <> ; XREF: TFileNameDatabase__Constructor+21A_w
|
||||
; TFileNameDatabase__Constructor+224_w ...
|
||||
; TFileNameDatabase__Constructor+224_w
|
||||
MarStream TByteStream <> ; XREF: TFileNameDatabase__Destructor+3_o
|
||||
; TFileNameDatabase__Constructor+214_o
|
||||
TFileNameDatabase ends
|
||||
@@ -249,11 +249,11 @@ TFileNameDatabase ends
|
||||
|
||||
MAR_FILE struc ; (sizeof=0xC)
|
||||
pDatabasePtr TFileNameDatabasePtr <> ; XREF: MAR_FILE__Constructor+1D_w
|
||||
; MAR_FILE__CreateDatabase+22_w ...
|
||||
; MAR_FILE__CreateDatabase+22_w
|
||||
pbMarFileData dd ? ; XREF: MAR_FILE__Constructor+1A_w
|
||||
; MAR_FILE__CreateDatabase+1B_r ...
|
||||
; MAR_FILE__CreateDatabase+1B_r
|
||||
cbMarFileData dd ? ; XREF: MAR_FILE__Constructor+12_w
|
||||
; MAR_FILE__CreateDatabase+18_r ...
|
||||
; MAR_FILE__CreateDatabase+18_r
|
||||
MAR_FILE ends
|
||||
|
||||
.DATA
|
||||
@@ -368,7 +368,7 @@ operator_delete ENDP
|
||||
|
||||
|
||||
TSparseArray__GetItemValue proc near ; CODE XREF: sub_1957350+1A_p
|
||||
; TFileNameDatabase__CheckNextPathFragment+103_p ...
|
||||
; TFileNameDatabase__CheckNextPathFragment+103_p
|
||||
|
||||
arg_0 = dword ptr 8
|
||||
|
||||
@@ -451,7 +451,7 @@ loc_1959BDE: ; CODE XREF: TSparseArray__GetItemValue+
|
||||
add esi, eax
|
||||
|
||||
loc_1959BE0: ; CODE XREF: TSparseArray__GetItemValue+26_j
|
||||
; TSparseArray__GetItemValue+45_j ...
|
||||
; TSparseArray__GetItemValue+45_j
|
||||
mov ebx, edi ; jumptable 01959B88 default case
|
||||
shr ebx, 5
|
||||
test bl, 1
|
||||
@@ -525,7 +525,7 @@ TSparseArray__GetItemValue endp
|
||||
|
||||
TArchiveDatabase__sub_1959CB0 proc near
|
||||
; CODE XREF: TFileNameDatabase__CheckNextPathFragment+A1_p
|
||||
; sub_1958B00+E8_p ...
|
||||
; sub_1958B00+E8_p
|
||||
|
||||
pThis = dword ptr -4
|
||||
dwKey = dword ptr 8
|
||||
@@ -614,7 +614,7 @@ loc_1959D55: ; CODE XREF: TFileNameDatabase__FindNext
|
||||
mov ecx, [ebp+pThis]
|
||||
|
||||
loc_1959D5F: ; CODE XREF: TFileNameDatabase__FindNextMatch+62_j
|
||||
; TFileNameDatabase__FindNextMatch+7C_j ...
|
||||
; TFileNameDatabase__FindNextMatch+7C_j
|
||||
mov ecx, [ecx+28h]
|
||||
lea esi, [eax+eax*2]
|
||||
mov edi, [ecx+esi*4] ; EDI = Struct68_00.ArrayTriplets_20.ItemArray[eax]
|
||||
@@ -709,7 +709,7 @@ loc_1959E49: ; CODE XREF: TFileNameDatabase__FindNext
|
||||
lea edx, [edx+esi-1C0h]
|
||||
|
||||
loc_1959E53: ; CODE XREF: TFileNameDatabase__FindNextMatch+FE_j
|
||||
; TFileNameDatabase__FindNextMatch+10B_j ...
|
||||
; TFileNameDatabase__FindNextMatch+10B_j
|
||||
mov eax, [ebp+pThis]
|
||||
mov ebx, [eax+8]
|
||||
mov ecx, [ebx+edi*4]
|
||||
@@ -809,7 +809,7 @@ TArchiveDatabase__sub_1959CB0 endp
|
||||
; Attributes: bp-based frame
|
||||
|
||||
TNameIndexStruct__CheckNameFragment proc near ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+6B_p
|
||||
; TFileNameDatabase__CheckNextPathFragment+191_p ...
|
||||
; TFileNameDatabase__CheckNextPathFragment+191_p
|
||||
|
||||
pThis = dword ptr -4
|
||||
pUnknownStruct1C= dword ptr 8
|
||||
@@ -845,7 +845,7 @@ loc_195A1A1: ; CODE XREF: TNameIndexStruct__CheckName
|
||||
jb short loc_195A1A1
|
||||
|
||||
loc_195A1BD: ; CODE XREF: TNameIndexStruct__CheckNameFragment+2C_j
|
||||
; TNameIndexStruct__CheckNameFragment+5Ej ...
|
||||
; TNameIndexStruct__CheckNameFragment+5Ej
|
||||
pop edi
|
||||
pop esi
|
||||
xor al, al
|
||||
@@ -907,7 +907,7 @@ TNameIndexStruct__CheckNameFragment endp
|
||||
; Attributes: bp-based frame
|
||||
|
||||
sub_1957350 proc near ; CODE XREF: sub_1957B80+D8p
|
||||
; sub_1957B80:loc_1957C73p ...
|
||||
; sub_1957B80:loc_1957C73p
|
||||
|
||||
arg_0 = dword ptr 8
|
||||
|
||||
@@ -971,7 +971,7 @@ sub_1957350 endp
|
||||
; Attributes: bp-based frame
|
||||
|
||||
sub_1959F50 proc near ; CODE XREF: sub_1957B80+14C_p
|
||||
; sub_1958980+62_p ...
|
||||
; sub_1958980+62_p
|
||||
|
||||
var_4 = dword ptr -4
|
||||
arg_0 = dword ptr 8
|
||||
@@ -1046,7 +1046,7 @@ loc_1959FCA: ; CODE XREF: sub_1959F50+76_j
|
||||
mov ecx, [ebp+var_4]
|
||||
|
||||
loc_1959FD4: ; CODE XREF: sub_1959F50+51_j
|
||||
; sub_1959F50+5B_j ...
|
||||
; sub_1959F50+5B_j
|
||||
mov edi, [ecx+28h]
|
||||
lea esi, [eax+eax*2]
|
||||
sub edx, [edi+esi*4]
|
||||
@@ -1124,7 +1124,7 @@ loc_195A064: ; CODE XREF: sub_1959F50+CD_j
|
||||
sub edx, esi
|
||||
|
||||
loc_195A066: ; CODE XREF: sub_1959F50+B5_j
|
||||
; sub_1959F50+BC_j ...
|
||||
; sub_1959F50+BC_j
|
||||
mov ebx, [ecx+8]
|
||||
mov esi, [ebx+edi*4]
|
||||
mov eax, esi
|
||||
@@ -1223,7 +1223,7 @@ sub_1959F50 endp
|
||||
; Attributes: bp-based frame
|
||||
|
||||
sub_1957B80 proc near ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+5E_p
|
||||
; TFileNameDatabase__CheckNextPathFragment+180_p ...
|
||||
; TFileNameDatabase__CheckNextPathFragment+180_p
|
||||
|
||||
pStruct40 = dword ptr -4
|
||||
arg_0 = dword ptr 8
|
||||
@@ -1300,7 +1300,7 @@ loc_1957C05: ; CODE XREF: sub_1957B80+6C_j
|
||||
jb loc_1957B95
|
||||
|
||||
loc_1957C25: ; CODE XREF: sub_1957B80+67_j
|
||||
; sub_1957B80+7C_j ...
|
||||
; sub_1957B80+7C_j
|
||||
pop edi
|
||||
pop esi
|
||||
xor al, al
|
||||
@@ -1765,7 +1765,7 @@ TFileNameDatabase__FindFileInDatabase endp
|
||||
; Attributes: bp-based frame
|
||||
|
||||
TGenericArray__SetMaxItems_BYTE proc near ; CODE XREF: sub_19582E0+2Cp
|
||||
; TStruct40__InitSearchBuffers+2Cp ...
|
||||
; TStruct40__InitSearchBuffers+2Cp
|
||||
|
||||
ByteCount = dword ptr 8
|
||||
|
||||
@@ -1820,7 +1820,7 @@ TGenericArray__SetMaxItems_BYTE endp
|
||||
|
||||
TGenericArray__SetMaxItems_STRUCT14 proc near
|
||||
; CODE XREF: TGenericArray__InsertItem_STRUCT14+2Cp
|
||||
; TGenericArray__sub_19583A0+2Dp ...
|
||||
; TGenericArray__sub_19583A0+2Dp
|
||||
|
||||
var_4 = dword ptr -4
|
||||
ItemCount = dword ptr 8
|
||||
@@ -2086,7 +2086,7 @@ TGenericArray__InsertItem_STRUCT14 endp
|
||||
; Attributes: bp-based frame
|
||||
|
||||
CopyNameFragment proc near ; CODE XREF: sub_1958980+DAp
|
||||
; sub_1958D70+6Dp ...
|
||||
; sub_1958D70+6Dp
|
||||
|
||||
pThis = dword ptr -4
|
||||
pStruct1C = dword ptr 8
|
||||
@@ -2284,7 +2284,7 @@ CopyNameFragment endp
|
||||
; Attributes: bp-based frame
|
||||
|
||||
sub_1958D70 proc near ; CODE XREF: sub_1958980+C9p
|
||||
; sub_1958D70+59p ...
|
||||
; sub_1958D70+59p
|
||||
|
||||
var_C = dword ptr -0Ch
|
||||
var_8 = dword ptr -8
|
||||
@@ -2582,7 +2582,7 @@ sub_1958D70 endp
|
||||
; Attributes: bp-based frame
|
||||
|
||||
TFileNameDatabase__sub_1959CB0 proc near ; CODE XREF: TFileNameDatabase__CheckNextPathFragment+A1p
|
||||
; TFileNameDatabase__sub_1958B00+E8p ...
|
||||
; TFileNameDatabase__sub_1958B00+E8p
|
||||
|
||||
pThis = dword ptr -4
|
||||
dwKey = dword ptr 8
|
||||
@@ -2671,7 +2671,7 @@ loc_1959D55: ; CODE XREF: TFileNameDatabase__sub_1959
|
||||
mov ecx, [ebp+pThis]
|
||||
|
||||
loc_1959D5F: ; CODE XREF: TFileNameDatabase__sub_1959CB0+62j
|
||||
; TFileNameDatabase__sub_1959CB0+7Cj ...
|
||||
; TFileNameDatabase__sub_1959CB0+7Cj
|
||||
mov ecx, [ecx+TFileNameDatabase.Struct68_00.ArrayTriplets_20.ItemArray]
|
||||
lea esi, [eax+eax*2]
|
||||
mov edi, [ecx+esi*4] ; EDI = Struct68_00.ArrayTriplets_20.ItemArray[eax]
|
||||
@@ -2766,7 +2766,7 @@ loc_1959E49: ; CODE XREF: TFileNameDatabase__sub_1959
|
||||
lea edx, [edx+esi-1C0h]
|
||||
|
||||
loc_1959E53: ; CODE XREF: TFileNameDatabase__sub_1959CB0+FEj
|
||||
; TFileNameDatabase__sub_1959CB0+10Bj ...
|
||||
; TFileNameDatabase__sub_1959CB0+10Bj
|
||||
mov eax, [ebp+pThis]
|
||||
mov ebx, [eax+TFileNameDatabase.Struct68_00.ItemIsPresent.ItemArray]
|
||||
mov ecx, [ebx+edi*4]
|
||||
@@ -2866,7 +2866,7 @@ TFileNameDatabase__sub_1959CB0 endp
|
||||
; Attributes: bp-based frame
|
||||
|
||||
TNameIndexStruct__CheckAndCopyNameFragment proc near ; CODE XREF: TArchiveDatabase__sub_1958B00+74p
|
||||
; TArchiveDatabase__sub_1958B00+1E0p ...
|
||||
; TArchiveDatabase__sub_1958B00+1E0p
|
||||
|
||||
var_C = dword ptr -0Ch
|
||||
pThis = dword ptr -8
|
||||
@@ -3206,7 +3206,7 @@ TNameIndexStruct__CheckAndCopyNameFragment endp
|
||||
; Attributes: bp-based frame
|
||||
|
||||
sub_1959010 proc near ; CODE XREF: TArchiveDatabase__sub_1958B00+67p
|
||||
; TArchiveDatabase__sub_1958B00+1CFp ...
|
||||
; TArchiveDatabase__sub_1958B00+1CFp
|
||||
|
||||
pFragmentInfo = dword ptr -0Ch
|
||||
var_8 = dword ptr -8
|
||||
@@ -3262,7 +3262,7 @@ loc_195907F: ; CODE XREF: sub_1959010+5Ej
|
||||
jnz loc_195912E
|
||||
|
||||
loc_1959087: ; CODE XREF: sub_1959010+90j
|
||||
; sub_1959010+1F3j ...
|
||||
; sub_1959010+1F3j
|
||||
pop edi
|
||||
pop esi
|
||||
xor al, al
|
||||
@@ -3962,14 +3962,14 @@ loc_19594B0: ; CODE XREF: TFileNameDatabase__sub_1959
|
||||
; ---------------------------------------------------------------------------
|
||||
|
||||
loc_1959522: ; CODE XREF: TFileNameDatabase__sub_1959460+26Bj
|
||||
; TFileNameDatabase__sub_1959460+2D9j ...
|
||||
; TFileNameDatabase__sub_1959460+2D9j
|
||||
mov ebx, [ebp+pThis]
|
||||
jmp short loc_1959530
|
||||
; ---------------------------------------------------------------------------
|
||||
align 10h
|
||||
|
||||
loc_1959530: ; CODE XREF: TFileNameDatabase__sub_1959460+23j
|
||||
; TFileNameDatabase__sub_1959460+97j ...
|
||||
; TFileNameDatabase__sub_1959460+97j
|
||||
mov edx, [esi+TStruct40.ItemCount]
|
||||
cmp edx, [esi+TStruct40.array_18.ItemCount]
|
||||
jnz loc_19595BD
|
||||
199
dep/CascLib/src/CascRootFile_Ovr.cpp
Normal file
199
dep/CascLib/src/CascRootFile_Ovr.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/*****************************************************************************/
|
||||
/* CascRootFile_Ovr.cpp Copyright (c) Ladislav Zezula 2015 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Support for loading Overwatch ROOT file */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 28.10.15 1.00 Lad The first version of CascRootFile_Ovr.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structure definitions for Overwatch root file
|
||||
|
||||
typedef struct _CASC_FILE_ENTRY
|
||||
{
|
||||
ENCODING_KEY EncodingKey; // Encoding key
|
||||
ULONGLONG FileNameHash; // File name hash
|
||||
DWORD dwFileName; // Offset of the file name in the name cache
|
||||
} CASC_FILE_ENTRY, *PCASC_FILE_ENTRY;
|
||||
|
||||
struct TRootHandler_Ovr : public TRootHandler
|
||||
{
|
||||
// Linear global list of file entries
|
||||
DYNAMIC_ARRAY FileTable;
|
||||
|
||||
// Linear global list of names
|
||||
DYNAMIC_ARRAY FileNames;
|
||||
|
||||
// Global map of FileName -> FileEntry
|
||||
PCASC_MAP pRootMap;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static int InsertFileEntry(
|
||||
TRootHandler_Ovr * pRootHandler,
|
||||
const char * szFileName,
|
||||
LPBYTE pbEncodingKey)
|
||||
{
|
||||
PCASC_FILE_ENTRY pFileEntry;
|
||||
size_t nLength = strlen(szFileName);
|
||||
|
||||
// Attempt to insert the file name to the global buffer
|
||||
szFileName = (char *)Array_Insert(&pRootHandler->FileNames, szFileName, nLength + 1);
|
||||
if(szFileName == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Attempt to insert the entry to the array of file entries
|
||||
pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1);
|
||||
if(pFileEntry == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Fill the file entry
|
||||
pFileEntry->EncodingKey = *(PENCODING_KEY)pbEncodingKey;
|
||||
pFileEntry->FileNameHash = CalcFileNameHash(szFileName);
|
||||
pFileEntry->dwFileName = Array_IndexOf(&pRootHandler->FileNames, szFileName);
|
||||
|
||||
// Insert the file entry to the map
|
||||
assert(Map_FindObject(pRootHandler->pRootMap, &pFileEntry->FileNameHash, NULL) == NULL);
|
||||
Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Implementation of Overwatch root file
|
||||
|
||||
static int OvrHandler_Insert(
|
||||
TRootHandler_Ovr * pRootHandler,
|
||||
const char * szFileName,
|
||||
LPBYTE pbEncodingKey)
|
||||
{
|
||||
return InsertFileEntry(pRootHandler, szFileName, pbEncodingKey);
|
||||
}
|
||||
|
||||
static LPBYTE OvrHandler_Search(TRootHandler_Ovr * pRootHandler, TCascSearch * pSearch, PDWORD /* PtrFileSize */, PDWORD /* PtrLocaleFlags */)
|
||||
{
|
||||
PCASC_FILE_ENTRY pFileEntry;
|
||||
|
||||
// Are we still inside the root directory range?
|
||||
while(pSearch->IndexLevel1 < pRootHandler->FileTable.ItemCount)
|
||||
{
|
||||
// Retrieve the file item
|
||||
pFileEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, pSearch->IndexLevel1);
|
||||
strcpy(pSearch->szFileName, (char *)Array_ItemAt(&pRootHandler->FileNames, pFileEntry->dwFileName));
|
||||
|
||||
// Prepare the pointer to the next search
|
||||
pSearch->IndexLevel1++;
|
||||
return pFileEntry->EncodingKey.Value;
|
||||
}
|
||||
|
||||
// No more entries
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void OvrHandler_EndSearch(TRootHandler_Ovr * /* pRootHandler */, TCascSearch * /* pSearch */)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
static LPBYTE OvrHandler_GetKey(TRootHandler_Ovr * pRootHandler, const char * szFileName)
|
||||
{
|
||||
ULONGLONG FileNameHash = CalcFileNameHash(szFileName);
|
||||
|
||||
return (LPBYTE)Map_FindObject(pRootHandler->pRootMap, &FileNameHash, NULL);
|
||||
}
|
||||
|
||||
static void OvrHandler_Close(TRootHandler_Ovr * pRootHandler)
|
||||
{
|
||||
if(pRootHandler != NULL)
|
||||
{
|
||||
// Free the file map
|
||||
if(pRootHandler->pRootMap)
|
||||
Map_Free(pRootHandler->pRootMap);
|
||||
pRootHandler->pRootMap = NULL;
|
||||
|
||||
// Free the array of the file names and file items
|
||||
Array_Free(&pRootHandler->FileTable);
|
||||
Array_Free(&pRootHandler->FileNames);
|
||||
|
||||
// Free the root file itself
|
||||
CASC_FREE(pRootHandler);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
{
|
||||
TRootHandler_Ovr * pRootHandler;
|
||||
ENCODING_KEY KeyBuffer;
|
||||
QUERY_KEY EncodingKey = {KeyBuffer.Value, MD5_HASH_SIZE};
|
||||
void * pTextFile;
|
||||
size_t nLength;
|
||||
char szOneLine[0x200];
|
||||
char szFileName[MAX_PATH+1];
|
||||
DWORD dwFileCountMax = hs->pEncodingMap->TableSize;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Allocate the root handler object
|
||||
hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_Ovr, 1);
|
||||
if(pRootHandler == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Fill-in the handler functions
|
||||
memset(pRootHandler, 0, sizeof(TRootHandler_Ovr));
|
||||
pRootHandler->Insert = (ROOT_INSERT)OvrHandler_Insert;
|
||||
pRootHandler->Search = (ROOT_SEARCH)OvrHandler_Search;
|
||||
pRootHandler->EndSearch = (ROOT_ENDSEARCH)OvrHandler_EndSearch;
|
||||
pRootHandler->GetKey = (ROOT_GETKEY)OvrHandler_GetKey;
|
||||
pRootHandler->Close = (ROOT_CLOSE)OvrHandler_Close;
|
||||
|
||||
// Fill-in the flags
|
||||
pRootHandler->dwRootFlags |= ROOT_FLAG_HAS_NAMES;
|
||||
|
||||
// Allocate the linear array of file entries
|
||||
nError = Array_Create(&pRootHandler->FileTable, CASC_FILE_ENTRY, 0x10000);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
|
||||
// Allocate the buffer for the file names
|
||||
nError = Array_Create(&pRootHandler->FileNames, char, 0x10000);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
|
||||
// Create map of ROOT_ENTRY -> FileEntry
|
||||
pRootHandler->pRootMap = Map_Create(dwFileCountMax, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_ENTRY, FileNameHash));
|
||||
if(pRootHandler->pRootMap == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Parse the ROOT file
|
||||
pTextFile = ListFile_FromBuffer(pbRootFile, cbRootFile);
|
||||
if(pTextFile != NULL)
|
||||
{
|
||||
// Skip the first line, containing "#MD5|CHUNK_ID|FILENAME|INSTALLPATH"
|
||||
ListFile_GetNextLine(pTextFile, szOneLine, _maxchars(szOneLine));
|
||||
|
||||
// Parse the next lines
|
||||
while((nLength = ListFile_GetNextLine(pTextFile, szOneLine, _maxchars(szOneLine))) > 0)
|
||||
{
|
||||
// Parse the line
|
||||
nError = ParseRootFileLine(szOneLine, szOneLine + nLength, &EncodingKey, szFileName, _maxchars(szFileName));
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
InsertFileEntry(pRootHandler, szFileName, KeyBuffer.Value);
|
||||
}
|
||||
}
|
||||
|
||||
ListFile_Free(pTextFile);
|
||||
}
|
||||
|
||||
// Succeeded
|
||||
return nError;
|
||||
}
|
||||
531
dep/CascLib/src/CascRootFile_WoW6.cpp
Normal file
531
dep/CascLib/src/CascRootFile_WoW6.cpp
Normal file
@@ -0,0 +1,531 @@
|
||||
/*****************************************************************************/
|
||||
/* CascOpenStorage.cpp Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Storage functions for CASC */
|
||||
/* Note: WoW6 offsets refer to WoW.exe 6.0.3.19116 (32-bit) */
|
||||
/* SHA1: c10e9ffb7d040a37a356b96042657e1a0c95c0dd */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 29.04.14 1.00 Lad The first version of CascOpenStorage.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
|
||||
#define ROOT_SEARCH_PHASE_INITIALIZING 0
|
||||
#define ROOT_SEARCH_PHASE_LISTFILE 1
|
||||
#define ROOT_SEARCH_PHASE_NAMELESS 2
|
||||
#define ROOT_SEARCH_PHASE_FINISHED 2
|
||||
|
||||
// On-disk version of locale block
|
||||
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;
|
||||
|
||||
// On-disk version of root entry
|
||||
typedef struct _FILE_ROOT_ENTRY
|
||||
{
|
||||
ENCODING_KEY EncodingKey; // MD5 of the file
|
||||
ULONGLONG FileNameHash; // Jenkins hash of the file name
|
||||
|
||||
} FILE_ROOT_ENTRY, *PFILE_ROOT_ENTRY;
|
||||
|
||||
|
||||
typedef struct _CASC_ROOT_BLOCK
|
||||
{
|
||||
PFILE_LOCALE_BLOCK pLocaleBlockHdr; // Pointer to the locale block
|
||||
PDWORD FileDataIds; // Pointer to the array of File Data IDs
|
||||
PFILE_ROOT_ENTRY pRootEntries;
|
||||
|
||||
} CASC_ROOT_BLOCK, *PCASC_ROOT_BLOCK;
|
||||
|
||||
// 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_FILE_ENTRY
|
||||
{
|
||||
ENCODING_KEY EncodingKey; // File encoding key (MD5)
|
||||
ULONGLONG FileNameHash; // Jenkins hash of the file name
|
||||
DWORD FileDataId; // File Data Index
|
||||
DWORD Locales; // Locale flags of the file
|
||||
|
||||
} CASC_FILE_ENTRY, *PCASC_FILE_ENTRY;
|
||||
|
||||
struct TRootHandler_WoW6 : public TRootHandler
|
||||
{
|
||||
// Linear global list of file entries
|
||||
DYNAMIC_ARRAY FileTable;
|
||||
|
||||
// Global map of FileName -> FileEntry
|
||||
PCASC_MAP pRootMap;
|
||||
|
||||
// For counting files
|
||||
DWORD dwTotalFileCount;
|
||||
DWORD FileDataId;
|
||||
};
|
||||
|
||||
// Prototype for root file parsing routine
|
||||
typedef int (*PARSE_ROOT)(TRootHandler_WoW6 * pRootHandler, PCASC_ROOT_BLOCK pBlockInfo);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static bool IsFileDataIdName(const char * szFileName)
|
||||
{
|
||||
BYTE BinaryValue[4];
|
||||
|
||||
// File name must begin with "File", case insensitive
|
||||
if(AsciiToUpperTable_BkSlash[szFileName[0]] == 'F' &&
|
||||
AsciiToUpperTable_BkSlash[szFileName[1]] == 'I' &&
|
||||
AsciiToUpperTable_BkSlash[szFileName[2]] == 'L' &&
|
||||
AsciiToUpperTable_BkSlash[szFileName[3]] == 'E')
|
||||
{
|
||||
// Then, 8 hexadecimal digits must follow
|
||||
if(ConvertStringToBinary(szFileName + 4, 8, BinaryValue) == ERROR_SUCCESS)
|
||||
{
|
||||
// Must be followed by an extension or end-of-string
|
||||
return (szFileName[0x0C] == 0 || szFileName[0x0C] == '.');
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search by FileDataId
|
||||
PCASC_FILE_ENTRY FindRootEntry(DYNAMIC_ARRAY & FileTable, DWORD FileDataId)
|
||||
{
|
||||
PCASC_FILE_ENTRY pStartEntry = (PCASC_FILE_ENTRY)FileTable.ItemArray;
|
||||
PCASC_FILE_ENTRY pMidlEntry;
|
||||
PCASC_FILE_ENTRY pEndEntry = pStartEntry + FileTable.ItemCount - 1;
|
||||
int nResult;
|
||||
|
||||
// Perform binary search on the table
|
||||
while(pStartEntry < pEndEntry)
|
||||
{
|
||||
// Calculate the middle of the interval
|
||||
pMidlEntry = pStartEntry + ((pEndEntry - pStartEntry) / 2);
|
||||
|
||||
// Did we find it?
|
||||
nResult = (int)FileDataId - (int)pMidlEntry->FileDataId;
|
||||
if(nResult == 0)
|
||||
return pMidlEntry;
|
||||
|
||||
// Move the interval to the left or right
|
||||
(nResult < 0) ? pEndEntry = pMidlEntry : pStartEntry = pMidlEntry + 1;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Search by file name hash
|
||||
// Also used in CascSearchFile
|
||||
PCASC_FILE_ENTRY FindRootEntry(PCASC_MAP pRootMap, const char * szFileName, DWORD * PtrTableIndex)
|
||||
{
|
||||
// Calculate the HASH value of the normalized file name
|
||||
ULONGLONG FileNameHash = CalcFileNameHash(szFileName);
|
||||
|
||||
// Perform the hash search
|
||||
return (PCASC_FILE_ENTRY)Map_FindObject(pRootMap, &FileNameHash, PtrTableIndex);
|
||||
}
|
||||
|
||||
LPBYTE VerifyLocaleBlock(PCASC_ROOT_BLOCK pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd)
|
||||
{
|
||||
// Validate the file locale block
|
||||
pBlockInfo->pLocaleBlockHdr = (PFILE_LOCALE_BLOCK)pbFilePointer;
|
||||
pbFilePointer = (LPBYTE)(pBlockInfo->pLocaleBlockHdr + 1);
|
||||
if(pbFilePointer > pbFileEnd)
|
||||
return NULL;
|
||||
|
||||
// Validate the array of 32-bit integers
|
||||
pBlockInfo->FileDataIds = (PDWORD)pbFilePointer;
|
||||
pbFilePointer = (LPBYTE)(pBlockInfo->FileDataIds + pBlockInfo->pLocaleBlockHdr->NumberOfFiles);
|
||||
if(pbFilePointer > pbFileEnd)
|
||||
return NULL;
|
||||
|
||||
// Validate the array of root entries
|
||||
pBlockInfo->pRootEntries = (PFILE_ROOT_ENTRY)pbFilePointer;
|
||||
pbFilePointer = (LPBYTE)(pBlockInfo->pRootEntries + pBlockInfo->pLocaleBlockHdr->NumberOfFiles);
|
||||
if(pbFilePointer > pbFileEnd)
|
||||
return NULL;
|
||||
|
||||
// Return the position of the next block
|
||||
return pbFilePointer;
|
||||
}
|
||||
|
||||
static int ParseRoot_CountFiles(
|
||||
TRootHandler_WoW6 * pRootHandler,
|
||||
PCASC_ROOT_BLOCK pRootBlock)
|
||||
{
|
||||
// Add the file count to the total file count
|
||||
pRootHandler->dwTotalFileCount += pRootBlock->pLocaleBlockHdr->NumberOfFiles;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int ParseRoot_AddRootEntries(
|
||||
TRootHandler_WoW6 * pRootHandler,
|
||||
PCASC_ROOT_BLOCK pRootBlock)
|
||||
{
|
||||
PCASC_FILE_ENTRY pFileEntry;
|
||||
|
||||
// Sanity checks
|
||||
assert(pRootHandler->FileTable.ItemArray != NULL);
|
||||
assert(pRootHandler->FileTable.ItemCountMax != 0);
|
||||
|
||||
// WoW.exe (build 19116): Blocks with zero files are skipped
|
||||
for(DWORD i = 0; i < pRootBlock->pLocaleBlockHdr->NumberOfFiles; i++)
|
||||
{
|
||||
// Create new entry, with overflow check
|
||||
if(pRootHandler->FileTable.ItemCount >= pRootHandler->FileTable.ItemCountMax)
|
||||
return ERROR_INSUFFICIENT_BUFFER;
|
||||
pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1);
|
||||
|
||||
// (004147A3) Prepare the CASC_FILE_ENTRY structure
|
||||
pFileEntry->FileNameHash = pRootBlock->pRootEntries[i].FileNameHash;
|
||||
pFileEntry->FileDataId = pRootHandler->FileDataId + pRootBlock->FileDataIds[i];
|
||||
pFileEntry->Locales = pRootBlock->pLocaleBlockHdr->Locales;
|
||||
pFileEntry->EncodingKey = pRootBlock->pRootEntries[i].EncodingKey;
|
||||
|
||||
// Also, insert the entry to the map
|
||||
Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash);
|
||||
|
||||
// Update the local File Data Id
|
||||
assert((pFileEntry->FileDataId + 1) > pFileEntry->FileDataId);
|
||||
pRootHandler->FileDataId = pFileEntry->FileDataId + 1;
|
||||
|
||||
// Move to the next root entry
|
||||
pFileEntry++;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int ParseWowRootFileInternal(
|
||||
TRootHandler_WoW6 * pRootHandler,
|
||||
PARSE_ROOT pfnParseRoot,
|
||||
LPBYTE pbRootFile,
|
||||
LPBYTE pbRootFileEnd,
|
||||
DWORD dwLocaleMask,
|
||||
bool bLoadBlocksWithFlags80,
|
||||
BYTE HighestBitValue)
|
||||
{
|
||||
CASC_ROOT_BLOCK RootBlock;
|
||||
|
||||
// Now parse the root file
|
||||
while(pbRootFile < pbRootFileEnd)
|
||||
{
|
||||
// Validate the file locale block
|
||||
pbRootFile = VerifyLocaleBlock(&RootBlock, pbRootFile, pbRootFileEnd);
|
||||
if(pbRootFile == NULL)
|
||||
break;
|
||||
|
||||
// WoW.exe (build 19116): Entries with flag 0x100 set are skipped
|
||||
if(RootBlock.pLocaleBlockHdr->Flags & 0x100)
|
||||
continue;
|
||||
|
||||
// WoW.exe (build 19116): Entries with flag 0x80 set are skipped if arg_4 is set to FALSE (which is by default)
|
||||
if((RootBlock.pLocaleBlockHdr->Flags & 0x80) && bLoadBlocksWithFlags80 == 0)
|
||||
continue;
|
||||
|
||||
// WoW.exe (build 19116): Entries with (flags >> 0x1F) not equal to arg_8 are skipped
|
||||
if((RootBlock.pLocaleBlockHdr->Flags >> 0x1F) != HighestBitValue)
|
||||
continue;
|
||||
|
||||
// WoW.exe (build 19116): Locales other than defined mask are skipped too
|
||||
if((RootBlock.pLocaleBlockHdr->Locales & dwLocaleMask) == 0)
|
||||
continue;
|
||||
|
||||
// Now call the custom function
|
||||
pfnParseRoot(pRootHandler, &RootBlock);
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
// Code from WoW.exe
|
||||
if(dwLocaleMask == CASC_LOCALE_DUAL_LANG)
|
||||
{
|
||||
// Is this english version of WoW?
|
||||
if(arg_4 == CASC_LOCALE_BIT_ENUS)
|
||||
{
|
||||
LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENGB, false, HighestBitValue);
|
||||
LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENUS, false, HighestBitValue);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// Is this portuguese version of WoW?
|
||||
if(arg_4 == CASC_LOCALE_BIT_PTBR)
|
||||
{
|
||||
LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTPT, false, HighestBitValue);
|
||||
LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTBR, false, HighestBitValue);
|
||||
}
|
||||
}
|
||||
|
||||
LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, (1 << arg_4), false, HighestBitValue);
|
||||
*/
|
||||
|
||||
static int ParseWowRootFile2(
|
||||
TRootHandler_WoW6 * pRootHandler,
|
||||
PARSE_ROOT pfnParseRoot,
|
||||
LPBYTE pbRootFile,
|
||||
LPBYTE pbRootFileEnd,
|
||||
DWORD dwLocaleMask,
|
||||
BYTE HighestBitValue)
|
||||
{
|
||||
// Load the locale as-is
|
||||
ParseWowRootFileInternal(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, dwLocaleMask, false, HighestBitValue);
|
||||
|
||||
// If we wanted enGB, we also load enUS for the missing files
|
||||
if(dwLocaleMask == CASC_LOCALE_ENGB)
|
||||
ParseWowRootFileInternal(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, CASC_LOCALE_ENUS, false, HighestBitValue);
|
||||
|
||||
if(dwLocaleMask == CASC_LOCALE_PTPT)
|
||||
ParseWowRootFileInternal(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, CASC_LOCALE_PTBR, false, HighestBitValue);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// WoW.exe: 004146C7 (BuildManifest::Load)
|
||||
static int ParseWowRootFile(
|
||||
TRootHandler_WoW6 * pRootHandler,
|
||||
PARSE_ROOT pfnParseRoot,
|
||||
LPBYTE pbRootFile,
|
||||
LPBYTE pbRootFileEnd,
|
||||
DWORD dwLocaleMask)
|
||||
{
|
||||
ParseWowRootFile2(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, dwLocaleMask, 0);
|
||||
ParseWowRootFile2(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, dwLocaleMask, 1);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Implementation of WoW6 root file
|
||||
|
||||
static int WowHandler_Insert(
|
||||
TRootHandler_WoW6 * pRootHandler,
|
||||
const char * szFileName,
|
||||
LPBYTE pbEncodingKey)
|
||||
{
|
||||
PCASC_FILE_ENTRY pFileEntry;
|
||||
DWORD FileDataId = 0;
|
||||
|
||||
// Don't let the number of items to overflow
|
||||
if(pRootHandler->FileTable.ItemCount >= pRootHandler->FileTable.ItemCountMax)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Insert the item to the linear file list
|
||||
pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1);
|
||||
if(pFileEntry != NULL)
|
||||
{
|
||||
// Get the file data ID of the previous item (0 if this is the first one)
|
||||
if(pRootHandler->FileTable.ItemCount > 1)
|
||||
FileDataId = pFileEntry[-1].FileDataId;
|
||||
|
||||
// Fill-in the new entry
|
||||
pFileEntry->EncodingKey = *(PENCODING_KEY)pbEncodingKey;
|
||||
pFileEntry->FileNameHash = CalcFileNameHash(szFileName);
|
||||
pFileEntry->FileDataId = FileDataId + 1;
|
||||
pFileEntry->Locales = CASC_LOCALE_ALL;
|
||||
|
||||
// Verify collisions (debug version only)
|
||||
assert(Map_FindObject(pRootHandler->pRootMap, &pFileEntry->FileNameHash, NULL) == NULL);
|
||||
|
||||
// Insert the entry to the map
|
||||
Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash);
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static LPBYTE WowHandler_Search(TRootHandler_WoW6 * pRootHandler, TCascSearch * pSearch, PDWORD /* PtrFileSize */, PDWORD PtrLocaleFlags)
|
||||
{
|
||||
PCASC_FILE_ENTRY pFileEntry;
|
||||
|
||||
// Only if we have a listfile
|
||||
if(pSearch->pCache != NULL)
|
||||
{
|
||||
// Keep going through the listfile
|
||||
while(ListFile_GetNext(pSearch->pCache, pSearch->szMask, pSearch->szFileName, MAX_PATH))
|
||||
{
|
||||
// Find the root entry
|
||||
pFileEntry = FindRootEntry(pRootHandler->pRootMap, pSearch->szFileName, NULL);
|
||||
if(pFileEntry != NULL)
|
||||
{
|
||||
// Give the caller the locale mask
|
||||
if(PtrLocaleFlags != NULL)
|
||||
PtrLocaleFlags[0] = pFileEntry->Locales;
|
||||
return pFileEntry->EncodingKey.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No more files
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static LPBYTE WowHandler_GetKey(TRootHandler_WoW6 * pRootHandler, const char * szFileName)
|
||||
{
|
||||
PCASC_FILE_ENTRY pFileEntry;
|
||||
DWORD FileDataId;
|
||||
BYTE FileDataIdLE[4];
|
||||
|
||||
// Open by FileDataId. The file name must be as following:
|
||||
// File########.xxx, where '#' are hexa-decimal numbers (case insensitive).
|
||||
// Extension is ignored in that case
|
||||
if(IsFileDataIdName(szFileName))
|
||||
{
|
||||
ConvertStringToBinary(szFileName + 4, 8, FileDataIdLE);
|
||||
FileDataId = ConvertBytesToInteger_4(FileDataIdLE);
|
||||
|
||||
pFileEntry = FindRootEntry(pRootHandler->FileTable, FileDataId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find by the file name hash
|
||||
pFileEntry = FindRootEntry(pRootHandler->pRootMap, szFileName, NULL);
|
||||
}
|
||||
|
||||
return (pFileEntry != NULL) ? pFileEntry->EncodingKey.Value : NULL;
|
||||
}
|
||||
|
||||
static void WowHandler_EndSearch(TRootHandler_WoW6 * /* pRootHandler */, TCascSearch * pSearch)
|
||||
{
|
||||
if(pSearch->pRootContext != NULL)
|
||||
CASC_FREE(pSearch->pRootContext);
|
||||
pSearch->pRootContext = NULL;
|
||||
}
|
||||
|
||||
static void WowHandler_Close(TRootHandler_WoW6 * pRootHandler)
|
||||
{
|
||||
if(pRootHandler != NULL)
|
||||
{
|
||||
Array_Free(&pRootHandler->FileTable);
|
||||
Map_Free(pRootHandler->pRootMap);
|
||||
CASC_FREE(pRootHandler);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
static void TRootHandlerWoW6_Dump(
|
||||
TCascStorage * hs,
|
||||
TDumpContext * dc, // Pointer to an opened file
|
||||
LPBYTE pbRootFile,
|
||||
DWORD cbRootFile,
|
||||
const TCHAR * szListFile,
|
||||
int nDumpLevel)
|
||||
{
|
||||
PCASC_ENCODING_ENTRY pEncodingEntry;
|
||||
CASC_ROOT_BLOCK BlockInfo;
|
||||
PLISTFILE_MAP pListMap;
|
||||
QUERY_KEY EncodingKey;
|
||||
LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
|
||||
LPBYTE pbFilePointer;
|
||||
char szOneLine[0x100];
|
||||
DWORD i;
|
||||
|
||||
// Create the listfile map
|
||||
pListMap = ListFile_CreateMap(szListFile);
|
||||
|
||||
// Dump the root entries
|
||||
for(pbFilePointer = pbRootFile; pbFilePointer <= pbRootFileEnd; )
|
||||
{
|
||||
// Validate the root block
|
||||
pbFilePointer = VerifyLocaleBlock(&BlockInfo, pbFilePointer, pbRootFileEnd);
|
||||
if(pbFilePointer == NULL)
|
||||
break;
|
||||
|
||||
// Dump the locale block
|
||||
dump_print(dc, "Flags: %08X Locales: %08X NumberOfFiles: %u\n"
|
||||
"=========================================================\n",
|
||||
BlockInfo.pLocaleBlockHdr->Flags,
|
||||
BlockInfo.pLocaleBlockHdr->Locales,
|
||||
BlockInfo.pLocaleBlockHdr->NumberOfFiles);
|
||||
|
||||
// Dump the hashes and encoding keys
|
||||
for(i = 0; i < BlockInfo.pLocaleBlockHdr->NumberOfFiles; i++)
|
||||
{
|
||||
// Dump the entry
|
||||
dump_print(dc, "%08X %08X-%08X %s %s\n",
|
||||
(DWORD)(BlockInfo.FileDataIds[i]),
|
||||
(DWORD)(BlockInfo.pRootEntries[i].FileNameHash >> 0x20),
|
||||
(DWORD)(BlockInfo.pRootEntries[i].FileNameHash),
|
||||
StringFromMD5(BlockInfo.pRootEntries[i].EncodingKey.Value, szOneLine),
|
||||
ListFile_FindName(pListMap, BlockInfo.pRootEntries[i].FileNameHash));
|
||||
|
||||
// Find the encoding entry in the encoding table
|
||||
if(nDumpLevel >= DUMP_LEVEL_ENCODING_FILE)
|
||||
{
|
||||
EncodingKey.pbData = BlockInfo.pRootEntries[i].EncodingKey.Value;
|
||||
EncodingKey.cbData = MD5_HASH_SIZE;
|
||||
pEncodingEntry = FindEncodingEntry(hs, &EncodingKey, NULL);
|
||||
CascDumpEncodingEntry(hs, dc, pEncodingEntry, nDumpLevel);
|
||||
}
|
||||
}
|
||||
|
||||
// Put extra newline
|
||||
dump_print(dc, "\n");
|
||||
}
|
||||
|
||||
ListFile_FreeMap(pListMap);
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int RootHandler_CreateWoW6(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask)
|
||||
{
|
||||
TRootHandler_WoW6 * pRootHandler;
|
||||
LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
|
||||
int nError;
|
||||
|
||||
// Verify the size
|
||||
if(pbRootFile == NULL || cbRootFile <= sizeof(PFILE_LOCALE_BLOCK))
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
|
||||
// Allocate the root handler object
|
||||
hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_WoW6, 1);
|
||||
if(pRootHandler == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Fill-in the handler functions
|
||||
memset(pRootHandler, 0, sizeof(TRootHandler_WoW6));
|
||||
pRootHandler->Insert = (ROOT_INSERT)WowHandler_Insert;
|
||||
pRootHandler->Search = (ROOT_SEARCH)WowHandler_Search;
|
||||
pRootHandler->EndSearch = (ROOT_ENDSEARCH)WowHandler_EndSearch;
|
||||
pRootHandler->GetKey = (ROOT_GETKEY)WowHandler_GetKey;
|
||||
pRootHandler->Close = (ROOT_CLOSE)WowHandler_Close;
|
||||
|
||||
#ifdef _DEBUG
|
||||
pRootHandler->Dump = TRootHandlerWoW6_Dump; // Support for ROOT file dump
|
||||
#endif // _DEBUG
|
||||
|
||||
// Count the files that are going to be loaded
|
||||
ParseWowRootFile(pRootHandler, ParseRoot_CountFiles, pbRootFile, pbRootFileEnd, dwLocaleMask);
|
||||
pRootHandler->dwTotalFileCount += CASC_EXTRA_FILES;
|
||||
|
||||
// Create linear table that will contain all root items
|
||||
nError = Array_Create(&pRootHandler->FileTable, CASC_FILE_ENTRY, pRootHandler->dwTotalFileCount);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
|
||||
// Create the map of FileHash ->FileEntry
|
||||
pRootHandler->pRootMap = Map_Create(pRootHandler->dwTotalFileCount, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_ENTRY, FileNameHash));
|
||||
if(pRootHandler->pRootMap == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Parse the root file again and insert all files to the map
|
||||
ParseWowRootFile(pRootHandler, ParseRoot_AddRootEntries, pbRootFile, pbRootFileEnd, dwLocaleMask);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
@@ -99,7 +99,7 @@ void CopyString(char * szTarget, const wchar_t * szSource, size_t cchLength)
|
||||
szTarget[cchLength] = 0;
|
||||
}
|
||||
|
||||
char * NewStr(const char * szString, size_t nCharsToReserve)
|
||||
char * CascNewStr(const char * szString, size_t nCharsToReserve)
|
||||
{
|
||||
char * szNewString = NULL;
|
||||
size_t nLength;
|
||||
@@ -118,7 +118,7 @@ char * NewStr(const char * szString, size_t nCharsToReserve)
|
||||
return szNewString;
|
||||
}
|
||||
|
||||
wchar_t * NewStr(const wchar_t * szString, size_t nCharsToReserve)
|
||||
wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve)
|
||||
{
|
||||
wchar_t * szNewString = NULL;
|
||||
size_t nLength;
|
||||
@@ -137,22 +137,20 @@ wchar_t * NewStr(const wchar_t * szString, size_t nCharsToReserve)
|
||||
return szNewString;
|
||||
}
|
||||
|
||||
TCHAR * NewStrFromAnsi(LPBYTE pbStringBegin, LPBYTE pbStringEnd)
|
||||
TCHAR * CascNewStrFromAnsi(const char * szBegin, const char * szEnd)
|
||||
{
|
||||
TCHAR * szNewString = NULL;
|
||||
TCHAR * szStringPtr = NULL;
|
||||
size_t nLength = (size_t)(pbStringEnd - pbStringBegin);
|
||||
|
||||
if(pbStringEnd > pbStringBegin)
|
||||
// Only if the entry is valid
|
||||
if(szBegin != NULL && szEnd > szBegin)
|
||||
{
|
||||
szNewString = szStringPtr = CASC_ALLOC(TCHAR, nLength + 1);
|
||||
// Allocate and copy the string
|
||||
szNewString = CASC_ALLOC(TCHAR, (szEnd - szBegin + 1));
|
||||
if(szNewString != NULL)
|
||||
{
|
||||
CopyString(szStringPtr, (const char *)pbStringBegin, nLength);
|
||||
szStringPtr[nLength] = 0;
|
||||
}
|
||||
CopyString(szNewString, szBegin, (szEnd - szBegin));
|
||||
}
|
||||
|
||||
// Return the string
|
||||
return szNewString;
|
||||
}
|
||||
|
||||
@@ -218,30 +216,60 @@ TCHAR * CombinePath(const TCHAR * szDirectory, const TCHAR * szSubDir)
|
||||
return szFullPath;
|
||||
}
|
||||
|
||||
void NormalizeFileName_UpperBkSlash(char * szTrgFileName, const char * szSrcFileName, size_t cchMaxChars)
|
||||
TCHAR * CombinePathAndString(const TCHAR * szPath, const char * szString, size_t nLength)
|
||||
{
|
||||
char * szTrgFileEnd = szTrgFileName + cchMaxChars;
|
||||
size_t i;
|
||||
TCHAR * szFullPath = NULL;
|
||||
TCHAR * szSubDir;
|
||||
|
||||
// Normalize the file name: ToLower + BackSlashToSlash
|
||||
for(i = 0; szSrcFileName[i] != 0 && szTrgFileName < szTrgFileEnd; i++)
|
||||
szTrgFileName[i] = AsciiToUpperTable_BkSlash[szSrcFileName[i]];
|
||||
// Create the subdir string
|
||||
szSubDir = CASC_ALLOC(TCHAR, nLength + 1);
|
||||
if(szSubDir != NULL)
|
||||
{
|
||||
CopyString(szSubDir, szString, nLength);
|
||||
szFullPath = CombinePath(szPath, szSubDir);
|
||||
CASC_FREE(szSubDir);
|
||||
}
|
||||
|
||||
assert(szSrcFileName[i] == 0);
|
||||
szTrgFileName[i] = 0;
|
||||
return szFullPath;
|
||||
}
|
||||
|
||||
void NormalizeFileName_LowerSlash(char * szTrgFileName, const char * szSrcFileName, size_t cchMaxChars)
|
||||
size_t NormalizeFileName(const unsigned char * NormTable, char * szNormName, const char * szFileName, size_t cchMaxChars)
|
||||
{
|
||||
char * szTrgFileEnd = szTrgFileName + cchMaxChars;
|
||||
char * szNormNameEnd = szNormName + cchMaxChars;
|
||||
size_t i;
|
||||
|
||||
// Normalize the file name: ToLower + BackSlashToSlash
|
||||
for(i = 0; szSrcFileName[i] != 0 && szTrgFileName < szTrgFileEnd; i++)
|
||||
szTrgFileName[i] = AsciiToLowerTable_Slash[szSrcFileName[i]];
|
||||
for(i = 0; szFileName[0] != 0 && szNormName < szNormNameEnd; i++)
|
||||
*szNormName++ = NormTable[*szFileName++];
|
||||
|
||||
assert(szSrcFileName[i] == 0);
|
||||
szTrgFileName[i] = 0;
|
||||
// Terminate the string
|
||||
szNormName[0] = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
size_t NormalizeFileName_UpperBkSlash(char * szNormName, const char * szFileName, size_t cchMaxChars)
|
||||
{
|
||||
return NormalizeFileName(AsciiToUpperTable_BkSlash, szNormName, szFileName, cchMaxChars);
|
||||
}
|
||||
|
||||
size_t NormalizeFileName_LowerSlash(char * szNormName, const char * szFileName, size_t cchMaxChars)
|
||||
{
|
||||
return NormalizeFileName(AsciiToLowerTable_Slash, szNormName, szFileName, cchMaxChars);
|
||||
}
|
||||
|
||||
ULONGLONG CalcFileNameHash(const char * szFileName)
|
||||
{
|
||||
char szNormName[MAX_PATH+1];
|
||||
uint32_t dwHashHigh = 0;
|
||||
uint32_t dwHashLow = 0;
|
||||
size_t nLength;
|
||||
|
||||
// Normalize the file name - convert to uppercase, slashes to backslashes
|
||||
nLength = NormalizeFileName_UpperBkSlash(szNormName, szFileName, MAX_PATH);
|
||||
|
||||
// Calculate the HASH value of the normalized file name
|
||||
hashlittle2(szNormName, nLength, &dwHashHigh, &dwHashLow);
|
||||
return ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow;
|
||||
}
|
||||
|
||||
int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue)
|
||||
@@ -256,6 +284,22 @@ int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue)
|
||||
return (Digit > 0x0F) ? ERROR_BAD_FORMAT : ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int ConvertStringToInt08(const char * szString, PDWORD PtrValue)
|
||||
{
|
||||
BYTE DigitOne = AsciiToUpperTable_BkSlash[szString[0]] - '0';
|
||||
BYTE DigitTwo = AsciiToUpperTable_BkSlash[szString[1]] - '0';
|
||||
|
||||
// Fix the digits
|
||||
if(DigitOne > 9)
|
||||
DigitOne -= 'A' - '9' - 1;
|
||||
if(DigitTwo > 9)
|
||||
DigitTwo -= 'A' - '9' - 1;
|
||||
|
||||
// Combine them into a value
|
||||
PtrValue[0] = (DigitOne << 0x04) | DigitTwo;
|
||||
return (DigitOne <= 0x0F && DigitTwo <= 0x0F) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
int ConvertStringToInt32(const TCHAR * szString, size_t nMaxDigits, PDWORD PtrValue)
|
||||
{
|
||||
// The number of digits must be even
|
||||
@@ -290,6 +334,41 @@ int ConvertStringToInt32(const TCHAR * szString, size_t nMaxDigits, PDWORD PtrVa
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// Converts string blob to binary blob.
|
||||
int ConvertStringToBinary(
|
||||
const char * szString,
|
||||
size_t nMaxDigits,
|
||||
LPBYTE pbBinary)
|
||||
{
|
||||
const char * szStringEnd = szString + nMaxDigits;
|
||||
DWORD dwCounter = 0;
|
||||
BYTE DigitValue;
|
||||
BYTE ByteValue = 0;
|
||||
|
||||
// Convert the string
|
||||
while(szString < szStringEnd)
|
||||
{
|
||||
// Retrieve the digit converted to hexa
|
||||
DigitValue = (BYTE)(AsciiToUpperTable_BkSlash[szString[0]] - '0');
|
||||
if(DigitValue > 9)
|
||||
DigitValue -= 'A' - '9' - 1;
|
||||
if(DigitValue > 0x0F)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Insert the digit to the binary buffer
|
||||
ByteValue = (ByteValue << 0x04) | DigitValue;
|
||||
dwCounter++;
|
||||
|
||||
// If we reached the second digit, it means that we need
|
||||
// to flush the byte value and move on
|
||||
if((dwCounter & 0x01) == 0)
|
||||
*pbBinary++ = ByteValue;
|
||||
szString++;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer)
|
||||
{
|
||||
char * szSaveBuffer = szBuffer;
|
||||
@@ -308,6 +387,11 @@ char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer)
|
||||
return szSaveBuffer;
|
||||
}
|
||||
|
||||
char * StringFromMD5(LPBYTE md5, char * szBuffer)
|
||||
{
|
||||
return StringFromBinary(md5, MD5_HASH_SIZE, szBuffer);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File name utilities
|
||||
|
||||
@@ -341,87 +425,55 @@ const char * GetPlainFileName(const char * szFileName)
|
||||
|
||||
bool CheckWildCard(const char * szString, const char * szWildCard)
|
||||
{
|
||||
const char * szSubString;
|
||||
int nSubStringLength;
|
||||
int nMatchCount = 0;
|
||||
const char * szWildCardPtr;
|
||||
|
||||
// When the mask is empty, it never matches
|
||||
if(szWildCard == NULL || *szWildCard == 0)
|
||||
return false;
|
||||
|
||||
// If the wildcard contains just "*", then it always matches
|
||||
if(szWildCard[0] == '*' && szWildCard[1] == 0)
|
||||
return true;
|
||||
|
||||
// Do normal test
|
||||
for(;;)
|
||||
{
|
||||
// If there is '?' in the wildcard, we skip one char
|
||||
while(*szWildCard == '?')
|
||||
while(szWildCard[0] == '?')
|
||||
{
|
||||
if(szString[0] == 0)
|
||||
return false;
|
||||
|
||||
szWildCard++;
|
||||
szString++;
|
||||
}
|
||||
|
||||
// If there is '*', means zero or more chars. We have to
|
||||
// find the sequence after '*'
|
||||
if(*szWildCard == '*')
|
||||
// Handle '*'
|
||||
szWildCardPtr = szWildCard;
|
||||
if(szWildCardPtr[0] != 0)
|
||||
{
|
||||
// More stars is equal to one star
|
||||
while(*szWildCard == '*' || *szWildCard == '?')
|
||||
szWildCard++;
|
||||
|
||||
// If we found end of the wildcard, it's a match
|
||||
if(*szWildCard == 0)
|
||||
return true;
|
||||
|
||||
// Determine the length of the substring in szWildCard
|
||||
szSubString = szWildCard;
|
||||
while(*szSubString != 0 && *szSubString != '?' && *szSubString != '*')
|
||||
szSubString++;
|
||||
nSubStringLength = (int)(szSubString - szWildCard);
|
||||
nMatchCount = 0;
|
||||
|
||||
// Now we have to find a substring in szString,
|
||||
// that matches the substring in szWildCard
|
||||
while(*szString != 0)
|
||||
if(szWildCardPtr[0] == '*')
|
||||
{
|
||||
// Calculate match count
|
||||
while(nMatchCount < nSubStringLength)
|
||||
{
|
||||
if(AsciiToUpperTable_BkSlash[(BYTE)szString[nMatchCount]] != AsciiToUpperTable_BkSlash[(BYTE)szWildCard[nMatchCount]])
|
||||
break;
|
||||
if(szString[nMatchCount] == 0)
|
||||
break;
|
||||
nMatchCount++;
|
||||
}
|
||||
szWildCardPtr++;
|
||||
|
||||
// If the match count has reached substring length, we found a match
|
||||
if(nMatchCount == nSubStringLength)
|
||||
{
|
||||
szWildCard += nMatchCount;
|
||||
szString += nMatchCount;
|
||||
break;
|
||||
}
|
||||
if(szWildCardPtr[0] == '*')
|
||||
continue;
|
||||
|
||||
// No match, move to the next char in szString
|
||||
nMatchCount = 0;
|
||||
szString++;
|
||||
if(szWildCardPtr[0] == 0)
|
||||
return true;
|
||||
|
||||
if(AsciiToUpperTable_BkSlash[szWildCardPtr[0]] == AsciiToUpperTable_BkSlash[szString[0]])
|
||||
{
|
||||
if(CheckWildCard(szString, szWildCardPtr))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(AsciiToUpperTable_BkSlash[szWildCardPtr[0]] != AsciiToUpperTable_BkSlash[szString[0]])
|
||||
return false;
|
||||
|
||||
szWildCard = szWildCardPtr + 1;
|
||||
}
|
||||
|
||||
if(szString[0] == 0)
|
||||
return false;
|
||||
szString++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we came to the end of the string, compare it to the wildcard
|
||||
if(AsciiToUpperTable_BkSlash[(BYTE)*szString] != AsciiToUpperTable_BkSlash[(BYTE)*szWildCard])
|
||||
return false;
|
||||
|
||||
// If we arrived to the end of the string, it's a match
|
||||
if(*szString == 0)
|
||||
return true;
|
||||
|
||||
// Otherwise, continue in comparing
|
||||
szWildCard++;
|
||||
szString++;
|
||||
return (szString[0] == 0) ? true : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,14 +28,6 @@ extern unsigned char AsciiToLowerTable_Slash[256];
|
||||
extern unsigned char AsciiToUpperTable_BkSlash[256];
|
||||
extern unsigned char IntToHexChar[];
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GetLastError/SetLastError support for non-Windows platform
|
||||
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
int GetLastError();
|
||||
void SetLastError(int nError);
|
||||
#endif // PLATFORM_WINDOWS
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// String manipulation
|
||||
|
||||
@@ -43,19 +35,25 @@ void CopyString(char * szTarget, const char * szSource, size_t cchLength);
|
||||
void CopyString(wchar_t * szTarget, const char * szSource, size_t cchLength);
|
||||
void CopyString(char * szTarget, const wchar_t * szSource, size_t cchLength);
|
||||
|
||||
char * NewStr(const char * szString, size_t nCharsToReserve);
|
||||
wchar_t * NewStr(const wchar_t * szString, size_t nCharsToReserve);
|
||||
char * CascNewStr(const char * szString, size_t nCharsToReserve);
|
||||
wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve);
|
||||
|
||||
TCHAR * NewStrFromAnsi(LPBYTE pbStringBegin, LPBYTE pbStringEnd);
|
||||
TCHAR * CascNewStrFromAnsi(const char * szBegin, const char * szEnd);
|
||||
|
||||
TCHAR * CombinePath(const TCHAR * szPath, const TCHAR * szSubDir);
|
||||
TCHAR * CombinePathAndString(const TCHAR * szPath, const char * szString, size_t nLength);
|
||||
|
||||
void NormalizeFileName_UpperBkSlash(char * szTrgFileName, const char * szSrcFileName, size_t cchMaxChars);
|
||||
void NormalizeFileName_LowerSlash(char * szTrgFileName, const char * szSrcFileName, size_t cchMaxChars);
|
||||
size_t NormalizeFileName_UpperBkSlash(char * szNormName, const char * szFileName, size_t cchMaxChars);
|
||||
size_t NormalizeFileName_LowerSlash(char * szNormName, const char * szFileName, size_t cchMaxChars);
|
||||
|
||||
ULONGLONG CalcFileNameHash(const char * szFileName);
|
||||
|
||||
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);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File name utilities
|
||||
|
||||
29
dep/CascLib/src/common/Directory.h
Normal file
29
dep/CascLib/src/common/Directory.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*****************************************************************************/
|
||||
/* Directory.h Copyright (c) Ladislav Zezula 2015 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Directory functions for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 30.10.15 1.00 Lad The first version of Directory.h */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __DIRECTORY_H__
|
||||
#define __DIRECTORY_H__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Scanning a directory
|
||||
|
||||
typedef bool (*INDEX_FILE_FOUND)(const TCHAR * szFileName, PDWORD IndexArray, PDWORD OldIndexArray, void * pvContext);
|
||||
|
||||
bool DirectoryExists(const TCHAR * szDirectory);
|
||||
|
||||
int ScanIndexDirectory(
|
||||
const TCHAR * szIndexPath,
|
||||
INDEX_FILE_FOUND pfnOnFileFound,
|
||||
PDWORD IndexArray,
|
||||
PDWORD OldIndexArray,
|
||||
void * pvContext
|
||||
);
|
||||
|
||||
#endif // __DIRECTORY_H__
|
||||
153
dep/CascLib/src/common/DumpContext.cpp
Normal file
153
dep/CascLib/src/common/DumpContext.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
/*****************************************************************************/
|
||||
/* DumpContext.cpp Copyright (c) Ladislav Zezula 2015 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Implementation of dump context */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 16.03.15 1.00 Lad Created */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "../CascLib.h"
|
||||
#include "../CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static TCHAR * FormatFileName(TCascStorage * hs, const TCHAR * szNameFormat)
|
||||
{
|
||||
TCHAR * szFileName;
|
||||
TCHAR * szSrc;
|
||||
TCHAR * szTrg;
|
||||
|
||||
// Create copy of the file name
|
||||
szFileName = szSrc = szTrg = CascNewStr(szNameFormat, 0);
|
||||
if(szFileName != NULL)
|
||||
{
|
||||
// Format the file name
|
||||
while(szSrc[0] != 0)
|
||||
{
|
||||
if(szSrc[0] == _T('%'))
|
||||
{
|
||||
// Replace "%build%" with a build number
|
||||
if(!_tcsncmp(szSrc, _T("%build%"), 7))
|
||||
{
|
||||
szTrg += _stprintf(szTrg, _T("%u"), hs->dwBuildNumber);
|
||||
szSrc += 7;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Just copy the character
|
||||
*szTrg++ = *szSrc++;
|
||||
}
|
||||
|
||||
// Terminate the target file name
|
||||
szTrg[0] = 0;
|
||||
}
|
||||
|
||||
return szFileName;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
TDumpContext * CreateDumpContext(TCascStorage * hs, const TCHAR * szNameFormat)
|
||||
{
|
||||
TDumpContext * dc;
|
||||
TCHAR * szFileName;
|
||||
|
||||
// Validate the storage handle
|
||||
if(hs != NULL)
|
||||
{
|
||||
// Calculate the number of bytes needed for dump context
|
||||
dc = CASC_ALLOC(TDumpContext, 1);
|
||||
if(dc != NULL)
|
||||
{
|
||||
// Initialize the dump context
|
||||
memset(dc, 0, sizeof(TDumpContext));
|
||||
|
||||
// Format the real file name
|
||||
szFileName = FormatFileName(hs, szNameFormat);
|
||||
if(szFileName != NULL)
|
||||
{
|
||||
// Create the file
|
||||
dc->pStream = FileStream_CreateFile(szFileName, 0);
|
||||
if(dc->pStream != NULL)
|
||||
{
|
||||
// Initialize buffers
|
||||
dc->pbBufferBegin =
|
||||
dc->pbBufferPtr = dc->DumpBuffer;
|
||||
dc->pbBufferEnd = dc->DumpBuffer + CASC_DUMP_BUFFER_SIZE;
|
||||
|
||||
// Success
|
||||
CASC_FREE(szFileName);
|
||||
return dc;
|
||||
}
|
||||
|
||||
// File create failed --> delete the file name
|
||||
CASC_FREE(szFileName);
|
||||
}
|
||||
|
||||
// Free the dump context
|
||||
CASC_FREE(dc);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dump_print(TDumpContext * dc, const char * szFormat, ...)
|
||||
{
|
||||
va_list argList;
|
||||
char szBuffer[0x200];
|
||||
int nLength = 0;
|
||||
|
||||
// Only if the dump context is valid
|
||||
if(dc != NULL)
|
||||
{
|
||||
// Print the buffer using sprintf
|
||||
va_start(argList, szFormat);
|
||||
nLength = vsprintf(szBuffer, szFormat, argList);
|
||||
assert(nLength < 0x200);
|
||||
va_end(argList);
|
||||
|
||||
// Copy the string. Replace "\n" with "\n\r"
|
||||
for(int i = 0; i < nLength; i++)
|
||||
{
|
||||
// Flush the buffer, if needed
|
||||
if((dc->pbBufferPtr + 2) >= dc->pbBufferEnd)
|
||||
{
|
||||
FileStream_Write(dc->pStream, NULL, dc->pbBufferBegin, (DWORD)(dc->pbBufferPtr - dc->pbBufferBegin));
|
||||
dc->pbBufferPtr = dc->pbBufferBegin;
|
||||
}
|
||||
|
||||
// Copy the char
|
||||
if(szBuffer[i] == 0x0A)
|
||||
*dc->pbBufferPtr++ = 0x0D;
|
||||
*dc->pbBufferPtr++ = szBuffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
return nLength;
|
||||
}
|
||||
|
||||
int dump_close(TDumpContext * dc)
|
||||
{
|
||||
// Only if the dump context is valid
|
||||
if(dc != NULL)
|
||||
{
|
||||
// Flush the dump context if there are some data
|
||||
if(dc->pbBufferPtr > dc->pbBufferBegin)
|
||||
FileStream_Write(dc->pStream, NULL, dc->pbBufferBegin, (DWORD)(dc->pbBufferPtr - dc->pbBufferBegin));
|
||||
dc->pbBufferPtr = dc->pbBufferBegin;
|
||||
|
||||
// Free the file stream and the entire context
|
||||
if(dc->pStream != NULL)
|
||||
FileStream_Close(dc->pStream);
|
||||
CASC_FREE(dc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
38
dep/CascLib/src/common/DumpContext.h
Normal file
38
dep/CascLib/src/common/DumpContext.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*****************************************************************************/
|
||||
/* DumpContext.h Copyright (c) Ladislav Zezula 2015 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Interface for TDumpContext */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 16.03.15 1.00 Lad Created */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __DUMP_CONTEXT_H__
|
||||
#define __DUMP_CONTEXT_H__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Defines
|
||||
|
||||
// Size of the buffer for the dump context
|
||||
#define CASC_DUMP_BUFFER_SIZE 0x10000
|
||||
|
||||
// Structure for dump context
|
||||
struct TDumpContext
|
||||
{
|
||||
TFileStream * pStream; // Pointer to the open stream
|
||||
LPBYTE pbBufferBegin; // Begin of the dump buffer
|
||||
LPBYTE pbBufferPtr; // Current dump buffer pointer
|
||||
LPBYTE pbBufferEnd; // End of the dump buffer
|
||||
|
||||
BYTE DumpBuffer[CASC_DUMP_BUFFER_SIZE]; // Dump buffer
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Dump context functions
|
||||
|
||||
TDumpContext * CreateDumpContext(struct _TCascStorage * hs, const TCHAR * szNameFormat);
|
||||
int dump_print(TDumpContext * dc, const char * szFormat, ...);
|
||||
int dump_close(TDumpContext * dc);
|
||||
|
||||
#endif // __DUMP_CONTEXT_H__
|
||||
101
dep/CascLib/src/common/DynamicArray.cpp
Normal file
101
dep/CascLib/src/common/DynamicArray.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/*****************************************************************************/
|
||||
/* DynamicArray.cpp Copyright (c) Ladislav Zezula 2015 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Description: */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 30.10.15 1.00 Lad The first version of DynamicArray.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "../CascLib.h"
|
||||
#include "../CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static bool EnlargeArray(PDYNAMIC_ARRAY pArray, size_t NewItemCount)
|
||||
{
|
||||
char * NewItemArray;
|
||||
size_t ItemCountMax;
|
||||
|
||||
// We expect the array to be already allocated
|
||||
assert(pArray->ItemArray != NULL);
|
||||
assert(pArray->ItemCountMax != 0);
|
||||
|
||||
// Shall we enlarge the table?
|
||||
if(NewItemCount > pArray->ItemCountMax)
|
||||
{
|
||||
// Calculate new table size
|
||||
ItemCountMax = pArray->ItemCountMax;
|
||||
while(ItemCountMax < NewItemCount)
|
||||
ItemCountMax = ItemCountMax << 1;
|
||||
|
||||
// Allocate new table
|
||||
NewItemArray = CASC_REALLOC(char, pArray->ItemArray, pArray->ItemSize * ItemCountMax);
|
||||
if(NewItemArray == NULL)
|
||||
return false;
|
||||
|
||||
// Set the new table size
|
||||
pArray->ItemCountMax = ItemCountMax;
|
||||
pArray->ItemArray = NewItemArray;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int Array_Create_(PDYNAMIC_ARRAY pArray, size_t ItemSize, size_t ItemCountMax)
|
||||
{
|
||||
pArray->ItemArray = CASC_ALLOC(char, (ItemSize * ItemCountMax));
|
||||
if(pArray->ItemArray == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
pArray->ItemCountMax = ItemCountMax;
|
||||
pArray->ItemCount = 0;
|
||||
pArray->ItemSize = ItemSize;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
void * Array_Insert(PDYNAMIC_ARRAY pArray, const void * NewItems, size_t NewItemCount)
|
||||
{
|
||||
char * NewItemPtr;
|
||||
|
||||
// Try to enlarge the buffer, if needed
|
||||
if(!EnlargeArray(pArray, pArray->ItemCount + NewItemCount))
|
||||
return NULL;
|
||||
NewItemPtr = pArray->ItemArray + (pArray->ItemCount * pArray->ItemSize);
|
||||
|
||||
// Copy the old item(s), if any
|
||||
if(NewItems != NULL)
|
||||
memcpy(NewItemPtr, NewItems, (NewItemCount * pArray->ItemSize));
|
||||
|
||||
// Increment the size of the array
|
||||
pArray->ItemCount += NewItemCount;
|
||||
return NewItemPtr;
|
||||
}
|
||||
|
||||
void * Array_ItemAt(PDYNAMIC_ARRAY pArray, size_t ItemIndex)
|
||||
{
|
||||
assert(ItemIndex < pArray->ItemCount);
|
||||
return pArray->ItemArray + (ItemIndex * pArray->ItemSize);
|
||||
}
|
||||
|
||||
size_t Array_IndexOf(PDYNAMIC_ARRAY pArray, const void * ArrayPtr)
|
||||
{
|
||||
char * ArrayItem = (char *)ArrayPtr;
|
||||
|
||||
assert(pArray->ItemArray <= ArrayItem && ArrayItem <= pArray->ItemArray + (pArray->ItemCount * pArray->ItemSize));
|
||||
return ((ArrayItem - pArray->ItemArray) / pArray->ItemSize);
|
||||
}
|
||||
|
||||
void Array_Free(PDYNAMIC_ARRAY pArray)
|
||||
{
|
||||
if(pArray != NULL && pArray->ItemArray != NULL)
|
||||
{
|
||||
CASC_FREE(pArray->ItemArray);
|
||||
}
|
||||
}
|
||||
37
dep/CascLib/src/common/DynamicArray.h
Normal file
37
dep/CascLib/src/common/DynamicArray.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*****************************************************************************/
|
||||
/* DynamicArray.h Copyright (c) Ladislav Zezula 2015 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Common array implementation */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 30.10.15 1.00 Lad The first version of DynamicArray.h */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __DYNAMIC_ARRAY_H__
|
||||
#define __DYNAMIC_ARRAY_H__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structures
|
||||
|
||||
typedef struct _DYNAMIC_ARRAY
|
||||
{
|
||||
char * ItemArray; // Pointer to items
|
||||
size_t ItemCountMax; // Current number of items
|
||||
size_t ItemCount; // Total size of the buffer
|
||||
size_t ItemSize; // Size of the single item
|
||||
|
||||
} DYNAMIC_ARRAY, *PDYNAMIC_ARRAY;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions for managing the array
|
||||
|
||||
int Array_Create_(PDYNAMIC_ARRAY pArray, size_t ItemSize, size_t ItemCountMax);
|
||||
void * Array_Insert(PDYNAMIC_ARRAY pArray, const void * NewItems, size_t NewItemCount);
|
||||
void * Array_ItemAt(PDYNAMIC_ARRAY pArray, size_t ItemIndex);
|
||||
size_t Array_IndexOf(PDYNAMIC_ARRAY pArray, const void * ArrayPtr);
|
||||
void Array_Free(PDYNAMIC_ARRAY pArray);
|
||||
|
||||
#define Array_Create(pArray, type, ItemCountMax) Array_Create_(pArray, sizeof(type), ItemCountMax)
|
||||
|
||||
#endif // __DYNAMIC_ARRAY__
|
||||
@@ -16,7 +16,6 @@
|
||||
#define __CASCLIB_SELF__
|
||||
#include "../CascLib.h"
|
||||
#include "../CascCommon.h"
|
||||
#include "FileStream.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "wininet.lib") // Internet functions for HTTP stream
|
||||
|
||||
@@ -13,176 +13,33 @@
|
||||
#include "../CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Listfile entry structure
|
||||
// Listfile cache structure
|
||||
|
||||
#define CACHE_BUFFER_SIZE 0x1000 // Size of the cache buffer
|
||||
|
||||
typedef bool (*RELOAD_CACHE)(void * pvCacheContext, LPBYTE pbBuffer, DWORD dwBytesToRead);
|
||||
typedef void (*CLOSE_STREAM)(void * pvCacheContext);
|
||||
|
||||
struct TListFileCache
|
||||
typedef struct _LISTFILE_CACHE
|
||||
{
|
||||
RELOAD_CACHE pfnReloadCache; // Function for reloading the cache
|
||||
CLOSE_STREAM pfnCloseStream; // Function for closing the stream
|
||||
void * pvCacheContext; // Reload context passed to reload function
|
||||
char * szMask; // Self-relative pointer to file mask
|
||||
DWORD dwFileSize; // Total size of the cached file
|
||||
DWORD dwFilePos; // Position of the cache in the file
|
||||
BYTE * pBegin; // The begin of the listfile cache
|
||||
BYTE * pPos;
|
||||
BYTE * pEnd; // The last character in the file cache
|
||||
char * pBegin; // The begin of the listfile cache
|
||||
char * pPos; // Current position in the cache
|
||||
char * pEnd; // The last character in the file cache
|
||||
|
||||
BYTE Buffer[CACHE_BUFFER_SIZE];
|
||||
// char MaskBuff[1] // Followed by the name mask (if any)
|
||||
};
|
||||
// Followed by the cache (variable length)
|
||||
|
||||
} LISTFILE_CACHE, *PLISTFILE_CACHE;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
// Creating the listfile cache for the given amount of data
|
||||
|
||||
static bool ReloadCache_ExternalFile(void * pvCacheContext, LPBYTE pbBuffer, DWORD dwBytesToRead)
|
||||
static PLISTFILE_CACHE CreateListFileCache(DWORD dwFileSize)
|
||||
{
|
||||
TFileStream * pStream = (TFileStream *)pvCacheContext;
|
||||
|
||||
return FileStream_Read(pStream, NULL, pbBuffer, dwBytesToRead);
|
||||
}
|
||||
|
||||
static void CloseStream_ExternalFile(void * pvCacheContext)
|
||||
{
|
||||
TFileStream * pStream = (TFileStream *)pvCacheContext;
|
||||
|
||||
return FileStream_Close(pStream);
|
||||
}
|
||||
|
||||
|
||||
// Reloads the cache. Returns number of characters
|
||||
// that has been loaded into the cache.
|
||||
static DWORD ReloadListFileCache(TListFileCache * pCache)
|
||||
{
|
||||
DWORD dwBytesToRead = 0;
|
||||
|
||||
// Only do something if the cache is empty
|
||||
if(pCache->pPos >= pCache->pEnd)
|
||||
{
|
||||
// Move the file position forward
|
||||
pCache->dwFilePos += CACHE_BUFFER_SIZE;
|
||||
if(pCache->dwFilePos >= pCache->dwFileSize)
|
||||
return 0;
|
||||
|
||||
// Get the number of bytes remaining
|
||||
dwBytesToRead = pCache->dwFileSize - pCache->dwFilePos;
|
||||
if(dwBytesToRead > CACHE_BUFFER_SIZE)
|
||||
dwBytesToRead = CACHE_BUFFER_SIZE;
|
||||
|
||||
// Load the next data chunk to the cache
|
||||
// If we didn't read anything, it might mean that the block
|
||||
// of the file is not available
|
||||
// We stop reading the file at this point, because the rest
|
||||
// of the listfile is unreliable
|
||||
if(!pCache->pfnReloadCache(pCache->pvCacheContext, pCache->Buffer, dwBytesToRead))
|
||||
return 0;
|
||||
|
||||
// Set the buffer pointers
|
||||
pCache->pBegin =
|
||||
pCache->pPos = &pCache->Buffer[0];
|
||||
pCache->pEnd = pCache->pBegin + dwBytesToRead;
|
||||
}
|
||||
|
||||
return dwBytesToRead;
|
||||
}
|
||||
|
||||
static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, size_t nMaxChars)
|
||||
{
|
||||
char * szLineBegin = szLine;
|
||||
char * szLineEnd = szLine + nMaxChars - 1;
|
||||
char * szExtraString = NULL;
|
||||
|
||||
// Skip newlines, spaces, tabs and another non-printable stuff
|
||||
for(;;)
|
||||
{
|
||||
// If we need to reload the cache, do it
|
||||
if(pCache->pPos == pCache->pEnd)
|
||||
{
|
||||
if(ReloadListFileCache(pCache) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// If we found a non-whitespace character, stop
|
||||
if(pCache->pPos[0] > 0x20)
|
||||
break;
|
||||
|
||||
// Skip the character
|
||||
pCache->pPos++;
|
||||
}
|
||||
|
||||
// Copy the remaining characters
|
||||
while(szLine < szLineEnd)
|
||||
{
|
||||
// If we need to reload the cache, do it now and resume copying
|
||||
if(pCache->pPos == pCache->pEnd)
|
||||
{
|
||||
if(ReloadListFileCache(pCache) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// If we have found a newline, stop loading
|
||||
if(pCache->pPos[0] == 0x0D || pCache->pPos[0] == 0x0A)
|
||||
break;
|
||||
|
||||
// Blizzard listfiles can also contain information about patch:
|
||||
// Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326)
|
||||
if(pCache->pPos[0] == '~')
|
||||
szExtraString = szLine;
|
||||
|
||||
// Copy the character
|
||||
*szLine++ = *pCache->pPos++;
|
||||
}
|
||||
|
||||
// Terminate line with zero
|
||||
*szLine = 0;
|
||||
|
||||
// If there was extra string after the file name, clear it
|
||||
if(szExtraString != NULL)
|
||||
{
|
||||
if(szExtraString[0] == '~' && szExtraString[1] == 'P')
|
||||
{
|
||||
szLine = szExtraString;
|
||||
*szExtraString = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the length of the line
|
||||
return (szLine - szLineBegin);
|
||||
}
|
||||
|
||||
static TListFileCache * CreateListFileCache(RELOAD_CACHE pfnReloadCache, CLOSE_STREAM pfnCloseStream, void * pvCacheContext, DWORD dwFileSize)
|
||||
{
|
||||
TListFileCache * pCache = NULL;
|
||||
DWORD dwBytesToRead;
|
||||
PLISTFILE_CACHE pCache;
|
||||
|
||||
// Allocate cache for one file block
|
||||
pCache = (TListFileCache *)CASC_ALLOC(BYTE, sizeof(TListFileCache));
|
||||
pCache = (PLISTFILE_CACHE)CASC_ALLOC(BYTE, sizeof(LISTFILE_CACHE) + dwFileSize);
|
||||
if(pCache != NULL)
|
||||
{
|
||||
// Clear the entire structure
|
||||
memset(pCache, 0, sizeof(TListFileCache));
|
||||
pCache->pfnReloadCache = pfnReloadCache;
|
||||
pCache->pfnCloseStream = pfnCloseStream;
|
||||
pCache->pvCacheContext = pvCacheContext;
|
||||
pCache->dwFileSize = dwFileSize;
|
||||
|
||||
// Load the file cache from the file
|
||||
dwBytesToRead = CASCLIB_MIN(CACHE_BUFFER_SIZE, dwFileSize);
|
||||
if(pfnReloadCache(pvCacheContext, pCache->Buffer, dwBytesToRead))
|
||||
{
|
||||
// Allocate pointers
|
||||
pCache->pBegin = pCache->pPos = &pCache->Buffer[0];
|
||||
pCache->pEnd = pCache->pBegin + dwBytesToRead;
|
||||
}
|
||||
else
|
||||
{
|
||||
ListFile_Free(pCache);
|
||||
pCache = NULL;
|
||||
}
|
||||
// Set the initial pointers
|
||||
pCache->pBegin =
|
||||
pCache->pPos = (char *)(pCache + 1);
|
||||
pCache->pEnd = pCache->pBegin + dwFileSize;
|
||||
}
|
||||
|
||||
// Return the cache
|
||||
@@ -194,7 +51,7 @@ static TListFileCache * CreateListFileCache(RELOAD_CACHE pfnReloadCache, CLOSE_S
|
||||
|
||||
void * ListFile_OpenExternal(const TCHAR * szListFile)
|
||||
{
|
||||
TListFileCache * pCache;
|
||||
PLISTFILE_CACHE pCache = NULL;
|
||||
TFileStream * pStream;
|
||||
ULONGLONG FileSize = 0;
|
||||
|
||||
@@ -204,46 +61,135 @@ void * ListFile_OpenExternal(const TCHAR * szListFile)
|
||||
{
|
||||
// Retrieve the size of the external listfile
|
||||
FileStream_GetSize(pStream, &FileSize);
|
||||
if(0 < FileSize && FileSize <= 0xFFFFFFFF)
|
||||
{
|
||||
// Create the cache for the listfile
|
||||
pCache = CreateListFileCache(ReloadCache_ExternalFile, CloseStream_ExternalFile, pStream, (DWORD)FileSize);
|
||||
if(0 < FileSize && FileSize <= 0x30000000)
|
||||
{
|
||||
// Create the in-memory cache for the entire listfile
|
||||
// The listfile does not have any data loaded yet
|
||||
pCache = CreateListFileCache((DWORD)FileSize);
|
||||
if(pCache != NULL)
|
||||
return pCache;
|
||||
{
|
||||
if(!FileStream_Read(pStream, NULL, pCache->pBegin, (DWORD)FileSize))
|
||||
{
|
||||
ListFile_Free(pCache);
|
||||
pCache = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the file stream
|
||||
FileStream_Close(pStream);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return pCache;
|
||||
}
|
||||
|
||||
void * ListFile_FromBuffer(LPBYTE pbBuffer, DWORD cbBuffer)
|
||||
{
|
||||
PLISTFILE_CACHE pCache = NULL;
|
||||
|
||||
// Create the in-memory cache for the entire listfile
|
||||
// The listfile does not have any data loaded yet
|
||||
pCache = CreateListFileCache(cbBuffer);
|
||||
if(pCache != NULL)
|
||||
memcpy(pCache->pBegin, pbBuffer, cbBuffer);
|
||||
|
||||
return pCache;
|
||||
}
|
||||
|
||||
// Performs the MD5-based check on the listfile
|
||||
bool ListFile_VerifyMD5(void * pvListFile, LPBYTE pbHashMD5)
|
||||
{
|
||||
PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile;
|
||||
|
||||
// Must be at the beginning
|
||||
assert(pCache->pPos == pCache->pBegin);
|
||||
|
||||
// Verify the MD5 hash for the entire block
|
||||
return VerifyDataBlockHash(pCache->pBegin, (DWORD)(pCache->pEnd - pCache->pBegin), pbHashMD5);
|
||||
}
|
||||
|
||||
size_t ListFile_GetNextLine(void * pvListFile, const char ** pszLineBegin, const char ** pszLineEnd)
|
||||
{
|
||||
PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile;
|
||||
char * szExtraString = NULL;
|
||||
char * szLineBegin;
|
||||
char * szLineEnd;
|
||||
|
||||
// Skip newlines, spaces, tabs and another non-printable stuff
|
||||
while(pCache->pPos < pCache->pEnd && pCache->pPos[0] <= 0x20)
|
||||
pCache->pPos++;
|
||||
|
||||
// Remember the begin of the line
|
||||
szLineBegin = pCache->pPos;
|
||||
|
||||
// Copy the remaining characters
|
||||
while(pCache->pPos < pCache->pEnd)
|
||||
{
|
||||
// If we have found a newline, stop loading
|
||||
if(pCache->pPos[0] == 0x0D || pCache->pPos[0] == 0x0A)
|
||||
break;
|
||||
|
||||
// Blizzard listfiles can also contain information about patch:
|
||||
// Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326)
|
||||
if(pCache->pPos[0] == '~')
|
||||
szExtraString = pCache->pPos;
|
||||
|
||||
// Move the position by one character forward
|
||||
pCache->pPos++;
|
||||
}
|
||||
|
||||
// Remember the end of the line
|
||||
szLineEnd = (szExtraString != NULL && szExtraString[0] == '~' && szExtraString[1] == 'P') ? szExtraString : pCache->pPos;
|
||||
|
||||
// Give the caller the positions of the begin and end of the line
|
||||
pszLineBegin[0] = szLineBegin;
|
||||
pszLineEnd[0] = szLineEnd;
|
||||
return (size_t)(szLineEnd - szLineBegin);
|
||||
}
|
||||
|
||||
size_t ListFile_GetNextLine(void * pvListFile, char * szBuffer, size_t nMaxChars)
|
||||
{
|
||||
const char * szLineBegin = NULL;
|
||||
const char * szLineEnd = NULL;
|
||||
size_t nLength;
|
||||
|
||||
// Retrieve the next line
|
||||
nLength = ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd);
|
||||
|
||||
// Check the length
|
||||
if(nLength > nMaxChars)
|
||||
{
|
||||
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy the line to the user buffer
|
||||
memcpy(szBuffer, szLineBegin, nLength);
|
||||
szBuffer[nLength] = 0;
|
||||
return nLength;
|
||||
}
|
||||
|
||||
size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer, size_t nMaxChars)
|
||||
{
|
||||
TListFileCache * pCache = (TListFileCache *)pvListFile;
|
||||
size_t nLength = 0;
|
||||
int nError = ERROR_INVALID_PARAMETER;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Check for parameters
|
||||
if(pCache != NULL)
|
||||
for(;;)
|
||||
{
|
||||
for(;;)
|
||||
// Read the (next) line
|
||||
nLength = ListFile_GetNextLine(pvListFile, szBuffer, nMaxChars);
|
||||
if(nLength == 0)
|
||||
{
|
||||
// Read the (next) line
|
||||
nLength = ReadListFileLine(pCache, szBuffer, nMaxChars);
|
||||
if(nLength == 0)
|
||||
{
|
||||
nError = ERROR_NO_MORE_FILES;
|
||||
break;
|
||||
}
|
||||
nError = ERROR_NO_MORE_FILES;
|
||||
break;
|
||||
}
|
||||
|
||||
// If some mask entered, check it
|
||||
if(CheckWildCard(szBuffer, szMask))
|
||||
{
|
||||
nError = ERROR_SUCCESS;
|
||||
break;
|
||||
}
|
||||
// If some mask entered, check it
|
||||
if(CheckWildCard(szBuffer, szMask))
|
||||
{
|
||||
nError = ERROR_SUCCESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,14 +200,9 @@ size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer,
|
||||
|
||||
void ListFile_Free(void * pvListFile)
|
||||
{
|
||||
TListFileCache * pCache = (TListFileCache *)pvListFile;
|
||||
|
||||
// Valid parameter check
|
||||
if(pCache != NULL)
|
||||
if(pvListFile != NULL)
|
||||
{
|
||||
if(pCache->pfnCloseStream != NULL)
|
||||
pCache->pfnCloseStream(pCache->pvCacheContext);
|
||||
CASC_FREE(pCache);
|
||||
CASC_FREE(pvListFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,11 +234,8 @@ static PLISTFILE_MAP ListMap_Create()
|
||||
static PLISTFILE_MAP ListMap_InsertName(PLISTFILE_MAP pListMap, const char * szFileName, size_t nLength)
|
||||
{
|
||||
PLISTFILE_ENTRY pListEntry;
|
||||
char szFileName2[MAX_PATH+1];
|
||||
size_t cbToAllocate;
|
||||
size_t cbEntrySize;
|
||||
uint32_t dwHashHigh = 0;
|
||||
uint32_t dwHashLow = 0;
|
||||
|
||||
// Make sure there is enough space in the list map
|
||||
cbEntrySize = sizeof(LISTFILE_ENTRY) + nLength;
|
||||
@@ -314,14 +252,10 @@ static PLISTFILE_MAP ListMap_InsertName(PLISTFILE_MAP pListMap, const char * szF
|
||||
|
||||
// Get the pointer to the first entry
|
||||
pListEntry = (PLISTFILE_ENTRY)((LPBYTE)(pListMap + 1) + pListMap->cbBuffer);
|
||||
|
||||
// Get the name hash
|
||||
NormalizeFileName_UpperBkSlash(szFileName2, szFileName, MAX_PATH);
|
||||
hashlittle2(szFileName2, nLength, &dwHashHigh, &dwHashLow);
|
||||
|
||||
// Calculate the HASH value of the normalized file name
|
||||
pListEntry->FileNameHash = ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow;
|
||||
pListEntry->FileNameHash = CalcFileNameHash(szFileName);
|
||||
pListEntry->cbEntrySize = (DWORD)cbEntrySize;
|
||||
|
||||
// Copy the file name to the entry
|
||||
memcpy(pListEntry->szFileName, szFileName, nLength);
|
||||
pListEntry->szFileName[nLength] = 0;
|
||||
|
||||
@@ -357,7 +291,7 @@ static PLISTFILE_MAP ListMap_Finish(PLISTFILE_MAP pListMap)
|
||||
pbEntry += pListEntry->cbEntrySize;
|
||||
|
||||
// Insert the entry to the map
|
||||
Map_InsertObject(pMap, pListEntry);
|
||||
Map_InsertObject(pMap, pListEntry, &pListEntry->FileNameHash);
|
||||
}
|
||||
|
||||
return pListMap;
|
||||
@@ -408,7 +342,7 @@ const char * ListFile_FindName(PLISTFILE_MAP pListMap, ULONGLONG FileNameHash)
|
||||
PLISTFILE_ENTRY pListEntry = NULL;
|
||||
|
||||
if(pListMap != NULL)
|
||||
pListEntry = (PLISTFILE_ENTRY)Map_FindObject(pListMap->pNameMap, &FileNameHash);
|
||||
pListEntry = (PLISTFILE_ENTRY)Map_FindObject(pListMap->pNameMap, &FileNameHash, NULL);
|
||||
return (pListEntry != NULL) ? pListEntry->szFileName : "";
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,10 @@ typedef struct _LISTFILE_MAP
|
||||
// Functions for parsing an external listfile
|
||||
|
||||
void * ListFile_OpenExternal(const TCHAR * szListFile);
|
||||
void * ListFile_FromBuffer(LPBYTE pbBuffer, DWORD cbBuffer);
|
||||
bool ListFile_VerifyMD5(void * pvListFile, LPBYTE pbHashMD5);
|
||||
size_t ListFile_GetNextLine(void * pvListFile, const char ** pszLineBegin, const char ** pszLineEnd);
|
||||
size_t ListFile_GetNextLine(void * pvListFile, char * szBuffer, size_t nMaxChars);
|
||||
size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer, size_t nMaxChars);
|
||||
void ListFile_Free(void * pvListFile);
|
||||
|
||||
|
||||
@@ -15,51 +15,82 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static DWORD CalcHashIndex(PCASC_MAP pMap, void * pvIdentifier)
|
||||
// Returns the extension, right after "."
|
||||
static const char * String_GetExtension(const char * szString)
|
||||
{
|
||||
const char * szExtension = strrchr(szString, '.');
|
||||
return (szExtension != NULL) ? szExtension + 1 : NULL;
|
||||
}
|
||||
|
||||
static DWORD CalcHashIndex_Key(PCASC_MAP pMap, void * pvKey)
|
||||
{
|
||||
LPBYTE pbKey = (LPBYTE)pvKey;
|
||||
DWORD dwHash = 0x7EEE7EEE;
|
||||
|
||||
// Is it a string table?
|
||||
if(pMap->KeyLength == KEY_LENGTH_STRING)
|
||||
{
|
||||
char * szString = (char *)pvIdentifier;
|
||||
// Construct the hash from the first 8 digits
|
||||
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[0];
|
||||
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[1];
|
||||
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[2];
|
||||
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[3];
|
||||
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[4];
|
||||
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[5];
|
||||
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[6];
|
||||
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[7];
|
||||
|
||||
for(size_t i = 0; szString[i] != 0; i++)
|
||||
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ szString[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
LPBYTE pbHash = (LPBYTE)pvIdentifier;
|
||||
// Return the hash limited by the table size
|
||||
return (dwHash % pMap->TableSize);
|
||||
}
|
||||
|
||||
// Construct the hash from the first 4 digits
|
||||
for(size_t i = 0; i < pMap->KeyLength; i++)
|
||||
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbHash[i];
|
||||
static DWORD CalcHashIndex_String(PCASC_MAP pMap, const char * szString, const char * szStringEnd)
|
||||
{
|
||||
LPBYTE pbKeyEnd = (LPBYTE)szStringEnd;
|
||||
LPBYTE pbKey = (LPBYTE)szString;
|
||||
DWORD dwHash = 0x7EEE7EEE;
|
||||
|
||||
// Hash the string itself
|
||||
while(pbKey < pbKeyEnd)
|
||||
{
|
||||
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ AsciiToUpperTable_BkSlash[pbKey[0]];
|
||||
pbKey++;
|
||||
}
|
||||
|
||||
// Return the hash limited by the table size
|
||||
return (dwHash % pMap->TableSize);
|
||||
}
|
||||
|
||||
static bool CompareIdentifier(PCASC_MAP pMap, void * pvTableEntry, void * pvIdentifier)
|
||||
static bool CompareObject_Key(PCASC_MAP pMap, void * pvObject, void * pvKey)
|
||||
{
|
||||
// Is it a string table?
|
||||
if(pMap->KeyLength == KEY_LENGTH_STRING)
|
||||
{
|
||||
char * szTableEntry = (char *)pvTableEntry;
|
||||
char * szIdentifier = (char *)pvIdentifier;
|
||||
LPBYTE pbObjectKey = (LPBYTE)pvObject + pMap->KeyOffset;
|
||||
|
||||
return (strcmp(szTableEntry, szIdentifier) == 0);
|
||||
}
|
||||
else
|
||||
return (memcmp(pbObjectKey, pvKey, pMap->KeyLength) == 0);
|
||||
}
|
||||
|
||||
static bool CompareObject_String(
|
||||
PCASC_MAP pMap,
|
||||
const char * szExistingString,
|
||||
const char * szString,
|
||||
const char * szStringEnd)
|
||||
{
|
||||
// Keep compiler happy
|
||||
CASCLIB_UNUSED(pMap);
|
||||
|
||||
// Compare the whole part, case insensitive
|
||||
while(szString < szStringEnd)
|
||||
{
|
||||
return (memcmp(pvTableEntry, pvIdentifier, pMap->KeyLength) == 0);
|
||||
if(AsciiToUpperTable_BkSlash[*szExistingString] != AsciiToUpperTable_BkSlash[*szString])
|
||||
return false;
|
||||
|
||||
szExistingString++;
|
||||
szString++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwMemberOffset)
|
||||
PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwKeyOffset)
|
||||
{
|
||||
PCASC_MAP pMap;
|
||||
size_t cbToAllocate;
|
||||
@@ -76,7 +107,7 @@ PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwMemberOffset)
|
||||
memset(pMap, 0, cbToAllocate);
|
||||
pMap->KeyLength = dwKeyLength;
|
||||
pMap->TableSize = dwTableSize;
|
||||
pMap->MemberOffset = dwMemberOffset;
|
||||
pMap->KeyOffset = dwKeyOffset;
|
||||
}
|
||||
|
||||
// Return the allocated map
|
||||
@@ -104,24 +135,28 @@ size_t Map_EnumObjects(PCASC_MAP pMap, void **ppvArray)
|
||||
return pMap->ItemCount;
|
||||
}
|
||||
|
||||
void * Map_FindObject(PCASC_MAP pMap, void * pvIdentifier)
|
||||
void * Map_FindObject(PCASC_MAP pMap, void * pvKey, PDWORD PtrIndex)
|
||||
{
|
||||
void * pvTableEntry;
|
||||
void * pvObject;
|
||||
DWORD dwHashIndex;
|
||||
|
||||
// Verify pointer to the map
|
||||
if(pMap != NULL)
|
||||
{
|
||||
// Construct the main index
|
||||
dwHashIndex = CalcHashIndex(pMap, pvIdentifier);
|
||||
dwHashIndex = CalcHashIndex_Key(pMap, pvKey);
|
||||
while(pMap->HashTable[dwHashIndex] != NULL)
|
||||
{
|
||||
// Get the pointer at that position
|
||||
pvTableEntry = pMap->HashTable[dwHashIndex];
|
||||
pvObject = pMap->HashTable[dwHashIndex];
|
||||
|
||||
// Compare the hash
|
||||
if(CompareIdentifier(pMap, pvTableEntry, pvIdentifier))
|
||||
return ((LPBYTE)pvTableEntry - pMap->MemberOffset);
|
||||
if(CompareObject_Key(pMap, pvObject, pvKey))
|
||||
{
|
||||
if(PtrIndex != NULL)
|
||||
PtrIndex[0] = dwHashIndex;
|
||||
return pvObject;
|
||||
}
|
||||
|
||||
// Move to the next entry
|
||||
dwHashIndex = (dwHashIndex + 1) % pMap->TableSize;
|
||||
@@ -132,9 +167,9 @@ void * Map_FindObject(PCASC_MAP pMap, void * pvIdentifier)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier)
|
||||
bool Map_InsertObject(PCASC_MAP pMap, void * pvNewObject, void * pvKey)
|
||||
{
|
||||
void * pvTableEntry;
|
||||
void * pvExistingObject;
|
||||
DWORD dwHashIndex;
|
||||
|
||||
// Verify pointer to the map
|
||||
@@ -145,14 +180,14 @@ bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier)
|
||||
return false;
|
||||
|
||||
// Construct the hash index
|
||||
dwHashIndex = CalcHashIndex(pMap, pvIdentifier);
|
||||
dwHashIndex = CalcHashIndex_Key(pMap, pvKey);
|
||||
while(pMap->HashTable[dwHashIndex] != NULL)
|
||||
{
|
||||
// Get the pointer at that position
|
||||
pvTableEntry = pMap->HashTable[dwHashIndex];
|
||||
pvExistingObject = pMap->HashTable[dwHashIndex];
|
||||
|
||||
// Check if hash being inserted conflicts with an existing hash
|
||||
if(CompareIdentifier(pMap, pvTableEntry, pvIdentifier))
|
||||
if(CompareObject_Key(pMap, pvExistingObject, pvKey))
|
||||
return false;
|
||||
|
||||
// Move to the next entry
|
||||
@@ -160,7 +195,7 @@ bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier)
|
||||
}
|
||||
|
||||
// Insert at that position
|
||||
pMap->HashTable[dwHashIndex] = pvIdentifier;
|
||||
pMap->HashTable[dwHashIndex] = pvNewObject;
|
||||
pMap->ItemCount++;
|
||||
return true;
|
||||
}
|
||||
@@ -169,6 +204,78 @@ bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map_InsertString(PCASC_MAP pMap, const char * szString, bool bCutExtension)
|
||||
{
|
||||
const char * szExistingString;
|
||||
const char * szStringEnd = NULL;
|
||||
DWORD dwHashIndex;
|
||||
|
||||
// Verify pointer to the map
|
||||
if(pMap != NULL)
|
||||
{
|
||||
// Limit check
|
||||
if((pMap->ItemCount + 1) >= pMap->TableSize)
|
||||
return false;
|
||||
|
||||
// Retrieve the length of the string without extension
|
||||
if(bCutExtension)
|
||||
szStringEnd = String_GetExtension(szString);
|
||||
if(szStringEnd == NULL)
|
||||
szStringEnd = szString + strlen(szString);
|
||||
|
||||
// Construct the hash index
|
||||
dwHashIndex = CalcHashIndex_String(pMap, szString, szStringEnd);
|
||||
while(pMap->HashTable[dwHashIndex] != NULL)
|
||||
{
|
||||
// Get the pointer at that position
|
||||
szExistingString = (const char *)pMap->HashTable[dwHashIndex];
|
||||
|
||||
// Check if hash being inserted conflicts with an existing hash
|
||||
if(CompareObject_String(pMap, szExistingString, szString, szStringEnd))
|
||||
return false;
|
||||
|
||||
// Move to the next entry
|
||||
dwHashIndex = (dwHashIndex + 1) % pMap->TableSize;
|
||||
}
|
||||
|
||||
// Insert at that position
|
||||
pMap->HashTable[dwHashIndex] = (void *)szString;
|
||||
pMap->ItemCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Failed
|
||||
return false;
|
||||
}
|
||||
|
||||
const char * Map_FindString(PCASC_MAP pMap, const char * szString, const char * szStringEnd)
|
||||
{
|
||||
const char * szExistingString;
|
||||
DWORD dwHashIndex;
|
||||
|
||||
// Verify pointer to the map
|
||||
if(pMap != NULL)
|
||||
{
|
||||
// Construct the main index
|
||||
dwHashIndex = CalcHashIndex_String(pMap, szString, szStringEnd);
|
||||
while(pMap->HashTable[dwHashIndex] != NULL)
|
||||
{
|
||||
// Get the pointer at that position
|
||||
szExistingString = (const char *)pMap->HashTable[dwHashIndex];
|
||||
|
||||
// Compare the hash
|
||||
if(CompareObject_String(pMap, szExistingString, szString, szStringEnd))
|
||||
return szExistingString;
|
||||
|
||||
// Move to the next entry
|
||||
dwHashIndex = (dwHashIndex + 1) % pMap->TableSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found, sorry
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Map_Free(PCASC_MAP pMap)
|
||||
{
|
||||
if(pMap != NULL)
|
||||
|
||||
@@ -20,20 +20,23 @@ typedef struct _CASC_MAP
|
||||
{
|
||||
size_t TableSize;
|
||||
size_t ItemCount; // Number of items in the map
|
||||
size_t MemberOffset; // How far is the hash from the begin of the structure (in bytes)
|
||||
size_t KeyOffset; // How far is the hash from the begin of the structure (in bytes)
|
||||
size_t KeyLength; // Length of the hash key
|
||||
void * HashTable[1]; // Pointer table
|
||||
|
||||
} CASC_MAP, *PCASC_MAP;
|
||||
|
||||
typedef bool (*MAP_COMPARE)(PCASC_MAP pMap, void * pvObject, void * pvKey);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions
|
||||
|
||||
PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwMemberOffset);
|
||||
PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwKeyOffset);
|
||||
size_t Map_EnumObjects(PCASC_MAP pMap, void **ppvArray);
|
||||
void * Map_FindObject(PCASC_MAP pMap, void * pvIdentifier);
|
||||
bool Map_InsertObject(PCASC_MAP pMap, void * pvIdentifier);
|
||||
void Map_Sort(PCASC_MAP pMap);
|
||||
void * Map_FindObject(PCASC_MAP pMap, void * pvKey, PDWORD PtrIndex);
|
||||
bool Map_InsertObject(PCASC_MAP pMap, void * pvNewObject, void * pvKey);
|
||||
const char * Map_FindString(PCASC_MAP pMap, const char * szString, const char * szStringEnd);
|
||||
bool Map_InsertString(PCASC_MAP pMap, const char * szString, bool bCutExtension);
|
||||
void Map_Free(PCASC_MAP pMap);
|
||||
|
||||
#endif // __HASHTOPTR_H__
|
||||
|
||||
78
dep/CascLib/src/common/RootHandler.cpp
Normal file
78
dep/CascLib/src/common/RootHandler.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/*****************************************************************************/
|
||||
/* RootHandler.cpp Copyright (c) Ladislav Zezula 2015 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Implementation of root handler */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 09.03.15 1.00 Lad Created */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "../CascLib.h"
|
||||
#include "../CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Common support
|
||||
|
||||
int RootHandler_Insert(TRootHandler * pRootHandler, const char * szFileName, LPBYTE pbEncodingKey)
|
||||
{
|
||||
if(pRootHandler == NULL || pRootHandler->Insert == NULL)
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
|
||||
return pRootHandler->Insert(pRootHandler, szFileName, pbEncodingKey);
|
||||
}
|
||||
|
||||
LPBYTE RootHandler_Search(TRootHandler * pRootHandler, struct _TCascSearch * pSearch, PDWORD PtrFileSize, PDWORD PtrLocaleFlags)
|
||||
{
|
||||
// Check if the root structure is valid at all
|
||||
if(pRootHandler == NULL)
|
||||
return NULL;
|
||||
|
||||
return pRootHandler->Search(pRootHandler, pSearch, PtrFileSize, PtrLocaleFlags);
|
||||
}
|
||||
|
||||
void RootHandler_EndSearch(TRootHandler * pRootHandler, struct _TCascSearch * pSearch)
|
||||
{
|
||||
// Check if the root structure is valid at all
|
||||
if(pRootHandler != NULL)
|
||||
{
|
||||
pRootHandler->EndSearch(pRootHandler, pSearch);
|
||||
}
|
||||
}
|
||||
|
||||
LPBYTE RootHandler_GetKey(TRootHandler * pRootHandler, const char * szFileName)
|
||||
{
|
||||
// Check if the root structure is valid at all
|
||||
if(pRootHandler == NULL)
|
||||
return NULL;
|
||||
|
||||
return pRootHandler->GetKey(pRootHandler, szFileName);
|
||||
}
|
||||
|
||||
void RootHandler_Dump(TCascStorage * hs, LPBYTE pbRootHandler, DWORD cbRootHandler, const TCHAR * szNameFormat, const TCHAR * szListFile, int nDumpLevel)
|
||||
{
|
||||
TDumpContext * dc;
|
||||
|
||||
// Only if the ROOT provider suports the dump option
|
||||
if(hs->pRootHandler != NULL && hs->pRootHandler->Dump != NULL)
|
||||
{
|
||||
// Create the dump file
|
||||
dc = CreateDumpContext(hs, szNameFormat);
|
||||
if(dc != NULL)
|
||||
{
|
||||
// Dump the content and close the file
|
||||
hs->pRootHandler->Dump(hs, dc, pbRootHandler, cbRootHandler, szListFile, nDumpLevel);
|
||||
dump_close(dc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RootHandler_Close(TRootHandler * pRootHandler)
|
||||
{
|
||||
// Check if the root structure is allocated at all
|
||||
if(pRootHandler != NULL)
|
||||
{
|
||||
pRootHandler->Close(pRootHandler);
|
||||
}
|
||||
}
|
||||
88
dep/CascLib/src/common/RootHandler.h
Normal file
88
dep/CascLib/src/common/RootHandler.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*****************************************************************************/
|
||||
/* RootHandler.h Copyright (c) Ladislav Zezula 2015 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Interface for root handlers */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 09.03.15 1.00 Lad Created */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __ROOT_HANDLER_H__
|
||||
#define __ROOT_HANDLER_H__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Defines
|
||||
|
||||
#define CASC_MNDX_ROOT_SIGNATURE 0x58444E4D // 'MNDX'
|
||||
#define CASC_DIABLO3_ROOT_SIGNATURE 0x8007D0C4
|
||||
#define CASC_OVERWATCH_ROOT_SIGNATURE 0x35444D23 // '#MD5'
|
||||
|
||||
#define ROOT_FLAG_HAS_NAMES 0x00000001 // The root file contains file names
|
||||
|
||||
#define DUMP_LEVEL_ROOT_FILE 1 // Dump root file
|
||||
#define DUMP_LEVEL_ENCODING_FILE 2 // Dump root file + encoding file
|
||||
#define DUMP_LEVEL_INDEX_ENTRIES 3 // Dump root file + encoding file + index entries
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Root file function prototypes
|
||||
|
||||
typedef int (*ROOT_INSERT)(
|
||||
struct TRootHandler * pRootHandler, // Pointer to an initialized root handler
|
||||
const char * szFileName, // Pointer to the file name
|
||||
LPBYTE pbEncodingKey // Pointer to the encoding key of the file name
|
||||
);
|
||||
|
||||
typedef LPBYTE (*ROOT_SEARCH)(
|
||||
struct TRootHandler * pRootHandler, // Pointer to an initialized root handler
|
||||
struct _TCascSearch * pSearch, // Pointer to the initialized search structure
|
||||
PDWORD PtrFileSize, // Pointer to receive file size (optional)
|
||||
PDWORD PtrLocaleFlags // Pointer to receive locale flags (optional)
|
||||
);
|
||||
|
||||
typedef void (*ROOT_ENDSEARCH)(
|
||||
struct TRootHandler * pRootHandler, // Pointer to an initialized root handler
|
||||
struct _TCascSearch * pSearch // Pointer to the initialized search structure
|
||||
);
|
||||
|
||||
typedef LPBYTE (*ROOT_GETKEY)(
|
||||
struct TRootHandler * pRootHandler, // Pointer to an initialized root handler
|
||||
const char * szFileName // Pointer to the name of a file
|
||||
);
|
||||
|
||||
typedef void (*ROOT_DUMP)(
|
||||
struct _TCascStorage * hs, // Pointer to the open storage
|
||||
TDumpContext * dc, // Opened dump context
|
||||
LPBYTE pbRootHandler, // Pointer to the loaded ROOT file
|
||||
DWORD cbRootHandler, // Length of the loaded ROOT file
|
||||
const TCHAR * szListFile,
|
||||
int nDumpLevel
|
||||
);
|
||||
|
||||
typedef void (*ROOT_CLOSE)(
|
||||
struct TRootHandler * pRootHandler // Pointer to an initialized root handler
|
||||
);
|
||||
|
||||
struct TRootHandler
|
||||
{
|
||||
ROOT_INSERT Insert; // Inserts an existing file name
|
||||
ROOT_SEARCH Search; // Performs the root file search
|
||||
ROOT_ENDSEARCH EndSearch; // Performs cleanup after searching
|
||||
ROOT_GETKEY GetKey; // Retrieves encoding key for a file name
|
||||
ROOT_DUMP Dump;
|
||||
ROOT_CLOSE Close; // Closing the root file
|
||||
|
||||
DWORD dwRootFlags; // Root flags - see the ROOT_FLAG_XXX
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int RootHandler_Insert(TRootHandler * pRootHandler, const char * szFileName, LPBYTE pbEncodingKey);
|
||||
LPBYTE RootHandler_Search(TRootHandler * pRootHandler, struct _TCascSearch * pSearch, PDWORD PtrFileSize, PDWORD PtrLocaleFlags);
|
||||
void RootHandler_EndSearch(TRootHandler * pRootHandler, struct _TCascSearch * pSearch);
|
||||
LPBYTE RootHandler_GetKey(TRootHandler * pRootHandler, const char * szFileName);
|
||||
void RootHandler_Dump(struct _TCascStorage * hs, LPBYTE pbRootHandler, DWORD cbRootHandler, const TCHAR * szNameFormat, const TCHAR * szListFile, int nDumpLevel);
|
||||
void RootHandler_Close(TRootHandler * pRootHandler);
|
||||
|
||||
#endif // __ROOT_HANDLER_H__
|
||||
@@ -19,8 +19,8 @@
|
||||
#if (ARGTYPE == 0)
|
||||
void crypt_argchk(char *v, char *s, int d)
|
||||
{
|
||||
fprintf(stderr, "LTC_ARGCHK '%s' failure on line %d of file %s\n",
|
||||
v, d, s);
|
||||
fprintf(stderr, "LTC_ARGCHK '%s' failure on line %d of file %s\n",
|
||||
v, d, s);
|
||||
(void)raise(SIGABRT);
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user