aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src/CascRootFile_Mndx.cpp
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2019-06-06 16:48:21 +0200
committerShauren <shauren.trinity@gmail.com>2019-06-08 17:09:24 +0200
commitfc330fd8ff0115804d9c4b53a1f810c00dd63de9 (patch)
treecfa10998fed66779834bf0b7a9b8b799d33d91d4 /dep/CascLib/src/CascRootFile_Mndx.cpp
parent82c7b6c5688495d90c4ee5995a4ff74039348296 (diff)
Dep/CascLib: Update to ladislav-zezula/CascLib@a1197edf0b3bd4d52c3f39be7fa7b44bb0b98012
Diffstat (limited to 'dep/CascLib/src/CascRootFile_Mndx.cpp')
-rw-r--r--dep/CascLib/src/CascRootFile_Mndx.cpp4975
1 files changed, 2166 insertions, 2809 deletions
diff --git a/dep/CascLib/src/CascRootFile_Mndx.cpp b/dep/CascLib/src/CascRootFile_Mndx.cpp
index bc81d088fc0..9dca8d30eb4 100644
--- a/dep/CascLib/src/CascRootFile_Mndx.cpp
+++ b/dep/CascLib/src/CascRootFile_Mndx.cpp
@@ -1,94 +1,102 @@
/*****************************************************************************/
-/* CascMndxRoot.cpp Copyright (c) Ladislav Zezula 2014 */
+/* CascRootFile_MNDX.cpp Copyright (c) Ladislav Zezula 2014 */
/*---------------------------------------------------------------------------*/
/* Common functions for CascLib */
/* Note: "HOTS" refers to Play.exe, v2.5.0.29049 (Heroes of the Storm Alpha) */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
-/* 18.05.14 1.00 Lad The first version of CascMndxRoot.cpp */
+/* 18.05.14 1.00 Lad The first version of CascRootFile_MNDX.cpp */
/*****************************************************************************/
#define __CASCLIB_SELF__
#include "CascLib.h"
#include "CascCommon.h"
-#include "CascMndx.h"
//-----------------------------------------------------------------------------
// Local defines
-#define CASC_MAR_SIGNATURE 0x0052414d // 'MAR\0'
+#define MNDX_MAR_SIGNATURE 0x0052414d // 'MAR\0'
+#define MAR_PACKAGE_NAMES 0 // MAR with package names only
+#define MAR_STRIPPED_NAMES 1 // MAR with names where packages were stripped
+#define MAR_FULL_NAMES 2 // MAR with full file names
+#define MAR_COUNT 3 // Maximum of 3 MAR files are supported
+
+#define MNDX_SEARCH_INITIALIZING 0
+#define MNDX_SEARCH_SEARCHING 2
+#define MNDX_SEARCH_FINISHED 4
+
+#define MNDX_MAX_ENTRIES(type) (0xFFFFFFFF / sizeof(type))
+
+#define MNDX_INVALID_SIZE_T ((size_t)(-1))
+
+#define MNDX_LAST_CKEY_ENTRY 0x80000000
//-----------------------------------------------------------------------------
// Local structures
-typedef struct _FILE_MNDX_HEADER
+typedef union _SETBITS
{
- DWORD Signature; // 'MNDX'
- DWORD HeaderVersion; // Must be <= 2
- DWORD FormatVersion;
+ struct
+ {
+ DWORD Lower08 : 8; // Number of set bits in the lower 1 byte
+ DWORD Lower16 : 8; // Number of set bits in the lower 2 bytes
+ DWORD Lower24 : 8; // Number of set bits in the lower 3 bytes
+ DWORD Lower32 : 8; // Number of set bits in the 32-bit integer
+ } u;
-} FILE_MNDX_HEADER, *PFILE_MNDX_HEADER;
+ DWORD SetBitsAll; // The total set bits mask
-typedef struct _FILE_MAR_INFO
+} SETBITS, *PSETBITS;
+
+typedef struct _HASH_ENTRY
{
- DWORD MarIndex;
- DWORD MarDataSize;
- DWORD MarDataSizeHi;
- DWORD MarDataOffset;
- DWORD MarDataOffsetHi;
-} FILE_MAR_INFO, *PFILE_MAR_INFO;
+ DWORD NodeIndex; // Index of the path node
+ DWORD NextIndex; // ID of the first subnode in the hash table
+
+ union
+ {
+ DWORD FragmentOffset; // Offset of the path fragment in the TPathFragmentTable
+ DWORD ChildTableIndex; // Starting search index for the child database (if child database is present)
+ char SingleChar; // If the upper 24 bits of the FragmentOffset is 0xFFFFFFFF, this single character
+ };
+ // Otherwise --> Offset to the name fragment table
+} HASH_ENTRY, *PHASH_ENTRY;
-typedef struct _CASC_MNDX_INFO
+typedef struct _FILE_MNDX_HEADER
{
- BYTE RootFileName[MD5_HASH_SIZE]; // Name (aka MD5) of the root file
+ DWORD Signature; // 'MNDX'
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;
+} FILE_MNDX_HEADER, *PFILE_MNDX_HEADER;
-typedef struct _CASC_MNDX_PACKAGES
+typedef struct _MNDX_PACKAGE
{
- 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
+ char * szFileName; // Pointer to file name
+ size_t nLength; // Length of the file name
+ size_t nIndex; // Package index
-} CASC_MNDX_PACKAGES, *PCASC_MNDX_PACKAGES;
+} MNDX_PACKAGE, *PMNDX_PACKAGE;
// Root file entry for CASC storages with MNDX root file (Heroes of the Storm)
// Corresponds to the in-file structure
-typedef struct _CASC_ROOT_ENTRY_MNDX
+typedef struct _MNDX_CKEY_ENTRY
{
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
+ BYTE CKey[MD5_HASH_SIZE]; // Content key for the file
+ DWORD ContentSize; // Uncompressed file size, in bytes
+
+} MNDX_CKEY_ENTRY, *PMNDX_CKEY_ENTRY;
-} CASC_ROOT_ENTRY_MNDX, *PCASC_ROOT_ENTRY_MNDX;
+typedef struct _FILE_MAR_INFO
+{
+ DWORD MarIndex;
+ DWORD MarDataSize;
+ DWORD MarDataSizeHi;
+ DWORD MarDataOffset;
+ DWORD MarDataOffsetHi;
+} FILE_MAR_INFO, *PFILE_MAR_INFO;
//-----------------------------------------------------------------------------
// Local variables
@@ -229,3396 +237,2745 @@ unsigned char table_1BA1818[0x800] =
// Local functions - Number of set bits in an integer
// HOTS: inlined
-DWORD GetNumberOfSetBits(DWORD Value32)
+static SETBITS GetNumberOfSetBits(DWORD Value32)
{
+ SETBITS SetBits;
+
Value32 = ((Value32 >> 1) & 0x55555555) + (Value32 & 0x55555555);
Value32 = ((Value32 >> 2) & 0x33333333) + (Value32 & 0x33333333);
Value32 = ((Value32 >> 4) & 0x0F0F0F0F) + (Value32 & 0x0F0F0F0F);
+ SetBits.SetBitsAll = Value32 * 0x01010101;
- return (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;
+ return SetBits;
}
-// HOTS: 01956F00
-TMndxFindResult::~TMndxFindResult()
+static DWORD GetNumberOfSetBits32(DWORD Value32)
{
- FreeStruct40();
+ return GetNumberOfSetBits(Value32).u.Lower32;
}
-// HOTS: 01956F30
-int TMndxFindResult::CreateStruct40()
+static LPBYTE CaptureData(LPBYTE pbRootPtr, LPBYTE pbRootEnd, void * pvBuffer, size_t cbLength)
{
- if(pStruct40 != NULL)
- return ERROR_INVALID_PARAMETER;
+ // Check whether there is enough data in the buffer
+ if((pbRootPtr + cbLength) > pbRootEnd)
+ return NULL;
- pStruct40 = new TStruct40();
- return (pStruct40 != NULL) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
+ // Copy the data
+ memcpy(pvBuffer, pbRootPtr, cbLength);
+ return pbRootPtr + cbLength;
}
-void TMndxFindResult::FreeStruct40()
-{
- if(pStruct40 != NULL)
- delete pStruct40;
- pStruct40 = NULL;
-}
+//-----------------------------------------------------------------------------
+// The TPathStop structure
-// HOTS: 01956E70
-int TMndxFindResult::SetSearchPath(
- const char * szNewSearchMask,
- size_t cchNewSearchMask)
+struct TPathStop
{
- if(szSearchMask == NULL && cchSearchMask != 0)
- return ERROR_INVALID_PARAMETER;
+ TPathStop()
+ {
+ LoBitsIndex = 0;
+ field_4 = 0;
+ Count = 0;
+ HiBitsIndex_PathFragment = CASC_INVALID_INDEX;
+ field_10 = 0xFFFFFFFF;
+ }
- if(pStruct40 != NULL)
- pStruct40->SearchPhase = CASC_SEARCH_INITIALIZING;
+ TPathStop(DWORD arg_0, DWORD arg_4, DWORD arg_8)
+ {
+ LoBitsIndex = arg_0;
+ field_4 = arg_4;
+ Count = arg_8;
+ HiBitsIndex_PathFragment = CASC_INVALID_INDEX;
+ field_10 = 0xFFFFFFFF;
+ }
- szSearchMask = szNewSearchMask;
- cchSearchMask = cchNewSearchMask;
- return ERROR_SUCCESS;
-}
+ DWORD LoBitsIndex;
+ DWORD field_4;
+ DWORD Count;
+ DWORD HiBitsIndex_PathFragment;
+ DWORD field_10;
+};
//-----------------------------------------------------------------------------
-// TByteStream functions
+// Basic array implementations
-// HOTS: 01959990
-TByteStream::TByteStream()
+class TByteStream
{
- pbByteData = NULL;
- pvMappedFile = NULL;
- cbByteData = 0;
- field_C = 0;
- hFile = 0;
- hMap = 0;
-}
+ public:
-// HOTS: 19599F0
-void TByteStream::ExchangeWith(TByteStream & Target)
-{
- TByteStream WorkBuff;
-
- WorkBuff = *this;
- *this = Target;
- Target = WorkBuff;
-}
+ // HOTS: 01959990
+ TByteStream()
+ {
+ pbByteData = NULL;
+ pvMappedFile = NULL;
+ cbByteData = 0;
+ field_C = 0;
+ hFile = 0;
+ hMap = 0;
+ }
-// HOTS: 19599F0
-int TByteStream::GetBytes(DWORD cbByteCount, PARRAY_POINTER PtrArray)
-{
- if(cbByteData < cbByteCount)
- return ERROR_BAD_FORMAT;
+ // HOTS: 19599F0
+ template <typename T>
+ int GetBytes(size_t length, T ** Pointer)
+ {
+ // Is there enough bytes in the array?
+ if(length > cbByteData)
+ return ERROR_BAD_FORMAT;
- // Give the buffer to the caller
- PtrArray->Bytes = pbByteData;
+ // Give the buffer to the caller
+ Pointer[0] = (T *)(pbByteData);
- // Move pointers
- pbByteData += cbByteCount;
- cbByteData -= cbByteCount;
- return ERROR_SUCCESS;
-}
+ // Move pointers
+ pbByteData += length;
+ cbByteData -= length;
+ 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;
+ int CopyBytes(void * value, size_t length)
+ {
+ // Is there enough bytes in the array?
+ if(length > cbByteData)
+ return ERROR_BAD_FORMAT;
- return GetBytes(ItemCount * 4, PtrArray);
-}
+ // Give the buffer to the caller
+ memcpy(value, pbByteData, length);
-// 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;
+ // Move pointers
+ pbByteData += length;
+ cbByteData -= length;
+ return ERROR_SUCCESS;
+ }
- return GetBytes(ItemCount * sizeof(TRIPLET), PtrArray);
-}
+ // HOTS: 1959A60
+ int SkipBytes(size_t cbByteCount)
+ {
+ LPBYTE Pointer;
-// 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<BYTE>(cbByteCount, &Pointer);
+ }
- return GetBytes(ItemCount, PtrArray);
-}
+ // HOTS: 1959AF0
+ int SetByteBuffer(LPBYTE pbNewByteData, size_t cbNewByteData)
+ {
+ if(pbNewByteData != NULL || cbNewByteData == 0)
+ {
+ pbByteData = pbNewByteData;
+ cbByteData = cbNewByteData;
+ return ERROR_SUCCESS;
+ }
-// 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;
+ // HOTS: 1957160 <DWORD>
+ template <typename T>
+ int GetValue(T & Value)
+ {
+ T * Pointer;
+ int nError;
- return GetBytes(cbByteCount, &Dummy);
-}
+ nError = GetBytes(sizeof(T), (LPBYTE *)(&Pointer));
+ if(nError != ERROR_SUCCESS)
+ return nError;
-// HOTS: 1959AF0
-int TByteStream::SetByteBuffer(LPBYTE pbNewByteData, DWORD cbNewByteData)
-{
- if(pbNewByteData != NULL || cbNewByteData == 0)
- {
- pbByteData = pbNewByteData;
- cbByteData = cbNewByteData;
+ Value = Pointer[0];
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;
+ // Retrieves the item count in the array
+ template <typename T>
+ int GetArrayItemCount(DWORD & ArraySize, DWORD & ItemCount)
+ {
+ ULONGLONG ByteCount;
+ int nError;
- // Give the result to the caller
- NumberOfBytes = (DWORD)ByteCount;
- ItemCount = (DWORD)(ByteCount / ItemSize);
- return ERROR_SUCCESS;
-}
+ // The first 8 bytes is the byte size of the array
+ nError = GetValue<ULONGLONG>(ByteCount);
+ if(nError != ERROR_SUCCESS)
+ return nError;
-//-----------------------------------------------------------------------------
-// TGenericArray functions
+ // Extract the number of bytes
+ if(ByteCount > 0xFFFFFFFF || (ByteCount % sizeof(T)) != 0)
+ return ERROR_BAD_FORMAT;
-TGenericArray::TGenericArray()
-{
- DataBuffer.Bytes = NULL;
- FirstValid.Bytes = NULL;
- ArrayPointer.Bytes = NULL;
- ItemCount = 0;
- MaxItemCount = 0;
- bIsValidArray = false;
-}
+ // Give the result to the caller
+ ItemCount = (DWORD)(ByteCount / sizeof(T));
+ ArraySize = (DWORD)(ByteCount);
+ return ERROR_SUCCESS;
+ }
-TGenericArray::~TGenericArray()
-{
- if(DataBuffer.Bytes != NULL)
- CASC_FREE(DataBuffer.Bytes);
-}
+ // HOTS: 1957190: <DWORD>
+ // HOTS: 19571E0: <BASEVALS>
+ // HOTS: 1957230: <BYTE>
+ // HOTS: 1957280: <HASH_ENTRY>
+ template <typename T>
+ int GetArray(T ** Pointer, size_t ItemCount)
+ {
+ int nError = ERROR_SUCCESS;
-// HOTS: inlined
-void TGenericArray::ExchangeWith(TGenericArray & Target)
-{
- TGenericArray WorkBuff;
+ // Verify parameters
+ if(Pointer == NULL && ItemCount != 0)
+ return ERROR_INVALID_PARAMETER;
+ if(ItemCount > MNDX_MAX_ENTRIES(T))
+ return ERROR_NOT_ENOUGH_MEMORY;
- WorkBuff = *this;
- *this = Target;
- Target = WorkBuff;
-}
+ // Allocate bytes for the array
+ if (Pointer != NULL)
+ {
+ Pointer[0] = CASC_ALLOC(T, ItemCount);
+ if (Pointer[0] == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
-// HOTS: Inlined
-void TGenericArray::CopyFrom(TGenericArray & Source)
-{
- if(DataBuffer.Bytes != NULL)
- CASC_FREE(DataBuffer.Bytes);
- *this = Source;
-}
+ // Get the pointer to the array
+ nError = CopyBytes(Pointer[0], sizeof(T) * ItemCount);
+ }
-// HOTS: 1957090 (SetDwordsValid)
-// HOTS: 19570B0 (SetTripletsValid)
-// HOTS: 19570D0 (? SetBitsValid ?)
-// HOTS: 19570F0 (SetNameFragmentsValid)
-int TGenericArray::SetArrayValid()
-{
- if(bIsValidArray != 0)
- return 1;
+ return nError;
+ }
- bIsValidArray = true;
- return ERROR_SUCCESS;
-}
+ LPBYTE pbByteData;
+ void * pvMappedFile;
+ size_t cbByteData;
+ DWORD field_C;
+ HANDLE hFile;
+ HANDLE hMap;
+};
+//-----------------------------------------------------------------------------
+// TGenericArray interface/implementation
-// HOTS: 19575A0
-void TGenericArray::SetMaxItems_CHARS(DWORD NewMaxItemCount)
+template <typename T>
+class TGenericArray
{
- ARRAY_POINTER OldDataBuffer = DataBuffer;
- ARRAY_POINTER NewDataBuffer;
+ public:
- // Allocate new data buffer
- NewDataBuffer.Chars = CASC_ALLOC(char, NewMaxItemCount);
- if(NewDataBuffer.Chars != NULL)
+ TGenericArray()
{
- // Copy the old items to the buffer
- for(DWORD i = 0; i < ItemCount; i++)
- {
- NewDataBuffer.Chars[i] = FirstValid.Chars[i];
- }
+ ItemArray = NULL;
+ ItemCount = 0;
+ MaxItemCount = 0;
+ bIsValidArray = false;
}
- 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;
+ ~TGenericArray()
+ {
+ CASC_FREE(ItemArray);
+ }
- // Allocate new data buffer
- NewDataBuffer.PathStopPtr = CASC_ALLOC(PATH_STOP, NewMaxItemCount);
- if(NewDataBuffer.PathStopPtr != NULL)
+ T & operator[] (size_t index)
{
- // Copy the old items to the buffer
- for(DWORD i = 0; i < ItemCount; i++)
- {
- NewDataBuffer.PathStopPtr[i] = FirstValid.PathStopPtr[i];
- }
+ assert(index < ItemCount);
+ return ItemArray[index];
}
- DataBuffer = NewDataBuffer;
- FirstValid = NewDataBuffer;
- ArrayPointer = NewDataBuffer;
- MaxItemCount = NewMaxItemCount;
- CASC_FREE(OldDataBuffer.PathStopPtr);
-}
+ // HOTS: 1957090 (SetDwordsValid)
+ // HOTS: 19570B0 (SetBaseValsValid)
+ // HOTS: 19570D0 (? SetBitsValid ?)
+ // HOTS: 19570F0 (SetPathFragmentsValid)
+ int SetArrayValid()
+ {
+ if(bIsValidArray != 0)
+ return ERROR_ALREADY_EXISTS;
-// HOTS: inline
-void TGenericArray::InsertOneItem_CHAR(char NewItem)
-{
- DWORD NewMaxItemCount;
- DWORD NewItemCount;
+ bIsValidArray = true;
+ return ERROR_SUCCESS;
+ }
- NewItemCount = ItemCount + 1;
- if(NewItemCount > MaxItemCount)
+ // HOTS: 19575A0 (char)
+ // HOTS: 1957600 (TPathStop)
+ void SetMaxItems(DWORD NewMaxItemCount)
{
- NewMaxItemCount = NewItemCount;
+ T * OldArray = ItemArray;
+ T * NewArray;
- if(MaxItemCount > (NewItemCount / 2))
+ // Allocate new data buffer
+ NewArray = CASC_ALLOC(T, NewMaxItemCount);
+ if(NewArray != NULL)
{
- if(MaxItemCount <= (CASC_MAX_ENTRIES(BYTE) / 2))
- NewMaxItemCount = MaxItemCount + MaxItemCount;
- else
- NewMaxItemCount = CASC_MAX_ENTRIES(BYTE);
+ // Copy the old items to the buffer
+ for(size_t i = 0; i < ItemCount; i++)
+ {
+ NewArray[i] = ItemArray[i];
+ }
}
- SetMaxItems_CHARS(NewMaxItemCount);
+ ItemArray = NewArray;
+ MaxItemCount = NewMaxItemCount;
+ CASC_FREE(OldArray);
}
- // 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)
+ // HOTS: 19575A0 (char)
+ // HOTS: 1957600 (TPathStop)
+ void SetMaxItemsIf(DWORD NewMaxItemCount)
{
- NewMaxItemCount = NewItemCount;
-
- if(MaxItemCount > (NewItemCount / 2))
+ if(NewMaxItemCount > MaxItemCount)
{
- if(MaxItemCount <= (CASC_MAX_ENTRIES(PATH_STOP) / 2))
- NewMaxItemCount = MaxItemCount + MaxItemCount;
- else
- NewMaxItemCount = CASC_MAX_ENTRIES(PATH_STOP);
- }
+ if(MaxItemCount > (NewMaxItemCount / 2))
+ {
+ if(MaxItemCount <= (MNDX_MAX_ENTRIES(T) / 2))
+ NewMaxItemCount = MaxItemCount + MaxItemCount;
+ else
+ NewMaxItemCount = MNDX_MAX_ENTRIES(T);
+ }
- SetMaxItems_PATH_STOP(NewMaxItemCount);
+ SetMaxItems(NewMaxItemCount);
+ }
}
- // Put the structure to the slot that has been reserved
- FirstValid.PathStopPtr[ItemCount++] = NewItem;
-}
+ // HOTS: inline <char>
+ // HOTS: 1958330 <TPathStop>
+ void Insert(T NewItem)
+ {
+ // Make sure we have enough capacity for the new item
+ SetMaxItemsIf(ItemCount + 1);
-// HOTS: 19583A0
-void TGenericArray::sub_19583A0(DWORD NewItemCount)
-{
- DWORD OldMaxItemCount = MaxItemCount;
+ // Put the character to the slot that has been reserved
+ ItemArray[ItemCount++] = NewItem;
+ }
- if(NewItemCount > MaxItemCount)
+ // HOTS: 19583A0 <TPathStop>
+ void GrowArray(DWORD NewItemCount)
{
- DWORD NewMaxItemCount = NewItemCount;
+ DWORD OldMaxItemCount = MaxItemCount;
- if(MaxItemCount > (NewItemCount / 2))
+ // Make sure we have enough capacity for new items
+ SetMaxItemsIf(NewItemCount);
+
+ // Initialize the newly inserted items
+ for(DWORD i = OldMaxItemCount; i < NewItemCount; i++)
{
- if(MaxItemCount <= (CASC_MAX_ENTRIES(PATH_STOP) / 2))
- NewMaxItemCount = MaxItemCount + MaxItemCount;
- else
- NewMaxItemCount = CASC_MAX_ENTRIES(PATH_STOP);
+ ItemArray[i] = T();
}
- SetMaxItems_PATH_STOP(NewMaxItemCount);
+ ItemCount = NewItemCount;
}
- // Initialize the newly inserted items
- for(DWORD i = OldMaxItemCount; i < NewItemCount; i++)
+ // HOTS: 1957440 <DWORD>
+ // HOTS: 19574E0 <BASEVALS>
+ // HOTS: 1957690 <BYTE>
+ // HOTS: 1957700 <HASH_ENTRY>
+ // HOTS: 195A220 <char>
+ // HOTS: 1958580 <TBitStream, DWORD>
+ int LoadFromStream(TByteStream & InStream)
{
- 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;
+ DWORD NumberOfBytes;
+ int 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.GetArrayItemCount<T>(NumberOfBytes, ItemCount);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- // Get and verify the number of items
- nError = InStream.GetValue_ItemCount(NumberOfBytes, ItemCount, sizeof(BYTE));
- if(nError != ERROR_SUCCESS)
- return nError;
+ // Get the pointer to the array
+ nError = InStream.GetArray<T>(&ItemArray, ItemCount);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- nError = InStream.GetArray_BYTES(&ArrayPointer, ItemCount);
- if(nError != ERROR_SUCCESS)
- return nError;
+ nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07);
- if(nError != ERROR_SUCCESS)
- return nError;
+ return SetArrayValid();
+ }
- return SetArrayValid();
-}
+ T * ItemArray;
+ DWORD ItemCount; // Number of items in the array
+ DWORD MaxItemCount; // Capacity of the array
+ bool bIsValidArray;
+};
-// HOTS: 1957700
-int TGenericArray::LoadFragmentInfos(TByteStream & InStream)
+class TBitEntryArray : public TGenericArray<DWORD>
{
- DWORD NumberOfBytes;
- int nError;
+ public:
- // 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;
+ TBitEntryArray() : TGenericArray()
+ {
+ BitsPerEntry = 0;
+ EntryBitMask = 0;
+ TotalEntries = 0;
+ }
- nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07);
- if(nError != ERROR_SUCCESS)
- return nError;
+ ~TBitEntryArray()
+ {}
- return SetArrayValid();
-}
+ DWORD GetItem(DWORD EntryIndex)
+ {
+ DWORD dwItemIndex = (EntryIndex * BitsPerEntry) >> 0x05;
+ DWORD dwStartBit = (EntryIndex * BitsPerEntry) & 0x1F;
+ DWORD dwEndBit = dwStartBit + BitsPerEntry;
+ DWORD dwResult;
-// HOTS: 195A220
-int TGenericArray::LoadStrings(TByteStream & InStream)
-{
- DWORD NumberOfBytes;
- int nError;
+ // If the end bit index is greater than 32,
+ // we also need to load from the next 32-bit item
+ if(dwEndBit > 0x20)
+ {
+ dwResult = (ItemArray[dwItemIndex + 1] << (0x20 - dwStartBit)) | (ItemArray[dwItemIndex] >> dwStartBit);
+ }
+ else
+ {
+ dwResult = ItemArray[dwItemIndex] >> dwStartBit;
+ }
- // Get and verify the number of items
- nError = InStream.GetValue_ItemCount(NumberOfBytes, ItemCount, sizeof(char));
- if(nError != ERROR_SUCCESS)
- return nError;
+ // Now we also need to mask the result by the bit mask
+ return dwResult & EntryBitMask;
+ }
- nError = InStream.GetArray_BYTES(&ArrayPointer, ItemCount);
- if(nError != ERROR_SUCCESS)
- return nError;
+ int LoadBitsFromStream(TByteStream & InStream)
+ {
+ ULONGLONG Value64 = 0;
+ int nError;
- nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07);
- if(nError != ERROR_SUCCESS)
- return nError;
+ nError = LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- return SetArrayValid();
-}
+ nError = InStream.GetValue<DWORD>(BitsPerEntry);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ if(BitsPerEntry > 0x20)
+ return ERROR_BAD_FORMAT;
-// HOTS: 19581C0
-int TGenericArray::LoadDwordsArray_Copy(TByteStream & InStream)
-{
- TGenericArray TempArray;
- int nError;
+ nError = InStream.GetValue<DWORD>(EntryBitMask);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- nError = TempArray.LoadDwordsArray(InStream);
- if(nError != ERROR_SUCCESS)
- return nError;
+ nError = InStream.GetValue<ULONGLONG>(Value64);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ if(Value64 > 0xFFFFFFFF)
+ return ERROR_BAD_FORMAT;
+ TotalEntries = (DWORD)Value64;
- CopyFrom(TempArray);
- return ERROR_SUCCESS;
-}
+ assert((BitsPerEntry * TotalEntries) / 32 <= ItemCount);
+ return ERROR_SUCCESS;
+ }
-// HOTS: 1958250
-int TGenericArray::LoadTripletsArray_Copy(TByteStream & InStream)
-{
- TGenericArray TempArray;
- int nError;
+ DWORD BitsPerEntry;
+ DWORD EntryBitMask;
+ DWORD TotalEntries;
+};
- nError = TempArray.LoadTripletsArray(InStream);
- if(nError != ERROR_SUCCESS)
- return nError;
+//-----------------------------------------------------------------------------
+// TSparseArray functions
- CopyFrom(TempArray);
- return ERROR_SUCCESS;
-}
+#define INDEX_TO_GROUP(val) (val >> 9)
+#define GROUP_TO_INDEX(grp) (grp << 9)
-// HOTS: 1958420
-int TGenericArray::LoadBytes_Copy(TByteStream & InStream)
+// For each 0x200-th bit, this contains information about amount of "1" bits
+typedef struct _BASEVALS
{
- TGenericArray TempArray;
- int nError;
+ DWORD BaseValue200; // Item value of every 0x200-th item
- nError = TempArray.LoadByteArray(InStream);
- if(nError != ERROR_SUCCESS)
- return nError;
+ DWORD AddValue40 : 7; // For each 0x40 items (above the base200),
+ DWORD AddValue80 : 8; // we have extra shortcut to the item value
+ DWORD AddValueC0 : 8; // that is to be added to BaseValue200
+ DWORD AddValue100 : 9;
+ DWORD AddValue140 : 9;
+ DWORD AddValue180 : 9;
+ DWORD AddValue1C0 : 9;
- CopyFrom(TempArray);
- return 0;
-}
+ DWORD __xalignment : 5; // Filling
+} BASEVALS, *PBASEVALS;
-// HOTS: 19584F0
-int TGenericArray::LoadFragmentInfos_Copy(TByteStream & InStream)
+class TSparseArray
{
- TGenericArray TempArray;
- int nError;
-
- nError = TempArray.LoadFragmentInfos(InStream);
- if(nError != ERROR_SUCCESS)
- return nError;
+ public:
- CopyFrom(TempArray);
- return ERROR_SUCCESS;
-}
+ TSparseArray()
+ {
+ TotalItemCount = 0;
+ ValidItemCount = 0;
+ }
-// HOTS: 195A360
-int TGenericArray::LoadStringsWithCopy(TByteStream & InStream)
-{
- TGenericArray TempArray;
- int nError;
+ // HOTS: 1958630
+ int LoadFromStream(TByteStream & InStream)
+ {
+ DWORD total_count = 0;
+ DWORD valid_count = 0;
+ int nError;
- nError = TempArray.LoadStrings(InStream);
- if(nError != ERROR_SUCCESS)
- return nError;
+ nError = ItemBits.LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- CopyFrom(TempArray);
- return ERROR_SUCCESS;
-}
+ nError = InStream.GetValue<DWORD>(total_count);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ nError = InStream.GetValue<DWORD>(valid_count);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ if(valid_count > total_count)
+ return ERROR_FILE_CORRUPT;
-//-----------------------------------------------------------------------------
-// TBitEntryArray functions
+ TotalItemCount = total_count;
+ ValidItemCount = valid_count;
-TBitEntryArray::TBitEntryArray()
-{
- BitsPerEntry = 0;
- EntryBitMask = 0;
- TotalEntries = 0;
-}
+ nError = BaseVals.LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
-TBitEntryArray::~TBitEntryArray()
-{}
+ nError = IndexToItem0.LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
-// HOTS: 01957D20
-void TBitEntryArray::ExchangeWith(TBitEntryArray & Target)
-{
- TBitEntryArray WorkBuff;
+ nError = IndexToItem1.LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- WorkBuff = *this;
- *this = Target;
- Target = WorkBuff;
-}
+ return ERROR_SUCCESS;
+ }
-// HOTS: 1958580
-int TBitEntryArray::LoadFromStream(TByteStream & InStream)
-{
- ARRAY_POINTER Pointer;
- ULONGLONG Value = 0;
- int nError;
+ // Returns true if the array is empty
+ bool IsEmpty()
+ {
+ return (TotalItemCount == 0);
+ }
- nError = LoadDwordsArray_Copy(InStream);
- if(nError != ERROR_SUCCESS)
- return nError;
+ // Returns true if the item at n-th position is present
+ bool IsItemPresent(size_t index)
+ {
+ // (index >> 0x05) gives the DWORD, (1 << (ItemIndex & 0x1F)) gives the bit
+ return (ItemBits[index >> 0x05] & (1 << (index & 0x1F))) ? true : false;
+ }
- nError = InStream.GetBytes(sizeof(DWORD), &Pointer);
- if(nError != ERROR_SUCCESS)
- return nError;
+ // Retrieves the value of the n-th item in the sparse array.
+ // Note that for items that are not present, the value is equal
+ // to the nearest lower present value
+ DWORD GetItemValueAt(size_t index)
+ {
+ BASEVALS & SetBitsCount = BaseVals[index >> 0x09];
+ DWORD IntValue;
+ DWORD BitMask;
- BitsPerEntry = Pointer.Uint32s[0];
- if(BitsPerEntry > 0x20)
- return ERROR_BAD_FORMAT;
+ //
+ // Since we don't want to count bits for the entire array,
+ // there are item value shortcuts every 0x200 items,
+ // and then every 0x40 items above the 0x200 base
+ //
- nError = InStream.GetBytes(sizeof(DWORD), &Pointer);
- if(nError != ERROR_SUCCESS)
- return nError;
- EntryBitMask = Pointer.Uint32s[0];
+ // 1) We have base value for every 0x200-th item
+ IntValue = SetBitsCount.BaseValue200;
- nError = InStream.GetBytes(sizeof(ULONGLONG), &Pointer);
- if(nError == ERROR_SUCCESS)
- Value = Pointer.Int64Ptr[0];
- if(Value > 0xFFFFFFFF)
- return ERROR_BAD_FORMAT;
- TotalEntries = (DWORD)Value;
+ // 2) Add the base value for each 0x40-th item above the 0x200 base
+ switch(((index >> 0x06) & 0x07) - 1)
+ {
+ case 0: // Add the 1st value (7 bits)
+ IntValue += SetBitsCount.AddValue40;
+ break;
- assert((BitsPerEntry * TotalEntries) / 32 <= ItemCount);
- return ERROR_SUCCESS;
-}
+ case 1: // Add the 2nd value (8 bits)
+ IntValue += SetBitsCount.AddValue80;
+ break;
-// HOTS: 1959300
-int TBitEntryArray::LoadFromStream_Exchange(TByteStream & InStream)
-{
- TBitEntryArray TempArray;
- int nError;
+ case 2: // Add the 3rd value (8 bits)
+ IntValue += SetBitsCount.AddValueC0;
+ break;
- nError = TempArray.LoadFromStream(InStream);
- if(nError != ERROR_SUCCESS)
- return nError;
+ case 3: // Add the 4th value (9 bits)
+ IntValue += SetBitsCount.AddValue100;
+ break;
- ExchangeWith(TempArray);
- return ERROR_SUCCESS;
-}
+ case 4: // Add the 5th value (9 bits)
+ IntValue += SetBitsCount.AddValue140;
+ break;
-//-----------------------------------------------------------------------------
-// TStruct40 functions
+ case 5: // Add the 6th value (9 bits)
+ IntValue += SetBitsCount.AddValue180;
+ break;
-TStruct40::TStruct40()
-{
- ItemIndex = 0;
- CharIndex = 0;
- ItemCount = 0;
- SearchPhase = CASC_SEARCH_INITIALIZING;
-}
+ case 6: // Add the 7th value (9 bits)
+ IntValue += SetBitsCount.AddValue1C0;
+ break;
+ }
-// HOTS: 19586B0
-void TStruct40::InitSearchBuffers()
-{
- DWORD NewMaxItemCount;
+ // 3) Count the bits of the higher DWORD, if the index 0x20 - 0x30 above the 0x200 base
+ if(index & 0x20)
+ IntValue += GetNumberOfSetBits32(ItemBits[(index >> 0x05) - 1]);
- array_00.ItemCount = 0;
+ // 4) Count the bits in the current DWORD (masked by bit index mask)
+ BitMask = (1 << (index & 0x1F)) - 1;
+ return IntValue + GetNumberOfSetBits32(ItemBits[index >> 0x05] & BitMask);
+ }
- // HOTS: 19586BD
- if(array_00.MaxItemCount < 0x40)
+ DWORD FindGroup_Items0(DWORD index)
{
- // HOTS: 19586C2
- NewMaxItemCount = 0x40;
+ // Setup the group range to search
+ DWORD minGroup = (IndexToItem0[INDEX_TO_GROUP(index) + 0]) >> 9;
+ DWORD maxGroup = (IndexToItem0[INDEX_TO_GROUP(index) + 1] + 0x1FF) >> 9;
- if(array_00.MaxItemCount > 0x20)
+ // Search the groups and find the BASEVALS structure
+ // For spans less than 10 groups, use sequential search, otherwise binary search.
+ if ((maxGroup - minGroup) < 10)
{
- if(array_00.MaxItemCount <= 0x7FFFFFFF)
- NewMaxItemCount = array_00.MaxItemCount + array_00.MaxItemCount;
- else
- NewMaxItemCount = CASC_MAX_ENTRIES(BYTE);
+ // HOTS: 1959CF7
+ while (index >= GROUP_TO_INDEX(minGroup) - BaseVals[minGroup + 1].BaseValue200 + 0x200)
+ {
+ // HOTS: 1959D14
+ minGroup++;
+ }
}
+ else
+ {
+ // HOTS: 1959D2E
+ while ((minGroup + 1) < maxGroup)
+ {
+ // HOTS: 1959D38
+ DWORD middleValue = (maxGroup + minGroup) >> 1;
- array_00.SetMaxItems_CHARS(NewMaxItemCount);
- }
+ if (index < (maxGroup << 0x09) - BaseVals[maxGroup].BaseValue200)
+ {
+ // HOTS: 01959D4B
+ maxGroup = middleValue;
+ }
+ else
+ {
+ // HOTS: 1959D50
+ minGroup = middleValue;
+ }
+ }
+ }
- // HOTS: 19586E1
- // Set the new item count
- PathStops.sub_19583A0(0);
+ return minGroup;
+ }
- if(PathStops.MaxItemCount < 4)
+ DWORD FindGroup_Items1(DWORD index)
{
- // HOTS: 19586F2
- NewMaxItemCount = 4;
+ DWORD groupIndex = (index >> 0x09);
+ DWORD startValue = IndexToItem1[groupIndex] >> 9;
+ DWORD nextValue = (IndexToItem1[groupIndex + 1] + 0x1FF) >> 9;
- // HOTS: 19586EA
- if(PathStops.MaxItemCount > 2)
+ // Find the BASEVALS structure which the start index belongs to
+ // For less than 10 values, use sequential search. Otherwise, use binary search
+ if ((nextValue - startValue) < 10)
{
- if(PathStops.MaxItemCount <= 0x6666666)
- NewMaxItemCount = PathStops.MaxItemCount + PathStops.MaxItemCount;
- else
- NewMaxItemCount = CASC_MAX_ENTRIES(PATH_STOP);
+ // HOTS: 01959F94
+ while (index >= BaseVals[startValue + 1].BaseValue200)
+ {
+ // HOTS: 1959FA3
+ startValue++;
+ }
}
+ else
+ {
+ // Binary search (HOTS: 1959FAD)
+ if ((startValue + 1) < nextValue)
+ {
+ // HOTS: 1959FB4
+ DWORD middleValue = (nextValue + startValue) >> 1;
- // 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;
-}
+ if (index < BaseVals[middleValue].BaseValue200)
+ {
+ // HOTS: 1959FC4
+ nextValue = middleValue;
+ }
+ else
+ {
+ // HOTS: 1959FC8
+ startValue = middleValue;
+ }
+ }
+ }
-// HOTS: 1959380
-int TSparseArray::LoadFromStream_Exchange(TByteStream & InStream)
-{
- TSparseArray NewStruct68;
- int nError;
+ return startValue;
+ }
- nError = NewStruct68.LoadFromStream(InStream);
- if(nError != ERROR_SUCCESS)
- return nError;
+ // Returns the value of Item0[index] (HOTS: 1959CB0)
+ DWORD GetItem0(DWORD index)
+ {
+ SETBITS zeroBits;
+ DWORD groupIndex;
+ DWORD dwordIndex;
+ DWORD itemIndex;
+ DWORD bitGroup;
+ DWORD edx = index;
- ExchangeWith(NewStruct68);
- return ERROR_SUCCESS;
-}
+#ifdef _DEBUG
+ //if (TotalItemCount > 0x200)
+ //{
+ // FILE * fp = fopen("e:\\Ladik\\Appdir\\CascLib\\doc\\mndx-sparse-array.txt", "wt");
+ // Dump(fp);
+ // fclose(fp);
+ //}
+#endif
-// HOTS: 1959B60
-DWORD TSparseArray::GetItemValue(DWORD ItemIndex)
-{
- PTRIPLET pTriplet;
- DWORD DwordIndex;
- DWORD BaseValue;
- DWORD BitMask;
+ // If the index is at begin of the group, we just return the start value
+ if ((index & 0x1FF) == 0)
+ return IndexToItem0[INDEX_TO_GROUP(index)];
- //
- // 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
+ // Find the group where the index belongs to
+ groupIndex = FindGroup_Items0(index);
- // Upper 23 bits contain index to the table
- pTriplet = BaseValues.TripletArray + (ItemIndex >> 0x09);
- BaseValue = pTriplet->BaseValue;
+ // HOTS: 1959D5F
+ edx += BaseVals[groupIndex].BaseValue200 - (groupIndex << 0x09);
+ dwordIndex = (groupIndex << 4);
- // 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;
+ if (edx < 0x100 - BaseVals[groupIndex].AddValue100)
+ {
+ // HOTS: 1959D8C
+ if (edx < 0x80 - BaseVals[groupIndex].AddValue80)
+ {
+ // HOTS: 01959DA2
+ if (edx >= 0x40 - BaseVals[groupIndex].AddValue40)
+ {
+ // HOTS: 01959DB7
+ dwordIndex += 2;
+ edx = edx + BaseVals[groupIndex].AddValue40 - 0x40;
+ }
+ }
+ else
+ {
+ // HOTS: 1959DC0
+ if (edx < 0xC0 - BaseVals[groupIndex].AddValueC0)
+ {
+ // HOTS: 1959DD3
+ dwordIndex += 4;
+ edx = edx + BaseVals[groupIndex].AddValue80 - 0x80;
+ }
+ else
+ {
+ // HOTS: 1959DD3
+ dwordIndex += 6;
+ edx = edx + BaseVals[groupIndex].AddValueC0 - 0xC0;
+ }
+ }
+ }
+ else
+ {
+ // HOTS: 1959DE8
+ if (edx < 0x180 - BaseVals[groupIndex].AddValue180)
+ {
+ // HOTS: 01959E00
+ if (edx < 0x140 - BaseVals[groupIndex].AddValue140)
+ {
+ // HOTS: 1959E11
+ dwordIndex += 8;
+ edx = edx + BaseVals[groupIndex].AddValue100 - 0x100;
+ }
+ else
+ {
+ // HOTS: 1959E1D
+ dwordIndex += 10;
+ edx = edx + BaseVals[groupIndex].AddValue140 - 0x140;
+ }
+ }
+ else
+ {
+ // HOTS: 1959E29
+ if (edx < 0x1C0 - BaseVals[groupIndex].AddValue1C0)
+ {
+ // HOTS: 1959E3D
+ dwordIndex += 12;
+ edx = edx + BaseVals[groupIndex].AddValue180 - 0x180;
+ }
+ else
+ {
+ // HOTS: 1959E49
+ dwordIndex += 14;
+ edx = edx + BaseVals[groupIndex].AddValue1C0 - 0x1C0;
+ }
+ }
+ }
- case 1: // Add the 2nd value (8 bits)
- BaseValue += (pTriplet->Value2 >> 0x07) & 0xFF;
- break;
+ // HOTS: 1959E53:
+ // Calculate the number of bits set in the value of "bitGroup"
+ bitGroup = ~ItemBits[dwordIndex];
+ zeroBits = GetNumberOfSetBits(bitGroup);
- case 2: // Add the 3rd value (8 bits)
- BaseValue += (pTriplet->Value2 >> 0x0F) & 0xFF;
- break;
+ if (edx >= zeroBits.u.Lower32)
+ {
+ // HOTS: 1959ea4
+ bitGroup = ~ItemBits[++dwordIndex];
+ edx = edx - zeroBits.u.Lower32;
+ zeroBits = GetNumberOfSetBits(bitGroup);
+ }
- case 3: // Add the 4th value (9 bits)
- BaseValue += (pTriplet->Value2 >> 0x17);
- break;
+ // Re-calculate the item index
+ itemIndex = (dwordIndex << 0x05);
- case 4: // Add the 5th value (9 bits)
- BaseValue += (pTriplet->Value3 & 0x1FF);
- break;
+ // HOTS: 1959eea
+ if (edx < zeroBits.u.Lower16)
+ {
+ // HOTS: 1959EFC
+ if (edx >= zeroBits.u.Lower08)
+ {
+ // HOTS: 1959F05
+ bitGroup >>= 0x08;
+ itemIndex += 0x08;
+ edx -= zeroBits.u.Lower08;
+ }
+ }
+ else
+ {
+ // HOTS: 1959F0D
+ if (edx < zeroBits.u.Lower24)
+ {
+ // HOTS: 1959F19
+ bitGroup >>= 0x10;
+ itemIndex += 0x10;
+ edx -= zeroBits.u.Lower16;
+ }
+ else
+ {
+ // HOTS: 1959F23
+ bitGroup >>= 0x18;
+ itemIndex += 0x18;
+ edx -= zeroBits.u.Lower24;
+ }
+ }
- case 5: // Add the 6th value (9 bits)
- BaseValue += (pTriplet->Value3 >> 0x09) & 0x1FF;
- break;
+ // HOTS: 1959f2b
+ edx = edx << 0x08;
+ bitGroup = bitGroup & 0xFF;
- case 6: // Add the 7th value (9 bits)
- BaseValue += (pTriplet->Value3 >> 0x12) & 0x1FF;
- break;
+ // BUGBUG: Possible buffer overflow here. Happens when dwItemIndex >= 0x9C.
+ // The same happens in Heroes of the Storm (build 29049), so I am not sure
+ // if this is a bug or a case that never happens
+ assert((bitGroup + edx) < sizeof(table_1BA1818));
+ return table_1BA1818[bitGroup + edx] + itemIndex;
}
- //
- // 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);
-}
+ DWORD GetItem1(DWORD index)
+ {
+ SETBITS setBits;
+ DWORD distFromBase;
+ DWORD groupIndex;
+ DWORD dwordIndex;
+ DWORD itemIndex;
+ DWORD bitGroup;
-//-----------------------------------------------------------------------------
-// TNameIndexStruct functions
+ // If the index is at begin of the group, we just return the start value
+ if ((index & 0x1FF) == 0)
+ return IndexToItem1[INDEX_TO_GROUP(index)];
-// HOTS: 0195A290
-TNameIndexStruct::TNameIndexStruct()
-{}
+ // Find the group where the index belongs to
+ groupIndex = FindGroup_Items1(index);
-// HOTS: inlined
-TNameIndexStruct::~TNameIndexStruct()
-{}
+ // Calculate the base200 dword index (HOTS: 1959FD4)
+ distFromBase = index - BaseVals[groupIndex].BaseValue200;
+ dwordIndex = groupIndex << 0x04;
-// HOTS: 195A180
-bool TNameIndexStruct::CheckNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs)
-{
- TStruct40 * pStruct40 = pStruct1C->pStruct40;
- const char * szPathFragment;
- const char * szSearchMask;
+ // Calculate the dword index including the sub-checkpoint
+ if (distFromBase < BaseVals[groupIndex].AddValue100)
+ {
+ // HOTS: 1959FF1
+ if (distFromBase < BaseVals[groupIndex].AddValue80)
+ {
+ // HOTS: 0195A000
+ if (distFromBase >= BaseVals[groupIndex].AddValue40)
+ {
+ // HOTS: 195A007
+ distFromBase = distFromBase - BaseVals[groupIndex].AddValue40;
+ dwordIndex += 2;
+ }
+ }
+ else
+ {
+ // HOTS: 195A00E
+ if (distFromBase < BaseVals[groupIndex].AddValueC0)
+ {
+ // HOTS: 195A01A
+ distFromBase = distFromBase - BaseVals[groupIndex].AddValue80;
+ dwordIndex += 4;
+ }
+ else
+ {
+ // HOTS: 195A01F
+ distFromBase = distFromBase - BaseVals[groupIndex].AddValueC0;
+ dwordIndex += 6;
+ }
+ }
+ }
+ else
+ {
+ // HOTS: 195A026
+ if (distFromBase < BaseVals[groupIndex].AddValue180)
+ {
+ // HOTS: 195A037
+ if (distFromBase < BaseVals[groupIndex].AddValue140)
+ {
+ // HOTS: 195A041
+ distFromBase = distFromBase - BaseVals[groupIndex].AddValue100;
+ dwordIndex += 8;
+ }
+ else
+ {
+ // HOTS: 195A048
+ distFromBase = distFromBase - BaseVals[groupIndex].AddValue140;
+ dwordIndex += 10;
+ }
+ }
+ else
+ {
+ // HOTS: 195A04D
+ if (distFromBase < BaseVals[groupIndex].AddValue1C0)
+ {
+ // HOTS: 195A05A
+ distFromBase = distFromBase - BaseVals[groupIndex].AddValue180;
+ dwordIndex += 12;
+ }
+ else
+ {
+ // HOTS: 195A061
+ distFromBase = distFromBase - BaseVals[groupIndex].AddValue1C0;
+ dwordIndex += 14;
+ }
+ }
+ }
- 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;
+ // HOTS: 195A066
+ bitGroup = ItemBits[dwordIndex];
+ setBits = GetNumberOfSetBits(bitGroup);
- // Keep searching as long as the name matches with the fragment
- while(szPathFragment[pStruct40->CharIndex] == szSearchMask[pStruct40->CharIndex])
+ // Get total number of set bits in the bit group
+ if (distFromBase >= setBits.u.Lower32)
{
- // 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;
+ // HOTS: 195A0B2
+ bitGroup = ItemBits[++dwordIndex];
+ distFromBase = distFromBase - setBits.u.Lower32;
+ setBits = GetNumberOfSetBits(bitGroup);
}
- return false;
- }
- else
- {
- // Get the offset of the fragment to compare.
- szPathFragment = (const char *)(NameFragments.CharArray);
- szSearchMask = pStruct1C->szSearchMask;
+ // Calculate the item index
+ itemIndex = (dwordIndex << 0x05);
- // Keep searching as long as the name matches with the fragment
- while(szPathFragment[dwFragOffs] == szSearchMask[pStruct40->CharIndex])
+ // Get the number of set bits in the lower word (HOTS: 195A0F6)
+ if (distFromBase < setBits.u.Lower16)
+ {
+ // HOTS: 195A111
+ if (distFromBase >= setBits.u.Lower08)
+ {
+ // HOTS: 195A111
+ itemIndex = itemIndex + 0x08;
+ bitGroup = bitGroup >> 0x08;
+ distFromBase = distFromBase - setBits.u.Lower08;
+ }
+ }
+ else
{
- // 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;
+ // HOTS: 195A119
+ if (distFromBase < setBits.u.Lower24)
+ {
+ // HOTS: 195A125
+ bitGroup = bitGroup >> 0x10;
+ itemIndex = itemIndex + 0x10;
+ distFromBase = distFromBase - setBits.u.Lower16;
+ }
+ else
+ {
+ // HOTS: 195A12F
+ bitGroup = bitGroup >> 0x18;
+ itemIndex = itemIndex + 0x18;
+ distFromBase = distFromBase - setBits.u.Lower24;
+ }
}
- return false;
- }
-}
+ bitGroup = bitGroup & 0xFF;
+ distFromBase = distFromBase << 0x08;
-// HOTS: 195A570
-bool TNameIndexStruct::CheckAndCopyNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs)
-{
- TStruct40 * pStruct40 = pStruct1C->pStruct40;
- const char * szPathFragment;
- const char * szSearchMask;
+ // BUGBUG: Potential buffer overflow
+ // Happens in Heroes of the Storm when index == 0x5B
+ assert((bitGroup + distFromBase) < sizeof(table_1BA1818));
+ return table_1BA1818[bitGroup + distFromBase] + itemIndex;
+ }
- if(!Struct68.TotalItemCount)
+#ifdef _DEBUG
+ void Dump(FILE * fp)
{
- // 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;
+ size_t * ArrayNormal;
+ size_t * ArrayInvert;
+ size_t IndexNormal = 0;
+ size_t IndexInvert = 0;
- // Keep copying as long as we don't reach the end of the search mask
- while(pStruct40->CharIndex < pStruct1C->cchSearchMask)
+ // Output numbers of set bits for every 0x200-th item
+ fprintf(fp, "Number of set bits for every 0x200-th index\n"
+ "========================================================\n"
+ " Index Base200h +40 +80 +C0 +100 +140 +180 +1C0\n"
+ "--------------------------------------------------------\n");
+ for (size_t i = 0; i < BaseVals.ItemCount; i++)
{
- // 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;
+ fprintf(fp, "[%08zX]: %08x %04x %04x %04x %04x %04x %04x %04x\n", GROUP_TO_INDEX(i), BaseVals[i].BaseValue200,
+ BaseVals[i].AddValue40,
+ BaseVals[i].AddValue80,
+ BaseVals[i].AddValueC0,
+ BaseVals[i].AddValue100,
+ BaseVals[i].AddValue140,
+ BaseVals[i].AddValue180,
+ BaseVals[i].AddValue1C0);
}
+ fprintf(fp, "\n");
- // 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)
+ // Output values of Item1 and Item0 for every 0x200-th index
+ fprintf(fp, "Item0 and Item1 for every 0x200-th index\n"
+ "========================================\n"
+ " Index Item0 Item1\n"
+ "-----------------------------\n");
+ for (size_t i = 0; i < IndexToItem0.ItemCount; i++)
{
- pStruct40->array_00.InsertOneItem_CHAR(szPathFragment[0]);
- szPathFragment++;
+ fprintf(fp, "[%08zX]: %08x %08x\n", GROUP_TO_INDEX(i), IndexToItem0[i], IndexToItem1[i]);
}
- }
- else
- {
- // Get the offset of the fragment to compare
- // HOTS: 195A6B7
- szPathFragment = NameFragments.CharArray;
- szSearchMask = pStruct1C->szSearchMask;
+ fprintf(fp, "\n");
- // 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++;
+ // Output values of Item1 and Item0 for every index
+ ArrayNormal = new size_t[TotalItemCount];
+ ArrayInvert = new size_t[TotalItemCount];
+ if (ArrayNormal && ArrayInvert)
+ {
+ // Invalidate both arrays
+ memset(ArrayNormal, 0xFF, TotalItemCount * sizeof(size_t));
+ memset(ArrayInvert, 0xFF, TotalItemCount * sizeof(size_t));
- // Keep going as long as the given bit is not set
- if(Struct68.IsItemPresent(dwFragOffs++))
- return true;
- }
+ // Load the both arrays
+ for (size_t i = 0; i < TotalItemCount; i++)
+ {
+ if (IsItemPresent(i))
+ ArrayNormal[IndexNormal++] = i;
+ else
+ ArrayInvert[IndexInvert++] = i;
+ }
- // Fixup the address of the fragment
- szPathFragment += dwFragOffs;
+ // Output both arrays
+ fprintf(fp, "Item0 and Item1 for every index\n"
+ "========================================\n"
+ " Index Item0 Item1\n"
+ "-----------------------------\n");
+ for (size_t i = 0; i < TotalItemCount; i++)
+ {
+ char NormalValue[0x20];
+ char InvertValue[0x20];
- // 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++;
+ if (ArrayNormal[i] == MNDX_INVALID_SIZE_T && ArrayInvert[i] == MNDX_INVALID_SIZE_T)
+ break;
+ fprintf(fp, "[%08zX]: %8s %8s\n", i, DumpValue(InvertValue, _countof(InvertValue), ArrayInvert[i]), DumpValue(NormalValue, _countof(NormalValue), ArrayNormal[i]));
+ }
+ fprintf(fp, "\n");
}
- }
- return true;
-}
+ // Free both arrays
+ delete[] ArrayNormal;
+ delete[] ArrayInvert;
-// 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)
+ // Output array of all values
+ fprintf(fp, "Item List: Index -> Value\n==========================\n");
+ for (size_t i = 0; i < TotalItemCount; i++)
{
- // Insert the character to the path being built
- pStruct40->array_00.InsertOneItem_CHAR(*szPathFragment++);
+ if (IsItemPresent(i))
+ {
+ fprintf(fp, "[%08zX]: %08x\n", i, GetItemValueAt(i));
+ }
+ else
+ {
+ fprintf(fp, "[%08zX]: NOT PRESENT\n", i);
+ }
}
+ fprintf(fp, "\n");
}
- 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;
- }
+ char * DumpValue(char * szBuffer, size_t cchBuffer, size_t value)
+ {
+ CascStrPrintf(szBuffer, cchBuffer, (value != MNDX_INVALID_SIZE_T) ? "%08zX" : " - ", value);
+ return szBuffer;
}
-}
-
-// 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;
+#endif
- ExchangeWith(TempIndexStruct);
- return ERROR_SUCCESS;
-}
+ TGenericArray<DWORD> ItemBits; // A bit array for each item. 1 if the item is present.
+ size_t TotalItemCount; // Total number of items in the array
+ size_t ValidItemCount; // Number of present items in the array
+ TGenericArray<BASEVALS> BaseVals; // For each 0x200-th item, this contains the number of set bits up to that 0x200-th item
+ TGenericArray<DWORD> IndexToItem0; // Mapping of index to invert item. An "invert" item is an item whose bit in "ItemBits" is zero.
+ TGenericArray<DWORD> IndexToItem1; // Mapping of index to normal item. An "normal" item is an item whose bit in "ItemBits" is set.
+};
//-----------------------------------------------------------------------------
-// TStruct10 functions
-
-TStruct10::TStruct10()
-{
- field_0 = 0x03;
- field_4 = 0x200;
- field_8 = 0x1000;
- field_C = 0x20000;
-}
+// TStruct40 functions
-// HOTS: inline
-void TStruct10::CopyFrom(TStruct10 & Target)
+class TStruct40
{
- field_0 = Target.field_0;
- field_4 = Target.field_4;
- field_8 = Target.field_8;
- field_C = Target.field_C;
-}
+ public:
-// HOTS: 1956FD0
-int TStruct10::sub_1956FD0(DWORD dwBitMask)
-{
- switch(dwBitMask & 0xF80)
+ TStruct40()
{
- 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;
+ NodeIndex = 0;
+ ItemCount = 0;
+ PathLength = 0;
+ SearchPhase = MNDX_SEARCH_INITIALIZING;
}
- return ERROR_INVALID_PARAMETER;
-}
-
-// HOTS: 1957050
-int TStruct10::sub_1957050(DWORD dwBitMask)
-{
- switch(dwBitMask & 0xF0000)
+ // HOTS: 19586B0
+ void BeginSearch()
{
- case 0x00:
- field_C = 0x20000;
- return ERROR_SUCCESS;
+ // HOTS: 19586BD
+ PathBuffer.ItemCount = 0;
+ PathBuffer.SetMaxItemsIf(0x40);
- case 0x10000:
- field_C = 0x10000;
- return ERROR_SUCCESS;
+ // HOTS: 19586E1
+ // Set the new item count
+ PathStops.GrowArray(0);
+ PathStops.SetMaxItemsIf(4);
- case 0x20000:
- field_C = 0x20000;
- return ERROR_SUCCESS;
+ PathLength = 0;
+ NodeIndex = 0;
+ ItemCount = 0;
+ SearchPhase = MNDX_SEARCH_SEARCHING;
}
- return ERROR_INVALID_PARAMETER;
-}
-
-// HOTS: 19572E0
-int TStruct10::sub_19572E0(DWORD dwBitMask)
-{
- DWORD dwSubMask;
- int nError;
+ DWORD CalcHashValue(const char * szPath)
+ {
+ return (BYTE)(szPath[PathLength]) ^ (NodeIndex << 0x05) ^ NodeIndex;
+ }
- if(dwBitMask & 0xFFF00000)
- return ERROR_INVALID_PARAMETER;
+ TGenericArray<TPathStop> PathStops; // Array of path checkpoints
+ TGenericArray<char> PathBuffer; // Buffer for building a file name
+ DWORD NodeIndex; // ID of a path node being searched; starting with 0
+ DWORD PathLength; // Length of the path in the PathBuffer
+ DWORD ItemCount;
+ DWORD SearchPhase; // 0 = initializing, 2 = searching, 4 = finished
+};
- dwSubMask = dwBitMask & 0x7F;
- if(dwSubMask)
- field_0 = dwSubMask;
+//-----------------------------------------------------------------------------
+// Local functions - TMndxSearch
- nError = sub_1956FD0(dwBitMask);
- if(nError != ERROR_SUCCESS)
- return nError;
+class TMndxSearch
+{
+ public:
- dwSubMask = dwBitMask & 0xF000;
- if(dwSubMask == 0 || dwSubMask == 0x1000)
+ // HOTS: 01956EE0
+ TMndxSearch()
{
- field_8 = 0x1000;
- return sub_1957050(dwBitMask);
+ szSearchMask = NULL;
+ cchSearchMask = 0;
+ szFoundPath = NULL;
+ cchFoundPath = 0;
+ nIndex = 0;
}
- if(dwSubMask == 0x2000)
+ // HOTS: 01956F00
+ ~TMndxSearch()
+ {}
+
+ // HOTS: 01956E70
+ int SetSearchMask(
+ const char * szNewSearchMask,
+ size_t cchNewSearchMask)
{
- field_8 = 0x2000;
- return sub_1957050(dwBitMask);
- }
+ if(szSearchMask == NULL && cchSearchMask != 0)
+ return ERROR_INVALID_PARAMETER;
- return ERROR_INVALID_PARAMETER;
-}
+ Struct40.SearchPhase = MNDX_SEARCH_INITIALIZING;
-// HOTS: 1957800
-int TStruct10::sub_1957800(DWORD dwBitMask)
-{
- TStruct10 TempStruct;
- int nError;
-
- nError = TempStruct.sub_19572E0(dwBitMask);
- if(nError != ERROR_SUCCESS)
- return nError;
+ szSearchMask = szNewSearchMask;
+ cchSearchMask = cchNewSearchMask;
+ return ERROR_SUCCESS;
+ }
- CopyFrom(TempStruct);
- return ERROR_SUCCESS;
-}
+ TStruct40 Struct40;
+ const char * szSearchMask; // Search mask without wildcards
+ size_t cchSearchMask; // Length of the search mask
+ const char * szFoundPath; // Found path name
+ size_t cchFoundPath; // Length of the found path name
+ DWORD nIndex; // Index of the file name
+};
//-----------------------------------------------------------------------------
-// TFileNameDatabase functions
+// TPathFragmentTable class. This class implements table of the path fragments.
+// These path fragments can either by terminated by zeros (ASCIIZ)
+// or can be marked by the external "PathMarks" structure
-// HOTS: 01958730
-TFileNameDatabase::TFileNameDatabase()
-{
- NameFragIndexMask = 0;
- field_214 = 0;
-}
-
-// HOTS: inlined
-void TFileNameDatabase::ExchangeWith(TFileNameDatabase & Target)
+class TPathFragmentTable
{
- 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);
+ public:
- dwTemp = NameFragIndexMask;
- NameFragIndexMask = Target.NameFragIndexMask;
- Target.NameFragIndexMask = dwTemp;
+ // HOTS: 0195A290
+ TPathFragmentTable()
+ {}
- dwTemp = field_214;
- field_214 = Target.field_214;
- Target.field_214 = dwTemp;
+ // HOTS: inlined
+ ~TPathFragmentTable()
+ {}
- Struct10.CopyFrom(Target.Struct10);
-}
+ // HOTS: 195A180
+ bool ComparePathFragment(TMndxSearch * pSearch, size_t nFragmentOffset)
+ {
+ TStruct40 * pStruct40 = &pSearch->Struct40;
-// 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)
+ // Do we have path fragment separators in an external structure?
+ if(PathMarks.IsEmpty())
{
- // HOTS: 1959D14
- edi += 0x200;
- pTriplet++;
+ // Keep searching as long as the name matches with the fragment
+ while(PathFragments[nFragmentOffset] == pSearch->szSearchMask[pStruct40->PathLength])
+ {
+ // Move to the next character
+ pStruct40->PathLength++;
+ nFragmentOffset++;
- ebx = edi - pTriplet->BaseValue + 0x200;
- eax++;
+ // Is it the end of the fragment or end of the path?
+ if(PathFragments[nFragmentOffset] == 0)
+ return true;
+ if(pStruct40->PathLength >= pSearch->cchSearchMask)
+ return false;
+ }
+
+ return false;
}
- }
- else
- {
- // HOTS: 1959D2E
- while((eax + 1) < esi)
+ else
{
- // HOTS: 1959D38
- // ecx = Struct68_00.BaseValues.TripletArray;
- esi = (esi + eax) >> 1;
- ebx = (esi << 0x09) - Struct68_00.BaseValues.TripletArray[esi].BaseValue;
- if(edx < ebx)
+ // Keep searching as long as the name matches with the fragment
+ while(PathFragments[nFragmentOffset] == pSearch->szSearchMask[pStruct40->PathLength])
{
- // HOTS: 01959D4B
- dwItemIndex = esi;
- }
- else
- {
- // HOTS: 1959D50
- eax = esi;
- esi = dwItemIndex;
+ // Move to the next character
+ pStruct40->PathLength++;
+
+ // Is it the end of the path fragment?
+ if(PathMarks.IsItemPresent(nFragmentOffset++))
+ return true;
+ if(nFragmentOffset >= pSearch->cchSearchMask)
+ return false;
}
+
+ return false;
}
}
- // HOTS: 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: 195A3F0
+ void CopyPathFragment(TMndxSearch * pSearch, size_t nFragmentOffset)
{
- // HOTS: 1959D8C
- ecx = ((eax >> 0x07) & 0xFF);
- esi = 0x80 - ecx;
- if(edx < esi)
+ TStruct40 * pStruct40 = &pSearch->Struct40;
+
+ // Do we have path fragment separators in an external structure?
+ if (PathMarks.IsEmpty())
{
- // HOTS: 01959DA2
- eax = eax & 0x7F;
- ecx = 0x40 - eax;
- if(edx >= ecx)
+ // HOTS: 195A40C
+ while (PathFragments[nFragmentOffset] != 0)
{
- // HOTS: 01959DB7
- edi += 2;
- edx = edx + eax - 0x40;
+ // Insert the character to the path being built
+ pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset++]);
}
}
else
{
- // HOTS: 1959DC0
- eax = (eax >> 0x0F) & 0xFF;
- esi = 0xC0 - eax;
- if(edx < esi)
+ // HOTS: 195A4B3
+ while(!PathMarks.IsItemPresent(nFragmentOffset))
{
- // HOTS: 1959DD3
- edi += 4;
- edx = edx + ecx - 0x80;
- }
- else
- {
- // HOTS: 1959DD3
- edi += 6;
- edx = edx + eax - 0xC0;
+ // Insert the character to the path being built
+ pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset++]);
}
}
}
- else
+
+ // HOTS: 195A570
+ bool CompareAndCopyPathFragment(TMndxSearch * pSearch, size_t nFragmentOffset)
{
- // HOTS: 1959DE8
- esi = pTriplet->Value3;
- eax = ((esi >> 0x09) & 0x1FF);
- ebx = 0x180 - eax;
- if(edx < ebx)
+ TStruct40 * pStruct40 = &pSearch->Struct40;
+
+ // Do we have path fragment separators in an external structure?
+ if(PathMarks.IsEmpty())
{
- // HOTS: 01959E00
- esi = esi & 0x1FF;
- eax = (0x140 - esi);
- if(edx < eax)
+ // Keep copying as long as we don't reach the end of the search mask
+ while(pStruct40->PathLength < pSearch->cchSearchMask)
{
- // HOTS: 1959E11
- edi = edi + 8;
- edx = edx + ecx - 0x100;
+ // HOTS: 195A5A0
+ if(PathFragments[nFragmentOffset] != pSearch->szSearchMask[pStruct40->PathLength])
+ return false;
+
+ // HOTS: 195A5B7
+ pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset++]);
+ pStruct40->PathLength++;
+
+ // If we found the end of the fragment, return success
+ if(PathFragments[nFragmentOffset] == 0)
+ return true;
}
- else
+
+ // HOTS: 195A660
+ // Now we need to copy the rest of the fragment
+ while(PathFragments[nFragmentOffset] != 0)
{
- // HOTS: 1959E1D
- edi = edi + 0x0A;
- edx = edx + esi - 0x140;
+ pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset]);
+ nFragmentOffset++;
}
}
else
{
- // HOTS: 1959E29
- esi = (esi >> 0x12) & 0x1FF;
- ecx = (0x1C0 - esi);
- if(edx < ecx)
+ // Keep copying as long as we don't reach the end of the search mask
+ while(nFragmentOffset < pSearch->cchSearchMask)
{
- // HOTS: 1959E3D
- edi = edi + 0x0C;
- edx = edx + eax - 0x180;
+ if(PathFragments[nFragmentOffset] != pSearch->szSearchMask[pStruct40->PathLength])
+ return false;
+
+ pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset]);
+ pStruct40->PathLength++;
+
+ // If we found the end of the fragment, return success
+ if(PathMarks.IsItemPresent(nFragmentOffset++))
+ return true;
}
- else
+
+ // Now we need to copy the rest of the fragment
+ while(!PathMarks.IsItemPresent(nFragmentOffset))
{
- // HOTS: 1959E49
- edi = edi + 0x0E;
- edx = edx + esi - 0x1C0;
+ // HOTS: 195A7A6
+ pStruct40->PathBuffer.Insert(PathFragments[nFragmentOffset++]);
}
}
- }
- // 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;
+ return true;
+ }
- if(edx >= esi)
+ // HOTS: 0195A820
+ int LoadFromStream(TByteStream & InStream)
{
- // HOTS: 1959ea4
- ecx = ~Struct68_00.ItemBits.Uint32Array[++edi];
- edx = edx - esi;
- eax = GetNumberOfSetBits(ecx);
+ int nError;
+
+ nError = PathFragments.LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ return PathMarks.LoadFromStream(InStream);
}
- // 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)
+ TGenericArray<char> PathFragments;
+ TSparseArray PathMarks;
+};
+
+//-----------------------------------------------------------------------------
+// TStruct10 functions
+
+class TStruct10
+{
+ public:
+
+ TStruct10()
{
- // HOTS: 1959EFC
- eax = eax & 0xFF;
- if(edx >= eax)
- {
- // HOTS: 1959F05
- ecx >>= 0x08;
- edi += 0x08;
- edx -= eax;
- }
+ field_0 = 0x03;
+ field_4 = 0x200;
+ field_8 = 0x1000;
+ field_C = 0x20000;
}
- else
+
+ // HOTS: 1956FD0
+ int sub_1956FD0(DWORD dwBitMask)
{
- // HOTS: 1959F0D
- eax = (eax >> 0x10) & 0xFF;
- if(edx < eax)
+ switch(dwBitMask & 0xF80)
{
- // 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;
+ case 0x00:
+ field_4 = 0x200;
+ return ERROR_SUCCESS;
- // 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;
-}
+ case 0x80:
+ field_4 = 0x80;
+ return ERROR_SUCCESS;
-DWORD TFileNameDatabase::sub_1959F50(DWORD arg_0)
-{
- PTRIPLET pTriplet;
- PDWORD ItemArray;
- DWORD eax, ebx, ecx, edx, esi, edi;
+ case 0x100:
+ field_4 = 0x100;
+ return ERROR_SUCCESS;
- edx = arg_0;
- eax = arg_0 >> 0x09;
- if((arg_0 & 0x1FF) == 0)
- return Struct68_00.ArrayDwords_50.Uint32Array[eax];
+ case 0x200:
+ field_4 = 0x200;
+ return ERROR_SUCCESS;
- ItemArray = Struct68_00.ArrayDwords_50.Uint32Array + eax;
- eax = (ItemArray[0] >> 0x09);
- edi = (ItemArray[1] + 0x1FF) >> 0x09;
+ case 0x400:
+ field_4 = 0x400;
+ return ERROR_SUCCESS;
- if((eax + 0x0A) > edi)
- {
- // HOTS: 01959F94
- pTriplet = Struct68_00.BaseValues.TripletArray + eax + 1;
- while(edx >= pTriplet->BaseValue)
- {
- // HOTS: 1959FA3
- pTriplet++;
- eax++;
+ case 0x800:
+ field_4 = 0x800;
+ return ERROR_SUCCESS;
}
+
+ return ERROR_INVALID_PARAMETER;
}
- else
+
+ // HOTS: 1957050
+ int sub_1957050(DWORD dwBitMask)
{
- // Binary search
- // HOTS: 1959FAD
- if((eax + 1) < edi)
+ switch(dwBitMask & 0xF0000)
{
- // HOTS: 1959FB4
- esi = (edi + eax) >> 1;
- if(edx < Struct68_00.BaseValues.TripletArray[esi].BaseValue)
- {
- // HOTS: 1959FC4
- edi = esi;
- }
- else
- {
- // HOTS: 1959FC8
- eax = esi;
- }
+ 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: 1959FD4
- pTriplet = Struct68_00.BaseValues.TripletArray + eax;
- edx = edx - pTriplet->BaseValue;
- edi = eax << 0x04;
- eax = pTriplet->Value2;
- ebx = (eax >> 0x17);
- if(edx < ebx)
+ // HOTS: 19572E0
+ int sub_19572E0(DWORD dwBitMask)
{
- // HOTS: 1959FF1
- esi = (eax >> 0x07) & 0xFF;
- if(edx < esi)
+ 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)
{
- // HOTS: 0195A000
- eax = eax & 0x7F;
- if(edx >= eax)
- {
- // HOTS: 195A007
- edi = edi + 2;
- edx = edx - eax;
- }
+ field_8 = 0x1000;
+ return sub_1957050(dwBitMask);
}
- else
+
+ if(dwSubMask == 0x2000)
{
- // HOTS: 195A00E
- eax = (eax >> 0x0F) & 0xFF;
- if(edx < eax)
- {
- // HOTS: 195A01A
- edi += 4;
- edx = edx - esi;
- }
- else
- {
- // HOTS: 195A01F
- edi += 6;
- edx = edx - eax;
- }
+ field_8 = 0x2000;
+ return sub_1957050(dwBitMask);
}
+
+ return ERROR_INVALID_PARAMETER;
}
- else
+
+ // HOTS: 1957800
+ int sub_1957800(DWORD dwBitMask)
{
- // 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;
- }
- }
+ return sub_19572E0(dwBitMask);
}
- // HOTS: 195A066
- esi = Struct68_00.ItemBits.Uint32Array[edi];
- eax = GetNumberOfSetBits(esi);
- ecx = eax >> 0x18;
+ DWORD field_0;
+ DWORD field_4;
+ DWORD field_8;
+ DWORD field_C;
+};
+
+//-----------------------------------------------------------------------------
+// TFileNameDatabase interface/implementation
+
+class TFileNameDatabase
+{
+ public:
- if(edx >= ecx)
+ // HOTS: 01958730
+ TFileNameDatabase()
{
- // HOTS: 195A0B2
- esi = Struct68_00.ItemBits.Uint32Array[++edi];
- edx = edx - ecx;
- eax = GetNumberOfSetBits(esi);
+ HashTableMask = 0;
+ field_214 = 0;
+ pChildDB = NULL;
}
- // HOTS: 195A0F6
- ecx = (eax >> 0x08) & 0xFF;
+ ~TFileNameDatabase()
+ {
+ delete pChildDB;
+ }
- edi = (edi << 0x05);
- if(edx < ecx)
+ // Returns nonzero if the name fragment match is a single-char match
+ bool IsPathFragmentSingleChar(HASH_ENTRY * pHashEntry)
{
- // HOTS: 195A111
- eax = eax & 0xFF;
- if(edx >= eax)
- {
- // HOTS: 195A111
- edi = edi + 0x08;
- esi = esi >> 0x08;
- edx = edx - eax;
- }
+ return ((pHashEntry->FragmentOffset & 0xFFFFFF00) == 0xFFFFFF00);
+ }
+
+ // Returns true if the given collision path fragment is a string (aka more than 1 char)
+ bool IsPathFragmentString(size_t index)
+ {
+ return CollisionHiBitsIndexes.IsItemPresent(index);
}
- else
+
+ // HOTS: 1957350, inlined
+ DWORD GetPathFragmentOffset1(DWORD index_lobits)
{
- // HOTS: 195A119
- eax = (eax >> 0x10) & 0xFF;
- if(edx < eax)
+ DWORD index_hibits = CollisionHiBitsIndexes.GetItemValueAt(index_lobits);
+
+ return (HiBitsTable.GetItem(index_hibits) << 0x08) | LoBitsTable[index_lobits];
+ }
+
+ // Retrieves fragment_offset/subtable_index of the path fragment, with check for starting value
+ DWORD GetPathFragmentOffset2(DWORD & index_hibits, DWORD index_lobits)
+ {
+ // If the hi-bits index is invalid, we need to get its starting value
+ if (index_hibits == CASC_INVALID_INDEX)
{
- // HOTS: 195A125
- esi = esi >> 0x10;
- edi = edi + 0x10;
- edx = edx - ecx;
+/*
+ printf("\n");
+ for (DWORD i = 0; i < CollisionHiBitsIndexes.TotalItemCount; i++)
+ {
+ if (CollisionHiBitsIndexes.IsItemPresent(i))
+ printf("[%02X] = %02X\n", i, CollisionHiBitsIndexes.GetIntValueAt(i));
+ else
+ printf("[%02X] = NOT_PRESENT\n", i);
+ }
+*/
+ index_hibits = CollisionHiBitsIndexes.GetItemValueAt(index_lobits);
}
else
{
- // HOTS: 195A12F
- esi = esi >> 0x18;
- edi = edi + 0x18;
- edx = edx - eax;
+ index_hibits++;
}
+
+ // Now we use both NodeIndex and HiBits index for retrieving the path fragment index
+ return (HiBitsTable.GetItem(index_hibits) << 0x08) | LoBitsTable[index_lobits];
}
- esi = esi & 0xFF;
- edx = edx << 0x08;
+ // HOTS: 1956DA0
+ int Load(LPBYTE pbMarData, size_t cbMarData)
+ {
+ TByteStream ByteStream;
+ DWORD dwSignature;
+ int nError;
- // 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;
-}
+ if(pbMarData == NULL && cbMarData != 0)
+ return ERROR_INVALID_PARAMETER;
-// 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))
+ nError = ByteStream.SetByteBuffer(pbMarData, cbMarData);
+ if(nError == ERROR_SUCCESS)
{
- pStruct40->ItemIndex = NameFragTable.NameFragArray[NameFragIndex].NextIndex;
- pStruct40->CharIndex++;
- return true;
- }
+ // Get pointer to MAR signature
+ nError = ByteStream.GetValue<DWORD>(dwSignature);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- // 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;
+ // Verify the signature
+ if(dwSignature != MNDX_MAR_SIGNATURE)
+ return ERROR_BAD_FORMAT;
+
+ // HOTS: 1956E11
+ nError = LoadFromStream(ByteStream);
}
- pStruct40->ItemIndex = NameFragTable.NameFragArray[NameFragIndex].NextIndex;
- return true;
+ return nError;
}
- //
- // Conflict: Multiple hashes give the same table index
- //
+ // HOTS: 19584B0
+ int SetChildDatabase(TFileNameDatabase * pNewDB)
+ {
+ if(pNewDB != NULL && pChildDB == pNewDB)
+ return ERROR_INVALID_PARAMETER;
- // HOTS: 1957A0E
- CollisionIndex = sub_1959CB0(pStruct40->ItemIndex) + 1;
- if(!Struct68_00.IsItemPresent(CollisionIndex))
- return false;
+ if(pChildDB != NULL)
+ delete pChildDB;
+ pChildDB = pNewDB;
+ return ERROR_SUCCESS;
+ }
- pStruct40->ItemIndex = (CollisionIndex - pStruct40->ItemIndex - 1);
- HiBitsIndex = 0xFFFFFFFF;
+ // HOTS: 1957970
+ bool ComparePathFragment(TMndxSearch * pSearch)
+ {
+ TStruct40 * pStruct40 = &pSearch->Struct40;
+ PHASH_ENTRY pHashEntry;
+ DWORD ColTableIndex;
+ DWORD HiBitsIndex;
+ DWORD NodeIndex;
-// CascDumpSparseArray("E:\\casc-array-68.txt", &FileNameIndexes);
-// CascDumpSparseArray("E:\\casc-array-D0.txt", &Struct68_D0);
+ // Calculate the item hash from the current char and fragment ID
+ NodeIndex = pStruct40->CalcHashValue(pSearch->szSearchMask) & HashTableMask;
+ pHashEntry = &HashTable[NodeIndex];
- // 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))
+ // Does the hash value ID match?
+ if(pHashEntry->NodeIndex == pStruct40->NodeIndex)
{
- if(HiBitsIndex == 0xFFFFFFFF)
+ // Check if there is single character match
+ if (!IsPathFragmentSingleChar(pHashEntry))
{
- // HOTS: 1957A6C
- HiBitsIndex = Struct68_D0.GetItemValue(pStruct40->ItemIndex);
+ // Check if there is a name fragment match
+ if (pChildDB != NULL)
+ {
+ if (!pChildDB->ComparePathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex))
+ return false;
+ }
+ else
+ {
+ if (!PathFragmentTable.ComparePathFragment(pSearch, pHashEntry->FragmentOffset))
+ return false;
+ }
}
else
{
- // HOTS: 1957A7F
- HiBitsIndex++;
+ pStruct40->PathLength++;
}
- // HOTS: 1957A83
- SaveCharIndex = pStruct40->CharIndex;
+ pStruct40->NodeIndex = pHashEntry->NextIndex;
+ return true;
+ }
+
+ //
+ // Conflict: Multiple node IDs give the same table index
+ //
- // Get the name fragment offset as combined value from lower 8 bits and upper bits
- FragOffs = GetNameFragmentOffsetEx(pStruct40->ItemIndex, HiBitsIndex);
+ // HOTS: 1957A0E
+ ColTableIndex = CollisionTable.GetItem0(pStruct40->NodeIndex) + 1;
+ pStruct40->NodeIndex = (ColTableIndex - pStruct40->NodeIndex - 1);
+ HiBitsIndex = CASC_INVALID_INDEX;
- // Compare the string with the fragment name database
- if(NextDB.pDB != NULL)
+ // HOTS: 1957A41:
+ while(CollisionTable.IsItemPresent(ColTableIndex))
+ {
+ // HOTS: 1957A41
+ // Check if the low 8 bits if the fragment offset contain a single character
+ // or an offset to a name fragment
+ if(IsPathFragmentString(pStruct40->NodeIndex))
{
- // HOTS: 1957AEC
- if(NextDB.pDB->sub_1957B80(pStruct1C, FragOffs))
- return true;
+ DWORD FragmentOffset = GetPathFragmentOffset2(HiBitsIndex, pStruct40->NodeIndex);
+ DWORD SavePathLength = pStruct40->PathLength; // HOTS: 1957A83
+
+ // Do we have a child database?
+ if(pChildDB != NULL)
+ {
+ // HOTS: 1957AEC
+ if(pChildDB->ComparePathFragmentByIndex(pSearch, FragmentOffset))
+ return true;
+ }
+ else
+ {
+ // HOTS: 1957AF7
+ if(PathFragmentTable.ComparePathFragment(pSearch, FragmentOffset))
+ return true;
+ }
+
+ // HOTS: 1957B0E
+ // If there was partial match with the fragment, end the search
+ if(pStruct40->PathLength != SavePathLength)
+ return false;
}
else
{
- // HOTS: 1957AF7
- if(IndexStruct_174.CheckNameFragment(pStruct1C, FragOffs))
+ // HOTS: 1957B1C
+ if(LoBitsTable[pStruct40->NodeIndex] == pSearch->szSearchMask[pStruct40->PathLength])
+ {
+ pStruct40->PathLength++;
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->NodeIndex++;
+ ColTableIndex++;
}
- // HOTS: 1957B32
- pStruct40->ItemIndex++;
- CollisionIndex++;
+ return false;
}
- 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(;;)
+ // HOTS: 1957B80
+ bool ComparePathFragmentByIndex(TMndxSearch * pSearch, DWORD TableIndex)
{
- pNameEntry = NameFragTable.NameFragArray + (edi & NameFragIndexMask);
- if(edi == pNameEntry->NextIndex)
+ TStruct40 * pStruct40 = &pSearch->Struct40;
+ PHASH_ENTRY pHashEntry;
+ DWORD eax;
+
+ // HOTS: 1957B95
+ for (;;)
{
- // HOTS: 01957BB4
- if((pNameEntry->FragOffs & 0xFFFFFF00) != 0xFFFFFF00)
+ // Get the hasn table item
+ pHashEntry = &HashTable[TableIndex & HashTableMask];
+
+ //
+ if (TableIndex == pHashEntry->NextIndex)
{
- // HOTS: 1957BC7
- if(NextDB.pDB != NULL)
+ // HOTS: 01957BB4
+ if (!IsPathFragmentSingleChar(pHashEntry))
{
- // HOTS: 1957BD3
- if(!NextDB.pDB->sub_1957B80(pStruct1C, pNameEntry->FragOffs))
- return false;
+ // HOTS: 1957BC7
+ if (pChildDB != NULL)
+ {
+ // HOTS: 1957BD3
+ if (!pChildDB->ComparePathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex))
+ return false;
+ }
+ else
+ {
+ // HOTS: 1957BE0
+ if (!PathFragmentTable.ComparePathFragment(pSearch, pHashEntry->FragmentOffset))
+ return false;
+ }
}
else
{
- // HOTS: 1957BE0
- if(!IndexStruct_174.CheckNameFragment(pStruct1C, pNameEntry->FragOffs))
+ // HOTS: 1957BEE
+ if (pSearch->szSearchMask[pStruct40->PathLength] != pHashEntry->SingleChar)
return false;
+ pStruct40->PathLength++;
}
- }
- 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;
+ // HOTS: 1957C05
+ TableIndex = pHashEntry->NodeIndex;
+ if (TableIndex == 0)
+ return true;
- if(pStruct40->CharIndex >= pStruct1C->cchSearchMask)
- return false;
- }
- else
- {
- // HOTS: 1957C30
- if(Struct68_D0.IsItemPresent(edi))
+ if (pStruct40->PathLength >= pSearch->cchSearchMask)
+ return false;
+ }
+ else
{
- // HOTS: 1957C4C
- if(NextDB.pDB != NULL)
+ // HOTS: 1957C30
+ if (IsPathFragmentString(TableIndex))
{
- // HOTS: 1957C58
- FragOffs = GetNameFragmentOffset(edi);
- if(!NextDB.pDB->sub_1957B80(pStruct1C, FragOffs))
- return false;
+ DWORD FragmentOffset = GetPathFragmentOffset1(TableIndex);
+
+ // HOTS: 1957C4C
+ if (pChildDB != NULL)
+ {
+ // HOTS: 1957C58
+ if (!pChildDB->ComparePathFragmentByIndex(pSearch, FragmentOffset))
+ return false;
+ }
+ else
+ {
+ // HOTS: 1957350
+ if (!PathFragmentTable.ComparePathFragment(pSearch, FragmentOffset))
+ return false;
+ }
}
else
{
- // HOTS: 1957350
- FragOffs = GetNameFragmentOffset(edi);
- if(!IndexStruct_174.CheckNameFragment(pStruct1C, FragOffs))
+ // HOTS: 1957C8E
+ if (LoBitsTable[TableIndex] != pSearch->szSearchMask[pStruct40->PathLength])
return false;
- }
- }
- else
- {
- // HOTS: 1957C8E
- if(FrgmDist_LoBits.ByteArray[edi] != pStruct1C->szSearchMask[pStruct40->CharIndex])
- return false;
- pStruct40->CharIndex++;
- }
+ pStruct40->PathLength++;
+ }
- // HOTS: 1957CB2
- if(edi <= field_214)
- return true;
+ // HOTS: 1957CB2
+ if (TableIndex <= field_214)
+ return true;
- if(pStruct40->CharIndex >= pStruct1C->cchSearchMask)
- return false;
+ if (pStruct40->PathLength >= pSearch->cchSearchMask)
+ return false;
- eax = sub_1959F50(edi);
- edi = (eax - edi - 1);
+ eax = CollisionTable.GetItem1(TableIndex);
+ TableIndex = (eax - TableIndex - 1);
+ }
}
}
-}
-// HOTS: 1958D70
-void TFileNameDatabase::sub_1958D70(TMndxFindResult * pStruct1C, DWORD arg_4)
-{
- TStruct40 * pStruct40 = pStruct1C->pStruct40;
- PNAME_FRAG pNameEntry;
-
- // HOTS: 1958D84
- for(;;)
+ // HOTS: 1958D70
+ void CopyPathFragmentByIndex(TMndxSearch * pSearch, DWORD TableIndex)
{
- pNameEntry = NameFragTable.NameFragArray + (arg_4 & NameFragIndexMask);
- if(arg_4 == pNameEntry->NextIndex)
+ TStruct40 * pStruct40 = &pSearch->Struct40;
+ PHASH_ENTRY pHashEntry;
+
+ // HOTS: 1958D84
+ for (;;)
{
- // HOTS: 1958DA6
- if((pNameEntry->FragOffs & 0xFFFFFF00) != 0xFFFFFF00)
+ pHashEntry = &HashTable[TableIndex & HashTableMask];
+ if (TableIndex == pHashEntry->NextIndex)
{
- // HOTS: 1958DBA
- if(NextDB.pDB != NULL)
+ // HOTS: 1958DA6
+ if (!IsPathFragmentSingleChar(pHashEntry))
{
- NextDB.pDB->sub_1958D70(pStruct1C, pNameEntry->FragOffs);
+ // HOTS: 1958DBA
+ if (pChildDB != NULL)
+ {
+ pChildDB->CopyPathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex);
+ }
+ else
+ {
+ PathFragmentTable.CopyPathFragment(pSearch, pHashEntry->FragmentOffset);
+ }
}
else
{
- IndexStruct_174.CopyNameFragment(pStruct1C, pNameEntry->FragOffs);
+ // HOTS: 1958DE7
+ // Insert the low 8 bits to the path being built
+ pStruct40->PathBuffer.Insert(pHashEntry->SingleChar);
}
+
+ // HOTS: 1958E71
+ TableIndex = pHashEntry->NodeIndex;
+ if (TableIndex == 0)
+ return;
}
else
{
- // HOTS: 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)
+ // HOTS: 1958E8E
+ if (IsPathFragmentString(TableIndex))
{
- NextDB.pDB->sub_1958D70(pStruct1C, FragOffs);
+ DWORD FragmentOffset = GetPathFragmentOffset1(TableIndex);
+
+ // HOTS: 1958EAF
+ if (pChildDB != NULL)
+ {
+ pChildDB->CopyPathFragmentByIndex(pSearch, FragmentOffset);
+ }
+ else
+ {
+ PathFragmentTable.CopyPathFragment(pSearch, FragmentOffset);
+ }
}
else
{
- IndexStruct_174.CopyNameFragment(pStruct1C, FragOffs);
+ // HOTS: 1958F50
+ // Insert one character to the path being built
+ pStruct40->PathBuffer.Insert(LoBitsTable[TableIndex]);
}
- }
- 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;
+ // HOTS: 1958FDE
+ if (TableIndex <= field_214)
+ return;
- arg_4 = 0xFFFFFFFF - arg_4 + sub_1959F50(arg_4);
+ TableIndex = 0xFFFFFFFF - TableIndex + CollisionTable.GetItem1(TableIndex);
+ }
}
}
-}
-
-// HOTS: 1959010
-bool TFileNameDatabase::sub_1959010(TMndxFindResult * pStruct1C, DWORD arg_4)
-{
- TStruct40 * pStruct40 = pStruct1C->pStruct40;
- PNAME_FRAG pNameEntry;
- // HOTS: 1959024
- for(;;)
+ // HOTS: 1958B00
+ bool CompareAndCopyPathFragment(TMndxSearch * pSearch)
{
- pNameEntry = NameFragTable.NameFragArray + (arg_4 & NameFragIndexMask);
- if(arg_4 == pNameEntry->NextIndex)
+ TStruct40 * pStruct40 = &pSearch->Struct40;
+ PHASH_ENTRY pHashEntry;
+ DWORD HiBitsIndex;
+ DWORD ColTableIndex;
+ DWORD TableIndex;
+/*
+ FILE * fp = fopen("E:\\PathFragmentTable.txt", "wt");
+ if (fp != NULL)
{
- // HOTS: 1959047
- if((pNameEntry->FragOffs & 0xFFFFFF00) != 0xFFFFFF00)
+ for (DWORD i = 0; i < HashTable.ItemCount; i++)
{
- // HOTS: 195905A
- if(NextDB.pDB != NULL)
- {
- if(!NextDB.pDB->sub_1959010(pStruct1C, pNameEntry->FragOffs))
- return false;
- }
- else
+ FragOffs = HashTable[i].FragOffs;
+ fprintf(fp, "%02x ('%c') %08X %08X %08X", i, (0x20 <= i && i < 0x80) ? i : 0x20, HashTable[i].ItemIndex, HashTable[i].NextIndex, FragOffs);
+
+ if(FragOffs != 0x00800000)
{
- if(!IndexStruct_174.CheckAndCopyNameFragment(pStruct1C, pNameEntry->FragOffs))
- return false;
+ if((FragOffs & 0xFFFFFF00) == 0xFFFFFF00)
+ fprintf(fp, " '%c'", (char)(FragOffs & 0xFF));
+ else
+ fprintf(fp, " %s", &PathFragmentTable.PathFragments[FragOffs]);
}
- }
- 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++;
+ fprintf(fp, "\n");
}
- // HOTS: 195912E
- arg_4 = pNameEntry->ItemIndex;
- if(arg_4 == 0)
- return true;
+ fclose(fp);
}
- else
+*/
+ // Calculate the item hash from the current char and fragment ID
+ TableIndex = pStruct40->CalcHashValue(pSearch->szSearchMask) & HashTableMask;
+ pHashEntry = &HashTable[TableIndex];
+
+ // Does the hash value ID match?
+ if(pStruct40->NodeIndex == pHashEntry->NodeIndex)
{
- // HOTS: 1959147
- if(Struct68_D0.IsItemPresent(arg_4))
+ // If the higher 24 bits are set, then the fragment is just one letter,
+ // contained directly in the table.
+ if(!IsPathFragmentSingleChar(pHashEntry))
{
- DWORD FragOffs;
-
- // HOTS: 195917C
- FragOffs = GetNameFragmentOffset(arg_4);
- if(NextDB.pDB != NULL)
+ // HOTS: 1958B59
+ if (pChildDB != NULL)
{
- if(!NextDB.pDB->sub_1959010(pStruct1C, FragOffs))
+ if (!pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex))
return false;
}
else
{
- if(!IndexStruct_174.CheckAndCopyNameFragment(pStruct1C, FragOffs))
+ if (!PathFragmentTable.CompareAndCopyPathFragment(pSearch, pHashEntry->FragmentOffset))
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: 1958B88
+ pStruct40->PathBuffer.Insert(pHashEntry->SingleChar);
+ pStruct40->PathLength++;
}
- }
-
- // 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);
+ // HOTS: 1958BCA
+ pStruct40->NodeIndex = pHashEntry->NextIndex;
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: 1958BE5
+ ColTableIndex = CollisionTable.GetItem0(pStruct40->NodeIndex) + 1;
+ pStruct40->NodeIndex = (ColTableIndex - pStruct40->NodeIndex - 1);
+ HiBitsIndex = CASC_INVALID_INDEX;
- // HOTS: 19595CC
- if(Struct68_00.IsItemPresent(pPathStop->field_4++))
+ // Keep searching while we have a valid collision table entry
+ while(CollisionTable.IsItemPresent(ColTableIndex))
{
- // HOTS: 19595F2
- pStruct40->ItemCount++;
-
- if(Struct68_D0.IsItemPresent(pPathStop->ItemIndex))
+ // If we have high bits in the the bit at NodeIndex is set, it means that there is fragment offset
+ // If not, the byte in LoBitsTable is the character
+ if(IsPathFragmentString(pStruct40->NodeIndex))
{
- // HOTS: 1959617
- if(pPathStop->field_C == 0xFFFFFFFF)
- pPathStop->field_C = Struct68_D0.GetItemValue(pPathStop->ItemIndex);
- else
- pPathStop->field_C++;
+ DWORD FragmentOffset = GetPathFragmentOffset2(HiBitsIndex, pStruct40->NodeIndex);
+ DWORD SavePathLength = pStruct40->PathLength; // HOTS: 1958C62
- // HOTS: 1959630
- FragOffs = GetNameFragmentOffsetEx(pPathStop->ItemIndex, pPathStop->field_C);
- if(NextDB.pDB != NULL)
+ // Do we have a child database?
+ if(pChildDB != NULL)
{
- // HOTS: 1959649
- NextDB.pDB->sub_1958D70(pStruct1C, FragOffs);
+ // HOTS: 1958CCB
+ if(pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, FragmentOffset))
+ return true;
}
else
{
- // HOTS: 1959654
- IndexStruct_174.CopyNameFragment(pStruct1C, FragOffs);
+ // HOTS: 1958CD6
+ if(PathFragmentTable.CompareAndCopyPathFragment(pSearch, FragmentOffset))
+ return true;
}
+
+ // HOTS: 1958CED
+ if(SavePathLength != pStruct40->PathLength)
+ return false;
}
else
{
- // HOTS: 1959665
- // Insert one character to the path being built
- pStruct40->array_00.InsertOneItem_CHAR(FrgmDist_LoBits.CharArray[pPathStop->ItemIndex]);
+ // HOTS: 1958CFB
+ if(LoBitsTable[pStruct40->NodeIndex] == pSearch->szSearchMask[pStruct40->PathLength])
+ {
+ // HOTS: 1958D11
+ pStruct40->PathBuffer.Insert(LoBitsTable[pStruct40->NodeIndex]);
+ pStruct40->PathLength++;
+ return true;
+ }
}
- // HOTS: 19596AE
- pPathStop->field_8 = pStruct40->array_00.ItemCount;
+ // HOTS: 1958D11
+ pStruct40->NodeIndex++;
+ ColTableIndex++;
+ }
+
+ return false;
+ }
+
+ // HOTS: 1959010
+ bool CompareAndCopyPathFragmentByIndex(TMndxSearch * pSearch, DWORD TableIndex)
+ {
+ TStruct40 * pStruct40 = &pSearch->Struct40;
+ PHASH_ENTRY pHashEntry;
- // HOTS: 19596b6
- if(FileNameIndexes.IsItemPresent(pPathStop->ItemIndex))
+ // HOTS: 1959024
+ for(;;)
+ {
+ pHashEntry = &HashTable[TableIndex & HashTableMask];
+ if(TableIndex == pHashEntry->NextIndex)
{
- // HOTS: 19596D1
- if(pPathStop->field_10 == 0xFFFFFFFF)
+ // HOTS: 1959047
+ if(!IsPathFragmentSingleChar(pHashEntry))
{
- // HOTS: 19596D9
- pPathStop->field_10 = FileNameIndexes.GetItemValue(pPathStop->ItemIndex);
+ // HOTS: 195905A
+ if(pChildDB != NULL)
+ {
+ if(!pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, pHashEntry->ChildTableIndex))
+ return false;
+ }
+ else
+ {
+ if(!PathFragmentTable.CompareAndCopyPathFragment(pSearch, pHashEntry->FragmentOffset))
+ return false;
+ }
}
else
{
- pPathStop->field_10++;
+ // HOTS: 1959092
+ if(pHashEntry->SingleChar != pSearch->szSearchMask[pStruct40->PathLength])
+ return false;
+
+ // Insert the low 8 bits to the path being built
+ pStruct40->PathBuffer.Insert(pHashEntry->SingleChar);
+ pStruct40->PathLength++;
}
- // HOTS: 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: 195912E
+ TableIndex = pHashEntry->NodeIndex;
+ if(TableIndex == 0)
+ return true;
}
-
- // 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)
+ else
{
- // HOTS: 1959717
- NewMaxItemCount = edi;
-
- if(pStruct40->array_00.MaxItemCount > (edi / 2))
+ // HOTS: 1959147
+ if(IsPathFragmentString(TableIndex))
{
- if(pStruct40->array_00.MaxItemCount > 0x7FFFFFFF)
+ // HOTS: 195917C
+ DWORD FragmentOffset = GetPathFragmentOffset1(TableIndex);
+
+ if(pChildDB != NULL)
{
- NewMaxItemCount = 0xFFFFFFFF;
+ if(!pChildDB->CompareAndCopyPathFragmentByIndex(pSearch, FragmentOffset))
+ return false;
}
else
{
- NewMaxItemCount = pStruct40->array_00.MaxItemCount + pStruct40->array_00.MaxItemCount;
+ if(!PathFragmentTable.CompareAndCopyPathFragment(pSearch, FragmentOffset))
+ return false;
}
}
+ else
+ {
+ // HOTS: 195920E
+ if(LoBitsTable[TableIndex] != pSearch->szSearchMask[pStruct40->PathLength])
+ return false;
- pStruct40->array_00.SetMaxItems_CHARS(NewMaxItemCount);
- }
+ // Insert one character to the path being built
+ pStruct40->PathBuffer.Insert(LoBitsTable[TableIndex]);
+ pStruct40->PathLength++;
+ }
- // HOTS: 1959749
- pStruct40->array_00.ItemCount = edi;
- pStruct40->ItemCount--;
- }
- }
-}
+ // HOTS: 19592B6
+ if(TableIndex <= field_214)
+ return true;
-// 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;
- }
+ TableIndex = 0xFFFFFFFF - TableIndex + CollisionTable.GetItem1(TableIndex);
+ }
- // 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: 19592D5
+ if(pStruct40->PathLength >= pSearch->cchSearchMask)
+ break;
}
- // HOTS: 1958BCA
- pStruct40->ItemIndex = NameFragTable.NameFragArray[ItemIndex].NextIndex;
+ CopyPathFragmentByIndex(pSearch, TableIndex);
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(;;)
+ // HOTS: 1959460
+ bool DoSearch(TMndxSearch * pSearch)
{
- if(Struct68_D0.IsItemPresent(pStruct40->ItemIndex))
+ TStruct40 * pStruct40 = &pSearch->Struct40;
+ TPathStop * pPathStop;
+ DWORD edi;
+
+ // Perform action based on the search phase
+ switch (pStruct40->SearchPhase)
{
- // HOTS: 1958C0E
- if(var_4 == 0xFFFFFFFF)
- {
- // HOTS: 1958C4B
- var_4 = Struct68_D0.GetItemValue(pStruct40->ItemIndex);
- }
- else
+ case MNDX_SEARCH_INITIALIZING:
{
- var_4++;
- }
+ // HOTS: 1959489
+ pStruct40->BeginSearch();
- // HOTS: 1958C62
- SaveCharIndex = pStruct40->CharIndex;
+ // If the caller passed a part of the search path, we need to find that one
+ while (pStruct40->PathLength < pSearch->cchSearchMask)
+ {
+ if (!CompareAndCopyPathFragment(pSearch))
+ {
+ pStruct40->SearchPhase = MNDX_SEARCH_FINISHED;
+ return false;
+ }
+ }
- 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))
+ // HOTS: 19594b0
+ TPathStop PathStop(pStruct40->NodeIndex, 0, pStruct40->PathBuffer.ItemCount);
+ pStruct40->PathStops.Insert(PathStop);
+ pStruct40->ItemCount = 1;
+
+ if (FileNameIndexes.IsItemPresent(pStruct40->NodeIndex))
+ {
+ pSearch->szFoundPath = &pStruct40->PathBuffer[0];
+ pSearch->cchFoundPath = pStruct40->PathBuffer.ItemCount;
+ pSearch->nIndex = FileNameIndexes.GetItemValueAt(pStruct40->NodeIndex);
return true;
+ }
}
+ // No break here, go straight to the MNDX_SEARCH_SEARCHING
- // HOTS: 1958CED
- if(SaveCharIndex != pStruct40->CharIndex)
- return false;
- }
- else
- {
- // HOTS: 1958CFB
- if(FrgmDist_LoBits.ByteArray[pStruct40->ItemIndex] == pStruct1C->szSearchMask[pStruct40->CharIndex])
+ case MNDX_SEARCH_SEARCHING:
{
- // HOTS: 1958D11
- pStruct40->array_00.InsertOneItem_CHAR(FrgmDist_LoBits.ByteArray[pStruct40->ItemIndex]);
- pStruct40->CharIndex++;
- return true;
- }
- }
+ // HOTS: 1959522
+ for (;;)
+ {
+ // HOTS: 1959530
+ if (pStruct40->ItemCount == pStruct40->PathStops.ItemCount)
+ {
+ TPathStop * pLastStop;
+ DWORD ColTableIndex;
- // HOTS: 1958D11
- pStruct40->ItemIndex++;
- CollisionIndex++;
+ pLastStop = &pStruct40->PathStops[pStruct40->PathStops.ItemCount - 1];
- if(!Struct68_00.IsItemPresent(CollisionIndex))
- break;
- }
+ ColTableIndex = CollisionTable.GetItem0(pLastStop->LoBitsIndex) + 1;
- return false;
-}
+ // Insert a new structure
+ TPathStop PathStop(ColTableIndex - pLastStop->LoBitsIndex - 1, ColTableIndex, 0);
+ pStruct40->PathStops.Insert(PathStop);
+ }
-// HOTS: 1957EF0
-bool TFileNameDatabase::FindFileInDatabase(TMndxFindResult * pStruct1C)
-{
- TStruct40 * pStruct40 = pStruct1C->pStruct40;
+ // HOTS: 19595BD
+ pPathStop = &pStruct40->PathStops[pStruct40->ItemCount];
- pStruct40->ItemIndex = 0;
- pStruct40->CharIndex = 0;
- pStruct40->SearchPhase = CASC_SEARCH_INITIALIZING;
+ // HOTS: 19595CC
+ if (CollisionTable.IsItemPresent(pPathStop->field_4++))
+ {
+ // HOTS: 19595F2
+ pStruct40->ItemCount++;
+
+ if (IsPathFragmentString(pPathStop->LoBitsIndex))
+ {
+ DWORD FragmentOffset = GetPathFragmentOffset2(pPathStop->HiBitsIndex_PathFragment, pPathStop->LoBitsIndex);
+
+ // HOTS: 1959630
+ if (pChildDB != NULL)
+ {
+ // HOTS: 1959649
+ pChildDB->CopyPathFragmentByIndex(pSearch, FragmentOffset);
+ }
+ else
+ {
+ // HOTS: 1959654
+ PathFragmentTable.CopyPathFragment(pSearch, FragmentOffset);
+ }
+ }
+ else
+ {
+ // HOTS: 1959665
+ // Insert one character to the path being built
+ pStruct40->PathBuffer.Insert(LoBitsTable[pPathStop->LoBitsIndex]);
+ }
+
+ // HOTS: 19596AE
+ pPathStop->Count = pStruct40->PathBuffer.ItemCount;
+
+ // HOTS: 19596b6
+ if (FileNameIndexes.IsItemPresent(pPathStop->LoBitsIndex))
+ {
+ // HOTS: 19596D1
+ if (pPathStop->field_10 == 0xFFFFFFFF)
+ {
+ // HOTS: 19596D9
+ pPathStop->field_10 = FileNameIndexes.GetItemValueAt(pPathStop->LoBitsIndex);
+ }
+ else
+ {
+ pPathStop->field_10++;
+ }
+
+ // HOTS: 1959755
+ pSearch->szFoundPath = &pStruct40->PathBuffer[0];
+ pSearch->cchFoundPath = pStruct40->PathBuffer.ItemCount;
+ pSearch->nIndex = pPathStop->field_10;
+ return true;
+ }
+ }
+ else
+ {
+ // HOTS: 19596E9
+ if (pStruct40->ItemCount == 1)
+ {
+ pStruct40->SearchPhase = MNDX_SEARCH_FINISHED;
+ return false;
+ }
+
+ // HOTS: 19596F5
+ pStruct40->PathStops[pStruct40->ItemCount - 1].LoBitsIndex++;
+ edi = pStruct40->PathStops[pStruct40->ItemCount - 2].Count;
+ pStruct40->PathBuffer.SetMaxItemsIf(edi);
+
+ // HOTS: 1959749
+ pStruct40->PathBuffer.ItemCount = edi;
+ pStruct40->ItemCount--;
+ }
+ }
+ }
- if(pStruct1C->cchSearchMask > 0)
- {
- while(pStruct40->CharIndex < pStruct1C->cchSearchMask)
- {
- // HOTS: 01957F12
- if(!CheckNextPathFragment(pStruct1C))
- return false;
+ case MNDX_SEARCH_FINISHED:
+ break;
}
- }
- // 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;
+ // HOTS: 1957EF0
+ bool FindFileInDatabase(TMndxSearch * pSearch)
+ {
+ TStruct40 * pStruct40 = &pSearch->Struct40;
- nError = FileNameIndexes.LoadFromStream_Exchange(InStream);
- if(nError != ERROR_SUCCESS)
- return nError;
+ pStruct40->NodeIndex = 0;
+ pStruct40->PathLength = 0;
+ pStruct40->SearchPhase = MNDX_SEARCH_INITIALIZING;
- 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;
+ if(pSearch->cchSearchMask > 0)
+ {
+ while(pStruct40->PathLength < pSearch->cchSearchMask)
+ {
+ // HOTS: 01957F12
+ if(!ComparePathFragment(pSearch))
+ return false;
+ }
+ }
- nError = FrgmDist_HiBits.LoadFromStream_Exchange(InStream);
- if(nError != ERROR_SUCCESS)
- return nError;
+ // HOTS: 1957F26
+ if(!FileNameIndexes.IsItemPresent(pStruct40->NodeIndex))
+ return false;
- // HOTS: 019597F5
- nError = IndexStruct_174.LoadFromStream_Exchange(InStream);
- if(nError != ERROR_SUCCESS)
- return nError;
+ pSearch->szFoundPath = pSearch->szSearchMask;
+ pSearch->cchFoundPath = pSearch->cchSearchMask;
+ pSearch->nIndex = FileNameIndexes.GetItemValueAt(pStruct40->NodeIndex);
+ return true;
+ }
- // HOTS: 0195980A
- if(Struct68_D0.ValidItemCount != 0 && IndexStruct_174.NameFragments.ItemCount == 0)
+ // HOTS: 1959790
+ int LoadFromStream(TByteStream & InStream)
{
- TFileNameDatabase * pNextDB = new TFileNameDatabase;
+ DWORD dwBitMask;
+ int nError;
- nError = NextDB.SetDatabase(pNextDB);
+ nError = CollisionTable.LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
- if(NextDB.pDB == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- nError = NextDB.pDB->LoadFromStream(InStream);
+ nError = FileNameIndexes.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;
-}
+ nError = CollisionHiBitsIndexes.LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
-// HOTS: 1956C60
-int TFileNameDatabasePtr::FindFileInDatabase(TMndxFindResult * pStruct1C)
-{
- int nError = ERROR_SUCCESS;
+ // HOTS: 019597CD
+ nError = LoBitsTable.LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- if(pDB == NULL)
- return ERROR_INVALID_PARAMETER;
+ nError = HiBitsTable.LoadBitsFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- nError = pStruct1C->CreateStruct40();
- if(nError != ERROR_SUCCESS)
- return nError;
+ // HOTS: 019597F5
+ nError = PathFragmentTable.LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- if(!pDB->FindFileInDatabase(pStruct1C))
- nError = ERROR_FILE_NOT_FOUND;
+ // HOTS: 0195980A
+ if(CollisionHiBitsIndexes.ValidItemCount != 0 && PathFragmentTable.PathFragments.ItemCount == 0)
+ {
+ TFileNameDatabase * pNewDB;
- pStruct1C->FreeStruct40();
- return nError;
-}
+ pNewDB = new TFileNameDatabase;
+ if (pNewDB == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
-// HOTS: 1956CE0
-int TFileNameDatabasePtr::sub_1956CE0(TMndxFindResult * pStruct1C, bool * pbFindResult)
-{
- int nError = ERROR_SUCCESS;
+ nError = SetChildDatabase(pNewDB);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- if(pDB == NULL)
- return ERROR_INVALID_PARAMETER;
+ nError = pChildDB->LoadFromStream(InStream);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ }
- // Create the pStruct40, if not initialized yet
- if(pStruct1C->pStruct40 == NULL)
- {
- nError = pStruct1C->CreateStruct40();
+ // HOTS: 0195986B
+ nError = HashTable.LoadFromStream(InStream);
if(nError != ERROR_SUCCESS)
return nError;
- }
- *pbFindResult = pDB->sub_1959460(pStruct1C);
- return nError;
-}
+ HashTableMask = HashTable.ItemCount - 1;
-// HOTS: 1956D20
-int TFileNameDatabasePtr::GetFileNameCount(PDWORD PtrFileNameCount)
-{
- if(pDB == NULL)
- return ERROR_INVALID_PARAMETER;
+ nError = InStream.GetValue<DWORD>(field_214);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- PtrFileNameCount[0] = pDB->FileNameIndexes.ValidItemCount;
- return ERROR_SUCCESS;
-}
+ nError = InStream.GetValue<DWORD>(dwBitMask);
+ if(nError != ERROR_SUCCESS)
+ return nError;
-// HOTS: 1956DA0
-int TFileNameDatabasePtr::CreateDatabase(LPBYTE pbMarData, DWORD cbMarData)
-{
- TFileNameDatabase * pDatabase;
- TByteStream ByteStream;
- int nError;
+ return Struct10.sub_1957800(dwBitMask);
+ }
- if(pbMarData == NULL && cbMarData != 0)
- return ERROR_INVALID_PARAMETER;
+ TSparseArray CollisionTable; // Table of valid collisions, indexed by NodeIndex
+ TSparseArray FileNameIndexes; // Array of file name indexes
+ TSparseArray CollisionHiBitsIndexes; // Table of indexes of high bits (above 8 bits) for collisions
- pDatabase = new TFileNameDatabase;
- if(pDatabase != NULL)
- {
- nError = ByteStream.SetByteBuffer(pbMarData, cbMarData);
- if(nError == ERROR_SUCCESS)
- {
- // HOTS: 1956E11
- nError = pDatabase->LoadFromStream_Exchange(ByteStream);
- if(nError == ERROR_SUCCESS)
- {
- pDB = pDatabase;
- return ERROR_SUCCESS;
- }
- }
+ // This pair of arrays serves for fast conversion from node index to FragmentOffset / FragmentChar
+ TGenericArray<BYTE> LoBitsTable; // Array of lower 8 bits of name fragment offset
+ TBitEntryArray HiBitsTable; // Array of upper x bits of name fragment offset
- delete pDatabase;
- return nError;
- }
- else
- {
- return ERROR_NOT_ENOUGH_MEMORY;
- }
-}
+ TPathFragmentTable PathFragmentTable;
+ TFileNameDatabase * pChildDB;
-// HOTS: 19584B0
-int TFileNameDatabasePtr::SetDatabase(TFileNameDatabase * pNewDB)
-{
- if(pNewDB != NULL && pDB == pNewDB)
- return ERROR_INVALID_PARAMETER;
+ TGenericArray<HASH_ENTRY> HashTable; // Hash table for searching name fragments
- if(pDB != NULL)
- delete pDB;
- pDB = pNewDB;
- return ERROR_SUCCESS;
-}
+ DWORD HashTableMask; // Mask to get hash table index from hash value
+ DWORD field_214;
+ TStruct10 Struct10;
+};
//-----------------------------------------------------------------------------
// Local functions - MAR file
-// HOTS: 00E94180
-static void MAR_FILE_CreateDatabase(PMAR_FILE pMarFile)
+class TMndxMarFile
{
- pMarFile->pDatabasePtr = new TFileNameDatabasePtr;
- if(pMarFile->pDatabasePtr != NULL)
- pMarFile->pDatabasePtr->CreateDatabase(pMarFile->pbMarData, pMarFile->cbMarData);
-}
+ public:
-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)
+ TMndxMarFile()
{
- if(pMarFile->pDatabasePtr != NULL)
- delete pMarFile->pDatabasePtr;
- if(pMarFile->pbMarData != NULL)
- CASC_FREE(pMarFile->pbMarData);
-
- CASC_FREE(pMarFile);
+ pDatabase = NULL;
+ pbMarData = NULL;
+ cbMarData = 0;
}
-}
-
-//-----------------------------------------------------------------------------
-// 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)
+ ~TMndxMarFile()
{
- // 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;
+ if(pDatabase != NULL)
+ delete pDatabase;
+ CASC_FREE(pbMarData);
}
- 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);
+ // HOTS: 00E94180
+ int LoadRootData(FILE_MAR_INFO & MarInfo, LPBYTE pbRootFile, LPBYTE pbRootEnd)
+ {
+ // Allocate the MAR data
+ pbMarData = CASC_ALLOC(BYTE, MarInfo.MarDataSize);
+ cbMarData = MarInfo.MarDataSize;
+ if(pbMarData == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
- // 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;
- }
- }
+ // Capture the MAR data
+ if(!CaptureData(pbRootFile + MarInfo.MarDataOffset, pbRootEnd, pbMarData, cbMarData))
+ return ERROR_FILE_CORRUPT;
- // Fill the limits
- pPackages->NameEntries = nNewNameEntries;
- pPackages->NameBufferUsed = pOldPackages->NameBufferUsed;
- pPackages->NameBufferMax = nNewNameBufferMax;
+ // Create the file name database
+ pDatabase = new TFileNameDatabase();
+ if(pDatabase == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
- // Switch the name lists
- CASC_FREE(pOldPackages);
+ return pDatabase->Load(pbMarData, cbMarData);
}
- // The slot is expected to be empty at the moment
- assert(pPackages->Packages[nPackageIndex].szFileName == NULL);
- assert(pPackages->Packages[nPackageIndex].nLength == 0);
+ // HOTS: 1956C60
+ int SearchFile(TMndxSearch * pSearch)
+ {
+ int nError = ERROR_SUCCESS;
- // 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;
-}
+ if(pDatabase == NULL)
+ return ERROR_INVALID_PARAMETER;
-static int LoadPackageNames(PCASC_MNDX_INFO pMndxInfo, PCASC_MNDX_PACKAGES * ppPackages)
-{
- TMndxFindResult Struct1C;
- PCASC_MNDX_PACKAGES pPackages = NULL;
- PMAR_FILE pMarFile;
+ if(!pDatabase->FindFileInDatabase(pSearch))
+ nError = ERROR_FILE_NOT_FOUND;
- // Sanity checks
- assert(pMndxInfo != NULL);
+ return nError;
+ }
- // Prepare the file name search in the top level directory
- pMarFile = pMndxInfo->pMarFile1;
- Struct1C.SetSearchPath("", 0);
+ // HOTS: 1956CE0
+ int DoSearch(TMndxSearch * pSearch, bool * pbFindResult)
+ {
+ int nError = ERROR_SUCCESS;
- // Allocate initial name list structure
- pPackages = AllocatePackages(CASC_PACKAGES_INIT, 0x1000);
- if(pPackages == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
+ if(pDatabase == NULL)
+ return ERROR_INVALID_PARAMETER;
- // Keep searching as long as we find something
- for(;;)
- {
- bool bFindResult = false;
+ *pbFindResult = pDatabase->DoSearch(pSearch);
+ return nError;
+ }
- // Search the next file name
- pMarFile->pDatabasePtr->sub_1956CE0(&Struct1C, &bFindResult);
- if(bFindResult == false)
- break;
+ // HOTS: 1956D20
+ int GetFileNameCount(size_t * PtrFileNameCount)
+ {
+ if(pDatabase == NULL)
+ return ERROR_INVALID_PARAMETER;
- // 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;
+ PtrFileNameCount[0] = pDatabase->FileNameIndexes.ValidItemCount;
+ return ERROR_SUCCESS;
}
- // Give the packages to the caller
- if(ppPackages != NULL)
- ppPackages[0] = pPackages;
- return ERROR_SUCCESS;
-}
+// protected:
+ TFileNameDatabase * pDatabase;
+ LPBYTE pbMarData;
+ size_t cbMarData;
+};
//-----------------------------------------------------------------------------
// Implementation of root file functions
-struct TRootHandler_MNDX : public TRootHandler
+typedef struct _FILE_MNDX_INFO
{
- CASC_MNDX_INFO MndxInfo;
+ BYTE RootFileName[MD5_HASH_SIZE]; // Name (aka MD5) of the root file
+ DWORD HeaderVersion; // Must be <= 2
+ DWORD FormatVersion;
+ DWORD field_1C;
+ DWORD field_20;
+ DWORD MarInfoOffset; // Offset of the first MAR entry info
+ DWORD MarInfoCount; // Number of the MAR info entries
+ DWORD MarInfoSize; // Size of the MAR info entry
+ DWORD CKeyEntriesOffset; // Offset of the CKey entries, relative to begin of the root file
+ DWORD CKeyEntriesCount; // Number of CKeys (files) in the root file
+ DWORD FileNameCount; // Number of unique file names. More files with the same name in the different packages can exist
+ DWORD CKeyEntrySize; // Size of one CKey root entry
+ TMndxMarFile * MarFiles[MAR_COUNT]; // File name list for the packages
- PCASC_ROOT_ENTRY_MNDX * ppValidEntries;
- PCASC_ROOT_ENTRY_MNDX pMndxEntries;
- PCASC_MNDX_PACKAGES pPackages; // Linear list of present packages
-};
+} FILE_MNDX_INFO, *PFILE_MNDX_INFO;
-PCASC_MNDX_PACKAGE FindMndxPackage(TRootHandler_MNDX * pRootHandler, const char * szFileName)
+struct TMndxHandler
{
- 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;
- }
- }
- }
+ public:
- // Give the package pointer or NULL if not found
- return pMatching;
-}
+ //
+ // Constructor and destructor
+ //
-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;
+ TMndxHandler()
+ {
+ memset(this, 0, sizeof(TMndxHandler));
+ }
- // Search the database for the file name
- if(pMndxInfo->bRootFileLoaded)
+ ~TMndxHandler()
{
- Struct1C.SetSearchPath(szFileName, strlen(szFileName));
+ PMNDX_PACKAGE pPackage;
+ size_t i;
- // 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;
+ for(i = 0; i < MAR_COUNT; i++)
+ delete MndxInfo.MarFiles[i];
+ CASC_FREE(FileNameIndexToCKeyIndex);
+ pCKeyEntries = NULL;
- // The found MNDX index must fall into range of valid MNDX entries
- if(Struct1C.FileNameIndex < pMndxInfo->MndxEntriesValid)
+ for(i = 0; i < Packages.ItemCount(); i++)
{
- // 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;
+ pPackage = (PMNDX_PACKAGE)Packages.ItemAt(i);
+ CASC_FREE(pPackage->szFileName);
}
+ Packages.Free();
}
- 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;
+ //
+ // Helper functions
+ //
- // If the first time, allocate the structure for the search result
- if(pSearch->pRootContext == NULL)
+ static LPBYTE CaptureRootHeader(FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootPtr, LPBYTE pbRootEnd)
{
- // Create the new search structure
- pStruct1C = new TMndxFindResult;
- if(pStruct1C == NULL)
+ // Capture the root header
+ pbRootPtr = CaptureData(pbRootPtr, pbRootEnd, &MndxHeader, sizeof(FILE_MNDX_HEADER));
+ if (pbRootPtr == 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;
-
- // Keep searching
- for(;;)
- {
- // Search the next file name (our code)
- pMarFile->pDatabasePtr->sub_1956CE0(pStruct1C, &bFindResult);
- if (bFindResult == false)
+ // Check signature and version
+ if (MndxHeader.Signature != CASC_MNDX_ROOT_SIGNATURE || MndxHeader.FormatVersion > 2 || MndxHeader.FormatVersion < 1)
return NULL;
- // If we have no wild mask, we found it
- if (pSearch->szMask == NULL || pSearch->szMask[0] == 0)
- break;
-
- // Copy the found name to the buffer
- memcpy(pSearch->szFileName, pStruct1C->szFoundPath, pStruct1C->cchFoundPath);
- pSearch->szFileName[pStruct1C->cchFoundPath] = 0;
- if (CheckWildCard(pSearch->szFileName, pSearch->szMask))
- break;
+ // Passed
+ return pbRootPtr + sizeof(FILE_MNDX_HEADER);
}
- // 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++;
+ int LoadPackageNames()
+ {
+ TMndxMarFile * pMarFile = MndxInfo.MarFiles[MAR_PACKAGE_NAMES];
+ TMndxSearch Search;
+ PMNDX_PACKAGE pPackage;
+ size_t nPackageCount = 0x40;
+ bool bFindResult = false;
+ int nError;
- // Find the root entry
- nError = SearchMndxInfo(pRootHandler, szStrippedName, (DWORD)(pPackage - pRootHandler->pPackages->Packages), &pRootEntry);
- if(nError != ERROR_SUCCESS || pRootEntry == NULL)
- return NULL;
+ // Prepare the file name search in the top level directory
+ Search.SetSearchMask("", 0);
- // Return the encoding key
- return pRootEntry->EncodingKey;
-}
+ // Allocate initial name list structure
+ pMarFile->GetFileNameCount(&nPackageCount);
+ nError = Packages.Create<MNDX_PACKAGE>(nPackageCount);
+ if(nError != ERROR_SUCCESS)
+ return nError;
-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);
-}
+ // Reset the package array
+ Packages.Reset();
-//-----------------------------------------------------------------------------
-// Public functions - MNDX info
+ // Keep searching as long as we find something
+ while(pMarFile->DoSearch(&Search, &bFindResult) == ERROR_SUCCESS && bFindResult)
+ {
+ // Insert new package to the array
+ assert(Search.nIndex < nPackageCount);
+ pPackage = (PMNDX_PACKAGE)Packages.InsertAt(Search.nIndex);
+ if (pPackage != NULL)
+ {
+ // The package mut not be initialized yet
+ assert(pPackage->szFileName == NULL);
+
+ // Allocate space for the file name
+ pPackage->szFileName = CASC_ALLOC(char, Search.cchFoundPath + 1);
+ if (pPackage->szFileName == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Fill the package structure
+ memcpy(pPackage->szFileName, Search.szFoundPath, Search.cchFoundPath);
+ pPackage->szFileName[Search.cchFoundPath] = 0;
+ pPackage->nLength = Search.cchFoundPath;
+ pPackage->nIndex = Search.nIndex;
+ }
+ }
-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);
+ // Give the packages to the caller
+ return ERROR_SUCCESS;
}
- // 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++)
+ int Load(const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd)
{
- // 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;
+ TMndxMarFile * pMarFile;
+ FILE_MAR_INFO MarInfo;
+ size_t nFilePointer = 0;
+ DWORD i;
+ int nError = ERROR_SUCCESS;
- // Allocate MAR_FILE structure
- pMarFile = CASC_ALLOC(MAR_FILE, 1);
- if(pMarFile == NULL)
- {
- nError = ERROR_NOT_ENOUGH_MEMORY;
- break;
- }
+ // Copy the header into the MNDX info
+ MndxInfo.HeaderVersion = MndxHeader.HeaderVersion;
+ MndxInfo.FormatVersion = MndxHeader.FormatVersion;
+ nFilePointer += sizeof(FILE_MNDX_HEADER);
- // Allocate space for the MAR data
- pMarFile->pDatabasePtr = NULL;
- pMarFile->pbMarData = CASC_ALLOC(BYTE, MarInfo.MarDataSize);
- pMarFile->cbMarData = MarInfo.MarDataSize;
- if(pMarFile->pbMarData == NULL)
+ // Header version 2 has 2 extra fields that we need to load
+ if(MndxInfo.HeaderVersion == 2)
{
- nError = ERROR_NOT_ENOUGH_MEMORY;
- break;
+ if(!CaptureData(pbRootFile + nFilePointer, pbRootEnd, &MndxInfo.field_1C, sizeof(DWORD) + sizeof(DWORD)))
+ return ERROR_FILE_CORRUPT;
+ nFilePointer += sizeof(DWORD) + sizeof(DWORD);
}
- // Read the MAR data
- if(!RootFileRead(pbRootFile + MarInfo.MarDataOffset, pbRootFileEnd, pMarFile->pbMarData, pMarFile->cbMarData))
+ // Load the rest of the file header
+ if(!CaptureData(pbRootFile + nFilePointer, pbRootEnd, &MndxInfo.MarInfoOffset, 0x1C))
+ return ERROR_FILE_CORRUPT;
+
+ // Verify the structure
+ if(MndxInfo.MarInfoCount > MAR_COUNT || MndxInfo.MarInfoSize != sizeof(FILE_MAR_INFO))
+ return ERROR_FILE_CORRUPT;
+
+ // Load all MAR infos
+ for(i = 0; i < MndxInfo.MarInfoCount; i++)
{
- nError = ERROR_FILE_CORRUPT;
- break;
- }
+ // Capture the n-th MAR info
+ nFilePointer = MndxInfo.MarInfoOffset + (MndxInfo.MarInfoSize * i);
+ if(!CaptureData(pbRootFile + nFilePointer, pbRootEnd, &MarInfo, sizeof(FILE_MAR_INFO)))
+ return ERROR_FILE_CORRUPT;
- // HOTS: 00E94FF1
- MAR_FILE_CreateDatabase(pMarFile);
- if(i == 0)
- pMndxInfo->pMarFile1 = pMarFile;
- if(i == 1)
- pMndxInfo->pMarFile2 = pMarFile;
- if(i == 2)
- pMndxInfo->pMarFile3 = pMarFile;
- }
+ // Allocate MAR_FILE structure
+ pMarFile = new TMndxMarFile();
+ if(pMarFile == NULL)
+ {
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
- // 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;
- }
+ // Create the database from the MAR data
+ nError = pMarFile->LoadRootData(MarInfo, pbRootFile, pbRootEnd);
+ if(nError != ERROR_SUCCESS)
+ break;
- // Load the complete array of MNDX entries
- if(nError == ERROR_SUCCESS)
- {
- TFileNameDatabasePtr * pDbPtr = pMndxInfo->pMarFile2->pDatabasePtr;
- DWORD FileNameCount;
+ // Assign the MAR file to the MNDX info structure
+ MndxInfo.MarFiles[i] = pMarFile;
+ }
- nError = pDbPtr->GetFileNameCount(&FileNameCount);
- if(nError == ERROR_SUCCESS && FileNameCount == pMndxInfo->MndxEntriesValid)
+ // All three MAR files must be loaded
+ // HOTS: 00E9503B
+ if(nError == ERROR_SUCCESS)
{
- 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;
+ if(MndxInfo.MarFiles[MAR_PACKAGE_NAMES] == NULL || MndxInfo.MarFiles[MAR_STRIPPED_NAMES] == NULL || MndxInfo.MarFiles[MAR_FULL_NAMES] == NULL)
+ nError = ERROR_BAD_FORMAT;
+ if(MndxInfo.CKeyEntrySize != sizeof(MNDX_CKEY_ENTRY))
+ nError = ERROR_BAD_FORMAT;
}
- 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)
+ // Load the array of Ckey entries. All present files are in the array,
+ // the same names (differentiated by package ID) are groupped together
+ if(nError == ERROR_SUCCESS)
{
- PCASC_ROOT_ENTRY_MNDX pRootEntry = pRootHandler->pMndxEntries;
- DWORD ValidEntryCount = 1; // edx
- DWORD nIndex1 = 0;
+ size_t CKeyEntriesSize;
+ size_t FileNameCount = 0;
- // The first entry is always valid
- pRootHandler->ppValidEntries[nIndex1++] = pRootHandler->pMndxEntries;
+ pMarFile = MndxInfo.MarFiles[MAR_STRIPPED_NAMES];
+ nError = ERROR_FILE_CORRUPT;
- // Put the remaining entries
- for(i = 0; i < pMndxInfo->MndxEntriesTotal; i++, pRootEntry++)
+ // Capture the array of CKey entries
+ if(pMarFile->GetFileNameCount(&FileNameCount) == ERROR_SUCCESS && FileNameCount == MndxInfo.FileNameCount)
{
- if(ValidEntryCount > pMndxInfo->MndxEntriesValid)
- break;
-
- if(pRootEntry->Flags & 0x80000000)
+ CKeyEntriesSize = MndxInfo.CKeyEntriesCount * MndxInfo.CKeyEntrySize;
+ if ((pbRootFile + MndxInfo.CKeyEntriesOffset + CKeyEntriesSize) <= pbRootEnd)
{
- pRootHandler->ppValidEntries[nIndex1++] = pRootEntry + 1;
- ValidEntryCount++;
+ pCKeyEntries = (PMNDX_CKEY_ENTRY)(pbRootFile + MndxInfo.CKeyEntriesOffset);
+ nError = ERROR_SUCCESS;
}
}
-
- // 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
+ // Pick the CKey entries that are the first with a given name
+ if(nError == ERROR_SUCCESS)
+ {
+ assert(MndxInfo.FileNameCount <= MndxInfo.CKeyEntriesCount);
+ FileNameIndexToCKeyIndex = CASC_ALLOC(PMNDX_CKEY_ENTRY, MndxInfo.FileNameCount + 1);
+ if(FileNameIndexToCKeyIndex != NULL)
+ {
+ PMNDX_CKEY_ENTRY pRootEntry = pCKeyEntries;
+ DWORD nFileNameIndex = 0;
- // Return the result
- return nError;
-}
+ // The first entry is always beginning of a file name group
+ FileNameIndexToCKeyIndex[nFileNameIndex++] = pRootEntry;
-//----------------------------------------------------------------------------
-// Unit tests
+ // Get the remaining file name groups
+ for(i = 0; i < MndxInfo.CKeyEntriesCount; i++, pRootEntry++)
+ {
+ if (nFileNameIndex > MndxInfo.FileNameCount)
+ break;
-#if defined(_DEBUG) && defined(_X86_) && defined(CASCLIB_TEST)
-/*
-extern "C" {
- bool _cdecl sub_1958B00_x86(TFileNameDatabase * pDB, TMndxFindResult * pStruct1C);
- 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);
-}
+ if (pRootEntry->Flags & MNDX_LAST_CKEY_ENTRY)
+ {
+ FileNameIndexToCKeyIndex[nFileNameIndex++] = pRootEntry + 1;
+ }
+ }
-extern "C" void * allocate_zeroed_memory_x86(size_t bytes)
-{
- void * ptr = CASC_ALLOC(BYTE, bytes);
+ // Verify the final number of file names
+ if ((nFileNameIndex - 1) != MndxInfo.FileNameCount)
+ nError = ERROR_BAD_FORMAT;
+ }
+ else
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ }
- if(ptr != NULL)
- memset(ptr, 0, bytes);
- return ptr;
-}
+ // Load the package names from the 0-th MAR file
+ if(nError == ERROR_SUCCESS)
+ {
+ nError = LoadPackageNames();
+ }
-extern "C" void free_memory_x86(void * ptr)
-{
- if(ptr != NULL)
- {
- CASC_FREE(ptr);
+ return nError;
}
-}
-
-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)
+ int LoadFileNames(TCascStorage * hs, CASC_FILE_TREE & FileTree)
{
- nError = pStruct1C->CreateStruct40();
- if(nError != ERROR_SUCCESS)
- return nError;
- }
-
- *pbFindResult = sub_1959460_x86(pDatabasePtr->pDB, pStruct1C);
- return nError;
-}
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ PMNDX_CKEY_ENTRY pRootEntry;
+ PMNDX_CKEY_ENTRY pRootEnd = pCKeyEntries + MndxInfo.CKeyEntriesCount;
+ PMNDX_PACKAGE pPackage;
+ TMndxMarFile * pMarFile = MndxInfo.MarFiles[MAR_STRIPPED_NAMES];
+ TMndxSearch Search;
+ char szFileName[MAX_PATH];
+ bool bFindResult = false;
+ int nError;
-static void TestFileSearch_SubStrings(PMAR_FILE pMarFile, char * szFileName, size_t nLength)
-{
- TMndxFindResult Struct1C_1;
- TMndxFindResult Struct1C_2;
+ // Setup the search mask
+ Search.SetSearchMask("", 0);
-// if(strcmp(szFileName, "mods/heroes.stormmod/base.stormassets/assets/textures/storm_temp_war3_btnstatup.dds"))
-// return;
+ // Keep searching ad long as we found something
+ while ((nError = pMarFile->DoSearch(&Search, &bFindResult)) == ERROR_SUCCESS && bFindResult)
+ {
+ // Sanity check
+ assert(Search.cchFoundPath < MAX_PATH);
- // 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;
+ // The found file name index must fall into range of file names
+ if (Search.nIndex < MndxInfo.FileNameCount)
+ {
+ // Retrieve the first-in-group CKey entry of that name
+ pRootEntry = FileNameIndexToCKeyIndex[Search.nIndex];
- // Keep searching
- for(;;)
- {
- bool bFindResult1 = false;
- bool bFindResult2 = false;
+ // Now take all files of that name, prepend their package name and insert to file tree
+ while(pRootEntry < pRootEnd)
+ {
+ // Find the appropriate CKey entry in the central storage
+ pCKeyEntry = FindCKeyEntry_CKey(hs, pRootEntry->CKey);
+ if (pCKeyEntry != NULL)
+ {
+ size_t nPackageIndex = pRootEntry->Flags & 0x00FFFFFF;
- // Search the next file name (orig HOTS code)
- sub_1956CE0_x86(pMarFile->pDatabasePtr, &Struct1C_1, &bFindResult1);
+ // Retrieve the package for this entry
+ pPackage = (PMNDX_PACKAGE)Packages.ItemAt(nPackageIndex);
+ if (pPackage != NULL)
+ {
+ // Sanity check
+ assert(pPackage->nIndex == nPackageIndex);
- // Search the next file name (our code)
- pMarFile->pDatabasePtr->sub_1956CE0(&Struct1C_2, &bFindResult2);
+ // Merge the package name and file name
+ MakeFileName(szFileName, _countof(szFileName), pPackage, &Search);
- // 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);
+ // Insert the entry to the file tree
+ FileTree.InsertByName(pCKeyEntry, szFileName);
+ }
+ }
- // Stop the search in case of failure
- if(bFindResult1 == false || bFindResult2 == false)
- break;
+ // Is this the last-in-group entry?
+ if (pRootEntry->Flags & MNDX_LAST_CKEY_ENTRY)
+ break;
+ pRootEntry++;
+ }
+ }
}
- // Free the search structures
- Struct1C_1.FreeStruct40();
- Struct1C_2.FreeStruct40();
- nLength--;
+ return nError;
}
-}
-
-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);
+ //
+ // Helper functions
+ //
- // Keep searching
- for(;;)
+ void MakeFileName(char * szBuffer, size_t cchBuffer, PMNDX_PACKAGE pPackage, TMndxSearch * pSearch)
{
- bool bFindResult1 = false;
- bool bFindResult2 = false;
-
- // Search the next file name (orig HOTS code)
- sub_1956CE0_x86(pMarFile->pDatabasePtr, &Struct1C_1, &bFindResult1);
+ char * szBufferEnd = szBuffer + cchBuffer - 1;
- // 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);
- }
+ // Buffer length check
+ assert((pPackage->nLength + 1 + pSearch->cchFoundPath + 1) < cchBuffer);
- // 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;
+ // Copy the package name
+ if ((szBuffer + pPackage->nLength) < szBufferEnd)
+ {
+ memcpy(szBuffer, pPackage->szFileName, pPackage->nLength);
+ szBuffer += pPackage->nLength;
+ }
- // Perform the search using original HOTS code
- {
- TMndxFindResult Struct1C;
+ // Append slash
+ if ((szBuffer + 1) < szBufferEnd)
+ *szBuffer++ = '/';
- Struct1C.CreateStruct40();
- Struct1C.SetSearchPath(szFileName, nLength);
+ // Append file name
+ if ((szBuffer + pSearch->cchFoundPath) < szBufferEnd)
+ {
+ memcpy(szBuffer, pSearch->szFoundPath, pSearch->cchFoundPath);
+ szBuffer += pSearch->cchFoundPath;
+ }
- // Call the original HOTS function
- sub_1957EF0_x86(pDB, &Struct1C);
- dwFileNameIndex1 = Struct1C.FileNameIndex;
+ szBuffer[0] = 0;
}
- // Perform the search using our code
- {
- TMndxFindResult Struct1C;
+ protected:
- Struct1C.CreateStruct40();
- Struct1C.SetSearchPath(szFileName, nLength);
+ FILE_MNDX_INFO MndxInfo;
- // Call our function
- pDB->FindFileInDatabase(&Struct1C);
- dwFileNameIndex2 = Struct1C.FileNameIndex;
- }
+ PMNDX_CKEY_ENTRY * FileNameIndexToCKeyIndex;
+ PMNDX_CKEY_ENTRY pCKeyEntries;
+ CASC_ARRAY Packages; // Linear list of present packages
+};
- // Compare both
- assert(dwFileNameIndex1 == dwFileNameIndex2);
-}
+//-----------------------------------------------------------------------------
+// Handler definition for MNDX root file
-static void TestMndxFunctions(PMAR_FILE pMarFile)
+struct TRootHandler_MNDX : public TFileTreeRoot
{
- TFileNameDatabase * pDB = pMarFile->pDatabasePtr->pDB;
+ public:
- // Exercise function sub_19573D0
- for(DWORD arg_0 = 0; arg_0 < 0x100; arg_0++)
+ TRootHandler_MNDX() : TFileTreeRoot(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);
- }
+ // MNDX supports file names and CKeys
+ dwFeatures |= CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY;
}
- // Exercise function GetItemValue
- for(DWORD i = 0; i < 0x10000; i++)
+ int Load(TCascStorage * hs, const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd)
{
- DWORD dwResult1 = GetItemValue_x86(&pDB->Struct68_D0, i);
- DWORD dwResult2 = pDB->Struct68_D0.GetItemValue(i);
+ TMndxHandler Handler;
+ int nError;
- 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);
+ // Load and parse the entire MNDX structure
+ nError = Handler.Load(MndxHeader, pbRootFile, pbRootEnd);
+ if (nError == ERROR_SUCCESS)
+ {
+ // Search all file names and insert them into the file tree
+ nError = Handler.LoadFileNames(hs, FileTree);
+ }
- assert(dwResult1 == dwResult2);
+ return nError;
}
+};
- // 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);
- }
-}
+//-----------------------------------------------------------------------------
+// Public functions - MNDX info
-void TestMndxRootFile(PCASC_MNDX_INFO pMndxInfo)
+int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
{
- 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);
+ TRootHandler_MNDX * pRootHandler = NULL;
+ FILE_MNDX_HEADER MndxHeader;
+ LPBYTE pbRootEnd = pbRootFile + cbRootFile;
+ int nError = ERROR_BAD_FORMAT;
- // 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)
+ // Verify the header of the ROOT file
+ if(TMndxHandler::CaptureRootHeader(MndxHeader, pbRootFile, pbRootEnd) != NULL)
{
- // Check every file in the database
- while((nLength = ListFile_GetNext(pvListFile, "*", szFileName, MAX_PATH)) != 0)
+ // Allocate the root handler object
+ pRootHandler = new TRootHandler_MNDX();
+ if(pRootHandler != NULL)
{
- // 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);
+ // Load the root directory. If load failed, we free the object
+ nError = pRootHandler->Load(hs, MndxHeader, pbRootFile, pbRootEnd);
+ if(nError != ERROR_SUCCESS)
+ {
+ delete pRootHandler;
+ pRootHandler = NULL;
+ }
}
-
- ListFile_Free(pvListFile);
}
+
+ // Assign the root directory (or NULL) and return error
+ hs->pRootHandler = pRootHandler;
+ return nError;
}
-*/
-#endif // defined(_DEBUG) && defined(_X86_) && defined(CASCLIB_TEST)