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