aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src/CascRootFile_MNDX.cpp
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2019-06-09 21:14:42 +0200
committerShauren <shauren.trinity@gmail.com>2019-06-09 21:14:42 +0200
commit138e822d859fd9ff9d79e1ce16823992ad43aec4 (patch)
treef1e22a1afbaa3457e6169146f350c91b9493f926 /dep/CascLib/src/CascRootFile_MNDX.cpp
parentc4f11447546836844ade509991b4219c88ebaaa3 (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.cpp2981
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;
+}