aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src/CascMndxRoot.cpp
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2014-10-10 20:17:30 +0200
committerShauren <shauren.trinity@gmail.com>2014-10-10 20:17:30 +0200
commit88ae3da6373dee1f04d03b823ee63d6f1db1502e (patch)
treef9ac27f0a743a57b70e90b37f5971e024992eb00 /dep/CascLib/src/CascMndxRoot.cpp
parentbc97908822c4afa23740ce70151c2486c340e2c2 (diff)
Tools/Extractors: Updated map extractor
Diffstat (limited to 'dep/CascLib/src/CascMndxRoot.cpp')
-rw-r--r--dep/CascLib/src/CascMndxRoot.cpp3476
1 files changed, 3476 insertions, 0 deletions
diff --git a/dep/CascLib/src/CascMndxRoot.cpp b/dep/CascLib/src/CascMndxRoot.cpp
new file mode 100644
index 00000000000..a788b652573
--- /dev/null
+++ b/dep/CascLib/src/CascMndxRoot.cpp
@@ -0,0 +1,3476 @@
+/*****************************************************************************/
+/* CascMndxRoot.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 CascMndxRoot.cpp */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "CascLib.h"
+#include "CascCommon.h"
+#include "CascMndxRoot.h"
+
+//-----------------------------------------------------------------------------
+// Local defines
+
+#define CASC_MAR_SIGNATURE 0x0052414d // 'MAR\0'
+
+//-----------------------------------------------------------------------------
+// Local structures
+
+typedef struct _FILE_MNDX_HEADER
+{
+ DWORD Signature; // 'MNDX'
+ DWORD HeaderVersion; // Must be <= 2
+ DWORD FormatVersion;
+
+} FILE_MNDX_HEADER, *PFILE_MNDX_HEADER;
+
+typedef struct _FILE_MAR_INFO
+{
+ DWORD MarIndex;
+ DWORD MarDataSize;
+ DWORD MarDataSizeHi;
+ DWORD MarDataOffset;
+ DWORD MarDataOffsetHi;
+} FILE_MAR_INFO, *PFILE_MAR_INFO;
+
+//-----------------------------------------------------------------------------
+// Testing functions prototypes
+
+#if defined(_DEBUG) && defined(_X86_) && defined(CASCLIB_TEST)
+extern "C" bool _cdecl sub_1958B00_x86(TFileNameDatabase * pDB, TMndxFindResult * pStruct1C);
+void TestMndxRootFile(PCASC_MNDX_INFO pMndxInfo);
+#endif
+
+//-----------------------------------------------------------------------------
+// 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
+DWORD GetNumberOfSetBits(DWORD Value32)
+{
+ Value32 = ((Value32 >> 1) & 0x55555555) + (Value32 & 0x55555555);
+ Value32 = ((Value32 >> 2) & 0x33333333) + (Value32 & 0x33333333);
+ Value32 = ((Value32 >> 4) & 0x0F0F0F0F) + (Value32 & 0x0F0F0F0F);
+
+ return (Value32 * 0x01010101);
+}
+
+#define GetNumbrOfSetBits32(x) (GetNumberOfSetBits(x) >> 0x18)
+
+//-----------------------------------------------------------------------------
+// Local functions - common
+
+static bool RootFileRead(LPBYTE pbFilePointer, LPBYTE pbFileEnd, void * pvBuffer, size_t dwBytesToRead)
+{
+ if((size_t)(pbFileEnd - pbFilePointer) < dwBytesToRead)
+ return false;
+
+ memcpy(pvBuffer, pbFilePointer, dwBytesToRead);
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Local functions - TMndxFindResult
+
+// HOTS: 01956EE0
+TMndxFindResult::TMndxFindResult()
+{
+ szSearchMask = NULL;
+ cchSearchMask = 0;
+ field_8 = 0;
+ szFoundPath = NULL;
+ cchFoundPath = 0;
+ FileNameIndex = 0;
+ pStruct40 = NULL;
+}
+
+// HOTS: 01956F00
+TMndxFindResult::~TMndxFindResult()
+{
+ FreeStruct40();
+}
+
+// HOTS: 01956F30
+int TMndxFindResult::CreateStruct40()
+{
+ if(pStruct40 != NULL)
+ return ERROR_INVALID_PARAMETER;
+
+ pStruct40 = new TStruct40();
+ return (pStruct40 != NULL) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
+}
+
+void TMndxFindResult::FreeStruct40()
+{
+ if(pStruct40 != NULL)
+ delete pStruct40;
+ pStruct40 = NULL;
+}
+
+// HOTS: 01956E70
+int TMndxFindResult::SetSearchPath(
+ const char * szNewSearchMask,
+ size_t cchNewSearchMask)
+{
+ if(szSearchMask == NULL && cchSearchMask != 0)
+ return ERROR_INVALID_PARAMETER;
+
+ if(pStruct40 != NULL)
+ pStruct40->SearchPhase = CASC_SEARCH_INITIALIZING;
+
+ szSearchMask = szNewSearchMask;
+ cchSearchMask = cchNewSearchMask;
+ return ERROR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// TByteStream functions
+
+// HOTS: 01959990
+TByteStream::TByteStream()
+{
+ pbByteData = NULL;
+ pvMappedFile = NULL;
+ cbByteData = 0;
+ field_C = 0;
+ hFile = 0;
+ hMap = 0;
+}
+
+// HOTS: 19599F0
+void TByteStream::ExchangeWith(TByteStream & Target)
+{
+ TByteStream WorkBuff;
+
+ WorkBuff = *this;
+ *this = Target;
+ Target = WorkBuff;
+}
+
+// HOTS: 19599F0
+int TByteStream::GetBytes(DWORD cbByteCount, PARRAY_POINTER PtrArray)
+{
+ if(cbByteData < cbByteCount)
+ return ERROR_BAD_FORMAT;
+
+ // Give the buffer to the caller
+ PtrArray->Bytes = pbByteData;
+
+ // Move pointers
+ pbByteData += cbByteCount;
+ cbByteData -= cbByteCount;
+ return ERROR_SUCCESS;
+}
+
+// HOTS: 1957190
+int TByteStream::GetArray_DWORDs(PARRAY_POINTER PtrArray, DWORD ItemCount)
+{
+ if(PtrArray == NULL && ItemCount != 0)
+ return ERROR_INVALID_PARAMETER;
+ if(ItemCount > CASC_MAX_ENTRIES(DWORD))
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ return GetBytes(ItemCount * 4, PtrArray);
+}
+
+// HOTS: 19571E0
+int TByteStream::GetArray_Triplets(PARRAY_POINTER PtrArray, DWORD ItemCount)
+{
+ if(PtrArray == NULL && ItemCount != 0)
+ return ERROR_INVALID_PARAMETER;
+ if(ItemCount > CASC_MAX_ENTRIES(TRIPLET))
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ return GetBytes(ItemCount * sizeof(TRIPLET), PtrArray);
+}
+
+// HOTS: 1957230
+int TByteStream::GetArray_BYTES(PARRAY_POINTER PtrArray, DWORD ItemCount)
+{
+ if(PtrArray == NULL && ItemCount != 0)
+ return ERROR_INVALID_PARAMETER;
+ if(ItemCount > CASC_MAX_ENTRIES(BYTE))
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ return GetBytes(ItemCount, PtrArray);
+}
+
+// HOTS: 1957280
+int TByteStream::GetArray_NameTable(PARRAY_POINTER PtrArray, DWORD ItemCount)
+{
+ if(PtrArray == NULL && ItemCount != 0)
+ return ERROR_INVALID_PARAMETER;
+ if(ItemCount > CASC_MAX_ENTRIES(NAME_FRAG))
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ return GetBytes(ItemCount * sizeof(NAME_FRAG), PtrArray);
+}
+
+// HOTS: 1959A60
+int TByteStream::SkipBytes(DWORD cbByteCount)
+{
+ ARRAY_POINTER Dummy;
+
+ return GetBytes(cbByteCount, &Dummy);
+}
+
+// HOTS: 1959AF0
+int TByteStream::SetByteBuffer(LPBYTE pbNewByteData, DWORD cbNewByteData)
+{
+ if(pbNewByteData != NULL || cbNewByteData == 0)
+ {
+ pbByteData = pbNewByteData;
+ cbByteData = cbNewByteData;
+ return ERROR_SUCCESS;
+ }
+
+ return ERROR_INVALID_PARAMETER;
+}
+
+
+// HOTS: 1957160
+int TByteStream::GetValue_DWORD(DWORD & Value)
+{
+ ARRAY_POINTER Pointer;
+ int nError;
+
+ nError = GetBytes(sizeof(DWORD), &Pointer);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ Value = Pointer.Uint32s[0];
+ return ERROR_SUCCESS;
+}
+
+int TByteStream::GetValue_ItemCount(DWORD & NumberOfBytes, DWORD & ItemCount, DWORD ItemSize)
+{
+ ARRAY_POINTER Pointer;
+ ULONGLONG ByteCount;
+ int nError;
+
+ // Verify if there is at least - 8 bytes
+ nError = GetBytes(sizeof(ULONGLONG), &Pointer);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // Extract the number of bytes
+ ByteCount = Pointer.Int64Ptr[0];
+ if(ByteCount > 0xFFFFFFFF || (ByteCount % ItemSize) != 0)
+ return ERROR_BAD_FORMAT;
+
+ // Give the result to the caller
+ NumberOfBytes = (DWORD)ByteCount;
+ ItemCount = (DWORD)(ByteCount / ItemSize);
+ return ERROR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// TGenericArray functions
+
+TGenericArray::TGenericArray()
+{
+ DataBuffer.Bytes = NULL;
+ FirstValid.Bytes = NULL;
+ ArrayPointer.Bytes = NULL;
+ ItemCount = 0;
+ MaxItemCount = 0;
+ bIsValidArray = false;
+}
+
+TGenericArray::~TGenericArray()
+{
+ if(DataBuffer.Bytes != NULL)
+ CASC_FREE(DataBuffer.Bytes);
+}
+
+// HOTS: inlined
+void TGenericArray::ExchangeWith(TGenericArray & Target)
+{
+ TGenericArray WorkBuff;
+
+ WorkBuff = *this;
+ *this = Target;
+ Target = WorkBuff;
+}
+
+// HOTS: Inlined
+void TGenericArray::CopyFrom(TGenericArray & Source)
+{
+ if(DataBuffer.Bytes != NULL)
+ CASC_FREE(DataBuffer.Bytes);
+ *this = Source;
+}
+
+// HOTS: 1957090 (SetDwordsValid)
+// HOTS: 19570B0 (SetTripletsValid)
+// HOTS: 19570D0 (? SetBitsValid ?)
+// HOTS: 19570F0 (SetNameFragmentsValid)
+int TGenericArray::SetArrayValid()
+{
+ if(bIsValidArray != 0)
+ return 1;
+
+ bIsValidArray = true;
+ return ERROR_SUCCESS;
+}
+
+
+// HOTS: 19575A0
+void TGenericArray::SetMaxItems_CHARS(DWORD NewMaxItemCount)
+{
+ ARRAY_POINTER OldDataBuffer = DataBuffer;
+ ARRAY_POINTER NewDataBuffer;
+
+ // Allocate new data buffer
+ NewDataBuffer.Chars = CASC_ALLOC(char, NewMaxItemCount);
+ if(NewDataBuffer.Chars != NULL)
+ {
+ // Copy the old items to the buffer
+ for(DWORD i = 0; i < ItemCount; i++)
+ {
+ NewDataBuffer.Chars[i] = FirstValid.Chars[i];
+ }
+ }
+
+ DataBuffer = NewDataBuffer;
+ FirstValid = NewDataBuffer;
+ ArrayPointer = NewDataBuffer;
+ MaxItemCount = NewMaxItemCount;
+ CASC_FREE(OldDataBuffer.Chars);
+}
+
+// HOTS: 1957600
+void TGenericArray::SetMaxItems_PATH_STOP(DWORD NewMaxItemCount)
+{
+ ARRAY_POINTER OldDataBuffer = DataBuffer;
+ ARRAY_POINTER NewDataBuffer;
+
+ // Allocate new data buffer
+ NewDataBuffer.PathStopPtr = CASC_ALLOC(PATH_STOP, NewMaxItemCount);
+ if(NewDataBuffer.PathStopPtr != NULL)
+ {
+ // Copy the old items to the buffer
+ for(DWORD i = 0; i < ItemCount; i++)
+ {
+ NewDataBuffer.PathStopPtr[i] = FirstValid.PathStopPtr[i];
+ }
+ }
+
+ DataBuffer = NewDataBuffer;
+ FirstValid = NewDataBuffer;
+ ArrayPointer = NewDataBuffer;
+ MaxItemCount = NewMaxItemCount;
+ CASC_FREE(OldDataBuffer.PathStopPtr);
+}
+
+// HOTS: inline
+void TGenericArray::InsertOneItem_CHAR(char NewItem)
+{
+ DWORD NewMaxItemCount;
+ DWORD NewItemCount;
+
+ NewItemCount = ItemCount + 1;
+ if(NewItemCount > MaxItemCount)
+ {
+ NewMaxItemCount = NewItemCount;
+
+ if(MaxItemCount > (NewItemCount / 2))
+ {
+ if(MaxItemCount <= (CASC_MAX_ENTRIES(BYTE) / 2))
+ NewMaxItemCount = MaxItemCount + MaxItemCount;
+ else
+ NewMaxItemCount = CASC_MAX_ENTRIES(BYTE);
+ }
+
+ SetMaxItems_CHARS(NewMaxItemCount);
+ }
+
+ // Put the character to the slot that has been reserved
+ FirstValid.Chars[ItemCount++] = NewItem;
+}
+
+// HOTS: 1958330, inline
+void TGenericArray::InsertOneItem_PATH_STOP(PATH_STOP & NewItem)
+{
+ DWORD NewMaxItemCount;
+ DWORD NewItemCount;
+
+ NewItemCount = ItemCount + 1;
+ if(NewItemCount > MaxItemCount)
+ {
+ NewMaxItemCount = NewItemCount;
+
+ if(MaxItemCount > (NewItemCount / 2))
+ {
+ if(MaxItemCount <= (CASC_MAX_ENTRIES(PATH_STOP) / 2))
+ NewMaxItemCount = MaxItemCount + MaxItemCount;
+ else
+ NewMaxItemCount = CASC_MAX_ENTRIES(PATH_STOP);
+ }
+
+ SetMaxItems_PATH_STOP(NewMaxItemCount);
+ }
+
+ // Put the structure to the slot that has been reserved
+ FirstValid.PathStopPtr[ItemCount++] = NewItem;
+}
+
+// HOTS: 19583A0
+void TGenericArray::sub_19583A0(DWORD NewItemCount)
+{
+ DWORD OldMaxItemCount = MaxItemCount;
+
+ if(NewItemCount > MaxItemCount)
+ {
+ DWORD NewMaxItemCount = NewItemCount;
+
+ if(MaxItemCount > (NewItemCount / 2))
+ {
+ if(MaxItemCount <= (CASC_MAX_ENTRIES(PATH_STOP) / 2))
+ NewMaxItemCount = MaxItemCount + MaxItemCount;
+ else
+ NewMaxItemCount = CASC_MAX_ENTRIES(PATH_STOP);
+ }
+
+ SetMaxItems_PATH_STOP(NewMaxItemCount);
+ }
+
+ // Initialize the newly inserted items
+ for(DWORD i = OldMaxItemCount; i < NewItemCount; i++)
+ {
+ FirstValid.PathStopPtr[i].ItemIndex = 0;
+ FirstValid.PathStopPtr[i].field_4 = 0;
+ FirstValid.PathStopPtr[i].field_8 = 0;
+ FirstValid.PathStopPtr[i].field_C = 0xFFFFFFFF;
+ FirstValid.PathStopPtr[i].field_10 = 0xFFFFFFFF;
+ }
+
+ ItemCount = NewItemCount;
+}
+
+// HOTS: 1957440
+int TGenericArray::LoadDwordsArray(TByteStream & InStream)
+{
+ DWORD NumberOfBytes;
+ int nError;
+
+ // Get and verify the number of items
+ nError = InStream.GetValue_ItemCount(NumberOfBytes, ItemCount, sizeof(DWORD));
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = InStream.GetArray_DWORDs(&ArrayPointer, ItemCount);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ return SetArrayValid();
+}
+
+// HOTS: 19574E0
+int TGenericArray::LoadTripletsArray(TByteStream & InStream)
+{
+ DWORD NumberOfBytes;
+ int nError;
+
+ // Get and verify the number of items
+ nError = InStream.GetValue_ItemCount(NumberOfBytes, ItemCount, sizeof(TRIPLET));
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = InStream.GetArray_Triplets(&ArrayPointer, ItemCount);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ return SetArrayValid();
+}
+
+// HOTS: 1957690
+int TGenericArray::LoadByteArray(TByteStream & InStream)
+{
+ DWORD NumberOfBytes;
+ int nError;
+
+ // Get and verify the number of items
+ nError = InStream.GetValue_ItemCount(NumberOfBytes, ItemCount, sizeof(BYTE));
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = InStream.GetArray_BYTES(&ArrayPointer, ItemCount);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ return SetArrayValid();
+}
+
+// HOTS: 1957700
+int TGenericArray::LoadFragmentInfos(TByteStream & InStream)
+{
+ DWORD NumberOfBytes;
+ int nError;
+
+ // Get and verify the number of items
+ nError = InStream.GetValue_ItemCount(NumberOfBytes, ItemCount, sizeof(NAME_FRAG));
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = InStream.GetArray_NameTable(&ArrayPointer, ItemCount);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ return SetArrayValid();
+}
+
+// HOTS: 195A220
+int TGenericArray::LoadStrings(TByteStream & InStream)
+{
+ DWORD NumberOfBytes;
+ int nError;
+
+ // Get and verify the number of items
+ nError = InStream.GetValue_ItemCount(NumberOfBytes, ItemCount, sizeof(char));
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = InStream.GetArray_BYTES(&ArrayPointer, ItemCount);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ return SetArrayValid();
+}
+
+// HOTS: 19581C0
+int TGenericArray::LoadDwordsArray_Copy(TByteStream & InStream)
+{
+ TGenericArray TempArray;
+ int nError;
+
+ nError = TempArray.LoadDwordsArray(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ CopyFrom(TempArray);
+ return ERROR_SUCCESS;
+}
+
+// HOTS: 1958250
+int TGenericArray::LoadTripletsArray_Copy(TByteStream & InStream)
+{
+ TGenericArray TempArray;
+ int nError;
+
+ nError = TempArray.LoadTripletsArray(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ CopyFrom(TempArray);
+ return ERROR_SUCCESS;
+}
+
+// HOTS: 1958420
+int TGenericArray::LoadBytes_Copy(TByteStream & InStream)
+{
+ TGenericArray TempArray;
+ int nError;
+
+ nError = TempArray.LoadByteArray(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ CopyFrom(TempArray);
+ return 0;
+}
+
+// HOTS: 19584F0
+int TGenericArray::LoadFragmentInfos_Copy(TByteStream & InStream)
+{
+ TGenericArray TempArray;
+ int nError;
+
+ nError = TempArray.LoadFragmentInfos(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ CopyFrom(TempArray);
+ return ERROR_SUCCESS;
+}
+
+// HOTS: 195A360
+int TGenericArray::LoadStringsWithCopy(TByteStream & InStream)
+{
+ TGenericArray TempArray;
+ int nError;
+
+ nError = TempArray.LoadStrings(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ CopyFrom(TempArray);
+ return ERROR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// TBitEntryArray functions
+
+TBitEntryArray::TBitEntryArray()
+{
+ BitsPerEntry = 0;
+ EntryBitMask = 0;
+ TotalEntries = 0;
+}
+
+TBitEntryArray::~TBitEntryArray()
+{}
+
+// HOTS: 01957D20
+void TBitEntryArray::ExchangeWith(TBitEntryArray & Target)
+{
+ TBitEntryArray WorkBuff;
+
+ WorkBuff = *this;
+ *this = Target;
+ Target = WorkBuff;
+}
+
+// HOTS: 1958580
+int TBitEntryArray::LoadFromStream(TByteStream & InStream)
+{
+ ARRAY_POINTER Pointer;
+ ULONGLONG Value = 0;
+ int nError;
+
+ nError = LoadDwordsArray_Copy(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = InStream.GetBytes(sizeof(DWORD), &Pointer);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ BitsPerEntry = Pointer.Uint32s[0];
+ if(BitsPerEntry > 0x20)
+ return ERROR_BAD_FORMAT;
+
+ nError = InStream.GetBytes(sizeof(DWORD), &Pointer);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ EntryBitMask = Pointer.Uint32s[0];
+
+ nError = InStream.GetBytes(sizeof(ULONGLONG), &Pointer);
+ if(nError == ERROR_SUCCESS)
+ Value = Pointer.Int64Ptr[0];
+ if(Value > 0xFFFFFFFF)
+ return ERROR_BAD_FORMAT;
+ TotalEntries = (DWORD)Value;
+
+ assert((BitsPerEntry * TotalEntries) / 32 <= ItemCount);
+ return ERROR_SUCCESS;
+}
+
+// HOTS: 1959300
+int TBitEntryArray::LoadFromStream_Exchange(TByteStream & InStream)
+{
+ TBitEntryArray TempArray;
+ int nError;
+
+ nError = TempArray.LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ ExchangeWith(TempArray);
+ return ERROR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// TStruct40 functions
+
+TStruct40::TStruct40()
+{
+ ItemIndex = 0;
+ CharIndex = 0;
+ ItemCount = 0;
+ SearchPhase = CASC_SEARCH_INITIALIZING;
+}
+
+// HOTS: 19586B0
+void TStruct40::InitSearchBuffers()
+{
+ DWORD NewMaxItemCount;
+
+ array_00.ItemCount = 0;
+
+ // HOTS: 19586BD
+ if(array_00.MaxItemCount < 0x40)
+ {
+ // HOTS: 19586C2
+ NewMaxItemCount = 0x40;
+
+ if(array_00.MaxItemCount > 0x20)
+ {
+ if(array_00.MaxItemCount <= 0x7FFFFFFF)
+ NewMaxItemCount = array_00.MaxItemCount + array_00.MaxItemCount;
+ else
+ NewMaxItemCount = CASC_MAX_ENTRIES(BYTE);
+ }
+
+ array_00.SetMaxItems_CHARS(NewMaxItemCount);
+ }
+
+ // HOTS: 19586E1
+ // Set the new item count
+ PathStops.sub_19583A0(0);
+
+ if(PathStops.MaxItemCount < 4)
+ {
+ // HOTS: 19586F2
+ NewMaxItemCount = 4;
+
+ // HOTS: 19586EA
+ if(PathStops.MaxItemCount > 2)
+ {
+ if(PathStops.MaxItemCount <= 0x6666666)
+ NewMaxItemCount = PathStops.MaxItemCount + PathStops.MaxItemCount;
+ else
+ NewMaxItemCount = CASC_MAX_ENTRIES(PATH_STOP);
+ }
+
+ // HOTS: 195870B
+ PathStops.SetMaxItems_PATH_STOP(NewMaxItemCount);
+ }
+
+ ItemIndex = 0;
+ CharIndex = 0;
+ ItemCount = 0;
+ SearchPhase = CASC_SEARCH_SEARCHING;
+}
+
+//-----------------------------------------------------------------------------
+// TSparseArray functions
+
+TSparseArray::TSparseArray()
+{
+ TotalItemCount = 0;
+ ValidItemCount = 0;
+}
+
+// HOTS: 1957DA0
+void TSparseArray::ExchangeWith(TSparseArray & Target)
+{
+ TSparseArray WorkBuff;
+
+ WorkBuff = *this;
+ *this = Target;
+ Target = WorkBuff;
+}
+
+// HOTS: 1958630
+int TSparseArray::LoadFromStream(TByteStream & InStream)
+{
+ ARRAY_POINTER Pointer;
+ int nError;
+
+ nError = ItemBits.LoadDwordsArray_Copy(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = InStream.GetBytes(sizeof(DWORD), &Pointer);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ TotalItemCount = Pointer.Uint32s[0];
+
+ nError = InStream.GetBytes(sizeof(DWORD), &Pointer);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ ValidItemCount = Pointer.Uint32s[0];
+
+ if(ValidItemCount > TotalItemCount)
+ return ERROR_FILE_CORRUPT;
+
+ nError = BaseValues.LoadTripletsArray_Copy(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = ArrayDwords_38.LoadDwordsArray_Copy(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = ArrayDwords_50.LoadDwordsArray_Copy(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ return ERROR_SUCCESS;
+}
+
+// HOTS: 1959380
+int TSparseArray::LoadFromStream_Exchange(TByteStream & InStream)
+{
+ TSparseArray NewStruct68;
+ int nError;
+
+ nError = NewStruct68.LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ ExchangeWith(NewStruct68);
+ return ERROR_SUCCESS;
+}
+
+// HOTS: 1959B60
+DWORD TSparseArray::GetItemValue(DWORD ItemIndex)
+{
+ PTRIPLET pTriplet;
+ DWORD DwordIndex;
+ DWORD BaseValue;
+ DWORD BitMask;
+
+ //
+ // Divide the low-8-bits index to four parts:
+ //
+ // |-----------------------|---|------------|
+ // | A (23 bits) | B | C |
+ // |-----------------------|---|------------|
+ //
+ // A (23-bits): Index to the table (60 bits per entry)
+ //
+ // Layout of the table entry:
+ // |--------------------------------|-------|--------|--------|---------|---------|---------|---------|-----|
+ // | Base Value | val[0]| val[1] | val[2] | val[3] | val[4] | val[5] | val[6] | - |
+ // | 32 bits | 7 bits| 8 bits | 8 bits | 9 bits | 9 bits | 9 bits | 9 bits |5bits|
+ // |--------------------------------|-------|--------|--------|---------|---------|---------|---------|-----|
+ //
+ // B (3 bits) : Index of the variable-bit value in the array (val[#], see above)
+ //
+ // C (32 bits): Number of bits to be checked (up to 0x3F bits).
+ // Number of set bits is then added to the values obtained from A and B
+
+ // Upper 23 bits contain index to the table
+ pTriplet = BaseValues.TripletArray + (ItemIndex >> 0x09);
+ BaseValue = pTriplet->BaseValue;
+
+ // Next 3 bits contain the index to the VBR
+ switch(((ItemIndex >> 0x06) & 0x07) - 1)
+ {
+ case 0: // Add the 1st value (7 bits)
+ BaseValue += (pTriplet->Value2 & 0x7F);
+ break;
+
+ case 1: // Add the 2nd value (8 bits)
+ BaseValue += (pTriplet->Value2 >> 0x07) & 0xFF;
+ break;
+
+ case 2: // Add the 3rd value (8 bits)
+ BaseValue += (pTriplet->Value2 >> 0x0F) & 0xFF;
+ break;
+
+ case 3: // Add the 4th value (9 bits)
+ BaseValue += (pTriplet->Value2 >> 0x17);
+ break;
+
+ case 4: // Add the 5th value (9 bits)
+ BaseValue += (pTriplet->Value3 & 0x1FF);
+ break;
+
+ case 5: // Add the 6th value (9 bits)
+ BaseValue += (pTriplet->Value3 >> 0x09) & 0x1FF;
+ break;
+
+ case 6: // Add the 7th value (9 bits)
+ BaseValue += (pTriplet->Value3 >> 0x12) & 0x1FF;
+ break;
+ }
+
+ //
+ // Take the upper 27 bits as an index to DWORD array, take lower 5 bits
+ // as number of bits to mask. Then calculate number of set bits in the value
+ // masked value.
+ //
+
+ // Get the index into the array of DWORDs
+ DwordIndex = (ItemIndex >> 0x05);
+
+ // Add number of set bits in the masked value up to 0x3F bits
+ if(ItemIndex & 0x20)
+ BaseValue += GetNumbrOfSetBits32(ItemBits.Uint32Array[DwordIndex - 1]);
+
+ BitMask = (1 << (ItemIndex & 0x1F)) - 1;
+ return BaseValue + GetNumbrOfSetBits32(ItemBits.Uint32Array[DwordIndex] & BitMask);
+}
+
+//-----------------------------------------------------------------------------
+// TNameIndexStruct functions
+
+// HOTS: 0195A290
+TNameIndexStruct::TNameIndexStruct()
+{}
+
+// HOTS: inlined
+TNameIndexStruct::~TNameIndexStruct()
+{}
+
+// HOTS: 195A180
+bool TNameIndexStruct::CheckNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs)
+{
+ TStruct40 * pStruct40 = pStruct1C->pStruct40;
+ const char * szPathFragment;
+ const char * szSearchMask;
+
+ if(!Struct68.TotalItemCount)
+ {
+ // Get the offset of the fragment to compare. For convenience with pStruct40->CharIndex,
+ // subtract the CharIndex from the fragment offset
+ szPathFragment = (NameFragments.CharArray + dwFragOffs - pStruct40->CharIndex);
+ szSearchMask = pStruct1C->szSearchMask;
+
+ // Keep searching as long as the name matches with the fragment
+ while(szPathFragment[pStruct40->CharIndex] == szSearchMask[pStruct40->CharIndex])
+ {
+ // Move to the next character
+ pStruct40->CharIndex++;
+
+ // Is it the end of the fragment or end of the path?
+ if(szPathFragment[pStruct40->CharIndex] == 0)
+ return true;
+ if(pStruct40->CharIndex >= pStruct1C->cchSearchMask)
+ return false;
+ }
+
+ return false;
+ }
+ else
+ {
+ // Get the offset of the fragment to compare.
+ szPathFragment = (const char *)(NameFragments.CharArray);
+ szSearchMask = pStruct1C->szSearchMask;
+
+ // Keep searching as long as the name matches with the fragment
+ while(szPathFragment[dwFragOffs] == szSearchMask[pStruct40->CharIndex])
+ {
+ // Move to the next character
+ pStruct40->CharIndex++;
+
+ // Is it the end of the fragment or end of the path?
+ if(Struct68.IsItemPresent(dwFragOffs++))
+ return true;
+ if(dwFragOffs >= pStruct1C->cchSearchMask)
+ return false;
+ }
+
+ return false;
+ }
+}
+
+// HOTS: 195A570
+bool TNameIndexStruct::CheckAndCopyNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs)
+{
+ TStruct40 * pStruct40 = pStruct1C->pStruct40;
+ const char * szPathFragment;
+ const char * szSearchMask;
+
+ if(!Struct68.TotalItemCount)
+ {
+ // Get the offset of the fragment to compare. For convenience with pStruct40->CharIndex,
+ // subtract the CharIndex from the fragment offset
+ szPathFragment = (const char *)(NameFragments.CharArray + dwFragOffs - pStruct40->CharIndex);
+ szSearchMask = pStruct1C->szSearchMask;
+
+ // Keep copying as long as we don't reach the end of the search mask
+ while(pStruct40->CharIndex < pStruct1C->cchSearchMask)
+ {
+ // HOTS: 195A5A0
+ if(szPathFragment[pStruct40->CharIndex] != szSearchMask[pStruct40->CharIndex])
+ return false;
+
+ // HOTS: 195A5B7
+ pStruct40->array_00.InsertOneItem_CHAR(szPathFragment[pStruct40->CharIndex]);
+ pStruct40->CharIndex++;
+
+ if(szPathFragment[pStruct40->CharIndex] == 0)
+ return true;
+ }
+
+ // Fixup the address of the fragment
+ szPathFragment += pStruct40->CharIndex;
+
+ // HOTS: 195A660
+ // Now we need to copy the rest of the fragment
+ while(szPathFragment[0] != 0)
+ {
+ pStruct40->array_00.InsertOneItem_CHAR(szPathFragment[0]);
+ szPathFragment++;
+ }
+ }
+ else
+ {
+ // Get the offset of the fragment to compare
+ // HOTS: 195A6B7
+ szPathFragment = NameFragments.CharArray;
+ szSearchMask = pStruct1C->szSearchMask;
+
+ // Keep copying as long as we don't reach the end of the search mask
+ while(dwFragOffs < pStruct1C->cchSearchMask)
+ {
+ if(szPathFragment[dwFragOffs] != szSearchMask[pStruct40->CharIndex])
+ return false;
+
+ pStruct40->array_00.InsertOneItem_CHAR(szPathFragment[dwFragOffs]);
+ pStruct40->CharIndex++;
+
+ // Keep going as long as the given bit is not set
+ if(Struct68.IsItemPresent(dwFragOffs++))
+ return true;
+ }
+
+ // Fixup the address of the fragment
+ szPathFragment += dwFragOffs;
+
+ // Now we need to copy the rest of the fragment
+ while(Struct68.IsItemPresent(dwFragOffs++) == 0)
+ {
+ // HOTS: 195A7A6
+ pStruct40->array_00.InsertOneItem_CHAR(szPathFragment[0]);
+ szPathFragment++;
+ }
+ }
+
+ return true;
+}
+
+// HOTS: 195A3F0
+void TNameIndexStruct::CopyNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs)
+{
+ TStruct40 * pStruct40 = pStruct1C->pStruct40;
+ const char * szPathFragment;
+
+ // HOTS: 195A3FA
+ if(!Struct68.TotalItemCount)
+ {
+ // HOTS: 195A40C
+ szPathFragment = NameFragments.CharArray + dwFragOffs;
+ while(szPathFragment[0] != 0)
+ {
+ // Insert the character to the path being built
+ pStruct40->array_00.InsertOneItem_CHAR(*szPathFragment++);
+ }
+ }
+ else
+ {
+ // HOTS: 195A4B3
+ for(;;)
+ {
+ // Insert the character to the path being built
+ pStruct40->array_00.InsertOneItem_CHAR(NameFragments.CharArray[dwFragOffs]);
+
+ // Keep going as long as the given bit is not set
+ if(Struct68.IsItemPresent(dwFragOffs++))
+ break;
+ }
+ }
+}
+
+// HOTS: 0195A300
+void TNameIndexStruct::ExchangeWith(TNameIndexStruct & Target)
+{
+ TNameIndexStruct WorkBuff;
+
+ WorkBuff = *this;
+ *this = Target;
+ Target = WorkBuff;
+}
+
+// HOTS: 0195A820
+int TNameIndexStruct::LoadFromStream(TByteStream & InStream)
+{
+ int nError;
+
+ nError = NameFragments.LoadStringsWithCopy(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ return Struct68.LoadFromStream_Exchange(InStream);
+}
+
+// HOTS: 195A850
+int TNameIndexStruct::LoadFromStream_Exchange(TByteStream & InStream)
+{
+ TNameIndexStruct TempIndexStruct;
+ int nError;
+
+ nError = TempIndexStruct.LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ ExchangeWith(TempIndexStruct);
+ return ERROR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// TStruct10 functions
+
+TStruct10::TStruct10()
+{
+ field_0 = 0x03;
+ field_4 = 0x200;
+ field_8 = 0x1000;
+ field_C = 0x20000;
+}
+
+// HOTS: inline
+void TStruct10::CopyFrom(TStruct10 & Target)
+{
+ field_0 = Target.field_0;
+ field_4 = Target.field_4;
+ field_8 = Target.field_8;
+ field_C = Target.field_C;
+}
+
+// HOTS: 1956FD0
+int TStruct10::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 TStruct10::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 TStruct10::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 TStruct10::sub_1957800(DWORD dwBitMask)
+{
+ TStruct10 TempStruct;
+ int nError;
+
+ nError = TempStruct.sub_19572E0(dwBitMask);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ CopyFrom(TempStruct);
+ return ERROR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// TFileNameDatabase functions
+
+// HOTS: 01958730
+TFileNameDatabase::TFileNameDatabase()
+{
+ NameFragIndexMask = 0;
+ field_214 = 0;
+}
+
+// HOTS: inlined
+void TFileNameDatabase::ExchangeWith(TFileNameDatabase & Target)
+{
+ TFileNameDatabasePtr TempPtr;
+ DWORD dwTemp;
+
+ Struct68_00.ExchangeWith(Target.Struct68_00);
+ FileNameIndexes.ExchangeWith(Target.FileNameIndexes);
+ Struct68_D0.ExchangeWith(Target.Struct68_D0);
+
+ FrgmDist_LoBits.ExchangeWith(Target.FrgmDist_LoBits);
+ FrgmDist_HiBits.ExchangeWith(Target.FrgmDist_HiBits);
+
+ IndexStruct_174.ExchangeWith(Target.IndexStruct_174);
+
+ TempPtr = NextDB;
+ NextDB = Target.NextDB;
+ Target.NextDB = TempPtr;
+
+ NameFragTable.ExchangeWith(Target.NameFragTable);
+
+ dwTemp = NameFragIndexMask;
+ NameFragIndexMask = Target.NameFragIndexMask;
+ Target.NameFragIndexMask = dwTemp;
+
+ dwTemp = field_214;
+ field_214 = Target.field_214;
+ Target.field_214 = dwTemp;
+
+ Struct10.CopyFrom(Target.Struct10);
+}
+
+// HOTS: 1959CB0
+DWORD TFileNameDatabase::sub_1959CB0(DWORD dwItemIndex)
+{
+ PTRIPLET pTriplet;
+ DWORD dwKeyShifted = (dwItemIndex >> 9);
+ DWORD eax, ebx, ecx, edx, esi, edi;
+
+ // If lower 9 is zero
+ edx = dwItemIndex;
+ if((edx & 0x1FF) == 0)
+ return Struct68_00.ArrayDwords_38.Uint32Array[dwKeyShifted];
+
+ eax = Struct68_00.ArrayDwords_38.Uint32Array[dwKeyShifted] >> 9;
+ esi = (Struct68_00.ArrayDwords_38.Uint32Array[dwKeyShifted + 1] + 0x1FF) >> 9;
+ dwItemIndex = esi;
+
+ if((eax + 0x0A) >= esi)
+ {
+ // HOTS: 1959CF7
+ pTriplet = Struct68_00.BaseValues.TripletArray + eax + 1;
+ edi = (eax << 0x09);
+ ebx = edi - pTriplet->BaseValue + 0x200;
+ while(edx >= ebx)
+ {
+ // HOTS: 1959D14
+ edi += 0x200;
+ pTriplet++;
+
+ ebx = edi - pTriplet->BaseValue + 0x200;
+ eax++;
+ }
+ }
+ else
+ {
+ // HOTS: 1959D2E
+ while((eax + 1) < esi)
+ {
+ // HOTS: 1959D38
+ // ecx = Struct68_00.BaseValues.TripletArray;
+ esi = (esi + eax) >> 1;
+ ebx = (esi << 0x09) - Struct68_00.BaseValues.TripletArray[esi].BaseValue;
+ if(edx < ebx)
+ {
+ // HOTS: 01959D4B
+ dwItemIndex = esi;
+ }
+ else
+ {
+ // HOTS: 1959D50
+ eax = esi;
+ esi = dwItemIndex;
+ }
+ }
+ }
+
+ // HOTS: 1959D5F
+ pTriplet = Struct68_00.BaseValues.TripletArray + eax;
+ edx += pTriplet->BaseValue - (eax << 0x09);
+ edi = (eax << 4);
+
+ eax = pTriplet->Value2;
+ ecx = (eax >> 0x17);
+ ebx = 0x100 - ecx;
+ if(edx < ebx)
+ {
+ // HOTS: 1959D8C
+ ecx = ((eax >> 0x07) & 0xFF);
+ esi = 0x80 - ecx;
+ if(edx < esi)
+ {
+ // HOTS: 01959DA2
+ eax = eax & 0x7F;
+ ecx = 0x40 - eax;
+ if(edx >= ecx)
+ {
+ // HOTS: 01959DB7
+ edi += 2;
+ edx = edx + eax - 0x40;
+ }
+ }
+ else
+ {
+ // HOTS: 1959DC0
+ eax = (eax >> 0x0F) & 0xFF;
+ esi = 0xC0 - eax;
+ if(edx < esi)
+ {
+ // HOTS: 1959DD3
+ edi += 4;
+ edx = edx + ecx - 0x80;
+ }
+ else
+ {
+ // HOTS: 1959DD3
+ edi += 6;
+ edx = edx + eax - 0xC0;
+ }
+ }
+ }
+ else
+ {
+ // HOTS: 1959DE8
+ esi = pTriplet->Value3;
+ eax = ((esi >> 0x09) & 0x1FF);
+ ebx = 0x180 - eax;
+ if(edx < ebx)
+ {
+ // HOTS: 01959E00
+ esi = esi & 0x1FF;
+ eax = (0x140 - esi);
+ if(edx < eax)
+ {
+ // HOTS: 1959E11
+ edi = edi + 8;
+ edx = edx + ecx - 0x100;
+ }
+ else
+ {
+ // HOTS: 1959E1D
+ edi = edi + 0x0A;
+ edx = edx + esi - 0x140;
+ }
+ }
+ else
+ {
+ // HOTS: 1959E29
+ esi = (esi >> 0x12) & 0x1FF;
+ ecx = (0x1C0 - esi);
+ if(edx < ecx)
+ {
+ // HOTS: 1959E3D
+ edi = edi + 0x0C;
+ edx = edx + eax - 0x180;
+ }
+ else
+ {
+ // HOTS: 1959E49
+ edi = edi + 0x0E;
+ edx = edx + esi - 0x1C0;
+ }
+ }
+ }
+
+ // HOTS: 1959E53:
+ // Calculate the number of bits set in the value of "ecx"
+ ecx = ~Struct68_00.ItemBits.Uint32Array[edi];
+ eax = GetNumberOfSetBits(ecx);
+ esi = eax >> 0x18;
+
+ if(edx >= esi)
+ {
+ // HOTS: 1959ea4
+ ecx = ~Struct68_00.ItemBits.Uint32Array[++edi];
+ edx = edx - esi;
+ eax = GetNumberOfSetBits(ecx);
+ }
+
+ // HOTS: 1959eea
+ // ESI gets the number of set bits in the lower 16 bits of ECX
+ esi = (eax >> 0x08) & 0xFF;
+ edi = edi << 0x05;
+ if(edx < esi)
+ {
+ // HOTS: 1959EFC
+ eax = eax & 0xFF;
+ if(edx >= eax)
+ {
+ // HOTS: 1959F05
+ ecx >>= 0x08;
+ edi += 0x08;
+ edx -= eax;
+ }
+ }
+ else
+ {
+ // HOTS: 1959F0D
+ eax = (eax >> 0x10) & 0xFF;
+ if(edx < eax)
+ {
+ // HOTS: 1959F19
+ ecx >>= 0x10;
+ edi += 0x10;
+ edx -= esi;
+ }
+ else
+ {
+ // HOTS: 1959F23
+ ecx >>= 0x18;
+ edi += 0x18;
+ edx -= eax;
+ }
+ }
+
+ // HOTS: 1959f2b
+ edx = edx << 0x08;
+ ecx = ecx & 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((ecx + edx) < sizeof(table_1BA1818));
+ return table_1BA1818[ecx + edx] + edi;
+}
+
+DWORD TFileNameDatabase::sub_1959F50(DWORD arg_0)
+{
+ PTRIPLET pTriplet;
+ PDWORD ItemArray;
+ DWORD eax, ebx, ecx, edx, esi, edi;
+
+ edx = arg_0;
+ eax = arg_0 >> 0x09;
+ if((arg_0 & 0x1FF) == 0)
+ return Struct68_00.ArrayDwords_50.Uint32Array[eax];
+
+ ItemArray = Struct68_00.ArrayDwords_50.Uint32Array + eax;
+ eax = (ItemArray[0] >> 0x09);
+ edi = (ItemArray[1] + 0x1FF) >> 0x09;
+
+ if((eax + 0x0A) > edi)
+ {
+ // HOTS: 01959F94
+ pTriplet = Struct68_00.BaseValues.TripletArray + eax + 1;
+ while(edx >= pTriplet->BaseValue)
+ {
+ // HOTS: 1959FA3
+ pTriplet++;
+ eax++;
+ }
+ }
+ else
+ {
+ // Binary search
+ // HOTS: 1959FAD
+ if((eax + 1) < edi)
+ {
+ // HOTS: 1959FB4
+ esi = (edi + eax) >> 1;
+ if(edx < Struct68_00.BaseValues.TripletArray[esi].BaseValue)
+ {
+ // HOTS: 1959FC4
+ edi = esi;
+ }
+ else
+ {
+ // HOTS: 1959FC8
+ eax = esi;
+ }
+ }
+ }
+
+ // HOTS: 1959FD4
+ pTriplet = Struct68_00.BaseValues.TripletArray + eax;
+ edx = edx - pTriplet->BaseValue;
+ edi = eax << 0x04;
+ eax = pTriplet->Value2;
+ ebx = (eax >> 0x17);
+ if(edx < ebx)
+ {
+ // HOTS: 1959FF1
+ esi = (eax >> 0x07) & 0xFF;
+ if(edx < esi)
+ {
+ // HOTS: 0195A000
+ eax = eax & 0x7F;
+ if(edx >= eax)
+ {
+ // HOTS: 195A007
+ edi = edi + 2;
+ edx = edx - eax;
+ }
+ }
+ else
+ {
+ // HOTS: 195A00E
+ eax = (eax >> 0x0F) & 0xFF;
+ if(edx < eax)
+ {
+ // HOTS: 195A01A
+ edi += 4;
+ edx = edx - esi;
+ }
+ else
+ {
+ // HOTS: 195A01F
+ edi += 6;
+ edx = edx - eax;
+ }
+ }
+ }
+ else
+ {
+ // HOTS: 195A026
+ esi = pTriplet->Value3;
+ eax = (pTriplet->Value3 >> 0x09) & 0x1FF;
+ if(edx < eax)
+ {
+ // HOTS: 195A037
+ esi = esi & 0x1FF;
+ if(edx < esi)
+ {
+ // HOTS: 195A041
+ edi = edi + 8;
+ edx = edx - ebx;
+ }
+ else
+ {
+ // HOTS: 195A048
+ edi = edi + 0x0A;
+ edx = edx - esi;
+ }
+ }
+ else
+ {
+ // HOTS: 195A04D
+ esi = (esi >> 0x12) & 0x1FF;
+ if(edx < esi)
+ {
+ // HOTS: 195A05A
+ edi = edi + 0x0C;
+ edx = edx - eax;
+ }
+ else
+ {
+ // HOTS: 195A061
+ edi = edi + 0x0E;
+ edx = edx - esi;
+ }
+ }
+ }
+
+ // HOTS: 195A066
+ esi = Struct68_00.ItemBits.Uint32Array[edi];
+ eax = GetNumberOfSetBits(esi);
+ ecx = eax >> 0x18;
+
+ if(edx >= ecx)
+ {
+ // HOTS: 195A0B2
+ esi = Struct68_00.ItemBits.Uint32Array[++edi];
+ edx = edx - ecx;
+ eax = GetNumberOfSetBits(esi);
+ }
+
+ // HOTS: 195A0F6
+ ecx = (eax >> 0x08) & 0xFF;
+
+ edi = (edi << 0x05);
+ if(edx < ecx)
+ {
+ // HOTS: 195A111
+ eax = eax & 0xFF;
+ if(edx >= eax)
+ {
+ // HOTS: 195A111
+ edi = edi + 0x08;
+ esi = esi >> 0x08;
+ edx = edx - eax;
+ }
+ }
+ else
+ {
+ // HOTS: 195A119
+ eax = (eax >> 0x10) & 0xFF;
+ if(edx < eax)
+ {
+ // HOTS: 195A125
+ esi = esi >> 0x10;
+ edi = edi + 0x10;
+ edx = edx - ecx;
+ }
+ else
+ {
+ // HOTS: 195A12F
+ esi = esi >> 0x18;
+ edi = edi + 0x18;
+ edx = edx - eax;
+ }
+ }
+
+ esi = esi & 0xFF;
+ edx = edx << 0x08;
+
+ // BUGBUG: Potential buffer overflow
+ // Happens in Heroes of the Storm when arg_0 == 0x5B
+ assert((esi + edx) < sizeof(table_1BA1818));
+ return table_1BA1818[esi + edx] + edi;
+}
+
+// HOTS: 1957970
+bool TFileNameDatabase::CheckNextPathFragment(TMndxFindResult * pStruct1C)
+{
+ TStruct40 * pStruct40 = pStruct1C->pStruct40;
+ LPBYTE pbPathName = (LPBYTE)pStruct1C->szSearchMask;
+ DWORD CollisionIndex;
+ DWORD NameFragIndex;
+ DWORD SaveCharIndex;
+ DWORD HiBitsIndex;
+ DWORD FragOffs;
+
+ // Calculate index of the next name fragment in the name fragment table
+ NameFragIndex = ((pStruct40->ItemIndex << 0x05) ^ pStruct40->ItemIndex ^ pbPathName[pStruct40->CharIndex]) & NameFragIndexMask;
+
+ // Does the hash value match?
+ if(NameFragTable.NameFragArray[NameFragIndex].ItemIndex == pStruct40->ItemIndex)
+ {
+ // Check if there is single character match
+ if(IS_SINGLE_CHAR_MATCH(NameFragTable, NameFragIndex))
+ {
+ pStruct40->ItemIndex = NameFragTable.NameFragArray[NameFragIndex].NextIndex;
+ pStruct40->CharIndex++;
+ return true;
+ }
+
+ // Check if there is a name fragment match
+ if(NextDB.pDB != NULL)
+ {
+ if(!NextDB.pDB->sub_1957B80(pStruct1C, NameFragTable.NameFragArray[NameFragIndex].FragOffs))
+ return false;
+ }
+ else
+ {
+ if(!IndexStruct_174.CheckNameFragment(pStruct1C, NameFragTable.NameFragArray[NameFragIndex].FragOffs))
+ return false;
+ }
+
+ pStruct40->ItemIndex = NameFragTable.NameFragArray[NameFragIndex].NextIndex;
+ return true;
+ }
+
+ //
+ // Conflict: Multiple hashes give the same table index
+ //
+
+ // HOTS: 1957A0E
+ CollisionIndex = sub_1959CB0(pStruct40->ItemIndex) + 1;
+ if(!Struct68_00.IsItemPresent(CollisionIndex))
+ return false;
+
+ pStruct40->ItemIndex = (CollisionIndex - pStruct40->ItemIndex - 1);
+ HiBitsIndex = 0xFFFFFFFF;
+
+// CascDumpSparseArray("E:\\casc-array-68.txt", &FileNameIndexes);
+// CascDumpSparseArray("E:\\casc-array-D0.txt", &Struct68_D0);
+
+ // HOTS: 1957A41:
+ do
+ {
+ // HOTS: 1957A41
+ // Check if the low 8 bits if the fragment offset contain a single character
+ // or an offset to a name fragment
+ if(Struct68_D0.IsItemPresent(pStruct40->ItemIndex))
+ {
+ if(HiBitsIndex == 0xFFFFFFFF)
+ {
+ // HOTS: 1957A6C
+ HiBitsIndex = Struct68_D0.GetItemValue(pStruct40->ItemIndex);
+ }
+ else
+ {
+ // HOTS: 1957A7F
+ HiBitsIndex++;
+ }
+
+ // HOTS: 1957A83
+ SaveCharIndex = pStruct40->CharIndex;
+
+ // Get the name fragment offset as combined value from lower 8 bits and upper bits
+ FragOffs = GetNameFragmentOffsetEx(pStruct40->ItemIndex, HiBitsIndex);
+
+ // Compare the string with the fragment name database
+ if(NextDB.pDB != NULL)
+ {
+ // HOTS: 1957AEC
+ if(NextDB.pDB->sub_1957B80(pStruct1C, FragOffs))
+ return true;
+ }
+ else
+ {
+ // HOTS: 1957AF7
+ if(IndexStruct_174.CheckNameFragment(pStruct1C, FragOffs))
+ return true;
+ }
+
+ // HOTS: 1957B0E
+ // If there was partial match with the fragment, end the search
+ if(pStruct40->CharIndex != SaveCharIndex)
+ return false;
+ }
+ else
+ {
+ // HOTS: 1957B1C
+ if(FrgmDist_LoBits.ByteArray[pStruct40->ItemIndex] == pStruct1C->szSearchMask[pStruct40->CharIndex])
+ {
+ pStruct40->CharIndex++;
+ return true;
+ }
+ }
+
+ // HOTS: 1957B32
+ pStruct40->ItemIndex++;
+ CollisionIndex++;
+ }
+ while(Struct68_00.IsItemPresent(CollisionIndex));
+ return false;
+}
+
+// HOTS: 1957B80
+bool TFileNameDatabase::sub_1957B80(TMndxFindResult * pStruct1C, DWORD arg_4)
+{
+ TStruct40 * pStruct40 = pStruct1C->pStruct40;
+ PNAME_FRAG pNameEntry;
+ DWORD FragOffs;
+ DWORD eax, edi;
+
+ edi = arg_4;
+
+ // HOTS: 1957B95
+ for(;;)
+ {
+ pNameEntry = NameFragTable.NameFragArray + (edi & NameFragIndexMask);
+ if(edi == pNameEntry->NextIndex)
+ {
+ // HOTS: 01957BB4
+ if((pNameEntry->FragOffs & 0xFFFFFF00) != 0xFFFFFF00)
+ {
+ // HOTS: 1957BC7
+ if(NextDB.pDB != NULL)
+ {
+ // HOTS: 1957BD3
+ if(!NextDB.pDB->sub_1957B80(pStruct1C, pNameEntry->FragOffs))
+ return false;
+ }
+ else
+ {
+ // HOTS: 1957BE0
+ if(!IndexStruct_174.CheckNameFragment(pStruct1C, pNameEntry->FragOffs))
+ return false;
+ }
+ }
+ else
+ {
+ // HOTS: 1957BEE
+ if(pStruct1C->szSearchMask[pStruct40->CharIndex] != (char)pNameEntry->FragOffs)
+ return false;
+ pStruct40->CharIndex++;
+ }
+
+ // HOTS: 1957C05
+ edi = pNameEntry->ItemIndex;
+ if(edi == 0)
+ return true;
+
+ if(pStruct40->CharIndex >= pStruct1C->cchSearchMask)
+ return false;
+ }
+ else
+ {
+ // HOTS: 1957C30
+ if(Struct68_D0.IsItemPresent(edi))
+ {
+ // HOTS: 1957C4C
+ if(NextDB.pDB != NULL)
+ {
+ // HOTS: 1957C58
+ FragOffs = GetNameFragmentOffset(edi);
+ if(!NextDB.pDB->sub_1957B80(pStruct1C, FragOffs))
+ return false;
+ }
+ else
+ {
+ // HOTS: 1957350
+ FragOffs = GetNameFragmentOffset(edi);
+ if(!IndexStruct_174.CheckNameFragment(pStruct1C, FragOffs))
+ return false;
+ }
+ }
+ else
+ {
+ // HOTS: 1957C8E
+ if(FrgmDist_LoBits.ByteArray[edi] != pStruct1C->szSearchMask[pStruct40->CharIndex])
+ return false;
+
+ pStruct40->CharIndex++;
+ }
+
+ // HOTS: 1957CB2
+ if(edi <= field_214)
+ return true;
+
+ if(pStruct40->CharIndex >= pStruct1C->cchSearchMask)
+ return false;
+
+ eax = sub_1959F50(edi);
+ edi = (eax - edi - 1);
+ }
+ }
+}
+
+// HOTS: 1958D70
+void TFileNameDatabase::sub_1958D70(TMndxFindResult * pStruct1C, DWORD arg_4)
+{
+ TStruct40 * pStruct40 = pStruct1C->pStruct40;
+ PNAME_FRAG pNameEntry;
+
+ // HOTS: 1958D84
+ for(;;)
+ {
+ pNameEntry = NameFragTable.NameFragArray + (arg_4 & NameFragIndexMask);
+ if(arg_4 == pNameEntry->NextIndex)
+ {
+ // HOTS: 1958DA6
+ if((pNameEntry->FragOffs & 0xFFFFFF00) != 0xFFFFFF00)
+ {
+ // HOTS: 1958DBA
+ if(NextDB.pDB != NULL)
+ {
+ NextDB.pDB->sub_1958D70(pStruct1C, pNameEntry->FragOffs);
+ }
+ else
+ {
+ IndexStruct_174.CopyNameFragment(pStruct1C, pNameEntry->FragOffs);
+ }
+ }
+ else
+ {
+ // HOTS: 1958DE7
+ // Insert the low 8 bits to the path being built
+ pStruct40->array_00.InsertOneItem_CHAR((char)(pNameEntry->FragOffs & 0xFF));
+ }
+
+ // HOTS: 1958E71
+ arg_4 = pNameEntry->ItemIndex;
+ if(arg_4 == 0)
+ return;
+ }
+ else
+ {
+ // HOTS: 1958E8E
+ if(Struct68_D0.IsItemPresent(arg_4))
+ {
+ DWORD FragOffs;
+
+ // HOTS: 1958EAF
+ FragOffs = GetNameFragmentOffset(arg_4);
+ if(NextDB.pDB != NULL)
+ {
+ NextDB.pDB->sub_1958D70(pStruct1C, FragOffs);
+ }
+ else
+ {
+ IndexStruct_174.CopyNameFragment(pStruct1C, FragOffs);
+ }
+ }
+ else
+ {
+ // HOTS: 1958F50
+ // Insert one character to the path being built
+ pStruct40->array_00.InsertOneItem_CHAR(FrgmDist_LoBits.CharArray[arg_4]);
+ }
+
+ // HOTS: 1958FDE
+ if(arg_4 <= field_214)
+ return;
+
+ arg_4 = 0xFFFFFFFF - arg_4 + sub_1959F50(arg_4);
+ }
+ }
+}
+
+// HOTS: 1959010
+bool TFileNameDatabase::sub_1959010(TMndxFindResult * pStruct1C, DWORD arg_4)
+{
+ TStruct40 * pStruct40 = pStruct1C->pStruct40;
+ PNAME_FRAG pNameEntry;
+
+ // HOTS: 1959024
+ for(;;)
+ {
+ pNameEntry = NameFragTable.NameFragArray + (arg_4 & NameFragIndexMask);
+ if(arg_4 == pNameEntry->NextIndex)
+ {
+ // HOTS: 1959047
+ if((pNameEntry->FragOffs & 0xFFFFFF00) != 0xFFFFFF00)
+ {
+ // HOTS: 195905A
+ if(NextDB.pDB != NULL)
+ {
+ if(!NextDB.pDB->sub_1959010(pStruct1C, pNameEntry->FragOffs))
+ return false;
+ }
+ else
+ {
+ if(!IndexStruct_174.CheckAndCopyNameFragment(pStruct1C, pNameEntry->FragOffs))
+ return false;
+ }
+ }
+ else
+ {
+ // HOTS: 1959092
+ if((char)(pNameEntry->FragOffs & 0xFF) != pStruct1C->szSearchMask[pStruct40->CharIndex])
+ return false;
+
+ // Insert the low 8 bits to the path being built
+ pStruct40->array_00.InsertOneItem_CHAR((char)(pNameEntry->FragOffs & 0xFF));
+ pStruct40->CharIndex++;
+ }
+
+ // HOTS: 195912E
+ arg_4 = pNameEntry->ItemIndex;
+ if(arg_4 == 0)
+ return true;
+ }
+ else
+ {
+ // HOTS: 1959147
+ if(Struct68_D0.IsItemPresent(arg_4))
+ {
+ DWORD FragOffs;
+
+ // HOTS: 195917C
+ FragOffs = GetNameFragmentOffset(arg_4);
+ if(NextDB.pDB != NULL)
+ {
+ if(!NextDB.pDB->sub_1959010(pStruct1C, FragOffs))
+ return false;
+ }
+ else
+ {
+ if(!IndexStruct_174.CheckAndCopyNameFragment(pStruct1C, FragOffs))
+ return false;
+ }
+ }
+ else
+ {
+ // HOTS: 195920E
+ if(FrgmDist_LoBits.CharArray[arg_4] != pStruct1C->szSearchMask[pStruct40->CharIndex])
+ return false;
+
+ // Insert one character to the path being built
+ pStruct40->array_00.InsertOneItem_CHAR(FrgmDist_LoBits.CharArray[arg_4]);
+ pStruct40->CharIndex++;
+ }
+
+ // HOTS: 19592B6
+ if(arg_4 <= field_214)
+ return true;
+
+ arg_4 = 0xFFFFFFFF - arg_4 + sub_1959F50(arg_4);
+ }
+
+ // HOTS: 19592D5
+ if(pStruct40->CharIndex >= pStruct1C->cchSearchMask)
+ break;
+ }
+
+ sub_1958D70(pStruct1C, arg_4);
+ return true;
+}
+
+// HOTS: 1959460
+bool TFileNameDatabase::sub_1959460(TMndxFindResult * pStruct1C)
+{
+ TStruct40 * pStruct40 = pStruct1C->pStruct40;
+ PPATH_STOP pPathStop;
+ PATH_STOP PathStop;
+ DWORD NewMaxItemCount;
+ DWORD FragOffs;
+ DWORD edi;
+
+ if(pStruct40->SearchPhase == CASC_SEARCH_FINISHED)
+ return false;
+
+ if(pStruct40->SearchPhase != CASC_SEARCH_SEARCHING)
+ {
+ // HOTS: 1959489
+ pStruct40->InitSearchBuffers();
+
+ // If the caller passed a part of the search path, we need to find that one
+ while(pStruct40->CharIndex < pStruct1C->cchSearchMask)
+ {
+ if(!sub_1958B00(pStruct1C))
+ {
+ pStruct40->SearchPhase = CASC_SEARCH_FINISHED;
+ return false;
+ }
+ }
+
+ // HOTS: 19594b0
+ PathStop.ItemIndex = pStruct40->ItemIndex;
+ PathStop.field_4 = 0;
+ PathStop.field_8 = pStruct40->array_00.ItemCount;
+ PathStop.field_C = 0xFFFFFFFF;
+ PathStop.field_10 = 0xFFFFFFFF;
+ pStruct40->PathStops.InsertOneItem_PATH_STOP(PathStop);
+ pStruct40->ItemCount = 1;
+
+ if(FileNameIndexes.IsItemPresent(pStruct40->ItemIndex))
+ {
+ pStruct1C->szFoundPath = pStruct40->array_00.FirstValid.Chars;
+ pStruct1C->cchFoundPath = pStruct40->array_00.ItemCount;
+ pStruct1C->FileNameIndex = FileNameIndexes.GetItemValue(pStruct40->ItemIndex);
+ return true;
+ }
+ }
+
+ // HOTS: 1959522
+ for(;;)
+ {
+ // HOTS: 1959530
+ if(pStruct40->ItemCount == pStruct40->PathStops.ItemCount)
+ {
+ PPATH_STOP pLastStop;
+ DWORD CollisionIndex;
+
+ pLastStop = pStruct40->PathStops.FirstValid.PathStopPtr + pStruct40->PathStops.ItemCount - 1;
+ CollisionIndex = sub_1959CB0(pLastStop->ItemIndex) + 1;
+
+ // Insert a new structure
+ PathStop.ItemIndex = CollisionIndex - pLastStop->ItemIndex - 1;;
+ PathStop.field_4 = CollisionIndex;
+ PathStop.field_8 = 0;
+ PathStop.field_C = 0xFFFFFFFF;
+ PathStop.field_10 = 0xFFFFFFFF;
+ pStruct40->PathStops.InsertOneItem_PATH_STOP(PathStop);
+ }
+
+ // HOTS: 19595BD
+ pPathStop = pStruct40->PathStops.FirstValid.PathStopPtr + pStruct40->ItemCount;
+
+ // HOTS: 19595CC
+ if(Struct68_00.IsItemPresent(pPathStop->field_4++))
+ {
+ // HOTS: 19595F2
+ pStruct40->ItemCount++;
+
+ if(Struct68_D0.IsItemPresent(pPathStop->ItemIndex))
+ {
+ // HOTS: 1959617
+ if(pPathStop->field_C == 0xFFFFFFFF)
+ pPathStop->field_C = Struct68_D0.GetItemValue(pPathStop->ItemIndex);
+ else
+ pPathStop->field_C++;
+
+ // HOTS: 1959630
+ FragOffs = GetNameFragmentOffsetEx(pPathStop->ItemIndex, pPathStop->field_C);
+ if(NextDB.pDB != NULL)
+ {
+ // HOTS: 1959649
+ NextDB.pDB->sub_1958D70(pStruct1C, FragOffs);
+ }
+ else
+ {
+ // HOTS: 1959654
+ IndexStruct_174.CopyNameFragment(pStruct1C, FragOffs);
+ }
+ }
+ else
+ {
+ // HOTS: 1959665
+ // Insert one character to the path being built
+ pStruct40->array_00.InsertOneItem_CHAR(FrgmDist_LoBits.CharArray[pPathStop->ItemIndex]);
+ }
+
+ // HOTS: 19596AE
+ pPathStop->field_8 = pStruct40->array_00.ItemCount;
+
+ // HOTS: 19596b6
+ if(FileNameIndexes.IsItemPresent(pPathStop->ItemIndex))
+ {
+ // HOTS: 19596D1
+ if(pPathStop->field_10 == 0xFFFFFFFF)
+ {
+ // HOTS: 19596D9
+ pPathStop->field_10 = FileNameIndexes.GetItemValue(pPathStop->ItemIndex);
+ }
+ else
+ {
+ pPathStop->field_10++;
+ }
+
+ // HOTS: 1959755
+ pStruct1C->szFoundPath = pStruct40->array_00.FirstValid.Chars;
+ pStruct1C->cchFoundPath = pStruct40->array_00.ItemCount;
+ pStruct1C->FileNameIndex = pPathStop->field_10;
+ return true;
+ }
+ }
+ else
+ {
+ // HOTS: 19596E9
+ if(pStruct40->ItemCount == 1)
+ {
+ pStruct40->SearchPhase = CASC_SEARCH_FINISHED;
+ return false;
+ }
+
+ // HOTS: 19596F5
+ pPathStop = pStruct40->PathStops.FirstValid.PathStopPtr + pStruct40->ItemCount - 1;
+ pPathStop->ItemIndex++;
+
+ pPathStop = pStruct40->PathStops.FirstValid.PathStopPtr + pStruct40->ItemCount - 2;
+ edi = pPathStop->field_8;
+
+ if(edi > pStruct40->array_00.MaxItemCount)
+ {
+ // HOTS: 1959717
+ NewMaxItemCount = edi;
+
+ if(pStruct40->array_00.MaxItemCount > (edi / 2))
+ {
+ if(pStruct40->array_00.MaxItemCount > 0x7FFFFFFF)
+ {
+ NewMaxItemCount = 0xFFFFFFFF;
+ }
+ else
+ {
+ NewMaxItemCount = pStruct40->array_00.MaxItemCount + pStruct40->array_00.MaxItemCount;
+ }
+ }
+
+ pStruct40->array_00.SetMaxItems_CHARS(NewMaxItemCount);
+ }
+
+ // HOTS: 1959749
+ pStruct40->array_00.ItemCount = edi;
+ pStruct40->ItemCount--;
+ }
+ }
+}
+
+// HOTS: 1958B00
+bool TFileNameDatabase::sub_1958B00(TMndxFindResult * pStruct1C)
+{
+ TStruct40 * pStruct40 = pStruct1C->pStruct40;
+ LPBYTE pbPathName = (LPBYTE)pStruct1C->szSearchMask;
+ DWORD CollisionIndex;
+ DWORD FragmentOffset;
+ DWORD SaveCharIndex;
+ DWORD ItemIndex;
+ DWORD FragOffs;
+ DWORD var_4;
+
+ ItemIndex = pbPathName[pStruct40->CharIndex] ^ (pStruct40->ItemIndex << 0x05) ^ pStruct40->ItemIndex;
+ ItemIndex = ItemIndex & NameFragIndexMask;
+ if(pStruct40->ItemIndex == NameFragTable.NameFragArray[ItemIndex].ItemIndex)
+ {
+ // HOTS: 1958B45
+ FragmentOffset = NameFragTable.NameFragArray[ItemIndex].FragOffs;
+ if((FragmentOffset & 0xFFFFFF00) == 0xFFFFFF00)
+ {
+ // HOTS: 1958B88
+ pStruct40->array_00.InsertOneItem_CHAR((char)FragmentOffset);
+ pStruct40->ItemIndex = NameFragTable.NameFragArray[ItemIndex].NextIndex;
+ pStruct40->CharIndex++;
+ return true;
+ }
+
+ // HOTS: 1958B59
+ if(NextDB.pDB != NULL)
+ {
+ if(!NextDB.pDB->sub_1959010(pStruct1C, FragmentOffset))
+ return false;
+ }
+ else
+ {
+ if(!IndexStruct_174.CheckAndCopyNameFragment(pStruct1C, FragmentOffset))
+ return false;
+ }
+
+ // HOTS: 1958BCA
+ pStruct40->ItemIndex = NameFragTable.NameFragArray[ItemIndex].NextIndex;
+ return true;
+ }
+
+ // HOTS: 1958BE5
+ CollisionIndex = sub_1959CB0(pStruct40->ItemIndex) + 1;
+ if(!Struct68_00.IsItemPresent(CollisionIndex))
+ return false;
+
+ pStruct40->ItemIndex = (CollisionIndex - pStruct40->ItemIndex - 1);
+ var_4 = 0xFFFFFFFF;
+
+ // HOTS: 1958C20
+ for(;;)
+ {
+ if(Struct68_D0.IsItemPresent(pStruct40->ItemIndex))
+ {
+ // HOTS: 1958C0E
+ if(var_4 == 0xFFFFFFFF)
+ {
+ // HOTS: 1958C4B
+ var_4 = Struct68_D0.GetItemValue(pStruct40->ItemIndex);
+ }
+ else
+ {
+ var_4++;
+ }
+
+ // HOTS: 1958C62
+ SaveCharIndex = pStruct40->CharIndex;
+
+ FragOffs = GetNameFragmentOffsetEx(pStruct40->ItemIndex, var_4);
+ if(NextDB.pDB != NULL)
+ {
+ // HOTS: 1958CCB
+ if(NextDB.pDB->sub_1959010(pStruct1C, FragOffs))
+ return true;
+ }
+ else
+ {
+ // HOTS: 1958CD6
+ if(IndexStruct_174.CheckAndCopyNameFragment(pStruct1C, FragOffs))
+ return true;
+ }
+
+ // HOTS: 1958CED
+ if(SaveCharIndex != pStruct40->CharIndex)
+ return false;
+ }
+ else
+ {
+ // HOTS: 1958CFB
+ if(FrgmDist_LoBits.ByteArray[pStruct40->ItemIndex] == pStruct1C->szSearchMask[pStruct40->CharIndex])
+ {
+ // HOTS: 1958D11
+ pStruct40->array_00.InsertOneItem_CHAR(FrgmDist_LoBits.ByteArray[pStruct40->ItemIndex]);
+ pStruct40->CharIndex++;
+ return true;
+ }
+ }
+
+ // HOTS: 1958D11
+ pStruct40->ItemIndex++;
+ CollisionIndex++;
+
+ if(!Struct68_00.IsItemPresent(CollisionIndex))
+ break;
+ }
+
+ return false;
+}
+
+// HOTS: 1957EF0
+bool TFileNameDatabase::FindFileInDatabase(TMndxFindResult * pStruct1C)
+{
+ TStruct40 * pStruct40 = pStruct1C->pStruct40;
+
+ pStruct40->ItemIndex = 0;
+ pStruct40->CharIndex = 0;
+ pStruct40->SearchPhase = CASC_SEARCH_INITIALIZING;
+
+ if(pStruct1C->cchSearchMask > 0)
+ {
+ while(pStruct40->CharIndex < pStruct1C->cchSearchMask)
+ {
+ // HOTS: 01957F12
+ if(!CheckNextPathFragment(pStruct1C))
+ return false;
+ }
+ }
+
+ // HOTS: 1957F26
+ if(!FileNameIndexes.IsItemPresent(pStruct40->ItemIndex))
+ return false;
+
+ pStruct1C->szFoundPath = pStruct1C->szSearchMask;
+ pStruct1C->cchFoundPath = pStruct1C->cchSearchMask;
+ pStruct1C->FileNameIndex = FileNameIndexes.GetItemValue(pStruct40->ItemIndex);
+ return true;
+}
+
+// HOTS: 1959790
+int TFileNameDatabase::LoadFromStream(TByteStream & InStream)
+{
+ DWORD dwBitMask;
+ int nError;
+
+ nError = Struct68_00.LoadFromStream_Exchange(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = FileNameIndexes.LoadFromStream_Exchange(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = Struct68_D0.LoadFromStream_Exchange(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // HOTS: 019597CD
+ nError = FrgmDist_LoBits.LoadBytes_Copy(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = FrgmDist_HiBits.LoadFromStream_Exchange(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // HOTS: 019597F5
+ nError = IndexStruct_174.LoadFromStream_Exchange(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // HOTS: 0195980A
+ if(Struct68_D0.ValidItemCount != 0 && IndexStruct_174.NameFragments.ItemCount == 0)
+ {
+ TFileNameDatabase * pNextDB = new TFileNameDatabase;
+
+ nError = NextDB.SetDatabase(pNextDB);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ if(NextDB.pDB == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ nError = NextDB.pDB->LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ }
+
+ // HOTS: 0195986B
+ nError = NameFragTable.LoadFragmentInfos_Copy(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ NameFragIndexMask = NameFragTable.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);
+}
+
+// HOTS: 19598D0
+int TFileNameDatabase::LoadFromStream_Exchange(TByteStream & InStream)
+{
+ TFileNameDatabase TempDatabase;
+ ARRAY_POINTER Pointer;
+ DWORD dwSignature;
+ int nError;
+
+ // Get pointer to MAR signature
+ nError = InStream.GetBytes(sizeof(DWORD), &Pointer);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // Verify the signature
+ dwSignature = Pointer.Uint32s[0];
+ if(dwSignature != CASC_MAR_SIGNATURE)
+ return ERROR_BAD_FORMAT;
+
+ nError = TempDatabase.LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ MarStream.ExchangeWith(InStream);
+ ExchangeWith(TempDatabase);
+ return ERROR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// TFileNameDatabasePtr functions
+
+// HOTS: 01956D70
+TFileNameDatabasePtr::TFileNameDatabasePtr()
+{
+ pDB = NULL;
+}
+
+TFileNameDatabasePtr::~TFileNameDatabasePtr()
+{
+ delete pDB;
+}
+
+// HOTS: 1956C60
+int TFileNameDatabasePtr::FindFileInDatabase(TMndxFindResult * pStruct1C)
+{
+ int nError = ERROR_SUCCESS;
+
+ if(pDB == NULL)
+ return ERROR_INVALID_PARAMETER;
+
+ nError = pStruct1C->CreateStruct40();
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ if(!pDB->FindFileInDatabase(pStruct1C))
+ nError = ERROR_FILE_NOT_FOUND;
+
+ pStruct1C->FreeStruct40();
+ return nError;
+}
+
+// HOTS: 1956CE0
+int TFileNameDatabasePtr::sub_1956CE0(TMndxFindResult * pStruct1C, bool * pbFindResult)
+{
+ int nError = ERROR_SUCCESS;
+
+ if(pDB == NULL)
+ return ERROR_INVALID_PARAMETER;
+
+ // Create the pStruct40, if not initialized yet
+ if(pStruct1C->pStruct40 == NULL)
+ {
+ nError = pStruct1C->CreateStruct40();
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ }
+
+ *pbFindResult = pDB->sub_1959460(pStruct1C);
+ return nError;
+}
+
+// HOTS: 1956D20
+int TFileNameDatabasePtr::GetFileNameCount(PDWORD PtrFileNameCount)
+{
+ if(pDB == NULL)
+ return ERROR_INVALID_PARAMETER;
+
+ PtrFileNameCount[0] = pDB->FileNameIndexes.ValidItemCount;
+ return ERROR_SUCCESS;
+}
+
+// HOTS: 1956DA0
+int TFileNameDatabasePtr::CreateDatabase(LPBYTE pbMarData, DWORD cbMarData)
+{
+ TFileNameDatabase * pDatabase;
+ TByteStream ByteStream;
+ int nError;
+
+ if(pbMarData == NULL && cbMarData != 0)
+ return ERROR_INVALID_PARAMETER;
+
+ pDatabase = new TFileNameDatabase;
+ if(pDatabase == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ nError = ByteStream.SetByteBuffer(pbMarData, cbMarData);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // HOTS: 1956E11
+ nError = pDatabase->LoadFromStream_Exchange(ByteStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ pDB = pDatabase;
+ return ERROR_SUCCESS;
+}
+
+// HOTS: 19584B0
+int TFileNameDatabasePtr::SetDatabase(TFileNameDatabase * pNewDB)
+{
+ if(pNewDB != NULL && pDB == pNewDB)
+ return ERROR_INVALID_PARAMETER;
+
+ if(pDB != NULL)
+ delete pDB;
+ pDB = pNewDB;
+ return ERROR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// Local functions - MAR file
+
+// HOTS: 00E94180
+static void MAR_FILE_CreateDatabase(PMAR_FILE pMarFile)
+{
+ pMarFile->pDatabasePtr = new TFileNameDatabasePtr;
+ if(pMarFile->pDatabasePtr != NULL)
+ pMarFile->pDatabasePtr->CreateDatabase(pMarFile->pbMarData, pMarFile->cbMarData);
+}
+
+static int MAR_FILE_SearchFile(PMAR_FILE pMarFile, TMndxFindResult * pStruct1C)
+{
+ return pMarFile->pDatabasePtr->FindFileInDatabase(pStruct1C);
+}
+
+static void MAR_FILE_Destructor(PMAR_FILE pMarFile)
+{
+ if(pMarFile != NULL)
+ {
+ if(pMarFile->pDatabasePtr != NULL)
+ delete pMarFile->pDatabasePtr;
+ if(pMarFile->pbMarData != NULL)
+ CASC_FREE(pMarFile->pbMarData);
+
+ CASC_FREE(pMarFile);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Package functions
+
+// TODO: When working, increment these values to lower number of (re)allocations
+#define CASC_PACKAGES_INIT 0x10
+#define CASC_PACKAGES_DELTA 0x10
+
+static PCASC_PACKAGES AllocatePackages(size_t nNameEntries, size_t nNameBufferMax)
+{
+ PCASC_PACKAGES pPackages;
+ size_t cbToAllocate;
+
+ // Allocate space
+ cbToAllocate = sizeof(CASC_PACKAGES) + (nNameEntries * sizeof(CASC_PACKAGE)) + nNameBufferMax;
+ pPackages = (PCASC_PACKAGES)CASC_ALLOC(BYTE, cbToAllocate);
+ if(pPackages != NULL)
+ {
+ // Fill the structure
+ memset(pPackages, 0, cbToAllocate);
+
+ // Init the list entries
+ pPackages->szNameBuffer = (char *)(&pPackages->Packages[nNameEntries]);
+ pPackages->NameEntries = nNameEntries;
+ pPackages->NameBufferUsed = 0;
+ pPackages->NameBufferMax = nNameBufferMax;
+ }
+
+ return pPackages;
+}
+
+static PCASC_PACKAGES InsertToPackageList(
+ PCASC_PACKAGES pPackages,
+ const char * szFileName,
+ size_t cchFileName,
+ size_t nPackageIndex)
+{
+ size_t nNewNameEntries = pPackages->NameEntries;
+ size_t nNewNameBufferMax = pPackages->NameBufferMax;
+ size_t cbToAllocate;
+ char * szNameBuffer;
+
+ // Need to reallocate?
+ while(nPackageIndex >= nNewNameEntries)
+ nNewNameEntries = nNewNameEntries + CASC_PACKAGES_DELTA;
+ if((pPackages->NameBufferUsed + cchFileName + 1) > nNewNameBufferMax)
+ nNewNameBufferMax = nNewNameBufferMax + 0x1000;
+
+ // If any of the two variables overflowed, we need to reallocate the name list
+ if(nNewNameEntries > pPackages->NameEntries || nNewNameBufferMax > pPackages->NameBufferMax)
+ {
+ PCASC_PACKAGES pOldPackages = pPackages;
+
+ // Allocate new name list
+ cbToAllocate = sizeof(CASC_PACKAGES) + (nNewNameEntries * sizeof(CASC_PACKAGE)) + nNewNameBufferMax;
+ pPackages = (PCASC_PACKAGES)CASC_ALLOC(BYTE, cbToAllocate);
+ if(pPackages == NULL)
+ return NULL;
+
+ // Copy the old entries
+ memset(pPackages, 0, cbToAllocate);
+ pPackages->szNameBuffer = szNameBuffer = (char *)(&pPackages->Packages[nNewNameEntries]);
+ memcpy(pPackages->szNameBuffer, pOldPackages->szNameBuffer, pOldPackages->NameBufferUsed);
+
+ // Copy the old entries
+ for(size_t i = 0; i < pOldPackages->NameEntries; i++)
+ {
+ if(pOldPackages->Packages[i].szFileName != NULL)
+ {
+ pPackages->Packages[i].szFileName = pPackages->szNameBuffer + (pOldPackages->Packages[i].szFileName - pOldPackages->szNameBuffer);
+ pPackages->Packages[i].nLength = pOldPackages->Packages[i].nLength;
+ }
+ }
+
+ // Fill the limits
+ pPackages->NameEntries = nNewNameEntries;
+ pPackages->NameBufferUsed = pOldPackages->NameBufferUsed;
+ pPackages->NameBufferMax = nNewNameBufferMax;
+
+ // Switch the name lists
+ CASC_FREE(pOldPackages);
+ }
+
+ // The slot is expected to be empty at the moment
+ assert(pPackages->Packages[nPackageIndex].szFileName == NULL);
+ assert(pPackages->Packages[nPackageIndex].nLength == 0);
+
+ // Set the file name entry
+ szNameBuffer = pPackages->szNameBuffer + pPackages->NameBufferUsed;
+ pPackages->Packages[nPackageIndex].szFileName = szNameBuffer;
+ pPackages->Packages[nPackageIndex].nLength = cchFileName;
+ memcpy(szNameBuffer, szFileName, cchFileName);
+ pPackages->NameBufferUsed += (cchFileName + 1);
+ return pPackages;
+}
+
+static int LoadPackageNames(TCascStorage * hs)
+{
+ TMndxFindResult Struct1C;
+ PCASC_PACKAGES pPackages = NULL;
+ PMAR_FILE pMarFile;
+
+ // Sanity checks
+ assert(hs->pMndxInfo != NULL);
+
+ // Prepare the file name search in the top level directory
+ pMarFile = hs->pMndxInfo->pMarFile1;
+ Struct1C.SetSearchPath("", 0);
+
+ // Allocate initial name list structure
+ pPackages = AllocatePackages(CASC_PACKAGES_INIT, 0x1000);
+ if(pPackages == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Keep searching as long as we find something
+ for(;;)
+ {
+ bool bFindResult = false;
+
+ // Search the next file name
+ pMarFile->pDatabasePtr->sub_1956CE0(&Struct1C, &bFindResult);
+ if(bFindResult == false)
+ break;
+
+ // Insert the found name to the top level directory list
+ pPackages = InsertToPackageList(pPackages, Struct1C.szFoundPath, Struct1C.cchFoundPath, Struct1C.FileNameIndex);
+ if(pPackages == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ // Set the name list to the CASC storage structure
+ hs->pPackages = pPackages;
+ return ERROR_SUCCESS;
+}
+
+PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName)
+{
+ PCASC_PACKAGE pMatching = NULL;
+ PCASC_PACKAGE pPackage;
+ size_t nMaxLength = 0;
+ size_t nLength = strlen(szFileName);
+
+ // Packages must be loaded
+ assert(hs->pPackages != NULL);
+ pPackage = hs->pPackages->Packages;
+
+ // Find the longest matching name
+ for(size_t i = 0; i < hs->pPackages->NameEntries; i++, pPackage++)
+ {
+ if(pPackage->szFileName != NULL && pPackage->nLength < nLength && pPackage->nLength > nMaxLength)
+ {
+ // Compare the package name
+ if(!strncmp(szFileName, pPackage->szFileName, pPackage->nLength))
+ {
+ pMatching = pPackage;
+ nMaxLength = pPackage->nLength;
+ }
+ }
+ }
+
+ // Give the package pointer or NULL if not found
+ return pMatching;
+}
+
+static bool FillFindData(TCascSearch * pSearch, PCASC_FIND_DATA pFindData, TMndxFindResult * pStruct1C)
+{
+ CASC_ROOT_KEY_INFO RootKeyInfo;
+ TCascStorage * hs = pSearch->hs;
+ PCASC_PACKAGE pPackage;
+ char * szStrippedName;
+ int nError;
+
+ // Sanity check
+ assert(pStruct1C->cchFoundPath < MAX_PATH);
+
+ // Fill the file name
+ memcpy(pFindData->szFileName, pStruct1C->szFoundPath, pStruct1C->cchFoundPath);
+ pFindData->szFileName[pStruct1C->cchFoundPath] = 0;
+ pFindData->dwFileSize = CASC_INVALID_SIZE;
+
+ // Fill the file size
+ pPackage = FindMndxPackage(hs, pFindData->szFileName);
+ if(pPackage != NULL)
+ {
+ // Cut the package name off the full path
+ szStrippedName = pFindData->szFileName + pPackage->nLength;
+ while(szStrippedName[0] == '/')
+ szStrippedName++;
+
+ nError = SearchMndxInfo(hs->pMndxInfo, szStrippedName, (DWORD)(pPackage - hs->pPackages->Packages), &RootKeyInfo);
+ if(nError == ERROR_SUCCESS)
+ {
+ pFindData->dwFileSize = (DWORD)RootKeyInfo.FileSize;
+ }
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Public functions - MNDX info
+
+int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
+{
+ PFILE_MNDX_HEADER pMndxHeader = (PFILE_MNDX_HEADER)pbRootFile;
+ PCASC_MNDX_INFO pMndxInfo;
+ FILE_MAR_INFO MarInfo;
+ PMAR_FILE pMarFile;
+ LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
+ DWORD cbToAllocate;
+ DWORD dwFilePointer = 0;
+ DWORD i;
+ int nError = ERROR_SUCCESS;
+
+ // Check signature and the other variables
+ if(pMndxHeader->Signature != CASC_MNDX_SIGNATURE || pMndxHeader->FormatVersion > 2 || pMndxHeader->FormatVersion < 1)
+ return ERROR_BAD_FORMAT;
+
+ // Allocate space for the CASC_MNDX_INFO structure
+ pMndxInfo = CASC_ALLOC(CASC_MNDX_INFO, 1);
+ if(pMndxInfo == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Copy the header into the MNDX info
+ memset(pMndxInfo, 0, sizeof(CASC_MNDX_INFO));
+ pMndxInfo->HeaderVersion = pMndxHeader->HeaderVersion;
+ pMndxInfo->FormatVersion = pMndxHeader->FormatVersion;
+ dwFilePointer += sizeof(FILE_MNDX_HEADER);
+
+ // Header version 2 has 2 extra fields that we need to load
+ if(pMndxInfo->HeaderVersion == 2)
+ {
+ if(!RootFileRead(pbRootFile + dwFilePointer, pbRootFileEnd, &pMndxInfo->field_1C, sizeof(DWORD) + sizeof(DWORD)))
+ return ERROR_FILE_CORRUPT;
+ dwFilePointer += sizeof(DWORD) + sizeof(DWORD);
+ }
+
+ // Load the rest of the file header
+ if(!RootFileRead(pbRootFile + dwFilePointer, pbRootFileEnd, &pMndxInfo->MarInfoOffset, 0x1C))
+ return ERROR_FILE_CORRUPT;
+
+ // Verify the structure
+ if(pMndxInfo->MarInfoCount > CASC_MAX_MAR_FILES || pMndxInfo->MarInfoSize != sizeof(FILE_MAR_INFO))
+ return ERROR_FILE_CORRUPT;
+
+ // Load all MAR infos
+ for(i = 0; i < pMndxInfo->MarInfoCount; i++)
+ {
+ // Load the n-th MAR info
+ dwFilePointer = pMndxInfo->MarInfoOffset + (pMndxInfo->MarInfoSize * i);
+ if(!RootFileRead(pbRootFile + dwFilePointer, pbRootFileEnd, &MarInfo, sizeof(FILE_MAR_INFO)))
+ return ERROR_FILE_CORRUPT;
+
+ // Allocate MAR_FILE structure
+ pMarFile = CASC_ALLOC(MAR_FILE, 1);
+ if(pMarFile == NULL)
+ {
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ // Allocate space for the MAR data
+ pMarFile->pDatabasePtr = NULL;
+ pMarFile->pbMarData = CASC_ALLOC(BYTE, MarInfo.MarDataSize);
+ pMarFile->cbMarData = MarInfo.MarDataSize;
+ if(pMarFile->pbMarData == NULL)
+ {
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ // Read the MAR data
+ if(!RootFileRead(pbRootFile + MarInfo.MarDataOffset, pbRootFileEnd, pMarFile->pbMarData, pMarFile->cbMarData))
+ {
+ nError = ERROR_FILE_CORRUPT;
+ break;
+ }
+
+ // HOTS: 00E94FF1
+ MAR_FILE_CreateDatabase(pMarFile);
+ if(i == 0)
+ pMndxInfo->pMarFile1 = pMarFile;
+ if(i == 1)
+ pMndxInfo->pMarFile2 = pMarFile;
+ if(i == 2)
+ pMndxInfo->pMarFile3 = pMarFile;
+ }
+
+ // All three MAR files must be loaded
+ // HOTS: 00E9503B
+ if(nError == ERROR_SUCCESS)
+ {
+ if(pMndxInfo->pMarFile1 == NULL || pMndxInfo->pMarFile2 == NULL || pMndxInfo->pMarFile3 == NULL)
+ nError = ERROR_BAD_FORMAT;
+ if(pMndxInfo->MndxEntrySize != sizeof(CASC_MNDX_ENTRY))
+ nError = ERROR_BAD_FORMAT;
+ }
+
+ // Load the complete array of MNDX entries
+ if(nError == ERROR_SUCCESS)
+ {
+ TFileNameDatabasePtr * pDbPtr = pMndxInfo->pMarFile2->pDatabasePtr;
+ DWORD FileNameCount;
+
+ nError = pDbPtr->GetFileNameCount(&FileNameCount);
+ if(nError == ERROR_SUCCESS && FileNameCount == pMndxInfo->MndxEntriesValid)
+ {
+ cbToAllocate = pMndxInfo->MndxEntriesTotal * pMndxInfo->MndxEntrySize;
+ pMndxInfo->pMndxEntries = (PCASC_MNDX_ENTRY)CASC_ALLOC(BYTE, cbToAllocate);
+ if(pMndxInfo->pMndxEntries != NULL)
+ {
+ if(!RootFileRead(pbRootFile + pMndxInfo->MndxEntriesOffset, pbRootFileEnd, pMndxInfo->pMndxEntries, cbToAllocate))
+ nError = ERROR_FILE_CORRUPT;
+ }
+ else
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ nError = ERROR_FILE_CORRUPT;
+ }
+
+ // Pick the valid MNDX entries and put them to a separate array
+ if(nError == ERROR_SUCCESS)
+ {
+ assert(pMndxInfo->MndxEntriesValid <= pMndxInfo->MndxEntriesTotal);
+ pMndxInfo->ppValidEntries = CASC_ALLOC(PCASC_MNDX_ENTRY, pMndxInfo->MndxEntriesValid + 1);
+ if(pMndxInfo->ppValidEntries != NULL)
+ {
+ PCASC_MNDX_ENTRY pMndxEntry = pMndxInfo->pMndxEntries;
+ DWORD ValidEntryCount = 1; // edx
+ DWORD nIndex1 = 0;
+
+ // The first entry is always valid
+ pMndxInfo->ppValidEntries[nIndex1++] = pMndxInfo->pMndxEntries;
+
+ // Put the remaining entries
+ for(i = 0; i < pMndxInfo->MndxEntriesTotal; i++, pMndxEntry++)
+ {
+ if(ValidEntryCount > pMndxInfo->MndxEntriesValid)
+ break;
+
+ if(pMndxEntry->Flags & 0x80000000)
+ {
+ pMndxInfo->ppValidEntries[nIndex1++] = pMndxEntry + 1;
+ ValidEntryCount++;
+ }
+ }
+
+ // Verify the final number of valid entries
+ if((ValidEntryCount - 1) != pMndxInfo->MndxEntriesValid)
+ nError = ERROR_BAD_FORMAT;
+
+ // Mark the MNDX info as fully loaded
+ pMndxInfo->bRootFileLoaded = true;
+ }
+ else
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ // Save the MNDX info to the archive storage
+ if(nError == ERROR_SUCCESS)
+ {
+ // Store the MNDX database into the archive
+ hs->pMndxInfo = pMndxInfo;
+ pMndxInfo = NULL;
+
+#if defined(_DEBUG) && defined(_X86_) && defined(CASCLIB_TEST)
+// CascDumpNameFragTable("E:\\casc-name-fragment-table.txt", hs->pMndxInfo->pMarFile1);
+// CascDumpFileNames("E:\\casc-listfile.txt", hs->pMndxInfo->pMarFile1);
+ TestMndxRootFile(hs->pMndxInfo);
+#endif
+ // Load the top level entries
+ nError = LoadPackageNames(hs);
+ }
+
+ // If anything failed, free the memory remaining allocated
+ if(nError != ERROR_SUCCESS)
+ {
+ if(pMndxInfo != NULL)
+ FreeMndxInfo(pMndxInfo);
+ pMndxInfo = NULL;
+ }
+
+ return nError;
+}
+
+int SearchMndxInfo(PCASC_MNDX_INFO pMndxInfo, const char * szFileName, DWORD dwPackage, PCASC_ROOT_KEY_INFO pFoundInfo)
+{
+ PCASC_MNDX_ENTRY pMndxEntry;
+ TMndxFindResult Struct1C;
+
+ // Search the database for the file name
+ if(pMndxInfo->bRootFileLoaded)
+ {
+ Struct1C.SetSearchPath(szFileName, strlen(szFileName));
+
+ // Search the file name in the second MAR info (the one with stripped package names)
+ if(MAR_FILE_SearchFile(pMndxInfo->pMarFile2, &Struct1C) != ERROR_SUCCESS)
+ return ERROR_FILE_NOT_FOUND;
+
+ // The found MNDX index must fall into range of valid MNDX entries
+ if(Struct1C.FileNameIndex < pMndxInfo->MndxEntriesValid)
+ {
+ // HOTS: E945F4
+ pMndxEntry = pMndxInfo->ppValidEntries[Struct1C.FileNameIndex];
+ while((pMndxEntry->Flags & 0x00FFFFFF) != dwPackage)
+ {
+ // The highest bit serves as a terminator if set
+ if(pMndxEntry->Flags & 0x80000000)
+ return ERROR_FILE_NOT_FOUND;
+
+ pMndxEntry++;
+ }
+
+ // Fill the root info
+ memcpy(pFoundInfo->EncodingKey, pMndxEntry->EncodingKey, MD5_HASH_SIZE);
+ pFoundInfo->FileSize = pMndxEntry->FileSize;
+ pFoundInfo->Flags = (BYTE)((pMndxEntry->Flags >> 0x18) & 0x3F);
+ return ERROR_SUCCESS;
+ }
+ }
+
+ return ERROR_FILE_NOT_FOUND;
+}
+
+bool DoStorageSearch_MNDX(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
+{
+ TMndxFindResult * pStruct1C = NULL;
+ PCASC_MNDX_INFO pMndxInfo = pSearch->hs->pMndxInfo;
+ PMAR_FILE pMarFile = pMndxInfo->pMarFile3;
+ bool bFindResult = false;
+
+ // Sanity checks
+ assert(pMndxInfo != NULL);
+
+ // If the first time, allocate the structure for the search result
+ if(pSearch->pStruct1C == NULL)
+ {
+ // Create the new search structure
+ pSearch->pStruct1C = pStruct1C = new TMndxFindResult;
+ if(pSearch->pStruct1C == NULL)
+ return false;
+
+ // Setup the search mask
+ pStruct1C->SetSearchPath("", 0);
+ }
+
+ // Make shortcut for the search structure
+ assert(pSearch->pStruct1C != NULL);
+ pStruct1C = (TMndxFindResult *)pSearch->pStruct1C;
+
+ // Search the next file name (our code)
+ pMarFile->pDatabasePtr->sub_1956CE0(pStruct1C, &bFindResult);
+ if(bFindResult)
+ return FillFindData(pSearch, pFindData, pStruct1C);
+
+ return false;
+}
+
+void FreeMndxInfo(PCASC_MNDX_INFO pMndxInfo)
+{
+ if(pMndxInfo != NULL)
+ {
+ if(pMndxInfo->pMarFile1 != NULL)
+ MAR_FILE_Destructor(pMndxInfo->pMarFile1);
+ if(pMndxInfo->pMarFile2 != NULL)
+ MAR_FILE_Destructor(pMndxInfo->pMarFile2);
+ if(pMndxInfo->pMarFile3 != NULL)
+ MAR_FILE_Destructor(pMndxInfo->pMarFile3);
+ if(pMndxInfo->ppValidEntries != NULL)
+ CASC_FREE(pMndxInfo->ppValidEntries);
+ if(pMndxInfo->pMndxEntries != NULL)
+ CASC_FREE(pMndxInfo->pMndxEntries);
+ CASC_FREE(pMndxInfo);
+ }
+}
+
+//----------------------------------------------------------------------------
+// Unit tests
+
+#if defined(_DEBUG) && defined(_X86_) && defined(CASCLIB_TEST)
+
+extern "C" {
+ DWORD _cdecl sub_19573D0_x86(TFileNameDatabase * pDB, DWORD arg_0, DWORD arg_4);
+ DWORD _cdecl sub_1957EF0_x86(TFileNameDatabase * pDB, TMndxFindResult * pStruct1C);
+ bool _cdecl sub_1959460_x86(TFileNameDatabase * pDB, TMndxFindResult * pStruct1C);
+ DWORD _cdecl GetItemValue_x86(TSparseArray * pStruct, DWORD dwKey);
+ DWORD _cdecl sub_1959CB0_x86(TFileNameDatabase * pDB, DWORD dwKey);
+ DWORD _cdecl sub_1959F50_x86(TFileNameDatabase * pDB, DWORD arg_0);
+}
+
+extern "C" void * allocate_zeroed_memory_x86(size_t bytes)
+{
+ return calloc(bytes, 1);
+}
+
+extern "C" void free_memory_x86(void * ptr)
+{
+ if(ptr != NULL)
+ {
+ free(ptr);
+ }
+}
+
+static int sub_1956CE0_x86(TFileNameDatabasePtr * pDatabasePtr, TMndxFindResult * pStruct1C, bool * pbFindResult)
+{
+ int nError = ERROR_SUCCESS;
+
+ if(pDatabasePtr->pDB == NULL)
+ return ERROR_INVALID_PARAMETER;
+
+ // Create the pStruct40, if not initialized yet
+ if(pStruct1C->pStruct40 == NULL)
+ {
+ nError = pStruct1C->CreateStruct40();
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ }
+
+ *pbFindResult = sub_1959460_x86(pDatabasePtr->pDB, pStruct1C);
+ return nError;
+}
+/*
+static void TestFileSearch_SubStrings(PMAR_FILE pMarFile, char * szFileName, size_t nLength)
+{
+ TMndxFindResult Struct1C_1;
+ TMndxFindResult Struct1C_2;
+
+// if(strcmp(szFileName, "mods/heroes.stormmod/base.stormassets/assets/textures/storm_temp_war3_btnstatup.dds"))
+// return;
+
+ // Perform search on anything, that is longer than 4 chars
+ while(nLength >= 4)
+ {
+ // Set a substring as search name
+ Struct1C_1.SetSearchPath(szFileName, nLength);
+ Struct1C_2.SetSearchPath(szFileName, nLength);
+ szFileName[nLength] = 0;
+
+ // Keep searching
+ for(;;)
+ {
+ bool bFindResult1 = false;
+ bool bFindResult2 = false;
+
+ // Search the next file name (orig HOTS code)
+ sub_1956CE0_x86(pMarFile->pDatabasePtr, &Struct1C_1, &bFindResult1);
+
+ // Search the next file name (our code)
+ pMarFile->pDatabasePtr->sub_1956CE0(&Struct1C_2, &bFindResult2);
+
+ // Check the result
+ assert(bFindResult1 == bFindResult2);
+ assert(Struct1C_1.cchFoundPath == Struct1C_1.cchFoundPath);
+ assert(Struct1C_1.FileNameIndex == Struct1C_2.FileNameIndex);
+ assert(strncmp(Struct1C_1.szFoundPath, Struct1C_2.szFoundPath, Struct1C_1.cchFoundPath) == 0);
+ assert(Struct1C_1.cchFoundPath < MAX_PATH);
+
+ // Stop the search in case of failure
+ if(bFindResult1 == false || bFindResult2 == false)
+ break;
+ }
+
+ // Free the search structures
+ Struct1C_1.FreeStruct40();
+ Struct1C_2.FreeStruct40();
+ nLength--;
+ }
+}
+*/
+
+static void TestFindPackage(PMAR_FILE pMarFile, const char * szPackageName)
+{
+ TMndxFindResult Struct1C;
+
+ // Search the database for the file name
+ Struct1C.SetSearchPath(szPackageName, strlen(szPackageName));
+
+ // Search the file name in the second MAR info (the one with stripped package names)
+ MAR_FILE_SearchFile(pMarFile, &Struct1C);
+}
+
+static void TestFileSearch(PMAR_FILE pMarFile, const char * szFileName)
+{
+ TMndxFindResult Struct1C_1;
+ TMndxFindResult Struct1C_2;
+ size_t nLength = strlen(szFileName);
+ char szNameBuff[MAX_PATH + 1];
+
+ // Set an empty path as search mask (?)
+ Struct1C_1.SetSearchPath(szFileName, nLength);
+ Struct1C_2.SetSearchPath(szFileName, nLength);
+
+ // Keep searching
+ for(;;)
+ {
+ bool bFindResult1 = false;
+ bool bFindResult2 = false;
+
+ // Search the next file name (orig HOTS code)
+ sub_1956CE0_x86(pMarFile->pDatabasePtr, &Struct1C_1, &bFindResult1);
+
+ // Search the next file name (our code)
+ pMarFile->pDatabasePtr->sub_1956CE0(&Struct1C_2, &bFindResult2);
+
+ assert(bFindResult1 == bFindResult2);
+ assert(Struct1C_1.cchFoundPath == Struct1C_1.cchFoundPath);
+ assert(Struct1C_1.FileNameIndex == Struct1C_2.FileNameIndex);
+ assert(strncmp(Struct1C_1.szFoundPath, Struct1C_2.szFoundPath, Struct1C_1.cchFoundPath) == 0);
+ assert(Struct1C_1.cchFoundPath < MAX_PATH);
+
+ // Stop the search in case of failure
+ if(bFindResult1 == false || bFindResult2 == false)
+ break;
+
+ // Printf the found file name
+ memcpy(szNameBuff, Struct1C_2.szFoundPath, Struct1C_2.cchFoundPath);
+ szNameBuff[Struct1C_2.cchFoundPath] = 0;
+// printf("%s \r", szNameBuff);
+
+ // Perform sub-searches on this string and its substrings that are longer than 4 chars
+// TestFileSearch_SubStrings(pMarFile, szNameBuff, Struct1C_2.cchFoundPath);
+ }
+
+ // Free the search structures
+ Struct1C_1.FreeStruct40();
+ Struct1C_2.FreeStruct40();
+}
+
+static void TestMarFile(PMAR_FILE pMarFile, const char * szFileName, size_t nLength)
+{
+ TFileNameDatabase * pDB = pMarFile->pDatabasePtr->pDB;
+ DWORD dwFileNameIndex1 = 0xFFFFFFFF;
+ DWORD dwFileNameIndex2 = 0xFFFFFFFF;
+
+ // Perform the search using original HOTS code
+ {
+ TMndxFindResult Struct1C;
+
+ Struct1C.CreateStruct40();
+ Struct1C.SetSearchPath(szFileName, nLength);
+
+ // Call the original HOTS function
+ sub_1957EF0_x86(pDB, &Struct1C);
+ dwFileNameIndex1 = Struct1C.FileNameIndex;
+ }
+
+ // Perform the search using our code
+ {
+ TMndxFindResult Struct1C;
+
+ Struct1C.CreateStruct40();
+ Struct1C.SetSearchPath(szFileName, nLength);
+
+ // Call our function
+ pDB->FindFileInDatabase(&Struct1C);
+ dwFileNameIndex2 = Struct1C.FileNameIndex;
+ }
+
+ // Compare both
+ assert(dwFileNameIndex1 == dwFileNameIndex2);
+}
+
+static void TestMndxFunctions(PMAR_FILE pMarFile)
+{
+ TFileNameDatabase * pDB = pMarFile->pDatabasePtr->pDB;
+
+ // Exercise function sub_19573D0
+ for(DWORD arg_0 = 0; arg_0 < 0x100; arg_0++)
+ {
+ for(DWORD arg_4 = 0; arg_4 < 0x100; arg_4++)
+ {
+ DWORD dwResult1 = sub_19573D0_x86(pDB, arg_0, arg_4);
+ DWORD dwResult2 = pDB->GetNameFragmentOffsetEx(arg_0, arg_4);
+
+ assert(dwResult1 == dwResult2);
+ }
+ }
+
+ // Exercise function GetItemValue
+ for(DWORD i = 0; i < 0x10000; i++)
+ {
+ DWORD dwResult1 = GetItemValue_x86(&pDB->Struct68_D0, i);
+ DWORD dwResult2 = pDB->Struct68_D0.GetItemValue(i);
+
+ assert(dwResult1 == dwResult2);
+ }
+
+ // Exercise function sub_1959CB0
+ for(DWORD i = 0; i < 0x9C; i++)
+ {
+ DWORD dwResult1 = sub_1959CB0_x86(pDB, i);
+ DWORD dwResult2 = pDB->sub_1959CB0(i);
+
+ assert(dwResult1 == dwResult2);
+ }
+
+ // Exercise function sub_1959F50
+ for(DWORD i = 0; i < 0x40; i++)
+ {
+ DWORD dwResult1 = sub_1959F50_x86(pDB, i);
+ DWORD dwResult2 = pDB->sub_1959F50(i);
+
+ assert(dwResult1 == dwResult2);
+ }
+}
+
+void TestMndxRootFile(PCASC_MNDX_INFO pMndxInfo)
+{
+ size_t nLength;
+ char szFileName[MAX_PATH+1];
+ void * pvListFile;
+
+ // Exercise low level functions and compare their results
+ // with original code from Heroes of the Storm
+ TestMndxFunctions(pMndxInfo->pMarFile1);
+ TestMndxFunctions(pMndxInfo->pMarFile2);
+ TestMndxFunctions(pMndxInfo->pMarFile3);
+
+ // Find a "mods" in the package array
+ TestFindPackage(pMndxInfo->pMarFile3, "mods/heroes.stormmod/base.stormassets/assets/textures/glow_green2.dds");
+ TestMarFile(pMndxInfo->pMarFile3, "mods/heroes.stormmod/base.stormassets/assets/textures/glow_green2.dds", 69);
+
+ // Search the package MAR file aith a path shorter than a fragment
+ TestFileSearch(pMndxInfo->pMarFile1, "mods/heroes.s");
+
+ // Test the file search
+ TestFileSearch(pMndxInfo->pMarFile1, "");
+ TestFileSearch(pMndxInfo->pMarFile2, "");
+ TestFileSearch(pMndxInfo->pMarFile3, "");
+
+ // False file search
+ TestFileSearch(pMndxInfo->pMarFile2, "assets/textures/storm_temp_hrhu");
+
+ // Open the listfile stream and initialize the listfile cache
+ pvListFile = ListFile_OpenExternal(_T("e:\\Ladik\\Appdir\\CascLib\\listfile\\listfile-hots-29049.txt"));
+ if(pvListFile != NULL)
+ {
+ // Check every file in the database
+ while((nLength = ListFile_GetNext(pvListFile, "*", szFileName, MAX_PATH)) != 0)
+ {
+ // Normalize the file name: ToLower + BackSlashToSlash
+ NormalizeFileName_LowerSlash(szFileName);
+
+ // Check the file with all three MAR files
+ TestMarFile(pMndxInfo->pMarFile1, szFileName, nLength);
+ TestMarFile(pMndxInfo->pMarFile2, szFileName, nLength);
+ TestMarFile(pMndxInfo->pMarFile3, szFileName, nLength);
+ }
+
+ ListFile_Free(pvListFile);
+ }
+}
+#endif // defined(_DEBUG) && defined(_X86_) && defined(CASCLIB_TEST)