diff options
author | Shauren <shauren.trinity@gmail.com> | 2019-06-09 21:14:42 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2019-06-09 21:14:42 +0200 |
commit | 138e822d859fd9ff9d79e1ce16823992ad43aec4 (patch) | |
tree | f1e22a1afbaa3457e6169146f350c91b9493f926 /dep/CascLib/src/CascRootFile_MNDX.cpp | |
parent | c4f11447546836844ade509991b4219c88ebaaa3 (diff) |
Dep/CascLib: Update to ladislav-zezula/CascLib@03a3bcaed285ab0d8ff0845a65ffe7d1fa653960
Diffstat (limited to 'dep/CascLib/src/CascRootFile_MNDX.cpp')
-rw-r--r-- | dep/CascLib/src/CascRootFile_MNDX.cpp | 2981 |
1 files changed, 2981 insertions, 0 deletions
diff --git a/dep/CascLib/src/CascRootFile_MNDX.cpp b/dep/CascLib/src/CascRootFile_MNDX.cpp new file mode 100644 index 00000000000..9dca8d30eb4 --- /dev/null +++ b/dep/CascLib/src/CascRootFile_MNDX.cpp @@ -0,0 +1,2981 @@ +/*****************************************************************************/ +/* CascRootFile_MNDX.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Common functions for CascLib */ +/* Note: "HOTS" refers to Play.exe, v2.5.0.29049 (Heroes of the Storm Alpha) */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 18.05.14 1.00 Lad The first version of CascRootFile_MNDX.cpp */ +/*****************************************************************************/ + +#define __CASCLIB_SELF__ +#include "CascLib.h" +#include "CascCommon.h" + +//----------------------------------------------------------------------------- +// Local defines + +#define MNDX_MAR_SIGNATURE 0x0052414d // 'MAR\0' +#define MAR_PACKAGE_NAMES 0 // MAR with package names only +#define MAR_STRIPPED_NAMES 1 // MAR with names where packages were stripped +#define MAR_FULL_NAMES 2 // MAR with full file names +#define MAR_COUNT 3 // Maximum of 3 MAR files are supported + +#define MNDX_SEARCH_INITIALIZING 0 +#define MNDX_SEARCH_SEARCHING 2 +#define MNDX_SEARCH_FINISHED 4 + +#define MNDX_MAX_ENTRIES(type) (0xFFFFFFFF / sizeof(type)) + +#define MNDX_INVALID_SIZE_T ((size_t)(-1)) + +#define MNDX_LAST_CKEY_ENTRY 0x80000000 + +//----------------------------------------------------------------------------- +// Local structures + +typedef union _SETBITS +{ + struct + { + DWORD Lower08 : 8; // Number of set bits in the lower 1 byte + DWORD Lower16 : 8; // Number of set bits in the lower 2 bytes + DWORD Lower24 : 8; // Number of set bits in the lower 3 bytes + DWORD Lower32 : 8; // Number of set bits in the 32-bit integer + } u; + + DWORD SetBitsAll; // The total set bits mask + +} SETBITS, *PSETBITS; + +typedef struct _HASH_ENTRY +{ + DWORD NodeIndex; // Index of the path node + DWORD NextIndex; // ID of the first subnode in the hash table + + union + { + DWORD FragmentOffset; // Offset of the path fragment in the TPathFragmentTable + DWORD ChildTableIndex; // Starting search index for the child database (if child database is present) + char SingleChar; // If the upper 24 bits of the FragmentOffset is 0xFFFFFFFF, this single character + }; + // Otherwise --> Offset to the name fragment table +} HASH_ENTRY, *PHASH_ENTRY; + +typedef struct _FILE_MNDX_HEADER +{ + DWORD Signature; // 'MNDX' + DWORD HeaderVersion; // Must be <= 2 + DWORD FormatVersion; + +} FILE_MNDX_HEADER, *PFILE_MNDX_HEADER; + +typedef struct _MNDX_PACKAGE +{ + char * szFileName; // Pointer to file name + size_t nLength; // Length of the file name + size_t nIndex; // Package index + +} MNDX_PACKAGE, *PMNDX_PACKAGE; + +// Root file entry for CASC storages with MNDX root file (Heroes of the Storm) +// Corresponds to the in-file structure +typedef struct _MNDX_CKEY_ENTRY +{ + DWORD Flags; // High 8 bits: Flags, low 24 bits: package index + BYTE CKey[MD5_HASH_SIZE]; // Content key for the file + DWORD ContentSize; // Uncompressed file size, in bytes + +} MNDX_CKEY_ENTRY, *PMNDX_CKEY_ENTRY; + +typedef struct _FILE_MAR_INFO +{ + DWORD MarIndex; + DWORD MarDataSize; + DWORD MarDataSizeHi; + DWORD MarDataOffset; + DWORD MarDataOffsetHi; +} FILE_MAR_INFO, *PFILE_MAR_INFO; + +//----------------------------------------------------------------------------- +// Local variables + +unsigned char table_1BA1818[0x800] = +{ + 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x07, 0x07, 0x07, 0x01, 0x07, 0x02, 0x02, 0x01, 0x07, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x05, 0x05, 0x01, 0x05, 0x02, 0x02, 0x01, 0x05, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x05, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x06, 0x06, 0x01, 0x06, 0x02, 0x02, 0x01, 0x06, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x06, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x06, 0x05, 0x05, 0x01, 0x05, 0x02, 0x02, 0x01, 0x05, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x05, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x07, 0x07, 0x01, 0x07, 0x02, 0x02, 0x01, 0x07, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x05, 0x05, 0x01, 0x05, 0x02, 0x02, 0x01, 0x05, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x05, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x06, 0x06, 0x01, 0x06, 0x02, 0x02, 0x01, 0x06, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x06, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x06, 0x05, 0x05, 0x01, 0x05, 0x02, 0x02, 0x01, 0x05, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x05, 0x04, 0x04, 0x01, 0x04, 0x02, 0x02, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x02, 0x07, 0x07, 0x07, 0x03, 0x07, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x04, 0x07, 0x04, 0x04, 0x02, 0x07, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x02, 0x07, 0x05, 0x05, 0x03, 0x05, 0x03, 0x03, 0x02, + 0x07, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x02, 0x05, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x02, 0x07, 0x06, 0x06, 0x03, 0x06, 0x03, 0x03, 0x02, + 0x07, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x02, 0x06, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x02, 0x06, 0x05, 0x05, 0x03, 0x05, 0x03, 0x03, 0x02, + 0x06, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x02, 0x05, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x02, 0x07, 0x07, 0x07, 0x03, 0x07, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x04, 0x07, 0x04, 0x04, 0x02, 0x07, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x02, 0x07, 0x05, 0x05, 0x03, 0x05, 0x03, 0x03, 0x02, + 0x07, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x02, 0x05, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x02, 0x07, 0x06, 0x06, 0x03, 0x06, 0x03, 0x03, 0x02, + 0x07, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x02, 0x06, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x02, 0x06, 0x05, 0x05, 0x03, 0x05, 0x03, 0x03, 0x02, + 0x06, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x02, 0x05, 0x04, 0x04, 0x03, 0x04, 0x03, 0x03, 0x02, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x04, 0x07, 0x07, 0x07, 0x04, 0x07, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x03, + 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x04, 0x07, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x03, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x04, 0x07, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x03, + 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x04, 0x06, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x04, 0x07, 0x07, 0x07, 0x04, 0x07, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x03, + 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x04, 0x07, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x03, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x04, 0x07, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x03, + 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x04, 0x06, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x03, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07, 0x07, 0x05, 0x07, 0x05, 0x05, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, + 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x06, 0x06, 0x05, 0x06, 0x05, 0x05, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x06, 0x05, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07 +}; + +//----------------------------------------------------------------------------- +// Local functions - Number of set bits in an integer + +// HOTS: inlined +static SETBITS GetNumberOfSetBits(DWORD Value32) +{ + SETBITS SetBits; + + Value32 = ((Value32 >> 1) & 0x55555555) + (Value32 & 0x55555555); + Value32 = ((Value32 >> 2) & 0x33333333) + (Value32 & 0x33333333); + Value32 = ((Value32 >> 4) & 0x0F0F0F0F) + (Value32 & 0x0F0F0F0F); + SetBits.SetBitsAll = Value32 * 0x01010101; + + return SetBits; +} + +static DWORD GetNumberOfSetBits32(DWORD Value32) +{ + return GetNumberOfSetBits(Value32).u.Lower32; +} + +static LPBYTE CaptureData(LPBYTE pbRootPtr, LPBYTE pbRootEnd, void * pvBuffer, size_t cbLength) +{ + // Check whether there is enough data in the buffer + if((pbRootPtr + cbLength) > pbRootEnd) + return NULL; + + // Copy the data + memcpy(pvBuffer, pbRootPtr, cbLength); + return pbRootPtr + cbLength; +} + +//----------------------------------------------------------------------------- +// The TPathStop structure + +struct TPathStop +{ + TPathStop() + { + LoBitsIndex = 0; + field_4 = 0; + Count = 0; + HiBitsIndex_PathFragment = CASC_INVALID_INDEX; + field_10 = 0xFFFFFFFF; + } + + TPathStop(DWORD arg_0, DWORD arg_4, DWORD arg_8) + { + LoBitsIndex = arg_0; + field_4 = arg_4; + Count = arg_8; + HiBitsIndex_PathFragment = CASC_INVALID_INDEX; + field_10 = 0xFFFFFFFF; + } + + DWORD LoBitsIndex; + DWORD field_4; + DWORD Count; + DWORD HiBitsIndex_PathFragment; + DWORD field_10; +}; + +//----------------------------------------------------------------------------- +// Basic array implementations + +class TByteStream +{ + public: + + // HOTS: 01959990 + TByteStream() + { + pbByteData = NULL; + pvMappedFile = NULL; + cbByteData = 0; + field_C = 0; + hFile = 0; + hMap = 0; + } + + // HOTS: 19599F0 + template <typename T> + int GetBytes(size_t length, T ** Pointer) + { + // Is there enough bytes in the array? + if(length > cbByteData) + return ERROR_BAD_FORMAT; + + // Give the buffer to the caller + Pointer[0] = (T *)(pbByteData); + + // Move pointers + pbByteData += length; + cbByteData -= length; + return ERROR_SUCCESS; + } + + int CopyBytes(void * value, size_t length) + { + // Is there enough bytes in the array? + if(length > cbByteData) + return ERROR_BAD_FORMAT; + + // Give the buffer to the caller + memcpy(value, pbByteData, length); + + // Move pointers + pbByteData += length; + cbByteData -= length; + return ERROR_SUCCESS; + } + + // HOTS: 1959A60 + int SkipBytes(size_t cbByteCount) + { + LPBYTE Pointer; + + return GetBytes<BYTE>(cbByteCount, &Pointer); + } + + // HOTS: 1959AF0 + int SetByteBuffer(LPBYTE pbNewByteData, size_t cbNewByteData) + { + if(pbNewByteData != NULL || cbNewByteData == 0) + { + pbByteData = pbNewByteData; + cbByteData = cbNewByteData; + return ERROR_SUCCESS; + } + + return ERROR_INVALID_PARAMETER; + } + + // HOTS: 1957160 <DWORD> + template <typename T> + int GetValue(T & Value) + { + T * Pointer; + int nError; + + nError = GetBytes(sizeof(T), (LPBYTE *)(&Pointer)); + if(nError != ERROR_SUCCESS) + return nError; + + Value = Pointer[0]; + return ERROR_SUCCESS; + } + + // Retrieves the item count in the array + template <typename T> + int GetArrayItemCount(DWORD & ArraySize, DWORD & ItemCount) + { + ULONGLONG ByteCount; + int nError; + + // The first 8 bytes is the byte size of the array + nError = GetValue<ULONGLONG>(ByteCount); + if(nError != ERROR_SUCCESS) + return nError; + + // Extract the number of bytes + if(ByteCount > 0xFFFFFFFF || (ByteCount % sizeof(T)) != 0) + return ERROR_BAD_FORMAT; + + // Give the result to the caller + ItemCount = (DWORD)(ByteCount / sizeof(T)); + ArraySize = (DWORD)(ByteCount); + return ERROR_SUCCESS; + } + + // HOTS: 1957190: <DWORD> + // HOTS: 19571E0: <BASEVALS> + // HOTS: 1957230: <BYTE> + // HOTS: 1957280: <HASH_ENTRY> + template <typename T> + int GetArray(T ** Pointer, size_t ItemCount) + { + int nError = ERROR_SUCCESS; + + // Verify parameters + if(Pointer == NULL && ItemCount != 0) + return ERROR_INVALID_PARAMETER; + if(ItemCount > MNDX_MAX_ENTRIES(T)) + return ERROR_NOT_ENOUGH_MEMORY; + + // Allocate bytes for the array + if (Pointer != NULL) + { + Pointer[0] = CASC_ALLOC(T, ItemCount); + if (Pointer[0] == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Get the pointer to the array + nError = CopyBytes(Pointer[0], sizeof(T) * ItemCount); + } + + return nError; + } + + LPBYTE pbByteData; + void * pvMappedFile; + size_t cbByteData; + DWORD field_C; + HANDLE hFile; + HANDLE hMap; +}; + +//----------------------------------------------------------------------------- +// TGenericArray interface/implementation + +template <typename T> +class TGenericArray +{ + public: + + TGenericArray() + { + ItemArray = NULL; + ItemCount = 0; + MaxItemCount = 0; + bIsValidArray = false; + } + + ~TGenericArray() + { + CASC_FREE(ItemArray); + } + + T & operator[] (size_t index) + { + assert(index < ItemCount); + return ItemArray[index]; + } + + // HOTS: 1957090 (SetDwordsValid) + // HOTS: 19570B0 (SetBaseValsValid) + // HOTS: 19570D0 (? SetBitsValid ?) + // HOTS: 19570F0 (SetPathFragmentsValid) + int SetArrayValid() + { + if(bIsValidArray != 0) + return ERROR_ALREADY_EXISTS; + + bIsValidArray = true; + return ERROR_SUCCESS; + } + + // HOTS: 19575A0 (char) + // HOTS: 1957600 (TPathStop) + void SetMaxItems(DWORD NewMaxItemCount) + { + T * OldArray = ItemArray; + T * NewArray; + + // Allocate new data buffer + NewArray = CASC_ALLOC(T, NewMaxItemCount); + if(NewArray != NULL) + { + // Copy the old items to the buffer + for(size_t i = 0; i < ItemCount; i++) + { + NewArray[i] = ItemArray[i]; + } + } + + ItemArray = NewArray; + MaxItemCount = NewMaxItemCount; + CASC_FREE(OldArray); + } + + // HOTS: 19575A0 (char) + // HOTS: 1957600 (TPathStop) + void SetMaxItemsIf(DWORD NewMaxItemCount) + { + if(NewMaxItemCount > MaxItemCount) + { + if(MaxItemCount > (NewMaxItemCount / 2)) + { + if(MaxItemCount <= (MNDX_MAX_ENTRIES(T) / 2)) + NewMaxItemCount = MaxItemCount + MaxItemCount; + else + NewMaxItemCount = MNDX_MAX_ENTRIES(T); + } + + SetMaxItems(NewMaxItemCount); + } + } + + // HOTS: inline <char> + // HOTS: 1958330 <TPathStop> + void Insert(T NewItem) + { + // Make sure we have enough capacity for the new item + SetMaxItemsIf(ItemCount + 1); + + // Put the character to the slot that has been reserved + ItemArray[ItemCount++] = NewItem; + } + + // HOTS: 19583A0 <TPathStop> + void GrowArray(DWORD NewItemCount) + { + DWORD OldMaxItemCount = MaxItemCount; + + // Make sure we have enough capacity for new items + SetMaxItemsIf(NewItemCount); + + // Initialize the newly inserted items + for(DWORD i = OldMaxItemCount; i < NewItemCount; i++) + { + ItemArray[i] = T(); + } + + ItemCount = NewItemCount; + } + + // HOTS: 1957440 <DWORD> + // HOTS: 19574E0 <BASEVALS> + // HOTS: 1957690 <BYTE> + // HOTS: 1957700 <HASH_ENTRY> + // HOTS: 195A220 <char> + // HOTS: 1958580 <TBitStream, DWORD> + int LoadFromStream(TByteStream & InStream) + { + DWORD NumberOfBytes; + int nError; + + // Get and verify the number of items + nError = InStream.GetArrayItemCount<T>(NumberOfBytes, ItemCount); + if(nError != ERROR_SUCCESS) + return nError; + + // Get the pointer to the array + nError = InStream.GetArray<T>(&ItemArray, ItemCount); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07); + if(nError != ERROR_SUCCESS) + return nError; + + return SetArrayValid(); + } + + T * ItemArray; + DWORD ItemCount; // Number of items in the array + DWORD MaxItemCount; // Capacity of the array + bool bIsValidArray; +}; + +class TBitEntryArray : public TGenericArray<DWORD> +{ + public: + + TBitEntryArray() : TGenericArray() + { + BitsPerEntry = 0; + EntryBitMask = 0; + TotalEntries = 0; + } + + ~TBitEntryArray() + {} + + DWORD GetItem(DWORD EntryIndex) + { + DWORD dwItemIndex = (EntryIndex * BitsPerEntry) >> 0x05; + DWORD dwStartBit = (EntryIndex * BitsPerEntry) & 0x1F; + DWORD dwEndBit = dwStartBit + BitsPerEntry; + DWORD dwResult; + + // If the end bit index is greater than 32, + // we also need to load from the next 32-bit item + if(dwEndBit > 0x20) + { + dwResult = (ItemArray[dwItemIndex + 1] << (0x20 - dwStartBit)) | (ItemArray[dwItemIndex] >> dwStartBit); + } + else + { + dwResult = ItemArray[dwItemIndex] >> dwStartBit; + } + + // Now we also need to mask the result by the bit mask + return dwResult & EntryBitMask; + } + + int LoadBitsFromStream(TByteStream & InStream) + { + ULONGLONG Value64 = 0; + int nError; + + nError = LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.GetValue<DWORD>(BitsPerEntry); + if(nError != ERROR_SUCCESS) + return nError; + if(BitsPerEntry > 0x20) + return ERROR_BAD_FORMAT; + + nError = InStream.GetValue<DWORD>(EntryBitMask); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.GetValue<ULONGLONG>(Value64); + if(nError != ERROR_SUCCESS) + return nError; + if(Value64 > 0xFFFFFFFF) + return ERROR_BAD_FORMAT; + TotalEntries = (DWORD)Value64; + + assert((BitsPerEntry * TotalEntries) / 32 <= ItemCount); + return ERROR_SUCCESS; + } + + DWORD BitsPerEntry; + DWORD EntryBitMask; + DWORD TotalEntries; +}; + +//----------------------------------------------------------------------------- +// TSparseArray functions + +#define INDEX_TO_GROUP(val) (val >> 9) +#define GROUP_TO_INDEX(grp) (grp << 9) + +// For each 0x200-th bit, this contains information about amount of "1" bits +typedef struct _BASEVALS +{ + DWORD BaseValue200; // Item value of every 0x200-th item + + DWORD AddValue40 : 7; // For each 0x40 items (above the base200), + DWORD AddValue80 : 8; // we have extra shortcut to the item value + DWORD AddValueC0 : 8; // that is to be added to BaseValue200 + DWORD AddValue100 : 9; + DWORD AddValue140 : 9; + DWORD AddValue180 : 9; + DWORD AddValue1C0 : 9; + + DWORD __xalignment : 5; // Filling +} BASEVALS, *PBASEVALS; + +class TSparseArray +{ + public: + + TSparseArray() + { + TotalItemCount = 0; + ValidItemCount = 0; + } + + // HOTS: 1958630 + int LoadFromStream(TByteStream & InStream) + { + DWORD total_count = 0; + DWORD valid_count = 0; + int nError; + + nError = ItemBits.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.GetValue<DWORD>(total_count); + if(nError != ERROR_SUCCESS) + return nError; + nError = InStream.GetValue<DWORD>(valid_count); + if(nError != ERROR_SUCCESS) + return nError; + if(valid_count > total_count) + return ERROR_FILE_CORRUPT; + + TotalItemCount = total_count; + ValidItemCount = valid_count; + + nError = BaseVals.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = IndexToItem0.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = IndexToItem1.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + return ERROR_SUCCESS; + } + + // Returns true if the array is empty + bool IsEmpty() + { + return (TotalItemCount == 0); + } + + // Returns true if the item at n-th position is present + bool IsItemPresent(size_t index) + { + // (index >> 0x05) gives the DWORD, (1 << (ItemIndex & 0x1F)) gives the bit + return (ItemBits[index >> 0x05] & (1 << (index & 0x1F))) ? true : false; + } + + // Retrieves the value of the n-th item in the sparse array. + // Note that for items that are not present, the value is equal + // to the nearest lower present value + DWORD GetItemValueAt(size_t index) + { + BASEVALS & SetBitsCount = BaseVals[index >> 0x09]; + DWORD IntValue; + DWORD BitMask; + + // + // Since we don't want to count bits for the entire array, + // there are item value shortcuts every 0x200 items, + // and then every 0x40 items above the 0x200 base + // + + // 1) We have base value for every 0x200-th item + IntValue = SetBitsCount.BaseValue200; + + // 2) Add the base value for each 0x40-th item above the 0x200 base + switch(((index >> 0x06) & 0x07) - 1) + { + case 0: // Add the 1st value (7 bits) + IntValue += SetBitsCount.AddValue40; + break; + + case 1: // Add the 2nd value (8 bits) + IntValue += SetBitsCount.AddValue80; + break; + + case 2: // Add the 3rd value (8 bits) + IntValue += SetBitsCount.AddValueC0; + break; + + case 3: // Add the 4th value (9 bits) + IntValue += SetBitsCount.AddValue100; + break; + + case 4: // Add the 5th value (9 bits) + IntValue += SetBitsCount.AddValue140; + break; + + case 5: // Add the 6th value (9 bits) + IntValue += SetBitsCount.AddValue180; + break; + + case 6: // Add the 7th value (9 bits) + IntValue += SetBitsCount.AddValue1C0; + break; + } + + // 3) Count the bits of the higher DWORD, if the index 0x20 - 0x30 above the 0x200 base + if(index & 0x20) + IntValue += GetNumberOfSetBits32(ItemBits[(index >> 0x05) - 1]); + + // 4) Count the bits in the current DWORD (masked by bit index mask) + BitMask = (1 << (index & 0x1F)) - 1; + return IntValue + GetNumberOfSetBits32(ItemBits[index >> 0x05] & BitMask); + } + + DWORD FindGroup_Items0(DWORD index) + { + // Setup the group range to search + DWORD minGroup = (IndexToItem0[INDEX_TO_GROUP(index) + 0]) >> 9; + DWORD maxGroup = (IndexToItem0[INDEX_TO_GROUP(index) + 1] + 0x1FF) >> 9; + + // Search the groups and find the BASEVALS structure + // For spans less than 10 groups, use sequential search, otherwise binary search. + if ((maxGroup - minGroup) < 10) + { + // HOTS: 1959CF7 + while (index >= GROUP_TO_INDEX(minGroup) - BaseVals[minGroup + 1].BaseValue200 + 0x200) + { + // HOTS: 1959D14 + minGroup++; + } + } + else + { + // HOTS: 1959D2E + while ((minGroup + 1) < maxGroup) + { + // HOTS: 1959D38 + DWORD middleValue = (maxGroup + minGroup) >> 1; + + if (index < (maxGroup << 0x09) - BaseVals[maxGroup].BaseValue200) + { + // HOTS: 01959D4B + maxGroup = middleValue; + } + else + { + // HOTS: 1959D50 + minGroup = middleValue; + } + } + } + + return minGroup; + } + + DWORD FindGroup_Items1(DWORD index) + { + DWORD groupIndex = (index >> 0x09); + DWORD startValue = IndexToItem1[groupIndex] >> 9; + DWORD nextValue = (IndexToItem1[groupIndex + 1] + 0x1FF) >> 9; + + // Find the BASEVALS structure which the start index belongs to + // For less than 10 values, use sequential search. Otherwise, use binary search + if ((nextValue - startValue) < 10) + { + // HOTS: 01959F94 + while (index >= BaseVals[startValue + 1].BaseValue200) + { + // HOTS: 1959FA3 + startValue++; + } + } + else + { + // Binary search (HOTS: 1959FAD) + if ((startValue + 1) < nextValue) + { + // HOTS: 1959FB4 + DWORD middleValue = (nextValue + startValue) >> 1; + + if (index < BaseVals[middleValue].BaseValue200) + { + // HOTS: 1959FC4 + nextValue = middleValue; + } + else + { + // HOTS: 1959FC8 + startValue = middleValue; + } + } + } + + return startValue; + } + + // Returns the value of Item0[index] (HOTS: 1959CB0) + DWORD GetItem0(DWORD index) + { + SETBITS zeroBits; + DWORD groupIndex; + DWORD dwordIndex; + DWORD itemIndex; + DWORD bitGroup; + DWORD edx = index; + +#ifdef _DEBUG + //if (TotalItemCount > 0x200) + //{ + // FILE * fp = fopen("e:\\Ladik\\Appdir\\CascLib\\doc\\mndx-sparse-array.txt", "wt"); + // Dump(fp); + // fclose(fp); + //} +#endif + + // If the index is at begin of the group, we just return the start value + if ((index & 0x1FF) == 0) + return IndexToItem0[INDEX_TO_GROUP(index)]; + + // Find the group where the index belongs to + groupIndex = FindGroup_Items0(index); + + // HOTS: 1959D5F + edx += BaseVals[groupIndex].BaseValue200 - (groupIndex << 0x09); + dwordIndex = (groupIndex << 4); + + if (edx < 0x100 - BaseVals[groupIndex].AddValue100) + { + // HOTS: 1959D8C + if (edx < 0x80 - BaseVals[groupIndex].AddValue80) + { + // HOTS: 01959DA2 + if (edx >= 0x40 - BaseVals[groupIndex].AddValue40) + { + // HOTS: 01959DB7 + dwordIndex += 2; + edx = edx + BaseVals[groupIndex].AddValue40 - 0x40; + } + } + else + { + // HOTS: 1959DC0 + if (edx < 0xC0 - BaseVals[groupIndex].AddValueC0) + { + // HOTS: 1959DD3 + dwordIndex += 4; + edx = edx + BaseVals[groupIndex].AddValue80 - 0x80; + } + else + { + // HOTS: 1959DD3 + dwordIndex += 6; + edx = edx + BaseVals[groupIndex].AddValueC0 - 0xC0; + } + } + } + else + { + // HOTS: 1959DE8 + if (edx < 0x180 - BaseVals[groupIndex].AddValue180) + { + // HOTS: 01959E00 + if (edx < 0x140 - BaseVals[groupIndex].AddValue140) + { + // HOTS: 1959E11 + dwordIndex += 8; + edx = edx + BaseVals[groupIndex].AddValue100 - 0x100; + } + else + { + // HOTS: 1959E1D + dwordIndex += 10; + edx = edx + BaseVals[groupIndex].AddValue140 - 0x140; + } + } + else + { + // HOTS: 1959E29 + if (edx < 0x1C0 - BaseVals[groupIndex].AddValue1C0) + { + // HOTS: 1959E3D + dwordIndex += 12; + edx = edx + BaseVals[groupIndex].AddValue180 - 0x180; + } + else + { + // HOTS: 1959E49 + dwordIndex += 14; + edx = edx + BaseVals[groupIndex].AddValue1C0 - 0x1C0; + } + } + } + + // HOTS: 1959E53: + // Calculate the number of bits set in the value of "bitGroup" + bitGroup = ~ItemBits[dwordIndex]; + zeroBits = GetNumberOfSetBits(bitGroup); + + if (edx >= zeroBits.u.Lower32) + { + // HOTS: 1959ea4 + bitGroup = ~ItemBits[++dwordIndex]; + edx = edx - zeroBits.u.Lower32; + zeroBits = GetNumberOfSetBits(bitGroup); + } + + // Re-calculate the item index + itemIndex = (dwordIndex << 0x05); + + // HOTS: 1959eea + if (edx < zeroBits.u.Lower16) + { + // HOTS: 1959EFC + if (edx >= zeroBits.u.Lower08) + { + // HOTS: 1959F05 + bitGroup >>= 0x08; + itemIndex += 0x08; + edx -= zeroBits.u.Lower08; + } + } + else + { + // HOTS: 1959F0D + if (edx < zeroBits.u.Lower24) + { + // HOTS: 1959F19 + bitGroup >>= 0x10; + itemIndex += 0x10; + edx -= zeroBits.u.Lower16; + } + else + { + // HOTS: 1959F23 + bitGroup >>= 0x18; + itemIndex += 0x18; + edx -= zeroBits.u.Lower24; + } + } + + // HOTS: 1959f2b + edx = edx << 0x08; + bitGroup = bitGroup & 0xFF; + + // BUGBUG: Possible buffer overflow here. Happens when dwItemIndex >= 0x9C. + // The same happens in Heroes of the Storm (build 29049), so I am not sure + // if this is a bug or a case that never happens + assert((bitGroup + edx) < sizeof(table_1BA1818)); + return table_1BA1818[bitGroup + edx] + itemIndex; + } + + DWORD GetItem1(DWORD index) + { + SETBITS setBits; + DWORD distFromBase; + DWORD groupIndex; + DWORD dwordIndex; + DWORD itemIndex; + DWORD bitGroup; + + // If the index is at begin of the group, we just return the start value + if ((index & 0x1FF) == 0) + return IndexToItem1[INDEX_TO_GROUP(index)]; + + // Find the group where the index belongs to + groupIndex = FindGroup_Items1(index); + + // Calculate the base200 dword index (HOTS: 1959FD4) + distFromBase = index - BaseVals[groupIndex].BaseValue200; + dwordIndex = groupIndex << 0x04; + + // Calculate the dword index including the sub-checkpoint + if (distFromBase < BaseVals[groupIndex].AddValue100) + { + // HOTS: 1959FF1 + if (distFromBase < BaseVals[groupIndex].AddValue80) + { + // HOTS: 0195A000 + if (distFromBase >= BaseVals[groupIndex].AddValue40) + { + // HOTS: 195A007 + distFromBase = distFromBase - BaseVals[groupIndex].AddValue40; + dwordIndex += 2; + } + } + else + { + // HOTS: 195A00E + if (distFromBase < BaseVals[groupIndex].AddValueC0) + { + // HOTS: 195A01A + distFromBase = distFromBase - BaseVals[groupIndex].AddValue80; + dwordIndex += 4; + } + else + { + // HOTS: 195A01F + distFromBase = distFromBase - BaseVals[groupIndex].AddValueC0; + dwordIndex += 6; + } + } + } + else + { + // HOTS: 195A026 + if (distFromBase < BaseVals[groupIndex].AddValue180) + { + // HOTS: 195A037 + if (distFromBase < BaseVals[groupIndex].AddValue140) + { + // HOTS: 195A041 + distFromBase = distFromBase - BaseVals[groupIndex].AddValue100; + dwordIndex += 8; + } + else + { + // HOTS: 195A048 + distFromBase = distFromBase - BaseVals[groupIndex].AddValue140; + dwordIndex += 10; + } + } + else + { + // HOTS: 195A04D + if (distFromBase < BaseVals[groupIndex].AddValue1C0) + { + // HOTS: 195A05A + distFromBase = distFromBase - BaseVals[groupIndex].AddValue180; + dwordIndex += 12; + } + else + { + // HOTS: 195A061 + distFromBase = distFromBase - BaseVals[groupIndex].AddValue1C0; + dwordIndex += 14; + } + } + } + + // HOTS: 195A066 + bitGroup = ItemBits[dwordIndex]; + setBits = GetNumberOfSetBits(bitGroup); + + // Get total number of set bits in the bit group + if (distFromBase >= setBits.u.Lower32) + { + // HOTS: 195A0B2 + bitGroup = ItemBits[++dwordIndex]; + distFromBase = distFromBase - setBits.u.Lower32; + setBits = GetNumberOfSetBits(bitGroup); + } + + // Calculate the item index + itemIndex = (dwordIndex << 0x05); + + // Get the number of set bits in the lower word (HOTS: 195A0F6) + if (distFromBase < setBits.u.Lower16) + { + // HOTS: 195A111 + if (distFromBase >= setBits.u.Lower08) + { + // HOTS: 195A111 + itemIndex = itemIndex + 0x08; + bitGroup = bitGroup >> 0x08; + distFromBase = distFromBase - setBits.u.Lower08; + } + } + else + { + // HOTS: 195A119 + if (distFromBase < setBits.u.Lower24) + { + // HOTS: 195A125 + bitGroup = bitGroup >> 0x10; + itemIndex = itemIndex + 0x10; + distFromBase = distFromBase - setBits.u.Lower16; + } + else + { + // HOTS: 195A12F + bitGroup = bitGroup >> 0x18; + itemIndex = itemIndex + 0x18; + distFromBase = distFromBase - setBits.u.Lower24; + } + } + + bitGroup = bitGroup & 0xFF; + distFromBase = distFromBase << 0x08; + + // BUGBUG: Potential buffer overflow + // Happens in Heroes of the Storm when index == 0x5B + assert((bitGroup + distFromBase) < sizeof(table_1BA1818)); + return table_1BA1818[bitGroup + distFromBase] + itemIndex; + } + +#ifdef _DEBUG + void Dump(FILE * fp) + { + size_t * ArrayNormal; + size_t * ArrayInvert; + size_t IndexNormal = 0; + size_t IndexInvert = 0; + + // Output numbers of set bits for every 0x200-th item + fprintf(fp, "Number of set bits for every 0x200-th index\n" + "========================================================\n" + " Index Base200h +40 +80 +C0 +100 +140 +180 +1C0\n" + "--------------------------------------------------------\n"); + for (size_t i = 0; i < BaseVals.ItemCount; i++) + { + fprintf(fp, "[%08zX]: %08x %04x %04x %04x %04x %04x %04x %04x\n", GROUP_TO_INDEX(i), BaseVals[i].BaseValue200, + BaseVals[i].AddValue40, + BaseVals[i].AddValue80, + BaseVals[i].AddValueC0, + BaseVals[i].AddValue100, + BaseVals[i].AddValue140, + BaseVals[i].AddValue180, + BaseVals[i].AddValue1C0); + } + fprintf(fp, "\n"); + + // Output values of Item1 and Item0 for every 0x200-th index + fprintf(fp, "Item0 and Item1 for every 0x200-th index\n" + "========================================\n" + " Index Item0 Item1\n" + "-----------------------------\n"); + for (size_t i = 0; i < IndexToItem0.ItemCount; i++) + { + fprintf(fp, "[%08zX]: %08x %08x\n", GROUP_TO_INDEX(i), IndexToItem0[i], IndexToItem1[i]); + } + fprintf(fp, "\n"); + + + // Output values of Item1 and Item0 for every index + ArrayNormal = new size_t[TotalItemCount]; + ArrayInvert = new size_t[TotalItemCount]; + if (ArrayNormal && ArrayInvert) + { + // Invalidate both arrays + memset(ArrayNormal, 0xFF, TotalItemCount * sizeof(size_t)); + memset(ArrayInvert, 0xFF, TotalItemCount * sizeof(size_t)); + + // Load the both arrays + for (size_t i = 0; i < TotalItemCount; i++) + { + if (IsItemPresent(i)) + ArrayNormal[IndexNormal++] = i; + else + ArrayInvert[IndexInvert++] = i; + } + + // Output both arrays + fprintf(fp, "Item0 and Item1 for every index\n" + "========================================\n" + " Index Item0 Item1\n" + "-----------------------------\n"); + for (size_t i = 0; i < TotalItemCount; i++) + { + char NormalValue[0x20]; + char InvertValue[0x20]; + + if (ArrayNormal[i] == MNDX_INVALID_SIZE_T && ArrayInvert[i] == MNDX_INVALID_SIZE_T) + break; + fprintf(fp, "[%08zX]: %8s %8s\n", i, DumpValue(InvertValue, _countof(InvertValue), ArrayInvert[i]), DumpValue(NormalValue, _countof(NormalValue), ArrayNormal[i])); + } + fprintf(fp, "\n"); + } + + // Free both arrays + delete[] ArrayNormal; + delete[] ArrayInvert; + + // Output array of all values + fprintf(fp, "Item List: Index -> Value\n==========================\n"); + for (size_t i = 0; i < TotalItemCount; i++) + { + if (IsItemPresent(i)) + { + fprintf(fp, "[%08zX]: %08x\n", i, GetItemValueAt(i)); + } + else + { + fprintf(fp, "[%08zX]: NOT PRESENT\n", i); + } + } + fprintf(fp, "\n"); + } + + char * DumpValue(char * szBuffer, size_t cchBuffer, size_t value) + { + CascStrPrintf(szBuffer, cchBuffer, (value != MNDX_INVALID_SIZE_T) ? "%08zX" : " - ", value); + return szBuffer; + } +#endif + + TGenericArray<DWORD> ItemBits; // A bit array for each item. 1 if the item is present. + size_t TotalItemCount; // Total number of items in the array + size_t ValidItemCount; // Number of present items in the array + TGenericArray<BASEVALS> BaseVals; // For each 0x200-th item, this contains the number of set bits up to that 0x200-th item + TGenericArray<DWORD> IndexToItem0; // Mapping of index to invert item. An "invert" item is an item whose bit in "ItemBits" is zero. + TGenericArray<DWORD> IndexToItem1; // Mapping of index to normal item. An "normal" item is an item whose bit in "ItemBits" is set. +}; + +//----------------------------------------------------------------------------- +// TStruct40 functions + +class TStruct40 +{ + public: + + TStruct40() + { + NodeIndex = 0; + ItemCount = 0; + PathLength = 0; + SearchPhase = MNDX_SEARCH_INITIALIZING; + } + + // HOTS: 19586B0 + void BeginSearch() + { + // HOTS: 19586BD + PathBuffer.ItemCount = 0; + PathBuffer.SetMaxItemsIf(0x40); + + // HOTS: 19586E1 + // Set the new item count + PathStops.GrowArray(0); + PathStops.SetMaxItemsIf(4); + + PathLength = 0; + NodeIndex = 0; + ItemCount = 0; + SearchPhase = MNDX_SEARCH_SEARCHING; + } + + DWORD CalcHashValue(const char * szPath) + { + return (BYTE)(szPath[PathLength]) ^ (NodeIndex << 0x05) ^ NodeIndex; + } + + TGenericArray<TPathStop> PathStops; // Array of path checkpoints + TGenericArray<char> PathBuffer; // Buffer for building a file name + DWORD NodeIndex; // ID of a path node being searched; starting with 0 + DWORD PathLength; // Length of the path in the PathBuffer + DWORD ItemCount; + DWORD SearchPhase; // 0 = initializing, 2 = searching, 4 = finished +}; + +//----------------------------------------------------------------------------- +// Local functions - TMndxSearch + +class TMndxSearch +{ + public: + + // HOTS: 01956EE0 + TMndxSearch() + { + szSearchMask = NULL; + cchSearchMask = 0; + szFoundPath = NULL; + cchFoundPath = 0; + nIndex = 0; + } + + // HOTS: 01956F00 + ~TMndxSearch() + {} + + // HOTS: 01956E70 + int SetSearchMask( + const char * szNewSearchMask, + size_t cchNewSearchMask) + { + if(szSearchMask == NULL && cchSearchMask != 0) + return ERROR_INVALID_PARAMETER; + + Struct40.SearchPhase = MNDX_SEARCH_INITIALIZING; + + szSearchMask = szNewSearchMask; + cchSearchMask = cchNewSearchMask; + return ERROR_SUCCESS; + } + + TStruct40 Struct40; + const char * szSearchMask; // Search mask without wildcards + size_t cchSearchMask; // Length of the search mask + const char * szFoundPath; // Found path name + size_t cchFoundPath; // Length of the found path name + DWORD nIndex; // Index of the file name +}; + +//----------------------------------------------------------------------------- +// TPathFragmentTable class. This class implements table of the path fragments. +// These path fragments can either by terminated by zeros (ASCIIZ) +// or can be marked by the external "PathMarks" structure + +class TPathFragmentTable +{ + public: + + // HOTS: 0195A290 + TPathFragmentTable() + {} + + // HOTS: inlined + ~TPathFragmentTable() + {} + + // HOTS: 195A180 + bool ComparePathFragment(TMndxSearch * pSearch, size_t nFragmentOffset) + { + TStruct40 * pStruct40 = &pSearch->Struct40; + + // Do we have path fragment separators in an external structure? + if(PathMarks.IsEmpty()) + { + // Keep searching as long as the name matches with the fragment + while(PathFragments[nFragmentOffset] == pSearch->szSearchMask[pStruct40->PathLength]) + { + // Move to the next character + pStruct40->PathLength++; + nFragmentOffset++; + + // Is it the end of the fragment or end of the path? + if(PathFragments[nFragmentOffset] == 0) + return true; + if(pStruct40->PathLength >= pSearch->cchSearchMask) + return false; + } + + return false; + } + else + { + // Keep searching as long as the name matches with the fragment + while(PathFragments[nFragmentOffset] == pSearch->szSearchMask[pStruct40->PathLength]) + { + // Move to the next character + pStruct40->PathLength++; + + // Is it the end of the path fragment? + if(PathMarks.IsItemPresent(nFragmentOffset++)) + return true; + if(nFragmentOffset >= pSearch->cchSearchMask) + return false; + } + + return false; + } + } + + // HOTS: 195A3F0 + void CopyPathFragment(TMndxSearch * pSearch, size_t nFragmentOffset) + { + TStruct40 * pStruct40 = &pSearch->Struct40; + + // Do we have path fragment separators in an external structure? + if (PathMarks.IsEmpty()) + { + // HOTS: 195A40C + while (PathFragments[nFragmentOffset] != 0) + { + // Insert the character to the path being built + pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset++]); + } + } + else + { + // HOTS: 195A4B3 + while(!PathMarks.IsItemPresent(nFragmentOffset)) + { + // Insert the character to the path being built + pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset++]); + } + } + } + + // HOTS: 195A570 + bool CompareAndCopyPathFragment(TMndxSearch * pSearch, size_t nFragmentOffset) + { + TStruct40 * pStruct40 = &pSearch->Struct40; + + // Do we have path fragment separators in an external structure? + if(PathMarks.IsEmpty()) + { + // Keep copying as long as we don't reach the end of the search mask + while(pStruct40->PathLength < pSearch->cchSearchMask) + { + // HOTS: 195A5A0 + if(PathFragments[nFragmentOffset] != pSearch->szSearchMask[pStruct40->PathLength]) + return false; + + // HOTS: 195A5B7 + pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset++]); + pStruct40->PathLength++; + + // If we found the end of the fragment, return success + if(PathFragments[nFragmentOffset] == 0) + return true; + } + + // HOTS: 195A660 + // Now we need to copy the rest of the fragment + while(PathFragments[nFragmentOffset] != 0) + { + pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset]); + nFragmentOffset++; + } + } + else + { + // Keep copying as long as we don't reach the end of the search mask + while(nFragmentOffset < pSearch->cchSearchMask) + { + if(PathFragments[nFragmentOffset] != pSearch->szSearchMask[pStruct40->PathLength]) + return false; + + pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset]); + pStruct40->PathLength++; + + // If we found the end of the fragment, return success + if(PathMarks.IsItemPresent(nFragmentOffset++)) + return true; + } + + // Now we need to copy the rest of the fragment + while(!PathMarks.IsItemPresent(nFragmentOffset)) + { + // HOTS: 195A7A6 + pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset++]); + } + } + + return true; + } + + // HOTS: 0195A820 + int LoadFromStream(TByteStream & InStream) + { + int nError; + + nError = PathFragments.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + return PathMarks.LoadFromStream(InStream); + } + + TGenericArray<char> PathFragments; + TSparseArray PathMarks; +}; + +//----------------------------------------------------------------------------- +// TStruct10 functions + +class TStruct10 +{ + public: + + TStruct10() + { + field_0 = 0x03; + field_4 = 0x200; + field_8 = 0x1000; + field_C = 0x20000; + } + + // HOTS: 1956FD0 + int sub_1956FD0(DWORD dwBitMask) + { + switch(dwBitMask & 0xF80) + { + case 0x00: + field_4 = 0x200; + return ERROR_SUCCESS; + + case 0x80: + field_4 = 0x80; + return ERROR_SUCCESS; + + case 0x100: + field_4 = 0x100; + return ERROR_SUCCESS; + + case 0x200: + field_4 = 0x200; + return ERROR_SUCCESS; + + case 0x400: + field_4 = 0x400; + return ERROR_SUCCESS; + + case 0x800: + field_4 = 0x800; + return ERROR_SUCCESS; + } + + return ERROR_INVALID_PARAMETER; + } + + // HOTS: 1957050 + int sub_1957050(DWORD dwBitMask) + { + switch(dwBitMask & 0xF0000) + { + case 0x00: + field_C = 0x20000; + return ERROR_SUCCESS; + + case 0x10000: + field_C = 0x10000; + return ERROR_SUCCESS; + + case 0x20000: + field_C = 0x20000; + return ERROR_SUCCESS; + } + + return ERROR_INVALID_PARAMETER; + } + + // HOTS: 19572E0 + int sub_19572E0(DWORD dwBitMask) + { + DWORD dwSubMask; + int nError; + + if(dwBitMask & 0xFFF00000) + return ERROR_INVALID_PARAMETER; + + dwSubMask = dwBitMask & 0x7F; + if(dwSubMask) + field_0 = dwSubMask; + + nError = sub_1956FD0(dwBitMask); + if(nError != ERROR_SUCCESS) + return nError; + + dwSubMask = dwBitMask & 0xF000; + if(dwSubMask == 0 || dwSubMask == 0x1000) + { + field_8 = 0x1000; + return sub_1957050(dwBitMask); + } + + if(dwSubMask == 0x2000) + { + field_8 = 0x2000; + return sub_1957050(dwBitMask); + } + + return ERROR_INVALID_PARAMETER; + } + + // HOTS: 1957800 + int sub_1957800(DWORD dwBitMask) + { + return sub_19572E0(dwBitMask); + } + + DWORD field_0; + DWORD field_4; + DWORD field_8; + DWORD field_C; +}; + +//----------------------------------------------------------------------------- +// TFileNameDatabase interface/implementation + +class TFileNameDatabase +{ + public: + + // HOTS: 01958730 + TFileNameDatabase() + { + HashTableMask = 0; + field_214 = 0; + pChildDB = NULL; + } + + ~TFileNameDatabase() + { + delete pChildDB; + } + + // Returns nonzero if the name fragment match is a single-char match + bool IsPathFragmentSingleChar(HASH_ENTRY * pHashEntry) + { + return ((pHashEntry->FragmentOffset & 0xFFFFFF00) == 0xFFFFFF00); + } + + // Returns true if the given collision path fragment is a string (aka more than 1 char) + bool IsPathFragmentString(size_t index) + { + return CollisionHiBitsIndexes.IsItemPresent(index); + } + + // HOTS: 1957350, inlined + DWORD GetPathFragmentOffset1(DWORD index_lobits) + { + DWORD index_hibits = CollisionHiBitsIndexes.GetItemValueAt(index_lobits); + + return (HiBitsTable.GetItem(index_hibits) << 0x08) | LoBitsTable[index_lobits]; + } + + // Retrieves fragment_offset/subtable_index of the path fragment, with check for starting value + DWORD GetPathFragmentOffset2(DWORD & index_hibits, DWORD index_lobits) + { + // If the hi-bits index is invalid, we need to get its starting value + if (index_hibits == CASC_INVALID_INDEX) + { +/* + printf("\n"); + for (DWORD i = 0; i < CollisionHiBitsIndexes.TotalItemCount; i++) + { + if (CollisionHiBitsIndexes.IsItemPresent(i)) + printf("[%02X] = %02X\n", i, CollisionHiBitsIndexes.GetIntValueAt(i)); + else + printf("[%02X] = NOT_PRESENT\n", i); + } +*/ + index_hibits = CollisionHiBitsIndexes.GetItemValueAt(index_lobits); + } + else + { + index_hibits++; + } + + // Now we use both NodeIndex and HiBits index for retrieving the path fragment index + return (HiBitsTable.GetItem(index_hibits) << 0x08) | LoBitsTable[index_lobits]; + } + + // HOTS: 1956DA0 + int Load(LPBYTE pbMarData, size_t cbMarData) + { + TByteStream ByteStream; + DWORD dwSignature; + int nError; + + if(pbMarData == NULL && cbMarData != 0) + return ERROR_INVALID_PARAMETER; + + nError = ByteStream.SetByteBuffer(pbMarData, cbMarData); + if(nError == ERROR_SUCCESS) + { + // Get pointer to MAR signature + nError = ByteStream.GetValue<DWORD>(dwSignature); + if(nError != ERROR_SUCCESS) + return nError; + + // Verify the signature + if(dwSignature != MNDX_MAR_SIGNATURE) + return ERROR_BAD_FORMAT; + + // HOTS: 1956E11 + nError = LoadFromStream(ByteStream); + } + + return nError; + } + + // HOTS: 19584B0 + int SetChildDatabase(TFileNameDatabase * pNewDB) + { + if(pNewDB != NULL && pChildDB == pNewDB) + return ERROR_INVALID_PARAMETER; + + if(pChildDB != NULL) + delete pChildDB; + pChildDB = pNewDB; + return ERROR_SUCCESS; + } + + // HOTS: 1957970 + bool ComparePathFragment(TMndxSearch * pSearch) + { + TStruct40 * pStruct40 = &pSearch->Struct40; + PHASH_ENTRY pHashEntry; + DWORD ColTableIndex; + DWORD HiBitsIndex; + DWORD NodeIndex; + + // Calculate the item hash from the current char and fragment ID + NodeIndex = pStruct40->CalcHashValue(pSearch->szSearchMask) & HashTableMask; + pHashEntry = &HashTable[NodeIndex]; + + // Does the hash value ID match? + if(pHashEntry->NodeIndex == pStruct40->NodeIndex) + { + // Check if there is single character match + if (!IsPathFragmentSingleChar(pHashEntry)) + { + // Check if there is a name fragment match + if (pChildDB != NULL) + { + if (!pChildDB->ComparePathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) + return false; + } + else + { + if (!PathFragmentTable.ComparePathFragment(pSearch, pHashEntry->FragmentOffset)) + return false; + } + } + else + { + pStruct40->PathLength++; + } + + pStruct40->NodeIndex = pHashEntry->NextIndex; + return true; + } + + // + // Conflict: Multiple node IDs give the same table index + // + + // HOTS: 1957A0E + ColTableIndex = CollisionTable.GetItem0(pStruct40->NodeIndex) + 1; + pStruct40->NodeIndex = (ColTableIndex - pStruct40->NodeIndex - 1); + HiBitsIndex = CASC_INVALID_INDEX; + + // HOTS: 1957A41: + while(CollisionTable.IsItemPresent(ColTableIndex)) + { + // HOTS: 1957A41 + // Check if the low 8 bits if the fragment offset contain a single character + // or an offset to a name fragment + if(IsPathFragmentString(pStruct40->NodeIndex)) + { + DWORD FragmentOffset = GetPathFragmentOffset2(HiBitsIndex, pStruct40->NodeIndex); + DWORD SavePathLength = pStruct40->PathLength; // HOTS: 1957A83 + + // Do we have a child database? + if(pChildDB != NULL) + { + // HOTS: 1957AEC + if(pChildDB->ComparePathFragmentByIndex(pSearch, FragmentOffset)) + return true; + } + else + { + // HOTS: 1957AF7 + if(PathFragmentTable.ComparePathFragment(pSearch, FragmentOffset)) + return true; + } + + // HOTS: 1957B0E + // If there was partial match with the fragment, end the search + if(pStruct40->PathLength != SavePathLength) + return false; + } + else + { + // HOTS: 1957B1C + if(LoBitsTable[pStruct40->NodeIndex] == pSearch->szSearchMask[pStruct40->PathLength]) + { + pStruct40->PathLength++; + return true; + } + } + + // HOTS: 1957B32 + pStruct40->NodeIndex++; + ColTableIndex++; + } + + return false; + } + + // HOTS: 1957B80 + bool ComparePathFragmentByIndex(TMndxSearch * pSearch, DWORD TableIndex) + { + TStruct40 * pStruct40 = &pSearch->Struct40; + PHASH_ENTRY pHashEntry; + DWORD eax; + + // HOTS: 1957B95 + for (;;) + { + // Get the hasn table item + pHashEntry = &HashTable[TableIndex & HashTableMask]; + + // + if (TableIndex == pHashEntry->NextIndex) + { + // HOTS: 01957BB4 + if (!IsPathFragmentSingleChar(pHashEntry)) + { + // HOTS: 1957BC7 + if (pChildDB != NULL) + { + // HOTS: 1957BD3 + if (!pChildDB->ComparePathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) + return false; + } + else + { + // HOTS: 1957BE0 + if (!PathFragmentTable.ComparePathFragment(pSearch, pHashEntry->FragmentOffset)) + return false; + } + } + else + { + // HOTS: 1957BEE + if (pSearch->szSearchMask[pStruct40->PathLength] != pHashEntry->SingleChar) + return false; + pStruct40->PathLength++; + } + + // HOTS: 1957C05 + TableIndex = pHashEntry->NodeIndex; + if (TableIndex == 0) + return true; + + if (pStruct40->PathLength >= pSearch->cchSearchMask) + return false; + } + else + { + // HOTS: 1957C30 + if (IsPathFragmentString(TableIndex)) + { + DWORD FragmentOffset = GetPathFragmentOffset1(TableIndex); + + // HOTS: 1957C4C + if (pChildDB != NULL) + { + // HOTS: 1957C58 + if (!pChildDB->ComparePathFragmentByIndex(pSearch, FragmentOffset)) + return false; + } + else + { + // HOTS: 1957350 + if (!PathFragmentTable.ComparePathFragment(pSearch, FragmentOffset)) + return false; + } + } + else + { + // HOTS: 1957C8E + if (LoBitsTable[TableIndex] != pSearch->szSearchMask[pStruct40->PathLength]) + return false; + + pStruct40->PathLength++; + } + + // HOTS: 1957CB2 + if (TableIndex <= field_214) + return true; + + if (pStruct40->PathLength >= pSearch->cchSearchMask) + return false; + + eax = CollisionTable.GetItem1(TableIndex); + TableIndex = (eax - TableIndex - 1); + } + } + } + + // HOTS: 1958D70 + void CopyPathFragmentByIndex(TMndxSearch * pSearch, DWORD TableIndex) + { + TStruct40 * pStruct40 = &pSearch->Struct40; + PHASH_ENTRY pHashEntry; + + // HOTS: 1958D84 + for (;;) + { + pHashEntry = &HashTable[TableIndex & HashTableMask]; + if (TableIndex == pHashEntry->NextIndex) + { + // HOTS: 1958DA6 + if (!IsPathFragmentSingleChar(pHashEntry)) + { + // HOTS: 1958DBA + if (pChildDB != NULL) + { + pChildDB->CopyPathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex); + } + else + { + PathFragmentTable.CopyPathFragment(pSearch, pHashEntry->FragmentOffset); + } + } + else + { + // HOTS: 1958DE7 + // Insert the low 8 bits to the path being built + pStruct40->PathBuffer.Insert(pHashEntry->SingleChar); + } + + // HOTS: 1958E71 + TableIndex = pHashEntry->NodeIndex; + if (TableIndex == 0) + return; + } + else + { + // HOTS: 1958E8E + if (IsPathFragmentString(TableIndex)) + { + DWORD FragmentOffset = GetPathFragmentOffset1(TableIndex); + + // HOTS: 1958EAF + if (pChildDB != NULL) + { + pChildDB->CopyPathFragmentByIndex(pSearch, FragmentOffset); + } + else + { + PathFragmentTable.CopyPathFragment(pSearch, FragmentOffset); + } + } + else + { + // HOTS: 1958F50 + // Insert one character to the path being built + pStruct40->PathBuffer.Insert(LoBitsTable[TableIndex]); + } + + // HOTS: 1958FDE + if (TableIndex <= field_214) + return; + + TableIndex = 0xFFFFFFFF - TableIndex + CollisionTable.GetItem1(TableIndex); + } + } + } + + // HOTS: 1958B00 + bool CompareAndCopyPathFragment(TMndxSearch * pSearch) + { + TStruct40 * pStruct40 = &pSearch->Struct40; + PHASH_ENTRY pHashEntry; + DWORD HiBitsIndex; + DWORD ColTableIndex; + DWORD TableIndex; +/* + FILE * fp = fopen("E:\\PathFragmentTable.txt", "wt"); + if (fp != NULL) + { + for (DWORD i = 0; i < HashTable.ItemCount; i++) + { + FragOffs = HashTable[i].FragOffs; + fprintf(fp, "%02x ('%c') %08X %08X %08X", i, (0x20 <= i && i < 0x80) ? i : 0x20, HashTable[i].ItemIndex, HashTable[i].NextIndex, FragOffs); + + if(FragOffs != 0x00800000) + { + if((FragOffs & 0xFFFFFF00) == 0xFFFFFF00) + fprintf(fp, " '%c'", (char)(FragOffs & 0xFF)); + else + fprintf(fp, " %s", &PathFragmentTable.PathFragments[FragOffs]); + } + fprintf(fp, "\n"); + } + + fclose(fp); + } +*/ + // Calculate the item hash from the current char and fragment ID + TableIndex = pStruct40->CalcHashValue(pSearch->szSearchMask) & HashTableMask; + pHashEntry = &HashTable[TableIndex]; + + // Does the hash value ID match? + if(pStruct40->NodeIndex == pHashEntry->NodeIndex) + { + // If the higher 24 bits are set, then the fragment is just one letter, + // contained directly in the table. + if(!IsPathFragmentSingleChar(pHashEntry)) + { + // HOTS: 1958B59 + if (pChildDB != NULL) + { + if (!pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) + return false; + } + else + { + if (!PathFragmentTable.CompareAndCopyPathFragment(pSearch, pHashEntry->FragmentOffset)) + return false; + } + } + else + { + // HOTS: 1958B88 + pStruct40->PathBuffer.Insert(pHashEntry->SingleChar); + pStruct40->PathLength++; + } + + // HOTS: 1958BCA + pStruct40->NodeIndex = pHashEntry->NextIndex; + return true; + } + + // HOTS: 1958BE5 + ColTableIndex = CollisionTable.GetItem0(pStruct40->NodeIndex) + 1; + pStruct40->NodeIndex = (ColTableIndex - pStruct40->NodeIndex - 1); + HiBitsIndex = CASC_INVALID_INDEX; + + // Keep searching while we have a valid collision table entry + while(CollisionTable.IsItemPresent(ColTableIndex)) + { + // If we have high bits in the the bit at NodeIndex is set, it means that there is fragment offset + // If not, the byte in LoBitsTable is the character + if(IsPathFragmentString(pStruct40->NodeIndex)) + { + DWORD FragmentOffset = GetPathFragmentOffset2(HiBitsIndex, pStruct40->NodeIndex); + DWORD SavePathLength = pStruct40->PathLength; // HOTS: 1958C62 + + // Do we have a child database? + if(pChildDB != NULL) + { + // HOTS: 1958CCB + if(pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, FragmentOffset)) + return true; + } + else + { + // HOTS: 1958CD6 + if(PathFragmentTable.CompareAndCopyPathFragment(pSearch, FragmentOffset)) + return true; + } + + // HOTS: 1958CED + if(SavePathLength != pStruct40->PathLength) + return false; + } + else + { + // HOTS: 1958CFB + if(LoBitsTable[pStruct40->NodeIndex] == pSearch->szSearchMask[pStruct40->PathLength]) + { + // HOTS: 1958D11 + pStruct40->PathBuffer.Insert(LoBitsTable[pStruct40->NodeIndex]); + pStruct40->PathLength++; + return true; + } + } + + // HOTS: 1958D11 + pStruct40->NodeIndex++; + ColTableIndex++; + } + + return false; + } + + // HOTS: 1959010 + bool CompareAndCopyPathFragmentByIndex(TMndxSearch * pSearch, DWORD TableIndex) + { + TStruct40 * pStruct40 = &pSearch->Struct40; + PHASH_ENTRY pHashEntry; + + // HOTS: 1959024 + for(;;) + { + pHashEntry = &HashTable[TableIndex & HashTableMask]; + if(TableIndex == pHashEntry->NextIndex) + { + // HOTS: 1959047 + if(!IsPathFragmentSingleChar(pHashEntry)) + { + // HOTS: 195905A + if(pChildDB != NULL) + { + if(!pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex)) + return false; + } + else + { + if(!PathFragmentTable.CompareAndCopyPathFragment(pSearch, pHashEntry->FragmentOffset)) + return false; + } + } + else + { + // HOTS: 1959092 + if(pHashEntry->SingleChar != pSearch->szSearchMask[pStruct40->PathLength]) + return false; + + // Insert the low 8 bits to the path being built + pStruct40->PathBuffer.Insert(pHashEntry->SingleChar); + pStruct40->PathLength++; + } + + // HOTS: 195912E + TableIndex = pHashEntry->NodeIndex; + if(TableIndex == 0) + return true; + } + else + { + // HOTS: 1959147 + if(IsPathFragmentString(TableIndex)) + { + // HOTS: 195917C + DWORD FragmentOffset = GetPathFragmentOffset1(TableIndex); + + if(pChildDB != NULL) + { + if(!pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, FragmentOffset)) + return false; + } + else + { + if(!PathFragmentTable.CompareAndCopyPathFragment(pSearch, FragmentOffset)) + return false; + } + } + else + { + // HOTS: 195920E + if(LoBitsTable[TableIndex] != pSearch->szSearchMask[pStruct40->PathLength]) + return false; + + // Insert one character to the path being built + pStruct40->PathBuffer.Insert(LoBitsTable[TableIndex]); + pStruct40->PathLength++; + } + + // HOTS: 19592B6 + if(TableIndex <= field_214) + return true; + + TableIndex = 0xFFFFFFFF - TableIndex + CollisionTable.GetItem1(TableIndex); + } + + // HOTS: 19592D5 + if(pStruct40->PathLength >= pSearch->cchSearchMask) + break; + } + + CopyPathFragmentByIndex(pSearch, TableIndex); + return true; + } + + // HOTS: 1959460 + bool DoSearch(TMndxSearch * pSearch) + { + TStruct40 * pStruct40 = &pSearch->Struct40; + TPathStop * pPathStop; + DWORD edi; + + // Perform action based on the search phase + switch (pStruct40->SearchPhase) + { + case MNDX_SEARCH_INITIALIZING: + { + // HOTS: 1959489 + pStruct40->BeginSearch(); + + // If the caller passed a part of the search path, we need to find that one + while (pStruct40->PathLength < pSearch->cchSearchMask) + { + if (!CompareAndCopyPathFragment(pSearch)) + { + pStruct40->SearchPhase = MNDX_SEARCH_FINISHED; + return false; + } + } + + // HOTS: 19594b0 + TPathStop PathStop(pStruct40->NodeIndex, 0, pStruct40->PathBuffer.ItemCount); + pStruct40->PathStops.Insert(PathStop); + pStruct40->ItemCount = 1; + + if (FileNameIndexes.IsItemPresent(pStruct40->NodeIndex)) + { + pSearch->szFoundPath = &pStruct40->PathBuffer[0]; + pSearch->cchFoundPath = pStruct40->PathBuffer.ItemCount; + pSearch->nIndex = FileNameIndexes.GetItemValueAt(pStruct40->NodeIndex); + return true; + } + } + // No break here, go straight to the MNDX_SEARCH_SEARCHING + + case MNDX_SEARCH_SEARCHING: + { + // HOTS: 1959522 + for (;;) + { + // HOTS: 1959530 + if (pStruct40->ItemCount == pStruct40->PathStops.ItemCount) + { + TPathStop * pLastStop; + DWORD ColTableIndex; + + pLastStop = &pStruct40->PathStops[pStruct40->PathStops.ItemCount - 1]; + + ColTableIndex = CollisionTable.GetItem0(pLastStop->LoBitsIndex) + 1; + + // Insert a new structure + TPathStop PathStop(ColTableIndex - pLastStop->LoBitsIndex - 1, ColTableIndex, 0); + pStruct40->PathStops.Insert(PathStop); + } + + // HOTS: 19595BD + pPathStop = &pStruct40->PathStops[pStruct40->ItemCount]; + + // HOTS: 19595CC + if (CollisionTable.IsItemPresent(pPathStop->field_4++)) + { + // HOTS: 19595F2 + pStruct40->ItemCount++; + + if (IsPathFragmentString(pPathStop->LoBitsIndex)) + { + DWORD FragmentOffset = GetPathFragmentOffset2(pPathStop->HiBitsIndex_PathFragment, pPathStop->LoBitsIndex); + + // HOTS: 1959630 + if (pChildDB != NULL) + { + // HOTS: 1959649 + pChildDB->CopyPathFragmentByIndex(pSearch, FragmentOffset); + } + else + { + // HOTS: 1959654 + PathFragmentTable.CopyPathFragment(pSearch, FragmentOffset); + } + } + else + { + // HOTS: 1959665 + // Insert one character to the path being built + pStruct40->PathBuffer.Insert(LoBitsTable[pPathStop->LoBitsIndex]); + } + + // HOTS: 19596AE + pPathStop->Count = pStruct40->PathBuffer.ItemCount; + + // HOTS: 19596b6 + if (FileNameIndexes.IsItemPresent(pPathStop->LoBitsIndex)) + { + // HOTS: 19596D1 + if (pPathStop->field_10 == 0xFFFFFFFF) + { + // HOTS: 19596D9 + pPathStop->field_10 = FileNameIndexes.GetItemValueAt(pPathStop->LoBitsIndex); + } + else + { + pPathStop->field_10++; + } + + // HOTS: 1959755 + pSearch->szFoundPath = &pStruct40->PathBuffer[0]; + pSearch->cchFoundPath = pStruct40->PathBuffer.ItemCount; + pSearch->nIndex = pPathStop->field_10; + return true; + } + } + else + { + // HOTS: 19596E9 + if (pStruct40->ItemCount == 1) + { + pStruct40->SearchPhase = MNDX_SEARCH_FINISHED; + return false; + } + + // HOTS: 19596F5 + pStruct40->PathStops[pStruct40->ItemCount - 1].LoBitsIndex++; + edi = pStruct40->PathStops[pStruct40->ItemCount - 2].Count; + pStruct40->PathBuffer.SetMaxItemsIf(edi); + + // HOTS: 1959749 + pStruct40->PathBuffer.ItemCount = edi; + pStruct40->ItemCount--; + } + } + } + + case MNDX_SEARCH_FINISHED: + break; + } + + return false; + } + + // HOTS: 1957EF0 + bool FindFileInDatabase(TMndxSearch * pSearch) + { + TStruct40 * pStruct40 = &pSearch->Struct40; + + pStruct40->NodeIndex = 0; + pStruct40->PathLength = 0; + pStruct40->SearchPhase = MNDX_SEARCH_INITIALIZING; + + if(pSearch->cchSearchMask > 0) + { + while(pStruct40->PathLength < pSearch->cchSearchMask) + { + // HOTS: 01957F12 + if(!ComparePathFragment(pSearch)) + return false; + } + } + + // HOTS: 1957F26 + if(!FileNameIndexes.IsItemPresent(pStruct40->NodeIndex)) + return false; + + pSearch->szFoundPath = pSearch->szSearchMask; + pSearch->cchFoundPath = pSearch->cchSearchMask; + pSearch->nIndex = FileNameIndexes.GetItemValueAt(pStruct40->NodeIndex); + return true; + } + + // HOTS: 1959790 + int LoadFromStream(TByteStream & InStream) + { + DWORD dwBitMask; + int nError; + + nError = CollisionTable.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = FileNameIndexes.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = CollisionHiBitsIndexes.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + // HOTS: 019597CD + nError = LoBitsTable.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + nError = HiBitsTable.LoadBitsFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + // HOTS: 019597F5 + nError = PathFragmentTable.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + // HOTS: 0195980A + if(CollisionHiBitsIndexes.ValidItemCount != 0 && PathFragmentTable.PathFragments.ItemCount == 0) + { + TFileNameDatabase * pNewDB; + + pNewDB = new TFileNameDatabase; + if (pNewDB == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + nError = SetChildDatabase(pNewDB); + if(nError != ERROR_SUCCESS) + return nError; + + nError = pChildDB->LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + } + + // HOTS: 0195986B + nError = HashTable.LoadFromStream(InStream); + if(nError != ERROR_SUCCESS) + return nError; + + HashTableMask = HashTable.ItemCount - 1; + + nError = InStream.GetValue<DWORD>(field_214); + if(nError != ERROR_SUCCESS) + return nError; + + nError = InStream.GetValue<DWORD>(dwBitMask); + if(nError != ERROR_SUCCESS) + return nError; + + return Struct10.sub_1957800(dwBitMask); + } + + TSparseArray CollisionTable; // Table of valid collisions, indexed by NodeIndex + TSparseArray FileNameIndexes; // Array of file name indexes + TSparseArray CollisionHiBitsIndexes; // Table of indexes of high bits (above 8 bits) for collisions + + // This pair of arrays serves for fast conversion from node index to FragmentOffset / FragmentChar + TGenericArray<BYTE> LoBitsTable; // Array of lower 8 bits of name fragment offset + TBitEntryArray HiBitsTable; // Array of upper x bits of name fragment offset + + TPathFragmentTable PathFragmentTable; + TFileNameDatabase * pChildDB; + + TGenericArray<HASH_ENTRY> HashTable; // Hash table for searching name fragments + + DWORD HashTableMask; // Mask to get hash table index from hash value + DWORD field_214; + TStruct10 Struct10; +}; + +//----------------------------------------------------------------------------- +// Local functions - MAR file + +class TMndxMarFile +{ + public: + + TMndxMarFile() + { + pDatabase = NULL; + pbMarData = NULL; + cbMarData = 0; + } + + ~TMndxMarFile() + { + if(pDatabase != NULL) + delete pDatabase; + CASC_FREE(pbMarData); + } + + // HOTS: 00E94180 + int LoadRootData(FILE_MAR_INFO & MarInfo, LPBYTE pbRootFile, LPBYTE pbRootEnd) + { + // Allocate the MAR data + pbMarData = CASC_ALLOC(BYTE, MarInfo.MarDataSize); + cbMarData = MarInfo.MarDataSize; + if(pbMarData == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Capture the MAR data + if(!CaptureData(pbRootFile + MarInfo.MarDataOffset, pbRootEnd, pbMarData, cbMarData)) + return ERROR_FILE_CORRUPT; + + // Create the file name database + pDatabase = new TFileNameDatabase(); + if(pDatabase == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + return pDatabase->Load(pbMarData, cbMarData); + } + + // HOTS: 1956C60 + int SearchFile(TMndxSearch * pSearch) + { + int nError = ERROR_SUCCESS; + + if(pDatabase == NULL) + return ERROR_INVALID_PARAMETER; + + if(!pDatabase->FindFileInDatabase(pSearch)) + nError = ERROR_FILE_NOT_FOUND; + + return nError; + } + + // HOTS: 1956CE0 + int DoSearch(TMndxSearch * pSearch, bool * pbFindResult) + { + int nError = ERROR_SUCCESS; + + if(pDatabase == NULL) + return ERROR_INVALID_PARAMETER; + + *pbFindResult = pDatabase->DoSearch(pSearch); + return nError; + } + + // HOTS: 1956D20 + int GetFileNameCount(size_t * PtrFileNameCount) + { + if(pDatabase == NULL) + return ERROR_INVALID_PARAMETER; + + PtrFileNameCount[0] = pDatabase->FileNameIndexes.ValidItemCount; + return ERROR_SUCCESS; + } + +// protected: + TFileNameDatabase * pDatabase; + LPBYTE pbMarData; + size_t cbMarData; +}; + +//----------------------------------------------------------------------------- +// Implementation of root file functions + +typedef struct _FILE_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 CKeyEntriesOffset; // Offset of the CKey entries, relative to begin of the root file + DWORD CKeyEntriesCount; // Number of CKeys (files) in the root file + DWORD FileNameCount; // Number of unique file names. More files with the same name in the different packages can exist + DWORD CKeyEntrySize; // Size of one CKey root entry + TMndxMarFile * MarFiles[MAR_COUNT]; // File name list for the packages + +} FILE_MNDX_INFO, *PFILE_MNDX_INFO; + +struct TMndxHandler +{ + public: + + // + // Constructor and destructor + // + + TMndxHandler() + { + memset(this, 0, sizeof(TMndxHandler)); + } + + ~TMndxHandler() + { + PMNDX_PACKAGE pPackage; + size_t i; + + for(i = 0; i < MAR_COUNT; i++) + delete MndxInfo.MarFiles[i]; + CASC_FREE(FileNameIndexToCKeyIndex); + pCKeyEntries = NULL; + + for(i = 0; i < Packages.ItemCount(); i++) + { + pPackage = (PMNDX_PACKAGE)Packages.ItemAt(i); + CASC_FREE(pPackage->szFileName); + } + Packages.Free(); + } + + // + // Helper functions + // + + static LPBYTE CaptureRootHeader(FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootPtr, LPBYTE pbRootEnd) + { + // Capture the root header + pbRootPtr = CaptureData(pbRootPtr, pbRootEnd, &MndxHeader, sizeof(FILE_MNDX_HEADER)); + if (pbRootPtr == NULL) + return NULL; + + // Check signature and version + if (MndxHeader.Signature != CASC_MNDX_ROOT_SIGNATURE || MndxHeader.FormatVersion > 2 || MndxHeader.FormatVersion < 1) + return NULL; + + // Passed + return pbRootPtr + sizeof(FILE_MNDX_HEADER); + } + + int LoadPackageNames() + { + TMndxMarFile * pMarFile = MndxInfo.MarFiles[MAR_PACKAGE_NAMES]; + TMndxSearch Search; + PMNDX_PACKAGE pPackage; + size_t nPackageCount = 0x40; + bool bFindResult = false; + int nError; + + // Prepare the file name search in the top level directory + Search.SetSearchMask("", 0); + + // Allocate initial name list structure + pMarFile->GetFileNameCount(&nPackageCount); + nError = Packages.Create<MNDX_PACKAGE>(nPackageCount); + if(nError != ERROR_SUCCESS) + return nError; + + // Reset the package array + Packages.Reset(); + + // Keep searching as long as we find something + while(pMarFile->DoSearch(&Search, &bFindResult) == ERROR_SUCCESS && bFindResult) + { + // Insert new package to the array + assert(Search.nIndex < nPackageCount); + pPackage = (PMNDX_PACKAGE)Packages.InsertAt(Search.nIndex); + if (pPackage != NULL) + { + // The package mut not be initialized yet + assert(pPackage->szFileName == NULL); + + // Allocate space for the file name + pPackage->szFileName = CASC_ALLOC(char, Search.cchFoundPath + 1); + if (pPackage->szFileName == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Fill the package structure + memcpy(pPackage->szFileName, Search.szFoundPath, Search.cchFoundPath); + pPackage->szFileName[Search.cchFoundPath] = 0; + pPackage->nLength = Search.cchFoundPath; + pPackage->nIndex = Search.nIndex; + } + } + + // Give the packages to the caller + return ERROR_SUCCESS; + } + + int Load(const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd) + { + TMndxMarFile * pMarFile; + FILE_MAR_INFO MarInfo; + size_t nFilePointer = 0; + DWORD i; + int nError = ERROR_SUCCESS; + + // Copy the header into the MNDX info + MndxInfo.HeaderVersion = MndxHeader.HeaderVersion; + MndxInfo.FormatVersion = MndxHeader.FormatVersion; + nFilePointer += sizeof(FILE_MNDX_HEADER); + + // Header version 2 has 2 extra fields that we need to load + if(MndxInfo.HeaderVersion == 2) + { + if(!CaptureData(pbRootFile + nFilePointer, pbRootEnd, &MndxInfo.field_1C, sizeof(DWORD) + sizeof(DWORD))) + return ERROR_FILE_CORRUPT; + nFilePointer += sizeof(DWORD) + sizeof(DWORD); + } + + // Load the rest of the file header + if(!CaptureData(pbRootFile + nFilePointer, pbRootEnd, &MndxInfo.MarInfoOffset, 0x1C)) + return ERROR_FILE_CORRUPT; + + // Verify the structure + if(MndxInfo.MarInfoCount > MAR_COUNT || MndxInfo.MarInfoSize != sizeof(FILE_MAR_INFO)) + return ERROR_FILE_CORRUPT; + + // Load all MAR infos + for(i = 0; i < MndxInfo.MarInfoCount; i++) + { + // Capture the n-th MAR info + nFilePointer = MndxInfo.MarInfoOffset + (MndxInfo.MarInfoSize * i); + if(!CaptureData(pbRootFile + nFilePointer, pbRootEnd, &MarInfo, sizeof(FILE_MAR_INFO))) + return ERROR_FILE_CORRUPT; + + // Allocate MAR_FILE structure + pMarFile = new TMndxMarFile(); + if(pMarFile == NULL) + { + nError = ERROR_NOT_ENOUGH_MEMORY; + break; + } + + // Create the database from the MAR data + nError = pMarFile->LoadRootData(MarInfo, pbRootFile, pbRootEnd); + if(nError != ERROR_SUCCESS) + break; + + // Assign the MAR file to the MNDX info structure + MndxInfo.MarFiles[i] = pMarFile; + } + + // All three MAR files must be loaded + // HOTS: 00E9503B + if(nError == ERROR_SUCCESS) + { + if(MndxInfo.MarFiles[MAR_PACKAGE_NAMES] == NULL || MndxInfo.MarFiles[MAR_STRIPPED_NAMES] == NULL || MndxInfo.MarFiles[MAR_FULL_NAMES] == NULL) + nError = ERROR_BAD_FORMAT; + if(MndxInfo.CKeyEntrySize != sizeof(MNDX_CKEY_ENTRY)) + nError = ERROR_BAD_FORMAT; + } + + // Load the array of Ckey entries. All present files are in the array, + // the same names (differentiated by package ID) are groupped together + if(nError == ERROR_SUCCESS) + { + size_t CKeyEntriesSize; + size_t FileNameCount = 0; + + pMarFile = MndxInfo.MarFiles[MAR_STRIPPED_NAMES]; + nError = ERROR_FILE_CORRUPT; + + // Capture the array of CKey entries + if(pMarFile->GetFileNameCount(&FileNameCount) == ERROR_SUCCESS && FileNameCount == MndxInfo.FileNameCount) + { + CKeyEntriesSize = MndxInfo.CKeyEntriesCount * MndxInfo.CKeyEntrySize; + if ((pbRootFile + MndxInfo.CKeyEntriesOffset + CKeyEntriesSize) <= pbRootEnd) + { + pCKeyEntries = (PMNDX_CKEY_ENTRY)(pbRootFile + MndxInfo.CKeyEntriesOffset); + nError = ERROR_SUCCESS; + } + } + } + + // Pick the CKey entries that are the first with a given name + if(nError == ERROR_SUCCESS) + { + assert(MndxInfo.FileNameCount <= MndxInfo.CKeyEntriesCount); + FileNameIndexToCKeyIndex = CASC_ALLOC(PMNDX_CKEY_ENTRY, MndxInfo.FileNameCount + 1); + if(FileNameIndexToCKeyIndex != NULL) + { + PMNDX_CKEY_ENTRY pRootEntry = pCKeyEntries; + DWORD nFileNameIndex = 0; + + // The first entry is always beginning of a file name group + FileNameIndexToCKeyIndex[nFileNameIndex++] = pRootEntry; + + // Get the remaining file name groups + for(i = 0; i < MndxInfo.CKeyEntriesCount; i++, pRootEntry++) + { + if (nFileNameIndex > MndxInfo.FileNameCount) + break; + + if (pRootEntry->Flags & MNDX_LAST_CKEY_ENTRY) + { + FileNameIndexToCKeyIndex[nFileNameIndex++] = pRootEntry + 1; + } + } + + // Verify the final number of file names + if ((nFileNameIndex - 1) != MndxInfo.FileNameCount) + nError = ERROR_BAD_FORMAT; + } + else + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Load the package names from the 0-th MAR file + if(nError == ERROR_SUCCESS) + { + nError = LoadPackageNames(); + } + + return nError; + } + + int LoadFileNames(TCascStorage * hs, CASC_FILE_TREE & FileTree) + { + PCASC_CKEY_ENTRY pCKeyEntry; + PMNDX_CKEY_ENTRY pRootEntry; + PMNDX_CKEY_ENTRY pRootEnd = pCKeyEntries + MndxInfo.CKeyEntriesCount; + PMNDX_PACKAGE pPackage; + TMndxMarFile * pMarFile = MndxInfo.MarFiles[MAR_STRIPPED_NAMES]; + TMndxSearch Search; + char szFileName[MAX_PATH]; + bool bFindResult = false; + int nError; + + // Setup the search mask + Search.SetSearchMask("", 0); + + // Keep searching ad long as we found something + while ((nError = pMarFile->DoSearch(&Search, &bFindResult)) == ERROR_SUCCESS && bFindResult) + { + // Sanity check + assert(Search.cchFoundPath < MAX_PATH); + + // The found file name index must fall into range of file names + if (Search.nIndex < MndxInfo.FileNameCount) + { + // Retrieve the first-in-group CKey entry of that name + pRootEntry = FileNameIndexToCKeyIndex[Search.nIndex]; + + // Now take all files of that name, prepend their package name and insert to file tree + while(pRootEntry < pRootEnd) + { + // Find the appropriate CKey entry in the central storage + pCKeyEntry = FindCKeyEntry_CKey(hs, pRootEntry->CKey); + if (pCKeyEntry != NULL) + { + size_t nPackageIndex = pRootEntry->Flags & 0x00FFFFFF; + + // Retrieve the package for this entry + pPackage = (PMNDX_PACKAGE)Packages.ItemAt(nPackageIndex); + if (pPackage != NULL) + { + // Sanity check + assert(pPackage->nIndex == nPackageIndex); + + // Merge the package name and file name + MakeFileName(szFileName, _countof(szFileName), pPackage, &Search); + + // Insert the entry to the file tree + FileTree.InsertByName(pCKeyEntry, szFileName); + } + } + + // Is this the last-in-group entry? + if (pRootEntry->Flags & MNDX_LAST_CKEY_ENTRY) + break; + pRootEntry++; + } + } + } + + return nError; + } + + // + // Helper functions + // + + void MakeFileName(char * szBuffer, size_t cchBuffer, PMNDX_PACKAGE pPackage, TMndxSearch * pSearch) + { + char * szBufferEnd = szBuffer + cchBuffer - 1; + + // Buffer length check + assert((pPackage->nLength + 1 + pSearch->cchFoundPath + 1) < cchBuffer); + + // Copy the package name + if ((szBuffer + pPackage->nLength) < szBufferEnd) + { + memcpy(szBuffer, pPackage->szFileName, pPackage->nLength); + szBuffer += pPackage->nLength; + } + + // Append slash + if ((szBuffer + 1) < szBufferEnd) + *szBuffer++ = '/'; + + // Append file name + if ((szBuffer + pSearch->cchFoundPath) < szBufferEnd) + { + memcpy(szBuffer, pSearch->szFoundPath, pSearch->cchFoundPath); + szBuffer += pSearch->cchFoundPath; + } + + szBuffer[0] = 0; + } + + protected: + + FILE_MNDX_INFO MndxInfo; + + PMNDX_CKEY_ENTRY * FileNameIndexToCKeyIndex; + PMNDX_CKEY_ENTRY pCKeyEntries; + CASC_ARRAY Packages; // Linear list of present packages +}; + +//----------------------------------------------------------------------------- +// Handler definition for MNDX root file + +struct TRootHandler_MNDX : public TFileTreeRoot +{ + public: + + TRootHandler_MNDX() : TFileTreeRoot(0) + { + // MNDX supports file names and CKeys + dwFeatures |= CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY; + } + + int Load(TCascStorage * hs, const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd) + { + TMndxHandler Handler; + int nError; + + // Load and parse the entire MNDX structure + nError = Handler.Load(MndxHeader, pbRootFile, pbRootEnd); + if (nError == ERROR_SUCCESS) + { + // Search all file names and insert them into the file tree + nError = Handler.LoadFileNames(hs, FileTree); + } + + return nError; + } +}; + +//----------------------------------------------------------------------------- +// Public functions - MNDX info + +int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) +{ + TRootHandler_MNDX * pRootHandler = NULL; + FILE_MNDX_HEADER MndxHeader; + LPBYTE pbRootEnd = pbRootFile + cbRootFile; + int nError = ERROR_BAD_FORMAT; + + // Verify the header of the ROOT file + if(TMndxHandler::CaptureRootHeader(MndxHeader, pbRootFile, pbRootEnd) != NULL) + { + // Allocate the root handler object + pRootHandler = new TRootHandler_MNDX(); + if(pRootHandler != NULL) + { + // Load the root directory. If load failed, we free the object + nError = pRootHandler->Load(hs, MndxHeader, pbRootFile, pbRootEnd); + if(nError != ERROR_SUCCESS) + { + delete pRootHandler; + pRootHandler = NULL; + } + } + } + + // Assign the root directory (or NULL) and return error + hs->pRootHandler = pRootHandler; + return nError; +} |