mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-22 18:15:31 +01:00
3611 lines
112 KiB
C++
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)
|