Files
TrinityCore/dep/CascLib/src/CascRootFile_Mndx.cpp
2017-03-07 17:14:16 +01:00

3611 lines
112 KiB
C++

/*****************************************************************************/
/* 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 "CascMndx.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;
typedef struct _CASC_MNDX_INFO
{
BYTE RootFileName[MD5_HASH_SIZE]; // Name (aka MD5) of the root file
DWORD HeaderVersion; // Must be <= 2
DWORD FormatVersion;
DWORD field_1C;
DWORD field_20;
DWORD MarInfoOffset; // Offset of the first MAR entry info
DWORD MarInfoCount; // Number of the MAR info entries
DWORD MarInfoSize; // Size of the MAR info entry
DWORD MndxEntriesOffset;
DWORD MndxEntriesTotal; // Total number of MNDX root entries
DWORD MndxEntriesValid; // Number of valid MNDX root entries
DWORD MndxEntrySize; // Size of one MNDX root entry
struct _MAR_FILE * pMarFile1; // File name list for the packages
struct _MAR_FILE * pMarFile2; // File name list for names stripped of package names
struct _MAR_FILE * pMarFile3; // File name list for complete names
// PCASC_ROOT_ENTRY_MNDX pMndxEntries;
// PCASC_ROOT_ENTRY_MNDX * ppValidEntries;
bool bRootFileLoaded; // true if the root info file was properly loaded
} CASC_MNDX_INFO, *PCASC_MNDX_INFO;
typedef struct _CASC_MNDX_PACKAGE
{
char * szFileName; // Pointer to file name
size_t nLength; // Length of the file name
} CASC_MNDX_PACKAGE, *PCASC_MNDX_PACKAGE;
typedef struct _CASC_MNDX_PACKAGES
{
char * szNameBuffer; // Pointer to the buffer for file names
size_t NameEntries; // Number of name entries in Names
size_t NameBufferUsed; // Number of bytes used in the name buffer
size_t NameBufferMax; // Total size of the name buffer
CASC_MNDX_PACKAGE Packages[1]; // List of packages
} CASC_MNDX_PACKAGES, *PCASC_MNDX_PACKAGES;
// Root file entry for CASC storages with MNDX root file (Heroes of the Storm)
// Corresponds to the in-file structure
typedef struct _CASC_ROOT_ENTRY_MNDX
{
DWORD Flags; // High 8 bits: Flags, low 24 bits: package index
BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key for the file
DWORD FileSize; // Uncompressed file size, in bytes
} CASC_ROOT_ENTRY_MNDX, *PCASC_ROOT_ENTRY_MNDX;
//-----------------------------------------------------------------------------
// Testing functions prototypes
#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)
{
// False if the file pointer is beyond the end
if(pbFilePointer > pbFileEnd)
return false;
// False if there is not enough bytes available
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_MNDX_PACKAGES AllocatePackages(size_t nNameEntries, size_t nNameBufferMax)
{
PCASC_MNDX_PACKAGES pPackages;
size_t cbToAllocate;
// Allocate space
cbToAllocate = sizeof(CASC_MNDX_PACKAGES) + (nNameEntries * sizeof(CASC_MNDX_PACKAGE)) + nNameBufferMax;
pPackages = (PCASC_MNDX_PACKAGES)CASC_ALLOC(BYTE, cbToAllocate);
if(pPackages != NULL)
{
// Fill the structure
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_MNDX_PACKAGES InsertToPackageList(
PCASC_MNDX_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_MNDX_PACKAGES pOldPackages = pPackages;
// Allocate new name list
cbToAllocate = sizeof(CASC_MNDX_PACKAGES) + (nNewNameEntries * sizeof(CASC_MNDX_PACKAGE)) + nNewNameBufferMax;
pPackages = (PCASC_MNDX_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(PCASC_MNDX_INFO pMndxInfo, PCASC_MNDX_PACKAGES * ppPackages)
{
TMndxFindResult Struct1C;
PCASC_MNDX_PACKAGES pPackages = NULL;
PMAR_FILE pMarFile;
// Sanity checks
assert(pMndxInfo != NULL);
// Prepare the file name search in the top level directory
pMarFile = 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;
}
// Give the packages to the caller
if(ppPackages != NULL)
ppPackages[0] = pPackages;
return ERROR_SUCCESS;
}
//-----------------------------------------------------------------------------
// Implementation of root file functions
struct TRootHandler_MNDX : public TRootHandler
{
CASC_MNDX_INFO MndxInfo;
PCASC_ROOT_ENTRY_MNDX * ppValidEntries;
PCASC_ROOT_ENTRY_MNDX pMndxEntries;
PCASC_MNDX_PACKAGES pPackages; // Linear list of present packages
};
PCASC_MNDX_PACKAGE FindMndxPackage(TRootHandler_MNDX * pRootHandler, const char * szFileName)
{
PCASC_MNDX_PACKAGE pMatching = NULL;
PCASC_MNDX_PACKAGE pPackage;
size_t nMaxLength = 0;
size_t nLength = strlen(szFileName);
// Packages must be loaded
assert(pRootHandler->pPackages != NULL);
pPackage = pRootHandler->pPackages->Packages;
//FILE * fp = fopen("E:\\packages.txt", "wt");
//for(size_t i = 0; i < hs->pPackages->NameEntries; i++, pPackage++)
//{
// if(pPackage->szFileName != NULL)
// fprintf(fp, "%s\n", pPackage->szFileName);
//}
//fclose(fp);
// Find the longest matching name
for(size_t i = 0; i < pRootHandler->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;
}
int SearchMndxInfo(TRootHandler_MNDX * pRootHandler, const char * szFileName, DWORD dwPackage, PCASC_ROOT_ENTRY_MNDX * ppRootEntry)
{
PCASC_ROOT_ENTRY_MNDX pRootEntry;
PCASC_MNDX_INFO pMndxInfo = &pRootHandler->MndxInfo;
TMndxFindResult Struct1C;
// Search the database for the file name
if(pMndxInfo->bRootFileLoaded)
{
Struct1C.SetSearchPath(szFileName, strlen(szFileName));
// Search the file name in the second MAR info (the one with stripped package names)
if(MAR_FILE_SearchFile(pMndxInfo->pMarFile2, &Struct1C) != ERROR_SUCCESS)
return ERROR_FILE_NOT_FOUND;
// The found MNDX index must fall into range of valid MNDX entries
if(Struct1C.FileNameIndex < pMndxInfo->MndxEntriesValid)
{
// HOTS: E945F4
pRootEntry = pRootHandler->ppValidEntries[Struct1C.FileNameIndex];
while((pRootEntry->Flags & 0x00FFFFFF) != dwPackage)
{
// The highest bit serves as a terminator if set
if(pRootEntry->Flags & 0x80000000)
return ERROR_FILE_NOT_FOUND;
pRootEntry++;
}
// Give the root entry pointer to the caller
if(ppRootEntry != NULL)
ppRootEntry[0] = pRootEntry;
return ERROR_SUCCESS;
}
}
return ERROR_FILE_NOT_FOUND;
}
static LPBYTE FillFindData(TRootHandler_MNDX * pRootHandler, TCascSearch * pSearch, TMndxFindResult * pStruct1C, PDWORD PtrFileSize)
{
PCASC_ROOT_ENTRY_MNDX pRootEntry = NULL;
PCASC_MNDX_PACKAGE pPackage;
char * szStrippedPtr;
char szStrippedName[MAX_PATH+1];
int nError;
// Sanity check
assert(pStruct1C->cchFoundPath < MAX_PATH);
// Fill the file name
memcpy(pSearch->szFileName, pStruct1C->szFoundPath, pStruct1C->cchFoundPath);
pSearch->szFileName[pStruct1C->cchFoundPath] = 0;
// Fill the file size
pPackage = FindMndxPackage(pRootHandler, pSearch->szFileName);
if(pPackage == NULL)
return NULL;
// Cut the package name off the full path
szStrippedPtr = pSearch->szFileName + pPackage->nLength;
while(szStrippedPtr[0] == '/')
szStrippedPtr++;
// We need to convert the stripped name to lowercase, replacing backslashes with slashes
NormalizeFileName_LowerSlash(szStrippedName, szStrippedPtr, MAX_PATH);
// Search the package
nError = SearchMndxInfo(pRootHandler, szStrippedName, (DWORD)(pPackage - pRootHandler->pPackages->Packages), &pRootEntry);
if(nError != ERROR_SUCCESS)
return NULL;
// Give the file size
if(PtrFileSize != NULL)
PtrFileSize[0] = pRootEntry->FileSize;
return pRootEntry->EncodingKey;
}
static int MndxHandler_Insert(TRootHandler_MNDX *, const char *, LPBYTE)
{
return ERROR_NOT_SUPPORTED;
}
static LPBYTE MndxHandler_Search(TRootHandler_MNDX * pRootHandler, TCascSearch * pSearch, PDWORD PtrFileSize, PDWORD /* PtrLocaleFlags */, PDWORD /* PtrFileDataId */)
{
TMndxFindResult * pStruct1C = NULL;
PCASC_MNDX_INFO pMndxInfo = &pRootHandler->MndxInfo;
PMAR_FILE pMarFile = pMndxInfo->pMarFile3;
bool bFindResult = false;
// If the first time, allocate the structure for the search result
if(pSearch->pRootContext == NULL)
{
// Create the new search structure
pStruct1C = new TMndxFindResult;
if(pStruct1C == NULL)
return NULL;
// Setup the search mask
pStruct1C->SetSearchPath("", 0);
pSearch->pRootContext = pStruct1C;
}
// Make shortcut for the search structure
assert(pSearch->pRootContext != NULL);
pStruct1C = (TMndxFindResult *)pSearch->pRootContext;
// Search the next file name (our code)
pMarFile->pDatabasePtr->sub_1956CE0(pStruct1C, &bFindResult);
if(bFindResult == false)
return NULL;
// Give the file size and encoding key
return FillFindData(pRootHandler, pSearch, pStruct1C, PtrFileSize);
}
static void MndxHandler_EndSearch(TRootHandler_MNDX * /* pRootHandler */, TCascSearch * pSearch)
{
if(pSearch != NULL)
delete (TMndxFindResult *)pSearch->pRootContext;
pSearch->pRootContext = NULL;
}
static DWORD MndxHandler_GetFileId(TRootHandler_MNDX * /* pRootHandler */, const char * /* szFileName */)
{
// Not implemented for HOTS
return 0;
}
static LPBYTE MndxHandler_GetKey(TRootHandler_MNDX * pRootHandler, const char * szFileName)
{
PCASC_ROOT_ENTRY_MNDX pRootEntry = NULL;
PCASC_MNDX_PACKAGE pPackage;
char * szStrippedName;
char szNormName[MAX_PATH+1];
int nError;
// Convert the file name to lowercase + slashes
NormalizeFileName_LowerSlash(szNormName, szFileName, MAX_PATH);
// Find the package number
pPackage = FindMndxPackage(pRootHandler, szNormName);
if(pPackage == NULL)
return NULL;
// Cut the package name off the full path
szStrippedName = szNormName + pPackage->nLength;
while(szStrippedName[0] == '/')
szStrippedName++;
// Find the root entry
nError = SearchMndxInfo(pRootHandler, szStrippedName, (DWORD)(pPackage - pRootHandler->pPackages->Packages), &pRootEntry);
if(nError != ERROR_SUCCESS || pRootEntry == NULL)
return NULL;
// Return the encoding key
return pRootEntry->EncodingKey;
}
static void MndxHandler_Close(TRootHandler_MNDX * pRootHandler)
{
if(pRootHandler->MndxInfo.pMarFile1 != NULL)
MAR_FILE_Destructor(pRootHandler->MndxInfo.pMarFile1);
if(pRootHandler->MndxInfo.pMarFile2 != NULL)
MAR_FILE_Destructor(pRootHandler->MndxInfo.pMarFile2);
if(pRootHandler->MndxInfo.pMarFile3 != NULL)
MAR_FILE_Destructor(pRootHandler->MndxInfo.pMarFile3);
if(pRootHandler->ppValidEntries != NULL)
CASC_FREE(pRootHandler->ppValidEntries);
if(pRootHandler->pMndxEntries != NULL)
CASC_FREE(pRootHandler->pMndxEntries);
if(pRootHandler->pPackages != NULL)
CASC_FREE(pRootHandler->pPackages);
CASC_FREE(pRootHandler);
}
//-----------------------------------------------------------------------------
// Public functions - MNDX info
int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
{
PFILE_MNDX_HEADER pMndxHeader = (PFILE_MNDX_HEADER)pbRootFile;
PCASC_MNDX_INFO pMndxInfo;
TRootHandler_MNDX * pRootHandler;
FILE_MAR_INFO MarInfo;
PMAR_FILE pMarFile;
LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
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 the structure for the MNDX root file
hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_MNDX, 1);
if(pRootHandler == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// Fill-in the handler functions
memset(pRootHandler, 0, sizeof(TRootHandler_MNDX));
pRootHandler->Insert = (ROOT_INSERT)MndxHandler_Insert;
pRootHandler->Search = (ROOT_SEARCH)MndxHandler_Search;
pRootHandler->EndSearch = (ROOT_ENDSEARCH)MndxHandler_EndSearch;
pRootHandler->GetKey = (ROOT_GETKEY)MndxHandler_GetKey;
pRootHandler->Close = (ROOT_CLOSE)MndxHandler_Close;
pRootHandler->GetFileId = (ROOT_GETFILEID)MndxHandler_GetFileId;
pMndxInfo = &pRootHandler->MndxInfo;
// Fill-in the flags
pRootHandler->dwRootFlags |= ROOT_FLAG_HAS_NAMES;
// Copy the header into the 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_ROOT_ENTRY_MNDX))
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;
pRootHandler->pMndxEntries = (PCASC_ROOT_ENTRY_MNDX)CASC_ALLOC(BYTE, cbToAllocate);
if(pRootHandler->pMndxEntries != NULL)
{
if(!RootFileRead(pbRootFile + pMndxInfo->MndxEntriesOffset, pbRootFileEnd, pRootHandler->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);
pRootHandler->ppValidEntries = CASC_ALLOC(PCASC_ROOT_ENTRY_MNDX, pMndxInfo->MndxEntriesValid + 1);
if(pRootHandler->ppValidEntries != NULL)
{
PCASC_ROOT_ENTRY_MNDX pRootEntry = pRootHandler->pMndxEntries;
DWORD ValidEntryCount = 1; // edx
DWORD nIndex1 = 0;
// The first entry is always valid
pRootHandler->ppValidEntries[nIndex1++] = pRootHandler->pMndxEntries;
// Put the remaining entries
for(i = 0; i < pMndxInfo->MndxEntriesTotal; i++, pRootEntry++)
{
if(ValidEntryCount > pMndxInfo->MndxEntriesValid)
break;
if(pRootEntry->Flags & 0x80000000)
{
pRootHandler->ppValidEntries[nIndex1++] = pRootEntry + 1;
ValidEntryCount++;
}
}
// Verify the final number of valid entries
if((ValidEntryCount - 1) != pMndxInfo->MndxEntriesValid)
nError = ERROR_BAD_FORMAT;
}
else
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Load the MNDX packages
if(nError == ERROR_SUCCESS)
{
nError = LoadPackageNames(pMndxInfo, &pRootHandler->pPackages);
pMndxInfo->bRootFileLoaded = (nError == ERROR_SUCCESS);
}
#if defined(_DEBUG) && defined(_X86_) && defined(CASCLIB_TEST)
// CascDumpNameFragTable("E:\\casc-name-fragment-table.txt", pMndxInfo->pMarFile1);
// CascDumpFileNames("E:\\casc-listfile.txt", pMndxInfo->pMarFile1);
// TestMndxRootFile(pRootHandler);
#endif
// Return the result
return nError;
}
//----------------------------------------------------------------------------
// 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)
{
void * ptr = CASC_ALLOC(BYTE, bytes);
if(ptr != NULL)
memset(ptr, 0, bytes);
return ptr;
}
extern "C" void free_memory_x86(void * ptr)
{
if(ptr != NULL)
{
CASC_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, szFileName, MAX_PATH);
// 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)