aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dep/CascLib/CMakeLists.txt33
-rw-r--r--dep/CascLib/src/CascCommon.cpp168
-rw-r--r--dep/CascLib/src/CascCommon.h554
-rw-r--r--dep/CascLib/src/CascDecompress.cpp22
-rw-r--r--dep/CascLib/src/CascDecrypt.cpp387
-rw-r--r--dep/CascLib/src/CascDumpData.cpp476
-rw-r--r--dep/CascLib/src/CascFiles.cpp1547
-rw-r--r--dep/CascLib/src/CascFindFile.cpp283
-rw-r--r--dep/CascLib/src/CascIndexFiles.cpp774
-rw-r--r--dep/CascLib/src/CascLib.def5
-rw-r--r--dep/CascLib/src/CascLib.h240
-rw-r--r--dep/CascLib/src/CascMndx.h359
-rw-r--r--dep/CascLib/src/CascOpenFile.cpp346
-rw-r--r--dep/CascLib/src/CascOpenStorage.cpp1825
-rw-r--r--dep/CascLib/src/CascPort.h82
-rw-r--r--dep/CascLib/src/CascReadFile.cpp913
-rw-r--r--dep/CascLib/src/CascRootFile_Diablo3.cpp1552
-rw-r--r--dep/CascLib/src/CascRootFile_Install.cpp121
-rw-r--r--dep/CascLib/src/CascRootFile_Mndx.cpp4975
-rw-r--r--dep/CascLib/src/CascRootFile_OW.cpp605
-rw-r--r--dep/CascLib/src/CascRootFile_Ovr.cpp213
-rw-r--r--dep/CascLib/src/CascRootFile_SC1.cpp214
-rw-r--r--dep/CascLib/src/CascRootFile_TVFS.cpp634
-rw-r--r--dep/CascLib/src/CascRootFile_Text.cpp121
-rw-r--r--dep/CascLib/src/CascRootFile_WoW.cpp481
-rw-r--r--dep/CascLib/src/CascRootFile_WoW6.cpp595
-rw-r--r--dep/CascLib/src/CascStructs.h255
-rw-r--r--dep/CascLib/src/DllMain.c24
-rw-r--r--dep/CascLib/src/common/Array.h208
-rw-r--r--dep/CascLib/src/common/Common.cpp709
-rw-r--r--dep/CascLib/src/common/Common.h334
-rw-r--r--dep/CascLib/src/common/Csv.cpp370
-rw-r--r--dep/CascLib/src/common/Csv.h105
-rw-r--r--dep/CascLib/src/common/Directory.cpp14
-rw-r--r--dep/CascLib/src/common/Directory.h2
-rw-r--r--dep/CascLib/src/common/DumpContext.cpp153
-rw-r--r--dep/CascLib/src/common/DumpContext.h38
-rw-r--r--dep/CascLib/src/common/DynamicArray.cpp101
-rw-r--r--dep/CascLib/src/common/DynamicArray.h37
-rw-r--r--dep/CascLib/src/common/FileStream.cpp95
-rw-r--r--dep/CascLib/src/common/FileStream.h8
-rw-r--r--dep/CascLib/src/common/FileTree.cpp684
-rw-r--r--dep/CascLib/src/common/FileTree.h116
-rw-r--r--dep/CascLib/src/common/ListFile.cpp293
-rw-r--r--dep/CascLib/src/common/ListFile.h33
-rw-r--r--dep/CascLib/src/common/Map.cpp287
-rw-r--r--dep/CascLib/src/common/Map.h360
-rw-r--r--dep/CascLib/src/common/RootHandler.cpp115
-rw-r--r--dep/CascLib/src/common/RootHandler.h149
-rw-r--r--dep/CascLib/src/libtomcrypt/src/hashes/hash_memory.c69
-rw-r--r--dep/CascLib/src/libtomcrypt/src/hashes/md5.c368
-rw-r--r--dep/CascLib/src/libtomcrypt/src/headers/tomcrypt.h87
-rw-r--r--dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_argchk.h38
-rw-r--r--dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cfg.h136
-rw-r--r--dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cipher.h891
-rw-r--r--dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_custom.h424
-rw-r--r--dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_hash.h378
-rw-r--r--dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_mac.h384
-rw-r--r--dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_macros.h424
-rw-r--r--dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_math.h500
-rw-r--r--dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_misc.h23
-rw-r--r--dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pk.h558
-rw-r--r--dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pkcs.h89
-rw-r--r--dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_prng.h199
-rw-r--r--dep/CascLib/src/libtomcrypt/src/misc/crypt_argchk.c30
-rw-r--r--dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_descriptor.c27
-rw-r--r--dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_is_valid.c36
-rw-r--r--dep/CascLib/src/libtomcrypt/src/misc/crypt_libc.c43
-rw-r--r--dep/CascLib/src/md5/md5.cpp291
-rw-r--r--dep/CascLib/src/md5/md5.h45
-rw-r--r--dep/PackageList.txt2
-rw-r--r--src/tools/extractor_common/CascHandles.cpp28
72 files changed, 12996 insertions, 14089 deletions
diff --git a/dep/CascLib/CMakeLists.txt b/dep/CascLib/CMakeLists.txt
index 0efe93771f1..a78d0508f0c 100644
--- a/dep/CascLib/CMakeLists.txt
+++ b/dep/CascLib/CMakeLists.txt
@@ -1,10 +1,12 @@
set(HEADER_FILES
src/CascCommon.h
src/CascLib.h
- src/CascMndx.h
src/CascPort.h
+ src/common/Array.h
src/common/Common.h
+ src/common/Csv.h
src/common/FileStream.h
+ src/common/FileTree.h
src/common/ListFile.h
src/common/Map.h
src/jenkins/lookup.h
@@ -13,11 +15,10 @@ set(HEADER_FILES
set(SRC_FILES
src/common/Common.cpp
src/common/Directory.cpp
- src/common/DumpContext.cpp
- src/common/DynamicArray.cpp
+ src/common/Csv.cpp
src/common/FileStream.cpp
+ src/common/FileTree.cpp
src/common/ListFile.cpp
- src/common/Map.cpp
src/common/RootHandler.cpp
src/jenkins/lookup3.c
src/CascCommon.cpp
@@ -26,26 +27,24 @@ set(SRC_FILES
src/CascDumpData.cpp
src/CascFiles.cpp
src/CascFindFile.cpp
+ src/CascIndexFiles.cpp
src/CascOpenFile.cpp
src/CascOpenStorage.cpp
src/CascReadFile.cpp
src/CascRootFile_Diablo3.cpp
- src/CascRootFile_Mndx.cpp
- src/CascRootFile_Ovr.cpp
- src/CascRootFile_SC1.cpp
- src/CascRootFile_WoW6.cpp
+ src/CascRootFile_Install.cpp
+ src/CascRootFile_MNDX.cpp
+ src/CascRootFile_Text.cpp
+ src/CascRootFile_TVFS.cpp
+ src/CascRootFile_OW.cpp
+ src/CascRootFile_WoW.cpp
)
-set(TOMCRYPT_FILES
- src/libtomcrypt/src/hashes/hash_memory.c
- src/libtomcrypt/src/hashes/md5.c
- src/libtomcrypt/src/misc/crypt_argchk.c
- src/libtomcrypt/src/misc/crypt_hash_descriptor.c
- src/libtomcrypt/src/misc/crypt_hash_is_valid.c
- src/libtomcrypt/src/misc/crypt_libc.c
+set(MD5_FILES
+ src/md5/md5.cpp
)
-add_library(casc STATIC ${SRC_FILES} ${TOMCRYPT_FILES})
+add_library(casc STATIC ${SRC_FILES} ${HEADER_FILES} ${MD5_FILES})
target_include_directories(casc
PUBLIC
@@ -53,6 +52,8 @@ target_include_directories(casc
PRIVATE
${CMAKE_SOURCE_DIR}/dep)
+target_compile_definitions(casc PUBLIC -D__SYS_ZLIB)
+
target_link_libraries(casc
PRIVATE
trinity-dependency-interface
diff --git a/dep/CascLib/src/CascCommon.cpp b/dep/CascLib/src/CascCommon.cpp
index 34c3df66b5c..f5e346ec14c 100644
--- a/dep/CascLib/src/CascCommon.cpp
+++ b/dep/CascLib/src/CascCommon.cpp
@@ -13,78 +13,132 @@
#include "CascCommon.h"
//-----------------------------------------------------------------------------
-// Conversion of big-endian to integer
+// Functions
-// Read the 24-bit big-endian offset into ULONGLONG
-DWORD ConvertBytesToInteger_3(LPBYTE ValueAsBytes)
+LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD * pcbFileData)
{
- DWORD Value = 0;
-
- Value = (Value << 0x08) | ValueAsBytes[0];
- Value = (Value << 0x08) | ValueAsBytes[1];
- Value = (Value << 0x08) | ValueAsBytes[2];
-
- return Value;
-}
-
-// Read the 32-bit big-endian offset into ULONGLONG
-DWORD ConvertBytesToInteger_4(LPBYTE ValueAsBytes)
-{
- DWORD Value = 0;
-
- Value = (Value << 0x08) | ValueAsBytes[0];
- Value = (Value << 0x08) | ValueAsBytes[1];
- Value = (Value << 0x08) | ValueAsBytes[2];
- Value = (Value << 0x08) | ValueAsBytes[3];
-
- return Value;
-}
+ LPBYTE pbFileData = NULL;
+ HANDLE hFile = NULL;
+ DWORD cbFileData = pcbFileData[0];
+ DWORD dwBytesRead = 0;
+ int nError = ERROR_SUCCESS;
+
+ // Open the file either by CKey or by EKey
+ if(OpenFileByCKeyEntry(hs, pCKeyEntry, CASC_STRICT_DATA_CHECK, &hFile))
+ {
+ // Retrieve the size of the file. Note that the caller might specify
+ // the real size of the file, in case the file size is not retrievable
+ // or if the size is wrong. Example: ENCODING file has size specified in BUILD
+ if(cbFileData == 0 || cbFileData == CASC_INVALID_SIZE)
+ {
+ cbFileData = CascGetFileSize(hFile, NULL);
+ if(cbFileData == 0 || cbFileData == CASC_INVALID_SIZE)
+ nError = ERROR_FILE_CORRUPT;
+ }
+
+ // Retrieve the size of the ENCODING file
+ if(nError == ERROR_SUCCESS)
+ {
+ // Allocate space for the ENCODING file
+ pbFileData = CASC_ALLOC(BYTE, cbFileData);
+ if(pbFileData != NULL)
+ {
+ // Read the entire file to memory
+ CascReadFile(hFile, pbFileData, cbFileData, &dwBytesRead);
+ if(dwBytesRead != cbFileData)
+ {
+ nError = ERROR_FILE_CORRUPT;
+ }
+ }
+ else
+ {
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ // Close the file
+ CascCloseFile(hFile);
+ }
+ else
+ {
+ nError = GetLastError();
+ }
-DWORD ConvertBytesToInteger_4_LE(LPBYTE ValueAsBytes)
-{
- DWORD Value = 0;
+ // Handle errors
+ if(nError != ERROR_SUCCESS)
+ {
+ // Free the file data
+ CASC_FREE(pbFileData);
+ cbFileData = 0;
- Value = (Value << 0x08) | ValueAsBytes[3];
- Value = (Value << 0x08) | ValueAsBytes[2];
- Value = (Value << 0x08) | ValueAsBytes[1];
- Value = (Value << 0x08) | ValueAsBytes[0];
+ // Set the last error
+ SetLastError(nError);
+ }
- return Value;
+ // Give the loaded file length
+ if(pcbFileData != NULL)
+ *pcbFileData = cbFileData;
+ return pbFileData;
}
-// Read the 40-bit big-endian offset into ULONGLONG
-ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes)
+LPBYTE LoadFileToMemory(const TCHAR * szFileName, DWORD * pcbFileData)
{
- ULONGLONG Value = 0;
-
- Value = (Value << 0x08) | ValueAsBytes[0];
- Value = (Value << 0x08) | ValueAsBytes[1];
- Value = (Value << 0x08) | ValueAsBytes[2];
- Value = (Value << 0x08) | ValueAsBytes[3];
- Value = (Value << 0x08) | ValueAsBytes[4];
-
- return Value;
-}
+ TFileStream * pStream;
+ ULONGLONG FileSize = 0;
+ LPBYTE pbFileData = NULL;
+ DWORD cbFileData = 0;
+
+ // Open the stream for read-only access and read the file
+ // Note that this fails when the game is running (sharing violation).
+ pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
+ if(pStream != NULL)
+ {
+ // Retrieve the file size
+ FileStream_GetSize(pStream, &FileSize);
+ cbFileData = (DWORD)FileSize;
+
+ // Do not load zero files or too larget files
+ if(0 < FileSize && FileSize <= 0x2000000)
+ {
+ // Allocate file data buffer. Make it 1 byte longer
+ // so string functions can put '\0' there
+ pbFileData = CASC_ALLOC(BYTE, cbFileData + 1);
+ if(pbFileData != NULL)
+ {
+ if(!FileStream_Read(pStream, NULL, pbFileData, cbFileData))
+ {
+ CASC_FREE(pbFileData);
+ cbFileData = 0;
+ }
+ }
+ else
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ cbFileData = 0;
+ }
+ }
+ else
+ {
+ SetLastError(ERROR_BAD_FORMAT);
+ cbFileData = 0;
+ assert(false);
+ }
+
+ // Close the file stream
+ FileStream_Close(pStream);
+ }
-void ConvertIntegerToBytes_4(DWORD Value, LPBYTE ValueAsBytes)
-{
- ValueAsBytes[0] = (Value >> 0x18) & 0xFF;
- ValueAsBytes[1] = (Value >> 0x10) & 0xFF;
- ValueAsBytes[2] = (Value >> 0x08) & 0xFF;
- ValueAsBytes[3] = (Value >> 0x00) & 0xFF;
+ // Give out values
+ if(pcbFileData != NULL)
+ pcbFileData[0] = cbFileData;
+ return pbFileData;
}
-//-----------------------------------------------------------------------------
-// Common fre routine of a CASC blob
-
void FreeCascBlob(PQUERY_KEY pBlob)
{
if(pBlob != NULL)
{
- if(pBlob->pbData != NULL)
- CASC_FREE(pBlob->pbData);
-
- pBlob->pbData = NULL;
+ CASC_FREE(pBlob->pbData);
pBlob->cbData = 0;
}
}
diff --git a/dep/CascLib/src/CascCommon.h b/dep/CascLib/src/CascCommon.h
index e38094171b8..3ce527061c2 100644
--- a/dep/CascLib/src/CascCommon.h
+++ b/dep/CascLib/src/CascCommon.h
@@ -23,341 +23,441 @@
#include "CascPort.h"
#include "common/Common.h"
-#include "common/DynamicArray.h"
+#include "common/Array.h"
#include "common/Map.h"
+#include "common/FileTree.h"
#include "common/FileStream.h"
#include "common/Directory.h"
#include "common/ListFile.h"
-#include "common/DumpContext.h"
+#include "common/Csv.h"
#include "common/RootHandler.h"
-// Headers from LibTomCrypt
-#include "libtomcrypt/src/headers/tomcrypt.h"
+// Headers from Alexander Peslyak's MD5 implementation
+#include "md5/md5.h"
// For HashStringJenkins
#include "jenkins/lookup.h"
+// On-disk CASC structures
+#include "CascStructs.h"
+
//-----------------------------------------------------------------------------
// CascLib private defines
-#define CASC_GAME_HOTS 0x00010000 // Heroes of the Storm
-#define CASC_GAME_WOW6 0x00020000 // World of Warcraft - Warlords of Draenor
-#define CASC_GAME_DIABLO3 0x00030000 // Diablo 3 since PTR 2.2.0
-#define CASC_GAME_OVERWATCH 0x00040000 // Overwatch since PTR 24919
-#define CASC_GAME_STARCRAFT2 0x00050000 // Starcraft II - Legacy of the Void, since build 38996
-#define CASC_GAME_STARCRAFT1 0x00060000 // Starcraft 1 (remastered)
-#define CASC_GAME_MASK 0xFFFF0000 // Mask for getting game ID
-
-#define CASC_INDEX_COUNT 0x10
-#define CASC_FILE_KEY_SIZE 0x09 // Size of the file key
-#define CASC_MAX_DATA_FILES 0x100
-#define CASC_EXTRA_FILES 0x20 // Number of extra entries to be reserved for additionally inserted files
-
-#define CASC_SEARCH_HAVE_NAME 0x0001 // Indicated that previous search found a name
-
-#define BLTE_HEADER_SIGNATURE 0x45544C42 // 'BLTE' header in the data files
-#define BLTE_HEADER_DELTA 0x1E // Distance of BLTE header from begin of the header area
-#define MAX_HEADER_AREA_SIZE 0x2A // Length of the file header area
-
-// File header area in the data.nnn:
-// BYTE HeaderHash[MD5_HASH_SIZE]; // MD5 of the frame array
-// DWORD dwFileSize; // Size of the file (see comment before CascGetFileSize for details)
-// BYTE SomeSize[4]; // Some size (big endian)
-// BYTE Padding[6]; // Padding (?)
-// DWORD dwSignature; // Must be "BLTE"
-// BYTE HeaderSizeAsBytes[4]; // Header size in bytes (big endian)
-// BYTE MustBe0F; // Must be 0x0F. Optional, only if HeaderSizeAsBytes != 0
-// BYTE FrameCount[3]; // Frame count (big endian). Optional, only if HeaderSizeAsBytes != 0
-
-// Prevent problems with CRT "min" and "max" functions,
-// as they are not defined on all platforms
-#define CASCLIB_MIN(a, b) ((a < b) ? a : b)
-#define CASCLIB_MAX(a, b) ((a > b) ? a : b)
-#define CASCLIB_UNUSED(p) ((void)(p))
-
-#define CASC_PACKAGE_BUFFER 0x1000
-
-#ifndef _maxchars
-#define _maxchars(buff) ((sizeof(buff) / sizeof(buff[0])) - 1)
+#ifdef _DEBUG
+#define BREAK_ON_XKEY3(CKey, v0, v1, v2) if(CKey[0] == v0 && CKey[1] == v1 && CKey[2] == v2) { __debugbreak(); }
+#define BREAKIF(condition) if(condition) { __debugbreak(); }
+#else
+#define BREAK_ON_XKEY3(CKey, v0, v1, v2) { /* NOTHING */ }
+#define BREAKIF(condition) { /* NOTHING */ }
#endif
//-----------------------------------------------------------------------------
// In-memory structures
-// See http://pxr.dk/wowdev/wiki/index.php?title=CASC for more information
-
-struct TFileStream;
typedef enum _CBLD_TYPE
{
CascBuildNone = 0, // No build type found
CascBuildInfo, // .build.info
CascBuildDb, // .build.db (older storages)
+ CascVersionsDb // versions (downloaded online)
} CBLD_TYPE, *PCBLD_TYPE;
-typedef struct _ENCODING_KEY
+// Tag file entry, loaded from the DOWNLOAD file
+typedef struct _CASC_TAG_ENTRY
{
- BYTE Value[MD5_HASH_SIZE]; // MD5 of the file
+ USHORT HashType; // Hash type
+ char TagName[1]; // Tag name. Variable length.
+} CASC_TAG_ENTRY, *PCASC_TAG_ENTRY;
-} ENCODING_KEY, *PENCODING_KEY;
-
-typedef struct _CASC_INDEX_ENTRY
+// Normalized header of the index files.
+// Both version 1 and version 2 are converted to this structure
+typedef struct _CASC_INDEX_HEADER
{
- BYTE IndexKey[CASC_FILE_KEY_SIZE]; // The first 9 bytes of the encoding key
- BYTE FileOffsetBE[5]; // Index of data file and offset within (big endian).
- BYTE FileSizeLE[4]; // Size occupied in the storage file (data.###). See comment before CascGetFileSize for details
-} CASC_INDEX_ENTRY, *PCASC_INDEX_ENTRY;
-
-typedef struct _CASC_MAPPING_TABLE
+ USHORT IndexVersion; // 5 for index v 1.0, 7 for index version 2.0
+ BYTE BucketIndex; // Should be the same as the first byte of the hex filename.
+ BYTE StorageOffsetLength; // Length, in bytes, of the StorageOffset field in the EKey entry
+ BYTE EncodedSizeLength; // Length, in bytes, of the EncodedSize in the EKey entry
+ BYTE EKeyLength; // Length, in bytes, of the (trimmed) EKey in the EKey entry
+ BYTE FileOffsetBits; // Number of bits of the archive file offset in StorageOffset field. Rest is data segment index
+ BYTE Alignment;
+ ULONGLONG SegmentSize; // Size of one data segment (aka data.### file)
+ size_t HeaderLength; // Length of the on-disk header structure, in bytes
+ size_t HeaderPadding; // Length of padding after the header
+ size_t EntryLength; // Length of the on-disk EKey entry structure, in bytes
+ size_t EKeyCount; // Number of EKey entries. Only supplied for index files version 1.
+
+} CASC_INDEX_HEADER, *PCASC_INDEX_HEADER;
+
+// Normalized footer of the archive index files (md5.index)
+typedef struct _CASC_ARCINDEX_FOOTER
{
- TCHAR * szFileName; // Name of the key mapping file
- LPBYTE pbFileData; // Pointer to the file data
- DWORD cbFileData; // Length of the file data
- BYTE ExtraBytes; // (?) Extra bytes in the key record
- BYTE SpanSizeBytes; // Size of field with file size
- BYTE SpanOffsBytes; // Size of field with file offset
- BYTE KeyBytes; // Size of the file key
- BYTE SegmentBits; // Number of bits for the file offset (rest is archive index)
- ULONGLONG MaxFileOffset;
-
- PCASC_INDEX_ENTRY pIndexEntries; // Sorted array of index entries
- DWORD nIndexEntries; // Number of index entries
-
-} CASC_MAPPING_TABLE, *PCASC_MAPPING_TABLE;
-
-typedef struct _CASC_FILE_FRAME
+ BYTE TocHash[MD5_HASH_SIZE]; // Hash of the structure
+ BYTE FooterHash[MD5_HASH_SIZE]; // MD5 hash of the footer, possibly shortened
+ BYTE Version; // Version of the index footer
+ BYTE OffsetBytes; // Length, in bytes, of the file offset field
+ BYTE SizeBytes; // Length, in bytes, of the file size field
+ BYTE EKeyBytes; // Length, in bytes, of the EKey field
+ BYTE FooterHashBytes; // Length, in bytes, of the hash and checksum
+ BYTE Alignment[3];
+ size_t ElementCount; // total number of elements in the index file
+ size_t PageLength; // Length, in bytes, of the index page
+ size_t ItemLength; // Length, in bytes, of the single item
+ size_t FooterLength; // Length, in bytes, of the index footer structure
+
+} CASC_ARCINDEX_FOOTER, *PCASC_ARCINDEX_FOOTER;
+
+// Normalized structure for archive index entry
+typedef struct _CASC_ARCINDEX_ENTRY
{
- DWORD FrameArchiveOffset; // Archive file pointer corresponding to the begin of the frame
- DWORD FrameFileOffset; // File pointer corresponding to the begin of the frame
- DWORD CompressedSize; // Compressed size of the file
- DWORD FrameSize; // Size of the frame
- BYTE md5[MD5_HASH_SIZE]; // MD5 hash of the file sector
-} CASC_FILE_FRAME, *PCASC_FILE_FRAME;
+ BYTE IndexHash[MD5_HASH_SIZE];
+ BYTE EKey[MD5_HASH_SIZE];
+ DWORD ArchiveOffset;
+ DWORD EncodedSize;
+} CASC_ARCINDEX_ENTRY, *PCASC_ARCINDEX_ENTRY;
-// The encoding file is in the form of:
-// * File header
-// * String block #1
-// * Table A header
-// * Table A entries
-// * Table B header
-// * Table B entries
-// * String block #2
-// http://pxr.dk/wowdev/wiki/index.php?title=CASC#Key_CASC_Files
+// Normalized header of the ENCODING file
typedef struct _CASC_ENCODING_HEADER
{
- BYTE Magic[2]; // "EN"
- BYTE Version; // Expected to be 1 by CascLib
- BYTE ChecksumSizeA; // The length of the checksums in Encoding Table
- BYTE ChecksumSizeB; // The length of the checksums in Encoding Layout Table
- BYTE Flags_TableA[2]; // Flags for Encoding Table
- BYTE Flags_TableB[2]; // Flags for Encoding Layout Table
- BYTE Entries_TableA[4]; // Number of segments in Encoding Table (big endian)
- BYTE Entries_TableB[4]; // Number of segments in Encoding Layout Table (big endian)
- BYTE field_11;
- BYTE Size_StringTable1[4]; // Size of the string block #1
-
+ USHORT Magic; // FILE_MAGIC_ENCODING ('EN')
+ USHORT Version; // Expected to be 1 by CascLib
+ BYTE CKeyLength; // The content key length in ENCODING file. Usually 0x10
+ BYTE EKeyLength; // The encoded key length in ENCODING file. Usually 0x10
+ DWORD CKeyPageSize; // Size of the CKey page in bytes
+ DWORD EKeyPageSize; // Size of the CKey page in bytes
+ DWORD CKeyPageCount; // Number of CKey pages in the page table
+ DWORD EKeyPageCount; // Number of EKey pages in the page table
+ DWORD ESpecBlockSize; // Size of the ESpec string block, in bytes
} CASC_ENCODING_HEADER, *PCASC_ENCODING_HEADER;
-typedef struct _CASC_ENCODING_ENTRY
+typedef struct _CASC_DOWNLOAD_HEADER
{
- USHORT KeyCount; // Number of index keys
- BYTE FileSizeBE[4]; // Compressed file size (header area + frame headers + compressed frames), in bytes
- BYTE EncodingKey[MD5_HASH_SIZE]; // File encoding key
+ USHORT Magic; // FILE_MAGIC_DOWNLOAD ('DL')
+ USHORT Version; // Version
+ USHORT EKeyLength; // Length of the EKey
+ USHORT EntryHasChecksum; // If nonzero, then the entry has checksum
+ DWORD EntryCount;
+ DWORD TagCount;
+ USHORT FlagByteSize;
+ USHORT BasePriority;
- // Followed by the index keys
- // (number of items = KeyCount)
- // Followed by the index keys (number of items = KeyCount)
-} CASC_ENCODING_ENTRY, *PCASC_ENCODING_ENTRY;
+ size_t HeaderLength; // Length of the on-disk header, in bytes
+ size_t EntryLength; // Length of the on-disk entry, in bytes
+
+} CASC_DOWNLOAD_HEADER, *PCASC_DOWNLOAD_HEADER;
+
+typedef struct _CASC_DOWNLOAD_ENTRY
+{
+ BYTE EKey[MD5_HASH_SIZE];
+ ULONGLONG EncodedSize;
+ DWORD Checksum;
+ DWORD Flags;
+ BYTE Priority;
+} CASC_DOWNLOAD_ENTRY, *PCASC_DOWNLOAD_ENTRY;
+
+// Capturable tag structure for loading from DOWNLOAD manifest
+typedef struct _CASC_TAG_ENTRY1
+{
+ const char * szTagName; // Tag name
+ LPBYTE Bitmap; // Bitmap
+ size_t BitmapLength; // Length of the bitmap, in bytes
+ size_t NameLength; // Length of the tag name, in bytes, not including '\0'
+ size_t TagLength; // Length of the on-disk tag, in bytes
+ DWORD TagValue; // Tag value
+} CASC_TAG_ENTRY1, *PCASC_TAG_ENTRY1;
+
+// Tag structure for storing in arrays
+typedef struct _CASC_TAG_ENTRY2
+{
+ size_t NameLength; // Length of the on-disk tag, in bytes
+ DWORD TagValue; // Tag value
+ char szTagName[0x08]; // Tag string. This member can be longer than declared. Aligned to 8 bytes.
+} CASC_TAG_ENTRY2, *PCASC_TAG_ENTRY2;
-// A version of CASC_ENCODING_ENTRY with one index key
-typedef struct _CASC_ENCODING_ENTRY_1
+typedef struct _CASC_INSTALL_HEADER
{
- USHORT KeyCount; // Number of index keys
- BYTE FileSizeBE[4]; // Compressed file size (header area + frame headers + compressed frames), in bytes
- BYTE EncodingKey[MD5_HASH_SIZE]; // File encoding key
- BYTE IndexKey[MD5_HASH_SIZE]; // File index key
+ USHORT Magic; // FILE_MAGIC_DOWNLOAD ('DL')
+ BYTE Version; // Version
+ BYTE EKeyLength; // Length of the EKey
+ DWORD EntryCount;
+ DWORD TagCount;
-} CASC_ENCODING_ENTRY_1, *PCASC_ENCODING_ENTRY_1;
+ size_t HeaderLength; // Length of the on-disk header, in bytes
+} CASC_INSTALL_HEADER, *PCASC_INSTALL_HEADER;
-#define GET_INDEX_KEY(pEncodingEntry) (pEncodingEntry->EncodingKey + MD5_HASH_SIZE)
-#define FAKE_ENCODING_ENTRY_SIZE (sizeof(CASC_ENCODING_ENTRY) + MD5_HASH_SIZE)
+typedef struct _CASC_FILE_FRAME
+{
+ CONTENT_KEY FrameHash; // MD5 hash of the file frame
+ DWORD DataFileOffset; // Offset in the data file (data.###)
+ DWORD FileOffset; // File offset of this frame
+ DWORD EncodedSize; // Encoded size of the frame
+ DWORD ContentSize; // Content size of the frame
+} CASC_FILE_FRAME, *PCASC_FILE_FRAME;
//-----------------------------------------------------------------------------
// Structures for CASC storage and CASC file
-typedef struct _TCascStorage
+struct TCascStorage
{
- const char * szClassName; // "TCascStorage"
+ TCascStorage();
+ ~TCascStorage();
+ TCascStorage * AddRef();
+ TCascStorage * Release();
+
+ static TCascStorage * IsValid(HANDLE hStorage);
+
+ PFNPROGRESSCALLBACK PfnCallback; // Callback to be called during CascOpen(Online)Storage
+ void * PtrCallbackParam;
+
const TCHAR * szIndexFormat; // Format of the index file name
- TCHAR * szRootPath; // This is the game directory
+ const char * szClassName; // "TCascStorage"
+ const char * szProductName; // String representation of the product name
+ TCHAR * szRootPath; // Path where the build file is
TCHAR * szDataPath; // This is the directory where data files are
- TCHAR * szBuildFile; // Build file name (.build.info or .build.db)
TCHAR * szIndexPath; // This is the directory where index files are
- TCHAR * szUrlPath; // URL to the Blizzard servers
+ TCHAR * szBuildFile; // Build file name (.build.info or .build.db)
+ TCHAR * szCdnServers; // Multi-SZ list of CDN servers
+ TCHAR * szCdnPath; // Remote CDN sub path for the product
+ TCHAR * szCodeName; // Product code name. Only for online storages
+ char * szRegion; // Product region. Only when "versions" is used as storage root file
+ CASC_PRODUCT Product; // Product enum value (see CASC_PRODUCT)
+ DWORD dwBuildNumber; // Product build number
DWORD dwRefCount; // Number of references
- DWORD dwGameInfo; // Game type
- DWORD dwBuildNumber; // Game build number
- DWORD dwFileBeginDelta; // This is number of bytes to shift back from archive offset (from index entry) to actual begin of file data
DWORD dwDefaultLocale; // Default locale, read from ".build.info"
+ DWORD dwFeatures; // List of CASC features. See CASC_FEATURE_XXX
+ bool bAllowOrphans; // If TRUE, then orphaned items are allowed
CBLD_TYPE BuildFileType; // Type of the build file
- QUERY_KEY CdnConfigKey;
- QUERY_KEY CdnBuildKey;
- QUERY_KEY ArchivesGroup; // Key array of the "archive-group"
+ QUERY_KEY CdnConfigKey; // Currently selected CDN config file. Points to "config\%02X\%02X\%s
+ QUERY_KEY CdnBuildKey; // Currently selected CDN build file. Points to "config\%02X\%02X\%s
+
+ QUERY_KEY ArchiveGroup; // Key array of the "archive-group"
QUERY_KEY ArchivesKey; // Key array of the "archives"
QUERY_KEY PatchArchivesKey; // Key array of the "patch-archives"
QUERY_KEY PatchArchivesGroup; // Key array of the "patch-archive-group"
- QUERY_KEY RootKey;
- QUERY_KEY PatchKey;
- QUERY_KEY DownloadKey;
- QUERY_KEY InstallKey;
- QUERY_KEY EncodingKey;
-
- TFileStream * DataFileArray[CASC_MAX_DATA_FILES]; // Data file handles
+ QUERY_KEY BuildFiles; // List of supported build files
- CASC_MAPPING_TABLE KeyMapping[CASC_INDEX_COUNT]; // Key mapping
- PCASC_MAP pIndexEntryMap; // Map of index entries
+ TFileStream * DataFiles[CASC_MAX_DATA_FILES]; // Array of open data files
- QUERY_KEY EncodingFile; // Content of the ENCODING file
- PCASC_MAP pEncodingMap; // Map of encoding entries
- DYNAMIC_ARRAY ExtraEntries; // Extra encoding entries
+ CASC_CKEY_ENTRY EncodingCKey; // Information about ENCODING file
+ CASC_CKEY_ENTRY DownloadCKey; // Information about DOWNLOAD file
+ CASC_CKEY_ENTRY InstallCKey; // Information about INSTALL file
+ CASC_CKEY_ENTRY PatchFile; // Information about PATCH file
+ CASC_CKEY_ENTRY RootFile; // Information about ROOT file
+ CASC_CKEY_ENTRY SizeFile; // Information about SIZE file
+ CASC_CKEY_ENTRY VfsRoot; // The main VFS root file
+ CASC_ARRAY VfsRootList; // List of CASC_EKEY_ENTRY for each TVFS sub-root
TRootHandler * pRootHandler; // Common handler for various ROOT file formats
-
-} TCascStorage;
-
-typedef struct _TCascFile
+ CASC_ARRAY ArcIndexArray; // Array of CASC_ARCINDEX_ENTRY, loaded from archive indexes
+ CASC_ARRAY IndexArray; // Array of CASC_CKEY_ENTRY loaded from local index files
+ CASC_ARRAY CKeyArray; // Array of CASC_CKEY_ENTRY, one entry for each physical file
+ CASC_ARRAY TagsArray; // Array of CASC_DOWNLOAD_TAG2
+ CASC_MAP ArcIndexMap; // Map of EKey -> CASC_ARCINDEX_ENTRY
+ CASC_MAP CKeyMap; // Map of CKey -> CASC_CKEY_ENTRY
+ CASC_MAP EKeyMap; // Map of EKey -> CASC_EKEY_ENTRY
+ size_t LocalFiles; // Number of files that are present locally
+ size_t TotalFiles; // Total number of files in the storage, some may not be present locally
+ size_t EKeyEntries; // Number of CKeyEntry-ies loaded from text build file
+ size_t OrphanItems; // Number of EKey entries in indexes that do not have their counterpart in ENCODING
+ size_t SkippedItems; // Number of EKey entries in indexes that were ignored due to insufficient capacity of CKeyArray
+ size_t EKeyLength; // EKey length from the index files
+ DWORD FileOffsetBits; // Nimber of bits in the storage offset which mean data segent offset
+
+ CASC_ARRAY ExtraKeysList; // List additional encryption keys
+ CASC_MAP EncryptionKeys; // Map of encryption keys
+
+};
+
+struct TCascFile
{
+ TCascFile(TCascStorage * ahs, PCASC_CKEY_ENTRY apCKeyEntry)
+ {
+ ULONGLONG StorageOffset;
+ ULONGLONG FileOffsMask;
+
+ // Reference the storage handle
+ hs = ahs->AddRef();
+ szClassName = "TCascFile";
+
+ // Init class variables
+ pCKeyEntry = apCKeyEntry;
+ pFrames = NULL;
+ pbFileCache = NULL;
+ cbFileCache = 0;
+
+ // Supply the sizes
+ StorageOffset = pCKeyEntry->StorageOffset;
+ FileOffsMask = ((ULONGLONG)1 << hs->FileOffsetBits) - 1;
+ ArchiveIndex = (DWORD)(StorageOffset >> hs->FileOffsetBits);
+ ArchiveOffset = (DWORD)(StorageOffset & FileOffsMask);
+ EncodedSize = pCKeyEntry->EncodedSize;
+ ContentSize = pCKeyEntry->ContentSize;
+ FilePointer = 0;
+ FrameCount = 0;
+ bVerifyIntegrity = false;
+ bDownloadFileIf = false;
+ bLocalFileStream = false;
+ }
+
+ ~TCascFile()
+ {
+ // Close (dereference) the archive handle
+ hs = hs->Release();
+ szClassName = NULL;
+
+ // Free the file cache and frame array
+ CASC_FREE(pbFileCache);
+ CASC_FREE(pFrames);
+
+ // If we are supposed to close the file stream, do it
+ if (pStream && bLocalFileStream)
+ FileStream_Close(pStream);
+ }
+
+ static TCascFile * IsValid(HANDLE hFile)
+ {
+ TCascFile * hf = (TCascFile *)hFile;
+
+ return (hf != INVALID_HANDLE_VALUE &&
+ hf != NULL &&
+ hf->hs != NULL &&
+ hf->szClassName != NULL &&
+ hf->pCKeyEntry != NULL) ? hf : NULL;
+ }
+
TCascStorage * hs; // Pointer to storage structure
TFileStream * pStream; // An open data stream
const char * szClassName; // "TCascFile"
- DWORD FilePointer; // Current file pointer
-
- DWORD ArchiveIndex; // Index of the archive (data.###)
- DWORD HeaderOffset; // Offset of the BLTE header, relative to the begin of the archive
- DWORD HeaderSize; // Length of the BLTE header
- DWORD FramesOffset; // Offset of the frame data, relative to the begin of the archive
- DWORD CompressedSize; // Compressed size of the file (in bytes)
- DWORD FileSize; // Size of file, in bytes
- BYTE FrameArrayHash[MD5_HASH_SIZE]; // MD5 hash of the frame array
-
+ PCASC_CKEY_ENTRY pCKeyEntry;
PCASC_FILE_FRAME pFrames; // Array of file frames
+ DWORD ArchiveIndex; // Index of the archive (data.###)
+ DWORD ArchiveOffset; // Offset in the archive (data.###)
+ DWORD FilePointer; // Current file pointer
+ DWORD EncodedSize; // Encoded size. This is the size of encoded header, all file frame headers and all file frames
+ DWORD ContentSize; // Content size. This is the size of the file content, aka the file size
DWORD FrameCount; // Number of the file frames
+ DWORD bVerifyIntegrity:1; // If true, then the data are validated more strictly when read
+ DWORD bDownloadFileIf:1; // If true, then the data will be downloaded from the online storage if missing
+ DWORD bLocalFileStream:1; // If true, then the file stream is a local file
LPBYTE pbFileCache; // Pointer to file cache
DWORD cbFileCache; // Size of the file cache
- DWORD CacheStart; // Starting offset in the cache
- DWORD CacheEnd; // Ending offset in the cache
-
-#ifdef CASCLIB_TEST // Extra fields for analyzing the file size problem
- DWORD FileSize_RootEntry; // File size, from the root entry
- DWORD FileSize_EncEntry; // File size, from the encoding entry
- DWORD FileSize_IdxEntry; // File size, from the index entry
- DWORD FileSize_HdrArea; // File size, as stated in the file header area
- DWORD FileSize_FrameSum; // File size as sum of frame sizes
-#endif
-} TCascFile;
+};
-typedef struct _TCascSearch
+struct TCascSearch
{
+ TCascSearch(TCascStorage * ahs, LPCTSTR aszListFile, const char * aszMask)
+ {
+ // Init the class
+ szClassName = "TCascSearch";
+ hs = ahs->AddRef();
+
+ // Init provider-specific data
+ pCache = NULL;
+ nFileIndex = 0;
+ nSearchState = 0;
+ bListFileUsed = false;
+
+ // Allocate mask
+ szListFile = CascNewStr(aszListFile);
+ szMask = CascNewStr((aszMask != NULL) ? aszMask : "*");
+ }
+
+ ~TCascSearch()
+ {
+ // Dereference the CASC storage
+ hs = hs->Release();
+ szClassName = NULL;
+
+ // Free the rest of the members
+ CASC_FREE(szMask);
+ CASC_FREE(szListFile);
+ CASC_FREE(pCache);
+ }
+
+ static TCascSearch * IsValid(HANDLE hFind)
+ {
+ TCascSearch * pSearch = (TCascSearch *)hFind;
+
+ return (hFind != INVALID_HANDLE_VALUE &&
+ hFind != NULL &&
+ pSearch->szClassName != NULL &&
+ !strcmp(pSearch->szClassName, "TCascSearch") &&
+ pSearch->szMask != NULL) ? pSearch : NULL;
+ }
+
TCascStorage * hs; // Pointer to the storage handle
const char * szClassName; // Contains "TCascSearch"
TCHAR * szListFile; // Name of the listfile
void * pCache; // Listfile cache
char * szMask; // Search mask
- char szFileName[MAX_PATH]; // Buffer for the file name
// Provider-specific data
- void * pRootContext; // Root-specific search context
- size_t IndexLevel1; // Root-specific search context
- size_t IndexLevel2; // Root-specific search context
- DWORD dwState; // Pointer to the search state (0 = listfile, 1 = nameless, 2 = done)
-
- BYTE BitArray[1]; // Bit array of encoding keys. Set for each entry that has already been reported
-
-} TCascSearch;
+ size_t nFileIndex; // Root-specific search context
+ DWORD nSearchState:8; // The current search state (0 = listfile, 1 = nameless, 2 = done)
+ DWORD bListFileUsed:1; // TRUE: The listfile has already been loaded
+};
//-----------------------------------------------------------------------------
-// Memory management
-//
-// We use our own macros for allocating/freeing memory. If you want
-// to redefine them, please keep the following rules:
-//
-// - The memory allocation must return NULL if not enough memory
-// (i.e not to throw exception)
-// - The allocating function does not need to fill the allocated buffer with zeros
-// - The reallocating function must support NULL as the previous block
-// - Memory freeing function doesn't have to test the pointer to NULL
-//
-
-#if defined(_MSC_VER) && defined(_DEBUG)
-
-#define CASC_REALLOC(type, ptr, count) (type *)HeapReAlloc(GetProcessHeap(), 0, ptr, ((count) * sizeof(type)))
-#define CASC_ALLOC(type, count) (type *)HeapAlloc(GetProcessHeap(), 0, ((count) * sizeof(type)))
-#define CASC_FREE(ptr) HeapFree(GetProcessHeap(), 0, ptr)
-
-#else
-
-#define CASC_REALLOC(type, ptr, count) (type *)realloc(ptr, (count) * sizeof(type))
-#define CASC_ALLOC(type, count) (type *)malloc((count) * sizeof(type))
-#define CASC_FREE(ptr) free(ptr)
+// Common functions (CascCommon.cpp)
-#endif
-
-//-----------------------------------------------------------------------------
-// Big endian number manipulation
-
-DWORD ConvertBytesToInteger_3(LPBYTE ValueAsBytes);
-DWORD ConvertBytesToInteger_4(LPBYTE ValueAsBytes);
-DWORD ConvertBytesToInteger_4_LE(LPBYTE ValueAsBytes);
-ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes);
-
-void ConvertIntegerToBytes_4(DWORD Value, LPBYTE ValueAsBytes);
+bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD dwOpenFlags, HANDLE * PtrFileHandle);
+LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD * pcbFileData);
+LPBYTE LoadFileToMemory(const TCHAR * szFileName, DWORD * pcbFileData);
void FreeCascBlob(PQUERY_KEY pQueryKey);
//-----------------------------------------------------------------------------
// Text file parsing (CascFiles.cpp)
-int LoadBuildInfo(TCascStorage * hs);
+int DownloadFileFromCDN(TCascStorage * hs, const TCHAR * szSubDir, LPBYTE pbEKey, const TCHAR * szExtension, TCHAR * szOutLocalPath, size_t cchOutLocalPath);
int CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory);
-
-int GetRootVariableIndex(const char * szLinePtr, const char * szLineEnd, const char * szVariableName, int * PtrIndex);
-int ParseRootFileLine(const char * szLinePtr, const char * szLineEnd, int nFileNameIndex, PQUERY_KEY pEncodingKey, char * szFileName, size_t nMaxChars);
+int LoadCdnsInfo(TCascStorage * hs);
+int LoadBuildInfo(TCascStorage * hs);
+int LoadCdnConfigFile(TCascStorage * hs);
+int LoadCdnBuildFile(TCascStorage * hs);
//-----------------------------------------------------------------------------
// Internal file functions
-TCascStorage * IsValidStorageHandle(HANDLE hStorage);
-TCascFile * IsValidFileHandle(HANDLE hFile);
+void * ProbeOutputBuffer(void * pvBuffer, size_t cbLength, size_t cbMinLength, size_t * pcbLengthNeeded);
+
+PCASC_CKEY_ENTRY FindCKeyEntry_CKey(TCascStorage * hs, LPBYTE pbCKey, PDWORD PtrIndex = NULL);
+PCASC_CKEY_ENTRY FindCKeyEntry_EKey(TCascStorage * hs, LPBYTE pbEKey, PDWORD PtrIndex = NULL);
-PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, PDWORD PtrIndex);
-PCASC_INDEX_ENTRY FindIndexEntry(TCascStorage * hs, PQUERY_KEY pIndexKey);
+size_t GetTagBitmapLength(LPBYTE pbFilePtr, LPBYTE pbFileEnd, DWORD EntryCount);
int CascDecompress(LPBYTE pvOutBuffer, PDWORD pcbOutBuffer, LPBYTE pvInBuffer, DWORD cbInBuffer);
-int CascDecrypt (LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex);
int CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer);
+int CascLoadEncryptionKeys(TCascStorage * hs);
+int CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex);
+
+//-----------------------------------------------------------------------------
+// Support for index files
+
+int LoadIndexFiles(TCascStorage * hs);
+
//-----------------------------------------------------------------------------
// Support for ROOT file
int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
+int RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
+int RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask);
int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
-int RootHandler_CreateWoW6(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask);
-int RootHandler_CreateSC1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
+int RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
+int RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
//-----------------------------------------------------------------------------
-// Dumping CASC data structures
+// Dumpers (CascDumpData.cpp)
#ifdef _DEBUG
-void CascDumpSparseArray(const char * szFileName, void * pvSparseArray);
-void CascDumpNameFragTable(const char * szFileName, void * pvMarFile);
-void CascDumpFileNames(const char * szFileName, void * pvMarFile);
-void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs);
-void CascDumpEncodingEntry(TCascStorage * hs, TDumpContext * dc, PCASC_ENCODING_ENTRY pEncodingEntry, int nDumpLevel);
-void CascDumpFile(const char * szFileName, HANDLE hFile);
-#endif // _DEBUG
+void CascDumpFile(HANDLE hFile, const char * szDumpFile = NULL);
+void CascDumpStorage(HANDLE hStorage, const char * szDumpFile = NULL);
+#endif
#endif // __CASCCOMMON_H__
diff --git a/dep/CascLib/src/CascDecompress.cpp b/dep/CascLib/src/CascDecompress.cpp
index e60adb8f97b..3262c59ddf7 100644
--- a/dep/CascLib/src/CascDecompress.cpp
+++ b/dep/CascLib/src/CascDecompress.cpp
@@ -18,29 +18,39 @@
int CascDecompress(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer)
{
z_stream z; // Stream information for zlib
+ uInt cbOutBuffer = *pcbOutBuffer;
int nResult;
+ int nError = ERROR_FILE_CORRUPT;
// Fill the stream structure for zlib
z.next_in = pbInBuffer;
z.avail_in = cbInBuffer;
z.total_in = cbInBuffer;
z.next_out = pbOutBuffer;
- z.avail_out = *pcbOutBuffer;
+ z.avail_out = cbOutBuffer;
z.total_out = 0;
z.zalloc = NULL;
z.zfree = NULL;
+ // Reset the total number of output bytes
+ cbOutBuffer = 0;
+
// Initialize the decompression structure
if((nResult = inflateInit(&z)) == Z_OK)
{
// Call zlib to decompress the data
nResult = inflate(&z, Z_NO_FLUSH);
- inflateEnd(&z);
+ if (nResult == Z_OK || nResult == Z_STREAM_END)
+ {
+ // Give the size of the uncompressed data
+ cbOutBuffer = z.total_out;
+ nError = ERROR_SUCCESS;
+ }
- // Give the size of the uncompressed data
- *pcbOutBuffer = z.total_out;
+ inflateEnd(&z);
}
- // Return an error code
- return (nResult == Z_OK || nResult == Z_STREAM_END) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
+ // Give the caller the number of bytes needed
+ pcbOutBuffer[0] = cbOutBuffer;
+ return nError;
}
diff --git a/dep/CascLib/src/CascDecrypt.cpp b/dep/CascLib/src/CascDecrypt.cpp
index 57040dd21fb..81e098ca178 100644
--- a/dep/CascLib/src/CascDecrypt.cpp
+++ b/dep/CascLib/src/CascDecrypt.cpp
@@ -15,6 +15,8 @@
//-----------------------------------------------------------------------------
// Local structures
+#define CASC_EXTRA_KEYS 0x80
+
typedef struct _CASC_ENCRYPTION_KEY
{
ULONGLONG KeyName; // "Name" of the key
@@ -31,125 +33,196 @@ typedef struct _CASC_SALSA20
//-----------------------------------------------------------------------------
// Known encryption keys. See https://wowdev.wiki/CASC for updates
+static const char * szKeyConstant16 = "expand 16-byte k";
+static const char * szKeyConstant32 = "expand 32-byte k";
+
static CASC_ENCRYPTION_KEY CascKeys[] =
{
- // Key Name Encryption key Seen in
- // ---------------------- ------------------------------------------------------------------------------------------------ -----------
-
- // Battle.net app
- { 0x2C547F26A2613E01ULL, { 0x37, 0xC5, 0x0C, 0x10, 0x2D, 0x4C, 0x9E, 0x3A, 0x5A, 0xC0, 0x69, 0xF0, 0x72, 0xB1, 0x41, 0x7D } }, // Battle.net App Alpha 1.5.0
-
- // Overwatch
- { 0xFB680CB6A8BF81F3ULL, { 0x62, 0xD9, 0x0E, 0xFA, 0x7F, 0x36, 0xD7, 0x1C, 0x39, 0x8A, 0xE2, 0xF1, 0xFE, 0x37, 0xBD, 0xB9 } }, // 0.8.0.24919_retailx64 (hardcoded)
- { 0x402CD9D8D6BFED98ULL, { 0xAE, 0xB0, 0xEA, 0xDE, 0xA4, 0x76, 0x12, 0xFE, 0x6C, 0x04, 0x1A, 0x03, 0x95, 0x8D, 0xF2, 0x41 } }, // 0.8.0.24919_retailx64 (hardcoded)
- { 0xDBD3371554F60306ULL, { 0x34, 0xE3, 0x97, 0xAC, 0xE6, 0xDD, 0x30, 0xEE, 0xFD, 0xC9, 0x8A, 0x2A, 0xB0, 0x93, 0xCD, 0x3C } }, // 0.8.0.24919_retailx64 (streamed from server)
- { 0x11A9203C9881710AULL, { 0x2E, 0x2C, 0xB8, 0xC3, 0x97, 0xC2, 0xF2, 0x4E, 0xD0, 0xB5, 0xE4, 0x52, 0xF1, 0x8D, 0xC2, 0x67 } }, // 0.8.0.24919_retailx64 (streamed from server)
- { 0xA19C4F859F6EFA54ULL, { 0x01, 0x96, 0xCB, 0x6F, 0x5E, 0xCB, 0xAD, 0x7C, 0xB5, 0x28, 0x38, 0x91, 0xB9, 0x71, 0x2B, 0x4B } }, // 0.8.0.24919_retailx64 (streamed from server)
- { 0x87AEBBC9C4E6B601ULL, { 0x68, 0x5E, 0x86, 0xC6, 0x06, 0x3D, 0xFD, 0xA6, 0xC9, 0xE8, 0x52, 0x98, 0x07, 0x6B, 0x3D, 0x42 } }, // 0.8.0.24919_retailx64 (streamed from server)
- { 0xDEE3A0521EFF6F03ULL, { 0xAD, 0x74, 0x0C, 0xE3, 0xFF, 0xFF, 0x92, 0x31, 0x46, 0x81, 0x26, 0x98, 0x57, 0x08, 0xE1, 0xB9 } }, // 0.8.0.24919_retailx64 (streamed from server)
- { 0x8C9106108AA84F07ULL, { 0x53, 0xD8, 0x59, 0xDD, 0xA2, 0x63, 0x5A, 0x38, 0xDC, 0x32, 0xE7, 0x2B, 0x11, 0xB3, 0x2F, 0x29 } }, // 0.8.0.24919_retailx64 (streamed from server)
- { 0x49166D358A34D815ULL, { 0x66, 0x78, 0x68, 0xCD, 0x94, 0xEA, 0x01, 0x35, 0xB9, 0xB1, 0x6C, 0x93, 0xB1, 0x12, 0x4A, 0xBA } }, // 0.8.0.24919_retailx64 (streamed from server)
- { 0x1463A87356778D14ULL, { 0x69, 0xBD, 0x2A, 0x78, 0xD0, 0x5C, 0x50, 0x3E, 0x93, 0x99, 0x49, 0x59, 0xB3, 0x0E, 0x5A, 0xEC } }, // ? 1.0.3.0 (streamed from server)
- { 0x5E152DE44DFBEE01ULL, { 0xE4, 0x5A, 0x17, 0x93, 0xB3, 0x7E, 0xE3, 0x1A, 0x8E, 0xB8, 0x5C, 0xEE, 0x0E, 0xEE, 0x1B, 0x68 } }, // ? 1.0.3.0 (streamed from server)
- { 0x9B1F39EE592CA415ULL, { 0x54, 0xA9, 0x9F, 0x08, 0x1C, 0xAD, 0x0D, 0x08, 0xF7, 0xE3, 0x36, 0xF4, 0x36, 0x8E, 0x89, 0x4C } }, // ? 1.0.3.0 (streamed from server)
- { 0x24C8B75890AD5917ULL, { 0x31, 0x10, 0x0C, 0x00, 0xFD, 0xE0, 0xCE, 0x18, 0xBB, 0xB3, 0x3F, 0x3A, 0xC1, 0x5B, 0x30, 0x9F } }, // ? 1.0.3.0 (included in game)
- { 0xEA658B75FDD4890FULL, { 0xDE, 0xC7, 0xA4, 0xE7, 0x21, 0xF4, 0x25, 0xD1, 0x33, 0x03, 0x98, 0x95, 0xC3, 0x60, 0x36, 0xF8 } }, // ? 1.0.3.0 (included in game)
- { 0x026FDCDF8C5C7105ULL, { 0x8F, 0x41, 0x80, 0x9D, 0xA5, 0x53, 0x66, 0xAD, 0x41, 0x6D, 0x3C, 0x33, 0x74, 0x59, 0xEE, 0xE3 } }, // (included in game)
- { 0xCAE3FAC925F20402ULL, { 0x98, 0xB7, 0x8E, 0x87, 0x74, 0xBF, 0x27, 0x50, 0x93, 0xCB, 0x1B, 0x5F, 0xC7, 0x14, 0x51, 0x1B } }, // (included in game)
- { 0x061581CA8496C80CULL, { 0xDA, 0x2E, 0xF5, 0x05, 0x2D, 0xB9, 0x17, 0x38, 0x0B, 0x8A, 0xA6, 0xEF, 0x7A, 0x5F, 0x8E, 0x6A } }, //
- { 0xBE2CB0FAD3698123ULL, { 0x90, 0x2A, 0x12, 0x85, 0x83, 0x6C, 0xE6, 0xDA, 0x58, 0x95, 0x02, 0x0D, 0xD6, 0x03, 0xB0, 0x65 } }, //
- { 0x57A5A33B226B8E0AULL, { 0xFD, 0xFC, 0x35, 0xC9, 0x9B, 0x9D, 0xB1, 0x1A, 0x32, 0x62, 0x60, 0xCA, 0x24, 0x6A, 0xCB, 0x41 } }, // 1.1.0.0.30200 Ana
- { 0x42B9AB1AF5015920ULL, { 0xC6, 0x87, 0x78, 0x82, 0x3C, 0x96, 0x4C, 0x6F, 0x24, 0x7A, 0xCC, 0x0F, 0x4A, 0x25, 0x84, 0xF8 } }, // 1.2.0.1.30684 Summer Games
- { 0x4F0FE18E9FA1AC1AULL, { 0x89, 0x38, 0x1C, 0x74, 0x8F, 0x65, 0x31, 0xBB, 0xFC, 0xD9, 0x77, 0x53, 0xD0, 0x6C, 0xC3, 0xCD } }, // 1.2.0.1.30684
- { 0x7758B2CF1E4E3E1BULL, { 0x3D, 0xE6, 0x0D, 0x37, 0xC6, 0x64, 0x72, 0x35, 0x95, 0xF2, 0x7C, 0x5C, 0xDB, 0xF0, 0x8B, 0xFA } }, // 1.2.0.1.30684
- { 0xE5317801B3561125ULL, { 0x7D, 0xD0, 0x51, 0x19, 0x9F, 0x84, 0x01, 0xF9, 0x5E, 0x4C, 0x03, 0xC8, 0x84, 0xDC, 0xEA, 0x33 } }, // 1.4.0.2.32143 Halloween Terror
- { 0x16B866D7BA3A8036ULL, { 0x13, 0x95, 0xE8, 0x82, 0xBF, 0x25, 0xB4, 0x81, 0xF6, 0x1A, 0x4D, 0x62, 0x11, 0x41, 0xDA, 0x6E } }, // 1.4.1.0.31804 Bastion Blizzcon 2016 skin
- { 0x11131FFDA0D18D30ULL, { 0xC3, 0x2A, 0xD1, 0xB8, 0x25, 0x28, 0xE0, 0xA4, 0x56, 0x89, 0x7B, 0x3C, 0xE1, 0xC2, 0xD2, 0x7E } }, // 1.5.0.1.32795 Sombra
- { 0xCAC6B95B2724144AULL, { 0x73, 0xE4, 0xBE, 0xA1, 0x45, 0xDF, 0x2B, 0x89, 0xB6, 0x5A, 0xEF, 0x02, 0xF8, 0x3F, 0xA2, 0x60 } }, // 1.5.0.1.32795 Ecopoint: Antarctica
- { 0xB7DBC693758A5C36ULL, { 0xBC, 0x3A, 0x92, 0xBF, 0xE3, 0x02, 0x51, 0x8D, 0x91, 0xCC, 0x30, 0x79, 0x06, 0x71, 0xBF, 0x10 } }, // 1.5.0.1.32795 Genji Oni skin
- { 0x90CA73B2CDE3164BULL, { 0x5C, 0xBF, 0xF1, 0x1F, 0x22, 0x72, 0x0B, 0xAC, 0xC2, 0xAE, 0x6A, 0xAD, 0x8F, 0xE5, 0x33, 0x17 } }, // 1.6.1.0.33236 Oasis map
- { 0x6DD3212FB942714AULL, { 0xE0, 0x2C, 0x16, 0x43, 0x60, 0x2E, 0xC1, 0x6C, 0x3A, 0xE2, 0xA4, 0xD2, 0x54, 0xA0, 0x8F, 0xD9 } }, // 1.6.1.0.33236
- { 0x11DDB470ABCBA130ULL, { 0x66, 0x19, 0x87, 0x66, 0xB1, 0xC4, 0xAF, 0x75, 0x89, 0xEF, 0xD1, 0x3A, 0xD4, 0xDD, 0x66, 0x7A } }, // 1.6.1.0.33236 Winter Wonderland
- { 0x5BEF27EEE95E0B4BULL, { 0x36, 0xBC, 0xD2, 0xB5, 0x51, 0xFF, 0x1C, 0x84, 0xAA, 0x3A, 0x39, 0x94, 0xCC, 0xEB, 0x03, 0x3E } }, //
- { 0x9359B46E49D2DA42ULL, { 0x17, 0x3D, 0x65, 0xE7, 0xFC, 0xAE, 0x29, 0x8A, 0x93, 0x63, 0xBD, 0x6A, 0xA1, 0x89, 0xF2, 0x00 } }, // Diablo's 20th anniversary
- { 0x1A46302EF8896F34ULL, { 0x80, 0x29, 0xAD, 0x54, 0x51, 0xD4, 0xBC, 0x18, 0xE9, 0xD0, 0xF5, 0xAC, 0x44, 0x9D, 0xC0, 0x55 } }, // 1.7.0.2.34156 Year of the Rooster
- { 0x693529F7D40A064CULL, { 0xCE, 0x54, 0x87, 0x3C, 0x62, 0xDA, 0xA4, 0x8E, 0xFF, 0x27, 0xFC, 0xC0, 0x32, 0xBD, 0x07, 0xE3 } }, // 1.8.0.0.34470 CTF Maps
- { 0x388B85AEEDCB685DULL, { 0xD9, 0x26, 0xE6, 0x59, 0xD0, 0x4A, 0x09, 0x6B, 0x24, 0xC1, 0x91, 0x51, 0x07, 0x6D, 0x37, 0x9A } }, // 1.8.0.0.34470 Numbani Update (Doomfist teaser)
- { 0xE218F69AAC6C104DULL, { 0xF4, 0x3D, 0x12, 0xC9, 0x4A, 0x9A, 0x52, 0x84, 0x97, 0x97, 0x1F, 0x1C, 0xBE, 0x41, 0xAD, 0x4D } }, // 1.9.0.0.34986 Orisa
- { 0xF432F0425363F250ULL, { 0xBA, 0x69, 0xF2, 0xB3, 0x3C, 0x27, 0x68, 0xF5, 0xF2, 0x9B, 0xFE, 0x78, 0xA5, 0xA1, 0xFA, 0xD5 } }, // 1.10.0.0.35455 Uprising
- { 0x061D52F86830B35DULL, { 0xD7, 0x79, 0xF9, 0xC6, 0xCC, 0x9A, 0x4B, 0xE1, 0x03, 0xA4, 0xE9, 0x0A, 0x73, 0x38, 0xF7, 0x93 } }, // 1.10.0.0.35455 D.Va Officer Skin (HotS Nexus Challenge 2)
- { 0x1275C84CF113EF65ULL, { 0xCF, 0x58, 0xB6, 0x93, 0x3E, 0xAF, 0x98, 0xAF, 0x53, 0xE7, 0x6F, 0x84, 0x26, 0xCC, 0x7E, 0x6C } }, //
- { 0xD9C7C7AC0F14C868ULL, { 0x3A, 0xFD, 0xF6, 0x8E, 0x3A, 0x5D, 0x63, 0xBA, 0xBA, 0x1E, 0x68, 0x21, 0x88, 0x3F, 0x06, 0x7D } }, //
- { 0xBD4E42661A432951ULL, { 0x6D, 0xE8, 0xE2, 0x8C, 0x85, 0x11, 0x64, 0x4D, 0x55, 0x95, 0xFC, 0x45, 0xE5, 0x35, 0x14, 0x72 } }, // 1.11.0.0.36376 Anniversary event
- { 0xC43CB14355249451ULL, { 0x0E, 0xA2, 0xB4, 0x4F, 0x96, 0xA2, 0x69, 0xA3, 0x86, 0x85, 0x6D, 0x04, 0x9A, 0x3D, 0xEC, 0x86 } }, // 1.12.0.0.37104 Horizon Lunar Colony
- { 0xE6D914F8E4744953ULL, { 0xC8, 0x47, 0x7C, 0x28, 0x9D, 0xCE, 0x66, 0xD9, 0x13, 0x65, 0x07, 0xA3, 0x3A, 0xA3, 0x33, 0x01 } }, // 1.13.0.0.37646 Doomfist
- { 0x5694C503F8C80178ULL, { 0x7F, 0x4C, 0xF1, 0xC1, 0xFB, 0xBA, 0xD9, 0x2B, 0x18, 0x43, 0x36, 0xD6, 0x77, 0xEB, 0xF9, 0x37 } }, // 1.13.0.0.37646 Doomfist
- { 0x21DBFD65F3E54269ULL, { 0xAB, 0x58, 0x0C, 0x38, 0x37, 0xCA, 0xF8, 0xA4, 0x61, 0xF2, 0x43, 0xA5, 0x66, 0xB2, 0xAE, 0x4D } }, // 1.13.0.0.37646 Summer Games 2017
- // { 0x27ABA5F88DD8D078ULL, {0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??}}, // 1.13.0.0.37646
- { 0x21E1F90E71D33C71ULL, { 0x32, 0x87, 0x42, 0x33, 0x91, 0x62, 0xB3, 0x26, 0x76, 0xC8, 0x03, 0xC2, 0x25, 0x59, 0x31, 0xA6 } }, // 1.14.1.0.39083 Deathmatch
- { 0xD9CB055BCDD40B6EULL, { 0x49, 0xFB, 0x44, 0x77, 0xA4, 0xA0, 0x82, 0x53, 0x27, 0xE9, 0xA7, 0x36, 0x82, 0xBE, 0xCD, 0x0C } }, // 1.15.0.0.????? Junkertown
- { 0x8175CE3C694C6659ULL, { 0xE3, 0xF3, 0xFA, 0x77, 0x26, 0xC7, 0x0D, 0x26, 0xAE, 0x13, 0x0D, 0x96, 0x9D, 0xDD, 0xF3, 0x99 } }, // 1.16.0.0.40011 Halloween 2017
- { 0xB8DE51690075435AULL, { 0xC0, 0x7E, 0x92, 0x60, 0xBB, 0x71, 0x12, 0x17, 0xE7, 0xDE, 0x6F, 0xED, 0x91, 0x1F, 0x42, 0x96 } }, // 1.16.0.0.????? Winston Blizzcon 2017 skin
- { 0xF6CF23955B5D437DULL, { 0xAE, 0xBA, 0x22, 0x73, 0x28, 0xA5, 0xB0, 0xAA, 0x9F, 0x51, 0xDA, 0xE3, 0xF6, 0xA7, 0xDF, 0xE4 } }, // 1.17.0.2.41350 Moira
-
- // Streamed WoW keys
- { 0xFA505078126ACB3EULL, { 0xBD, 0xC5, 0x18, 0x62, 0xAB, 0xED, 0x79, 0xB2, 0xDE, 0x48, 0xC8, 0xE7, 0xE6, 0x6C, 0x62, 0x00 } }, // 15 WOW-20740patch7.0.1_Beta
- { 0xFF813F7D062AC0BCULL, { 0xAA, 0x0B, 0x5C, 0x77, 0xF0, 0x88, 0xCC, 0xC2, 0xD3, 0x90, 0x49, 0xBD, 0x26, 0x7F, 0x06, 0x6D } }, // 25 WOW-20740patch7.0.1_Beta
- { 0xD1E9B5EDF9283668ULL, { 0x8E, 0x4A, 0x25, 0x79, 0x89, 0x4E, 0x38, 0xB4, 0xAB, 0x90, 0x58, 0xBA, 0x5C, 0x73, 0x28, 0xEE } }, // 39 WOW-20740patch7.0.1_Beta Enchanted Torch pet
- { 0xB76729641141CB34ULL, { 0x98, 0x49, 0xD1, 0xAA, 0x7B, 0x1F, 0xD0, 0x98, 0x19, 0xC5, 0xC6, 0x62, 0x83, 0xA3, 0x26, 0xEC } }, // 40 WOW-20740patch7.0.1_Beta Enchanted Pen pet
- { 0xFFB9469FF16E6BF8ULL, { 0xD5, 0x14, 0xBD, 0x19, 0x09, 0xA9, 0xE5, 0xDC, 0x87, 0x03, 0xF4, 0xB8, 0xBB, 0x1D, 0xFD, 0x9A } }, // 41 WOW-20740patch7.0.1_Beta
- { 0x23C5B5DF837A226CULL, { 0x14, 0x06, 0xE2, 0xD8, 0x73, 0xB6, 0xFC, 0x99, 0x21, 0x7A, 0x18, 0x08, 0x81, 0xDA, 0x8D, 0x62 } }, // 42 WOW-20740patch7.0.1_Beta Enchanted Cauldron pet
- // {0x3AE403EF40AC3037ULL, {0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??}}, // 51 WOW-21249patch7.0.3_Beta
- { 0xE2854509C471C554ULL, { 0x43, 0x32, 0x65, 0xF0, 0xCD, 0xEB, 0x2F, 0x4E, 0x65, 0xC0, 0xEE, 0x70, 0x08, 0x71, 0x4D, 0x9E } }, // 52 WOW-21249patch7.0.3_Beta Warcraft movie items
- { 0x8EE2CB82178C995AULL, { 0xDA, 0x6A, 0xFC, 0x98, 0x9E, 0xD6, 0xCA, 0xD2, 0x79, 0x88, 0x59, 0x92, 0xC0, 0x37, 0xA8, 0xEE } }, // 55 WOW-21531patch7.0.3_Beta BlizzCon 2016 Murlocs
- { 0x5813810F4EC9B005ULL, { 0x01, 0xBE, 0x8B, 0x43, 0x14, 0x2D, 0xD9, 0x9A, 0x9E, 0x69, 0x0F, 0xAD, 0x28, 0x8B, 0x60, 0x82 } }, // 56 WOW-21531patch7.0.3_Beta Fel Kitten
- { 0x7F9E217166ED43EAULL, { 0x05, 0xFC, 0x92, 0x7B, 0x9F, 0x4F, 0x5B, 0x05, 0x56, 0x81, 0x42, 0x91, 0x2A, 0x05, 0x2B, 0x0F } }, // 57 WOW-21531patch7.0.3_Beta Legion music
- { 0xC4A8D364D23793F7ULL, { 0xD1, 0xAC, 0x20, 0xFD, 0x14, 0x95, 0x7F, 0xAB, 0xC2, 0x71, 0x96, 0xE9, 0xF6, 0xE7, 0x02, 0x4A } }, // 58 WOW-21691patch7.0.3_Beta Demon Hunter #1 cinematic (legion_dh1)
- { 0x40A234AEBCF2C6E5ULL, { 0xC6, 0xC5, 0xF6, 0xC7, 0xF7, 0x35, 0xD7, 0xD9, 0x4C, 0x87, 0x26, 0x7F, 0xA4, 0x99, 0x4D, 0x45 } }, // 59 WOW-21691patch7.0.3_Beta Demon Hunter #2 cinematic (legion_dh2)
- { 0x9CF7DFCFCBCE4AE5ULL, { 0x72, 0xA9, 0x7A, 0x24, 0xA9, 0x98, 0xE3, 0xA5, 0x50, 0x0F, 0x38, 0x71, 0xF3, 0x76, 0x28, 0xC0 } }, // 60 WOW-21691patch7.0.3_Beta Val'sharah #1 cinematic (legion_val_yd)
- { 0x4E4BDECAB8485B4FULL, { 0x38, 0x32, 0xD7, 0xC4, 0x2A, 0xAC, 0x92, 0x68, 0xF0, 0x0B, 0xE7, 0xB6, 0xB4, 0x8E, 0xC9, 0xAF } }, // 61 WOW-21691patch7.0.3_Beta Val'sharah #2 cinematic (legion_val_yx)
- { 0x94A50AC54EFF70E4ULL, { 0xC2, 0x50, 0x1A, 0x72, 0x65, 0x4B, 0x96, 0xF8, 0x63, 0x50, 0xC5, 0xA9, 0x27, 0x96, 0x2F, 0x7A } }, // 62 WOW-21691patch7.0.3_Beta Sylvanas warchief cinematic (legion_org_vs)
- { 0xBA973B0E01DE1C2CULL, { 0xD8, 0x3B, 0xBC, 0xB4, 0x6C, 0xC4, 0x38, 0xB1, 0x7A, 0x48, 0xE7, 0x6C, 0x4F, 0x56, 0x54, 0xA3 } }, // 63 WOW-21691patch7.0.3_Beta Stormheim Sylvanas vs Greymane cinematic (legion_sth)
- { 0x494A6F8E8E108BEFULL, { 0xF0, 0xFD, 0xE1, 0xD2, 0x9B, 0x27, 0x4F, 0x6E, 0x7D, 0xBD, 0xB7, 0xFF, 0x81, 0x5F, 0xE9, 0x10 } }, // 64 WOW-21691patch7.0.3_Beta Harbingers Gul'dan video (legion_hrb_g)
- { 0x918D6DD0C3849002ULL, { 0x85, 0x70, 0x90, 0xD9, 0x26, 0xBB, 0x28, 0xAE, 0xDA, 0x4B, 0xF0, 0x28, 0xCA, 0xCC, 0x4B, 0xA3 } }, // 65 WOW-21691patch7.0.3_Beta Harbingers Khadgar video (legion_hrb_k)
- { 0x0B5F6957915ADDCAULL, { 0x4D, 0xD0, 0xDC, 0x82, 0xB1, 0x01, 0xC8, 0x0A, 0xBA, 0xC0, 0xA4, 0xD5, 0x7E, 0x67, 0xF8, 0x59 } }, // 66 WOW-21691patch7.0.3_Beta Harbingers Illidan video (legion_hrb_i)
- { 0x794F25C6CD8AB62BULL, { 0x76, 0x58, 0x3B, 0xDA, 0xCD, 0x52, 0x57, 0xA3, 0xF7, 0x3D, 0x15, 0x98, 0xA2, 0xCA, 0x2D, 0x99 } }, // 67 WOW-21846patch7.0.3_Beta Suramar cinematic (legion_su_i)
- { 0xA9633A54C1673D21ULL, { 0x1F, 0x8D, 0x46, 0x7F, 0x5D, 0x6D, 0x41, 0x1F, 0x8A, 0x54, 0x8B, 0x63, 0x29, 0xA5, 0x08, 0x7E } }, // 68 WOW-21846patch7.0.3_Beta legion_su_r cinematic
- { 0x5E5D896B3E163DEAULL, { 0x8A, 0xCE, 0x8D, 0xB1, 0x69, 0xE2, 0xF9, 0x8A, 0xC3, 0x6A, 0xD5, 0x2C, 0x08, 0x8E, 0x77, 0xC1 } }, // 69 WOW-21846patch7.0.3_Beta Broken Shore intro cinematic (legion_bs_i)
- { 0x0EBE36B5010DFD7FULL, { 0x9A, 0x89, 0xCC, 0x7E, 0x3A, 0xCB, 0x29, 0xCF, 0x14, 0xC6, 0x0B, 0xC1, 0x3B, 0x1E, 0x46, 0x16 } }, // 70 WOW-21846patch7.0.3_Beta Alliance Broken Shore cinematic (legion_bs_a)
- { 0x01E828CFFA450C0FULL, { 0x97, 0x2B, 0x6E, 0x74, 0x42, 0x0E, 0xC5, 0x19, 0xE6, 0xF9, 0xD9, 0x7D, 0x59, 0x4A, 0xA3, 0x7C } }, // 71 WOW-21846patch7.0.3_Beta Horde Broken Shore cinematic (legion_bs_h)
- { 0x4A7BD170FE18E6AEULL, { 0xAB, 0x55, 0xAE, 0x1B, 0xF0, 0xC7, 0xC5, 0x19, 0xAF, 0xF0, 0x28, 0xC1, 0x56, 0x10, 0xA4, 0x5B } }, // 72 WOW-21846patch7.0.3_Beta Khadgar & Light's Heart cinematic (legion_iq_lv)
- { 0x69549CB975E87C4FULL, { 0x7B, 0x6F, 0xA3, 0x82, 0xE1, 0xFA, 0xD1, 0x46, 0x5C, 0x85, 0x1E, 0x3F, 0x47, 0x34, 0xA1, 0xB3 } }, // 73 WOW-21846patch7.0.3_Beta legion_iq_id cinematic
- { 0x460C92C372B2A166ULL, { 0x94, 0x6D, 0x56, 0x59, 0xF2, 0xFA, 0xF3, 0x27, 0xC0, 0xB7, 0xEC, 0x82, 0x8B, 0x74, 0x8A, 0xDB } }, // 74 WOW-21952patch7.0.3_Beta Stormheim Alliance cinematic (legion_g_a_sth)
- { 0x8165D801CCA11962ULL, { 0xCD, 0x0C, 0x0F, 0xFA, 0xAD, 0x93, 0x63, 0xEC, 0x14, 0xDD, 0x25, 0xEC, 0xDD, 0x2A, 0x5B, 0x62 } }, // 75 WOW-21952patch7.0.3_Beta Stormheim Horde cinematic (legion_g_h_sth)
- { 0xA3F1C999090ADAC9ULL, { 0xB7, 0x2F, 0xEF, 0x4A, 0x01, 0x48, 0x8A, 0x88, 0xFF, 0x02, 0x28, 0x0A, 0xA0, 0x7A, 0x92, 0xBB } }, // 81 WOW-22578patch7.1.0_PTR Firecat Mount
- // {0x18AFDF5191923610ULL, {0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 82 WOW-22578patch7.1.0_PTR
- // {0x3C258426058FBD93ULL, {0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 91 WOW-23436patch7.2.0_PTR
- { 0x094E9A0474876B98ULL, { 0xE5, 0x33, 0xBB, 0x6D, 0x65, 0x72, 0x7A, 0x58, 0x32, 0x68, 0x0D, 0x62, 0x0B, 0x0B, 0xC1, 0x0B } }, // 92 WOW-23910patch7.2.5_PTR
- { 0x3DB25CB86A40335EULL, { 0x02, 0x99, 0x0B, 0x12, 0x26, 0x0C, 0x1E, 0x9F, 0xDD, 0x73, 0xFE, 0x47, 0xCB, 0xAB, 0x70, 0x24 } }, // 93 WOW-23789patch7.2.0_PTR
- { 0x0DCD81945F4B4686ULL, { 0x1B, 0x78, 0x9B, 0x87, 0xFB, 0x3C, 0x92, 0x38, 0xD5, 0x28, 0x99, 0x7B, 0xFA, 0xB4, 0x41, 0x86 } }, // 94 WOW-23789patch7.2.0_PTR
- { 0x486A2A3A2803BE89ULL, { 0x32, 0x67, 0x9E, 0xA7, 0xB0, 0xF9, 0x9E, 0xBF, 0x4F, 0xA1, 0x70, 0xE8, 0x47, 0xEA, 0x43, 0x9A } }, // 95 WOW-23789patch7.2.0_PTR
- { 0x71F69446AD848E06ULL, { 0xE7, 0x9A, 0xEB, 0x88, 0xB1, 0x50, 0x9F, 0x62, 0x8F, 0x38, 0x20, 0x82, 0x01, 0x74, 0x1C, 0x30 } }, // 97 WOW-24473patch7.3.0_PTR BlizzCon 2017 Mounts (AllianceShipMount and HordeZeppelinMount)
- { 0x211FCD1265A928E9ULL, { 0xA7, 0x36, 0xFB, 0xF5, 0x8D, 0x58, 0x7B, 0x39, 0x72, 0xCE, 0x15, 0x4A, 0x86, 0xAE, 0x45, 0x40 } }, // 98 WOW-24473patch7.3.0_PTR "Shadow" fox pet (store)
- { 0x0ADC9E327E42E98CULL, { 0x01, 0x7B, 0x34, 0x72, 0xC1, 0xDE, 0xE3, 0x04, 0xFA, 0x0B, 0x2F, 0xF8, 0xE5, 0x3F, 0xF7, 0xD6 } }, // 99 WOW-23910patch7.2.5_PTR
- { 0xBAE9F621B60174F1ULL, { 0x38, 0xC3, 0xFB, 0x39, 0xB4, 0x97, 0x17, 0x60, 0xB4, 0xB9, 0x82, 0xFE, 0x9F, 0x09, 0x50, 0x14 } }, // 100 WOW-24473patch7.3.0_PTR Rejection of the Gift cinematic (legion_73_agi)
- { 0x34DE1EEADC97115EULL, { 0x2E, 0x3A, 0x53, 0xD5, 0x9A, 0x49, 0x1E, 0x5C, 0xD1, 0x73, 0xF3, 0x37, 0xF7, 0xCD, 0x8C, 0x61 } }, // 101 WOW-24473patch7.3.0_PTR Resurrection of Alleria Windrunner cinematic (legion_73_avt)
- // { 0xE07E107F1390A3DFULL, {0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 102 WOW-25079patch7.3.2_PTR Tottle battle pet, Raptor mount, Horse mount (104 files)
- { 0x32690BF74DE12530ULL, { 0xA2, 0x55, 0x62, 0x10, 0xAE, 0x54, 0x22, 0xE6, 0xD6, 0x1E, 0xDA, 0xAF, 0x12, 0x2C, 0xB6, 0x37 } }, // 103 WOW-24781patch7.3.0_PTR legion_73_pan
- { 0xBF3734B1DCB04696ULL, { 0x48, 0x94, 0x61, 0x23, 0x05, 0x0B, 0x00, 0xA7, 0xEF, 0xB1, 0xC0, 0x29, 0xEE, 0x6C, 0xC4, 0x38 } }, // 104 WOW-25079patch7.3.2_PTR legion_73_afn
- { 0x74F4F78002A5A1BEULL, { 0xC1, 0x4E, 0xEC, 0x8D, 0x5A, 0xEE, 0xF9, 0x3F, 0xA8, 0x11, 0xD4, 0x50, 0xB4, 0xE4, 0x6E, 0x91 } }, // 105 WOW-25079patch7.3.2_PTR SilithusPhase01 map
- // { 0x423F07656CA27D23ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // WOW-25600patch7.3.5_PTR
- // { 0x0691678F83E8A75DULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // WOW-25600patch7.3.5_PTR
- // { 0xC02C78F40BEF5998ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // WOW-25600patch7.3.5_PTR
- // { 0x324498590F550556ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // WOW-25600patch7.3.5_PTR
- // { 0x8E00C6F405873583ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // WOW-25600patch7.3.5_PTR
- // { 0x78482170E4CFD4A6ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // WOW-25600patch7.3.5_PTR
- // { 0xB1EB52A64BFAF7BFULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // WOW-25600patch7.3.5_PTR
- { 0, { 0 } }
+ // Key Name Encryption key Seen in
+ // ---------------------- ------------------------------------------------------------------------------------------------ -----------
+
+ // Battle.net app
+ { 0x2C547F26A2613E01ULL, { 0x37, 0xC5, 0x0C, 0x10, 0x2D, 0x4C, 0x9E, 0x3A, 0x5A, 0xC0, 0x69, 0xF0, 0x72, 0xB1, 0x41, 0x7D } }, // Battle.net App Alpha 1.5.0
+
+ // Starcraft
+// { 0xD0CAE11366CEEA83ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? }}, // 1.12.3.2609 (build 45364)
+
+ // Overwatch
+ { 0xFB680CB6A8BF81F3ULL, { 0x62, 0xD9, 0x0E, 0xFA, 0x7F, 0x36, 0xD7, 0x1C, 0x39, 0x8A, 0xE2, 0xF1, 0xFE, 0x37, 0xBD, 0xB9 } }, // 0.8.0.24919_retailx64 (hardcoded)
+ { 0x402CD9D8D6BFED98ULL, { 0xAE, 0xB0, 0xEA, 0xDE, 0xA4, 0x76, 0x12, 0xFE, 0x6C, 0x04, 0x1A, 0x03, 0x95, 0x8D, 0xF2, 0x41 } }, // 0.8.0.24919_retailx64 (hardcoded)
+ { 0xDBD3371554F60306ULL, { 0x34, 0xE3, 0x97, 0xAC, 0xE6, 0xDD, 0x30, 0xEE, 0xFD, 0xC9, 0x8A, 0x2A, 0xB0, 0x93, 0xCD, 0x3C } }, // 0.8.0.24919_retailx64 (streamed from server)
+ { 0x11A9203C9881710AULL, { 0x2E, 0x2C, 0xB8, 0xC3, 0x97, 0xC2, 0xF2, 0x4E, 0xD0, 0xB5, 0xE4, 0x52, 0xF1, 0x8D, 0xC2, 0x67 } }, // 0.8.0.24919_retailx64 (streamed from server)
+ { 0xA19C4F859F6EFA54ULL, { 0x01, 0x96, 0xCB, 0x6F, 0x5E, 0xCB, 0xAD, 0x7C, 0xB5, 0x28, 0x38, 0x91, 0xB9, 0x71, 0x2B, 0x4B } }, // 0.8.0.24919_retailx64 (streamed from server)
+ { 0x87AEBBC9C4E6B601ULL, { 0x68, 0x5E, 0x86, 0xC6, 0x06, 0x3D, 0xFD, 0xA6, 0xC9, 0xE8, 0x52, 0x98, 0x07, 0x6B, 0x3D, 0x42 } }, // 0.8.0.24919_retailx64 (streamed from server)
+ { 0xDEE3A0521EFF6F03ULL, { 0xAD, 0x74, 0x0C, 0xE3, 0xFF, 0xFF, 0x92, 0x31, 0x46, 0x81, 0x26, 0x98, 0x57, 0x08, 0xE1, 0xB9 } }, // 0.8.0.24919_retailx64 (streamed from server)
+ { 0x8C9106108AA84F07ULL, { 0x53, 0xD8, 0x59, 0xDD, 0xA2, 0x63, 0x5A, 0x38, 0xDC, 0x32, 0xE7, 0x2B, 0x11, 0xB3, 0x2F, 0x29 } }, // 0.8.0.24919_retailx64 (streamed from server)
+ { 0x49166D358A34D815ULL, { 0x66, 0x78, 0x68, 0xCD, 0x94, 0xEA, 0x01, 0x35, 0xB9, 0xB1, 0x6C, 0x93, 0xB1, 0x12, 0x4A, 0xBA } }, // 0.8.0.24919_retailx64 (streamed from server)
+ { 0x1463A87356778D14ULL, { 0x69, 0xBD, 0x2A, 0x78, 0xD0, 0x5C, 0x50, 0x3E, 0x93, 0x99, 0x49, 0x59, 0xB3, 0x0E, 0x5A, 0xEC } }, // ? 1.0.3.0 (streamed from server)
+ { 0x5E152DE44DFBEE01ULL, { 0xE4, 0x5A, 0x17, 0x93, 0xB3, 0x7E, 0xE3, 0x1A, 0x8E, 0xB8, 0x5C, 0xEE, 0x0E, 0xEE, 0x1B, 0x68 } }, // ? 1.0.3.0 (streamed from server)
+ { 0x9B1F39EE592CA415ULL, { 0x54, 0xA9, 0x9F, 0x08, 0x1C, 0xAD, 0x0D, 0x08, 0xF7, 0xE3, 0x36, 0xF4, 0x36, 0x8E, 0x89, 0x4C } }, // ? 1.0.3.0 (streamed from server)
+ { 0x24C8B75890AD5917ULL, { 0x31, 0x10, 0x0C, 0x00, 0xFD, 0xE0, 0xCE, 0x18, 0xBB, 0xB3, 0x3F, 0x3A, 0xC1, 0x5B, 0x30, 0x9F } }, // ? 1.0.3.0 (included in game)
+ { 0xEA658B75FDD4890FULL, { 0xDE, 0xC7, 0xA4, 0xE7, 0x21, 0xF4, 0x25, 0xD1, 0x33, 0x03, 0x98, 0x95, 0xC3, 0x60, 0x36, 0xF8 } }, // ? 1.0.3.0 (included in game)
+ { 0x026FDCDF8C5C7105ULL, { 0x8F, 0x41, 0x80, 0x9D, 0xA5, 0x53, 0x66, 0xAD, 0x41, 0x6D, 0x3C, 0x33, 0x74, 0x59, 0xEE, 0xE3 } }, // (included in game)
+ { 0xCAE3FAC925F20402ULL, { 0x98, 0xB7, 0x8E, 0x87, 0x74, 0xBF, 0x27, 0x50, 0x93, 0xCB, 0x1B, 0x5F, 0xC7, 0x14, 0x51, 0x1B } }, // (included in game)
+ { 0x061581CA8496C80CULL, { 0xDA, 0x2E, 0xF5, 0x05, 0x2D, 0xB9, 0x17, 0x38, 0x0B, 0x8A, 0xA6, 0xEF, 0x7A, 0x5F, 0x8E, 0x6A } }, //
+ { 0xBE2CB0FAD3698123ULL, { 0x90, 0x2A, 0x12, 0x85, 0x83, 0x6C, 0xE6, 0xDA, 0x58, 0x95, 0x02, 0x0D, 0xD6, 0x03, 0xB0, 0x65 } }, //
+ { 0x57A5A33B226B8E0AULL, { 0xFD, 0xFC, 0x35, 0xC9, 0x9B, 0x9D, 0xB1, 0x1A, 0x32, 0x62, 0x60, 0xCA, 0x24, 0x6A, 0xCB, 0x41 } }, // 1.1.0.0.30200 Ana
+ { 0x42B9AB1AF5015920ULL, { 0xC6, 0x87, 0x78, 0x82, 0x3C, 0x96, 0x4C, 0x6F, 0x24, 0x7A, 0xCC, 0x0F, 0x4A, 0x25, 0x84, 0xF8 } }, // 1.2.0.1.30684 Summer Games
+ { 0x4F0FE18E9FA1AC1AULL, { 0x89, 0x38, 0x1C, 0x74, 0x8F, 0x65, 0x31, 0xBB, 0xFC, 0xD9, 0x77, 0x53, 0xD0, 0x6C, 0xC3, 0xCD } }, // 1.2.0.1.30684
+ { 0x7758B2CF1E4E3E1BULL, { 0x3D, 0xE6, 0x0D, 0x37, 0xC6, 0x64, 0x72, 0x35, 0x95, 0xF2, 0x7C, 0x5C, 0xDB, 0xF0, 0x8B, 0xFA } }, // 1.2.0.1.30684
+ { 0xE5317801B3561125ULL, { 0x7D, 0xD0, 0x51, 0x19, 0x9F, 0x84, 0x01, 0xF9, 0x5E, 0x4C, 0x03, 0xC8, 0x84, 0xDC, 0xEA, 0x33 } }, // 1.4.0.2.32143 Halloween Terror
+ { 0x16B866D7BA3A8036ULL, { 0x13, 0x95, 0xE8, 0x82, 0xBF, 0x25, 0xB4, 0x81, 0xF6, 0x1A, 0x4D, 0x62, 0x11, 0x41, 0xDA, 0x6E } }, // 1.4.1.0.31804 Bastion Blizzcon 2016 skin
+ { 0x11131FFDA0D18D30ULL, { 0xC3, 0x2A, 0xD1, 0xB8, 0x25, 0x28, 0xE0, 0xA4, 0x56, 0x89, 0x7B, 0x3C, 0xE1, 0xC2, 0xD2, 0x7E } }, // 1.5.0.1.32795 Sombra
+ { 0xCAC6B95B2724144AULL, { 0x73, 0xE4, 0xBE, 0xA1, 0x45, 0xDF, 0x2B, 0x89, 0xB6, 0x5A, 0xEF, 0x02, 0xF8, 0x3F, 0xA2, 0x60 } }, // 1.5.0.1.32795 Ecopoint: Antarctica
+ { 0xB7DBC693758A5C36ULL, { 0xBC, 0x3A, 0x92, 0xBF, 0xE3, 0x02, 0x51, 0x8D, 0x91, 0xCC, 0x30, 0x79, 0x06, 0x71, 0xBF, 0x10 } }, // 1.5.0.1.32795 Genji Oni skin
+ { 0x90CA73B2CDE3164BULL, { 0x5C, 0xBF, 0xF1, 0x1F, 0x22, 0x72, 0x0B, 0xAC, 0xC2, 0xAE, 0x6A, 0xAD, 0x8F, 0xE5, 0x33, 0x17 } }, // 1.6.1.0.33236 Oasis map
+ { 0x6DD3212FB942714AULL, { 0xE0, 0x2C, 0x16, 0x43, 0x60, 0x2E, 0xC1, 0x6C, 0x3A, 0xE2, 0xA4, 0xD2, 0x54, 0xA0, 0x8F, 0xD9 } }, // 1.6.1.0.33236
+ { 0x11DDB470ABCBA130ULL, { 0x66, 0x19, 0x87, 0x66, 0xB1, 0xC4, 0xAF, 0x75, 0x89, 0xEF, 0xD1, 0x3A, 0xD4, 0xDD, 0x66, 0x7A } }, // 1.6.1.0.33236 Winter Wonderland
+ { 0x5BEF27EEE95E0B4BULL, { 0x36, 0xBC, 0xD2, 0xB5, 0x51, 0xFF, 0x1C, 0x84, 0xAA, 0x3A, 0x39, 0x94, 0xCC, 0xEB, 0x03, 0x3E } }, //
+ { 0x9359B46E49D2DA42ULL, { 0x17, 0x3D, 0x65, 0xE7, 0xFC, 0xAE, 0x29, 0x8A, 0x93, 0x63, 0xBD, 0x6A, 0xA1, 0x89, 0xF2, 0x00 } }, // Diablo's 20th anniversary
+ { 0x1A46302EF8896F34ULL, { 0x80, 0x29, 0xAD, 0x54, 0x51, 0xD4, 0xBC, 0x18, 0xE9, 0xD0, 0xF5, 0xAC, 0x44, 0x9D, 0xC0, 0x55 } }, // 1.7.0.2.34156 Year of the Rooster
+ { 0x693529F7D40A064CULL, { 0xCE, 0x54, 0x87, 0x3C, 0x62, 0xDA, 0xA4, 0x8E, 0xFF, 0x27, 0xFC, 0xC0, 0x32, 0xBD, 0x07, 0xE3 } }, // 1.8.0.0.34470 CTF Maps
+ { 0x388B85AEEDCB685DULL, { 0xD9, 0x26, 0xE6, 0x59, 0xD0, 0x4A, 0x09, 0x6B, 0x24, 0xC1, 0x91, 0x51, 0x07, 0x6D, 0x37, 0x9A } }, // 1.8.0.0.34470 Numbani Update (Doomfist teaser)
+ { 0xE218F69AAC6C104DULL, { 0xF4, 0x3D, 0x12, 0xC9, 0x4A, 0x9A, 0x52, 0x84, 0x97, 0x97, 0x1F, 0x1C, 0xBE, 0x41, 0xAD, 0x4D } }, // 1.9.0.0.34986 Orisa
+ { 0xF432F0425363F250ULL, { 0xBA, 0x69, 0xF2, 0xB3, 0x3C, 0x27, 0x68, 0xF5, 0xF2, 0x9B, 0xFE, 0x78, 0xA5, 0xA1, 0xFA, 0xD5 } }, // 1.10.0.0.35455 Uprising
+ { 0x061D52F86830B35DULL, { 0xD7, 0x79, 0xF9, 0xC6, 0xCC, 0x9A, 0x4B, 0xE1, 0x03, 0xA4, 0xE9, 0x0A, 0x73, 0x38, 0xF7, 0x93 } }, // 1.10.0.0.35455 D.Va Officer Skin (HotS Nexus Challenge 2)
+ { 0x1275C84CF113EF65ULL, { 0xCF, 0x58, 0xB6, 0x93, 0x3E, 0xAF, 0x98, 0xAF, 0x53, 0xE7, 0x6F, 0x84, 0x26, 0xCC, 0x7E, 0x6C } }, //
+ { 0xD9C7C7AC0F14C868ULL, { 0x3A, 0xFD, 0xF6, 0x8E, 0x3A, 0x5D, 0x63, 0xBA, 0xBA, 0x1E, 0x68, 0x21, 0x88, 0x3F, 0x06, 0x7D } }, //
+ { 0xBD4E42661A432951ULL, { 0x6D, 0xE8, 0xE2, 0x8C, 0x85, 0x11, 0x64, 0x4D, 0x55, 0x95, 0xFC, 0x45, 0xE5, 0x35, 0x14, 0x72 } }, // 1.11.0.0.36376 Anniversary event
+ { 0xC43CB14355249451ULL, { 0x0E, 0xA2, 0xB4, 0x4F, 0x96, 0xA2, 0x69, 0xA3, 0x86, 0x85, 0x6D, 0x04, 0x9A, 0x3D, 0xEC, 0x86 } }, // 1.12.0.0.37104 Horizon Lunar Colony
+ { 0xE6D914F8E4744953ULL, { 0xC8, 0x47, 0x7C, 0x28, 0x9D, 0xCE, 0x66, 0xD9, 0x13, 0x65, 0x07, 0xA3, 0x3A, 0xA3, 0x33, 0x01 } }, // 1.13.0.0.37646 Doomfist
+ { 0x5694C503F8C80178ULL, { 0x7F, 0x4C, 0xF1, 0xC1, 0xFB, 0xBA, 0xD9, 0x2B, 0x18, 0x43, 0x36, 0xD6, 0x77, 0xEB, 0xF9, 0x37 } }, // 1.13.0.0.37646 Doomfist
+ { 0x21DBFD65F3E54269ULL, { 0xAB, 0x58, 0x0C, 0x38, 0x37, 0xCA, 0xF8, 0xA4, 0x61, 0xF2, 0x43, 0xA5, 0x66, 0xB2, 0xAE, 0x4D } }, // 1.13.0.0.37646 Summer Games 2017
+// { 0x27ABA5F88DD8D078ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 1.13.0.0.37646
+ { 0x21E1F90E71D33C71ULL, { 0x32, 0x87, 0x42, 0x33, 0x91, 0x62, 0xB3, 0x26, 0x76, 0xC8, 0x03, 0xC2, 0x25, 0x59, 0x31, 0xA6 } }, // 1.14.1.0.39083 Deathmatch
+ { 0xD9CB055BCDD40B6EULL, { 0x49, 0xFB, 0x44, 0x77, 0xA4, 0xA0, 0x82, 0x53, 0x27, 0xE9, 0xA7, 0x36, 0x82, 0xBE, 0xCD, 0x0C } }, // 1.15.0.0.????? Junkertown
+ { 0x8175CE3C694C6659ULL, { 0xE3, 0xF3, 0xFA, 0x77, 0x26, 0xC7, 0x0D, 0x26, 0xAE, 0x13, 0x0D, 0x96, 0x9D, 0xDD, 0xF3, 0x99 } }, // 1.16.0.0.40011 Halloween 2017
+ { 0xB8DE51690075435AULL, { 0xC0, 0x7E, 0x92, 0x60, 0xBB, 0x71, 0x12, 0x17, 0xE7, 0xDE, 0x6F, 0xED, 0x91, 0x1F, 0x42, 0x96 } }, // 1.16.0.0.????? Winston Blizzcon 2017 skin
+ { 0xF6CF23955B5D437DULL, { 0xAE, 0xBA, 0x22, 0x73, 0x28, 0xA5, 0xB0, 0xAA, 0x9F, 0x51, 0xDA, 0xE3, 0xF6, 0xA7, 0xDF, 0xE4 } }, // 1.17.0.2.41350 Moira
+ { 0x0E4D9426F2891F5CULL, { 0x9F, 0xF0, 0x64, 0xC3, 0x8B, 0xE5, 0x2C, 0xCD, 0xF7, 0x37, 0x48, 0x18, 0x0F, 0x62, 0x82, 0x05 } }, // 1.18.1.2.42076 Winter Wonderland 2017
+ { 0x9240BA6A2A0CF684ULL, { 0xDF, 0x2E, 0x37, 0xD7, 0x8B, 0x43, 0x10, 0x8F, 0xA6, 0x24, 0x20, 0x68, 0xB7, 0x0D, 0x1F, 0x65 } }, // 1.19.1.3.42563 Overwatch League
+ { 0x82297FBAB7F5EB80ULL, { 0xB5, 0x34, 0xC2, 0x09, 0x65, 0x85, 0x2F, 0xB1, 0x5A, 0xEC, 0xAC, 0x17, 0xE3, 0x81, 0xB4, 0x17 } }, // 1.19.1.3.42563 Jan 2017 Lootbox Update
+ { 0x9ADF00AA1A174A69ULL, { 0x9A, 0x4A, 0xC8, 0x99, 0x26, 0x1A, 0x2F, 0x1C, 0x69, 0x69, 0xF3, 0x93, 0x97, 0xC3, 0x58, 0xE7 } }, // 1.19.1.3.42563 Blizzard World
+ { 0xCFA05AA76B49F881ULL, { 0x52, 0x6D, 0xDD, 0xEF, 0x19, 0xBF, 0x37, 0x3C, 0x25, 0xB6, 0x29, 0xA3, 0x34, 0xCD, 0x72, 0x37 } }, // 1.19.1.3.42563 WoW's Battle For Azeroth Preorder
+ { 0x493455579DA0B18EULL, { 0xC0, 0xBA, 0xBF, 0x72, 0xAD, 0x2C, 0x05, 0xDF, 0xC1, 0x40, 0x17, 0xD1, 0xAD, 0xBF, 0x59, 0x77 } }, // 1.19.3.1.43036 Inaugural Season Spray/Icon ??
+ { 0x6362C5AD65DAE686ULL, { 0x62, 0xF6, 0x03, 0xD5, 0x39, 0x0F, 0x76, 0x3E, 0xD5, 0x17, 0x73, 0xF0, 0x16, 0x4F, 0xED, 0xB5 } }, // 1.19.3.1.43036 White/Gray OWL Skins ??
+ { 0x8162E5313A9C135DULL, { 0xF4, 0x07, 0x83, 0x4D, 0x95, 0x21, 0x58, 0x7C, 0x50, 0x12, 0xB0, 0xA5, 0x9D, 0x7E, 0x06, 0x4B } }, // 1.20.0.2.43435 Lunar New Year 2018 (Dog) / Ayutthaya / Comp CTF
+// { 0x68EAE8FDC008C381ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 1.20.0.2.43435
+ { 0xF412C6327C4BF091ULL, { 0x6F, 0xAF, 0xC6, 0x48, 0xCB, 0xF1, 0xC2, 0x11, 0x5B, 0x76, 0x95, 0x93, 0xC1, 0x70, 0xE7, 0x32 } }, // 1.20.0.2.43435 SC2 20th Anniversary (Kerrigan Skin)
+ { 0x3B3ED0874091B174ULL, { 0x5D, 0x09, 0xC2, 0x68, 0x8B, 0x1D, 0x9F, 0x1A, 0x4D, 0xB6, 0x46, 0x02, 0xC1, 0x66, 0x1D, 0x24 } }, // 1.21.?.?.????? Brigitte
+ { 0x37FD04E05D2A6292ULL, { 0xF0, 0x64, 0x55, 0xE5, 0x6C, 0xD1, 0x44, 0x91, 0x42, 0x95, 0xF2, 0xEF, 0x15, 0x3D, 0x23, 0xBA } }, // 1.21.?.?.????? Brigitte Cosmetics
+ { 0xC0DDC77552BE5794ULL, { 0xF2, 0xBB, 0x7F, 0x35, 0x99, 0x0E, 0x29, 0x00, 0xCB, 0xD8, 0x77, 0xB4, 0xD3, 0xA7, 0x13, 0x9C } }, // 1.22.?.?.????? OWL away skins
+ { 0x68D8EB839DC15D75ULL, { 0x29, 0xAF, 0xFE, 0xCA, 0x52, 0x99, 0xC4, 0x14, 0x0A, 0x12, 0xA6, 0x6F, 0x95, 0x4E, 0xF1, 0xE3 } }, // 1.22.?.?.????? Archives 2018 (Retribution)
+ { 0x209F33BBAC9D1295ULL, { 0xBD, 0x53, 0x54, 0x38, 0xD0, 0xCD, 0xEE, 0x0E, 0x95, 0x67, 0xE0, 0xEF, 0x67, 0x1C, 0x80, 0x9F } }, // 1.23.?.?.????? Rialto
+ { 0xA55F8C6F20454D94ULL, { 0x42, 0xD7, 0x62, 0x85, 0x41, 0x24, 0x61, 0xB0, 0xC7, 0x5A, 0xB7, 0x5F, 0xB5, 0x2F, 0x59, 0x6E } }, // 1.23.?.?.????? Mercy BCRF Items
+ { 0x3EEEDB8E7C29A09BULL, { 0x83, 0x7A, 0xC7, 0x93, 0x05, 0xE4, 0xBF, 0xBA, 0x3B, 0x22, 0x26, 0xF1, 0xB9, 0x82, 0x00, 0xBF } }, // 1.24.?.?.????? Anniversary 2018 Map/Items
+ { 0x22C1AF6758F8449EULL, { 0x28, 0xAA, 0x1B, 0xD2, 0xB9, 0xA1, 0xE3, 0x63, 0x39, 0x89, 0xB1, 0xBB, 0xF6, 0x4A, 0xEA, 0xC4 } }, // 1.26.?.?.????? Emily / Comp Season 11 / Comp 3v3 Items
+ { 0x11B2E01B9331799EULL, { 0x9A, 0x1C, 0x99, 0x30, 0x3D, 0x6A, 0x58, 0x97, 0x8E, 0x29, 0xC9, 0x88, 0x73, 0x32, 0x7A, 0x6A } }, // 1.26.?.?.????? Wrecking Ball
+ { 0x8498337C740329B3ULL, { 0xEC, 0x73, 0xD6, 0x63, 0xE3, 0xFC, 0x72, 0x41, 0x6B, 0x3E, 0x46, 0x79, 0x15, 0x70, 0x8B, 0x4F } }, // 1.26.?.?.????? Wrecking Ball Cosmetics
+ { 0xE6E8BCABE3CC96C1ULL, { 0x57, 0x72, 0x7E, 0x52, 0x60, 0x06, 0x65, 0xEA, 0xC0, 0x2B, 0xD8, 0x7F, 0x06, 0x92, 0x89, 0x8F } }, // 1.26.?.?.????? OWL Grand Finals Unlocks / Lucio Emote
+ { 0x5D4AC0DC6F3113BAULL, { 0xD1, 0xC9, 0xF1, 0xFF, 0x69, 0x58, 0x5D, 0x13, 0x98, 0xEB, 0xA0, 0x46, 0x34, 0x81, 0xF5, 0xCF } }, // 1.27.?.?.????? Summer Games 2018
+ { 0x27F9D85973DCD5AFULL, { 0xC5, 0xFE, 0x10, 0x15, 0xBC, 0xE0, 0xB7, 0x84, 0x8F, 0x02, 0x28, 0x68, 0xAF, 0xF6, 0x54, 0xD1 } }, // 1.27.?.?.????? Summer Games 2018
+ { 0x67AAD845CC0F03BDULL, { 0x6C, 0xD8, 0xAD, 0x3F, 0x37, 0xF5, 0x4A, 0xBE, 0xC7, 0x63, 0x02, 0x94, 0xD4, 0x90, 0x41, 0xBF } }, // 1.27.?.?.????? D.Va Nano Cola Challenge
+ { 0xCA13F0C79042A1A0ULL, { 0xFC, 0xD8, 0x9C, 0xE8, 0x81, 0x2E, 0x63, 0x46, 0x07, 0x6F, 0xC8, 0x2D, 0xD7, 0xA9, 0x24, 0x87 } }, // 1.28.?.?.????? Busan
+ { 0xC4D84093A32684BDULL, { 0x38, 0xE1, 0x82, 0x42, 0x3E, 0xED, 0xD8, 0xE3, 0xF5, 0x7A, 0xC1, 0xD4, 0x07, 0xB4, 0x70, 0xD0 } }, // 1.28.?.?.????? Blizzcon 2018 Sombra Demon Hunter Skin
+ { 0x0BFE5A2B3C606BA1ULL, { 0xD6, 0x41, 0x9B, 0x8E, 0x42, 0x82, 0x0B, 0x0B, 0x24, 0xE0, 0x8D, 0xA4, 0x44, 0xA0, 0x68, 0x22 } }, // 1.29.?.?.????? Halloween Terror 2018
+ { 0x402CD9D8D6BFED98ULL, { 0xAE, 0xB0, 0xEA, 0xDE, 0xA4, 0x76, 0x12, 0xFE, 0x6C, 0x04, 0x1A, 0x03, 0x95, 0x8D, 0xF2, 0x41 } }, // 1.29.?.?.?????
+ { 0xF1CBDF48147D26C6ULL, { 0x4B, 0x69, 0x44, 0x69, 0x51, 0x57, 0xD4, 0x3D, 0x33, 0xE4, 0x0B, 0x56, 0x92, 0x44, 0x5A, 0xDB } }, // 1.30.?.?.????? 1.30 + World Cup Viewer
+ { 0x01A48CFAE25F85CDULL, { 0x60, 0xD7, 0x7A, 0x58, 0x06, 0x2D, 0x03, 0xDC, 0x69, 0x3C, 0x01, 0x95, 0x4D, 0xD1, 0x80, 0x21 } }, // 1.30.?.?.????? 1.30 + World Cup Viewer
+ { 0xFEBBF66DAEF6C9BEULL, { 0x4A, 0x22, 0x0A, 0xE3, 0xA6, 0x80, 0x8E, 0xD2, 0x69, 0x74, 0x10, 0xC9, 0x4C, 0x1C, 0xE9, 0x70 } }, // 1.30.0.1.????? Ashe
+ { 0x2AD4834DAB3986ABULL, { 0xEA, 0x69, 0x71, 0xF7, 0x8A, 0xBE, 0xBD, 0x2A, 0xF8, 0x83, 0xD4, 0x6A, 0x54, 0x86, 0xF3, 0x9F } }, // 1.31.?.?.????? Winter 2018
+ { 0xD89B24D62F00A04EULL, { 0xC3, 0xA4, 0xD0, 0x10, 0xAA, 0xA7, 0x28, 0x7A, 0x3E, 0xAC, 0xCD, 0x45, 0xED, 0x47, 0x13, 0x45 } }, // 1.31.?.?.????? Bastet Challenge
+ { 0x32DDC40236DAEA7BULL, { 0x2D, 0xBD, 0xE4, 0xFB, 0x9F, 0xDA, 0x77, 0x6A, 0xA2, 0x94, 0x87, 0x08, 0x54, 0xFE, 0x9B, 0x02 } }, // 1.31.?.?.????? Lunar New Year 2019 (Pig)
+ { 0xF481EFC2302EE415ULL, { 0x37, 0xA7, 0xF2, 0xB8, 0x7D, 0x0B, 0x8B, 0x70, 0x04, 0x56, 0xC6, 0xA9, 0x2C, 0x71, 0xD6, 0xDD } }, // 1.33.?.?.????? Paris Map
+ { 0xD1AC8C1903524D9AULL, { 0xD7, 0x81, 0xD0, 0xAA, 0x35, 0xE5, 0xC1, 0x06, 0xBC, 0xA7, 0xCF, 0x01, 0xDE, 0xBD, 0x14, 0x94 } }, // 1.34.0.1.55918 Baptiste
+ { 0x71EEBE93590AA903ULL, { 0x0C, 0xCD, 0x10, 0xD4, 0x55, 0x3E, 0xEC, 0x7E, 0x97, 0xFD, 0x36, 0xA9, 0xE8, 0xAD, 0xF0, 0xFF } }, // 1.34.0.1.55918 Baptiste Cosmetics
+
+ // Streamed WoW keys
+ { 0xFA505078126ACB3EULL, { 0xBD, 0xC5, 0x18, 0x62, 0xAB, 0xED, 0x79, 0xB2, 0xDE, 0x48, 0xC8, 0xE7, 0xE6, 0x6C, 0x62, 0x00 } }, // 15 WOW-20740patch7.0.1_Beta <not used between 7.0 and 7.3>
+ { 0xFF813F7D062AC0BCULL, { 0xAA, 0x0B, 0x5C, 0x77, 0xF0, 0x88, 0xCC, 0xC2, 0xD3, 0x90, 0x49, 0xBD, 0x26, 0x7F, 0x06, 0x6D } }, // 25 WOW-20740patch7.0.1_Beta <not used between 7.0 and 7.3>
+ { 0xD1E9B5EDF9283668ULL, { 0x8E, 0x4A, 0x25, 0x79, 0x89, 0x4E, 0x38, 0xB4, 0xAB, 0x90, 0x58, 0xBA, 0x5C, 0x73, 0x28, 0xEE } }, // 39 WOW-20740patch7.0.1_Beta Enchanted Torch pet
+ { 0xB76729641141CB34ULL, { 0x98, 0x49, 0xD1, 0xAA, 0x7B, 0x1F, 0xD0, 0x98, 0x19, 0xC5, 0xC6, 0x62, 0x83, 0xA3, 0x26, 0xEC } }, // 40 WOW-20740patch7.0.1_Beta Enchanted Pen pet
+ { 0xFFB9469FF16E6BF8ULL, { 0xD5, 0x14, 0xBD, 0x19, 0x09, 0xA9, 0xE5, 0xDC, 0x87, 0x03, 0xF4, 0xB8, 0xBB, 0x1D, 0xFD, 0x9A } }, // 41 WOW-20740patch7.0.1_Beta <not used between 7.0 and 7.3>
+ { 0x23C5B5DF837A226CULL, { 0x14, 0x06, 0xE2, 0xD8, 0x73, 0xB6, 0xFC, 0x99, 0x21, 0x7A, 0x18, 0x08, 0x81, 0xDA, 0x8D, 0x62 } }, // 42 WOW-20740patch7.0.1_Beta Enchanted Cauldron pet
+// { 0x3AE403EF40AC3037ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 51 WOW-21249patch7.0.3_Beta <not used between 7.0 and 7.3>
+ { 0xE2854509C471C554ULL, { 0x43, 0x32, 0x65, 0xF0, 0xCD, 0xEB, 0x2F, 0x4E, 0x65, 0xC0, 0xEE, 0x70, 0x08, 0x71, 0x4D, 0x9E } }, // 52 WOW-21249patch7.0.3_Beta Warcraft movie items
+ { 0x8EE2CB82178C995AULL, { 0xDA, 0x6A, 0xFC, 0x98, 0x9E, 0xD6, 0xCA, 0xD2, 0x79, 0x88, 0x59, 0x92, 0xC0, 0x37, 0xA8, 0xEE } }, // 55 WOW-21531patch7.0.3_Beta BlizzCon 2016 Murlocs
+ { 0x5813810F4EC9B005ULL, { 0x01, 0xBE, 0x8B, 0x43, 0x14, 0x2D, 0xD9, 0x9A, 0x9E, 0x69, 0x0F, 0xAD, 0x28, 0x8B, 0x60, 0x82 } }, // 56 WOW-21531patch7.0.3_Beta Fel Kitten
+ { 0x7F9E217166ED43EAULL, { 0x05, 0xFC, 0x92, 0x7B, 0x9F, 0x4F, 0x5B, 0x05, 0x56, 0x81, 0x42, 0x91, 0x2A, 0x05, 0x2B, 0x0F } }, // 57 WOW-21531patch7.0.3_Beta Legion music <not used in 7.3>
+ { 0xC4A8D364D23793F7ULL, { 0xD1, 0xAC, 0x20, 0xFD, 0x14, 0x95, 0x7F, 0xAB, 0xC2, 0x71, 0x96, 0xE9, 0xF6, 0xE7, 0x02, 0x4A } }, // 58 WOW-21691patch7.0.3_Beta Demon Hunter #1 cinematic (legion_dh1)
+ { 0x40A234AEBCF2C6E5ULL, { 0xC6, 0xC5, 0xF6, 0xC7, 0xF7, 0x35, 0xD7, 0xD9, 0x4C, 0x87, 0x26, 0x7F, 0xA4, 0x99, 0x4D, 0x45 } }, // 59 WOW-21691patch7.0.3_Beta Demon Hunter #2 cinematic (legion_dh2)
+ { 0x9CF7DFCFCBCE4AE5ULL, { 0x72, 0xA9, 0x7A, 0x24, 0xA9, 0x98, 0xE3, 0xA5, 0x50, 0x0F, 0x38, 0x71, 0xF3, 0x76, 0x28, 0xC0 } }, // 60 WOW-21691patch7.0.3_Beta Val'sharah #1 cinematic (legion_val_yd)
+ { 0x4E4BDECAB8485B4FULL, { 0x38, 0x32, 0xD7, 0xC4, 0x2A, 0xAC, 0x92, 0x68, 0xF0, 0x0B, 0xE7, 0xB6, 0xB4, 0x8E, 0xC9, 0xAF } }, // 61 WOW-21691patch7.0.3_Beta Val'sharah #2 cinematic (legion_val_yx)
+ { 0x94A50AC54EFF70E4ULL, { 0xC2, 0x50, 0x1A, 0x72, 0x65, 0x4B, 0x96, 0xF8, 0x63, 0x50, 0xC5, 0xA9, 0x27, 0x96, 0x2F, 0x7A } }, // 62 WOW-21691patch7.0.3_Beta Sylvanas warchief cinematic (legion_org_vs)
+ { 0xBA973B0E01DE1C2CULL, { 0xD8, 0x3B, 0xBC, 0xB4, 0x6C, 0xC4, 0x38, 0xB1, 0x7A, 0x48, 0xE7, 0x6C, 0x4F, 0x56, 0x54, 0xA3 } }, // 63 WOW-21691patch7.0.3_Beta Stormheim Sylvanas vs Greymane cinematic (legion_sth)
+ { 0x494A6F8E8E108BEFULL, { 0xF0, 0xFD, 0xE1, 0xD2, 0x9B, 0x27, 0x4F, 0x6E, 0x7D, 0xBD, 0xB7, 0xFF, 0x81, 0x5F, 0xE9, 0x10 } }, // 64 WOW-21691patch7.0.3_Beta Harbingers Gul'dan video (legion_hrb_g)
+ { 0x918D6DD0C3849002ULL, { 0x85, 0x70, 0x90, 0xD9, 0x26, 0xBB, 0x28, 0xAE, 0xDA, 0x4B, 0xF0, 0x28, 0xCA, 0xCC, 0x4B, 0xA3 } }, // 65 WOW-21691patch7.0.3_Beta Harbingers Khadgar video (legion_hrb_k)
+ { 0x0B5F6957915ADDCAULL, { 0x4D, 0xD0, 0xDC, 0x82, 0xB1, 0x01, 0xC8, 0x0A, 0xBA, 0xC0, 0xA4, 0xD5, 0x7E, 0x67, 0xF8, 0x59 } }, // 66 WOW-21691patch7.0.3_Beta Harbingers Illidan video (legion_hrb_i)
+ { 0x794F25C6CD8AB62BULL, { 0x76, 0x58, 0x3B, 0xDA, 0xCD, 0x52, 0x57, 0xA3, 0xF7, 0x3D, 0x15, 0x98, 0xA2, 0xCA, 0x2D, 0x99 } }, // 67 WOW-21846patch7.0.3_Beta Suramar cinematic (legion_su_i)
+ { 0xA9633A54C1673D21ULL, { 0x1F, 0x8D, 0x46, 0x7F, 0x5D, 0x6D, 0x41, 0x1F, 0x8A, 0x54, 0x8B, 0x63, 0x29, 0xA5, 0x08, 0x7E } }, // 68 WOW-21846patch7.0.3_Beta legion_su_r cinematic
+ { 0x5E5D896B3E163DEAULL, { 0x8A, 0xCE, 0x8D, 0xB1, 0x69, 0xE2, 0xF9, 0x8A, 0xC3, 0x6A, 0xD5, 0x2C, 0x08, 0x8E, 0x77, 0xC1 } }, // 69 WOW-21846patch7.0.3_Beta Broken Shore intro cinematic (legion_bs_i)
+ { 0x0EBE36B5010DFD7FULL, { 0x9A, 0x89, 0xCC, 0x7E, 0x3A, 0xCB, 0x29, 0xCF, 0x14, 0xC6, 0x0B, 0xC1, 0x3B, 0x1E, 0x46, 0x16 } }, // 70 WOW-21846patch7.0.3_Beta Alliance Broken Shore cinematic (legion_bs_a)
+ { 0x01E828CFFA450C0FULL, { 0x97, 0x2B, 0x6E, 0x74, 0x42, 0x0E, 0xC5, 0x19, 0xE6, 0xF9, 0xD9, 0x7D, 0x59, 0x4A, 0xA3, 0x7C } }, // 71 WOW-21846patch7.0.3_Beta Horde Broken Shore cinematic (legion_bs_h)
+ { 0x4A7BD170FE18E6AEULL, { 0xAB, 0x55, 0xAE, 0x1B, 0xF0, 0xC7, 0xC5, 0x19, 0xAF, 0xF0, 0x28, 0xC1, 0x56, 0x10, 0xA4, 0x5B } }, // 72 WOW-21846patch7.0.3_Beta Khadgar & Light's Heart cinematic (legion_iq_lv)
+ { 0x69549CB975E87C4FULL, { 0x7B, 0x6F, 0xA3, 0x82, 0xE1, 0xFA, 0xD1, 0x46, 0x5C, 0x85, 0x1E, 0x3F, 0x47, 0x34, 0xA1, 0xB3 } }, // 73 WOW-21846patch7.0.3_Beta legion_iq_id cinematic
+ { 0x460C92C372B2A166ULL, { 0x94, 0x6D, 0x56, 0x59, 0xF2, 0xFA, 0xF3, 0x27, 0xC0, 0xB7, 0xEC, 0x82, 0x8B, 0x74, 0x8A, 0xDB } }, // 74 WOW-21952patch7.0.3_Beta Stormheim Alliance cinematic (legion_g_a_sth)
+ { 0x8165D801CCA11962ULL, { 0xCD, 0x0C, 0x0F, 0xFA, 0xAD, 0x93, 0x63, 0xEC, 0x14, 0xDD, 0x25, 0xEC, 0xDD, 0x2A, 0x5B, 0x62 } }, // 75 WOW-21952patch7.0.3_Beta Stormheim Horde cinematic (legion_g_h_sth)
+ { 0xA3F1C999090ADAC9ULL, { 0xB7, 0x2F, 0xEF, 0x4A, 0x01, 0x48, 0x8A, 0x88, 0xFF, 0x02, 0x28, 0x0A, 0xA0, 0x7A, 0x92, 0xBB } }, // 81 WOW-22578patch7.1.0_PTR Firecat Mount
+// { 0x18AFDF5191923610ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 82 WOW-22578patch7.1.0_PTR <not used between 7.1 and 7.3>
+// { 0x3C258426058FBD93ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 91 WOW-23436patch7.2.0_PTR <not used between 7.2 and 7.3>
+ { 0x094E9A0474876B98ULL, { 0xE5, 0x33, 0xBB, 0x6D, 0x65, 0x72, 0x7A, 0x58, 0x32, 0x68, 0x0D, 0x62, 0x0B, 0x0B, 0xC1, 0x0B } }, // 92 WOW-23910patch7.2.5_PTR shadowstalkerpanthermount, shadowstalkerpantherpet
+ { 0x3DB25CB86A40335EULL, { 0x02, 0x99, 0x0B, 0x12, 0x26, 0x0C, 0x1E, 0x9F, 0xDD, 0x73, 0xFE, 0x47, 0xCB, 0xAB, 0x70, 0x24 } }, // 93 WOW-23789patch7.2.0_PTR legion_72_ots
+ { 0x0DCD81945F4B4686ULL, { 0x1B, 0x78, 0x9B, 0x87, 0xFB, 0x3C, 0x92, 0x38, 0xD5, 0x28, 0x99, 0x7B, 0xFA, 0xB4, 0x41, 0x86 } }, // 94 WOW-23789patch7.2.0_PTR legion_72_tst
+ { 0x486A2A3A2803BE89ULL, { 0x32, 0x67, 0x9E, 0xA7, 0xB0, 0xF9, 0x9E, 0xBF, 0x4F, 0xA1, 0x70, 0xE8, 0x47, 0xEA, 0x43, 0x9A } }, // 95 WOW-23789patch7.2.0_PTR legion_72_ars
+ { 0x71F69446AD848E06ULL, { 0xE7, 0x9A, 0xEB, 0x88, 0xB1, 0x50, 0x9F, 0x62, 0x8F, 0x38, 0x20, 0x82, 0x01, 0x74, 0x1C, 0x30 } }, // 97 WOW-24473patch7.3.0_PTR BlizzCon 2017 Mounts (AllianceShipMount and HordeZeppelinMount)
+ { 0x211FCD1265A928E9ULL, { 0xA7, 0x36, 0xFB, 0xF5, 0x8D, 0x58, 0x7B, 0x39, 0x72, 0xCE, 0x15, 0x4A, 0x86, 0xAE, 0x45, 0x40 } }, // 98 WOW-24473patch7.3.0_PTR "Shadow" fox pet (store)
+ { 0x0ADC9E327E42E98CULL, { 0x01, 0x7B, 0x34, 0x72, 0xC1, 0xDE, 0xE3, 0x04, 0xFA, 0x0B, 0x2F, 0xF8, 0xE5, 0x3F, 0xF7, 0xD6 } }, // 99 WOW-23910patch7.2.5_PTR legion_72_tsf
+ { 0xBAE9F621B60174F1ULL, { 0x38, 0xC3, 0xFB, 0x39, 0xB4, 0x97, 0x17, 0x60, 0xB4, 0xB9, 0x82, 0xFE, 0x9F, 0x09, 0x50, 0x14 } }, // 100 WOW-24727patch7.3.0_PTR Rejection of the Gift cinematic (legion_73_agi)
+ { 0x34DE1EEADC97115EULL, { 0x2E, 0x3A, 0x53, 0xD5, 0x9A, 0x49, 0x1E, 0x5C, 0xD1, 0x73, 0xF3, 0x37, 0xF7, 0xCD, 0x8C, 0x61 } }, // 101 WOW-24727patch7.3.0_PTR Resurrection of Alleria Windrunner cinematic (legion_73_avt)
+ { 0xE07E107F1390A3DFULL, { 0x29, 0x0D, 0x27, 0xB0, 0xE8, 0x71, 0xF8, 0xC5, 0xB1, 0x4A, 0x14, 0xE5, 0x14, 0xD0, 0xF0, 0xD9 } }, // 102 WOW-25079patch7.3.2_PTR Tottle battle pet, Raptor mount, Horse mount (104 files)
+ { 0x32690BF74DE12530ULL, { 0xA2, 0x55, 0x62, 0x10, 0xAE, 0x54, 0x22, 0xE6, 0xD6, 0x1E, 0xDA, 0xAF, 0x12, 0x2C, 0xB6, 0x37 } }, // 103 WOW-24781patch7.3.0_PTR legion_73_pan
+ { 0xBF3734B1DCB04696ULL, { 0x48, 0x94, 0x61, 0x23, 0x05, 0x0B, 0x00, 0xA7, 0xEF, 0xB1, 0xC0, 0x29, 0xEE, 0x6C, 0xC4, 0x38 } }, // 104 WOW-25079patch7.3.2_PTR legion_73_afn
+ { 0x74F4F78002A5A1BEULL, { 0xC1, 0x4E, 0xEC, 0x8D, 0x5A, 0xEE, 0xF9, 0x3F, 0xA8, 0x11, 0xD4, 0x50, 0xB4, 0xE4, 0x6E, 0x91 } }, // 105 WOW-25079patch7.3.2_PTR SilithusPhase01 map
+// { 0x423F07656CA27D23ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 107 WOW-25600patch7.3.5_PTR bltestmap
+// { 0x0691678F83E8A75DULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 108 WOW-25600patch7.3.5_PTR filedataid 1782602-1782603
+// { 0x324498590F550556ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 109 WOW-25600patch7.3.5_PTR filedataid 1782615-1782619
+// { 0xC02C78F40BEF5998ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 110 WOW-25600patch7.3.5_PTR test/testtexture.blp (fdid 1782613)
+// { 0x47011412CCAAB541ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 111 WOW-25600patch7.3.5_PTR unused in 25600
+// { 0x23B6F5764CE2DDD6ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 112 WOW-25600patch7.3.5_PTR unused in 25600
+// { 0x8E00C6F405873583ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 113 WOW-25600patch7.3.5_PTR filedataid 1783470-1783472
+ { 0x78482170E4CFD4A6ULL, { 0x76, 0x85, 0x40, 0xC2, 0x0A, 0x5B, 0x15, 0x35, 0x83, 0xAD, 0x7F, 0x53, 0x13, 0x0C, 0x58, 0xFE } }, // 114 WOW-25600patch7.3.5_PTR Magni Bronzebeard VO
+ { 0xB1EB52A64BFAF7BFULL, { 0x45, 0x81, 0x33, 0xAA, 0x43, 0x94, 0x9A, 0x14, 0x16, 0x32, 0xC4, 0xF8, 0x59, 0x6D, 0xE2, 0xB0 } }, // 115 WOW-25600patch7.3.5_PTR dogmount, 50 files
+ { 0xFC6F20EE98D208F6ULL, { 0x57, 0x79, 0x0E, 0x48, 0xD3, 0x55, 0x00, 0xE7, 0x0D, 0xF8, 0x12, 0x59, 0x4F, 0x50, 0x7B, 0xE7 } }, // 117 WOW-25632patch7.3.5_PTR shop stuff
+ { 0x402CFABF2020D9B7ULL, { 0x67, 0x19, 0x7B, 0xCD, 0x9D, 0x0E, 0xF0, 0xC4, 0x08, 0x53, 0x78, 0xFA, 0xA6, 0x9A, 0x32, 0x64 } }, // 118 WOW-25678patch7.3.5_PTR filedataid 1854762
+ { 0x6FA0420E902B4FBEULL, { 0x27, 0xB7, 0x50, 0x18, 0x4E, 0x53, 0x29, 0xC4, 0xE4, 0x45, 0x5C, 0xBD, 0x3E, 0x1F, 0xD5, 0xAB } }, // 119 WOW-25744patch7.3.5_PTR legion_735_epa / legion_735_eph
+ { 0x1076074F2B350A2DULL, { 0x88, 0xBF, 0x0C, 0xD0, 0xD5, 0xBA, 0x15, 0x9A, 0xE7, 0xCB, 0x91, 0x6A, 0xFB, 0xE1, 0x38, 0x65 } }, // 121 WOW-26287patch8.0.1_Beta skiff
+ { 0x816F00C1322CDF52ULL, { 0x6F, 0x83, 0x22, 0x99, 0xA7, 0x57, 0x89, 0x57, 0xEE, 0x86, 0xB7, 0xF9, 0xF1, 0x5B, 0x01, 0x88 } }, // 122 WOW-26287patch8.0.1_Beta snowkid
+ { 0xDDD295C82E60DB3CULL, { 0x34, 0x29, 0xCC, 0x59, 0x27, 0xD1, 0x62, 0x97, 0x65, 0x97, 0x4F, 0xD9, 0xAF, 0xAB, 0x75, 0x80 } }, // 123 WOW-26287patch8.0.1_Beta redbird
+ { 0x83E96F07F259F799ULL, { 0x91, 0xF7, 0xD0, 0xE7, 0xA0, 0x2C, 0xDE, 0x0D, 0xE0, 0xBD, 0x36, 0x7F, 0xAB, 0xCB, 0x8A, 0x6E } }, // 124 WOW-26522patch8.0.1_Beta BlizzCon 2018 (Alliance and Horde banners and cloaks)
+ { 0x49FBFE8A717F03D5ULL, { 0xC7, 0x43, 0x77, 0x70, 0xCF, 0x15, 0x3A, 0x31, 0x35, 0xFA, 0x6D, 0xC5, 0xE4, 0xC8, 0x5E, 0x65 } }, // 225 WOW-27826patch8.1.0_PTR Meatwagon mount (Warcraft 3: Reforged)
+ { 0xC1E5D7408A7D4484ULL, { 0xA7, 0xD8, 0x8E, 0x52, 0x74, 0x9F, 0xA5, 0x45, 0x9D, 0x64, 0x45, 0x23, 0xF8, 0x35, 0x96, 0x51 } }, // 226 WOW-26871patch8.0.1_Beta Sylvanas Warbringer cinematic
+ { 0xE46276EB9E1A9854ULL, { 0xCC, 0xCA, 0x36, 0xE3, 0x02, 0xF9, 0x45, 0x9B, 0x1D, 0x60, 0x52, 0x6A, 0x31, 0xBE, 0x77, 0xC8 } }, // 227 WOW-26871patch8.0.1_Beta ltc_a, ltc_h and ltt cinematics
+ { 0xD245B671DD78648CULL, { 0x19, 0xDC, 0xB4, 0xD4, 0x5A, 0x65, 0x8B, 0x54, 0x35, 0x1D, 0xB7, 0xDD, 0xC8, 0x1D, 0xE7, 0x9E } }, // 228 WOW-26871patch8.0.1_Beta stz, zia, kta, jnm & ja cinematics
+ { 0x4C596E12D36DDFC3ULL, { 0xB8, 0x73, 0x19, 0x26, 0x38, 0x94, 0x99, 0xCB, 0xD4, 0xAD, 0xBF, 0x50, 0x06, 0xCA, 0x03, 0x91 } }, // 229 WOW-26871patch8.0.1_Beta bar cinematic
+ { 0x0C9ABD5081C06411ULL, { 0x25, 0xA7, 0x7C, 0xD8, 0x00, 0x19, 0x7E, 0xE6, 0xA3, 0x2D, 0xD6, 0x3F, 0x04, 0xE1, 0x15, 0xFA } }, // 230 WOW-26871patch8.0.1_Beta zcf cinematic
+ { 0x3C6243057F3D9B24ULL, { 0x58, 0xAE, 0x3E, 0x06, 0x42, 0x10, 0xE3, 0xED, 0xF9, 0xC1, 0x25, 0x9C, 0xDE, 0x91, 0x4C, 0x5D } }, // 231 WOW-26871patch8.0.1_Beta ktf cinematic
+ { 0x7827FBE24427E27DULL, { 0x34, 0xA4, 0x32, 0x04, 0x20, 0x73, 0xCD, 0x0B, 0x51, 0x62, 0x70, 0x68, 0xD2, 0xE0, 0xBD, 0x3E } }, // 232 WOW-26871patch8.0.1_Beta rot cinematic
+ { 0xFAF9237E1186CF66ULL, { 0xAE, 0x78, 0x78, 0x40, 0x04, 0x1E, 0x9B, 0x41, 0x98, 0xF4, 0x79, 0x71, 0x4D, 0xAD, 0x56, 0x2C } }, // 233 WOW-28048patch8.1.0_PTR encrypted db2 sections (battle pet?)
+// { 0x5DD92EE32BBF9ABDULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 234 WOW-27004patch8.0.1_Subm filedataid 2238294
+ { 0x0B68A7AF5F85F7EEULL, { 0x27, 0xAA, 0x01, 0x10, 0x82, 0xF5, 0xE8, 0xBB, 0xBD, 0x71, 0xD1, 0xBA, 0x04, 0xF6, 0xAB, 0xA4 } }, // 236 WOW-28151patch8.1.0_PTR encrypted06 Flying pig mount
+// { 0x01531713C83FCC39ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 237 WOW-28151patch8.1.0_PTR fdid 2460009, 2460732
+// { 0x76E4F6739A35E8D7ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 238 WOW-28294patch8.1.0_PTR starts at fdid 2492654, total of 12 fdids
+ { 0x66033F28DC01923CULL, { 0x9F, 0x95, 0x19, 0x86, 0x14, 0x90, 0xC5, 0xA9, 0xFF, 0xD4, 0xD8, 0x2A, 0x6D, 0x00, 0x67, 0xDB } }, // 239 WOW-28294patch8.1.0_PTR vulpine familiar mount
+ { 0xFCF34A9B05AE7E6AULL, { 0xE7, 0xC2, 0xC8, 0xF7, 0x7E, 0x30, 0xAC, 0x24, 0x0F, 0x39, 0xEC, 0x23, 0x97, 0x12, 0x96, 0xE5 } }, // 240 WOW-28151patch8.1.0_PTR fdid 2468985, 2471011, 2471012, 2471014, 2471016, 2471018, 2530045
+ { 0xE2F6BD41298A2AB9ULL, { 0xC5, 0xDC, 0x1B, 0xB4, 0x3B, 0x8C, 0xF3, 0xF0, 0x85, 0xD6, 0x98, 0x68, 0x26, 0xB9, 0x28, 0xEC } }, // 241 WOW-28151patch8.1.0_PTR fdid 2468988, 2471019, 2471020, 2471021, 2471022, 2471023
+ { 0x14C4257E557B49A1ULL, { 0x06, 0x4A, 0x97, 0x09, 0xF4, 0x2D, 0x50, 0xCB, 0x5F, 0x8B, 0x94, 0xBC, 0x1A, 0xCF, 0xDD, 0x5D } }, // 242 WOW-28440patch8.1.0_PTR dor cinematic
+ { 0x1254E65319C6EEFFULL, { 0x79, 0xD2, 0xB3, 0xD1, 0xCC, 0xB0, 0x15, 0x47, 0x4E, 0x71, 0x58, 0x81, 0x38, 0x64, 0xB8, 0xE6 } }, // 243 WOW-28440patch8.1.0_PTR akt cinematic
+// { 0xC8753773ADF1174CULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 244 WOW-28938patch8.1.5_PTR starts at fdid 2615771, total of 14 fdids
+// { 0x2170BCAA9FA96E22ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 245 WOW-28938patch8.1.5_PTR alpaca mount
+// { 0x75485627AA225F4DULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 246 WOW-28938patch8.1.5_PTR fdid 2741546, 2741548, 2741549
+ { 0x08717B15BF3C7955ULL, { 0x4B, 0x06, 0xBF, 0x9D, 0x17, 0x66, 0x3C, 0xEB, 0x33, 0x12, 0xEA, 0x3C, 0x69, 0xFB, 0xC5, 0xDD } }, // 248 WOW-29220patch8.1.5_PTR fdid 2823166
+ { 0x9FD609902B4B2E07ULL, { 0xAB, 0xE0, 0xC5, 0xF9, 0xC1, 0x23, 0xE6, 0xE2, 0x4E, 0x7B, 0xEA, 0x43, 0xC2, 0xBF, 0x00, 0xAC } }, // 250 WOW-29418patch8.1.5_PTR dpr cinematic
};
-
-static const char * szKeyConstant16 = "expand 16-byte k";
-static const char * szKeyConstant32 = "expand 32-byte k";
//-----------------------------------------------------------------------------
// Local functions
@@ -159,19 +232,6 @@ static DWORD Rol32(DWORD dwValue, DWORD dwRolCount)
return (dwValue << dwRolCount) | (dwValue >> (32 - dwRolCount));
}
-static LPBYTE FindCascKey(ULONGLONG KeyName)
-{
- // Search the known keys
- for(size_t i = 0; CascKeys[i].KeyName != 0; i++)
- {
- if(CascKeys[i].KeyName == KeyName)
- return CascKeys[i].Key;
- }
-
- // Key not found
- return NULL;
-}
-
static void Initialize(PCASC_SALSA20 pState, LPBYTE pbKey, DWORD cbKeyLength, LPBYTE pbVector)
{
const char * szConstants = (cbKeyLength == 32) ? szKeyConstant32 : szKeyConstant16;
@@ -296,7 +356,35 @@ static int Decrypt_Salsa20(LPBYTE pbOutBuffer, LPBYTE pbInBuffer, size_t cbInBuf
//-----------------------------------------------------------------------------
// Public functions
-int CascDecrypt(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex)
+int CascLoadEncryptionKeys(TCascStorage * hs)
+{
+ size_t nKeyCount = (sizeof(CascKeys) / sizeof(CASC_ENCRYPTION_KEY));
+ size_t nMaxItems = nKeyCount + CASC_EXTRA_KEYS;
+ int nError;
+
+ // Create fast map of KeyName -> Key
+ nError = hs->EncryptionKeys.Create(nMaxItems, sizeof(ULONGLONG), FIELD_OFFSET(CASC_ENCRYPTION_KEY, KeyName));
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // Insert all static keys
+ for (size_t i = 0; i < nKeyCount; i++)
+ hs->EncryptionKeys.InsertObject(&CascKeys[i], &CascKeys[i].KeyName);
+
+ // Create array for extra keys
+ nError = hs->ExtraKeysList.Create<CASC_ENCRYPTION_KEY>(CASC_EXTRA_KEYS);
+ return nError;
+}
+
+LPBYTE CascFindKey(TCascStorage * hs, ULONGLONG KeyName)
+{
+ PCASC_ENCRYPTION_KEY pKey;
+
+ pKey = (PCASC_ENCRYPTION_KEY)hs->EncryptionKeys.FindObject(&KeyName);
+ return (pKey != NULL) ? pKey->Key : NULL;
+}
+
+int CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex)
{
ULONGLONG KeyName = 0;
LPBYTE pbBufferEnd = pbInBuffer + cbInBuffer;
@@ -347,14 +435,14 @@ int CascDecrypt(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWOR
return ERROR_INSUFFICIENT_BUFFER;
// Check if we know the key
- pbKey = FindCascKey(KeyName);
+ pbKey = CascFindKey(hs, KeyName);
if(pbKey == NULL)
return ERROR_FILE_ENCRYPTED;
// Shuffle the Vector with the block index
// Note that there's no point to go beyond 32 bits, unless the file has
// more than 0xFFFFFFFF frames.
- for(int i = 0; i < sizeof(dwFrameIndex); i++)
+ for(size_t i = 0; i < sizeof(dwFrameIndex); i++)
{
Vector[i] = Vector[i] ^ (BYTE)((dwFrameIndex >> dwShift) & 0xFF);
dwShift += 8;
@@ -392,3 +480,38 @@ int CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, D
return ERROR_SUCCESS;
}
+bool WINAPI CascAddEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPBYTE Key)
+{
+ PCASC_ENCRYPTION_KEY pEncKey;
+ TCascStorage * hs;
+
+ // Validate the storage handle
+ hs = TCascStorage::IsValid(hStorage);
+ if (hs == NULL)
+ {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ // Don't allow more than CASC_EXTRA_KEYS keys
+ if (hs->ExtraKeysList.ItemCount() >= CASC_EXTRA_KEYS)
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return false;
+ }
+
+ // Insert the key to the array
+ pEncKey = (PCASC_ENCRYPTION_KEY)hs->ExtraKeysList.Insert(1);
+ if (pEncKey == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return false;
+ }
+
+ // Fill the key
+ memcpy(pEncKey->Key, Key, sizeof(pEncKey->Key));
+ pEncKey->KeyName = KeyName;
+
+ // Also insert the key to the map
+ return hs->EncryptionKeys.InsertObject(pEncKey, &pEncKey->KeyName);
+}
diff --git a/dep/CascLib/src/CascDumpData.cpp b/dep/CascLib/src/CascDumpData.cpp
index e6b4265d76b..40e791f7a15 100644
--- a/dep/CascLib/src/CascDumpData.cpp
+++ b/dep/CascLib/src/CascDumpData.cpp
@@ -11,47 +11,80 @@
#define __CASCLIB_SELF__
#include "CascLib.h"
#include "CascCommon.h"
-#include "CascMndx.h"
#ifdef _DEBUG // The entire feature is only valid for debug purposes
//-----------------------------------------------------------------------------
// Forward definitions
-//LPBYTE VerifyLocaleBlock(PROOT_BLOCK_INFO pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd);
+int CaptureEncodingHeader(CASC_ENCODING_HEADER & EnHeader, LPBYTE pbFileData, size_t cbFileData);
+int CaptureDownloadHeader(CASC_DOWNLOAD_HEADER & DlHeader, LPBYTE pbFileData, size_t cbFileData);
+int CaptureDownloadEntry(CASC_DOWNLOAD_HEADER & DlHeader, CASC_DOWNLOAD_ENTRY & DlEntry, LPBYTE pbFilePtr, LPBYTE pbFileEnd);
+int CaptureDownloadTag(CASC_DOWNLOAD_HEADER & DlHeader, CASC_TAG_ENTRY1 & DlTag, LPBYTE pbFilePtr, LPBYTE pbFileEnd);
//-----------------------------------------------------------------------------
-// Sort compare functions
+// Local functions
-static int CompareIndexEntries_FilePos(const void *, const void * pvIndexEntry1, const void * pvIndexEntry2)
+static char * StringFromLPTSTR(const TCHAR * szString, char * szBuffer, size_t cchBuffer)
{
- PCASC_INDEX_ENTRY pIndexEntry1 = (PCASC_INDEX_ENTRY)pvIndexEntry1;
- PCASC_INDEX_ENTRY pIndexEntry2 = (PCASC_INDEX_ENTRY)pvIndexEntry2;
- ULONGLONG FileOffset1 = ConvertBytesToInteger_5(pIndexEntry1->FileOffsetBE);
- ULONGLONG FileOffset2 = ConvertBytesToInteger_5(pIndexEntry2->FileOffsetBE);
- DWORD ArchIndex1 = (DWORD)(FileOffset1 >> 0x1E);
- DWORD ArchIndex2 = (DWORD)(FileOffset2 >> 0x1E);
-
- // First, compare the archive index
- if(ArchIndex1 < ArchIndex2)
- return -1;
- if(ArchIndex1 > ArchIndex2)
- return +1;
-
- // Second, compare the archive offset
- FileOffset1 &= 0x3FFFFFFF;
- FileOffset2 &= 0x3FFFFFFF;
- if(FileOffset1 < FileOffset2)
- return -1;
- if(FileOffset1 > FileOffset2)
- return +1;
-
- return 0;
+ char * szSaveBuffer = szBuffer;
+ char * szBufferEnd = szBuffer + cchBuffer - 1;
+
+ while (szBuffer < szBufferEnd && szString[0] != 0)
+ *szBuffer++ = (char)*szString++;
+ szBuffer[0] = 0;
+
+ return szSaveBuffer;
}
-//-----------------------------------------------------------------------------
-// Public functions
+// Either opens a dump file or returns "stdout"
+static FILE * OpenDumpFile(const char * szDumpFile)
+{
+ if(szDumpFile != NULL)
+ {
+ return fopen(szDumpFile, "wt");
+ }
+ else
+ {
+ return stdout;
+ }
+}
+
+// Closes the dump file if it was open by OpenDumpFile
+static void CloseDumpFile(const char * szDumpFile, FILE * fp)
+{
+ if(szDumpFile != NULL && fp != NULL)
+ fclose(fp);
+}
+
+static void DumpKey(FILE * fp, const char * szInFormat, LPBYTE pbData, size_t cbData)
+{
+ const char * szFormatSpec;
+ LPBYTE pbDataEnd = pbData + cbData;
+ char szFormat[0x100];
+ char szKey[MD5_STRING_SIZE + 1];
+
+ // First line: Copy the line with the complete format
+ CascStrCopy(szFormat, _countof(szFormat), szInFormat);
+ szFormatSpec = strstr(szFormat, "%s");
+ // Dump all keys, every one on a new line
+ while(pbData < pbDataEnd)
+ {
+ // Fump the line
+ StringFromBinary(pbData, MD5_HASH_SIZE, szKey);
+ fprintf(fp, szFormat, szKey);
+
+ // If there will be more lines, then we clear the entire part until "%s"
+ if(szFormatSpec != NULL)
+ memset(szFormat, ' ', (szFormatSpec - szFormat));
+
+ // Move pointers
+ pbData += MD5_HASH_SIZE;
+ }
+}
+
+/*
void CascDumpSparseArray(const char * szFileName, void * pvSparseArray)
{
TSparseArray * pSparseArray = (TSparseArray *)pvSparseArray;
@@ -93,8 +126,8 @@ void CascDumpNameFragTable(const char * szFileName, void * pMarFile)
fp = fopen(szFileName, "wt");
if(fp != NULL)
{
- PNAME_FRAG pNameTable = pDB->NameFragTable.NameFragArray;
- const char * szNames = pDB->IndexStruct_174.NameFragments.CharArray;
+ PNAME_FRAG pNameTable = pDB->NameFragTable.ItemArray;
+ const char * szNames = pDB->IndexStruct_174.ItemArray;
const char * szLastEntry;
char szMatchType[0x100];
@@ -114,9 +147,9 @@ void CascDumpNameFragTable(const char * szFileName, void * pMarFile)
{
// Prepare the entry
if(IS_SINGLE_CHAR_MATCH(pDB->NameFragTable, i))
- sprintf(szMatchType, "SINGLE_CHAR (\'%c\')", (pNameTable->FragOffs & 0xFF));
+ CascStrPrintf(szMatchType, _countof(szMatchType), "SINGLE_CHAR (\'%c\')", (pNameTable->FragOffs & 0xFF));
else
- sprintf(szMatchType, "NAME_FRAGMT (\"%s\")", szNames + pNameTable->FragOffs);
+ CascStrPrintf(szMatchType, _countof(szMatchType), "NAME_FRAGMT (\"%s\")", szNames + pNameTable->FragOffs);
}
// Dump the entry
@@ -169,147 +202,269 @@ void CascDumpFileNames(const char * szFileName, void * pvMarFile)
Struct1C.FreeStruct40();
}
-void CascDumpIndexEntry(
- TCascStorage * /* hs */,
- TDumpContext * dc,
- PCASC_INDEX_ENTRY pIndexEntry,
- int /* nDumpLevel */)
+
+void DumpEKeyEntries(TCascStorage * hs, FILE * fp)
{
- if(pIndexEntry != NULL)
+ // Dump header
+ fprintf(fp, "=== Ekey Entries ============================================================\n");
+
+ // Dump index entries from all index files
+ for(int file = 0; file < CASC_INDEX_COUNT; file++)
{
- ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffsetBE);
- DWORD ArchIndex = (DWORD)(FileOffset >> 0x1E);
- DWORD FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE);
-
- // Mask the file offset
- FileOffset &= 0x3FFFFFFF;
- dump_print(dc, " data.%03u at 0x%08x (0x%lx bytes)\n",
- ArchIndex,
- (DWORD)FileOffset,
- FileSize);
-
- //if(nDumpLevel > 2)
- //{
- // QueryKey.pbData = pIndexEntry->IndexKey;
- // QueryKey.cbData = MD5_HASH_SIZE;
- // if(CascOpenFileByIndexKey((HANDLE)hs, &QueryKey, 0, &hFile))
- // {
- // // Make sure that the data file is open and frame header loaded
- // CascGetFileSize(hFile, NULL);
- // hf = IsValidFileHandle(hFile);
- // assert(hf->pStream != NULL);
-
- // // Read the header area
- // FileOffset = hf->HeaderOffset - BLTE_HEADER_DELTA;
- // FileStream_Read(hf->pStream, &FileOffset, HeaderArea, sizeof(HeaderArea));
- // CascCloseFile(hFile);
-
- // // Dump the header area
- // dump_print(dc, " FileSize: %X Rest: %s\n",
- // ConvertBytesToInteger_4_LE(&HeaderArea[0x10]),
- // StringFromBinary(&HeaderArea[0x14], 10, szBuffer));
- // }
- //}
+ PCASC_EKEY_ENTRY pEKeyEntry = hs->IndexFile[file].pEKeyEntries;
+ DWORD nEKeyEntries = hs->IndexFile[file].nEKeyEntries;
+
+ // Only if they are present
+ if(pEKeyEntry && nEKeyEntries)
+ {
+// fprintf(fp, "--- Index: %03u --------------------------------------------------------------\n", file);
+ for(DWORD entry = 0; entry < nEKeyEntries; entry++, pEKeyEntry++)
+ DumpEKeyEntry(fp, pEKeyEntry->EKey, CASC_EKEY_SIZE, pEKeyEntry->StorageOffset, ConvertBytesToInteger_4_LE(pEKeyEntry->EncodedSize));
+ }
}
- else
+
+ // Dump tail
+ fprintf(fp, "=============================================================================\n\n");
+}
+*/
+
+//-----------------------------------------------------------------------------
+// Dumping the ENCODING manifest
+/*
+static void DumpESpecEntries(FILE * fp, LPBYTE pbDataPtr, LPBYTE pbDataEnd)
+{
+ fprintf(fp, "--- ESpec Entries -----------------------------------------------------------\n");
+
+ while(pbDataPtr < pbDataEnd)
{
- dump_print(dc, " NO INDEX ENTRY\n");
+ const char * szESpecEntry = (const char *)pbDataPtr;
+
+ // Find the end of the entry
+ while((pbDataPtr < pbDataEnd) && (pbDataPtr[0] != 0))
+ {
+ pbDataPtr++;
+ }
+
+ // Dump the entry
+ if(pbDataPtr[0] == 0)
+ {
+ fprintf(fp, "%s\n", szESpecEntry);
+ pbDataPtr++;
+ }
}
}
-void CascDumpEncodingEntry(
- TCascStorage * hs,
- TDumpContext * dc,
- PCASC_ENCODING_ENTRY pEncodingEntry,
- int nDumpLevel)
+static void DumpCKeyEntry(PCASC_CKEY_ENTRY pCKeyEntry, FILE * fp, DWORD nIndex)
{
- PCASC_INDEX_ENTRY pIndexEntry;
- QUERY_KEY QueryKey;
- LPBYTE pbIndexKey;
- char szMd5[MD5_STRING_SIZE+1];
+ LPBYTE pbEKey;
+ char szStringBuff[MD5_STRING_SIZE + 1];
- // If the encoding key exists
- if(pEncodingEntry != NULL)
+ // Print the CKey and number of EKeys
+ fprintf(fp, "[0x%02X] %s (EKeys: %02u): ", nIndex, StringFromBinary(pCKeyEntry->CKey, MD5_HASH_SIZE, szStringBuff), pCKeyEntry->EKeyCount);
+ pbEKey = pCKeyEntry->EKey;
+
+ // Print the EKeys
+ for(USHORT i = 0; i < pCKeyEntry->EKeyCount; i++, pbEKey += MD5_HASH_SIZE)
+ fprintf(fp, " %s ", StringFromBinary(pbEKey, MD5_HASH_SIZE, szStringBuff));
+ fprintf(fp, "\n");
+}
+
+static void DumpCKeyPages(FILE * fp, CASC_ENCODING_HEADER & EnHeader, LPBYTE pbDataPtr, LPBYTE pbDataEnd)
+{
+ // Get the CKey page header and the page itself
+ PFILE_CKEY_PAGE pPageHeader = (PFILE_CKEY_PAGE)pbDataPtr;
+ LPBYTE pbPageEntry = (LPBYTE)(pPageHeader + EnHeader.CKeyPageCount);
+
+ fprintf(fp, "--- CKey Pages --------------------------------------------------------------\n");
+ for(DWORD i = 0; i < EnHeader.CKeyPageCount; i++, pPageHeader++)
{
- dump_print(dc, " Size %lx Key Count: %u\n",
- ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE),
- pEncodingEntry->KeyCount);
+ LPBYTE pbCKeyEntry = pbPageEntry;
+ LPBYTE pbEndOfPage = pbPageEntry + EnHeader.CKeyPageSize;
+ DWORD nCKeyIndex = 0;
+
+ // Check if there is enough space in the buffer
+ if((pbPageEntry + EnHeader.CKeyPageSize) > pbDataEnd)
+ break;
- // Dump all index keys
- pbIndexKey = pEncodingEntry->EncodingKey + MD5_HASH_SIZE;
- for(DWORD j = 0; j < pEncodingEntry->KeyCount; j++, pbIndexKey += MD5_HASH_SIZE)
+ DumpKey(fp, "First CKey: %s\n", pPageHeader->FirstKey, EnHeader.CKeyLength);
+ DumpKey(fp, "Page hash: %s\n", pPageHeader->SegmentHash, MD5_HASH_SIZE);
+ fprintf(fp, "\n");
+
+ // Parse all CKey entries
+ while(pbCKeyEntry <= pbEndOfPage)
{
- // Dump the index key
- dump_print(dc, " %s\n", StringFromMD5(pbIndexKey, szMd5));
+ PCASC_CKEY_ENTRY pCKeyEntry = (PCASC_CKEY_ENTRY)pbCKeyEntry;
- // Dump the index entry as well
- if(nDumpLevel >= DUMP_LEVEL_INDEX_ENTRIES)
- {
- QueryKey.pbData = pbIndexKey;
- QueryKey.cbData = MD5_HASH_SIZE;
- pIndexEntry = FindIndexEntry(hs, &QueryKey);
- CascDumpIndexEntry(hs, dc, pIndexEntry, nDumpLevel);
- }
+ // Get pointer to the encoding entry
+ if(pCKeyEntry->EKeyCount == 0)
+ break;
+
+ // Dump this encoding entry
+ DumpCKeyEntry(pCKeyEntry, fp, nCKeyIndex++);
+
+ // Move to the next encoding entry
+ pbCKeyEntry += sizeof(FILE_CKEY_ENTRY) + ((pCKeyEntry->EKeyCount - 1) * EnHeader.CKeyLength);
+ }
+
+ // Move to the next segment
+ pbPageEntry += EnHeader.CKeyPageSize;
+ fprintf(fp, "\n");
+ }
+}
+
+void DumpEncodingManifest(TCascStorage * hs, LPBYTE pbData, ULONG cbData, FILE * fp)
+{
+ // Dump header
+ fprintf(fp, "=== ENCODING Manifest =======================================================\n");
+
+ // Dump the encoding file
+ if(hs->EncodingManifest.pbData && hs->EncodingManifest.cbData)
+ {
+ CASC_ENCODING_HEADER EnHeader;
+ LPBYTE pbFilePtr = hs->EncodingManifest.pbData;
+ DWORD cbDataSize;
+
+ // Capture the header of the ENCODING file
+ if(CaptureEncodingHeader(EnHeader, hs->EncodingManifest.pbData, hs->EncodingManifest.cbData) == ERROR_SUCCESS)
+ {
+ // Dump the ENCODING file info
+ fprintf(fp, "Magic: %u\n", EnHeader.Magic);
+ fprintf(fp, "Version: %u\n", EnHeader.Version);
+ fprintf(fp, "CKey Length: %02X\n", EnHeader.CKeyLength);
+ fprintf(fp, "EKey Length: %02X\n", EnHeader.EKeyLength);
+ fprintf(fp, "CKey page size: %08X\n", EnHeader.CKeyPageSize);
+ fprintf(fp, "CKey page count: %08X\n", EnHeader.CKeyPageCount);
+ fprintf(fp, "EKey page size: %08X\n", EnHeader.EKeyPageSize);
+ fprintf(fp, "EKey page count: %08X\n", EnHeader.EKeyPageCount);
+ fprintf(fp, "ESpec block size: %08X\n", EnHeader.ESpecBlockSize);
+ pbFilePtr += sizeof(FILE_ENCODING_HEADER);
+
+ // Dump all ESpec entries
+ DumpESpecEntries(fp, pbFilePtr, pbFilePtr + EnHeader.ESpecBlockSize);
+ pbFilePtr += EnHeader.ESpecBlockSize;
+
+ // Parse all CKey pages and dump them
+ cbDataSize = EnHeader.CKeyPageCount * (sizeof(FILE_CKEY_PAGE) + EnHeader.CKeyPageSize);
+ DumpCKeyPages(fp, EnHeader, pbFilePtr, pbFilePtr + cbDataSize);
+ pbFilePtr += cbDataSize;
}
}
else
{
- dump_print(dc, " NO ENCODING KEYS\n");
+ fprintf(fp, "(empty)\n");
}
-}
-void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs)
+ // Dump tail
+ fprintf(fp, "=============================================================================\n\n");
+}
+*/
+//-----------------------------------------------------------------------------
+// Dumping the DOWNLOAD manifest
+/*
+static void DumpDownloadEntries(FILE * fp, CASC_DOWNLOAD_HEADER & DlHeader, LPBYTE pbDownloadPtr, LPBYTE pbDownloadEnd)
{
- PCASC_INDEX_ENTRY * ppIndexEntries;
- FILE * fp;
- size_t nIndexEntries = hs->pIndexEntryMap->ItemCount;
- char szIndexKey[0x40];
+ fprintf(fp, "--- DOWNLOAD Entries --------------------------------------------------------\n");
+ fprintf(fp, "Index EKey FileSize Checksum Flags Prio\n");
+ fprintf(fp, "-------- -------------------------------- ---------- -------- -------- ----\n");
- // Create the dump file
- fp = fopen(szFileName, "wt");
- if(fp != NULL)
+ for(DWORD i = 0; i < DlHeader.EntryCount; i++)
{
- // Create linear aray
- ppIndexEntries = CASC_ALLOC(PCASC_INDEX_ENTRY, nIndexEntries);
- if(ppIndexEntries != NULL)
- {
- // Obtain the linear array of index entries
- Map_EnumObjects(hs->pIndexEntryMap, (void **)ppIndexEntries);
+ CASC_DOWNLOAD_ENTRY DlEntry;
+ char szEKey[MD5_STRING_SIZE+1];
+ char szChksum[0x40];
+ char szFlags[0x40];
+
+ if(CaptureDownloadEntry(DlHeader, DlEntry, pbDownloadPtr, pbDownloadEnd) != ERROR_SUCCESS)
+ break;
+
+ // Dump the entry
+ StringFromBinary(DlEntry.EKey, DlHeader.EKeyLength, szEKey);
+ CascStrPrintf(szChksum, _countof(szChksum), (DlHeader.EntryHasChecksum ? "%08X" : "<none> "), DlEntry.Checksum);
+ CascStrPrintf(szFlags, _countof(szFlags), (DlHeader.FlagByteSize ? "%08X" : "<none> "), DlEntry.Flags);
+ fprintf(fp, "[%06X] %-16s %010I64X %s %s %04X\n", i, szEKey, DlEntry.FileSize, szChksum, szFlags, DlEntry.Priority);
+
+ // Move to the next entry
+ pbDownloadPtr += DlHeader.EntryLength;
+ }
- // Sort the array by archive number and archive offset
- qsort_pointer_array((void **)ppIndexEntries, nIndexEntries, CompareIndexEntries_FilePos, NULL);
+ fprintf(fp, "\n");
+}
- // Parse the array
- fprintf(fp, "ArNo ArOffset FileSize IndexKey\n==== ======== ======== ================================\n");
- for(size_t i = 0; i < nIndexEntries; i++)
- {
- PCASC_INDEX_ENTRY pIndexEntry = ppIndexEntries[i];
- ULONGLONG ArchOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffsetBE);
- DWORD ArchIndex = (DWORD)(ArchOffset >> 0x1E);
- DWORD FileSize;
+static void DumpDownloadTags(FILE * fp, CASC_DOWNLOAD_HEADER & DlHeader, LPBYTE pbFilePtr, LPBYTE pbFileEnd)
+{
+ CASC_TAG_ENTRY1 DlTag;
- FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE);
- ArchOffset &= 0x3FFFFFFF;
+ fprintf(fp, "--- DOWNLOAD Tags ---------------------------------------------------------\n");
+ for(DWORD i = 0; i < DlHeader.TagCount; i++)
+ {
+ // Capture the download tag
+ if(CaptureDownloadTag(DlHeader, DlTag, pbFilePtr, pbFileEnd) != ERROR_SUCCESS)
+ break;
- fprintf(fp, " %02X %08X %08X %s\n", ArchIndex, (DWORD)ArchOffset, FileSize, StringFromBinary(pIndexEntry->IndexKey, CASC_FILE_KEY_SIZE, szIndexKey));
- }
+ // Dump the tag
+ fprintf(fp, "[%02X]: %08X = %s\n", i, DlTag.TagValue, DlTag.szTagName);
+ pbFilePtr += DlTag.TagLength;
+ }
+}
- CASC_FREE(ppIndexEntries);
- }
+void DumpDownloadManifest(TCascStorage * hs, FILE * fp)
+{
+ // Dump header
+ fprintf(fp, "=== DOWNLOAD Manifest =======================================================\n");
- fclose(fp);
+ // Dump the encoding file
+ if(hs->DownloadManifest.pbData && hs->DownloadManifest.cbData)
+ {
+ CASC_DOWNLOAD_HEADER DlHeader;
+ LPBYTE pbFileEnd = hs->DownloadManifest.pbData + hs->DownloadManifest.cbData;
+ LPBYTE pbFilePtr = hs->DownloadManifest.pbData;
+
+ // Capture the header of the ENCODING file
+ if(CaptureDownloadHeader(DlHeader, hs->DownloadManifest.pbData, hs->DownloadManifest.cbData) == ERROR_SUCCESS)
+ {
+ // Dump the DOWNLOAD header
+ fprintf(fp, "Magic: %u\n", DlHeader.Magic);
+ fprintf(fp, "Version: %u\n", DlHeader.Version);
+ fprintf(fp, "EKey Length: %02X\n", DlHeader.EKeyLength);
+ fprintf(fp, "Checksum present: %s\n", DlHeader.EntryHasChecksum ? "Yes" : "No");
+ fprintf(fp, "Entry Count: %08X\n", DlHeader.EntryCount);
+ fprintf(fp, "Tag Count: %08X\n", DlHeader.TagCount);
+ fprintf(fp, "Flag byte size: %08X\n", DlHeader.FlagByteSize);
+ fprintf(fp, "Base priority: %08X\n", DlHeader.BasePriority);
+ fprintf(fp, "Header length: %08X\n", (DWORD)DlHeader.HeaderLength);
+ fprintf(fp, "Entry length: %08X\n", (DWORD)DlHeader.EntryLength);
+ fprintf(fp, "\n");
+ pbFilePtr += DlHeader.HeaderLength;
+
+ // Dump all download entries
+ DumpDownloadEntries(fp, DlHeader, pbFilePtr, pbFileEnd);
+ pbFilePtr += DlHeader.EntryLength * DlHeader.EntryCount;
+
+ // Dump all tags
+ DumpDownloadTags(fp, DlHeader, pbFilePtr, pbFileEnd);
+ }
+ }
+ else
+ {
+ fprintf(fp, "(empty)\n");
}
+
+ // Dump tail
+ fprintf(fp, "=============================================================================\n\n");
}
+*/
+//-----------------------------------------------------------------------------
+// Public dumping functions
-void CascDumpFile(const char * szFileName, HANDLE hFile)
+void CascDumpFile(const char * szDumpFile, HANDLE hFile)
{
FILE * fp;
DWORD dwBytesRead = 1;
DWORD dwFilePos = CascSetFilePointer(hFile, 0, NULL, FILE_BEGIN);
BYTE Buffer[0x1000];
- // Create the dump file
- fp = fopen(szFileName, "wb");
+ // Create/open the dump file
+ fp = OpenDumpFile(szDumpFile);
if(fp != NULL)
{
// Read data as long as we can, write as long as we can
@@ -324,11 +479,54 @@ void CascDumpFile(const char * szFileName, HANDLE hFile)
break;
}
- // Close the local file
- fclose(fp);
-
// Restore the file pointer
CascSetFilePointer(hFile, dwFilePos, NULL, FILE_BEGIN);
+
+ // Close the dump file
+ CloseDumpFile(szDumpFile, fp);
+ }
+}
+
+void CascDumpStorage(HANDLE hStorage, const char * szDumpFile)
+{
+ TCascStorage * hs;
+ FILE * fp = stdout;
+ char szStringBuff[0x800];
+
+ // Verify the storage handle
+ hs = TCascStorage::IsValid(hStorage);
+ if(hs == NULL)
+ return;
+
+ // Create/open the dump file
+ fp = OpenDumpFile(szDumpFile);
+ if(fp != NULL)
+ {
+ // Dump the basic storage info
+ fprintf(fp, "=== Basic Storage Info ======================================================\n");
+ fprintf(fp, "DataPath: %s\n", StringFromLPTSTR(hs->szDataPath, szStringBuff, sizeof(szStringBuff)));
+ fprintf(fp, "IndexPath: %s\n", StringFromLPTSTR(hs->szIndexPath, szStringBuff, sizeof(szStringBuff)));
+ fprintf(fp, "BuildFile: %s\n", StringFromLPTSTR(hs->szBuildFile, szStringBuff, sizeof(szStringBuff)));
+ fprintf(fp, "CDN Server: %s\n", StringFromLPTSTR(hs->szCdnServers, szStringBuff, sizeof(szStringBuff)));
+ fprintf(fp, "CDN Path: %s\n", StringFromLPTSTR(hs->szCdnPath, szStringBuff, sizeof(szStringBuff)));
+ DumpKey(fp, "CDN Config Key: %s\n", hs->CdnConfigKey.pbData, hs->CdnConfigKey.cbData);
+ DumpKey(fp, "CDN Build Key: %s\n", hs->CdnBuildKey.pbData, hs->CdnBuildKey.cbData);
+ DumpKey(fp, "Archives Key: %s\n", hs->ArchivesKey.pbData, hs->ArchivesKey.cbData);
+ DumpKey(fp, "ROOT file: %s\n", hs->RootFile.CKey, CASC_CKEY_SIZE);
+ DumpKey(fp, "PATCH file: %s\n", hs->PatchFile.CKey, CASC_CKEY_SIZE);
+ DumpKey(fp, "ENCODING file: %s\n", hs->EncodingCKey.CKey, CASC_CKEY_SIZE);
+ DumpKey(fp, "DOWNLOAD file: %s\n", hs->DownloadCKey.CKey, CASC_CKEY_SIZE);
+ DumpKey(fp, "INSTALL file: %s\n", hs->InstallCKey.CKey, CASC_CKEY_SIZE);
+ fprintf(fp, "\n");
+
+ // Dump the complete ENCODING manifest
+// DumpEncodingManifest(hs, fp);
+
+ // Dump the complete ENCODING manifest
+// DumpDownloadManifest(hs, fp);
+
+ // Close the dump file
+ CloseDumpFile(szDumpFile, fp);
}
}
diff --git a/dep/CascLib/src/CascFiles.cpp b/dep/CascLib/src/CascFiles.cpp
index 3132992db4c..33f065b66d3 100644
--- a/dep/CascLib/src/CascFiles.cpp
+++ b/dep/CascLib/src/CascFiles.cpp
@@ -14,9 +14,13 @@
#include "CascCommon.h"
//-----------------------------------------------------------------------------
-// Local functions
+// Local defines
+
+typedef int (*PARSETEXTFILE)(TCascStorage * hs, void * pvListFile);
+typedef int (*PARSECSVFILE)(TCascStorage * hs, CASC_CSV & Csv);
+typedef int (*PARSE_VARIABLE)(TCascStorage * hs, const char * szVariableName, const char * szDataBegin, const char * szDataEnd, void * pvParam);
-typedef int (*PARSEINFOFILE)(TCascStorage * hs, void * pvListFile);
+#define MAX_VAR_NAME 80
//-----------------------------------------------------------------------------
// Local structures
@@ -27,47 +31,62 @@ struct TBuildFileInfo
CBLD_TYPE BuildFileType;
};
-struct TGameIdString
+struct TGameLocaleString
{
- const char * szGameInfo;
- size_t cchGameInfo;
- DWORD dwGameInfo;
+ const char * szLocale;
+ DWORD dwLocale;
};
static const TBuildFileInfo BuildTypes[] =
{
{_T(".build.info"), CascBuildInfo}, // Since HOTS build 30027, the game uses .build.info file for storage info
{_T(".build.db"), CascBuildDb}, // Older CASC storages
+ {_T("versions"), CascVersionsDb}, // Older CASC storages
{NULL, CascBuildNone}
};
static const TCHAR * DataDirs[] =
{
- _T("SC2Data"), // Starcraft II (Legacy of the Void) build 38749
- _T("Data\\Casc"), // Overwatch
+ _T("data") _T(PATH_SEP_STRING) _T("casc"), // Overwatch
+ _T("data"), // TACT casc (for Linux systems)
_T("Data"), // World of Warcraft, Diablo
+ _T("SC2Data"), // Starcraft II (Legacy of the Void) build 38749
_T("HeroesData"), // Heroes of the Storm
_T("BNTData"), // Heroes of the Storm, until build 30414
NULL,
};
-static const TGameIdString GameIds[] =
+static const TGameLocaleString LocaleStrings[] =
{
- {"Hero", 0x04, CASC_GAME_HOTS}, // Alpha build of Heroes of the Storm
- {"WoW", 0x03, CASC_GAME_WOW6}, // Alpha build of World of Warcraft - Warlords of Draenor
- {"Diablo3", 0x07, CASC_GAME_DIABLO3}, // Diablo III BETA 2.2.0
- {"Prometheus", 0x0A, CASC_GAME_OVERWATCH}, // Overwatch BETA since build 24919
- {"SC2", 0x03, CASC_GAME_STARCRAFT2}, // Starcraft II - Legacy of the Void
- {"Starcraft1", 0x0A, CASC_GAME_STARCRAFT1}, // Starcraft 1 (remake)
- {NULL, 0, 0},
+ {"enUS", CASC_LOCALE_ENUS},
+ {"koKR", CASC_LOCALE_KOKR},
+ {"frFR", CASC_LOCALE_FRFR},
+ {"deDE", CASC_LOCALE_DEDE},
+ {"zhCN", CASC_LOCALE_ZHCN},
+ {"esES", CASC_LOCALE_ESES},
+ {"zhTW", CASC_LOCALE_ZHTW},
+ {"enGB", CASC_LOCALE_ENGB},
+ {"enCN", CASC_LOCALE_ENCN},
+ {"enTW", CASC_LOCALE_ENTW},
+ {"esMX", CASC_LOCALE_ESMX},
+ {"ruRU", CASC_LOCALE_RURU},
+ {"ptBR", CASC_LOCALE_PTBR},
+ {"itIT", CASC_LOCALE_ITIT},
+ {"ptPT", CASC_LOCALE_PTPT},
+ {NULL, 0}
};
//-----------------------------------------------------------------------------
// Local functions
+static bool inline IsWhiteSpace(const char * szVarValue)
+{
+ return (0 <= szVarValue[0] && szVarValue[0] <= 0x20);
+}
+
static bool inline IsValueSeparator(const char * szVarValue)
{
- return ((0 <= szVarValue[0] && szVarValue[0] <= 0x20) || (szVarValue[0] == '|'));
+ return (IsWhiteSpace(szVarValue) || (szVarValue[0] == '|'));
}
static bool IsCharDigit(BYTE OneByte)
@@ -75,597 +94,606 @@ static bool IsCharDigit(BYTE OneByte)
return ('0' <= OneByte && OneByte <= '9');
}
-static DWORD GetLocaleMask(const char * szTag)
+static const char * CaptureDecimalInteger(const char * szDataPtr, const char * szDataEnd, PDWORD PtrValue)
{
- if(!strcmp(szTag, "enUS"))
- return CASC_LOCALE_ENUS;
+ const char * szSaveDataPtr = szDataPtr;
+ DWORD TotalValue = 0;
+ DWORD AddValue = 0;
- if(!strcmp(szTag, "koKR"))
- return CASC_LOCALE_KOKR;
+ // Skip all spaces
+ while (szDataPtr < szDataEnd && szDataPtr[0] == ' ')
+ szDataPtr++;
- if(!strcmp(szTag, "frFR"))
- return CASC_LOCALE_FRFR;
+ // Load the number
+ while (szDataPtr < szDataEnd && szDataPtr[0] != ' ')
+ {
+ // Must only contain decimal digits ('0' - '9')
+ if (!IsCharDigit(szDataPtr[0]))
+ break;
- if(!strcmp(szTag, "deDE"))
- return CASC_LOCALE_DEDE;
+ // Get the next value and verify overflow
+ AddValue = szDataPtr[0] - '0';
+ if ((TotalValue + AddValue) < TotalValue)
+ return NULL;
- if(!strcmp(szTag, "zhCN"))
- return CASC_LOCALE_ZHCN;
+ TotalValue = (TotalValue * 10) + AddValue;
+ szDataPtr++;
+ }
- if(!strcmp(szTag, "esES"))
- return CASC_LOCALE_ESES;
+ // If there were no decimal digits, we consider it failure
+ if(szDataPtr == szSaveDataPtr)
+ return NULL;
- if(!strcmp(szTag, "zhTW"))
- return CASC_LOCALE_ZHTW;
+ // Give the result
+ PtrValue[0] = TotalValue;
+ return szDataPtr;
+}
- if(!strcmp(szTag, "enGB"))
- return CASC_LOCALE_ENGB;
+static const char * CaptureSingleString(const char * szDataPtr, const char * szDataEnd, char * szBuffer, size_t ccBuffer)
+{
+ char * szBufferEnd = szBuffer + ccBuffer - 1;
- if(!strcmp(szTag, "enCN"))
- return CASC_LOCALE_ENCN;
+ // Skip all whitespaces
+ while (szDataPtr < szDataEnd && IsWhiteSpace(szDataPtr))
+ szDataPtr++;
- if(!strcmp(szTag, "enTW"))
- return CASC_LOCALE_ENTW;
+ // Copy the string
+ while (szDataPtr < szDataEnd && szBuffer < szBufferEnd && !IsWhiteSpace(szDataPtr) && szDataPtr[0] != '=')
+ *szBuffer++ = *szDataPtr++;
- if(!strcmp(szTag, "esMX"))
- return CASC_LOCALE_ESMX;
+ szBuffer[0] = 0;
+ return szDataPtr;
+}
- if(!strcmp(szTag, "ruRU"))
- return CASC_LOCALE_RURU;
+static const char * CaptureSingleHash(const char * szDataPtr, const char * szDataEnd, LPBYTE HashValue, size_t HashLength)
+{
+ const char * szHashString;
+ size_t HashStringLength = HashLength * 2;
- if(!strcmp(szTag, "ptBR"))
- return CASC_LOCALE_PTBR;
+ // Skip all whitespaces
+ while (szDataPtr < szDataEnd && IsWhiteSpace(szDataPtr))
+ szDataPtr++;
+ szHashString = szDataPtr;
- if(!strcmp(szTag, "itIT"))
- return CASC_LOCALE_ITIT;
+ // Count all hash characters
+ for (size_t i = 0; i < HashStringLength; i++)
+ {
+ if (szDataPtr >= szDataEnd || isxdigit(szDataPtr[0]) == 0)
+ return NULL;
+ szDataPtr++;
+ }
- if(!strcmp(szTag, "ptPT"))
- return CASC_LOCALE_PTPT;
+ // There must be a separator or end-of-string
+ if (szDataPtr > szDataEnd || IsWhiteSpace(szDataPtr) == false)
+ return NULL;
- return 0;
+ // Give the values
+ ConvertStringToBinary(szHashString, HashStringLength, HashValue);
+ return szDataPtr;
}
-static bool IsInfoVariable(const char * szLineBegin, const char * szLineEnd, const char * szVarName, const char * szVarType)
+static const char * CaptureHashCount(const char * szDataPtr, const char * szDataEnd, size_t * PtrHashCount)
{
- size_t nLength;
+ BYTE HashValue[MD5_HASH_SIZE];
+ size_t HashCount = 0;
- // Check the variable name
- nLength = strlen(szVarName);
- if((size_t)(szLineEnd - szLineBegin) > nLength)
+ // Capculate the hash count
+ while (szDataPtr < szDataEnd)
{
- // Check the variable name
- if(!_strnicmp(szLineBegin, szVarName, nLength))
- {
- // Skip variable name and the exclamation mark
- szLineBegin += nLength;
- if(szLineBegin < szLineEnd && szLineBegin[0] == '!')
- {
- // Skip the exclamation mark
- szLineBegin++;
+ // Check one hash
+ szDataPtr = CaptureSingleHash(szDataPtr, szDataEnd, HashValue, MD5_HASH_SIZE);
+ if (szDataPtr == NULL)
+ return NULL;
- // Check the variable type
- nLength = strlen(szVarType);
- if((size_t)(szLineEnd - szLineBegin) > nLength)
- {
- // Check the variable name
- if(!_strnicmp(szLineBegin, szVarType, nLength))
- {
- // Skip variable type and the doublecolon
- szLineBegin += nLength;
- return (szLineBegin < szLineEnd && szLineBegin[0] == ':');
- }
- }
- }
- }
+ // Skip all whitespaces
+ while (szDataPtr < szDataEnd && IsWhiteSpace(szDataPtr))
+ szDataPtr++;
+
+ HashCount++;
}
- return false;
+ // Give results
+ PtrHashCount[0] = HashCount;
+ return szDataPtr;
}
-static const char * SkipInfoVariable(const char * szLineBegin, const char * szLineEnd)
+static DWORD GetLocaleMask(const char * szTag)
{
- while(szLineBegin < szLineEnd)
+ for(size_t i = 0; LocaleStrings[i].szLocale != NULL; i++)
{
- if(szLineBegin[0] == '|')
- return szLineBegin + 1;
-
- szLineBegin++;
+ if(!strncmp(LocaleStrings[i].szLocale, szTag, 4))
+ {
+ return LocaleStrings[i].dwLocale;
+ }
}
- return NULL;
+ return 0;
}
-static TCHAR * CheckForIndexDirectory(TCascStorage * hs, const TCHAR * szSubDir)
+static bool CheckConfigFileVariable(
+ TCascStorage * hs, // Pointer to storage structure
+ const char * szLinePtr, // Pointer to the begin of the line
+ const char * szLineEnd, // Pointer to the end of the line
+ const char * szVarName, // Pointer to the variable to check
+ PARSE_VARIABLE PfnParseProc, // Pointer to the parsing function
+ void * pvParseParam) // Pointer to the parameter passed to parsing function
{
- TCHAR * szIndexPath;
+ char szVariableName[MAX_VAR_NAME];
- // Cpmbine the index path
- szIndexPath = CombinePath(hs->szDataPath, szSubDir);
- if(DirectoryExists(szIndexPath))
- {
- hs->szIndexPath = szIndexPath;
- return hs->szIndexPath;
- }
+ // Capture the variable from the line
+ szLinePtr = CaptureSingleString(szLinePtr, szLineEnd, szVariableName, sizeof(szVariableName));
+ if (szLinePtr == NULL)
+ return false;
- CASC_FREE(szIndexPath);
- return NULL;
-}
+ // Verify whether this is the variable
+ if (!CascCheckWildCard(szVariableName, szVarName))
+ return false;
-TCHAR * AppendBlobText(TCHAR * szBuffer, LPBYTE pbData, DWORD cbData, TCHAR chSeparator)
-{
- // Put the separator, if any
- if(chSeparator != 0)
- *szBuffer++ = chSeparator;
-
- // Copy the blob data as text
- for(DWORD i = 0; i < cbData; i++)
- {
- *szBuffer++ = IntToHexChar[pbData[0] >> 0x04];
- *szBuffer++ = IntToHexChar[pbData[0] & 0x0F];
- pbData++;
- }
+ // Skip the spaces and '='
+ while (szLinePtr < szLineEnd && (IsWhiteSpace(szLinePtr) || szLinePtr[0] == '='))
+ szLinePtr++;
- // Terminate the string
- *szBuffer = 0;
+ // Call the parsing function only if there is some data
+ if (szLinePtr >= szLineEnd)
+ return false;
- // Return new buffer position
- return szBuffer;
+ return (PfnParseProc(hs, szVariableName, szLinePtr, szLineEnd, pvParseParam) == ERROR_SUCCESS);
}
-static const char * CheckLineVariable(const char * szLineBegin, const char * szLineEnd, const char * szVarName)
+static int LoadHashArray(
+ PQUERY_KEY pBlob,
+ const char * szLinePtr,
+ const char * szLineEnd,
+ size_t HashCount)
{
- size_t nLineLength = (size_t)(szLineEnd - szLineBegin);
- size_t nNameLength = strlen(szVarName);
+ int nError = ERROR_NOT_ENOUGH_MEMORY;
- // If the line longer than the variable name?
- if(nLineLength > nNameLength)
+ // Allocate the blob buffer
+ pBlob->cbData = (DWORD)(HashCount * MD5_HASH_SIZE);
+ pBlob->pbData = CASC_ALLOC(BYTE, pBlob->cbData);
+ if (pBlob->pbData != NULL)
{
- if(!_strnicmp((const char *)szLineBegin, szVarName, nNameLength))
- {
- // Skip the variable name
- szLineBegin += nNameLength;
-
- // Skip the separator(s)
- while(szLineBegin < szLineEnd && IsValueSeparator(szLineBegin))
- szLineBegin++;
-
- // Check if there is "="
- if(szLineBegin >= szLineEnd || szLineBegin[0] != '=')
- return NULL;
- szLineBegin++;
-
- // Skip the separator(s)
- while(szLineBegin < szLineEnd && IsValueSeparator(szLineBegin))
- szLineBegin++;
+ LPBYTE pbBuffer = pBlob->pbData;
- // Check if there is "="
- if(szLineBegin >= szLineEnd)
- return NULL;
+ for (size_t i = 0; i < HashCount; i++)
+ {
+ // Capture the hash value
+ szLinePtr = CaptureSingleHash(szLinePtr, szLineEnd, pbBuffer, MD5_HASH_SIZE);
+ if (szLinePtr == NULL)
+ return ERROR_BAD_FORMAT;
- // Return the begin of the variable
- return szLineBegin;
+ // Move buffer
+ pbBuffer += MD5_HASH_SIZE;
}
+
+ nError = ERROR_SUCCESS;
}
- return NULL;
+ return nError;
}
-static int LoadInfoVariable(PQUERY_KEY pVarBlob, const char * szLineBegin, const char * szLineEnd, bool bHexaValue)
+static int LoadMultipleHashes(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd)
{
- const char * szLinePtr = szLineBegin;
-
- // Sanity checks
- assert(pVarBlob->pbData == NULL);
- assert(pVarBlob->cbData == 0);
+ size_t HashCount = 0;
+ int nError = ERROR_SUCCESS;
- // Check length of the variable
- while(szLinePtr < szLineEnd && szLinePtr[0] != '|')
- szLinePtr++;
+ // Retrieve the hash count
+ if (CaptureHashCount(szLineBegin, szLineEnd, &HashCount) == NULL)
+ return ERROR_BAD_FORMAT;
- // Allocate space for the blob
- if(bHexaValue)
+ // Do nothing if there is no data
+ if(HashCount != 0)
{
- // Initialize the blob
- pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) / 2);
- pVarBlob->cbData = (DWORD)((szLinePtr - szLineBegin) / 2);
- return ConvertStringToBinary(szLineBegin, (size_t)(szLinePtr - szLineBegin), pVarBlob->pbData);
+ nError = LoadHashArray(pBlob, szLineBegin, szLineEnd, HashCount);
}
- // Initialize the blob
- pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) + 1);
- pVarBlob->cbData = (DWORD)(szLinePtr - szLineBegin);
-
- // Check for success
- if(pVarBlob->pbData == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
+ return nError;
+}
- // Copy the string
- memcpy(pVarBlob->pbData, szLineBegin, pVarBlob->cbData);
- pVarBlob->pbData[pVarBlob->cbData] = 0;
- return ERROR_SUCCESS;
+// Loads a query key from the text form
+// A QueryKey is an array of "ContentKey EncodedKey1 ... EncodedKeyN"
+static int LoadQueryKey(TCascStorage * /* hs */, const char * /* szVariableName */, const char * szDataBegin, const char * szDataEnd, void * pvParam)
+{
+ return LoadMultipleHashes((PQUERY_KEY)pvParam, szDataBegin, szDataEnd);
}
-static void AppendConfigFilePath(TCHAR * szFileName, PQUERY_KEY pFileKey)
+static int LoadCKeyEntry(TCascStorage * hs, const char * szVariableName, const char * szDataPtr, const char * szDataEnd, void * pvParam)
{
- size_t nLength = _tcslen(szFileName);
+ PCASC_CKEY_ENTRY pCKeyEntry = (PCASC_CKEY_ENTRY)pvParam;
+ size_t nLength = strlen(szVariableName);
+ size_t HashCount = 0;
+
+ // If the variable ends at "-size", it means we need to capture the size
+ if((nLength > 5) && !strcmp(szVariableName + nLength - 5, "-size"))
+ {
+ DWORD ContentSize = CASC_INVALID_SIZE;
+ DWORD EncodedSize = CASC_INVALID_SIZE;
- // If there is no slash, append if
- if(nLength > 0 && szFileName[nLength - 1] != '\\' && szFileName[nLength - 1] != '/')
- szFileName[nLength++] = _T('/');
+ // Load the content size
+ szDataPtr = CaptureDecimalInteger(szDataPtr, szDataEnd, &ContentSize);
+ if(szDataPtr != NULL)
+ {
+ CaptureDecimalInteger(szDataPtr, szDataEnd, &EncodedSize);
+ pCKeyEntry->ContentSize = ContentSize;
+ pCKeyEntry->EncodedSize = EncodedSize;
+ return ERROR_SUCCESS;
+ }
+ }
+ else
+ {
+ // Get the number of hashes. It is expected to be 1 or 2
+ if(CaptureHashCount(szDataPtr, szDataEnd, &HashCount) != NULL)
+ {
+ // Capture the CKey
+ if(HashCount >= 1)
+ {
+ // Load the CKey. This should alway be there
+ szDataPtr = CaptureSingleHash(szDataPtr, szDataEnd, pCKeyEntry->CKey, MD5_HASH_SIZE);
+ if(szDataPtr == NULL)
+ return ERROR_BAD_FORMAT;
+ pCKeyEntry->Flags |= CASC_CE_HAS_CKEY;
+ }
- // Get to the end of the file name
- szFileName = szFileName + nLength;
+ // Capture EKey, if any
+ if(HashCount == 2)
+ {
+ // Load the EKey into the structure
+ szDataPtr = CaptureSingleHash(szDataPtr, szDataEnd, pCKeyEntry->EKey, MD5_HASH_SIZE);
+ if(szDataPtr == NULL)
+ return ERROR_BAD_FORMAT;
+ pCKeyEntry->Flags |= CASC_CE_HAS_EKEY;
+
+ // Increment the number of EKey entries loaded from text build file
+ hs->EKeyEntries++;
+ }
- // Append the "config" directory
- _tcscpy(szFileName, _T("config"));
- szFileName += 6;
+ return (HashCount == 1 || HashCount == 2) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
+ }
+ }
- // Append the first level directory
- szFileName = AppendBlobText(szFileName, pFileKey->pbData, 1, _T('/'));
- szFileName = AppendBlobText(szFileName, pFileKey->pbData + 1, 1, _T('/'));
- szFileName = AppendBlobText(szFileName, pFileKey->pbData, pFileKey->cbData, _T('/'));
+ // Unrecognized
+ return ERROR_BAD_FORMAT;
}
-static DWORD GetBlobCount(const char * szLineBegin, const char * szLineEnd)
+static int LoadVfsRootEntry(TCascStorage * hs, const char * szVariableName, const char * szDataPtr, const char * szDataEnd, void * pvParam)
{
- DWORD dwBlobCount = 0;
-
- // Until we find an end of the line
- while(szLineBegin < szLineEnd)
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ CASC_ARRAY * pArray = (CASC_ARRAY *)pvParam;
+ const char * szVarPtr = szVariableName;
+ const char * szVarEnd = szVarPtr + strlen(szVarPtr);
+ DWORD VfsRootIndex = CASC_INVALID_INDEX;
+
+ // Skip the "vfs-" part
+ if (!strncmp(szVariableName, "vfs-", 4))
{
- // Skip the blob
- while(szLineBegin < szLineEnd && IsValueSeparator(szLineBegin) == false)
- szLineBegin++;
+ // Then, there must be a decimal number as index
+ if ((szVarPtr = CaptureDecimalInteger(szVarPtr + 4, szVarEnd, &VfsRootIndex)) != NULL)
+ {
+ // We expect the array to be initialized
+ assert(pArray->IsInitialized());
- // Increment the number of blobs
- dwBlobCount++;
+ // The index of the key must not be NULL
+ if(VfsRootIndex != 0)
+ {
+ // Make sure that he entry is in the array
+ pCKeyEntry = (PCASC_CKEY_ENTRY)pArray->ItemAt(VfsRootIndex - 1);
+ if(pCKeyEntry == NULL)
+ {
+ // Insert a new entry
+ pCKeyEntry = (PCASC_CKEY_ENTRY)pArray->InsertAt(VfsRootIndex - 1);
+ if(pCKeyEntry == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Initialize the new entry
+ pCKeyEntry->Init();
+ }
- // Skip the separator
- while(szLineBegin < szLineEnd && IsValueSeparator(szLineBegin))
- szLineBegin++;
+ // Call just a normal parse function
+ return LoadCKeyEntry(hs, szVariableName, szDataPtr, szDataEnd, pCKeyEntry);
+ }
+ }
}
- return dwBlobCount;
+ return ERROR_SUCCESS;
}
-static int LoadBlobArray(
- PQUERY_KEY pBlob,
- const char * szLineBegin,
- const char * szLineEnd,
- DWORD dwMaxBlobs)
+static int LoadBuildProductId(TCascStorage * hs, const char * /* szVariableName */, const char * szDataBegin, const char * szDataEnd, void * /* pvParam */)
{
- LPBYTE pbBufferEnd = pBlob->pbData + pBlob->cbData;
- LPBYTE pbBuffer = pBlob->pbData;
- int nError = ERROR_SUCCESS;
+ DWORD dwBuildUid = 0;
- // Sanity check
- assert(pBlob->pbData != NULL);
- assert(pBlob->cbData != 0);
-
- // Until we find an end of the line
- while(szLineBegin < szLineEnd && dwMaxBlobs > 0)
+ // Convert up to 4 chars to DWORD
+ for(size_t i = 0; i < 4 && szDataBegin < szDataEnd; i++)
{
- const char * szBlobEnd = szLineBegin;
-
- // Find the end of the text blob
- while(szBlobEnd < szLineEnd && IsValueSeparator(szBlobEnd) == false)
- szBlobEnd++;
-
- // Verify the length of the found blob
- if((szBlobEnd - szLineBegin) != MD5_STRING_SIZE)
- return ERROR_BAD_FORMAT;
-
- // Verify if there is enough space in the buffer
- if((pbBufferEnd - pbBuffer) < MD5_HASH_SIZE)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Perform the conversion
- nError = ConvertStringToBinary(szLineBegin, MD5_STRING_SIZE, pbBuffer);
- if(nError != ERROR_SUCCESS)
- return nError;
+ dwBuildUid = (dwBuildUid << 0x08) | (BYTE)szDataBegin[0];
+ szDataBegin++;
+ }
- // Move pointers
- pbBuffer += MD5_HASH_SIZE;
- dwMaxBlobs--;
+ // Product-specific. See https://wowdev.wiki/TACT#Products
+ switch(dwBuildUid)
+ {
+ case 0x00006433: // 'd3'
+ case 0x00643364: // 'd3b': Diablo 3 Beta (2013)
+ case 0x6433636e: // 'd3cn': Diablo 3 China
+ case 0x00643374: // 'd3t': Diablo 3 Test
+ hs->szProductName = "Diablo 3";
+ hs->Product = Diablo3;
+ break;
- // Skip the separator
- while(szBlobEnd < szLineEnd && IsValueSeparator(szBlobEnd))
- szBlobEnd++;
- szLineBegin = szBlobEnd;
- }
+ case 0x64737432: // 'dst2':
+ hs->szProductName = "Destiny 2";
+ hs->Product = Destiny2;
+ break;
- return nError;
-}
+ case 0x00626e74: // 'bnt': Heroes of the Storm Alpha
+ case 0x6865726f: // 'hero': Heroes of the Storm Retail
+ case 0x73746f72: // 'stor': Heroes of the Storm (deprecated)
+ hs->szProductName = "Heroes Of The Storm";
+ hs->Product = HeroesOfTheStorm;
+ break;
-static int LoadMultipleBlobs(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd, DWORD dwBlobCount)
-{
- size_t nLength = (szLineEnd - szLineBegin);
+ case 0x0070726f: // 'pro':
+ case 0x70726f63: // 'proc':
+ case 0x70726f64: // 'prod': "prodev": Overwatch Dev
+ case 0x70726f65: // 'proe': Not on public CDNs
+ case 0x70726f74: // 'prot': Overwatch Test
+ case 0x70726f76: // 'prov': Overwatch Vendor
+ case 0x70726f6d: // 'prom': "proms": Overwatch World Cup Viewer
+ hs->szProductName = "Overwatch";
+ hs->Product = Overwatch;
+ break;
- // We expect each blob to have length of the encoding key and one space between
- if(nLength > (dwBlobCount * MD5_STRING_SIZE) + ((dwBlobCount - 1) * sizeof(char)))
- return ERROR_INVALID_PARAMETER;
+ case 0x00007331: // 's1': StarCraft 1
+ case 0x00733161: // 's1a': Starcraft 1 Alpha
+ case 0x00733174: // 's1t': StarCraft 1 Test
+ hs->szProductName = "Starcraft 1";
+ hs->Product = StarCraft1;
+ break;
- // Allocate the blob buffer
- pBlob->pbData = CASC_ALLOC(BYTE, dwBlobCount * MD5_HASH_SIZE);
- if(pBlob->pbData == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
+ case 0x00007332: // 's2': StarCraft 2
+ case 0x00733262: // 's2b': Starcraft 2 Beta
+ case 0x00733274: // 's2t': StarCraft 2 Test
+ case 0x00736332: // 'sc2': StarCraft 2 (deprecated)
+ hs->szProductName = "Starcraft 2";
+ hs->Product = StarCraft2;
+ break;
- // Set the buffer size and load the blob array
- pBlob->cbData = dwBlobCount * MD5_HASH_SIZE;
- return LoadBlobArray(pBlob, szLineBegin, szLineEnd, dwBlobCount);
-}
+ case 0x76697065: // "viper", "viperdev", "viperv1": Call of Duty Black Ops 4
+ hs->szProductName = "Call Of Duty Black Ops 4";
+ hs->Product = CallOfDutyBlackOps4;
+ break;
-static int LoadMultipleBlobs(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd)
-{
- return LoadMultipleBlobs(pBlob, szLineBegin, szLineEnd, GetBlobCount(szLineBegin, szLineEnd));
-}
+ case 0x00007733: // 'w3': Warcraft III
+ case 0x00773374: // 'w3t': Warcraft III Public Test
+ case 0x77617233: // 'war3': Warcraft III (old)
+ hs->szProductName = "WarCraft 3";
+ hs->Product = WarCraft3;
+ break;
-static int LoadSingleBlob(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd)
-{
- return LoadMultipleBlobs(pBlob, szLineBegin, szLineEnd, 1);
-}
+ case 0x00776f77: // 'wow': World of Warcraft
+ case 0x776f775f: // "wow_beta", "wow_classic", "wow_classic_beta"
+ case 0x776f7764: // "wowdev", "wowdemo"
+ case 0x776f7765: // "wowe1", "wowe3", "wowe3"
+ case 0x776f7774: // 'wowt': World of Warcraft Test
+ case 0x776f7776: // 'wowv': World of Warcraft Vendor
+ case 0x776f777a: // 'wowz': World of Warcraft Submission (previously Vendor)
+ hs->szProductName = "World Of Warcraft";
+ hs->Product = WorldOfWarcraft;
+ break;
-static int GetGameType(TCascStorage * hs, const char * szVarBegin, const char * szLineEnd)
-{
- // Go through all games that we support
- for(size_t i = 0; GameIds[i].szGameInfo != NULL; i++)
- {
- // Check the length of the variable
- if((size_t)(szLineEnd - szVarBegin) == GameIds[i].cchGameInfo)
- {
- // Check the string
- if(!_strnicmp(szVarBegin, GameIds[i].szGameInfo, GameIds[i].cchGameInfo))
- {
- hs->dwGameInfo = GameIds[i].dwGameInfo;
- return ERROR_SUCCESS;
- }
- }
+ default:
+ hs->szProductName = "Unknown Product";
+ hs->Product = UnknownProduct;
+ break;
}
- // Unknown/unsupported game
- assert(false);
- return ERROR_BAD_FORMAT;
+ return ERROR_SUCCESS;
}
// "B29049"
// "WOW-18125patch6.0.1"
// "30013_Win32_2_2_0_Ptr_ptr"
// "prometheus-0_8_0_0-24919"
-static int GetBuildNumber(TCascStorage * hs, const char * szVarBegin, const char * szLineEnd)
+static int LoadBuildNumber(TCascStorage * hs, const char * /* szVariableName */, const char * szDataBegin, const char * szDataEnd, void * /* pvParam */)
{
DWORD dwBuildNumber = 0;
+ DWORD dwMaxValue = 0;
- // Skip all non-digit characters
- while(szVarBegin < szLineEnd)
+ // Parse the string and take the largest decimal numeric value
+ // "build-name = 1.21.5.4037-retail"
+ while(szDataBegin < szDataEnd)
{
// There must be at least three digits (build 99 anyone?)
- if(IsCharDigit(szVarBegin[0]) && IsCharDigit(szVarBegin[1]) && IsCharDigit(szVarBegin[2]))
+ if(IsCharDigit(szDataBegin[0]))
{
- // Convert the build number string to value
- while(szVarBegin < szLineEnd && IsCharDigit(szVarBegin[0]))
- dwBuildNumber = (dwBuildNumber * 10) + (*szVarBegin++ - '0');
- break;
+ dwBuildNumber = (dwBuildNumber * 10) + (szDataBegin[0] - '0');
+ dwMaxValue = CASCLIB_MAX(dwBuildNumber, dwMaxValue);
+ }
+ else
+ {
+ // Reset build number when we find non-digit char
+ dwBuildNumber = 0;
}
// Move to the next
- szVarBegin++;
+ szDataBegin++;
}
- assert(dwBuildNumber != 0);
- hs->dwBuildNumber = dwBuildNumber;
- return (dwBuildNumber != 0) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
+ // If not there, just take value from build file
+ if((hs->dwBuildNumber = dwMaxValue) == 0)
+ hs->dwBuildNumber = hs->CdnBuildKey.pbData[0] % 100;
+ return ERROR_BAD_FORMAT;
}
-static int GetDefaultLocaleMask(TCascStorage * hs, PQUERY_KEY pTagsString)
+static int LoadQueryKey(const CASC_CSV_COLUMN & Column, QUERY_KEY & Key)
{
- char * szTagEnd = (char *)pTagsString->pbData + pTagsString->cbData;
- char * szTagPtr = (char *)pTagsString->pbData;
- char * szNext;
+ // Check the input data
+ if (Column.szValue == NULL)
+ return ERROR_BUFFER_OVERFLOW;
+ if (Column.nLength != MD5_STRING_SIZE)
+ return ERROR_BAD_FORMAT;
+
+ return LoadHashArray(&Key, Column.szValue, Column.szValue + Column.nLength, 1);
+}
+
+static int GetDefaultLocaleMask(TCascStorage * hs, const CASC_CSV_COLUMN & Column)
+{
+ const char * szTagEnd = Column.szValue + Column.nLength;
+ const char * szTagPtr = Column.szValue;
DWORD dwLocaleMask = 0;
while(szTagPtr < szTagEnd)
{
- // Get the next part
- szNext = strchr(szTagPtr, ' ');
- if(szNext != NULL)
- *szNext++ = 0;
-
- // Check whether the current tag is a language identifier
- dwLocaleMask = dwLocaleMask | GetLocaleMask(szTagPtr);
-
- // Get the next part
- if(szNext == NULL)
- break;
-
- // Skip spaces
- while(szNext < szTagEnd && szNext[0] == ' ')
- szNext++;
- szTagPtr = szNext;
+ // Could this be a locale string?
+ if((szTagPtr + 4) <= szTagEnd && (szTagPtr[4] == ',' || szTagPtr[4] == ' '))
+ {
+ // Check whether the current tag is a language identifier
+ dwLocaleMask = dwLocaleMask | GetLocaleMask(szTagPtr);
+ szTagPtr += 4;
+ }
+ else
+ {
+ szTagPtr++;
+ }
}
hs->dwDefaultLocale = dwLocaleMask;
return ERROR_SUCCESS;
}
-static void * FetchAndVerifyConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey)
+static int ParseFile_CDNS(TCascStorage * hs, CASC_CSV & Csv)
{
- TCHAR * szFileName;
- void * pvListFile = NULL;
+ const char * szWantedRegion = hs->szRegion;
+ TCHAR szCdnServers[MAX_PATH];
+ TCHAR szCdnPath[MAX_PATH];
+ size_t nLineCount;
- // Construct the local file name
- szFileName = CascNewStr(hs->szDataPath, 8 + 3 + 3 + 32);
- if(szFileName != NULL)
- {
- // Add the part where the config file path is
- AppendConfigFilePath(szFileName, pFileKey);
+ // Fix the region
+ if(szWantedRegion == NULL || !_stricmp(szWantedRegion, "beta") || !_stricmp(szWantedRegion, "xx"))
+ szWantedRegion = "us";
+
+ // Determine the row count
+ nLineCount = Csv.GetLineCount();
- // Load and verify the external listfile
- pvListFile = ListFile_OpenExternal(szFileName);
- if(pvListFile != NULL)
+ // Find the active config
+ for (size_t i = 0; i < nLineCount; i++)
+ {
+ // Is it the version we are looking for?
+ if(!strcmp(Csv[i]["Name!STRING:0"].szValue, szWantedRegion))
{
- if(!ListFile_VerifyMD5(pvListFile, pFileKey->pbData))
- {
- ListFile_Free(pvListFile);
- pvListFile = NULL;
- }
- }
+ // Save the list of CDN servers
+ CascStrCopy(szCdnServers, _countof(szCdnServers), Csv[i]["Hosts!STRING:0"].szValue);
+ hs->szCdnServers = CascNewStr(szCdnServers);
+
+ // Save the CDN subpath
+ CascStrCopy(szCdnPath, _countof(szCdnPath), Csv[i]["Path!STRING:0"].szValue);
+ hs->szCdnPath = CascNewStr(szCdnPath);
- // Free the file name
- CASC_FREE(szFileName);
+ // Check and return result
+ return (hs->szCdnServers && hs->szCdnPath) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
+ }
}
- return pvListFile;
+ return ERROR_FILE_NOT_FOUND;
}
-static int ParseFile_BuildInfo(TCascStorage * hs, void * pvListFile)
+static int ParseFile_BuildInfo(TCascStorage * hs, CASC_CSV & Csv)
{
- QUERY_KEY Active = {NULL, 0};
- QUERY_KEY TagString = {NULL, 0};
- QUERY_KEY CdnHost = {NULL, 0};
- QUERY_KEY CdnPath = {NULL, 0};
- const char * szLinePtr1;
- const char * szLineEnd1;
- const char * szLinePtr2;
- const char * szLineEnd2;
- size_t nLength1;
- size_t nLength2;
- int nError = ERROR_BAD_FORMAT;
-
- // Extract the first line, cotaining the headers
- nLength1 = ListFile_GetNextLine(pvListFile, &szLinePtr1, &szLineEnd1);
- if(nLength1 == 0)
- return ERROR_BAD_FORMAT;
+ size_t nLineCount = Csv.GetLineCount();
+ int nError;
- // Now parse the second and the next lines. We are looking for line
- // with "Active" set to 1
- for(;;)
+ // Find the active config
+ for(size_t i = 0; i < nLineCount; i++)
{
- // Read the next line
- nLength2 = ListFile_GetNextLine(pvListFile, &szLinePtr2, &szLineEnd2);
- if(nLength2 == 0)
- break;
-
- // Parse all variables
- while(szLinePtr1 < szLineEnd1)
+ // Is that build config active?
+ if (!strcmp(Csv[i]["Active!DEC:1"].szValue, "1"))
{
- // Check for variables we need
- if(IsInfoVariable(szLinePtr1, szLineEnd1, "Active", "DEC"))
- LoadInfoVariable(&Active, szLinePtr2, szLineEnd2, false);
- if(IsInfoVariable(szLinePtr1, szLineEnd1, "Build Key", "HEX"))
- LoadInfoVariable(&hs->CdnBuildKey, szLinePtr2, szLineEnd2, true);
- if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Key", "HEX"))
- LoadInfoVariable(&hs->CdnConfigKey, szLinePtr2, szLineEnd2, true);
- if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Hosts", "STRING"))
- LoadInfoVariable(&CdnHost, szLinePtr2, szLineEnd2, false);
- if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Path", "STRING"))
- LoadInfoVariable(&CdnPath, szLinePtr2, szLineEnd2, false);
- if(IsInfoVariable(szLinePtr1, szLineEnd1, "Tags", "STRING"))
- LoadInfoVariable(&TagString, szLinePtr2, szLineEnd2, false);
-
- // Move both line pointers
- szLinePtr1 = SkipInfoVariable(szLinePtr1, szLineEnd1);
- if(szLinePtr1 == NULL)
- break;
-
- szLinePtr2 = SkipInfoVariable(szLinePtr2, szLineEnd2);
- if(szLinePtr2 == NULL)
- break;
- }
+ // Extract the CDN build key
+ nError = LoadQueryKey(Csv[i]["Build Key!HEX:16"], hs->CdnBuildKey);
+ if (nError != ERROR_SUCCESS)
+ return nError;
+
+ // Extract the CDN config key
+ nError = LoadQueryKey(Csv[i]["CDN Key!HEX:16"], hs->CdnConfigKey);
+ if (nError != ERROR_SUCCESS)
+ return nError;
+
+ // If we found tags, we can extract language build from it
+ GetDefaultLocaleMask(hs, Csv[i]["Tags!STRING:0"]);
+
+ // If we found version, extract a build number
+ const CASC_CSV_COLUMN & VerColumn = Csv[i]["Version!STRING:0"];
+ if(VerColumn.szValue && VerColumn.nLength)
+ {
+ LoadBuildNumber(hs, NULL, VerColumn.szValue, VerColumn.szValue + VerColumn.nLength, NULL);
+ }
- // Stop parsing if found active config
- if(Active.pbData != NULL && *Active.pbData == '1')
- break;
+ // Verify all variables
+ return (hs->CdnBuildKey.pbData != NULL && hs->CdnConfigKey.pbData != NULL) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
+ }
+ }
- // Free the blobs
- FreeCascBlob(&Active);
- FreeCascBlob(&hs->CdnBuildKey);
- FreeCascBlob(&hs->CdnConfigKey);
- FreeCascBlob(&CdnHost);
- FreeCascBlob(&CdnPath);
- FreeCascBlob(&TagString);
+ return ERROR_FILE_NOT_FOUND;
+}
- // Rewind column names pointer back to start of line
- szLinePtr1 = szLineEnd1 - nLength1;
- }
+static int ParseFile_VersionsDb(TCascStorage * hs, CASC_CSV & Csv)
+{
+ size_t nLineCount = Csv.GetLineCount();
+ int nError;
- // All four must be present
- if(hs->CdnBuildKey.pbData != NULL &&
- hs->CdnConfigKey.pbData != NULL &&
- CdnHost.pbData != NULL &&
- CdnPath.pbData != NULL)
+ // Find the active config
+ for (size_t i = 0; i < nLineCount; i++)
{
- // Merge the CDN host and CDN path
- hs->szUrlPath = CASC_ALLOC(TCHAR, CdnHost.cbData + CdnPath.cbData + 1);
- if(hs->szUrlPath != NULL)
+ // Either take the version required or take the first one
+ if (hs->szRegion == NULL || !strcmp(Csv[i]["Region!STRING:0"].szValue, hs->szRegion))
{
- CopyString(hs->szUrlPath, (char *)CdnHost.pbData, CdnHost.cbData);
- CopyString(hs->szUrlPath + CdnHost.cbData, (char *)CdnPath.pbData, CdnPath.cbData);
- nError = ERROR_SUCCESS;
+ // Extract the CDN build key
+ nError = LoadQueryKey(Csv[i]["BuildConfig!HEX:16"], hs->CdnBuildKey);
+ if (nError != ERROR_SUCCESS)
+ return nError;
+
+ // Extract the CDN config key
+ nError = LoadQueryKey(Csv[i]["CDNConfig!HEX:16"], hs->CdnConfigKey);
+ if (nError != ERROR_SUCCESS)
+ return nError;
+
+ const CASC_CSV_COLUMN & VerColumn = Csv[i]["VersionsName!String:0"];
+ if (VerColumn.szValue && VerColumn.nLength)
+ {
+ LoadBuildNumber(hs, NULL, VerColumn.szValue, VerColumn.szValue + VerColumn.nLength, NULL);
+ }
+
+ // Verify all variables
+ return (hs->CdnBuildKey.pbData != NULL && hs->CdnConfigKey.pbData != NULL) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
}
}
- // If we found tags, we can extract language build from it
- if(TagString.pbData != NULL)
- GetDefaultLocaleMask(hs, &TagString);
-
- FreeCascBlob(&CdnHost);
- FreeCascBlob(&CdnPath);
- FreeCascBlob(&TagString);
- FreeCascBlob(&Active);
- return nError;
+ return ERROR_FILE_NOT_FOUND;
}
-static int ParseFile_BuildDb(TCascStorage * hs, void * pvListFile)
+static int ParseFile_BuildDb(TCascStorage * hs, CASC_CSV & Csv)
{
- const char * szLinePtr;
- const char * szLineEnd;
- char szOneLine[0x200];
- size_t nLength;
int nError;
- // Load the single line from the text file
- nLength = ListFile_GetNextLine(pvListFile, szOneLine, _maxchars(szOneLine));
- if(nLength == 0)
- return ERROR_BAD_FORMAT;
-
- // Set the line range
- szLinePtr = szOneLine;
- szLineEnd = szOneLine + nLength;
-
// Extract the CDN build key
- nError = LoadInfoVariable(&hs->CdnBuildKey, szLinePtr, szLineEnd, true);
- if(nError == ERROR_SUCCESS)
- {
- // Skip the variable
- szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd);
-
- // Load the CDN config hash
- nError = LoadInfoVariable(&hs->CdnConfigKey, szLinePtr, szLineEnd, true);
- if(nError == ERROR_SUCCESS)
- {
- // Skip the variable
- szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd);
+ nError = LoadQueryKey(Csv[CSV_ZERO][CSV_ZERO], hs->CdnBuildKey);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- // Skip the Locale/OS/code variable
- szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd);
+ // Extract the CDN config key
+ nError = LoadQueryKey(Csv[CSV_ZERO][1], hs->CdnConfigKey);
+ if (nError != ERROR_SUCCESS)
+ return nError;
- // Load the URL
- hs->szUrlPath = CascNewStrFromAnsi(szLinePtr, szLineEnd);
- if(hs->szUrlPath == NULL)
- nError = ERROR_NOT_ENOUGH_MEMORY;
- }
- }
+ // Load the the tags
+ GetDefaultLocaleMask(hs, Csv[CSV_ZERO][2]);
// Verify all variables
- if(hs->CdnBuildKey.pbData == NULL || hs->CdnConfigKey.pbData == NULL || hs->szUrlPath == NULL)
- nError = ERROR_BAD_FORMAT;
- return nError;
+ return (hs->CdnBuildKey.pbData != NULL && hs->CdnConfigKey.pbData != NULL) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
}
-static int LoadCdnConfigFile(TCascStorage * hs, void * pvListFile)
+static int ParseFile_CdnConfig(TCascStorage * hs, void * pvListFile)
{
const char * szLineBegin;
- const char * szVarBegin;
const char * szLineEnd;
int nError = ERROR_SUCCESS;
@@ -676,37 +704,28 @@ static int LoadCdnConfigFile(TCascStorage * hs, void * pvListFile)
if(!ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd))
break;
- // Archive group
- szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "archive-group");
- if(szVarBegin != NULL)
- {
- nError = LoadSingleBlob(&hs->ArchivesGroup, szVarBegin, szLineEnd);
+ // CDN key of ARCHIVE-GROUP. Archive-group is actually a very special '.index' file.
+ // It is essentially a merger of all .index files, with a structure change
+ // When ".index" added after the ARCHIVE-GROUP, we get file name in "indices" folder
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "archive-group", LoadQueryKey, &hs->ArchiveGroup))
continue;
- }
- // Archives
- szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "archives");
- if(szVarBegin != NULL)
- {
- nError = LoadMultipleBlobs(&hs->ArchivesKey, szVarBegin, szLineEnd);
+ // CDN key of all archives. When ".index" added to the string, we get file name in "indices" folder
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "archives", LoadQueryKey, &hs->ArchivesKey))
continue;
- }
- // Patch archive group
- szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "patch-archive-group");
- if(szVarBegin != NULL)
- {
- LoadSingleBlob(&hs->PatchArchivesGroup, szVarBegin, szLineEnd);
+ // CDN keys of patch archives (needs research)
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "patch-archives", LoadQueryKey, &hs->PatchArchivesKey))
continue;
- }
- // Patch archives
- szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "patch-archives");
- if(szVarBegin != NULL)
- {
- nError = LoadMultipleBlobs(&hs->PatchArchivesKey, szVarBegin, szLineEnd);
+ // CDN key of probably the combined patch index file (needs research)
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "patch-archive-group", LoadQueryKey, &hs->PatchArchivesGroup))
+ continue;
+
+ // List of build configs this config supports (optional)
+ // Points to file: "data\config\%02X\%02X\%s
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "builds", LoadQueryKey, &hs->BuildFiles))
continue;
- }
}
// Check if all required fields are present
@@ -716,78 +735,79 @@ static int LoadCdnConfigFile(TCascStorage * hs, void * pvListFile)
return nError;
}
-static int LoadCdnBuildFile(TCascStorage * hs, void * pvListFile)
+static int ParseFile_CdnBuild(TCascStorage * hs, void * pvListFile)
{
const char * szLineBegin;
- const char * szVarBegin;
const char * szLineEnd = NULL;
- int nError = ERROR_SUCCESS;
+ int nError;
- for(;;)
+ // Initialize the empty VFS array
+ nError = hs->VfsRootList.Create<CASC_CKEY_ENTRY>(0x10);
+ if (nError != ERROR_SUCCESS)
+ return nError;
+
+ // Parse all variables
+ while(ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd) != 0)
{
- // Get the next line
- if(!ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd))
- break;
+ // Product name and build name
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "build-uid", LoadBuildProductId, NULL))
+ continue;
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "build-name", LoadBuildNumber, NULL))
+ continue;
- // Game name
- szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "build-product");
- if(szVarBegin != NULL)
- {
- GetGameType(hs, szVarBegin, szLineEnd);
+ // Content key of the ROOT file. Look this up in ENCODING to get the encoded key
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "root*", LoadCKeyEntry, &hs->RootFile))
continue;
- }
- // Game build number
- szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "build-name");
- if(szVarBegin != NULL)
- {
- GetBuildNumber(hs, szVarBegin, szLineEnd);
+ // Content key [+ encoded key] of the INSTALL file
+ // If EKey is absent, you need to query the ENCODING file for it
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "install*", LoadCKeyEntry, &hs->InstallCKey))
continue;
- }
- // Root
- szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "root");
- if(szVarBegin != NULL)
- {
- LoadSingleBlob(&hs->RootKey, szVarBegin, szLineEnd);
+ // Content key [+ encoded key] of the DOWNLOAD file
+ // If EKey is absent, you need to query the ENCODING file for it
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "download*", LoadCKeyEntry, &hs->DownloadCKey))
continue;
- }
- // Patch
- szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "patch");
- if(szVarBegin != NULL)
- {
- LoadSingleBlob(&hs->PatchKey, szVarBegin, szLineEnd);
+ // Content key + encoded key of the ENCODING file. Contains CKey+EKey
+ // If either none or 1 is found, the game (at least Wow) switches to plain-data(?). Seen in build 20173
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "encoding*", LoadCKeyEntry, &hs->EncodingCKey))
continue;
- }
- // Download
- szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "download");
- if(szVarBegin != NULL)
- {
- LoadSingleBlob(&hs->DownloadKey, szVarBegin, szLineEnd);
+ // PATCH file
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "patch*", LoadCKeyEntry, &hs->PatchFile))
continue;
- }
- // Install
- szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "install");
- if(szVarBegin != NULL)
- {
- LoadSingleBlob(&hs->InstallKey, szVarBegin, szLineEnd);
+ // SIZE file
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "size*", LoadCKeyEntry, &hs->SizeFile))
continue;
- }
- // Encoding keys
- szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "encoding");
- if(szVarBegin != NULL)
- {
- nError = LoadMultipleBlobs(&hs->EncodingKey, szVarBegin, szLineEnd, 2);
+ // VFS root file (the root file of the storage VFS)
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "vfs-root*", LoadCKeyEntry, &hs->VfsRoot))
+ continue;
+
+ // Load a directory entry to the VFS
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "vfs-*", LoadVfsRootEntry, &hs->VfsRootList))
continue;
+ }
+
+ // Special case: Some builds of WoW (22267) don't have the variable in the build file
+ if(hs->szProductName == NULL || hs->Product == UnknownProduct)
+ {
+ if(hs->szCdnPath != NULL)
+ {
+ TCHAR * szPlainName = (TCHAR *)GetPlainFileName(hs->szCdnPath);
+
+ if(szPlainName[0] == 'w' && szPlainName[1] == 'o' && szPlainName[2] == 'w')
+ {
+ hs->szProductName = "World Of Warcraft";
+ hs->Product = WorldOfWarcraft;
+ }
}
}
- // Check the encoding keys
- if(hs->EncodingKey.pbData == NULL || hs->EncodingKey.cbData != MD5_HASH_SIZE * 2)
+ // Both CKey and EKey of ENCODING file is required
+ if((hs->EncodingCKey.Flags & (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY)) != (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY))
return ERROR_BAD_FORMAT;
return nError;
}
@@ -807,6 +827,7 @@ static int CheckDataDirectory(TCascStorage * hs, TCHAR * szDirectory)
// Does that directory exist?
if(DirectoryExists(szDataPath))
{
+ hs->szRootPath = CascNewStr(szDirectory);
hs->szDataPath = szDataPath;
return ERROR_SUCCESS;
}
@@ -819,91 +840,328 @@ static int CheckDataDirectory(TCascStorage * hs, TCHAR * szDirectory)
return nError;
}
+static int LoadCsvFile(TCascStorage * hs, const TCHAR * szFileName, PARSECSVFILE PfnParseProc, bool bHasHeader)
+{
+ CASC_CSV Csv(0x40, bHasHeader);
+ int nError = Csv.Load(szFileName);
-//-----------------------------------------------------------------------------
-// Public functions
+ // Load the external file to memory
+ if (nError == ERROR_SUCCESS)
+ nError = PfnParseProc(hs, Csv);
+ return nError;
+}
-int LoadBuildInfo(TCascStorage * hs)
+static const TCHAR * ExtractCdnServerName(TCHAR * szServerName, size_t cchServerName, const TCHAR * szCdnServers)
{
- PARSEINFOFILE PfnParseProc = NULL;
- void * pvListFile;
- int nError = ERROR_SUCCESS;
+ const TCHAR * szSeparator;
- switch(hs->BuildFileType)
+ if(szCdnServers[0] != 0)
{
- case CascBuildInfo:
- PfnParseProc = ParseFile_BuildInfo;
- break;
+ szSeparator = _tcschr(szCdnServers, _T(' '));
+ if(szSeparator != NULL)
+ {
+ // Copy one server name
+ CascStrCopy(szServerName, cchServerName, szCdnServers, (szSeparator - szCdnServers));
- case CascBuildDb:
- PfnParseProc = ParseFile_BuildDb;
- break;
+ // Skip all separators
+ while(szSeparator[0] == ' ' || szSeparator[0] == 0x09)
+ szSeparator++;
+ return szSeparator;
+ }
+ else
+ {
+ CascStrCopy(szServerName, MAX_PATH, szCdnServers);
+ return szCdnServers + _tcslen(szCdnServers);
+ }
+ }
- default:
- nError = ERROR_NOT_SUPPORTED;
- break;
+ return NULL;
+}
+
+static int ForcePathExist(const TCHAR * szFileName, bool bIsFileName)
+{
+ TCHAR * szLocalPath;
+ size_t nIndex;
+ bool bFirstSeparator = false;
+ int nError = ERROR_NOT_ENOUGH_MEMORY;
+
+ // Sanity checks
+ if(szFileName && szFileName[0])
+ {
+ szLocalPath = CascNewStr(szFileName);
+ if(szLocalPath != NULL)
+ {
+ // Get the end of search
+ if(bIsFileName)
+ CutLastPathPart(szLocalPath);
+
+ // Check the entire path
+ if(_taccess(szLocalPath, 0) != 0)
+ {
+ // Searth the entire path
+ for(nIndex = 0; szLocalPath[nIndex] != 0; nIndex++)
+ {
+ if(szLocalPath[nIndex] == '\\' || szLocalPath[nIndex] == '/')
+ {
+ // Cut the path and verify whether the folder/file exists
+ szLocalPath[nIndex] = 0;
+
+ // Skip the very first separator
+ if(bFirstSeparator == true)
+ {
+ // Is it there?
+ if(DirectoryExists(szLocalPath) == false && MakeDirectory(szLocalPath) == false)
+ {
+ nError = ERROR_PATH_NOT_FOUND;
+ break;
+ }
+ }
+
+ // Restore the character
+ szLocalPath[nIndex] = PATH_SEP_CHAR;
+ bFirstSeparator = true;
+ nError = ERROR_SUCCESS;
+ }
+ }
+
+ // Now check the final path
+ if(DirectoryExists(szLocalPath) || MakeDirectory(szLocalPath))
+ {
+ nError = ERROR_SUCCESS;
+ }
+ }
+ else
+ {
+ nError = ERROR_SUCCESS;
+ }
+
+ CASC_FREE(szLocalPath);
+ }
}
- // Parse the appropriate build file
- if(nError == ERROR_SUCCESS)
+ return nError;
+}
+
+static int DownloadFile(
+ const TCHAR * szRemoteName,
+ const TCHAR * szLocalName,
+ PULONGLONG PtrByteOffset,
+ DWORD cbReadSize,
+ DWORD dwPortFlags)
+{
+ TFileStream * pRemStream;
+ TFileStream * pLocStream;
+ LPBYTE pbFileData;
+ int nError = ERROR_NOT_ENOUGH_MEMORY;
+
+ // Open the remote stream
+ pRemStream = FileStream_OpenFile(szRemoteName, BASE_PROVIDER_HTTP | STREAM_PROVIDER_FLAT | dwPortFlags);
+ if (pRemStream != NULL)
{
- pvListFile = ListFile_OpenExternal(hs->szBuildFile);
- if(pvListFile != NULL)
+ // Will we download the entire file or just a part of it?
+ if (PtrByteOffset == NULL)
{
- // Parse the info file
- nError = PfnParseProc(hs, pvListFile);
- ListFile_Free(pvListFile);
+ ULONGLONG FileSize = 0;
+
+ // Retrieve the file size
+ if (FileStream_GetSize(pRemStream, &FileSize) && 0 < FileSize && FileSize < 0x10000000)
+ {
+ // Cut the file size down to 32 bits
+ cbReadSize = (DWORD)FileSize;
+ }
}
- else
- nError = ERROR_FILE_NOT_FOUND;
+
+ // Shall we read something?
+ if ((cbReadSize != 0) && (pbFileData = CASC_ALLOC(BYTE, cbReadSize)) != NULL)
+ {
+ // Read all required data from the remote file
+ if (FileStream_Read(pRemStream, PtrByteOffset, pbFileData, cbReadSize))
+ {
+ pLocStream = FileStream_CreateFile(szLocalName, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT);
+ if (pLocStream != NULL)
+ {
+ if (FileStream_Write(pLocStream, NULL, pbFileData, cbReadSize))
+ nError = ERROR_SUCCESS;
+
+ FileStream_Close(pLocStream);
+ }
+ }
+
+ // Free the data buffer
+ CASC_FREE(pbFileData);
+ }
+
+ // Close the remote stream
+ FileStream_Close(pRemStream);
+ }
+ else
+ {
+ nError = GetLastError();
}
- // If the .build.info OR .build.db file has been loaded,
- // proceed with loading the CDN config file and CDN build file
+ return nError;
+}
+
+static int DownloadFile(
+ TCascStorage * hs,
+ const TCHAR * szServerName,
+ const TCHAR * szServerPath1,
+ const TCHAR * szServerPath2,
+ const TCHAR * szLocalPath2,
+ PULONGLONG PtrByteOffset,
+ DWORD cbReadSize,
+ TCHAR * szOutLocalPath,
+ size_t cchOutLocalPath,
+ DWORD dwPortFlags,
+ bool bAlwaysDownload,
+ bool bWowClassicRedirect)
+{
+ TCHAR szRemotePath[MAX_PATH];
+ TCHAR szLocalPath[MAX_PATH];
+ size_t nLength;
+ int nError = ERROR_NOT_ENOUGH_MEMORY;
+
+ // Format the target URL
+ if(bWowClassicRedirect && !_tcsicmp(szServerPath1, _T("wow_classic_beta")))
+ szServerPath1 = _T("wow");
+ if (szLocalPath2 == NULL)
+ szLocalPath2 = szServerPath2;
+
+ // Create remote path
+ CombineUrlPath(szRemotePath, _countof(szRemotePath), szServerName, szServerPath1, szServerPath2);
+ CombineFilePath(szLocalPath, _countof(szLocalPath), hs->szRootPath, NULL, szLocalPath2);
+
+ // Make sure that the path exists
+ ForcePathExist(szLocalPath, true);
+
+ // If we are not forced to download a new one, try if local file exists.
+ if ((bAlwaysDownload == false) && (_taccess(szLocalPath, 0) == 0))
+ {
+ nError = ERROR_SUCCESS;
+ }
+ else
+ {
+ nError = DownloadFile(szRemotePath, szLocalPath, PtrByteOffset, cbReadSize, dwPortFlags);
+ }
+
+ // If succeeded, give the local path
if(nError == ERROR_SUCCESS)
{
- // Load the configuration file. Note that we don't
- // need it for anything, really, so we don't care if it fails
- pvListFile = FetchAndVerifyConfigFile(hs, &hs->CdnConfigKey);
- if(pvListFile != NULL)
+ // If the caller wanted local path, give it to him
+ if (szOutLocalPath && cchOutLocalPath)
{
- nError = LoadCdnConfigFile(hs, pvListFile);
- ListFile_Free(pvListFile);
+ nLength = _tcslen(szLocalPath);
+ if ((nLength + 1) <= cchOutLocalPath)
+ {
+ CascStrCopy(szOutLocalPath, cchOutLocalPath, szLocalPath);
+ }
}
}
- // Load the build file
- if(nError == ERROR_SUCCESS)
+ return nError;
+}
+
+static int FetchAndLoadConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey, PARSETEXTFILE PfnParseProc)
+{
+ const TCHAR * szPathType = _T("config");
+ TCHAR szLocalPath[MAX_PATH];
+ TCHAR szSubDir[0x80] = _T("");
+ void * pvListFile = NULL;
+ int nError = ERROR_CAN_NOT_COMPLETE;
+
+ // If online storage, we download the file. Otherwise, create a local path
+ if(hs->dwFeatures & CASC_FEATURE_ONLINE)
+ {
+ nError = DownloadFileFromCDN(hs, szPathType, pFileKey->pbData, NULL, szLocalPath, _countof(szLocalPath));
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ }
+ else
{
- pvListFile = FetchAndVerifyConfigFile(hs, &hs->CdnBuildKey);
- if(pvListFile != NULL)
+ CreateCascSubdirectoryName(szSubDir, _countof(szSubDir), szPathType, NULL, pFileKey->pbData);
+ CombineFilePath(szLocalPath, _countof(szLocalPath), hs->szDataPath, szSubDir);
+ }
+
+ // Load and verify the external listfile
+ pvListFile = ListFile_OpenExternal(szLocalPath);
+ if (pvListFile != NULL)
+ {
+ if (ListFile_VerifyMD5(pvListFile, pFileKey->pbData))
{
- nError = LoadCdnBuildFile(hs, pvListFile);
- ListFile_Free(pvListFile);
+ nError = PfnParseProc(hs, pvListFile);
}
else
- nError = ERROR_FILE_NOT_FOUND;
+ {
+ nError = ERROR_FILE_CORRUPT;
+ }
+ CASC_FREE(pvListFile);
+ }
+ else
+ {
+ nError = ERROR_FILE_NOT_FOUND;
}
- // Fill the index directory
- if(nError == ERROR_SUCCESS)
+ return nError;
+}
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+int DownloadFileFromCDN(TCascStorage * hs, const TCHAR * szSubDir, LPBYTE pbEKey, const TCHAR * szExtension, TCHAR * szOutLocalPath, size_t cchOutLocalPath)
+{
+ PCASC_ARCINDEX_ENTRY pIndexEntry;
+ const TCHAR * szCdnServers = hs->szCdnServers;
+ TCHAR szRemoteFolder[MAX_PATH];
+ TCHAR szLocalFolder[MAX_PATH];
+ TCHAR szServerName[MAX_PATH];
+ int nError = ERROR_CAN_NOT_COMPLETE;
+
+ // Try all download servers
+ while((szCdnServers = ExtractCdnServerName(szServerName, _countof(szServerName), szCdnServers)) != NULL)
{
- // First, check for more common "data" subdirectory
- if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("data"))) != NULL)
- return ERROR_SUCCESS;
+ // Create the local subdirectory
+ CreateCascSubdirectoryName(szLocalFolder, _countof(szLocalFolder), szSubDir, szExtension, pbEKey);
- // Second, try the "darch" subdirectory (older builds of HOTS - Alpha)
- if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("darch"))) != NULL)
- return ERROR_SUCCESS;
+ // If the EKey is in an archive, we need to change the source
+ if ((pIndexEntry = (PCASC_ARCINDEX_ENTRY)hs->ArcIndexMap.FindObject(pbEKey)) != NULL)
+ {
+ ULONGLONG ByteOffset;
+
+ // Change the subpath to an archive
+ CreateCascSubdirectoryName(szRemoteFolder, _countof(szRemoteFolder), szSubDir, szExtension, pIndexEntry->IndexHash);
+ ByteOffset = pIndexEntry->ArchiveOffset;
+ nError = DownloadFile(hs,
+ szServerName,
+ hs->szCdnPath,
+ szRemoteFolder,
+ szLocalFolder,
+ &ByteOffset,
+ pIndexEntry->EncodedSize,
+ szOutLocalPath,
+ cchOutLocalPath, 0, false, false);
+ }
+ else
+ {
+ nError = DownloadFile(hs,
+ szServerName,
+ hs->szCdnPath,
+ szLocalFolder,
+ szLocalFolder,
+ NULL,
+ 0,
+ szOutLocalPath,
+ cchOutLocalPath, 0, false, false);
+ }
- nError = ERROR_FILE_NOT_FOUND;
+ if (nError == ERROR_SUCCESS)
+ break;
}
return nError;
}
// Checks whether there is a ".build.info" or ".build.db".
-// If yes, the function sets "szRootPath" and "szDataPath"
+// If yes, the function sets "szDataPath" and "szIndexPath"
// in the storage structure and returns ERROR_SUCCESS
int CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory)
{
@@ -912,22 +1170,22 @@ int CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory)
int nError = ERROR_FILE_NOT_FOUND;
// Try to find any of the root files used in the history
- for(size_t i = 0; BuildTypes[i].szFileName != NULL; i++)
+ for (size_t i = 0; BuildTypes[i].szFileName != NULL; i++)
{
// Create the full name of the .agent.db file
szBuildFile = CombinePath(szDirectory, BuildTypes[i].szFileName);
- if(szBuildFile != NULL)
+ if (szBuildFile != NULL)
{
// Attempt to open the file
pStream = FileStream_OpenFile(szBuildFile, STREAM_FLAG_READ_ONLY);
- if(pStream != NULL)
+ if (pStream != NULL)
{
// Free the stream
FileStream_Close(pStream);
// Check for the data directory
nError = CheckDataDirectory(hs, szDirectory);
- if(nError == ERROR_SUCCESS)
+ if (nError == ERROR_SUCCESS)
{
hs->szBuildFile = szBuildFile;
hs->BuildFileType = BuildTypes[i].BuildFileType;
@@ -942,76 +1200,95 @@ int CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory)
return nError;
}
-//-----------------------------------------------------------------------------
-// Helpers for a config files that have multiple variables separated by "|"
-// The line structure is (Overwatch 24919): "#MD5|CHUNK_ID|FILENAME|INSTALLPATH"
-// The line structure is (Overwatch 27759): "#MD5|CHUNK_ID|PRIORITY|MPRIORITY|FILENAME|INSTALLPATH"
-// The line has all preceding spaces removed
-
-// Retrieves the index of a variable from the initial line
-int GetRootVariableIndex(const char * szLinePtr, const char * szLineEnd, const char * szVariableName, int * PtrIndex)
+int LoadBuildInfo(TCascStorage * hs)
{
- size_t nLength = strlen(szVariableName);
- int nIndex = 0;
-
- while(szLinePtr < szLineEnd)
+ PARSECSVFILE PfnParseProc = NULL;
+ bool bHasHeader = true;
+ int nError;
+
+ // If the storage is online storage, we need to download "versions"
+ if(hs->dwFeatures & CASC_FEATURE_ONLINE)
{
- // Check the variable there
- if(!_strnicmp(szLinePtr, szVariableName, nLength))
+ // Inform the user about loading the build.info/build.db/versions
+ if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Downloading the \"versions\" file", NULL, 0, 0))
+ return ERROR_CANCELLED;
+
+ // Attempt to download the "versions" file
+ nError = DownloadFile(hs, _T("us.patch.battle.net"), hs->szCodeName, _T("versions"), NULL, NULL, 0, NULL, 0, STREAM_FLAG_USE_PORT_1119, true, false);
+ if(nError != ERROR_SUCCESS)
{
- // Does the length match?
- if(szLinePtr[nLength] == '|' || szLinePtr[nLength] == '0')
- {
- PtrIndex[0] = nIndex;
- return ERROR_SUCCESS;
- }
+ return nError;
}
+ }
+
+ // We support either ".build.info" or ".build.db"
+ switch (hs->BuildFileType)
+ {
+ case CascBuildInfo:
+ PfnParseProc = ParseFile_BuildInfo;
+ break;
+
+ case CascVersionsDb:
+ PfnParseProc = ParseFile_VersionsDb;
+ break;
- // Get the next variable
- szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd);
- if(szLinePtr == NULL)
+ case CascBuildDb:
+ PfnParseProc = ParseFile_BuildDb;
+ bHasHeader = false;
break;
- nIndex++;
+
+ default:
+ return ERROR_NOT_SUPPORTED;
}
- return ERROR_BAD_FORMAT;
+ return LoadCsvFile(hs, hs->szBuildFile, PfnParseProc, bHasHeader);
}
-// Parses single line from Overwatch.
-int ParseRootFileLine(const char * szLinePtr, const char * szLineEnd, int nFileNameIndex, PQUERY_KEY PtrEncodingKey, char * szFileName, size_t nMaxChars)
+int LoadCdnsInfo(TCascStorage * hs)
{
- int nIndex = 0;
- int nError;
-
- // Extract the MD5 (aka encoding key)
- if(szLinePtr[MD5_STRING_SIZE] != '|')
- return ERROR_BAD_FORMAT;
+ TCHAR szLocalPath[MAX_PATH];
+ int nError = ERROR_SUCCESS;
- // Convert the encoding key to binary
- PtrEncodingKey->cbData = MD5_HASH_SIZE;
- nError = ConvertStringToBinary(szLinePtr, MD5_STRING_SIZE, PtrEncodingKey->pbData);
- if(nError != ERROR_SUCCESS)
- return nError;
+ // Sanity checks
+ assert(hs->dwFeatures & CASC_FEATURE_ONLINE);
- // Skip the variable
- szLinePtr += MD5_STRING_SIZE + 1;
- nIndex = 1;
+ // Inform the user about what we are doing
+ if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Downloading the \"cdns\" file", NULL, 0, 0))
+ return ERROR_CANCELLED;
- // Skip the variables until we find the file name
- while(szLinePtr < szLineEnd && nIndex < nFileNameIndex)
+ // Download file and parse it
+ nError = DownloadFile(hs, _T("us.patch.battle.net"), hs->szCodeName, _T("cdns"), NULL, NULL, 0, szLocalPath, _countof(szLocalPath), STREAM_FLAG_USE_PORT_1119, false, true);
+ if(nError == ERROR_SUCCESS)
{
- if(szLinePtr[0] == '|')
- nIndex++;
- szLinePtr++;
+ nError = LoadCsvFile(hs, szLocalPath, ParseFile_CDNS, true);
}
- // Extract the file name
- while(szLinePtr < szLineEnd && szLinePtr[0] != '|' && nMaxChars > 1)
- {
- *szFileName++ = *szLinePtr++;
- nMaxChars--;
- }
+ return nError;
+}
- *szFileName = 0;
- return ERROR_SUCCESS;
+int LoadCdnConfigFile(TCascStorage * hs)
+{
+ // The CKey for the CDN config should have been loaded already
+ assert(hs->CdnConfigKey.pbData != NULL && hs->CdnConfigKey.cbData == MD5_HASH_SIZE);
+
+ // Inform the user about what we are doing
+ if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Downloading CDN config file", NULL, 0, 0))
+ return ERROR_CANCELLED;
+
+ // Load the CDN config file
+ return FetchAndLoadConfigFile(hs, &hs->CdnConfigKey, ParseFile_CdnConfig);
+}
+
+int LoadCdnBuildFile(TCascStorage * hs)
+{
+ // The CKey for the CDN config should have been loaded already
+ assert(hs->CdnBuildKey.pbData != NULL && hs->CdnBuildKey.cbData == MD5_HASH_SIZE);
+
+ // Inform the user about what we are doing
+ if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Downloading CDN build file", NULL, 0, 0))
+ return ERROR_CANCELLED;
+
+ // Load the CDN config file. Note that we don't
+ // need it for anything, really, so we don't care if it fails
+ return FetchAndLoadConfigFile(hs, &hs->CdnBuildKey, ParseFile_CdnBuild);
}
diff --git a/dep/CascLib/src/CascFindFile.cpp b/dep/CascLib/src/CascFindFile.cpp
index a5ec5766654..129403188c5 100644
--- a/dep/CascLib/src/CascFindFile.cpp
+++ b/dep/CascLib/src/CascFindFile.cpp
@@ -15,192 +15,124 @@
//-----------------------------------------------------------------------------
// Local functions
-static TCascSearch * IsValidSearchHandle(HANDLE hFind)
+// Reset the search structure. Called before each search
+static void ResetFindData(PCASC_FIND_DATA pFindData)
{
- TCascSearch * pSearch = (TCascSearch *)hFind;
-
- return (pSearch != NULL && pSearch->szClassName != NULL && !strcmp(pSearch->szClassName, "TCascSearch") && pSearch->szMask != NULL) ? pSearch : NULL;
+ // Reset the variables
+ ZeroMemory16(pFindData->CKey);
+ ZeroMemory16(pFindData->EKey);
+ pFindData->szFileName[0] = 0;
+ pFindData->szPlainName = pFindData->szFileName;
+ pFindData->TagBitMask = 0;
+ pFindData->dwFileDataId = CASC_INVALID_ID;
+ pFindData->dwFileSize = CASC_INVALID_SIZE;
+ pFindData->dwLocaleFlags = CASC_INVALID_ID;
+ pFindData->dwContentFlags = CASC_INVALID_ID;
+ pFindData->NameType = CascNameFull;
+ pFindData->bFileAvailable = false;
+ pFindData->bCanOpenByName = false;
+ pFindData->bCanOpenByDataId = false;
+ pFindData->bCanOpenByCKey = false;
+ pFindData->bCanOpenByEKey = false;
}
-static void FreeSearchHandle(TCascSearch * pSearch)
+static void SupplyFakeFileName(PCASC_FIND_DATA pFindData)
{
- // Only if the storage handle is valid
- assert(pSearch != NULL);
-
- // Close (dereference) the archive handle
- if(pSearch->hs != NULL)
+ // If the file can be open by file data id, create fake file name
+ if(pFindData->bCanOpenByDataId)
{
- // Give root handler chance to free their stuff
- RootHandler_EndSearch(pSearch->hs->pRootHandler, pSearch);
+ CascStrPrintf(pFindData->szFileName, _countof(pFindData->szFileName), "FILE%08X.dat", pFindData->dwFileDataId);
+ pFindData->NameType = CascNameDataId;
+ return;
+ }
- // Dereference the storage handle
- CascCloseStorage((HANDLE)pSearch->hs);
- pSearch->hs = NULL;
+ // If the file can be open by CKey, convert the CKey to file name
+ if(pFindData->bCanOpenByCKey)
+ {
+ StringFromBinary(pFindData->CKey, MD5_HASH_SIZE, pFindData->szFileName);
+ pFindData->NameType = CascNameCKey;
+ return;
}
- // Free the file cache and frame array
- if(pSearch->szMask != NULL)
- CASC_FREE(pSearch->szMask);
- if(pSearch->szListFile != NULL)
- CASC_FREE(pSearch->szListFile);
-// if(pSearch->pStruct1C != NULL)
-// delete pSearch->pStruct1C;
- if(pSearch->pCache != NULL)
- ListFile_Free(pSearch->pCache);
-
- // Free the structure itself
- pSearch->szClassName = NULL;
- CASC_FREE(pSearch);
+ // CKey should be always present
+ StringFromBinary(pFindData->EKey, MD5_HASH_SIZE, pFindData->szFileName);
+ pFindData->NameType = CascNameEKey;
+ assert(pFindData->bCanOpenByEKey != false);
}
-static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szListFile, const char * szMask)
+static bool CopyCKeyEntryToFindData(PCASC_FIND_DATA pFindData, PCASC_CKEY_ENTRY pCKeyEntry)
{
- TCascSearch * pSearch;
- size_t cbToAllocate;
-
- // When using the MNDX info, do not allocate the extra bit array
- cbToAllocate = sizeof(TCascSearch) + ((hs->pEncodingMap->TableSize + 7) / 8);
- pSearch = (TCascSearch *)CASC_ALLOC(BYTE, cbToAllocate);
- if(pSearch != NULL)
- {
- // Initialize the structure
- memset(pSearch, 0, cbToAllocate);
- pSearch->szClassName = "TCascSearch";
-
- // Save the search handle
- pSearch->hs = hs;
- hs->dwRefCount++;
-
- // If the mask was not given, use default
- if(szMask == NULL)
- szMask = "*";
-
- // Save the other variables
- if(szListFile != NULL)
- {
- pSearch->szListFile = CascNewStr(szListFile, 0);
- if(pSearch->szListFile == NULL)
- {
- FreeSearchHandle(pSearch);
- return NULL;
- }
- }
-
- // Allocate the search mask
- pSearch->szMask = CascNewStr(szMask, 0);
- if(pSearch->szMask == NULL)
- {
- FreeSearchHandle(pSearch);
- return NULL;
- }
- }
-
- return pSearch;
+ // Supply both keys
+ CopyMemory16(pFindData->CKey, pCKeyEntry->CKey);
+ CopyMemory16(pFindData->EKey, pCKeyEntry->EKey);
+ pFindData->bCanOpenByCKey = (pCKeyEntry->Flags & CASC_CE_HAS_CKEY) ? true : false;
+ pFindData->bCanOpenByEKey = (pCKeyEntry->Flags & CASC_CE_HAS_EKEY) ? true : false;
+
+ // Supply the tag mask
+ pFindData->TagBitMask = pCKeyEntry->TagBitMask;
+
+ // Supply the plain name. Only do that if the found name is not a CKey/EKey
+ if(pFindData->szFileName[0] != 0)
+ pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName);
+
+ // If we retrieved the file size directly from the root provider, use it
+ // Otherwise, supply EncodedSize or ContentSize, whichever is available (but ContentSize > EncodedSize)
+ if(pFindData->dwFileSize == CASC_INVALID_SIZE)
+ pFindData->dwFileSize = pCKeyEntry->ContentSize;
+
+ // Set flag indicating that the file is locally available
+ pFindData->bFileAvailable = (pCKeyEntry->Flags & CASC_CE_FILE_IS_LOCAL);
+
+ // Supply a fake file name, if there is none supplied by the root handler
+ if(pFindData->szFileName[0] == 0)
+ SupplyFakeFileName(pFindData);
+ return true;
}
// Perform searching using root-specific provider.
// The provider may need the listfile
static bool DoStorageSearch_RootFile(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
{
- PCASC_ENCODING_ENTRY pEncodingEntry;
- PCASC_INDEX_ENTRY pIndexEntry;
- QUERY_KEY EncodingKey;
- QUERY_KEY IndexKey;
- LPBYTE pbEncodingKey;
- DWORD EncodingIndex = 0;
- DWORD ByteIndex;
- DWORD BitMask;
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ TCascStorage * hs = pSearch->hs;
+
+ // Reset the search structure
+ ResetFindData(pFindData);
+ // Enumerate over all files
for(;;)
{
- DWORD LocaleFlags = 0;
- DWORD FileDataId = CASC_INVALID_ID;
- DWORD FileSize = CASC_INVALID_SIZE;
-
- // Attempt to find (the next) file from the root entry
- pbEncodingKey = RootHandler_Search(pSearch->hs->pRootHandler, pSearch, &FileSize, &LocaleFlags, &FileDataId);
- if(pbEncodingKey == NULL)
+ // Attempt to find (the next) file from the root handler
+ pCKeyEntry = hs->pRootHandler->Search(pSearch, pFindData);
+ if(pCKeyEntry == NULL)
return false;
- // Verify whether the encoding key exists in the encoding table
- EncodingKey.pbData = pbEncodingKey;
- EncodingKey.cbData = MD5_HASH_SIZE;
- pEncodingEntry = FindEncodingEntry(pSearch->hs, &EncodingKey, &EncodingIndex);
- if(pEncodingEntry != NULL)
- {
- // Mark the item as already found
- // Note: Duplicate items are allowed while we are searching using file names
- // Do not exclude items from search if they were found before
- ByteIndex = (DWORD)(EncodingIndex / 8);
- BitMask = 1 << (EncodingIndex & 0x07);
- pSearch->BitArray[ByteIndex] |= BitMask;
-
- // Locate the index entry
- IndexKey.pbData = GET_INDEX_KEY(pEncodingEntry);
- IndexKey.cbData = MD5_HASH_SIZE;
- pIndexEntry = FindIndexEntry(pSearch->hs, &IndexKey);
- if(pIndexEntry == NULL)
- continue;
-
- // If we retrieved the file size directly from the root provider, use it
- // Otherwise, we need to retrieve it from the encoding entry
- if(FileSize == CASC_INVALID_SIZE)
- FileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE);
-
- // Fill-in the found file
- strcpy(pFindData->szFileName, pSearch->szFileName);
- memcpy(pFindData->EncodingKey, pEncodingEntry->EncodingKey, MD5_HASH_SIZE);
- pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName);
- pFindData->dwLocaleFlags = LocaleFlags;
- pFindData->dwFileDataId = FileDataId;
- pFindData->dwFileSize = FileSize;
- return true;
- }
+ // The entry is expected to be referenced by the root directory
+ assert(pCKeyEntry->RefCount != 0);
+
+ // Copy the CKey entry to the find data and return it
+ return CopyCKeyEntryToFindData(pFindData, pCKeyEntry);
}
}
-static bool DoStorageSearch_EncodingKey(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
+static bool DoStorageSearch_CKey(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
{
- PCASC_ENCODING_ENTRY pEncodingEntry;
- PCASC_INDEX_ENTRY pIndexEntry;
+ PCASC_CKEY_ENTRY pCKeyEntry;
TCascStorage * hs = pSearch->hs;
- QUERY_KEY IndexKey;
- DWORD ByteIndex;
- DWORD BitMask;
+ size_t nTotalItems = hs->CKeyArray.ItemCount();
- // Check for encoding keys that haven't been found yet
- while(pSearch->IndexLevel1 < hs->pEncodingMap->TableSize)
+ // Reset the find data structure
+ ResetFindData(pFindData);
+
+ // Check for CKeys that haven't been found yet
+ while(pSearch->nFileIndex < nTotalItems)
{
- // Check if that entry has been reported before
- ByteIndex = (DWORD)(pSearch->IndexLevel1 / 8);
- BitMask = 1 << (pSearch->IndexLevel1 & 0x07);
- if((pSearch->BitArray[ByteIndex] & BitMask) == 0)
+ // Locate the n-th CKey entry. If this entry is not referenced by the root handler, we include it in the search result
+ pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.ItemAt(pSearch->nFileIndex++);
+ if((pCKeyEntry->Flags & CASC_CE_FOLDER_ENTRY) == 0 && pCKeyEntry->RefCount == 0)
{
- // Locate the index entry
- pEncodingEntry = (PCASC_ENCODING_ENTRY)hs->pEncodingMap->HashTable[pSearch->IndexLevel1];
- if(pEncodingEntry != NULL)
- {
- IndexKey.pbData = GET_INDEX_KEY(pEncodingEntry);
- IndexKey.cbData = MD5_HASH_SIZE;
- pIndexEntry = FindIndexEntry(pSearch->hs, &IndexKey);
- if(pIndexEntry != NULL)
- {
- // Fill-in the found file
- memcpy(pFindData->EncodingKey, pEncodingEntry->EncodingKey, MD5_HASH_SIZE);
- pFindData->szFileName[0] = 0;
- pFindData->szPlainName = pFindData->szFileName;
- pFindData->dwLocaleFlags = CASC_LOCALE_NONE;
- pFindData->dwFileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE);
-
- // Mark the entry as already-found
- pSearch->BitArray[ByteIndex] |= BitMask;
- return true;
- }
- }
+ return CopyCKeyEntryToFindData(pFindData, pCKeyEntry);
}
-
- // Go to the next encoding entry
- pSearch->IndexLevel1++;
}
// Nameless search ended
@@ -210,36 +142,37 @@ static bool DoStorageSearch_EncodingKey(TCascSearch * pSearch, PCASC_FIND_DATA p
static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
{
// State 0: No search done yet
- if(pSearch->dwState == 0)
+ if(pSearch->nSearchState == 0)
{
// Does the search specify listfile?
if(pSearch->szListFile != NULL)
pSearch->pCache = ListFile_OpenExternal(pSearch->szListFile);
// Move the search phase to the listfile searching
- pSearch->IndexLevel1 = 0;
- pSearch->dwState++;
+ pSearch->nSearchState = 1;
+ pSearch->nFileIndex = 0;
}
// State 1: Searching the list file
- if(pSearch->dwState == 1)
+ if(pSearch->nSearchState == 1)
{
if(DoStorageSearch_RootFile(pSearch, pFindData))
return true;
// Move to the nameless search state
- pSearch->IndexLevel1 = 0;
- pSearch->dwState++;
+ pSearch->nSearchState = 2;
+ pSearch->nFileIndex = 0;
}
- // State 2: Searching the remaining entries
- if(pSearch->dwState == 2 && (pSearch->szMask == NULL || !strcmp(pSearch->szMask, "*")))
+ // State 2: Searching the remaining entries by CKey
+ if(pSearch->nSearchState == 2 && (pSearch->szMask == NULL || !strcmp(pSearch->szMask, "*")))
{
- if(DoStorageSearch_EncodingKey(pSearch, pFindData))
+ if(DoStorageSearch_CKey(pSearch, pFindData))
return true;
// Move to the final search state
- pSearch->dwState++;
+ pSearch->nSearchState++;
+ pSearch->nFileIndex = 0;
}
return false;
@@ -250,16 +183,16 @@ static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
HANDLE WINAPI CascFindFirstFile(
HANDLE hStorage,
- const char * szMask,
+ LPCSTR szMask,
PCASC_FIND_DATA pFindData,
- const TCHAR * szListFile)
+ LPCTSTR szListFile)
{
TCascStorage * hs;
TCascSearch * pSearch = NULL;
int nError = ERROR_SUCCESS;
// Check parameters
- if((hs = IsValidStorageHandle(hStorage)) == NULL)
+ if((hs = TCascStorage::IsValid(hStorage)) == NULL)
nError = ERROR_INVALID_HANDLE;
if(szMask == NULL || pFindData == NULL)
nError = ERROR_INVALID_PARAMETER;
@@ -267,11 +200,8 @@ HANDLE WINAPI CascFindFirstFile(
// Init the search structure and search handle
if(nError == ERROR_SUCCESS)
{
- // Clear the entire search structure
- memset(pFindData, 0, sizeof(CASC_FIND_DATA));
-
// Allocate the search handle
- pSearch = AllocateSearchHandle(hs, szListFile, szMask);
+ pSearch = new TCascSearch(hs, szListFile, szMask);
if(pSearch == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
@@ -285,8 +215,7 @@ HANDLE WINAPI CascFindFirstFile(
if(nError != ERROR_SUCCESS)
{
- if(pSearch != NULL)
- FreeSearchHandle(pSearch);
+ delete pSearch;
pSearch = (TCascSearch *)INVALID_HANDLE_VALUE;
}
@@ -299,7 +228,7 @@ bool WINAPI CascFindNextFile(
{
TCascSearch * pSearch;
- pSearch = IsValidSearchHandle(hFind);
+ pSearch = TCascSearch::IsValid(hFind);
if(pSearch == NULL || pFindData == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
@@ -314,13 +243,13 @@ bool WINAPI CascFindClose(HANDLE hFind)
{
TCascSearch * pSearch;
- pSearch = IsValidSearchHandle(hFind);
+ pSearch = TCascSearch::IsValid(hFind);
if(pSearch == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
- FreeSearchHandle(pSearch);
+ delete pSearch;
return true;
}
diff --git a/dep/CascLib/src/CascIndexFiles.cpp b/dep/CascLib/src/CascIndexFiles.cpp
new file mode 100644
index 00000000000..c54c874ec8c
--- /dev/null
+++ b/dep/CascLib/src/CascIndexFiles.cpp
@@ -0,0 +1,774 @@
+/*****************************************************************************/
+/* CascIndexFiles.cpp Copyright (c) Ladislav Zezula 2019 */
+/*---------------------------------------------------------------------------*/
+/* Index file support */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 23.05.19 1.00 Lad Created */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "CascLib.h"
+#include "CascCommon.h"
+
+//-----------------------------------------------------------------------------
+// Local variables
+
+static const TCHAR * szAllowedHexChars = _T("0123456789aAbBcCdDeEfF");
+static const TCHAR * szIndexFormat_V1 = _T("data.i%x%x");
+static const TCHAR * szIndexFormat_V2 = _T("%02x%08x.idx");
+
+// Limit for "orphaned" items - those that are in index files, but are not in ENCODING manifest
+#define CASC_MAX_ORPHANED_ITEMS 0x100
+
+//-----------------------------------------------------------------------------
+// Local functions
+
+// "data.iXY"
+static bool IsIndexFileName_V1(const TCHAR * szFileName)
+{
+ // Check if the name looks like a valid index file
+ return (_tcslen(szFileName) == 8 &&
+ _tcsnicmp(szFileName, _T("data.i"), 6) == 0 &&
+ _tcsspn(szFileName + 6, szAllowedHexChars) == 2);
+}
+
+static bool IsIndexFileName_V2(const TCHAR * szFileName)
+{
+ // Check if the name looks like a valid index file
+ return (_tcslen(szFileName) == 14 &&
+ _tcsspn(szFileName, _T("0123456789aAbBcCdDeEfF")) == 0x0A &&
+ _tcsicmp(szFileName + 0x0A, _T(".idx")) == 0);
+}
+
+static bool IndexDirectory_OnFileFound(
+ const TCHAR * szFileName,
+ PDWORD IndexArray,
+ PDWORD OldIndexArray,
+ void * pvContext)
+{
+ TCascStorage * hs = (TCascStorage *)pvContext;
+ DWORD IndexValue = 0;
+ DWORD IndexVersion = 0;
+
+ // Auto-detect the format of the index file name
+ if(hs->szIndexFormat == NULL)
+ {
+ if(IsIndexFileName_V2(szFileName))
+ hs->szIndexFormat = szIndexFormat_V2;
+ else if(IsIndexFileName_V1(szFileName))
+ hs->szIndexFormat = szIndexFormat_V1;
+ else
+ return false;
+ }
+
+ if(hs->szIndexFormat == szIndexFormat_V2)
+ {
+ // Check the index file name format
+ if(!IsIndexFileName_V2(szFileName))
+ return false;
+
+ // Get the main index from the first two digits
+ if(ConvertStringToInt32(szFileName, 2, &IndexValue) != ERROR_SUCCESS)
+ return false;
+ if(ConvertStringToInt32(szFileName + 2, 8, &IndexVersion) != ERROR_SUCCESS)
+ return false;
+ }
+ else if(hs->szIndexFormat == szIndexFormat_V1)
+ {
+ // Check the index file name format
+ if(!IsIndexFileName_V1(szFileName))
+ return false;
+
+ // Get the main index from the first two digits
+ if(ConvertDigitToInt32(szFileName + 6, &IndexValue) != ERROR_SUCCESS)
+ return false;
+ if(ConvertDigitToInt32(szFileName + 7, &IndexVersion) != ERROR_SUCCESS)
+ return false;
+ }
+ else
+ {
+ // Should never happen
+ assert(false);
+ return false;
+ }
+
+ // The index value must not be greater than 0x0F
+ if(IndexValue >= CASC_INDEX_COUNT)
+ return false;
+
+ // If the new subindex is greater than the previous one,
+ // use this one instead
+ if(IndexVersion > IndexArray[IndexValue])
+ {
+ OldIndexArray[IndexValue] = IndexArray[IndexValue];
+ IndexArray[IndexValue] = IndexVersion;
+ }
+ else if(IndexVersion > OldIndexArray[IndexValue])
+ {
+ OldIndexArray[IndexValue] = IndexVersion;
+ }
+
+ // Note: WoW6 only keeps last two index files
+ // Any additional index files are deleted at this point
+ return true;
+}
+
+static TCHAR * CreateIndexFileName(TCascStorage * hs, DWORD IndexValue, DWORD IndexVersion)
+{
+ TCHAR szPlainName[0x40];
+
+ // Sanity checks
+ assert(hs->szIndexFormat != NULL);
+ assert(hs->szIndexPath != NULL);
+ assert(IndexValue <= 0x0F);
+
+ // Create the full path
+ CascStrPrintf(szPlainName, _countof(szPlainName), hs->szIndexFormat, IndexValue, IndexVersion);
+ return CombinePath(hs->szIndexPath, szPlainName);
+}
+
+// Verifies a guarded block - data availability and checksum match
+static LPBYTE CaptureGuardedBlock1(LPBYTE pbFileData, LPBYTE pbFileEnd)
+{
+ PFILE_INDEX_GUARDED_BLOCK pBlock = (PFILE_INDEX_GUARDED_BLOCK)pbFileData;
+
+ // Check the guarded block
+ if((pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK)) >= pbFileEnd)
+ return NULL;
+ if((pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK) + pBlock->BlockSize) > pbFileEnd)
+ return NULL;
+
+ // Verify the hash
+ if(hashlittle(pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK), pBlock->BlockSize, 0) != pBlock->BlockHash)
+ return NULL;
+
+ // Give the output
+ return (LPBYTE)(pBlock + 1);
+}
+
+// Second method of checking a guarded block; hash is calculated entry-by-entry.
+// Unfortunately, due to implementation in hashlittle2(), we cannot calculate the hash of a continuous block
+static LPBYTE CaptureGuardedBlock2(LPBYTE pbFileData, LPBYTE pbFileEnd, size_t EntryLength, PDWORD PtrBlockSize = NULL)
+{
+ PFILE_INDEX_GUARDED_BLOCK pBlock = (PFILE_INDEX_GUARDED_BLOCK)pbFileData;
+ LPBYTE pbEntryPtr;
+ size_t EntryCount;
+ unsigned int HashHigh = 0;
+ unsigned int HashLow = 0;
+
+ // Check the guarded block. There must be enough bytes to contain FILE_INDEX_GUARDED_BLOCK
+ // and also the block length must not be NULL
+ if ((pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK)) >= pbFileEnd)
+ return NULL;
+ if (pBlock->BlockSize == 0 || (pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK) + pBlock->BlockSize) > pbFileEnd)
+ return NULL;
+
+ // Compute the hash entry-by-entry
+ pbEntryPtr = pbFileData + sizeof(FILE_INDEX_GUARDED_BLOCK);
+ EntryCount = pBlock->BlockSize / EntryLength;
+ for (size_t i = 0; i < EntryCount; i++)
+ {
+ hashlittle2(pbEntryPtr, EntryLength, &HashHigh, &HashLow);
+ pbEntryPtr += EntryLength;
+ }
+
+ // Verify hash
+ if (HashHigh != pBlock->BlockHash)
+ return NULL;
+
+ // Give the output
+ if (PtrBlockSize != NULL)
+ PtrBlockSize[0] = pBlock->BlockSize;
+ return (LPBYTE)(pBlock + 1);
+}
+
+// Third method of checking a guarded block; There is 32-bit hash, followed by EKey entry
+// The hash covers the EKey entry plus one byte
+static LPBYTE CaptureGuardedBlock3(LPBYTE pbFileData, LPBYTE pbFileEnd, size_t EntryLength)
+{
+ PDWORD PtrEntryHash = (PDWORD)pbFileData;
+ DWORD EntryHash;
+
+ // Check the guarded block. There must be enough bytes to contain single entry and the hash
+ // Also, the hash must be present
+ if ((pbFileData + sizeof(DWORD) + EntryLength) >= pbFileEnd)
+ return NULL;
+ if (PtrEntryHash[0] == 0)
+ return NULL;
+
+ EntryHash = hashlittle(pbFileData + sizeof(DWORD), EntryLength+1, 0) | 0x80000000;
+ if(EntryHash != PtrEntryHash[0])
+ return NULL;
+
+ // Give the output
+ return (LPBYTE)(PtrEntryHash + 1);
+}
+
+static bool CaptureIndexEntry(CASC_INDEX_HEADER & InHeader, PCASC_CKEY_ENTRY pCKeyEntry, LPBYTE pbEKeyEntry)
+{
+ // Zero both CKey and EKey
+ ZeroMemory16(pCKeyEntry->CKey);
+ ZeroMemory16(pCKeyEntry->EKey);
+
+ // Copy the EKey. We assume 9 bytes
+ pCKeyEntry->EKey[0x00] = pbEKeyEntry[0];
+ pCKeyEntry->EKey[0x01] = pbEKeyEntry[1];
+ pCKeyEntry->EKey[0x02] = pbEKeyEntry[2];
+ pCKeyEntry->EKey[0x03] = pbEKeyEntry[3];
+ pCKeyEntry->EKey[0x04] = pbEKeyEntry[4];
+ pCKeyEntry->EKey[0x05] = pbEKeyEntry[5];
+ pCKeyEntry->EKey[0x06] = pbEKeyEntry[6];
+ pCKeyEntry->EKey[0x07] = pbEKeyEntry[7];
+ pCKeyEntry->EKey[0x08] = pbEKeyEntry[8];
+ pCKeyEntry->EKey[0x09] = pbEKeyEntry[9];
+ pbEKeyEntry += InHeader.EKeyLength;
+
+ // Copy the storage offset
+ pCKeyEntry->StorageOffset = ConvertBytesToInteger_5(pbEKeyEntry);
+ pbEKeyEntry += InHeader.StorageOffsetLength;
+
+ // Clear the tag bit mask
+ pCKeyEntry->TagBitMask = 0;
+
+ // Copy the encoded length
+ pCKeyEntry->EncodedSize = ConvertBytesToInteger_4_LE(pbEKeyEntry);
+ pCKeyEntry->ContentSize = CASC_INVALID_SIZE;
+ pCKeyEntry->RefCount = 0;
+ pCKeyEntry->Priority = 0;
+ pCKeyEntry->Flags = CASC_CE_FILE_IS_LOCAL | CASC_CE_HAS_EKEY | CASC_CE_HAS_EKEY_PARTIAL;
+
+ // We ignore items that have EncodedSize of 0x1E
+ return (pCKeyEntry->EncodedSize > FIELD_OFFSET(BLTE_ENCODED_HEADER, Signature));
+}
+
+static void CheckForEncodingManifestCKey(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry)
+{
+ // If the encoding file was not found yet
+ if(hs->EncodingCKey.StorageOffset == CASC_INVALID_OFFS64)
+ {
+ if(!memcmp(pCKeyEntry->EKey, hs->EncodingCKey.EKey, hs->EKeyLength))
+ {
+ hs->EncodingCKey.StorageOffset = pCKeyEntry->StorageOffset;
+ hs->EncodingCKey.EncodedSize = pCKeyEntry->EncodedSize;
+ hs->EncodingCKey.Flags |= CASC_CE_FILE_IS_LOCAL;
+ }
+ }
+}
+
+static int LoadIndexItems(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbEKeyEntry, LPBYTE pbEKeyEnd)
+{
+ size_t EntryLength = InHeader.EntryLength;
+
+ while((pbEKeyEntry + EntryLength) <= pbEKeyEnd)
+ {
+ CASC_CKEY_ENTRY CKeyEntry;
+
+ // Capture the index entry and verify it.
+ if(CaptureIndexEntry(InHeader, &CKeyEntry, pbEKeyEntry))
+ {
+ // Insert new entry to the array of CKey entries
+ if(hs->IndexArray.Insert(&CKeyEntry, 1) == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Verify whether the key is not a CKEy entry for ENCODING file
+ CheckForEncodingManifestCKey(hs, &CKeyEntry);
+ }
+
+ pbEKeyEntry += EntryLength;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+static int CaptureIndexHeader_V1(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
+{
+ PFILE_INDEX_HEADER_V1 pIndexHeader = (PFILE_INDEX_HEADER_V1)pbFileData;
+ LPBYTE pbKeyEntries;
+ LPBYTE pbFileEnd = pbFileData + cbFileData;
+ size_t cbKeyEntries;
+ DWORD HeaderHash;
+
+ // Check the available size. Note that the index file can be just a header.
+ if((pbFileData + sizeof(FILE_INDEX_HEADER_V1)) > pbFileEnd)
+ return ERROR_BAD_FORMAT;
+ if(pIndexHeader->IndexVersion != 0x05 || pIndexHeader->BucketIndex != (BYTE)BucketIndex || pIndexHeader->field_8 == 0)
+ return ERROR_BAD_FORMAT;
+ if(pIndexHeader->EncodedSizeLength != 0x04 || pIndexHeader->StorageOffsetLength != 0x05 || pIndexHeader->EKeyLength != 0x09)
+ return ERROR_NOT_SUPPORTED;
+
+ // Verify the header hash
+ HeaderHash = pIndexHeader->HeaderHash;
+ pIndexHeader->HeaderHash = 0;
+ if(hashlittle(pbFileData, sizeof(FILE_INDEX_HEADER_V1), 0) != HeaderHash)
+ return ERROR_BAD_FORMAT;
+
+ // Return the header hash back
+ pIndexHeader->HeaderHash = HeaderHash;
+
+ // Copy the fields
+ InHeader.IndexVersion = pIndexHeader->IndexVersion;
+ InHeader.BucketIndex = pIndexHeader->BucketIndex;
+ InHeader.StorageOffsetLength = pIndexHeader->StorageOffsetLength;
+ InHeader.EncodedSizeLength = pIndexHeader->EncodedSizeLength;
+ InHeader.EKeyLength = pIndexHeader->EKeyLength;
+ InHeader.FileOffsetBits = pIndexHeader->FileOffsetBits;
+ InHeader.Alignment = 0;
+ InHeader.SegmentSize = pIndexHeader->SegmentSize;
+
+ // Determine the size of the header
+ InHeader.HeaderLength = sizeof(FILE_INDEX_HEADER_V1);
+ InHeader.HeaderPadding = 0;
+ InHeader.EntryLength = pIndexHeader->EKeyLength + pIndexHeader->StorageOffsetLength + pIndexHeader->EncodedSizeLength;
+ InHeader.EKeyCount = pIndexHeader->EKeyCount1 + pIndexHeader->EKeyCount2;
+
+ // Verify the entries hash - 1st block
+ pbKeyEntries = pbFileData + InHeader.HeaderLength;
+ cbKeyEntries = pIndexHeader->EKeyCount1 * InHeader.EntryLength;
+ if((pbKeyEntries + cbKeyEntries) > pbFileEnd)
+ return ERROR_FILE_CORRUPT;
+ if(hashlittle(pbKeyEntries, cbKeyEntries, 0) != pIndexHeader->KeysHash1)
+ return ERROR_FILE_CORRUPT;
+
+ // Verify the entries hash - 2nd block
+ pbKeyEntries = pbKeyEntries + cbKeyEntries;
+ cbKeyEntries = pIndexHeader->EKeyCount2 * InHeader.EntryLength;
+ if((pbKeyEntries + cbKeyEntries) > pbFileEnd)
+ return ERROR_FILE_CORRUPT;
+ if(hashlittle(pbKeyEntries, cbKeyEntries, 0) != pIndexHeader->KeysHash2)
+ return ERROR_FILE_CORRUPT;
+
+ return ERROR_SUCCESS;
+}
+
+static int CaptureIndexHeader_V2(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
+{
+ PFILE_INDEX_HEADER_V2 pIndexHeader;
+ LPBYTE pbFileEnd = pbFileData + cbFileData;
+
+ // Check for guarded block
+ if((pbFileData = CaptureGuardedBlock1(pbFileData, pbFileEnd)) == NULL)
+ return ERROR_FILE_CORRUPT;
+ pIndexHeader = (PFILE_INDEX_HEADER_V2)pbFileData;
+
+ // Verify the content of the index header
+ if(pIndexHeader->IndexVersion != 0x07 || pIndexHeader->BucketIndex != (BYTE)BucketIndex || pIndexHeader->ExtraBytes != 0x00)
+ return ERROR_BAD_FORMAT;
+ if(pIndexHeader->EncodedSizeLength != 0x04 || pIndexHeader->StorageOffsetLength != 0x05 || pIndexHeader->EKeyLength != 0x09)
+ return ERROR_BAD_FORMAT;
+
+ // Capture the values from the index header
+ InHeader.IndexVersion = pIndexHeader->IndexVersion;
+ InHeader.BucketIndex = pIndexHeader->BucketIndex;
+ InHeader.StorageOffsetLength = pIndexHeader->StorageOffsetLength;
+ InHeader.EncodedSizeLength = pIndexHeader->EncodedSizeLength;
+ InHeader.EKeyLength = pIndexHeader->EKeyLength;
+ InHeader.FileOffsetBits = pIndexHeader->FileOffsetBits;
+ InHeader.Alignment = 0;
+ InHeader.SegmentSize = pIndexHeader->SegmentSize;
+
+ // Supply the lengths
+ InHeader.HeaderLength = sizeof(FILE_INDEX_GUARDED_BLOCK) + sizeof(FILE_INDEX_HEADER_V2);
+ InHeader.HeaderPadding = 8;
+ InHeader.EntryLength = pIndexHeader->EKeyLength + pIndexHeader->StorageOffsetLength + pIndexHeader->EncodedSizeLength;
+ InHeader.EKeyCount = 0;
+ return ERROR_SUCCESS;
+}
+
+static int LoadIndexFile_V1(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData)
+{
+ LPBYTE pbEKeyEntries = pbFileData + InHeader.HeaderLength + InHeader.HeaderPadding;
+
+ // Remember the values from the index header
+ hs->FileOffsetBits = InHeader.FileOffsetBits;
+ hs->EKeyLength = InHeader.EKeyLength;
+
+ // Load the entries from a continuous array
+ return LoadIndexItems(hs, InHeader, pbEKeyEntries, pbFileData + cbFileData);
+}
+
+static int LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData)
+{
+ LPBYTE pbEKeyEntry;
+ LPBYTE pbFileEnd = pbFileData + cbFileData;
+ LPBYTE pbFilePtr = pbFileData + InHeader.HeaderLength + InHeader.HeaderPadding;
+ size_t EKeyEntriesLength;
+ DWORD BlockSize = 0;
+ int nError = ERROR_NOT_SUPPORTED;
+
+ // Remember the values from the index header
+ hs->FileOffsetBits = InHeader.FileOffsetBits;
+ hs->EKeyLength = InHeader.EKeyLength;
+
+ // Get the pointer to the first block of EKey entries
+ if((pbEKeyEntry = CaptureGuardedBlock2(pbFilePtr, pbFileEnd, InHeader.EntryLength, &BlockSize)) != NULL)
+ {
+ // Supply the number of EKey entries
+ InHeader.HeaderPadding += sizeof(FILE_INDEX_GUARDED_BLOCK);
+
+ // Load the continuous array of EKeys
+ return LoadIndexItems(hs, InHeader, pbEKeyEntry, pbEKeyEntry + BlockSize);
+ }
+
+ // Get the pointer to the second block of EKey entries.
+ // They are alway at the position aligned to 4096
+ EKeyEntriesLength = pbFileEnd - pbFilePtr;
+ if(EKeyEntriesLength >= 0x7800)
+ {
+ LPBYTE pbStartPage = pbFileData + 0x1000;
+ LPBYTE pbEndPage = pbStartPage + FILE_INDEX_PAGE_SIZE;
+ size_t AlignedLength = ALIGN_TO_SIZE(InHeader.EntryLength, 4);
+
+ // Parse the chunks with the EKey entries
+ while(pbStartPage < pbFileEnd)
+ {
+ pbEKeyEntry = pbStartPage;
+
+ while(pbEKeyEntry < pbEndPage)
+ {
+ CASC_CKEY_ENTRY CKeyEntry;
+
+ // Check the EKey entry protected by 32-bit hash
+ if((pbEKeyEntry = CaptureGuardedBlock3(pbEKeyEntry, pbEndPage, InHeader.EntryLength)) == NULL)
+ break;
+
+ // CASC\\0001: Encoding
+ //BREAK_ON_XKEY3(pbEKeyEntry, 0xbc, 0xe8, 0x23);
+
+ // Capture the index entry and verify it.
+ if(CaptureIndexEntry(InHeader, &CKeyEntry, pbEKeyEntry))
+ {
+ // Insert the EKey entry to the array
+ if(hs->IndexArray.Insert(&CKeyEntry, 1) == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Check whether the CKey entry is an encoding entry
+ CheckForEncodingManifestCKey(hs, &CKeyEntry);
+ }
+
+ // Move to the next entry
+ pbEKeyEntry += AlignedLength;
+ }
+
+ // Move to the next chunk
+ pbStartPage += FILE_INDEX_PAGE_SIZE;
+ }
+ nError = ERROR_SUCCESS;
+ }
+
+ return nError;
+}
+
+static int LoadIndexFile(TCascStorage * hs, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
+{
+ CASC_INDEX_HEADER InHeader;
+
+ // Check for CASC version 2
+ if(CaptureIndexHeader_V2(InHeader, pbFileData, cbFileData, BucketIndex) == ERROR_SUCCESS)
+ return LoadIndexFile_V2(hs, InHeader, pbFileData, cbFileData);
+
+ // Check for CASC index version 1
+ if(CaptureIndexHeader_V1(InHeader, pbFileData, cbFileData, BucketIndex) == ERROR_SUCCESS)
+ return LoadIndexFile_V1(hs, InHeader, pbFileData, cbFileData);
+
+ // Should never happen
+ assert(false);
+ return ERROR_BAD_FORMAT;
+}
+
+static int LoadIndexFile(TCascStorage * hs, const TCHAR * szFileName, DWORD BucketIndex)
+{
+ LPBYTE pbFileData;
+ DWORD cbFileData;
+ int nError = ERROR_SUCCESS;
+
+ // WoW6 actually reads THE ENTIRE file to memory. Verified on Mac build (x64).
+ pbFileData = LoadFileToMemory(szFileName, &cbFileData);
+ if(pbFileData && cbFileData)
+ {
+ // Parse and load the index file
+ nError = LoadIndexFile(hs, pbFileData, cbFileData, BucketIndex);
+ CASC_FREE(pbFileData);
+ }
+ else
+ {
+ nError = GetLastError();
+ }
+
+ return nError;
+}
+
+static int LoadLocalIndexFiles(TCascStorage * hs)
+{
+ TCHAR * szFileName;
+ DWORD OldIndexArray[CASC_INDEX_COUNT];
+ DWORD IndexArray[CASC_INDEX_COUNT];
+ int nError;
+
+ // Scan all index files and load contained EKEY entries
+ memset(OldIndexArray, 0, sizeof(OldIndexArray));
+ memset(IndexArray, 0, sizeof(IndexArray));
+ nError = ScanIndexDirectory(hs->szIndexPath, IndexDirectory_OnFileFound, IndexArray, OldIndexArray, hs);
+ if(nError == ERROR_SUCCESS)
+ {
+ // Initialize the array of index files
+ if((nError = hs->IndexArray.Create(sizeof(CASC_CKEY_ENTRY), 0x200000)) == ERROR_SUCCESS)
+ {
+ // Load each index file
+ for(DWORD i = 0; i < CASC_INDEX_COUNT; i++)
+ {
+ // Create the name of the index file
+ if((szFileName = CreateIndexFileName(hs, i, IndexArray[i])) != NULL)
+ {
+ // Inform the user about what we are doing
+ if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Loading index files", NULL, i, CASC_INDEX_COUNT))
+ {
+ nError = ERROR_CANCELLED;
+ break;
+ }
+
+ // Load the index file
+ if((nError = LoadIndexFile(hs, szFileName, i)) != ERROR_SUCCESS)
+ break;
+ CASC_FREE(szFileName);
+ }
+ }
+
+ // Remember the number of files that are present locally
+ hs->LocalFiles = hs->IndexArray.ItemCount();
+ }
+ }
+
+ return nError;
+}
+
+//-----------------------------------------------------------------------------
+// Online index files
+
+static int CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, DWORD cbIndexFile)
+{
+ FILE_INDEX_FOOTER<0x08> * pFooter08;
+ BYTE checksum_data[0x40] = { 0 };
+ BYTE md5_hash[MD5_HASH_SIZE];
+ DWORD checksum_data_length;
+
+ // Clear the entire structure
+ memset(&InFooter, 0, sizeof(CASC_ARCINDEX_FOOTER));
+
+ // Check the variant for checksum == 0x08
+ pFooter08 = (FILE_INDEX_FOOTER<0x08> *)(pbIndexFile + cbIndexFile - sizeof(FILE_INDEX_FOOTER<0x08>));
+ if (pFooter08->Version == 1 && pFooter08->Reserved[0] == 0 && pFooter08->Reserved[1] == 0 && pFooter08->FooterHashBytes == 8)
+ {
+ // Copy the entire structure
+ memcpy(InFooter.TocHash, pFooter08->TocHash, MD5_HASH_SIZE);
+ memcpy(InFooter.FooterHash, pFooter08->FooterHash, pFooter08->FooterHashBytes);
+ InFooter.Version = pFooter08->Version;
+ InFooter.OffsetBytes = pFooter08->OffsetBytes;
+ InFooter.SizeBytes = pFooter08->SizeBytes;
+ InFooter.EKeyBytes = pFooter08->EKeySizeBytes;
+ InFooter.FooterHashBytes = pFooter08->FooterHashBytes;
+ InFooter.PageLength = pFooter08->PageSizeKB << 10;
+ InFooter.ItemLength = pFooter08->EKeySizeBytes + pFooter08->OffsetBytes + pFooter08->SizeBytes;
+ InFooter.FooterLength = sizeof(FILE_INDEX_FOOTER<0x08>);
+ InFooter.ElementCount = ConvertBytesToInteger_4_LE(pFooter08->ElementCount);
+
+ // Verify the hash. FooterHash needs to be cleared in order to calculate footer hash properly
+ checksum_data_length = FIELD_OFFSET(FILE_INDEX_FOOTER<0x08>, FooterHash) - FIELD_OFFSET(FILE_INDEX_FOOTER<0x08>, Version);
+ memcpy(checksum_data, &pFooter08->Version, checksum_data_length);
+ CascCalculateDataBlockHash(checksum_data, sizeof(FILE_INDEX_FOOTER<0x08>) - MD5_HASH_SIZE, md5_hash);
+ if(!memcmp(md5_hash, InFooter.FooterHash, InFooter.FooterHashBytes))
+ return ERROR_SUCCESS;
+ }
+
+ assert(false);
+ return ERROR_BAD_FORMAT;
+}
+
+static int CaptureArcIndexEntry(CASC_ARCINDEX_FOOTER & InFooter, CASC_ARCINDEX_ENTRY & InEntry, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd)
+{
+ // If there enough bytes for one entry/
+ if ((pbIndexPage + InFooter.ItemLength) > pbIndexPageEnd)
+ return ERROR_BAD_FORMAT;
+
+ // Copy the item
+ memcpy(InEntry.EKey, pbIndexPage, InFooter.EKeyBytes);
+ pbIndexPage += InFooter.EKeyBytes;
+
+ // Copy the archive offset
+ InEntry.EncodedSize = ConvertBytesToInteger_X(pbIndexPage, InFooter.OffsetBytes);
+ pbIndexPage += InFooter.OffsetBytes;
+
+ // Copy thefile encoded size
+ InEntry.ArchiveOffset = ConvertBytesToInteger_X(pbIndexPage, InFooter.SizeBytes);
+ if (InEntry.ArchiveOffset >= 0x10000000)
+ return ERROR_BAD_FORMAT;
+
+ // Is there a valid hash?
+ return CascIsValidMD5(InEntry.EKey) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
+}
+
+static int VerifyIndexSize(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, size_t cbIndexFile, LPBYTE * PtrIndexEnd)
+{
+ size_t nPageCount;
+
+ // Set the new length (without the footer)
+ cbIndexFile = cbIndexFile - InFooter.FooterLength;
+ nPageCount = cbIndexFile / (InFooter.PageLength + MD5_HASH_SIZE);
+
+ // There must be equal or more pages
+ if(((InFooter.PageLength + MD5_HASH_SIZE) * nPageCount) > cbIndexFile)
+ return ERROR_BAD_FORMAT;
+
+ // Return the end-of-index
+ nPageCount = cbIndexFile / (InFooter.PageLength + MD5_HASH_SIZE);
+ PtrIndexEnd[0] = pbIndexFile + (nPageCount * InFooter.PageLength);
+ return ERROR_SUCCESS;
+}
+
+static int LoadArchiveIndexPage(TCascStorage * hs, CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexHash, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd)
+{
+ CASC_ARCINDEX_ENTRY InEntry;
+ int nError;
+
+ while (pbIndexPage < pbIndexPageEnd)
+ {
+ // Capture the index entry
+ nError = CaptureArcIndexEntry(InFooter, InEntry, pbIndexPage, pbIndexPageEnd);
+ if (nError != ERROR_SUCCESS)
+ break;
+
+ // Insert the index entry to the array
+ memcpy(InEntry.IndexHash, pbIndexHash, MD5_HASH_SIZE);
+ if (hs->ArcIndexArray.Insert(&InEntry, 1) == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Move to the next entry
+ pbIndexPage += InFooter.ItemLength;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+static int LoadArchiveIndexFile(TCascStorage * hs, LPBYTE pbIndexHash, LPBYTE pbIndexFile, DWORD cbIndexFile)
+{
+ CASC_ARCINDEX_FOOTER InFooter;
+ LPBYTE pbIndexEnd = NULL;
+ int nError;
+
+ // Validate and capture the footer
+ nError = CaptureArcIndexFooter(InFooter, pbIndexFile, cbIndexFile);
+ if (nError != ERROR_SUCCESS)
+ return nError;
+
+ // Verify the size of the index file
+ nError = VerifyIndexSize(InFooter, pbIndexFile, cbIndexFile, &pbIndexEnd);
+ if (nError != ERROR_SUCCESS)
+ return nError;
+
+ // Parse all pages
+ while (pbIndexFile < pbIndexEnd)
+ {
+ // Load the entire page
+ nError = LoadArchiveIndexPage(hs, InFooter, pbIndexHash, pbIndexFile, pbIndexFile + InFooter.PageLength);
+ if (nError != ERROR_SUCCESS)
+ break;
+
+ // Move to the next page
+ pbIndexFile += InFooter.PageLength;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+static int BuildMapOfArcIndices(TCascStorage * hs)
+{
+ PCASC_ARCINDEX_ENTRY pEntry;
+ size_t nItemCount = hs->ArcIndexArray.ItemCount();
+ int nError;
+
+ // Create the map
+ nError = hs->ArcIndexMap.Create(nItemCount, MD5_HASH_SIZE, FIELD_OFFSET(CASC_ARCINDEX_ENTRY, EKey));
+ if (nError != ERROR_SUCCESS)
+ return nError;
+
+ // Insert all items
+ for(size_t i = 0; i < nItemCount; i++)
+ {
+ pEntry = (PCASC_ARCINDEX_ENTRY)hs->ArcIndexArray.ItemAt(i);
+ if (pEntry != NULL)
+ {
+ if (!hs->ArcIndexMap.InsertObject(pEntry, pEntry->EKey))
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+ }
+
+ return nError;
+}
+
+static int LoadArchiveIndexFiles(TCascStorage * hs)
+{
+ LPBYTE pbFileData;
+ TCHAR szLocalPath[MAX_PATH];
+ DWORD cbFileData = 0;
+ size_t nArchiveCount = (hs->ArchivesKey.cbData / MD5_HASH_SIZE);
+ int nError = ERROR_SUCCESS;
+
+ // Create the array object for the indices
+ nError = hs->ArcIndexArray.Create(sizeof(CASC_ARCINDEX_ENTRY), 10000);
+ if (nError != ERROR_SUCCESS)
+ return nError;
+
+ // Load all the indices
+ for (size_t i = 0; i < nArchiveCount; i++)
+ {
+ LPBYTE pbIndexHash = hs->ArchivesKey.pbData + (i * MD5_HASH_SIZE);
+
+ // Inform the user about what we are doing
+ if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Downloading archive indexes", NULL, (DWORD)(i), (DWORD)(nArchiveCount)))
+ {
+ nError = ERROR_CANCELLED;
+ break;
+ }
+
+ // Make sure that we have local copy of the file
+ nError = DownloadFileFromCDN(hs, _T("data"), pbIndexHash, _T(".index"), szLocalPath, _countof(szLocalPath));
+ if (nError == ERROR_SUCCESS)
+ {
+ // Load the index file to memory
+ pbFileData = LoadFileToMemory(szLocalPath, &cbFileData);
+ if (pbFileData && cbFileData)
+ {
+ nError = LoadArchiveIndexFile(hs, pbIndexHash, pbFileData, cbFileData);
+ CASC_FREE(pbFileData);
+ }
+ }
+
+ // Break if an error
+ if (nError != ERROR_SUCCESS)
+ break;
+ }
+
+ // Build map of EKey -> CASC_ARCINDEX_ENTRY
+ if (nError == ERROR_SUCCESS)
+ nError = BuildMapOfArcIndices(hs);
+
+ return nError;
+}
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+int LoadIndexFiles(TCascStorage * hs)
+{
+ if (hs->dwFeatures & CASC_FEATURE_ONLINE)
+ {
+ return LoadArchiveIndexFiles(hs);
+ }
+ else
+ {
+ return LoadLocalIndexFiles(hs);
+ }
+} \ No newline at end of file
diff --git a/dep/CascLib/src/CascLib.def b/dep/CascLib/src/CascLib.def
index cb5f9166e49..12e7904cb84 100644
--- a/dep/CascLib/src/CascLib.def
+++ b/dep/CascLib/src/CascLib.def
@@ -9,12 +9,13 @@ LIBRARY CascLib.dll
EXPORTS
CascOpenStorage
+ CascOpenOnlineStorage
CascGetStorageInfo
+ CascAddEncryptionKey
CascCloseStorage
- CascOpenFileByIndexKey
- CascOpenFileByEncodingKey
CascOpenFile
+ CascGetFileInfo
CascGetFileSize
CascSetFilePointer
CascReadFile
diff --git a/dep/CascLib/src/CascLib.h b/dep/CascLib/src/CascLib.h
index 272d4b4ad4d..1fb1911eade 100644
--- a/dep/CascLib/src/CascLib.h
+++ b/dep/CascLib/src/CascLib.h
@@ -29,14 +29,17 @@ extern "C" {
//-----------------------------------------------------------------------------
// Defines
-#define CASCLIB_VERSION 0x0100 // Current version of CascLib (1.0)
-#define CASCLIB_VERSION_STRING "1.00" // String version of CascLib version
-
-// Values for CascOpenStorage
-#define CASC_STOR_XXXXX 0x00000001 // Not used
+#define CASCLIB_VERSION 0x0132 // Current version of CascLib (1.50)
+#define CASCLIB_VERSION_STRING "1.50" // String version of CascLib version
// Values for CascOpenFile
-#define CASC_OPEN_BY_ENCODING_KEY 0x00000001 // The name is just the encoding key; skip ROOT file processing
+#define CASC_OPEN_BY_NAME 0x00000000 // Open the file by name. This is the default value
+#define CASC_OPEN_BY_CKEY 0x00000001 // The name is just the content key; skip ROOT file processing
+#define CASC_OPEN_BY_EKEY 0x00000002 // The name is just the encoded key; skip ROOT file processing
+#define CASC_OPEN_BY_FILEID 0x00000003 // The name is CASC_IDTONAME(FileDataId)
+#define CASC_OPEN_TYPE_MASK 0x0000000F // The mask which gets open type from the dwFlags
+#define CASC_OPEN_FLAGS_MASK 0xFFFFFFF0 // The mask which gets open type from the dwFlags
+#define CASC_STRICT_DATA_CHECK 0x00000010 // Verify all data read from a file
#define CASC_LOCALE_ALL 0xFFFFFFFF
#define CASC_LOCALE_NONE 0x00000000
@@ -58,25 +61,14 @@ extern "C" {
#define CASC_LOCALE_ITIT 0x00008000
#define CASC_LOCALE_PTPT 0x00010000
-#define CASC_LOCALE_BIT_ENUS 0x01
-#define CASC_LOCALE_BIT_KOKR 0x02
-#define CASC_LOCALE_BIT_RESERVED 0x03
-#define CASC_LOCALE_BIT_FRFR 0x04
-#define CASC_LOCALE_BIT_DEDE 0x05
-#define CASC_LOCALE_BIT_ZHCN 0x06
-#define CASC_LOCALE_BIT_ESES 0x07
-#define CASC_LOCALE_BIT_ZHTW 0x08
-#define CASC_LOCALE_BIT_ENGB 0x09
-#define CASC_LOCALE_BIT_ENCN 0x0A
-#define CASC_LOCALE_BIT_ENTW 0x0B
-#define CASC_LOCALE_BIT_ESMX 0x0C
-#define CASC_LOCALE_BIT_RURU 0x0D
-#define CASC_LOCALE_BIT_PTBR 0x0E
-#define CASC_LOCALE_BIT_ITIT 0x0F
-#define CASC_LOCALE_BIT_PTPT 0x10
-
-
-#define MAX_CASC_KEY_LENGTH 0x10 // Maximum length of the key (equal to MD5 hash)
+// Content flags on WoW
+#define CASC_CFLAG_LOAD_ON_WINDOWS 0x08
+#define CASC_CFLAG_LOAD_ON_MAC 0x10
+#define CASC_CFLAG_LOW_VIOLENCE 0x80
+#define CASC_CFLAG_DONT_LOAD 0x100
+#define CASC_CFLAG_NO_NAME_HASH 0x10000000
+#define CASC_CFLAG_BUNDLE 0x40000000
+#define CASC_CFLAG_NO_COMPRESSION 0x80000000
#ifndef MD5_HASH_SIZE
#define MD5_HASH_SIZE 0x10
@@ -87,79 +79,205 @@ extern "C" {
#define SHA1_DIGEST_SIZE 0x14 // 160 bits
#endif
-#ifndef LANG_NEUTRAL
-#define LANG_NEUTRAL 0x00 // Neutral locale
-#endif
-
// Return value for CascGetFileSize and CascSetFilePointer
+#define CASC_INVALID_INDEX 0xFFFFFFFF
#define CASC_INVALID_SIZE 0xFFFFFFFF
#define CASC_INVALID_POS 0xFFFFFFFF
#define CASC_INVALID_ID 0xFFFFFFFF
-
-// Flags for CascGetStorageInfo
-#define CASC_FEATURE_LISTFILE 0x00000001 // The storage supports listfile
+#define CASC_INVALID_OFFS64 0xFFFFFFFFFFFFFFFF
+
+// Flags for CASC_STORAGE_FEATURES::dwFeatures
+#define CASC_FEATURE_FILE_NAMES 0x00000001 // File names are supported by the storage
+#define CASC_FEATURE_ROOT_CKEY 0x00000002 // Present if the storage's ROOT returns CKey
+#define CASC_FEATURE_TAGS 0x00000002 // Tags are supported by the storage
+#define CASC_FEATURE_FNAME_HASHES 0x00000004 // The storage contains file name hashes on ALL files
+#define CASC_FEATURE_FNAME_HASHES_OPTIONAL 0x00000008 // The storage contains file name hashes for SOME files
+#define CASC_FEATURE_FILE_DATA_IDS 0x00000010 // The storage indexes files by FileDataId
+#define CASC_FEATURE_LOCALE_FLAGS 0x00000020 // Locale flags are supported
+#define CASC_FEATURE_CONTENT_FLAGS 0x00000040 // Content flags are supported
+#define CASC_FEATURE_ONLINE 0x00000080 // The storage is an online storage
+
+// Macro to convert FileDataId to the argument of CascOpenFile
+#define CASC_FILE_DATA_ID(FileDataId) ((LPCSTR)(size_t)FileDataId)
+#define CASC_FILE_DATA_ID_FROM_STRING(szFileName) ((DWORD)(size_t)szFileName)
//-----------------------------------------------------------------------------
// Structures
typedef enum _CASC_STORAGE_INFO_CLASS
{
- CascStorageFileCount,
+ // Returns the number of local files in the storage. Note that files
+ // can exist under different names, so the total number of files in the archive
+ // can be higher than the value returned by this info class
+ CascStorageLocalFileCount,
+
+ // Returns the total file count, including the offline files
+ CascStorageTotalFileCount,
+
+ // Returns the CASC_STORAGE_FEATURES structure.
CascStorageFeatures,
- CascStorageGameInfo,
- CascStorageGameBuild,
CascStorageInstalledLocales,
+ CascStorageProduct, // Gives CASC_STORAGE_PRODUCT
+ CascStorageTags, // Gives CASC_STORAGE_TAGS structure
CascStorageInfoClassMax
} CASC_STORAGE_INFO_CLASS, *PCASC_STORAGE_INFO_CLASS;
-
-typedef struct _QUERY_KEY
+typedef enum _CASC_FILE_INFO_CLASS
+{
+ CascFileContentKey,
+ CascFileEncodedKey,
+ CascFileFullInfo, // Gives CASC_FILE_FULL_INFO structure
+ CascFileInfoClassMax
+} CASC_FILE_INFO_CLASS, *PCASC_FILE_INFO_CLASS;
+
+// See https://wowdev.wiki/TACT#Products
+typedef enum _CASC_PRODUCT
{
- LPBYTE pbData;
- DWORD cbData;
-} QUERY_KEY, *PQUERY_KEY;
+ UnknownProduct,
+ HeroesOfTheStorm,
+ Diablo3,
+ Overwatch,
+ StarCraft1,
+ StarCraft2,
+ WorldOfWarcraft,
+ WarCraft3,
+ Destiny2,
+ CallOfDutyBlackOps4,
+ Odin,
+ MaxProductValue
+
+} CASC_PRODUCT, *PCASC_PRODUCT;
+
+// CascLib may provide a fake name, constructed from file data id, CKey or EKey.
+// This enum helps to see what name was actually returned
+// Note that any of these names can be passed to CascOpenFile with no extra flags
+typedef enum _CASC_NAME_TYPE
+{
+ CascNameFull, // Fully qualified file name
+ CascNameDataId, // Name created from file data id (FILE%08X.dat)
+ CascNameCKey, // Name created as string representation of CKey
+ CascNameEKey // Name created as string representation of EKey
+} CASC_NAME_TYPE, *PCASC_NAME_TYPE;
// Structure for SFileFindFirstFile and SFileFindNextFile
typedef struct _CASC_FIND_DATA
{
- char szFileName[MAX_PATH]; // Full name of the found file
- char * szPlainName; // Plain name of the found file
- BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key
- DWORD dwLocaleFlags; // Locale flags (WoW only)
- DWORD dwFileDataId; // File data ID (WoW only)
- DWORD dwFileSize; // Size of the file
+ // Full name of the found file. In case when this is CKey/EKey,
+ // this will be just string representation of the key stored in 'FileKey'
+ char szFileName[MAX_PATH];
+
+ // Content key. This is present if the CASC_FEATURE_ROOT_CKEY is present
+ BYTE CKey[MD5_HASH_SIZE];
+
+ // Encoded key. This is always present.
+ BYTE EKey[MD5_HASH_SIZE];
+
+ // Tag mask. Only valid if the storage supports tags, otherwise 0
+ ULONGLONG TagBitMask;
+
+ // Plain name of the found file. Pointing inside the 'szFileName' array
+ char * szPlainName;
+
+ // File data ID. Only valid if the storage supports file data IDs, otherwise CASC_INVALID_ID
+ DWORD dwFileDataId;
+
+ // Size of the file, as retrieved from CKey entry or EKey entry
+ DWORD dwFileSize;
+
+ // Locale flags. Only valid if the storage supports locale flags, otherwise CASC_INVALID_ID
+ DWORD dwLocaleFlags;
+
+ // Content flags. Only valid if the storage supports content flags, otherwise CASC_INVALID_ID
+ DWORD dwContentFlags;
+
+ // Hints as for which open method is suitable
+ DWORD bFileAvailable:1; // If true the file is available locally
+ DWORD bCanOpenByName:1;
+ DWORD bCanOpenByDataId:1;
+ DWORD bCanOpenByCKey:1;
+ DWORD bCanOpenByEKey:1;
+ CASC_NAME_TYPE NameType;
} CASC_FIND_DATA, *PCASC_FIND_DATA;
-//-----------------------------------------------------------------------------
-// Callback functions
+typedef struct _CASC_STORAGE_TAG
+{
+ LPCSTR szTagName; // Tag name (zero terminated, ANSI)
+ DWORD TagNameLength; // Length of the tag name
+ DWORD TagValue; // Tag value
+} CASC_STORAGE_TAG, *PCASC_STORAGE_TAG;
-typedef struct TFileStream TFileStream;
-typedef void (WINAPI * STREAM_DOWNLOAD_CALLBACK)(void * pvUserData, ULONGLONG ByteOffset, DWORD dwTotalBytes);
+typedef struct _CASC_STORAGE_TAGS
+{
+ size_t TagCount; // Number of items in the Tags array
+ size_t Reserved; // Reserved for future use
-//-----------------------------------------------------------------------------
-// We have our own qsort implementation, optimized for sorting array of pointers
+ CASC_STORAGE_TAG Tags[1]; // Array of CASC tags
+
+} CASC_STORAGE_TAGS, *PCASC_STORAGE_TAGS;
+
+typedef struct _CASC_STORAGE_PRODUCT
+{
+ LPCSTR szProductName;
+ DWORD dwBuildNumber;
+ CASC_PRODUCT Product;
+
+} CASC_STORAGE_PRODUCT, *PCASC_STORAGE_PRODUCT;
-void qsort_pointer_array(void ** base, size_t num, int (*compare)(const void *, const void *, const void *), const void * context);
+typedef struct _CASC_FILE_FULL_INFO
+{
+ BYTE CKey[MD5_HASH_SIZE]; // CKey
+ BYTE EKey[MD5_HASH_SIZE]; // EKey
+ char DataFileName[0x10]; // Plain name of the data file where the file is stored
+ ULONGLONG StorageOffset; // Offset of the file over the entire storage
+ ULONGLONG SegmentOffset; // Offset of the file in the segment file ("data.###")
+ ULONGLONG TagBitMask; // Bitmask of tags. Zero if not supported
+ ULONGLONG FileNameHash; // Hash of the file name. Zero if not supported
+ DWORD SegmentIndex; // Index of the segment file (aka 0 = "data.000")
+ DWORD FileDataId; // File data ID. CASC_INVALID_ID if not supported.
+ DWORD ContentSize; // Content size of the file
+ DWORD EncodedSize; // Encoded size of the file
+ DWORD LocaleFlags; // Locale flags. CASC_INVALID_ID if not supported.
+ DWORD ContentFlags; // Locale flags. CASC_INVALID_ID if not supported
+
+} CASC_FILE_FULL_INFO, *PCASC_FILE_FULL_INFO;
+
+//-----------------------------------------------------------------------------
+// Some operations (e.g. opening an online storage) may take long time.
+// This callback allows an application to be notified about loading progress.
+// This callback only works for a single CascOpen(Online)Storage call.
+
+typedef bool (WINAPI * PFNPROGRESSCALLBACK)( // Return 'true' to cancel the loading process
+ void * PtrUserParam, // User-specific parameter passed to the callback
+ LPCSTR szWork, // Text for the current activity (example: "Loading "ENCODING" file")
+ LPCSTR szObject, // (optional) name of the object tied to the activity (example: index file name)
+ DWORD nCurrent, // (optional) current object being processed
+ DWORD nTotal // (optional) If non-zero, this is the total number of objects to process
+ );
+
+void WINAPI CascSetProgressCallback(
+ PFNPROGRESSCALLBACK PtrUserCallback, // Pointer to the callback function that will be called during opening the storage
+ void * PtrUserParam // Arbitrary user parameter that will be passed to the callback
+ );
//-----------------------------------------------------------------------------
// Functions for storage manipulation
-bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwLocaleMask, HANDLE * phStorage);
+bool WINAPI CascOpenStorage(LPCTSTR szDataPath, DWORD dwLocaleMask, HANDLE * phStorage);
+bool WINAPI CascOpenOnlineStorage(LPCTSTR szLocalCache, LPCSTR szCodeName, LPCSTR szRegion, DWORD dwLocaleMask, HANDLE * phStorage);
bool WINAPI CascGetStorageInfo(HANDLE hStorage, CASC_STORAGE_INFO_CLASS InfoClass, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded);
+bool WINAPI CascAddEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPBYTE Key);
bool WINAPI CascCloseStorage(HANDLE hStorage);
-bool WINAPI CascOpenFileByIndexKey(HANDLE hStorage, PQUERY_KEY pIndexKey, DWORD dwFlags, HANDLE * phFile);
-bool WINAPI CascOpenFileByEncodingKey(HANDLE hStorage, PQUERY_KEY pEncodingKey, DWORD dwFlags, HANDLE * phFile);
-bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocale, DWORD dwFlags, HANDLE * phFile);
+bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocaleFlags, DWORD dwOpenFlags, HANDLE * phFile);
+bool WINAPI CascGetFileInfo(HANDLE hFile, CASC_FILE_INFO_CLASS InfoClass, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded);
DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh);
-DWORD WINAPI CascGetFileId(HANDLE hStorage, const char * szFileName);
DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod);
bool WINAPI CascReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, PDWORD pdwRead);
bool WINAPI CascCloseFile(HANDLE hFile);
-HANDLE WINAPI CascFindFirstFile(HANDLE hStorage, const char * szMask, PCASC_FIND_DATA pFindData, const TCHAR * szListFile);
+HANDLE WINAPI CascFindFirstFile(HANDLE hStorage, LPCSTR szMask, PCASC_FIND_DATA pFindData, LPCTSTR szListFile);
bool WINAPI CascFindNextFile(HANDLE hFind, PCASC_FIND_DATA pFindData);
bool WINAPI CascFindClose(HANDLE hFind);
@@ -168,8 +286,8 @@ bool WINAPI CascFindClose(HANDLE hFind);
#ifndef PLATFORM_WINDOWS
-int GetLastError();
-void SetLastError(int nError);
+DWORD GetLastError();
+void SetLastError(DWORD dwErrCode);
#endif // PLATFORM_WINDOWS
diff --git a/dep/CascLib/src/CascMndx.h b/dep/CascLib/src/CascMndx.h
deleted file mode 100644
index 2ce268e40a6..00000000000
--- a/dep/CascLib/src/CascMndx.h
+++ /dev/null
@@ -1,359 +0,0 @@
-/*****************************************************************************/
-/* CascMndxRoot.h Copyright (c) Ladislav Zezula 2014 */
-/*---------------------------------------------------------------------------*/
-/* Interface file for MNDX structures */
-/*---------------------------------------------------------------------------*/
-/* Date Ver Who Comment */
-/* -------- ---- --- ------- */
-/* 15.05.14 1.00 Lad Created */
-/*****************************************************************************/
-
-#ifndef __CASC_MNDX_ROOT__
-#define __CASC_MNDX_ROOT__
-
-class TFileNameDatabase;
-
-#define CASC_MAX_MAR_FILES 3 // Maximum of 3 MAR files are supported
-#define CASC_MNDX_SIGNATURE 0x58444E4D // 'MNDX'
-
-#define CASC_MAX_ENTRIES(type) (0xFFFFFFFF / sizeof(type))
-
-#define CASC_SEARCH_INITIALIZING 0
-#define CASC_SEARCH_SEARCHING 2
-#define CASC_SEARCH_FINISHED 4
-
-typedef struct _TRIPLET
-{
- DWORD BaseValue;
- DWORD Value2;
- DWORD Value3;
-} TRIPLET, *PTRIPLET;
-
-typedef struct _NAME_FRAG
-{
- DWORD ItemIndex; // Back index to various tables
- DWORD NextIndex; // The following item index
- DWORD FragOffs; // Higher 24 bits are 0xFFFFFF00 --> A single matching character
- // Otherwise --> Offset to the name fragment table
-} NAME_FRAG, *PNAME_FRAG;
-
-typedef struct _PATH_STOP
-{
- DWORD ItemIndex;
- DWORD field_4;
- DWORD field_8;
- DWORD field_C;
- DWORD field_10;
-} PATH_STOP, *PPATH_STOP;
-
-typedef union _ARRAY_POINTER
-{
- LPBYTE Bytes; // Pointer to an octet
- char * Chars; // Pointer to a character
- PDWORD Uint32s; // Pointer to a DWORD
- PTRIPLET Triplets; // Pointer to TRIPLET
- PNAME_FRAG NameFrags; // Pointer to name fragment entry
- PPATH_STOP PathStopPtr; // Pointer to path checkpoint
- PULONGLONG Int64Ptr; // Pointer to 64-bit integer
-
-} ARRAY_POINTER, *PARRAY_POINTER;
-
-// Simple access to various tables within TGenericArray
-#define ByteArray ArrayPointer.Bytes
-#define CharArray ArrayPointer.Chars
-#define Uint32Array ArrayPointer.Uint32s
-#define TripletArray ArrayPointer.Triplets
-#define NameFragArray ArrayPointer.NameFrags
-
-class TByteStream
-{
- public:
-
- TByteStream();
-
- void ExchangeWith(TByteStream & Target);
- int GetBytes(DWORD cbByteCount, PARRAY_POINTER PtrArray);
- int SkipBytes(DWORD cbByteCount);
- int SetByteBuffer(LPBYTE pbNewMarData, DWORD cbNewMarData);
- int GetValue_DWORD(DWORD & Value);
- int GetValue_ItemCount(DWORD & NumberOfBytes, DWORD & ItemCount, DWORD ItemSize);
- int GetArray_DWORDs(PARRAY_POINTER PtrArray, DWORD ItemCount);
- int GetArray_Triplets(PARRAY_POINTER PtrArray, DWORD ItemCount);
- int GetArray_NameTable(PARRAY_POINTER PtrArray, DWORD ItemCount);
- int GetArray_BYTES(PARRAY_POINTER PtrArray, DWORD ItemCount);
-
- LPBYTE pbByteData;
- void * pvMappedFile;
- DWORD cbByteData;
- DWORD field_C;
- HANDLE hFile;
- HANDLE hMap;
-};
-
-class TGenericArray
-{
- public:
-
- TGenericArray();
- ~TGenericArray();
-
- int SetArrayValid();
-
- void ExchangeWith(TGenericArray & Target);
- void CopyFrom(TGenericArray & Source);
-
- void SetMaxItems_CHARS(DWORD NewMaxItemCount);
- void SetMaxItems_PATH_STOP(DWORD NewMaxItemCount);
-
- void InsertOneItem_CHAR(char OneChar);
- void InsertOneItem_PATH_STOP(PATH_STOP & NewItem);
-
- void sub_19583A0(DWORD NewItemCount);
-
- int LoadDwordsArray(TByteStream & InStream);
- int LoadTripletsArray(TByteStream & InStream);
- int LoadByteArray(TByteStream & InStream);
- int LoadFragmentInfos(TByteStream & InStream);
- int LoadStrings(TByteStream & InStream);
-
- int LoadDwordsArray_Copy(TByteStream & InStream);
- int LoadTripletsArray_Copy(TByteStream & InStream);
- int LoadBytes_Copy(TByteStream & InStream);
- int LoadFragmentInfos_Copy(TByteStream & InStream);
- int LoadStringsWithCopy(TByteStream & InStream);
-
- ARRAY_POINTER DataBuffer;
- ARRAY_POINTER FirstValid;
-
- ARRAY_POINTER ArrayPointer;
- DWORD ItemCount; // Number of items in the array
- DWORD MaxItemCount; // Capacity of the array
- bool bIsValidArray;
-};
-
-class TBitEntryArray : public TGenericArray
-{
- public:
-
- TBitEntryArray();
- ~TBitEntryArray();
-
- DWORD GetBitEntry(DWORD EntryIndex)
- {
- DWORD dwItemIndex = (EntryIndex * BitsPerEntry) >> 0x05;
- DWORD dwStartBit = (EntryIndex * BitsPerEntry) & 0x1F;
- DWORD dwEndBit = dwStartBit + BitsPerEntry;
- DWORD dwResult;
-
- // If the end bit index is greater than 32,
- // we also need to load from the next 32-bit item
- if(dwEndBit > 0x20)
- {
- dwResult = (Uint32Array[dwItemIndex + 1] << (0x20 - dwStartBit)) | (Uint32Array[dwItemIndex] >> dwStartBit);
- }
- else
- {
- dwResult = Uint32Array[dwItemIndex] >> dwStartBit;
- }
-
- // Now we also need to mask the result by the bit mask
- return dwResult & EntryBitMask;
- }
-
- void ExchangeWith(TBitEntryArray & Target);
- int LoadFromStream(TByteStream & InStream);
- int LoadFromStream_Exchange(TByteStream & InStream);
-
- DWORD BitsPerEntry;
- DWORD EntryBitMask;
- DWORD TotalEntries;
-};
-
-class TStruct40
-{
- public:
-
- TStruct40();
-
- void InitSearchBuffers();
-
- TGenericArray array_00;
- TGenericArray PathStops; // Array of path checkpoints
- DWORD ItemIndex; // Current name fragment: Index to various tables
- DWORD CharIndex;
- DWORD ItemCount;
- DWORD SearchPhase; // 0 = initializing, 2 = searching, 4 = finished
-};
-
-class TMndxFindResult
-{
- public:
-
- TMndxFindResult();
- ~TMndxFindResult();
-
- int CreateStruct40();
- void FreeStruct40();
-
- int SetSearchPath(const char * szNewSearchPath, size_t cchNewSearchPath);
-
- const char * szSearchMask; // Search mask without wildcards
- size_t cchSearchMask; // Length of the search mask
- DWORD field_8;
- const char * szFoundPath; // Found path name
- size_t cchFoundPath; // Length of the found path name
- DWORD FileNameIndex; // Index of the file name
- TStruct40 * pStruct40;
-};
-
-class TSparseArray
-{
- public:
-
- TSparseArray();
-
- void ExchangeWith(TSparseArray & TargetObject);
- int LoadFromStream(TByteStream & InStream);
- int LoadFromStream_Exchange(TByteStream & InStream);
-
- // Returns true if the item at n-th position is present
- DWORD IsItemPresent(DWORD ItemIndex)
- {
- return (ItemBits.Uint32Array[ItemIndex >> 0x05] & (1 << (ItemIndex & 0x1F)));
- }
-
- DWORD GetItemValue(DWORD ItemIndex);
-
- TGenericArray ItemBits; // Bit array for each item (1 = item is present)
- DWORD TotalItemCount; // Total number of items in the array
- DWORD ValidItemCount; // Number of present items in the array
- TGenericArray BaseValues; // Array of base values for item indexes >= 0x200
- TGenericArray ArrayDwords_38;
- TGenericArray ArrayDwords_50;
-};
-
-class TNameIndexStruct
-{
- public:
-
- TNameIndexStruct();
- ~TNameIndexStruct();
-
- bool CheckNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs);
- bool CheckAndCopyNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs);
- void CopyNameFragment(TMndxFindResult * pStruct1C, DWORD dwFragOffs);
-
- void ExchangeWith(TNameIndexStruct & Target);
- int LoadFromStream(TByteStream & InStream);
- int LoadFromStream_Exchange(TByteStream & InStream);
-
- TGenericArray NameFragments;
- TSparseArray Struct68;
-};
-
-class TStruct10
-{
- public:
-
- TStruct10();
-
- void CopyFrom(TStruct10 & Target);
- int sub_1956FD0(DWORD dwBitMask);
- int sub_1957050(DWORD dwBitMask);
- int sub_19572E0(DWORD dwBitMask);
- int sub_1957800(DWORD dwBitMask);
-
- DWORD field_0;
- DWORD field_4;
- DWORD field_8;
- DWORD field_C;
-};
-
-class TFileNameDatabasePtr
-{
- public:
-
- TFileNameDatabasePtr();
- ~TFileNameDatabasePtr();
-
- int FindFileInDatabase(TMndxFindResult * pStruct1C);
- int sub_1956CE0(TMndxFindResult * pStruct1C, bool * pbFindResult);
-
- int GetFileNameCount(PDWORD PtrFileNameCount);
- int CreateDatabase(LPBYTE pbMarData, DWORD cbMarData);
- int SetDatabase(TFileNameDatabase * pNewDB);
-
- TFileNameDatabase * pDB;
-};
-
-class TFileNameDatabase
-{
- public:
-
- TFileNameDatabase();
-
- void ExchangeWith(TFileNameDatabase & Target);
- int LoadFromStream(TByteStream & InStream);
- int LoadFromStream_Exchange(TByteStream & InStream);
-
- DWORD sub_1959CB0(DWORD dwHashValue);
- DWORD sub_1959F50(DWORD arg_0);
-
- // Retrieves the name fragment distance
- // HOTS: 19573D0/inlined
- DWORD GetNameFragmentOffsetEx(DWORD LoBitsIndex, DWORD HiBitsIndex)
- {
- return (FrgmDist_HiBits.GetBitEntry(HiBitsIndex) << 0x08) | FrgmDist_LoBits.ByteArray[LoBitsIndex];
- }
-
- // HOTS: 1957350, inlined
- DWORD GetNameFragmentOffset(DWORD LoBitsIndex)
- {
- return GetNameFragmentOffsetEx(LoBitsIndex, Struct68_D0.GetItemValue(LoBitsIndex));
- }
-
- bool sub_1957B80(TMndxFindResult * pStruct1C, DWORD dwKey);
- bool CheckNextPathFragment(TMndxFindResult * pStruct1C);
- bool FindFileInDatabase(TMndxFindResult * pStruct1C);
-
- void sub_1958D70(TMndxFindResult * pStruct1C, DWORD arg_4);
- bool sub_1959010(TMndxFindResult * pStruct1C, DWORD arg_4);
- bool sub_1958B00(TMndxFindResult * pStruct1C);
- bool sub_1959460(TMndxFindResult * pStruct1C);
-
- TSparseArray Struct68_00;
- TSparseArray FileNameIndexes; // Array of file name indexes
- TSparseArray Struct68_D0;
-
- // This pair of arrays serves for fast conversion from name hash to fragment offset
- TGenericArray FrgmDist_LoBits; // Array of lower 8 bits of name fragment offset
- TBitEntryArray FrgmDist_HiBits; // Array of upper x bits of name fragment offset
-
- TNameIndexStruct IndexStruct_174;
- TFileNameDatabasePtr NextDB;
-
- TGenericArray NameFragTable;
-
- DWORD NameFragIndexMask;
- DWORD field_214;
- TStruct10 Struct10;
- TByteStream MarStream;
-};
-
-typedef struct _MAR_FILE
-{
- TFileNameDatabasePtr * pDatabasePtr;
- LPBYTE pbMarData;
- DWORD cbMarData;
-} MAR_FILE, *PMAR_FILE;
-
-//-----------------------------------------------------------------------------
-// Macros
-
-// Returns nonzero if the name fragment match is a single-char match
-inline bool IS_SINGLE_CHAR_MATCH(TGenericArray & Table, DWORD ItemIndex)
-{
- return ((Table.NameFragArray[ItemIndex].FragOffs & 0xFFFFFF00) == 0xFFFFFF00);
-}
-
-#endif // __CASC_MNDX_ROOT__
diff --git a/dep/CascLib/src/CascOpenFile.cpp b/dep/CascLib/src/CascOpenFile.cpp
index 9db3691af48..741ab1e1005 100644
--- a/dep/CascLib/src/CascOpenFile.cpp
+++ b/dep/CascLib/src/CascOpenFile.cpp
@@ -15,196 +15,63 @@
//-----------------------------------------------------------------------------
// Local functions
-TCascFile * IsValidFileHandle(HANDLE hFile)
+PCASC_CKEY_ENTRY FindCKeyEntry_CKey(TCascStorage * hs, LPBYTE pbCKey, PDWORD PtrIndex)
{
- TCascFile * hf = (TCascFile *)hFile;
-
- return (hf != NULL && hf->hs != NULL && hf->szClassName != NULL && !strcmp(hf->szClassName, "TCascFile")) ? hf : NULL;
+ return (PCASC_CKEY_ENTRY)hs->CKeyMap.FindObject(pbCKey, PtrIndex);
}
-PCASC_INDEX_ENTRY FindIndexEntry(TCascStorage * hs, PQUERY_KEY pIndexKey)
+PCASC_CKEY_ENTRY FindCKeyEntry_EKey(TCascStorage * hs, LPBYTE pbEKey, PDWORD PtrIndex)
{
- PCASC_INDEX_ENTRY pIndexEntry = NULL;
-
- if(hs->pIndexEntryMap != NULL)
- pIndexEntry = (PCASC_INDEX_ENTRY)Map_FindObject(hs->pIndexEntryMap, pIndexKey->pbData, NULL);
-
- return pIndexEntry;
-}
-
-PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, PDWORD PtrIndex)
-{
- PCASC_ENCODING_ENTRY pEncodingEntry = NULL;
-
- if(hs->pEncodingMap != NULL)
- pEncodingEntry = (PCASC_ENCODING_ENTRY)Map_FindObject(hs->pEncodingMap, pEncodingKey->pbData, PtrIndex);
-
- return pEncodingEntry;
+ return (PCASC_CKEY_ENTRY)hs->EKeyMap.FindObject(pbEKey, PtrIndex);
}
-static TCascFile * CreateFileHandle(TCascStorage * hs, PCASC_INDEX_ENTRY pIndexEntry)
+bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD dwOpenFlags, HANDLE * PtrFileHandle)
{
- ULONGLONG FileOffsMask = ((ULONGLONG)1 << hs->KeyMapping[0].SegmentBits) - 1;
- ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffsetBE);
- TCascFile * hf;
+ TCascFile * hf = NULL;
+ int nError = ERROR_FILE_NOT_FOUND;
- // Allocate the CASC file structure
- hf = (TCascFile *)CASC_ALLOC(TCascFile, 1);
- if(hf != NULL)
+ // If the CKey entry is NULL, we consider the file non-existant
+ if(pCKeyEntry != NULL)
{
- // Initialize the structure
- memset(hf, 0, sizeof(TCascFile));
- hf->ArchiveIndex = (DWORD)(FileOffset >> hs->KeyMapping[0].SegmentBits);
- hf->HeaderOffset = (DWORD)(FileOffset & FileOffsMask);
- hf->szClassName = "TCascFile";
-
- // Copy the file size. Note that for all files except ENCODING,
- // this is the compressed file size
- hf->CompressedSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE);
-
- // For now, we set the file size to be equal to compressed size
- // This is used when loading the ENCODING file, which does not
- // have entry in the encoding table
- hf->FileSize = hf->CompressedSize;
-
- // Increment the number of references to the archive
- hs->dwRefCount++;
- hf->hs = hs;
- }
-
- return hf;
-}
-
-static bool OpenFileByIndexKey(TCascStorage * hs, PQUERY_KEY pIndexKey, DWORD dwFlags, TCascFile ** ppCascFile)
-{
- PCASC_INDEX_ENTRY pIndexEntry;
- int nError = ERROR_SUCCESS;
-
- CASCLIB_UNUSED(dwFlags);
-
- // Find the key entry in the array of file keys
- pIndexEntry = FindIndexEntry(hs, pIndexKey);
- if(pIndexEntry == NULL)
- nError = ERROR_FILE_NOT_FOUND;
-
- // Create the file handle structure
- if(nError == ERROR_SUCCESS)
- {
- ppCascFile[0] = CreateFileHandle(hs, pIndexEntry);
- if(ppCascFile[0] == NULL)
- nError = ERROR_FILE_NOT_FOUND;
+ // Create the file handle structure
+ if((hf = new TCascFile(hs, pCKeyEntry)) != NULL)
+ {
+ hf->bVerifyIntegrity = (dwOpenFlags & CASC_STRICT_DATA_CHECK) ? true : false;
+ hf->bDownloadFileIf = (hs->dwFeatures & CASC_FEATURE_ONLINE) ? true : false;
+ nError = ERROR_SUCCESS;
+ }
+ else
+ {
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ }
}
-#ifdef CASCLIB_TEST
- if(nError == ERROR_SUCCESS && ppCascFile[0] != NULL)
- {
- ppCascFile[0]->FileSize_IdxEntry = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE);
- }
-#endif
+ // Give the output parameter, no matter what
+ PtrFileHandle[0] = (HANDLE)hf;
+ // Handle last error
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return (nError == ERROR_SUCCESS);
}
-static bool OpenFileByEncodingKey(TCascStorage * hs, PQUERY_KEY pEncodingKey, DWORD dwFlags, TCascFile ** ppCascFile)
-{
- PCASC_ENCODING_ENTRY pEncodingEntry;
- QUERY_KEY IndexKey;
-
- // Find the encoding entry
- pEncodingEntry = FindEncodingEntry(hs, pEncodingKey, NULL);
- if(pEncodingEntry == NULL)
- {
- SetLastError(ERROR_FILE_NOT_FOUND);
- return false;
- }
-
- // Prepare the file index and open the file by index
- // Note: We don't know what to do if there is more than just one index key
- // We always take the first file present. Is that correct?
- IndexKey.pbData = GET_INDEX_KEY(pEncodingEntry);
- IndexKey.cbData = MD5_HASH_SIZE;
- if(OpenFileByIndexKey(hs, &IndexKey, dwFlags, ppCascFile))
- {
- // Check if the file handle was created
- if(ppCascFile[0] != NULL)
- {
- // Fill-in the file size. For all files except ENCODING,
- // this overrides the value stored in the index entry.
- ppCascFile[0]->FileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE);
-
-#ifdef CASCLIB_TEST
- ppCascFile[0]->FileSize_EncEntry = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE);
-#endif
- return true;
- }
- }
-
- return false;
-}
-
//-----------------------------------------------------------------------------
// Public functions
-bool WINAPI CascOpenFileByIndexKey(HANDLE hStorage, PQUERY_KEY pIndexKey, DWORD dwFlags, HANDLE * phFile)
-{
- TCascStorage * hs;
-
- // Validate the storage handle
- hs = IsValidStorageHandle(hStorage);
- if(hs == NULL)
- {
- SetLastError(ERROR_INVALID_HANDLE);
- return false;
- }
-
- // Validate the other parameters
- if(pIndexKey == NULL || pIndexKey->pbData == NULL || pIndexKey->cbData == 0 || phFile == NULL)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- return false;
- }
-
- // Use the internal function to open the file
- return OpenFileByIndexKey(hs, pIndexKey, dwFlags, (TCascFile **)phFile);
-}
-
-bool WINAPI CascOpenFileByEncodingKey(HANDLE hStorage, PQUERY_KEY pEncodingKey, DWORD dwFlags, HANDLE * phFile)
+bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocaleFlags, DWORD dwOpenFlags, HANDLE * PtrFileHandle)
{
+ PCASC_CKEY_ENTRY pCKeyEntry = NULL;
TCascStorage * hs;
-
- // Validate the storage handle
- hs = IsValidStorageHandle(hStorage);
- if(hs == NULL)
- {
- SetLastError(ERROR_INVALID_HANDLE);
- return false;
- }
-
- // Validate the other parameters
- if(pEncodingKey == NULL || pEncodingKey->pbData == NULL || pEncodingKey->cbData == 0 || phFile == NULL)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- return false;
- }
-
- // Use the internal function fo open the file
- return OpenFileByEncodingKey(hs, pEncodingKey, dwFlags, (TCascFile **)phFile);
-}
-
-bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocale, DWORD dwFlags, HANDLE * phFile)
-{
- TCascStorage * hs;
- QUERY_KEY EncodingKey;
- LPBYTE pbEncodingKey;
- BYTE KeyBuffer[MD5_HASH_SIZE];
+ const char * szFileName;
+ DWORD FileDataId = CASC_INVALID_ID;
+ BYTE CKeyEKeyBuffer[MD5_HASH_SIZE];
int nError = ERROR_SUCCESS;
- CASCLIB_UNUSED(dwLocale);
+ // This parameter is not used
+ CASCLIB_UNUSED(dwLocaleFlags);
// Validate the storage handle
- hs = IsValidStorageHandle(hStorage);
+ hs = TCascStorage::IsValid(hStorage);
if(hs == NULL)
{
SetLastError(ERROR_INVALID_HANDLE);
@@ -212,107 +79,104 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocal
}
// Validate the other parameters
- if(szFileName == NULL || szFileName[0] == 0 || phFile == NULL)
+ if(PtrFileHandle == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
- // If the user is opening the file via encoding key, skip the ROOT file processing
- if((dwFlags & CASC_OPEN_BY_ENCODING_KEY) == 0)
- {
- // Let the root directory provider get us the encoding key
- pbEncodingKey = RootHandler_GetKey(hs->pRootHandler, szFileName);
- if(pbEncodingKey == NULL)
- {
+ // Retrieve the CKey/EKey from the file name in different modes
+ switch(dwOpenFlags & CASC_OPEN_TYPE_MASK)
+ {
+ case CASC_OPEN_BY_NAME:
+
+ // The 'pvFileName' must be zero terminated ANSI file name
+ szFileName = (const char *)pvFileName;
+ if(szFileName == NULL || szFileName[0] == 0)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ // The first chance: Try to find the file by name (using the root handler)
+ pCKeyEntry = hs->pRootHandler->GetFile(hs, szFileName);
+ if(pCKeyEntry != NULL)
+ break;
+
+ // Second chance: If the file name is actually a file data id, we convert it to file data ID
+ if(IsFileDataIdName(szFileName, FileDataId))
+ {
+ pCKeyEntry = hs->pRootHandler->GetFile(hs, FileDataId);
+ if(pCKeyEntry != NULL)
+ break;
+ }
+
+ // Third chance: If the file name is a string representation of CKey/EKey, we try to query for CKey
+ if(IsFileCKeyEKeyName(szFileName, CKeyEKeyBuffer))
+ {
+ pCKeyEntry = FindCKeyEntry_CKey(hs, CKeyEKeyBuffer);
+ if(pCKeyEntry != NULL)
+ break;
+
+ pCKeyEntry = FindCKeyEntry_EKey(hs, CKeyEKeyBuffer);
+ if(pCKeyEntry != NULL)
+ break;
+ }
+
SetLastError(ERROR_FILE_NOT_FOUND);
return false;
- }
- // Setup the encoding key
- EncodingKey.pbData = pbEncodingKey;
- EncodingKey.cbData = MD5_HASH_SIZE;
- }
- else
- {
- // Check the length of the file name
- if(strlen(szFileName) < MD5_STRING_SIZE)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- return false;
- }
+ case CASC_OPEN_BY_CKEY:
- // Convert the file name to binary blob
- EncodingKey.pbData = KeyBuffer;
- EncodingKey.cbData = MD5_HASH_SIZE;
- nError = ConvertStringToBinary(szFileName, MD5_STRING_SIZE, KeyBuffer);
- }
+ // The 'pvFileName' must be a pointer to 16-byte CKey or EKey
+ if(pvFileName == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
- // Use the encoding key to find the file in the encoding table entry
- if(nError == ERROR_SUCCESS)
- {
- if(!OpenFileByEncodingKey(hs, &EncodingKey, dwFlags, (TCascFile **)phFile))
- {
- assert(GetLastError() != ERROR_SUCCESS);
- nError = GetLastError();
- }
- }
+ // Search the CKey map in order to find the CKey entry
+ pCKeyEntry = FindCKeyEntry_CKey(hs, (LPBYTE)pvFileName);
+ break;
-#ifdef CASCLIB_TEST
-// if(phFile[0] != NULL && pRootEntryMndx != NULL)
-// {
-// ((TCascFile *)(phFile[0]))->FileSize_RootEntry = pRootEntryMndx->FileSize;
-// }
-#endif
+ case CASC_OPEN_BY_EKEY:
- if(nError != ERROR_SUCCESS)
- SetLastError(nError);
- return (nError == ERROR_SUCCESS);
-}
+ // The 'pvFileName' must be a pointer to 16-byte CKey or EKey
+ if(pvFileName == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
-DWORD WINAPI CascGetFileId(HANDLE hStorage, const char * szFileName)
-{
- TCascStorage * hs;
+ // Search the CKey map in order to find the CKey entry
+ pCKeyEntry = FindCKeyEntry_EKey(hs, (LPBYTE)pvFileName);
+ break;
- // Validate the storage handle
- hs = IsValidStorageHandle(hStorage);
- if (hs == NULL)
- {
- SetLastError(ERROR_INVALID_HANDLE);
- return false;
- }
+ case CASC_OPEN_BY_FILEID:
- // Validate the other parameters
- if (szFileName == NULL || szFileName[0] == 0)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- return false;
+ // Retrieve the file CKey/EKey
+ pCKeyEntry = hs->pRootHandler->GetFile(hs, CASC_FILE_DATA_ID_FROM_STRING(pvFileName));
+ break;
+
+ default:
+
+ // Unknown open mode
+ nError = ERROR_INVALID_PARAMETER;
+ break;
}
- return RootHandler_GetFileId(hs->pRootHandler, szFileName);
+ // Perform the open operation
+ return OpenFileByCKeyEntry(hs, pCKeyEntry, dwOpenFlags, PtrFileHandle);
}
bool WINAPI CascCloseFile(HANDLE hFile)
{
TCascFile * hf;
- hf = IsValidFileHandle(hFile);
- if(hf != NULL)
+ hf = TCascFile::IsValid(hFile);
+ if (hf != NULL)
{
- // Close (dereference) the archive handle
- if(hf->hs != NULL)
- CascCloseStorage((HANDLE)hf->hs);
- hf->hs = NULL;
-
- // Free the file cache and frame array
- if(hf->pbFileCache != NULL)
- CASC_FREE(hf->pbFileCache);
- if(hf->pFrames != NULL)
- CASC_FREE(hf->pFrames);
-
- // Free the structure itself
- hf->szClassName = NULL;
- CASC_FREE(hf);
+ delete hf;
return true;
}
diff --git a/dep/CascLib/src/CascOpenStorage.cpp b/dep/CascLib/src/CascOpenStorage.cpp
index ec277f67c13..53031169cbb 100644
--- a/dep/CascLib/src/CascOpenStorage.cpp
+++ b/dep/CascLib/src/CascOpenStorage.cpp
@@ -15,1101 +15,1221 @@
#include "CascCommon.h"
//-----------------------------------------------------------------------------
-// Local structures
+// Local defines
-// Size of one segment in the ENCODING table
-// The segment is filled by entries of type
-#define CASC_ENCODING_SEGMENT_SIZE 0x1000
+// Limit for "orphaned" items - those that are in index files, but are not in ENCODING manifest
+#define CASC_MAX_ORPHANED_ITEMS 0x100
-typedef struct _BLOCK_SIZE_AND_HASH
-{
- DWORD cbBlockSize;
- DWORD dwBlockHash;
+//-----------------------------------------------------------------------------
+// Local variables
-} BLOCK_SIZE_AND_HASH, *PBLOCK_SIZE_AND_HASH;
+static PFNPROGRESSCALLBACK PfnProgressCallback = NULL;
+static void * PtrProgressParam = NULL;
-typedef struct _FILE_INDEX_HEADER_V1
-{
- USHORT field_0;
- BYTE KeyIndex; // Key index (0 for data.i0x, 1 for data.i1x, 2 for data.i2x etc.)
- BYTE align_3;
- DWORD field_4;
- ULONGLONG field_8;
- ULONGLONG MaxFileOffset;
- BYTE SpanSizeBytes;
- BYTE SpanOffsBytes;
- BYTE KeyBytes;
- BYTE SegmentBits; // Number of bits for file offset
- DWORD KeyCount1;
- DWORD KeyCount2;
- DWORD KeysHash1;
- DWORD KeysHash2;
- DWORD dwHeaderHash;
-} FILE_INDEX_HEADER_V1, *PFILE_INDEX_HEADER_V1;
-
-typedef struct _FILE_INDEX_HEADER_V2
-{
- USHORT IndexVersion; // Must be 0x07
- BYTE KeyIndex; // Must be equal to the file key index
- BYTE ExtraBytes; // (?) Extra bytes in the key record
- BYTE SpanSizeBytes; // Size of field with file size
- BYTE SpanOffsBytes; // Size of field with file offset
- BYTE KeyBytes; // Size of the file key (bytes)
- BYTE SegmentBits; // Number of bits for the file offset (rest is archive index)
- ULONGLONG MaxFileOffset;
-
-} FILE_INDEX_HEADER_V2, *PFILE_INDEX_HEADER_V2;
-
-typedef struct _FILE_ENCODING_SEGMENT
+//-----------------------------------------------------------------------------
+// TCascStorage service functions
+
+TCascStorage::TCascStorage()
{
- BYTE FirstEncodingKey[MD5_HASH_SIZE]; // The first encoding key in the segment
- BYTE SegmentHash[MD5_HASH_SIZE]; // MD5 hash of the entire segment
+ // Prepare the base storage parameters
+ szClassName = "TCascStorage";
+ pRootHandler = NULL;
+ dwDefaultLocale = CASC_LOCALE_ENUS | CASC_LOCALE_ENGB;
+ dwRefCount = 1;
+
+ szRootPath = szDataPath = szIndexPath = szBuildFile = szCdnServers = szCdnPath = szCodeName = NULL;
+ szProductName = NULL;
+ szIndexFormat = NULL;
+ szRegion = NULL;
+
+ memset(DataFiles, 0, sizeof(DataFiles));
+ Product = UnknownProduct;
+ dwBuildNumber = 0;
+ dwFeatures = 0;
+ bAllowOrphans = false;
+ BuildFileType = CascBuildNone;
+
+ LocalFiles = TotalFiles = EKeyEntries = OrphanItems = SkippedItems = EKeyLength = FileOffsetBits = 0;
+
+ // Take the callback param and data. Zero the global pointers
+ PfnCallback = PfnProgressCallback;
+ PtrCallbackParam = PtrProgressParam;
+ PfnProgressCallback = NULL;
+ PtrProgressParam = NULL;
+}
-} FILE_ENCODING_SEGMENT, *PFILE_ENCODING_SEGMENT;
+TCascStorage::~TCascStorage()
+{
+ // Free the root handler
+ if(pRootHandler != NULL)
+ delete pRootHandler;
+ pRootHandler = NULL;
-//-----------------------------------------------------------------------------
-// Local variables
+ // Close all data files
+ for(size_t i = 0; i < CASC_MAX_DATA_FILES; i++)
+ {
+ FileStream_Close(DataFiles[i]);
+ DataFiles[i] = NULL;
+ }
-static const TCHAR * szAllowedHexChars = _T("0123456789aAbBcCdDeEfF");
-static const TCHAR * szIndexFormat_V1 = _T("data.i%x%x");
-static const TCHAR * szIndexFormat_V2 = _T("%02x%08x.idx");
+ // Free the file paths
+ CASC_FREE(szDataPath);
+ CASC_FREE(szRootPath);
+ CASC_FREE(szBuildFile);
+ CASC_FREE(szIndexPath);
+ CASC_FREE(szCdnServers);
+ CASC_FREE(szCdnPath);
+ CASC_FREE(szCodeName);
+ CASC_FREE(szRegion);
+
+ // Free the blobs
+ FreeCascBlob(&CdnConfigKey);
+ FreeCascBlob(&CdnBuildKey);
+
+ FreeCascBlob(&ArchiveGroup);
+ FreeCascBlob(&ArchivesKey);
+ FreeCascBlob(&PatchArchivesKey);
+ FreeCascBlob(&PatchArchivesGroup);
+ FreeCascBlob(&BuildFiles);
+ szClassName = NULL;
+}
-//-----------------------------------------------------------------------------
-// Local functions
+TCascStorage * TCascStorage::AddRef()
+{
+ dwRefCount++;
+ return this;
+}
-inline void CopyFileKey(LPBYTE Trg, LPBYTE Src)
+TCascStorage * TCascStorage::Release()
{
- Trg[0x00] = Src[0x00];
- Trg[0x01] = Src[0x01];
- Trg[0x02] = Src[0x02];
- Trg[0x03] = Src[0x03];
- Trg[0x04] = Src[0x04];
- Trg[0x05] = Src[0x05];
- Trg[0x06] = Src[0x06];
- Trg[0x07] = Src[0x07];
- Trg[0x08] = Src[0x08];
- Trg[0x09] = Src[0x09];
- Trg[0x0A] = Src[0x0A];
- Trg[0x0B] = Src[0x0B];
- Trg[0x0C] = Src[0x0C];
- Trg[0x0D] = Src[0x0D];
- Trg[0x0E] = Src[0x0E];
- Trg[0x0F] = Src[0x0F];
+ if (dwRefCount == 1)
+ {
+ delete this;
+ return NULL;
+ }
+
+ dwRefCount--;
+ return NULL;
}
-TCascStorage * IsValidStorageHandle(HANDLE hStorage)
+TCascStorage * TCascStorage::IsValid(HANDLE hStorage)
{
TCascStorage * hs = (TCascStorage *)hStorage;
return (hs != NULL && hs->szClassName != NULL && !strcmp(hs->szClassName, "TCascStorage")) ? hs : NULL;
}
-// "data.iXY"
-static bool IsIndexFileName_V1(const TCHAR * szFileName)
-{
- // Check if the name looks like a valid index file
- return (_tcslen(szFileName) == 8 &&
- _tcsnicmp(szFileName, _T("data.i"), 6) == 0 &&
- _tcsspn(szFileName + 6, szAllowedHexChars) == 2);
-}
+//-----------------------------------------------------------------------------
+// Local functions
-static bool IsIndexFileName_V2(const TCHAR * szFileName)
+void * ProbeOutputBuffer(void * pvBuffer, size_t cbLength, size_t cbMinLength, size_t * pcbLengthNeeded)
{
- // Check if the name looks like a valid index file
- return (_tcslen(szFileName) == 14 &&
- _tcsspn(szFileName, _T("0123456789aAbBcCdDeEfF")) == 0x0A &&
- _tcsicmp(szFileName + 0x0A, _T(".idx")) == 0);
+ // Verify the output length
+ if(cbLength < cbMinLength)
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ pvBuffer = NULL;
+ }
+
+ // Give the output length and return result
+ if(pcbLengthNeeded != NULL)
+ pcbLengthNeeded[0] = cbMinLength;
+ return pvBuffer;
}
-static bool IsRootFile_Starcraft1(LPBYTE pbFileData, DWORD cbFileData)
+static TCHAR * CheckForIndexDirectory(TCascStorage * hs, const TCHAR * szSubDir)
{
- LPBYTE pbFileEnd = pbFileData + cbFileData;
- LPBYTE pbHashName;
- LPBYTE pbHashEnd;
-
- // Skip the file name
- while(pbFileData < pbFileEnd && pbFileData[0] != '|')
- pbFileData++;
- if(pbFileData[0] != '|')
- return false;
+ TCHAR * szIndexPath;
- // Then, a MD5 must follow
- pbHashName = pbHashEnd = pbFileData + 1;
- while(pbHashEnd < pbFileEnd && pbHashEnd[0] != 0x0A)
- pbHashEnd++;
- if(pbHashEnd[0] != 0x0A)
- return false;
+ // Combine the index path
+ szIndexPath = CombinePath(hs->szDataPath, szSubDir);
+ if (!DirectoryExists(szIndexPath))
+ {
+ CASC_FREE(szIndexPath);
+ }
- // The length must be exactly 32 characters
- return ((pbHashEnd - pbHashName) == 32);
+ return szIndexPath;
}
-static bool IsCascIndexHeader_V1(LPBYTE pbFileData, DWORD cbFileData)
+static int CreateCKeyMaps(TCascStorage * hs, CASC_ENCODING_HEADER & EnHeader)
{
- PFILE_INDEX_HEADER_V1 pIndexHeader = (PFILE_INDEX_HEADER_V1)pbFileData;
- DWORD dwHeaderHash;
- bool bResult = false;
+ size_t nEstimatedEntries = (EnHeader.CKeyPageCount * EnHeader.CKeyPageSize) / sizeof(FILE_CKEY_ENTRY);
+ size_t nIxEntries = hs->IndexArray.ItemCount();
+ int nError;
- // Check the size
- if(cbFileData >= sizeof(FILE_INDEX_HEADER_V1))
+ // Orphaned items: These are present in INDEX files (by EKey), but missing in the ENCODING manifest.
+ // Probably a bug in generator of "2018 - New CASC\00001", but we want to open the storage nontheless.
+ if(nEstimatedEntries < 0x100)
{
- // Save the header hash
- dwHeaderHash = pIndexHeader->dwHeaderHash;
- pIndexHeader->dwHeaderHash = 0;
+ nEstimatedEntries = nEstimatedEntries + nIxEntries;
+ hs->bAllowOrphans = true;
+ }
- // Calculate the hash
- if(hashlittle(pIndexHeader, sizeof(FILE_INDEX_HEADER_V1), 0) == dwHeaderHash)
- bResult = true;
+ // Allow some room for extra entries
+ nEstimatedEntries += CASC_MAX_ORPHANED_ITEMS;
- // Put the hash back
- pIndexHeader->dwHeaderHash = dwHeaderHash;
- }
+ // Create the array of CKey items
+ nError = hs->CKeyArray.Create(sizeof(CASC_CKEY_ENTRY), nEstimatedEntries);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // Create the map CKey -> CASC_CKEY_ENTRY
+ nError = hs->CKeyMap.Create(nEstimatedEntries, EnHeader.CKeyLength, FIELD_OFFSET(CASC_CKEY_ENTRY, CKey));
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // Create the map EKey -> CASC_CKEY_ENTRY
+ nError = hs->EKeyMap.Create(nEstimatedEntries, hs->EKeyLength, FIELD_OFFSET(CASC_CKEY_ENTRY, EKey));
+ if(nError != ERROR_SUCCESS)
+ return nError;
- return bResult;
+ return ERROR_SUCCESS;
}
-static bool IsCascIndexHeader_V2(LPBYTE pbFileData, DWORD cbFileData)
+int CaptureEncodingHeader(CASC_ENCODING_HEADER & EnHeader, LPBYTE pbFileData, size_t cbFileData)
{
- PBLOCK_SIZE_AND_HASH pSizeAndHash = (PBLOCK_SIZE_AND_HASH)pbFileData;
- unsigned int HashHigh = 0;
- unsigned int HashLow = 0;
+ PFILE_ENCODING_HEADER pFileHeader = (PFILE_ENCODING_HEADER)pbFileData;
- // Check for the header
- if(cbFileData < sizeof(BLOCK_SIZE_AND_HASH) || pSizeAndHash->cbBlockSize < 0x10)
- return false;
- if(cbFileData < pSizeAndHash->cbBlockSize + sizeof(BLOCK_SIZE_AND_HASH))
- return false;
+ // Check the signature ('EN') and version
+ if(cbFileData < sizeof(FILE_ENCODING_HEADER) || pFileHeader->Magic != FILE_MAGIC_ENCODING || pFileHeader->Version != 0x01)
+ return ERROR_BAD_FORMAT;
- // The index header for CASC v 2.0 begins with length and checksum
- hashlittle2(pSizeAndHash + 1, pSizeAndHash->cbBlockSize, &HashHigh, &HashLow);
- return (HashHigh == pSizeAndHash->dwBlockHash);
+ // Note that we don't support CKey and EKey sizes other than 0x10 in the ENCODING file
+ if(pFileHeader->CKeyLength != MD5_HASH_SIZE || pFileHeader->EKeyLength != MD5_HASH_SIZE)
+ return ERROR_BAD_FORMAT;
+
+ EnHeader.Magic = pFileHeader->Magic;
+ EnHeader.Version = pFileHeader->Version;
+ EnHeader.CKeyLength = pFileHeader->CKeyLength;
+ EnHeader.EKeyLength = pFileHeader->EKeyLength;
+ EnHeader.CKeyPageCount = ConvertBytesToInteger_4(pFileHeader->CKeyPageCount);
+ EnHeader.CKeyPageSize = ConvertBytesToInteger_2(pFileHeader->CKeyPageSize) * 1024;
+ EnHeader.EKeyPageCount = ConvertBytesToInteger_4(pFileHeader->EKeyPageCount);
+ EnHeader.EKeyPageSize = ConvertBytesToInteger_2(pFileHeader->EKeyPageSize) * 1024;
+ EnHeader.ESpecBlockSize = ConvertBytesToInteger_4(pFileHeader->ESpecBlockSize);
+ return ERROR_SUCCESS;
}
-static bool CutLastPathPart(TCHAR * szWorkPath)
+static int LoadEncodingCKeyPage(TCascStorage * hs, CASC_ENCODING_HEADER & EnHeader, LPBYTE pbPageBegin, LPBYTE pbEndOfPage)
{
- size_t nLength = _tcslen(szWorkPath);
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ PFILE_CKEY_ENTRY pFileEntry;
+ LPBYTE pbFileEntry = pbPageBegin;
+
+ // Sanity checks
+ assert(hs->CKeyMap.IsInitialized());
+ assert(hs->EKeyMap.IsInitialized());
+
+ // Parse all encoding entries
+ while(pbFileEntry < pbEndOfPage)
+ {
+ // Get pointer to the encoding entry
+ pFileEntry = (PFILE_CKEY_ENTRY)pbFileEntry;
+ if(pFileEntry->EKeyCount == 0)
+ break;
+
+ // Example of a file entry with multiple EKeys:
+ // Overwatch build 24919, CKey: 0e 90 94 fa d2 cb 85 ac d0 7c ea 09 f9 c5 ba 00
+// BREAKIF(pFileEntry->EKeyCount > 1);
- // Go one character back
- if(nLength > 0)
- nLength--;
+ // Insert the CKey entry into the array
+ pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1);
+ if(pCKeyEntry != NULL)
+ {
+ // Supply both CKey and EKey. Rewrite EKey regardless, because ENCODING manifest contains a full one
+ CopyMemory16(pCKeyEntry->CKey, pFileEntry->CKey);
+ CopyMemory16(pCKeyEntry->EKey, pFileEntry->EKey);
+ pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64;
+ pCKeyEntry->TagBitMask = 0;
+ pCKeyEntry->EncodedSize = CASC_INVALID_SIZE;
+ pCKeyEntry->ContentSize = ConvertBytesToInteger_4(pFileEntry->ContentSize);
+ pCKeyEntry->RefCount = 0;
+ pCKeyEntry->Priority = 0;
+ pCKeyEntry->Flags = (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_IN_ENCODING);
+
+ // Insert the item into both maps
+ hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
+ hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
+ }
- // Cut ending (back)slashes, if any
- while(nLength > 0 && (szWorkPath[nLength] == _T('\\') || szWorkPath[nLength] == _T('/')))
- nLength--;
+ // Move to the next encoding entry
+ pbFileEntry = pbFileEntry + 2 + 4 + EnHeader.CKeyLength + (pFileEntry->EKeyCount * EnHeader.EKeyLength);
+ }
+ return ERROR_SUCCESS;
+}
+
+static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pSourceEntry, bool bAllowOrphans, bool * pbAllocatedNewEntry)
+{
+ PCASC_CKEY_ENTRY pCKeyEntry = NULL;
+ bool bAllocatedNewEntry = false;
- // Cut the last path part
- while(nLength > 0)
+ if(pSourceEntry->Flags & CASC_CE_HAS_EKEY)
{
- // End of path?
- if(szWorkPath[nLength] == _T('\\') || szWorkPath[nLength] == _T('/'))
+ // If there is that item already, reuse it
+ pCKeyEntry = FindCKeyEntry_EKey(hs, pSourceEntry->EKey);
+ if(pCKeyEntry == NULL)
{
- szWorkPath[nLength] = 0;
- return true;
+ // Increment number of orphaned index entries
+ hs->OrphanItems++;
+
+ // Insert the orphan item only of they are allowed and if we won't overflow the array
+ if(bAllowOrphans && (hs->CKeyArray.ItemCount() + 1) < hs->CKeyArray.ItemCountMax())
+ {
+ // Insert a new entry to the array
+ pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1);
+ if(pCKeyEntry != NULL)
+ {
+ // Copy CKey, EKey and some flags
+ if(pSourceEntry->Flags & CASC_CE_HAS_CKEY)
+ CopyMemory16(pCKeyEntry->CKey, pSourceEntry->CKey);
+
+ if(pSourceEntry->Flags & CASC_CE_HAS_EKEY)
+ CopyMemory16(pCKeyEntry->EKey, pSourceEntry->EKey);
+
+ pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64;
+ pCKeyEntry->TagBitMask = 0;
+ pCKeyEntry->RefCount = 0;
+ pCKeyEntry->Priority = 0;
+
+ pCKeyEntry->EncodedSize = CASC_INVALID_SIZE;
+ pCKeyEntry->ContentSize = CASC_INVALID_SIZE;
+ pCKeyEntry->Flags = (pSourceEntry->Flags & (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_HAS_EKEY_PARTIAL));
+ bAllocatedNewEntry = true;
+ }
+ }
+ else
+ {
+ hs->SkippedItems++;
+ }
}
+ }
+
+ if(pbAllocatedNewEntry != NULL)
+ pbAllocatedNewEntry[0] = bAllocatedNewEntry;
+ return pCKeyEntry;
+}
+
+static PCASC_CKEY_ENTRY CopyBuildFileItemToCKeyArray(TCascStorage * hs, PCASC_CKEY_ENTRY pSourceEntry)
+{
+ PCASC_CKEY_ENTRY pCKeyEntry = NULL;
+ bool bAllocatedNewEntry = false;
- // Go one character back
- nLength--;
+ pCKeyEntry = InsertCKeyEntry(hs, pSourceEntry, true, &bAllocatedNewEntry);
+ if(pCKeyEntry != NULL)
+ {
+ // Fill the values that might be known
+ if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE)
+ pCKeyEntry->EncodedSize = pSourceEntry->EncodedSize;
+ if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
+ pCKeyEntry->ContentSize = pSourceEntry->ContentSize;
+
+ // If this is a new entry, we need to insert it to the maps
+ if(bAllocatedNewEntry)
+ {
+ if(pCKeyEntry->Flags & CASC_CE_HAS_CKEY)
+ hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
+ if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY)
+ hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
+ }
}
- return false;
+ return pCKeyEntry;
}
-static int InsertExtraFile(
- TCascStorage * hs,
- const char * szFileName,
- PQUERY_KEY pQueryKey)
+static int CopyBuildFileItemsToCKeyArray(TCascStorage * hs)
{
- // If the given key is not encoding key (aka, it's an index key),
- // we need to create a fake encoding entry
- if(pQueryKey->cbData == MD5_HASH_SIZE * 2)
+ // Insert the well-known files
+ CopyBuildFileItemToCKeyArray(hs, &hs->EncodingCKey);
+ CopyBuildFileItemToCKeyArray(hs, &hs->DownloadCKey);
+ CopyBuildFileItemToCKeyArray(hs, &hs->InstallCKey);
+ CopyBuildFileItemToCKeyArray(hs, &hs->PatchFile);
+ CopyBuildFileItemToCKeyArray(hs, &hs->RootFile);
+ CopyBuildFileItemToCKeyArray(hs, &hs->SizeFile);
+ CopyBuildFileItemToCKeyArray(hs, &hs->VfsRoot);
+
+ // Insert all VFS roots
+ for(size_t i = 0; i < hs->VfsRootList.ItemCount(); i++)
{
- PCASC_ENCODING_ENTRY pNewEntry;
- PCASC_INDEX_ENTRY pIndexEntry;
- QUERY_KEY IndexKey;
-
- // Find the entry in the index table in order to get the file size
- IndexKey.pbData = pQueryKey->pbData + MD5_HASH_SIZE;
- IndexKey.cbData = MD5_HASH_SIZE;
- pIndexEntry = FindIndexEntry(hs, &IndexKey);
- if(pIndexEntry == NULL)
- return ERROR_FILE_NOT_FOUND;
-
- // Create a fake entry in the encoding map
- pNewEntry = (PCASC_ENCODING_ENTRY)Array_Insert(&hs->ExtraEntries, NULL, 1);
- if(pNewEntry == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Fill the encoding entry
- pNewEntry->KeyCount = 1;
- pNewEntry->FileSizeBE[0] = pIndexEntry->FileSizeLE[3];
- pNewEntry->FileSizeBE[1] = pIndexEntry->FileSizeLE[2];
- pNewEntry->FileSizeBE[2] = pIndexEntry->FileSizeLE[1];
- pNewEntry->FileSizeBE[3] = pIndexEntry->FileSizeLE[0];
- memcpy(pNewEntry->EncodingKey, pQueryKey->pbData, MD5_HASH_SIZE);
- memcpy(pNewEntry + 1, pQueryKey->pbData + MD5_HASH_SIZE, MD5_HASH_SIZE);
-
- // Insert the entry to the map of encoding keys
- Map_InsertObject(hs->pEncodingMap, pNewEntry, pNewEntry->EncodingKey);
+ PCASC_CKEY_ENTRY pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i);
+ CopyBuildFileItemToCKeyArray(hs, pCKeyEntry);
}
- // Now we need to insert the entry to the root handler in order
- // to be able to translate file name to encoding key
- return RootHandler_Insert(hs->pRootHandler, szFileName, pQueryKey->pbData);
+ return ERROR_SUCCESS;
}
-static int InitializeCascDirectories(TCascStorage * hs, const TCHAR * szDataPath)
+static int CopyIndexItemsToCKeyArray(TCascStorage * hs)
{
- TCHAR * szWorkPath;
- int nError = ERROR_NOT_ENOUGH_MEMORY;
+ PCASC_CKEY_ENTRY pIndexEntry;
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ size_t nItemCount = hs->IndexArray.ItemCount();
+ bool bAllocatedNewEntry = false;
- // Find the root directory of the storage. The root directory
- // is the one where ".build.info" is.
- szWorkPath = CascNewStr(szDataPath, 0);
- if(szWorkPath != NULL)
+ // Iterate over all index items
+ for(size_t i = 0; i < nItemCount; i++)
{
- // Get the length and go up until we find the ".build.info" or ".build.db"
- for(;;)
+ // Get the n-th index entry
+ pIndexEntry = (PCASC_CKEY_ENTRY)hs->IndexArray.ItemAt(i);
+
+ // Sometimes, there are multiple items with the same EKey in the index files
+ // Example: "2018 - New CASC\00001", EKey 37 89 16 5b 2d cc 71 c1 25 00 00 00 00 00 00 00
+ // Positions: 0x2D, 0x2E, 0x2F
+ //BREAK_ON_XKEY3(pIndexEntry->EKey, 0x37, 0x89, 0x16);
+
+ // Copy the index entry to the central storage
+ if((pCKeyEntry = InsertCKeyEntry(hs, pIndexEntry, hs->bAllowOrphans, &bAllocatedNewEntry)) != NULL)
{
- // Is this a game directory?
- nError = CheckGameDirectory(hs, szWorkPath);
- if(nError == ERROR_SUCCESS)
+ // Make sure that the CKey is zeroed when not present
+ if((pCKeyEntry->Flags & CASC_CE_HAS_CKEY) == 0)
+ ZeroMemory16(pCKeyEntry->CKey);
+
+ // Only copy the storage offset and sizes if not available yet
+ if(pCKeyEntry->StorageOffset == CASC_INVALID_OFFS64)
{
- nError = ERROR_SUCCESS;
- break;
+ pCKeyEntry->StorageOffset = pIndexEntry->StorageOffset;
+ pCKeyEntry->EncodedSize = pIndexEntry->EncodedSize;
}
- // Cut one path part
- if(!CutLastPathPart(szWorkPath))
+ if(bAllocatedNewEntry)
{
- nError = ERROR_FILE_NOT_FOUND;
- break;
+ if(pCKeyEntry->Flags & CASC_CE_HAS_CKEY)
+ hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
+ if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY)
+ hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
}
- }
- // Free the work path buffer
- CASC_FREE(szWorkPath);
+ // Mark the file as available locally
+ pCKeyEntry->Flags |= CASC_CE_FILE_IS_LOCAL;
+ }
}
- return nError;
+ // We free the index array at this point
+ hs->IndexArray.Free();
+ return ERROR_SUCCESS;
}
-static bool IndexDirectory_OnFileFound(
- const TCHAR * szFileName,
- PDWORD IndexArray,
- PDWORD OldIndexArray,
- void * pvContext)
+static int LoadEncodingManifest(TCascStorage * hs)
{
- TCascStorage * hs = (TCascStorage *)pvContext;
- DWORD IndexValue = 0;
- DWORD IndexVersion = 0;
+ LPBYTE pbEncodingFile;
+ DWORD cbEncodingFile = 0;
+ int nError = ERROR_SUCCESS;
- // Auto-detect the format of the index file name
- if(hs->szIndexFormat == NULL)
- {
- if(IsIndexFileName_V1(szFileName))
- hs->szIndexFormat = szIndexFormat_V1;
- else if(IsIndexFileName_V2(szFileName))
- hs->szIndexFormat = szIndexFormat_V2;
- else
- return false;
- }
+ // Inform the user about what we are doing
+ if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Loading ENCODING manifest", NULL, 0, 0))
+ return ERROR_CANCELLED;
- if(hs->szIndexFormat == szIndexFormat_V1)
+ // Load the entire encoding file to memory
+ pbEncodingFile = LoadInternalFileToMemory(hs, &hs->EncodingCKey, &cbEncodingFile);
+ if(pbEncodingFile != NULL && cbEncodingFile != 0)
{
- // Check the index file name format
- if(!IsIndexFileName_V1(szFileName))
- return false;
+ CASC_ENCODING_HEADER EnHeader;
- // Get the main index from the first two digits
- if(ConvertDigitToInt32(szFileName + 6, &IndexValue) != ERROR_SUCCESS)
- return false;
- if(ConvertDigitToInt32(szFileName + 7, &IndexVersion) != ERROR_SUCCESS)
- return false;
- }
-
- else if(hs->szIndexFormat == szIndexFormat_V2)
- {
- // Check the index file name format
- if(!IsIndexFileName_V2(szFileName))
- return false;
+ // Capture the header of the ENCODING file
+ nError = CaptureEncodingHeader(EnHeader, pbEncodingFile, cbEncodingFile);
+ if(nError == ERROR_SUCCESS)
+ {
+ // Get the CKey page header and the first page
+ PFILE_CKEY_PAGE pPageHeader = (PFILE_CKEY_PAGE)(pbEncodingFile + sizeof(FILE_ENCODING_HEADER) + EnHeader.ESpecBlockSize);
+ LPBYTE pbCKeyPage = (LPBYTE)(pPageHeader + EnHeader.CKeyPageCount);
+
+ // Since ENCODING contains the full list of all files (even those not downloaded),
+ // we can now make a fair estimate about how large maps shall we create.
+ // So, we can build the maps CKey and EKey map.
+ if((nError = CreateCKeyMaps(hs, EnHeader)) == ERROR_SUCCESS)
+ {
+ // Go through all CKey pages and verify them
+ for(DWORD i = 0; i < EnHeader.CKeyPageCount; i++)
+ {
+ PFILE_CKEY_ENTRY pCKeyEntry = (PFILE_CKEY_ENTRY)pbCKeyPage;
+
+ // Check if there is enough space in the buffer
+ if((pbCKeyPage + EnHeader.CKeyPageSize) > (pbEncodingFile + cbEncodingFile))
+ {
+ nError = ERROR_FILE_CORRUPT;
+ break;
+ }
+
+ // Check the hash of the entire segment
+ // Note that verifying takes considerable time of the storage loading
+// if(!VerifyDataBlockHash(pbCKeyPage, EnHeader.CKeyPageSize, pEncodingSegment->SegmentHash))
+// {
+// nError = ERROR_FILE_CORRUPT;
+// break;
+// }
+
+ // Check if the CKey matches with the expected first value
+ if(memcmp(pCKeyEntry->CKey, pPageHeader[i].FirstKey, CASC_CKEY_SIZE))
+ {
+ nError = ERROR_FILE_CORRUPT;
+ break;
+ }
+
+ // Load the entire page of CKey entries.
+ // This operation will never fail, because all memory is already pre-allocated
+ nError = LoadEncodingCKeyPage(hs, EnHeader, pbCKeyPage, pbCKeyPage + EnHeader.CKeyPageSize);
+ if(nError != ERROR_SUCCESS)
+ break;
+
+ // Move to the next CKey page
+ pbCKeyPage += EnHeader.CKeyPageSize;
+ }
+ }
+ }
- // Get the main index from the first two digits
- if(ConvertStringToInt32(szFileName, 2, &IndexValue) != ERROR_SUCCESS)
- return false;
- if(ConvertStringToInt32(szFileName + 2, 8, &IndexVersion) != ERROR_SUCCESS)
- return false;
- }
- else
- {
- // Should never happen
- assert(false);
- return false;
- }
+ // All CKey->EKey entries from the text build files need to be copied to the CKey array
+ // This also includes the ENCODING file itself, which is vital for later loading
+ if(nError == ERROR_SUCCESS)
+ {
+ nError = CopyBuildFileItemsToCKeyArray(hs);
+ }
- // The index value must not be greater than 0x0F
- if(IndexValue >= CASC_INDEX_COUNT)
- return false;
+ // Now supply all the entries from the index files
+ if(nError == ERROR_SUCCESS)
+ {
+ nError = CopyIndexItemsToCKeyArray(hs);
+ }
- // If the new subindex is greater than the previous one,
- // use this one instead
- if(IndexVersion > IndexArray[IndexValue])
- {
- OldIndexArray[IndexValue] = IndexArray[IndexValue];
- IndexArray[IndexValue] = IndexVersion;
+ // Free the loaded ENCODING file
+ CASC_FREE(pbEncodingFile);
}
- else if(IndexVersion > OldIndexArray[IndexValue])
+ else
{
- OldIndexArray[IndexValue] = IndexVersion;
+ nError = GetLastError();
}
- // Note: WoW6 only keeps last two index files
- // Any additional index files are deleted at this point
- return true;
+ return nError;
}
-static TCHAR * CreateIndexFileName(TCascStorage * hs, DWORD IndexValue, DWORD IndexVersion)
+size_t GetTagBitmapLength(LPBYTE pbFilePtr, LPBYTE pbFileEnd, DWORD EntryCount)
{
- TCHAR szPlainName[0x40];
-
- // Sanity checks
- assert(hs->szIndexFormat != NULL);
- assert(hs->szIndexPath != NULL);
- assert(IndexValue <= 0x0F);
-
- // Create the full path
- _stprintf(szPlainName, hs->szIndexFormat, IndexValue, IndexVersion);
- return CombinePath(hs->szIndexPath, szPlainName);
-}
+ size_t nBitmapLength;
-static int VerifyAndParseKeyMapping_V1(PCASC_MAPPING_TABLE pKeyMapping, DWORD KeyIndex)
-{
- PFILE_INDEX_HEADER_V1 pIndexHeader = (PFILE_INDEX_HEADER_V1)pKeyMapping->pbFileData;
- DWORD dwDataHash1;
- DWORD dwDataHash2;
-
- // Verify the format
- if(pIndexHeader->field_0 != 0x0005)
- return ERROR_NOT_SUPPORTED;
- if(pIndexHeader->KeyIndex != KeyIndex)
- return ERROR_NOT_SUPPORTED;
- if(pIndexHeader->field_8 == 0)
- return ERROR_NOT_SUPPORTED;
-
- // Verofy the bit counts
- if(pIndexHeader->SpanSizeBytes != 0x04 ||
- pIndexHeader->SpanOffsBytes != 0x05 ||
- pIndexHeader->KeyBytes != 0x09)
- return ERROR_NOT_SUPPORTED;
-
- pKeyMapping->ExtraBytes = 0;
- pKeyMapping->SpanSizeBytes = pIndexHeader->SpanSizeBytes;
- pKeyMapping->SpanOffsBytes = pIndexHeader->SpanOffsBytes;
- pKeyMapping->KeyBytes = pIndexHeader->KeyBytes;
- pKeyMapping->SegmentBits = pIndexHeader->SegmentBits;
- pKeyMapping->MaxFileOffset = pIndexHeader->MaxFileOffset;
-
- // Get the pointer to the key entry array
- pKeyMapping->nIndexEntries = pIndexHeader->KeyCount1 + pIndexHeader->KeyCount2;
- if(pKeyMapping->nIndexEntries != 0)
- pKeyMapping->pIndexEntries = (PCASC_INDEX_ENTRY)(pKeyMapping->pbFileData + sizeof(FILE_INDEX_HEADER_V1));
-
- // Verify hashes
- dwDataHash1 = hashlittle(pKeyMapping->pIndexEntries, pIndexHeader->KeyCount1 * sizeof(CASC_INDEX_ENTRY), 0);
- dwDataHash2 = hashlittle(pKeyMapping->pIndexEntries + pIndexHeader->KeyCount1, pIndexHeader->KeyCount2 * sizeof(CASC_INDEX_ENTRY), 0);
- if(dwDataHash1 != pIndexHeader->KeysHash1 || dwDataHash2 != pIndexHeader->KeysHash2)
- return ERROR_FILE_CORRUPT;
+ nBitmapLength = (EntryCount / 8) + ((EntryCount & 0x07) ? 1 : 0);
+ if ((pbFilePtr + nBitmapLength) > pbFileEnd)
+ nBitmapLength = (pbFileEnd - pbFilePtr);
- return ERROR_SUCCESS;
+ return nBitmapLength;
}
-static int VerifyAndParseKeyMapping_V2(PCASC_MAPPING_TABLE pKeyMapping, DWORD KeyIndex)
+int CaptureDownloadHeader(CASC_DOWNLOAD_HEADER & DlHeader, LPBYTE pbFileData, size_t cbFileData)
{
- PFILE_INDEX_HEADER_V2 pIndexHeader = (PFILE_INDEX_HEADER_V2)pKeyMapping->pbFileData;
- PBLOCK_SIZE_AND_HASH pSizeAndHash;
- LPBYTE pbLastPartEnd;
- LPBYTE pbLastPart;
- DWORD FilePosition;
- DWORD LastPartLength;
- unsigned int HashHigh = 0;
- unsigned int HashLow = 0;
-
- // The index header v2 begins after the SizeAndHash
- pSizeAndHash = (PBLOCK_SIZE_AND_HASH)pKeyMapping->pbFileData;
- pIndexHeader = (PFILE_INDEX_HEADER_V2)(pSizeAndHash + 1);
- if(pIndexHeader->IndexVersion != 0x07 || pIndexHeader->KeyIndex != KeyIndex)
- return ERROR_BAD_FORMAT;
+ PFILE_DOWNLOAD_HEADER pFileHeader = (PFILE_DOWNLOAD_HEADER)pbFileData;
- if(pIndexHeader->ExtraBytes != 0x00 ||
- pIndexHeader->SpanSizeBytes != 0x04 ||
- pIndexHeader->SpanOffsBytes != 0x05 ||
- pIndexHeader->KeyBytes != CASC_FILE_KEY_SIZE)
+ // Check the signature ('DL') and version
+ if(cbFileData < sizeof(FILE_DOWNLOAD_HEADER) || pFileHeader->Magic != FILE_MAGIC_DOWNLOAD || pFileHeader->Version > 3)
return ERROR_BAD_FORMAT;
- // Remember the sizes
- pKeyMapping->ExtraBytes = pIndexHeader->ExtraBytes;
- pKeyMapping->SpanSizeBytes = pIndexHeader->SpanSizeBytes;
- pKeyMapping->SpanOffsBytes = pIndexHeader->SpanOffsBytes;
- pKeyMapping->KeyBytes = pIndexHeader->KeyBytes;
- pKeyMapping->SegmentBits = pIndexHeader->SegmentBits;
- pKeyMapping->MaxFileOffset = pIndexHeader->MaxFileOffset;
-
- // Get the data position
- FilePosition = (sizeof(BLOCK_SIZE_AND_HASH) + pSizeAndHash->cbBlockSize + 0x0F) & 0xFFFFFFF0;
- if((FilePosition + 0x08) > pKeyMapping->cbFileData)
+ // Note that we don't support CKey sizes greater than 0x10 in the DOWNLOAD file
+ if(pFileHeader->EKeyLength > MD5_HASH_SIZE)
return ERROR_BAD_FORMAT;
- // Get the pointer to "size+hash" block
- pSizeAndHash = (PBLOCK_SIZE_AND_HASH)(pKeyMapping->pbFileData + FilePosition);
- FilePosition += 0x08;
+ // Capture the header version 1
+ memset(&DlHeader, 0, sizeof(CASC_DOWNLOAD_HEADER));
+ DlHeader.Magic = pFileHeader->Magic;
+ DlHeader.Version = pFileHeader->Version;
+ DlHeader.EKeyLength = pFileHeader->EKeyLength;
+ DlHeader.EntryHasChecksum = pFileHeader->EntryHasChecksum;
+ DlHeader.EntryCount = ConvertBytesToInteger_4(pFileHeader->EntryCount);
+ DlHeader.TagCount = ConvertBytesToInteger_2(pFileHeader->TagCount);
+ DlHeader.HeaderLength = FIELD_OFFSET(FILE_DOWNLOAD_HEADER, FlagByteSize);
+ DlHeader.EntryLength = DlHeader.EKeyLength + 5 + 1 + (DlHeader.EntryHasChecksum ? 4 : 0);
+
+ // Capture header version 2
+ if (pFileHeader->Version >= 2)
+ {
+ DlHeader.FlagByteSize = pFileHeader->FlagByteSize;
+ DlHeader.HeaderLength = FIELD_OFFSET(FILE_DOWNLOAD_HEADER, BasePriority);
+ DlHeader.EntryLength += DlHeader.FlagByteSize;
- if((FilePosition + pSizeAndHash->cbBlockSize) > pKeyMapping->cbFileData)
- return ERROR_BAD_FORMAT;
- if(pSizeAndHash->cbBlockSize < sizeof(CASC_INDEX_ENTRY))
- return ERROR_BAD_FORMAT;
+ // Capture header version 3
+ if (pFileHeader->Version >= 3)
+ {
+ DlHeader.BasePriority = pFileHeader->BasePriority;
+ DlHeader.HeaderLength = sizeof(FILE_DOWNLOAD_HEADER);
+ }
+ }
- // Remember the array of file keys
- pKeyMapping->pIndexEntries = (PCASC_INDEX_ENTRY)(pKeyMapping->pbFileData + FilePosition);
- pKeyMapping->nIndexEntries = pSizeAndHash->cbBlockSize / sizeof(CASC_INDEX_ENTRY);
- FilePosition += pSizeAndHash->cbBlockSize;
+ return ERROR_SUCCESS;
+}
- // Verify the integrity of the key array
- for(DWORD i = 0; i < pKeyMapping->nIndexEntries; i++)
- hashlittle2(pKeyMapping->pIndexEntries + i, sizeof(CASC_INDEX_ENTRY), &HashHigh, &HashLow);
- if(HashHigh != pSizeAndHash->dwBlockHash)
+int CaptureDownloadEntry(CASC_DOWNLOAD_HEADER & DlHeader, CASC_DOWNLOAD_ENTRY & DlEntry, LPBYTE pbFilePtr, LPBYTE pbFileEnd)
+{
+ // Check the range
+ if((pbFilePtr + DlHeader.EntryLength) >= pbFileEnd)
return ERROR_BAD_FORMAT;
+ memset(&DlEntry, 0, sizeof(CASC_DOWNLOAD_ENTRY));
- // Align the data position up to next 0x1000
- FilePosition = ALIGN_TO_SIZE(FilePosition, 0x1000);
- if(FilePosition > pKeyMapping->cbFileData)
- return ERROR_BAD_FORMAT;
+ // Copy the EKey
+ memcpy(DlEntry.EKey, pbFilePtr, DlHeader.EKeyLength);
+ pbFilePtr += DlHeader.EKeyLength;
- LastPartLength = pKeyMapping->cbFileData - FilePosition;
- if(LastPartLength < 0x7800)
- return ERROR_BAD_FORMAT;
+ // Convert the file size
+ DlEntry.EncodedSize = ConvertBytesToInteger_5(pbFilePtr);
+ pbFilePtr += 5;
- pbLastPart = pKeyMapping->pbFileData + FilePosition;
- pbLastPartEnd = pbLastPart + ((LastPartLength >> 0x09) << 0x09);
+ // Copy the file priority
+ DlEntry.Priority = pbFilePtr[0];
+ pbFilePtr++;
- while(pbLastPart < pbLastPartEnd)
+ // Copy the checksum
+ if(DlHeader.EntryHasChecksum)
{
- for(int i = 0; i < 0x1F8; i += 0x18)
- {
- PDWORD PtrLastPart = (PDWORD)pbLastPart;
- if(PtrLastPart[0] == 0)
- return ERROR_SUCCESS;
-
- HashLow = hashlittle(PtrLastPart + i, 0x13, 0) | 0x80000000;
- if(HashLow != PtrLastPart[0])
- return ERROR_BAD_FORMAT;
- }
-
- pbLastPart += 0x200;
+ DlEntry.Checksum = ConvertBytesToInteger_4(pbFilePtr);
+ pbFilePtr += 4;
}
+ // Copy the flags
+ DlEntry.Flags = ConvertBytesToInteger_X(pbFilePtr, DlHeader.FlagByteSize);
return ERROR_SUCCESS;
}
-static int VerifyAndParseKeyMapping(PCASC_MAPPING_TABLE pKeyMapping, DWORD KeyIndex)
+int CaptureDownloadTag(CASC_DOWNLOAD_HEADER & DlHeader, CASC_TAG_ENTRY1 & DlTag, LPBYTE pbFilePtr, LPBYTE pbFileEnd)
{
- // Sanity checks
- assert(pKeyMapping->pbFileData != NULL);
- assert(pKeyMapping->cbFileData != 0);
+ LPBYTE pbSaveFilePtr = pbFilePtr;
- // Check for CASC version 2
- if(IsCascIndexHeader_V2(pKeyMapping->pbFileData, pKeyMapping->cbFileData))
- return VerifyAndParseKeyMapping_V2(pKeyMapping, KeyIndex);
+ // Prepare the tag structure
+ memset(&DlTag, 0, sizeof(CASC_TAG_ENTRY1));
+ DlTag.szTagName = (const char *)pbFilePtr;
- // Check for CASC version 1
- if(IsCascIndexHeader_V1(pKeyMapping->pbFileData, pKeyMapping->cbFileData))
- return VerifyAndParseKeyMapping_V1(pKeyMapping, KeyIndex);
+ // Skip the tag string
+ while(pbFilePtr < pbFileEnd && pbFilePtr[0] != 0)
+ pbFilePtr++;
+ if(pbFilePtr >= pbFileEnd)
+ return ERROR_BAD_FORMAT;
+
+ // Save the length of the tag name
+ DlTag.NameLength = (pbFilePtr - pbSaveFilePtr);
+ pbFilePtr++;
- // Unknown CASC version
- assert(false);
- return ERROR_BAD_FORMAT;
+ // Get the tag value
+ if((pbFilePtr + sizeof(DWORD)) > pbFileEnd)
+ return ERROR_BAD_FORMAT;
+ DlTag.TagValue = ConvertBytesToInteger_2(pbFilePtr);
+ pbFilePtr += 2;
+
+ // Get the bitmap
+ DlTag.Bitmap = pbFilePtr;
+
+ // Get the bitmap length.
+ // If the bitmap is last in the list and it's shorter than declared, we make it shorter
+ DlTag.BitmapLength = GetTagBitmapLength(pbFilePtr, pbFileEnd, DlHeader.EntryCount);
+
+ // Get the entry length
+ DlTag.TagLength = (pbFilePtr - pbSaveFilePtr) + DlTag.BitmapLength;
+ return ERROR_SUCCESS;
}
-static int LoadKeyMapping(PCASC_MAPPING_TABLE pKeyMapping, DWORD KeyIndex)
+static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHeader, LPBYTE pbFileData, LPBYTE pbFileEnd)
{
- TFileStream * pStream;
- ULONGLONG FileSize = 0;
+ PCASC_TAG_ENTRY1 TagArray = NULL;
+ LPBYTE pbEntries = pbFileData + DlHeader.HeaderLength;
+ LPBYTE pbEntry = pbEntries;
+ LPBYTE pbTags = pbEntries + DlHeader.EntryLength * DlHeader.EntryCount;
+ LPBYTE pbTag = pbTags;
+ size_t nMaxNameLength = 0;
+ size_t nTagEntryLengh = 0;
int nError = ERROR_SUCCESS;
- // Sanity checks
- assert(pKeyMapping->szFileName != NULL && pKeyMapping->szFileName[0] != 0);
-
- // Open the stream for read-only access and read the file
- pStream = FileStream_OpenFile(pKeyMapping->szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
- if(pStream != NULL)
+ // Does the storage support tags?
+ if(DlHeader.TagCount != 0)
{
- // Retrieve the file size
- FileStream_GetSize(pStream, &FileSize);
- if(0 < FileSize && FileSize <= 0x200000)
+ // Remember that we support tags
+ hs->dwFeatures |= CASC_FEATURE_TAGS;
+
+ // Allocate space for the tag array
+ TagArray = CASC_ALLOC(CASC_TAG_ENTRY1, DlHeader.TagCount);
+ if(TagArray != NULL)
{
- // WoW6 actually reads THE ENTIRE file to memory
- // Verified on Mac build (x64)
- pKeyMapping->pbFileData = CASC_ALLOC(BYTE, (DWORD)FileSize);
- pKeyMapping->cbFileData = (DWORD)FileSize;
+ // Get the longest tag name
+ for(DWORD i = 0; i < DlHeader.TagCount; i++)
+ {
+ if(CaptureDownloadTag(DlHeader, TagArray[i], pbTag, pbFileEnd) == ERROR_SUCCESS)
+ nMaxNameLength = CASCLIB_MAX(nMaxNameLength, TagArray[i].NameLength);
+ pbTag = pbTag + TagArray[i].TagLength;
+ }
+
+ // Determine the tag entry length
+ nTagEntryLengh = FIELD_OFFSET(CASC_TAG_ENTRY2, szTagName) + nMaxNameLength;
+ nTagEntryLengh = ALIGN_TO_SIZE(nTagEntryLengh, 8);
- // Load the data to memory and parse it
- if(pKeyMapping->pbFileData != NULL)
+ // Load the tags into array in the storage structure
+ nError = hs->TagsArray.Create(nTagEntryLengh, DlHeader.TagCount);
+ if(nError == ERROR_SUCCESS)
{
- if(FileStream_Read(pStream, NULL, pKeyMapping->pbFileData, pKeyMapping->cbFileData))
+ // Convert the array of CASC_DOWNLOAD_TAG1 to array of CASC_DOWNLOAD_TAG2
+ for(DWORD i = 0; i < DlHeader.TagCount; i++)
{
- nError = VerifyAndParseKeyMapping(pKeyMapping, KeyIndex);
+ PCASC_TAG_ENTRY1 pSourceTag = &TagArray[i];
+ PCASC_TAG_ENTRY2 pTargetTag;
+
+ // Insert the tag to the array
+ pTargetTag = (PCASC_TAG_ENTRY2)hs->TagsArray.Insert(1);
+ if(pTargetTag == NULL)
+ {
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ // Copy the tag structure
+ memset(pTargetTag, 0, nTagEntryLengh);
+ memcpy(pTargetTag->szTagName, pSourceTag->szTagName, pSourceTag->NameLength);
+ pTargetTag->NameLength = pSourceTag->NameLength;
+ pTargetTag->TagValue = pSourceTag->TagValue;
}
}
- else
- nError = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
- assert(false);
- nError = ERROR_BAD_FORMAT;
+ nError = ERROR_NOT_ENOUGH_MEMORY;
}
-
- // Close the file stream
- FileStream_Close(pStream);
}
- else
- nError = GetLastError();
-
- return ERROR_SUCCESS;
-}
-static int CreateArrayOfIndexEntries(TCascStorage * hs)
-{
- PCASC_MAP pMap;
- DWORD TotalCount = 0;
- int nError = ERROR_NOT_ENOUGH_MEMORY;
+ // Now parse all entries. For each entry, mark the corresponding tag bit in the EKey table
+ for(DWORD i = 0; i < DlHeader.EntryCount; i++)
+ {
+ CASC_DOWNLOAD_ENTRY DlEntry;
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ size_t BitMaskOffset = (i / 8);
+ BYTE BitMaskBit = 0x80 >> (i % 8);
- // Count the total number of files in the storage
- for(size_t i = 0; i < CASC_INDEX_COUNT; i++)
- TotalCount += hs->KeyMapping[i].nIndexEntries;
+ // Capture the download entry
+ if(CaptureDownloadEntry(DlHeader, DlEntry, pbEntry, pbFileEnd) != ERROR_SUCCESS)
+ break;
- // Create the map of all index entries
- pMap = Map_Create(TotalCount, CASC_FILE_KEY_SIZE, FIELD_OFFSET(CASC_INDEX_ENTRY, IndexKey));
- if(pMap != NULL)
- {
- // Put all index entries in the map
- for(size_t i = 0; i < CASC_INDEX_COUNT; i++)
+ // Make sure we have the entry in CKey table
+ pCKeyEntry = FindCKeyEntry_EKey(hs, DlEntry.EKey);
+ if(pCKeyEntry != NULL)
{
- PCASC_INDEX_ENTRY pIndexEntry = hs->KeyMapping[i].pIndexEntries;
- DWORD nIndexEntries = hs->KeyMapping[i].nIndexEntries;
+ ULONGLONG TagBit = 1;
+ size_t TagItemCount = hs->TagsArray.ItemCount();
- for(DWORD j = 0; j < nIndexEntries; j++)
+ // Supply the tag bits
+ for(size_t j = 0; j < TagItemCount; j++)
{
- // Insert the index entry to the map
- // Note that duplicate entries will not be inserted to the map
- //
- // Duplicate entries in WoW-WOD build 18179:
- // 9e dc a7 8f e2 09 ad d8 b7 (encoding file)
- // f3 5e bb fb d1 2b 3f ef 8b
- // c8 69 9f 18 a2 5e df 7e 52
- Map_InsertObject(pMap, pIndexEntry, pIndexEntry->IndexKey);
-
- // Move to the next entry
- pIndexEntry++;
+ // Set the bit in the entry, if the tag for it is present
+ if((BitMaskOffset < TagArray[j].BitmapLength) && (TagArray[j].Bitmap[BitMaskOffset] & BitMaskBit))
+ pCKeyEntry->TagBitMask |= TagBit;
+
+ // Move to the next bit
+ TagBit <<= 1;
}
+
+ // If the EKey has partial EKey only, fix that
+ if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY_PARTIAL)
+ {
+ CopyMemory16(pCKeyEntry->EKey, DlEntry.EKey);
+ pCKeyEntry->Flags &= ~CASC_CE_HAS_EKEY_PARTIAL;
+ }
+
+ // Supply the priority
+ pCKeyEntry->Priority = DlEntry.Priority;
+ pCKeyEntry->Flags |= CASC_CE_IN_DOWNLOAD;
}
- // Store the map to the storage handle
- hs->pIndexEntryMap = pMap;
- nError = ERROR_SUCCESS;
+ // Move to the next entry
+ pbEntry += DlHeader.EntryLength;
}
+ // Free the tag array, if any
+ CASC_FREE(TagArray);
+
+ // Remember the total file count
+ hs->TotalFiles = hs->CKeyArray.ItemCount();
return nError;
}
-static int CreateMapOfEncodingKeys(TCascStorage * hs, PFILE_ENCODING_SEGMENT pEncodingSegment, DWORD dwNumSegments)
+static int LoadDownloadManifest(TCascStorage * hs)
{
- PCASC_ENCODING_ENTRY pEncodingEntry;
- DWORD dwMaxEntries;
+ PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->DownloadCKey.CKey);
+ LPBYTE pbDownloadFile = NULL;
+ DWORD cbDownloadFile = 0;
int nError = ERROR_SUCCESS;
- // Sanity check
- assert(hs->pIndexEntryMap != NULL);
- assert(hs->pEncodingMap == NULL);
-
- // Calculate the largest eventual number of encoding entries
- // Add space for extra entries
- dwMaxEntries = (dwNumSegments * CASC_ENCODING_SEGMENT_SIZE) / (sizeof(CASC_ENCODING_ENTRY) + MD5_HASH_SIZE);
+ // Inform the user about what we are doing
+ if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Loading DOWNLOAD manifest", NULL, 0, 0))
+ return ERROR_CANCELLED;
- // Create the map of the encoding entries
- hs->pEncodingMap = Map_Create(dwMaxEntries + CASC_EXTRA_FILES, MD5_HASH_SIZE, FIELD_OFFSET(CASC_ENCODING_ENTRY, EncodingKey));
- if(hs->pEncodingMap != NULL)
+ // Load the entire DOWNLOAD file to memory
+ pbDownloadFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbDownloadFile);
+ if(pbDownloadFile != NULL && cbDownloadFile != 0)
{
- LPBYTE pbStartOfSegment = (LPBYTE)(pEncodingSegment + dwNumSegments);
+ CASC_DOWNLOAD_HEADER DlHeader;
- // Parse all segments
- for(DWORD i = 0; i < dwNumSegments; i++)
+ // Capture the header of the DOWNLOAD file
+ nError = CaptureDownloadHeader(DlHeader, pbDownloadFile, cbDownloadFile);
+ if(nError == ERROR_SUCCESS)
{
- LPBYTE pbEncodingEntry = pbStartOfSegment;
- LPBYTE pbEndOfSegment = pbStartOfSegment + CASC_ENCODING_SEGMENT_SIZE - sizeof(CASC_ENCODING_ENTRY) - MD5_HASH_SIZE;
-
- // Parse all encoding entries
- while(pbEncodingEntry <= pbEndOfSegment)
- {
- // Get pointer to the encoding entry
- pEncodingEntry = (PCASC_ENCODING_ENTRY)pbEncodingEntry;
- if(pEncodingEntry->KeyCount == 0)
- break;
-
- // Insert the pointer the array
- Map_InsertObject(hs->pEncodingMap, pEncodingEntry, pEncodingEntry->EncodingKey);
-
- // Move to the next encoding entry
- pbEncodingEntry += sizeof(CASC_ENCODING_ENTRY) + (pEncodingEntry->KeyCount * MD5_HASH_SIZE);
- }
-
- // Move to the next segment
- pbStartOfSegment += CASC_ENCODING_SEGMENT_SIZE;
+ // Parse the entire download manifest
+ nError = LoadDownloadManifest(hs, DlHeader, pbDownloadFile, pbDownloadFile + cbDownloadFile);
}
+
+ // Free the loaded manifest
+ CASC_FREE(pbDownloadFile);
}
- else
- nError = ERROR_NOT_ENOUGH_MEMORY;
+ // If the DOWNLOAD manifest is not present, we won't abort the downloading process.
return nError;
}
-static int LoadIndexFiles(TCascStorage * hs)
+//-----------------------------------------------------------------------------
+// INSTALL manifest. This is a replacement for ROOT, if loading ROOT fails
+// https://wowdev.wiki/TACT#Install_manifest
+
+static int LoadInstallManifest(TCascStorage * hs)
{
- DWORD IndexArray[CASC_INDEX_COUNT];
- DWORD OldIndexArray[CASC_INDEX_COUNT];
- int nError;
- int i;
+ PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->InstallCKey.CKey);
+ LPBYTE pbInstallFile = NULL;
+ DWORD cbInstallFile = 0;
+ int nError = ERROR_SUCCESS;
- // Scan all index files
- memset(IndexArray, 0, sizeof(IndexArray));
- memset(OldIndexArray, 0, sizeof(OldIndexArray));
- nError = ScanIndexDirectory(hs->szIndexPath, IndexDirectory_OnFileFound, IndexArray, OldIndexArray, hs);
- if(nError == ERROR_SUCCESS)
+ // Inform the user about what we are doing
+ if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Loading INSTALL manifest", NULL, 0, 0))
+ return ERROR_CANCELLED;
+
+ // Load the entire DOWNLOAD file to memory
+ pbInstallFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbInstallFile);
+ if (pbInstallFile != NULL && cbInstallFile != 0)
{
- // Load each index file
- for(i = 0; i < CASC_INDEX_COUNT; i++)
- {
- hs->KeyMapping[i].szFileName = CreateIndexFileName(hs, i, IndexArray[i]);
- if(hs->KeyMapping[i].szFileName != NULL)
- {
- nError = LoadKeyMapping(&hs->KeyMapping[i], i);
- if(nError != ERROR_SUCCESS)
- break;
- }
- }
+ nError = RootHandler_CreateInstall(hs, pbInstallFile, cbInstallFile);
+ CASC_FREE(pbInstallFile);
}
-
- // Now we need to build the map of the index entries
- if(nError == ERROR_SUCCESS)
+ else
{
- nError = CreateArrayOfIndexEntries(hs);
+ nError = GetLastError();
}
return nError;
}
-static LPBYTE LoadEncodingFileToMemory(HANDLE hFile, DWORD * pcbEncodingFile)
+static bool InsertWellKnownFile(TCascStorage * hs, const char * szFileName, CASC_CKEY_ENTRY & FakeCKeyEntry)
{
- CASC_ENCODING_HEADER EncodingHeader;
- LPBYTE pbEncodingFile = NULL;
- DWORD cbEncodingFile = 0;
- DWORD dwSegmentPos = 0;
- DWORD dwNumSegments = 0;
- DWORD dwBytesRead = 0;
- int nError = ERROR_BAD_FORMAT;
+ PCASC_CKEY_ENTRY pCKeyEntry = NULL;
- // Read the encoding header
- CascReadFile(hFile, &EncodingHeader, sizeof(CASC_ENCODING_HEADER), &dwBytesRead);
- if(dwBytesRead == sizeof(CASC_ENCODING_HEADER))
+ // We need to find the CKey entry in the central array
+ if(FakeCKeyEntry.Flags & CASC_CE_HAS_CKEY)
{
- // Check the version and sizes
- if(EncodingHeader.Version != 0x01 || EncodingHeader.ChecksumSizeA != MD5_HASH_SIZE || EncodingHeader.ChecksumSizeB != MD5_HASH_SIZE)
+ // Did we find anything?
+ pCKeyEntry = FindCKeyEntry_CKey(hs, FakeCKeyEntry.CKey);
+ if(pCKeyEntry != NULL)
{
- assert(false);
- return NULL;
+ // Insert the key to the root handler, unless it's already referenced by a name
+ if(pCKeyEntry->RefCount == 0)
+ hs->pRootHandler->Insert(szFileName, pCKeyEntry);
+ return true;
}
-
- // Get the number of segments
- dwNumSegments = ConvertBytesToInteger_4(EncodingHeader.Entries_TableA);
- dwSegmentPos = ConvertBytesToInteger_4(EncodingHeader.Size_StringTable1);
- if(EncodingHeader.Magic[0] == 'E' && EncodingHeader.Magic[1] == 'N' && dwSegmentPos != 0 && dwNumSegments != 0)
- nError = ERROR_SUCCESS;
- }
-
- // Calculate and allocate space for the entire file
- if(nError == ERROR_SUCCESS)
- {
- cbEncodingFile = sizeof(CASC_ENCODING_HEADER) +
- dwSegmentPos +
- dwNumSegments * (sizeof(FILE_ENCODING_SEGMENT) + CASC_ENCODING_SEGMENT_SIZE);
- pbEncodingFile = CASC_ALLOC(BYTE, cbEncodingFile);
- if(pbEncodingFile == NULL)
- nError = ERROR_NOT_ENOUGH_MEMORY;
}
- // If all went OK, we load the entire file to memory
- if(nError == ERROR_SUCCESS)
- {
- // Copy the header itself
- memcpy(pbEncodingFile, &EncodingHeader, sizeof(CASC_ENCODING_HEADER));
-
- // Read the rest of the data
- CascReadFile(hFile, pbEncodingFile + sizeof(CASC_ENCODING_HEADER), cbEncodingFile - sizeof(CASC_ENCODING_HEADER), &dwBytesRead);
- if(dwBytesRead != (cbEncodingFile - sizeof(CASC_ENCODING_HEADER)))
- nError = ERROR_FILE_CORRUPT;
- }
-
- // Give the loaded file length
- if(pcbEncodingFile != NULL)
- *pcbEncodingFile = cbEncodingFile;
- return pbEncodingFile;
+ return false;
}
-static LPBYTE LoadRootFileToMemory(HANDLE hFile, DWORD * pcbRootFile)
+static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
{
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ PDWORD FileSignature;
LPBYTE pbRootFile = NULL;
DWORD cbRootFile = 0;
- DWORD dwBytesRead = 0;
- int nError = ERROR_SUCCESS;
+ int nError = ERROR_BAD_FORMAT;
- // Retrieve the size of the ROOT file
- cbRootFile = CascGetFileSize(hFile, NULL);
- if(cbRootFile == 0)
- nError = ERROR_BAD_FORMAT;
+ // Sanity checks
+ assert(hs->CKeyMap.IsInitialized() == true);
+ assert(hs->pRootHandler == NULL);
- // Allocate space for the entire file
- if(nError == ERROR_SUCCESS)
- {
- pbRootFile = CASC_ALLOC(BYTE, cbRootFile);
- if(pbRootFile == NULL)
- nError = ERROR_NOT_ENOUGH_MEMORY;
- }
+ // Locale: The default parameter is 0 - in that case,
+ // we assign the default locale, loaded from the .build.info file
+ if(dwLocaleMask == 0)
+ dwLocaleMask = hs->dwDefaultLocale;
- // If all went OK, we load the entire file to memory
- if(nError == ERROR_SUCCESS)
+ // Prioritize the VFS root over legacy ROOT file
+ pCKeyEntry = (hs->VfsRoot.ContentSize != CASC_INVALID_SIZE) ? &hs->VfsRoot : &hs->RootFile;
+ pCKeyEntry = FindCKeyEntry_CKey(hs, pCKeyEntry->CKey);
+
+ // Inform the user about what we are doing
+ if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Loading ROOT manifest", NULL, 0, 0))
+ return ERROR_CANCELLED;
+
+ // Load the entire ROOT file to memory
+ pbRootFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbRootFile);
+ if(pbRootFile != NULL)
{
- // Read the entire file to memory
- CascReadFile(hFile, pbRootFile, cbRootFile, &dwBytesRead);
- if(dwBytesRead != cbRootFile)
- nError = ERROR_FILE_CORRUPT;
- }
+ // Ignore ROOT files that contain just a MD5 hash
+ if(cbRootFile > MD5_STRING_SIZE)
+ {
+ // Check the type of the ROOT file
+ FileSignature = (PDWORD)pbRootFile;
+ switch(FileSignature[0])
+ {
+ case CASC_MNDX_ROOT_SIGNATURE:
+ nError = RootHandler_CreateMNDX(hs, pbRootFile, cbRootFile);
+ break;
- // Give the loaded file length
- if(pcbRootFile != NULL)
- *pcbRootFile = cbRootFile;
- return pbRootFile;
-}
+ case CASC_DIABLO3_ROOT_SIGNATURE:
+ nError = RootHandler_CreateDiablo3(hs, pbRootFile, cbRootFile);
+ break;
-static int LoadEncodingFile(TCascStorage * hs)
-{
- PFILE_ENCODING_SEGMENT pEncodingSegment;
- QUERY_KEY EncodingKey;
- LPBYTE pbStartOfSegment;
- LPBYTE pbEncodingFile = NULL;
- HANDLE hFile = NULL;
- DWORD cbEncodingFile = 0;
- DWORD dwNumSegments = 0;
- DWORD dwSegmentsPos = 0;
- int nError = ERROR_SUCCESS;
+ case CASC_TVFS_ROOT_SIGNATURE:
+ nError = RootHandler_CreateTVFS(hs, pbRootFile, cbRootFile);
+ break;
- // Open the encoding file
- EncodingKey.pbData = hs->EncodingKey.pbData + MD5_HASH_SIZE;
- EncodingKey.cbData = MD5_HASH_SIZE;
- if(!CascOpenFileByIndexKey((HANDLE)hs, &EncodingKey, 0, &hFile))
- nError = GetLastError();
+ case CASC_WOW82_ROOT_SIGNATURE:
+ nError = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask);
+ break;
- // Load the entire ENCODING file to memory
- if(nError == ERROR_SUCCESS)
- {
- // Load the necessary part of the ENCODING file to memory
- pbEncodingFile = LoadEncodingFileToMemory(hFile, &cbEncodingFile);
- if(pbEncodingFile == NULL || cbEncodingFile <= sizeof(CASC_ENCODING_HEADER))
- nError = ERROR_FILE_CORRUPT;
+ default:
+
+ //
+ // Each of these handler creators must verify their format first.
+ // If the format was not recognized, they need to return ERROR_BAD_FORMAT
+ //
+
+ nError = RootHandler_CreateOverwatch(hs, pbRootFile, cbRootFile);
+ if(nError == ERROR_BAD_FORMAT)
+ {
+ nError = RootHandler_CreateStarcraft1(hs, pbRootFile, cbRootFile);
+ if(nError == ERROR_BAD_FORMAT)
+ {
+ nError = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask);
+ }
+ }
+ break;
+ }
+ }
- // Close the encoding file
- CascCloseFile(hFile);
+ // Free the root file
+ CASC_FREE(pbRootFile);
}
-
- // Verify all encoding segments
- if(nError == ERROR_SUCCESS)
+ else
{
- PCASC_ENCODING_HEADER pEncodingHeader = (PCASC_ENCODING_HEADER)pbEncodingFile;
-
- // Convert size and offset
- dwNumSegments = ConvertBytesToInteger_4(pEncodingHeader->Entries_TableA);
- dwSegmentsPos = ConvertBytesToInteger_4(pEncodingHeader->Size_StringTable1);
+ nError = GetLastError();
+ }
- // Store the encoding file to the CASC storage
- hs->EncodingFile.pbData = pbEncodingFile;
- hs->EncodingFile.cbData = cbEncodingFile;
+ return nError;
+}
- // Allocate the array of encoding segments
- pEncodingSegment = (PFILE_ENCODING_SEGMENT)(pbEncodingFile + sizeof(CASC_ENCODING_HEADER) + dwSegmentsPos);
- pbStartOfSegment = (LPBYTE)(pEncodingSegment + dwNumSegments);
+static int InitializeLocalDirectories(TCascStorage * hs, const TCHAR * szPath)
+{
+ TCHAR * szWorkPath;
+ int nError = ERROR_NOT_ENOUGH_MEMORY;
- // Go through all encoding segments and verify them
- for(DWORD i = 0; i < dwNumSegments; i++)
+ // Find the root directory of the storage. The root directory
+ // is the one with ".build.info" or ".build.db".
+ szWorkPath = CascNewStr(szPath);
+ if(szWorkPath != NULL)
+ {
+ // Get the length and go up until we find the ".build.info" or ".build.db"
+ for(;;)
{
- PCASC_ENCODING_ENTRY pEncodingEntry = (PCASC_ENCODING_ENTRY)pbStartOfSegment;
-
- // Check if there is enough space in the buffer
- if((pbStartOfSegment + CASC_ENCODING_SEGMENT_SIZE) > (pbEncodingFile + cbEncodingFile))
+ // Is this a game directory?
+ nError = CheckGameDirectory(hs, szWorkPath);
+ if(nError == ERROR_SUCCESS)
{
- nError = ERROR_FILE_CORRUPT;
+ nError = ERROR_SUCCESS;
break;
}
- // Check the hash of the entire segment
- // Note that verifying takes considerable time of the storage loading
-// if(!VerifyDataBlockHash(pbStartOfSegment, CASC_ENCODING_SEGMENT_SIZE, pEncodingSegment->SegmentHash))
-// {
-// nError = ERROR_FILE_CORRUPT;
-// break;
-// }
-
- // Check if the encoding key matches with the expected first value
- if(memcmp(pEncodingEntry->EncodingKey, pEncodingSegment->FirstEncodingKey, MD5_HASH_SIZE))
+ // Cut one path part
+ if(!CutLastPathPart(szWorkPath))
{
- nError = ERROR_FILE_CORRUPT;
+ nError = ERROR_FILE_NOT_FOUND;
break;
}
+ }
+
+ // Find the index directory
+ if (nError == ERROR_SUCCESS)
+ {
+ // First, check for more common "data" subdirectory
+ if ((hs->szIndexPath = CheckForIndexDirectory(hs, _T("data"))) != NULL)
+ nError = ERROR_SUCCESS;
+
+ // Second, try the "darch" subdirectory (older builds of HOTS - Alpha)
+ else if ((hs->szIndexPath = CheckForIndexDirectory(hs, _T("darch"))) != NULL)
+ nError = ERROR_SUCCESS;
- // Move to the next segment
- pbStartOfSegment += CASC_ENCODING_SEGMENT_SIZE;
- pEncodingSegment++;
+ else
+ nError = ERROR_FILE_NOT_FOUND;
}
- }
- // Create the map of the encoding keys
- // Note that the array of encoding keys is already sorted - no need to sort it
- if(nError == ERROR_SUCCESS)
- {
- pEncodingSegment = (PFILE_ENCODING_SEGMENT)(pbEncodingFile + sizeof(CASC_ENCODING_HEADER) + dwSegmentsPos);
- nError = CreateMapOfEncodingKeys(hs, pEncodingSegment, dwNumSegments);
+ // Free the work path buffer
+ CASC_FREE(szWorkPath);
}
return nError;
}
-static int LoadRootFile(TCascStorage * hs, DWORD dwLocaleMask)
+static int InitializeOnlineDirectories(TCascStorage * hs, LPCTSTR szLocalCache, LPCSTR szCodeName, LPCSTR szRegion)
{
- PDWORD FileSignature;
- HANDLE hFile = NULL;
- LPBYTE pbRootFile = NULL;
- DWORD cbRootFile = 0;
- int nError = ERROR_SUCCESS;
+ TCHAR szCodeNameT[0x40];
- // Sanity checks
- assert(hs->pEncodingMap != NULL);
- assert(hs->pRootHandler == NULL);
-
- // Locale: The default parameter is 0 - in that case,
- // we assign the default locale, loaded from the .build.info file
- if(dwLocaleMask == 0)
- dwLocaleMask = hs->dwDefaultLocale;
-
- // Load the entire ROOT file to memory
- if(!CascOpenFileByEncodingKey((HANDLE)hs, &hs->RootKey, 0, &hFile))
- nError = GetLastError();
-
- // Load the entire file to memory
- if(nError == ERROR_SUCCESS)
+ CascStrCopy(szCodeNameT, _countof(szCodeNameT), szCodeName);
+ hs->szRootPath = CombinePath(szLocalCache, szCodeNameT);
+ if (hs->szRootPath != NULL)
{
- pbRootFile = LoadRootFileToMemory(hFile, &cbRootFile);
- CascCloseFile(hFile);
+ // Create the name of the build file
+ hs->szBuildFile = CombinePath(hs->szRootPath, _T("versions"));
+ if(hs->szBuildFile != NULL)
+ {
+ hs->szCodeName = CascNewStr(szCodeNameT);
+ hs->szRegion = CascNewStr(szRegion);
+ if(hs->szCodeName != NULL)
+ {
+ hs->BuildFileType = CascVersionsDb;
+ hs->dwFeatures |= CASC_FEATURE_ONLINE;
+ return ERROR_SUCCESS;
+ }
+ }
}
- // Check if the version of the ROOT file
- if(nError == ERROR_SUCCESS && pbRootFile != NULL)
- {
- FileSignature = (PDWORD)pbRootFile;
- switch(FileSignature[0])
- {
- case CASC_MNDX_ROOT_SIGNATURE:
- nError = RootHandler_CreateMNDX(hs, pbRootFile, cbRootFile);
- break;
+ return ERROR_NOT_ENOUGH_MEMORY;
+}
- case CASC_DIABLO3_ROOT_SIGNATURE:
- nError = RootHandler_CreateDiablo3(hs, pbRootFile, cbRootFile);
- break;
+static DWORD GetStorageTotalFileCount(TCascStorage * hs)
+{
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ size_t nItemCount = hs->CKeyArray.ItemCount();
+ DWORD TotalFileCount = 0;
- case CASC_OVERWATCH_ROOT_SIGNATURE:
- nError = RootHandler_CreateOverwatch(hs, pbRootFile, cbRootFile);
- break;
+ for(size_t i = 0; i < nItemCount; i++)
+ {
+ if((pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.ItemAt(i)) != NULL)
+ {
+ if((pCKeyEntry->Flags & CASC_CE_FOLDER_ENTRY) == 0)
+ {
+ // If there is zero or one file name reference, we count the item as one file.
+ // If there is more than 1 name reference, we count the file as many times as number of references
+ DWORD RefCount = (pCKeyEntry->RefCount > 0) ? pCKeyEntry->RefCount : 1;
- default:
- if(IsRootFile_Starcraft1(pbRootFile, cbRootFile))
- nError = RootHandler_CreateSC1(hs, pbRootFile, cbRootFile);
- else
- nError = RootHandler_CreateWoW6(hs, pbRootFile, cbRootFile, dwLocaleMask);
- break;
+ // Add the number of references to the total file count
+ TotalFileCount += RefCount;
+ }
}
}
- // Insert entry for the
- if(nError == ERROR_SUCCESS)
- {
- InsertExtraFile(hs, "ENCODING", &hs->EncodingKey);
- InsertExtraFile(hs, "ROOT", &hs->RootKey);
- InsertExtraFile(hs, "DOWNLOAD", &hs->DownloadKey);
- InsertExtraFile(hs, "INSTALL", &hs->InstallKey);
- }
+ return TotalFileCount;
+}
-#ifdef _DEBUG
- if(nError == ERROR_SUCCESS)
+static bool GetStorageProduct(TCascStorage * hs, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded)
+{
+ PCASC_STORAGE_PRODUCT pProductInfo;
+
+ // Verify whether we have enough space in the buffer
+ pProductInfo = (PCASC_STORAGE_PRODUCT)ProbeOutputBuffer(pvStorageInfo, cbStorageInfo, sizeof(CASC_STORAGE_PRODUCT), pcbLengthNeeded);
+ if(pProductInfo != NULL)
{
- //RootFile_Dump(hs,
- // pbRootFile,
- // cbRootFile,
- // _T("\\casc_root_%build%.txt"),
- // _T("\\Ladik\\Appdir\\CascLib\\listfile\\listfile-wow6.txt"),
- // DUMP_LEVEL_INDEX_ENTRIES);
+ pProductInfo->szProductName = hs->szProductName;
+ pProductInfo->dwBuildNumber = hs->dwBuildNumber;
+ pProductInfo->Product = hs->Product;
}
-#endif
- // Free the root file
- CASC_FREE(pbRootFile);
- return nError;
+ return (pProductInfo != NULL);
}
-static TCascStorage * FreeCascStorage(TCascStorage * hs)
+static bool GetStorageTags(TCascStorage * hs, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded)
{
- size_t i;
+ PCASC_STORAGE_TAGS pTags;
+ PCASC_TAG_ENTRY2 pTag;
+ char * szNameBuffer;
+ size_t cbMinLength;
- if(hs != NULL)
+ // Does the storage support tags?
+ if(hs->TagsArray.IsInitialized() == false)
{
- // Free the root handler
- if(hs->pRootHandler != NULL)
- RootHandler_Close(hs->pRootHandler);
- hs->pRootHandler = NULL;
-
- // Free the extra encoding entries
- Array_Free(&hs->ExtraEntries);
-
- // Free the pointers to file entries
- if(hs->pEncodingMap != NULL)
- Map_Free(hs->pEncodingMap);
- if(hs->EncodingFile.pbData != NULL)
- CASC_FREE(hs->EncodingFile.pbData);
- if(hs->pIndexEntryMap != NULL)
- Map_Free(hs->pIndexEntryMap);
-
- // Close all data files
- for(i = 0; i < CASC_MAX_DATA_FILES; i++)
- {
- if(hs->DataFileArray[i] != NULL)
- {
- FileStream_Close(hs->DataFileArray[i]);
- hs->DataFileArray[i] = NULL;
- }
- }
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return false;
+ }
+
+ // Calculate the length of the tags
+ cbMinLength = FIELD_OFFSET(CASC_STORAGE_TAGS, Tags) + hs->TagsArray.ItemCount() * sizeof(CASC_STORAGE_TAG);
+ szNameBuffer = (char *)pvStorageInfo + cbMinLength;
+
+ // Also include the tag length
+ for(size_t i = 0; i < hs->TagsArray.ItemCount(); i++)
+ {
+ pTag = (PCASC_TAG_ENTRY2)hs->TagsArray.ItemAt(i);
+ cbMinLength = cbMinLength + pTag->NameLength + 1;
+ }
- // Close all key mappings
- for(i = 0; i < CASC_INDEX_COUNT; i++)
+ // Verify whether we have enough space in the buffer
+ pTags = (PCASC_STORAGE_TAGS)ProbeOutputBuffer(pvStorageInfo, cbStorageInfo, cbMinLength, pcbLengthNeeded);
+ if(pTags != NULL)
+ {
+ // Fill the output structure
+ pTags->TagCount = hs->TagsArray.ItemCount();
+ pTags->Reserved = 0;
+
+ // Copy the tags
+ for(size_t i = 0; i < hs->TagsArray.ItemCount(); i++)
{
- if(hs->KeyMapping[i].szFileName != NULL)
- CASC_FREE(hs->KeyMapping[i].szFileName);
- if(hs->KeyMapping[i].pbFileData != NULL)
- CASC_FREE(hs->KeyMapping[i].pbFileData);
- hs->KeyMapping[i].pIndexEntries = NULL;
+ // Get the source tag
+ pTag = (PCASC_TAG_ENTRY2)hs->TagsArray.ItemAt(i);
+
+ // Fill the target tag
+ pTags->Tags[i].szTagName = szNameBuffer;
+ pTags->Tags[i].TagNameLength = (DWORD)pTag->NameLength;
+ pTags->Tags[i].TagValue = pTag->TagValue;
+
+ // Copy the tag name
+ memcpy(szNameBuffer, pTag->szTagName, pTag->NameLength);
+ szNameBuffer[pTag->NameLength] = 0;
+ szNameBuffer = szNameBuffer + pTag->NameLength + 1;
}
-
- // Free the file paths
- if(hs->szRootPath != NULL)
- CASC_FREE(hs->szRootPath);
- if(hs->szDataPath != NULL)
- CASC_FREE(hs->szDataPath);
- if(hs->szBuildFile != NULL)
- CASC_FREE(hs->szBuildFile);
- if(hs->szIndexPath != NULL)
- CASC_FREE(hs->szIndexPath);
- if(hs->szUrlPath != NULL)
- CASC_FREE(hs->szUrlPath);
-
- // Free the blobs
- FreeCascBlob(&hs->CdnConfigKey);
- FreeCascBlob(&hs->CdnBuildKey);
- FreeCascBlob(&hs->ArchivesGroup);
- FreeCascBlob(&hs->ArchivesKey);
- FreeCascBlob(&hs->PatchArchivesKey);
- FreeCascBlob(&hs->PatchArchivesGroup);
- FreeCascBlob(&hs->RootKey);
- FreeCascBlob(&hs->PatchKey);
- FreeCascBlob(&hs->DownloadKey);
- FreeCascBlob(&hs->InstallKey);
- FreeCascBlob(&hs->EncodingKey);
-
- // Free the storage structure
- hs->szClassName = NULL;
- CASC_FREE(hs);
}
- return NULL;
+ return (pTags != NULL);
}
-//-----------------------------------------------------------------------------
-// Public functions
-
-bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwLocaleMask, HANDLE * phStorage)
+static int LoadCascStorage(TCascStorage * hs, DWORD dwLocaleMask)
{
- TCascStorage * hs;
int nError = ERROR_SUCCESS;
- // Allocate the storage structure
- hs = (TCascStorage *)CASC_ALLOC(TCascStorage, 1);
- if(hs == NULL)
- nError = ERROR_NOT_ENOUGH_MEMORY;
-
- // Load the storage configuration
- if(nError == ERROR_SUCCESS)
+ // For online storages, we need to load CDN servers
+ if ((nError == ERROR_SUCCESS) && (hs->dwFeatures & CASC_FEATURE_ONLINE))
{
- // Prepare the base storage parameters
- memset(hs, 0, sizeof(TCascStorage));
- hs->szClassName = "TCascStorage";
- hs->dwFileBeginDelta = 0xFFFFFFFF;
- hs->dwDefaultLocale = CASC_LOCALE_ENUS | CASC_LOCALE_ENGB;
- hs->dwRefCount = 1;
- nError = InitializeCascDirectories(hs, szDataPath);
+ nError = LoadCdnsInfo(hs);
}
- // Now we need to load the root file so we know the config files
+ // Now, load the main storage file ".build.info" (or ".build.db" in old storages)
if(nError == ERROR_SUCCESS)
{
nError = LoadBuildInfo(hs);
}
- // Load the index files
+ // If the .build.info OR .build.db file has been loaded,
+ // proceed with loading the CDN config file
+ if (nError == ERROR_SUCCESS)
+ {
+ nError = LoadCdnConfigFile(hs);
+ }
+
+ // Proceed with loading the CDN build file
+ if (nError == ERROR_SUCCESS)
+ {
+ nError = LoadCdnBuildFile(hs);
+ }
+
+ // Load the index files. Store information from the index files to the CKeyArray.
if(nError == ERROR_SUCCESS)
{
nError = LoadIndexFiles(hs);
}
- // Load the index files
+ // Load the ENCODING manifest
if(nError == ERROR_SUCCESS)
{
- nError = LoadEncodingFile(hs);
+ nError = LoadEncodingManifest(hs);
}
- // Initialize the dynamic array for extra files
- // Reserve space for 0x20 encoding entries
+ // We need to load the DOWNLOAD manifest. This will give us the information about
+ // how many physical files are in the storage, so we can start building file tables
if(nError == ERROR_SUCCESS)
{
- nError = Array_Create(&hs->ExtraEntries, CASC_ENCODING_ENTRY_1, CASC_EXTRA_FILES);
+ nError = LoadDownloadManifest(hs);
}
- // Load the index files
+ // Load the build manifest ("ROOT" file)
if(nError == ERROR_SUCCESS)
{
- nError = LoadRootFile(hs, dwLocaleMask);
+ // If we fail to load the ROOT file, we take the file names from the INSTALL manifest
+ nError = LoadBuildManifest(hs, dwLocaleMask);
+ if (nError != ERROR_SUCCESS)
+ {
+ nError = LoadInstallManifest(hs);
+ }
}
- // If something failed, free the storage and return
- if(nError != ERROR_SUCCESS)
+ // Insert entries for files with well-known names. Their CKeys are in the BUILD file
+ // See https://wowdev.wiki/TACT#Encoding_table for their list
+ if (nError == ERROR_SUCCESS)
+ {
+ InsertWellKnownFile(hs, "ENCODING", hs->EncodingCKey);
+ InsertWellKnownFile(hs, "DOWNLOAD", hs->DownloadCKey);
+ InsertWellKnownFile(hs, "INSTALL", hs->InstallCKey);
+ InsertWellKnownFile(hs, "PATCH", hs->PatchFile);
+ InsertWellKnownFile(hs, "ROOT", hs->RootFile);
+ InsertWellKnownFile(hs, "SIZE", hs->SizeFile);
+
+ // Also reset the total file count. CascGetStorageInfo will update it on next call
+ hs->TotalFiles = 0;
+ }
+
+ // Load the encryption keys
+ if (nError == ERROR_SUCCESS)
+ {
+ nError = CascLoadEncryptionKeys(hs);
+ }
+
+ return nError;
+}
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+void WINAPI CascSetProgressCallback(PFNPROGRESSCALLBACK PtrUserCallback, void * PtrUserParam)
+{
+ PfnProgressCallback = PtrUserCallback;
+ PtrProgressParam = PtrUserParam;
+}
+
+bool WINAPI CascOpenStorage(LPCTSTR szPath, DWORD dwLocaleMask, HANDLE * phStorage)
+{
+ TCascStorage * hs;
+ int nError = ERROR_NOT_ENOUGH_MEMORY;
+
+ // Allocate the storage structure
+ if((hs = new TCascStorage()) != NULL)
{
- hs = FreeCascStorage(hs);
- SetLastError(nError);
+ // Setup the directories
+ nError = InitializeLocalDirectories(hs, szPath);
+ if(nError == ERROR_SUCCESS)
+ {
+ nError = LoadCascStorage(hs, dwLocaleMask);
+ if(nError == ERROR_SUCCESS)
+ {
+ *phStorage = (HANDLE)hs;
+ return true;
+ }
+ }
+
+ // Delete the so-far-allocated storage
+ hs = hs->Release();
}
- *phStorage = (HANDLE)hs;
- return (nError == ERROR_SUCCESS);
+ // Failed
+ SetLastError(nError);
+ *phStorage = NULL;
+ return false;
+}
+
+// Allows to browse an online CDN storage
+// szLocalCache: Local folder, where the online file will be cached.
+// szCodeName: Product code name, e.g. "agent" for Battle.net Agent. More info: https://wowdev.wiki/TACT#Products
+// szRegion: The region (or subvariant) of the product. Corresponds to the first column of the "versions" file.
+bool WINAPI CascOpenOnlineStorage(LPCTSTR szLocalCache, LPCSTR szCodeName, LPCSTR szRegion, DWORD dwLocaleMask, HANDLE * phStorage)
+{
+ TCascStorage * hs;
+ int nError = ERROR_NOT_ENOUGH_MEMORY;
+
+ // Allocate the storage structure
+ if((hs = new TCascStorage()) != NULL)
+ {
+ // Setup the directories
+ nError = InitializeOnlineDirectories(hs, szLocalCache, szCodeName, szRegion);
+ if(nError == ERROR_SUCCESS)
+ {
+ nError = LoadCascStorage(hs, dwLocaleMask);
+ if(nError == ERROR_SUCCESS)
+ {
+ *phStorage = (HANDLE)hs;
+ return true;
+ }
+ }
+
+ // Delete the so-far-allocated storage
+ hs = hs->Release();
+ }
+
+ // Failed
+ SetLastError(nError);
+ *phStorage = NULL;
+ return false;
}
bool WINAPI CascGetStorageInfo(
@@ -1120,10 +1240,11 @@ bool WINAPI CascGetStorageInfo(
size_t * pcbLengthNeeded)
{
TCascStorage * hs;
+ PDWORD PtrOutputValue;
DWORD dwInfoValue = 0;
// Verify the storage handle
- hs = IsValidStorageHandle(hStorage);
+ hs = TCascStorage::IsValid(hStorage);
if(hs == NULL)
{
SetLastError(ERROR_INVALID_HANDLE);
@@ -1133,45 +1254,43 @@ bool WINAPI CascGetStorageInfo(
// Differentiate between info classes
switch(InfoClass)
{
- case CascStorageFileCount:
- dwInfoValue = (DWORD)hs->pIndexEntryMap->ItemCount;
- break;
-
- case CascStorageFeatures:
- dwInfoValue |= (hs->pRootHandler->dwRootFlags & ROOT_FLAG_HAS_NAMES) ? CASC_FEATURE_LISTFILE : 0;
+ case CascStorageLocalFileCount:
+ dwInfoValue = (DWORD)hs->LocalFiles;
break;
- case CascStorageGameInfo:
- dwInfoValue = hs->dwGameInfo;
+ case CascStorageTotalFileCount:
+ if(hs->TotalFiles == 0)
+ hs->TotalFiles = GetStorageTotalFileCount(hs);
+ dwInfoValue = (DWORD)hs->TotalFiles;
break;
- case CascStorageGameBuild:
- dwInfoValue = hs->dwBuildNumber;
+ case CascStorageFeatures:
+ dwInfoValue = hs->dwFeatures | hs->pRootHandler->GetFeatures();
break;
case CascStorageInstalledLocales:
dwInfoValue = hs->dwDefaultLocale;
break;
+ case CascStorageProduct:
+ return GetStorageProduct(hs, pvStorageInfo, cbStorageInfo, pcbLengthNeeded);
+
+ case CascStorageTags:
+ return GetStorageTags(hs, pvStorageInfo, cbStorageInfo, pcbLengthNeeded);
+
default:
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
//
- // Return the required DWORD value
+ // Default: return a 32-bit unsigned value
//
- if(cbStorageInfo < sizeof(DWORD))
- {
- *pcbLengthNeeded = sizeof(DWORD);
- SetLastError(ERROR_INSUFFICIENT_BUFFER);
- return false;
- }
-
- // Give the number of files
- *(PDWORD)pvStorageInfo = dwInfoValue;
- return true;
+ PtrOutputValue = (PDWORD)ProbeOutputBuffer(pvStorageInfo, cbStorageInfo, sizeof(DWORD), pcbLengthNeeded);
+ if(PtrOutputValue != NULL)
+ PtrOutputValue[0] = dwInfoValue;
+ return (PtrOutputValue != NULL);
}
bool WINAPI CascCloseStorage(HANDLE hStorage)
@@ -1179,7 +1298,7 @@ bool WINAPI CascCloseStorage(HANDLE hStorage)
TCascStorage * hs;
// Verify the storage handle
- hs = IsValidStorageHandle(hStorage);
+ hs = TCascStorage::IsValid(hStorage);
if(hs == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
@@ -1187,14 +1306,6 @@ bool WINAPI CascCloseStorage(HANDLE hStorage)
}
// Only free the storage if the reference count reaches 0
- if(hs->dwRefCount == 1)
- {
- FreeCascStorage(hs);
- return true;
- }
-
- // Just decrement number of references
- hs->dwRefCount--;
+ hs->Release();
return true;
}
-
diff --git a/dep/CascLib/src/CascPort.h b/dep/CascLib/src/CascPort.h
index 5d9a7104f7b..87a2f2fddbd 100644
--- a/dep/CascLib/src/CascPort.h
+++ b/dep/CascLib/src/CascPort.h
@@ -37,12 +37,20 @@
#define WIN32_LEAN_AND_MEAN
#endif
+//#pragma warning(disable:4995) // warning C4995: 'sprintf': name was marked as #pragma deprecated
+
#include <tchar.h>
#include <assert.h>
+ #include <intrin.h> // Support for intrinsic functions
#include <ctype.h>
+ #include <io.h>
#include <stdio.h>
+ #include <stdlib.h>
+ #include <direct.h>
+ #include <malloc.h>
#include <windows.h>
#include <wininet.h>
+ #include <strsafe.h>
#include <sys/types.h>
#define PLATFORM_LITTLE_ENDIAN
@@ -52,8 +60,10 @@
#define PLATFORM_32BIT
#endif
- #define PATH_SEPARATOR '\\'
- #define CREATE_DIRECTORY(name) CreateDirectory(name, NULL);
+ #define PATH_SEP_CHAR '\\'
+ #define PATH_SEP_STRING "\\"
+
+ #pragma intrinsic(memcmp, memcpy)
#define PLATFORM_WINDOWS
#define PLATFORM_DEFINED // The platform is known now
@@ -75,6 +85,8 @@
#include <dirent.h>
#include <errno.h>
#include <stddef.h>
+ #include <string.h>
+ #include <cassert>
// Support for PowerPC on Max OS X
#if (__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1)
@@ -90,9 +102,9 @@
#define PLATFORM_LITTLE_ENDIAN
#endif
- #define PATH_SEPARATOR '/'
- #define CREATE_DIRECTORY(name) mkdir(name, 0755)
-
+ #define PATH_SEP_CHAR '/'
+ #define PATH_SEP_STRING "/"
+
#define PLATFORM_MAC
#define PLATFORM_DEFINED // The platform is known now
@@ -103,7 +115,6 @@
// Assumption: we are not on Windows nor Macintosh, so this must be linux *grin*
#if !defined(PLATFORM_DEFINED)
-
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
@@ -117,12 +128,13 @@
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
+ #include <wchar.h>
#include <assert.h>
#include <errno.h>
- #define PATH_SEPARATOR '/'
- #define CREATE_DIRECTORY(name) mkdir(name, 0755)
-
+ #define PATH_SEP_CHAR '/'
+ #define PATH_SEP_STRING "/"
+
#define PLATFORM_LITTLE_ENDIAN
#define PLATFORM_LINUX
#define PLATFORM_DEFINED
@@ -145,20 +157,19 @@
typedef unsigned short USHORT;
typedef int LONG;
typedef unsigned int DWORD;
- typedef unsigned long DWORD_PTR;
- typedef long LONG_PTR;
- typedef long INT_PTR;
typedef long long LONGLONG;
typedef unsigned long long ULONGLONG;
typedef unsigned long long *PULONGLONG;
typedef void * HANDLE;
- typedef void * LPOVERLAPPED; // Unsupported on Linux and Mac
typedef char TCHAR;
typedef unsigned int LCID;
typedef LONG * PLONG;
typedef DWORD * PDWORD;
typedef BYTE * LPBYTE;
typedef char * LPSTR;
+ typedef const char * LPCSTR;
+ typedef TCHAR * LPTSTR;
+ typedef const TCHAR * LPCTSTR;
#ifdef PLATFORM_32BIT
#define _LZMA_UINT32_IS_ULONG
@@ -179,7 +190,6 @@
#define _T(x) x
#define _tcslen strlen
- #define _tcscpy strcpy
#define _tcscat strcat
#define _tcschr strchr
#define _tcsrchr strrchr
@@ -187,9 +197,9 @@
#define _tcsspn strspn
#define _tcsncmp strncmp
#define _tprintf printf
- #define _stprintf sprintf
#define _tremove remove
- #define _tmkdir mkdir
+ #define _taccess access
+ #define _access access
#define _stricmp strcasecmp
#define _strnicmp strncasecmp
@@ -212,6 +222,7 @@
#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
#define ERROR_SUCCESS 0
#define ERROR_FILE_NOT_FOUND ENOENT
+ #define ERROR_PATH_NOT_FOUND ENOENT
#define ERROR_ACCESS_DENIED EPERM
#define ERROR_INVALID_HANDLE EBADF
#define ERROR_NOT_ENOUGH_MEMORY ENOMEM
@@ -220,18 +231,34 @@
#define ERROR_DISK_FULL ENOSPC
#define ERROR_ALREADY_EXISTS EEXIST
#define ERROR_INSUFFICIENT_BUFFER ENOBUFS
- #define ERROR_BAD_FORMAT 1000 // No such error code under Linux
- #define ERROR_NO_MORE_FILES 1001 // No such error code under Linux
- #define ERROR_HANDLE_EOF 1002 // No such error code under Linux
- #define ERROR_CAN_NOT_COMPLETE 1003 // No such error code under Linux
- #define ERROR_FILE_CORRUPT 1004 // No such error code under Linux
- #define ERROR_FILE_ENCRYPTED 1005 // Returned by encrypted stream when can't find file key
+ #define ERROR_BAD_FORMAT 1000 // No such error code under Linux
+ #define ERROR_NO_MORE_FILES 1001 // No such error code under Linux
+ #define ERROR_HANDLE_EOF 1002 // No such error code under Linux
+ #define ERROR_CAN_NOT_COMPLETE 1003 // No such error code under Linux
+ #define ERROR_FILE_CORRUPT 1004 // No such error code under Linux
+ #define ERROR_FILE_ENCRYPTED 1005 // Returned by encrypted stream when can't find file key
#endif
#ifndef ERROR_FILE_INCOMPLETE
-#define ERROR_FILE_INCOMPLETE 1006 // The required file part is missing
+#define ERROR_FILE_INCOMPLETE 1006 // The required file part is missing
+#endif
+
+#ifndef ERROR_FILE_OFFLINE
+#define ERROR_FILE_OFFLINE 1007 // The file is not available in the local storage
#endif
+#ifndef ERROR_BUFFER_OVERFLOW
+#define ERROR_BUFFER_OVERFLOW 1008
+#endif
+
+#ifndef ERROR_CANCELLED
+#define ERROR_CANCELLED 1009
+#endif
+
+#ifndef _countof
+#define _countof(x) (sizeof(x) / sizeof(x[0]))
+#endif
+
//-----------------------------------------------------------------------------
// Swapping functions
@@ -273,4 +300,13 @@
#define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b))
#endif
+//-----------------------------------------------------------------------------
+// Forbidden functions, do not use
+
+#ifdef __CASCLIB_SELF__
+#define strcpy UNSAFE_DO_NOT_USE_STRCPY
+#define strcat UNSAFE_DO_NOT_USE_STRCAT
+#define sprintf UNSAFE_DO_NOT_USE_SPRINTF
+#endif
+
#endif // __CASCPORT_H__
diff --git a/dep/CascLib/src/CascReadFile.cpp b/dep/CascLib/src/CascReadFile.cpp
index 1b13e873f02..2720d6d5adb 100644
--- a/dep/CascLib/src/CascReadFile.cpp
+++ b/dep/CascLib/src/CascReadFile.cpp
@@ -1,4 +1,4 @@
-/*****************************************************************************/
+/****************************************************************************/
/* CascOpenFile.cpp Copyright (c) Ladislav Zezula 2014 */
/*---------------------------------------------------------------------------*/
/* System-dependent directory functions for CascLib */
@@ -13,302 +13,453 @@
#include "CascCommon.h"
//-----------------------------------------------------------------------------
-// Local structures
-
-typedef struct _BLTE_FRAME
-{
- BYTE CompressedSize[4]; // Compressed file size as big endian
- BYTE FrameSize[4]; // File size as big endian
- BYTE md5[MD5_HASH_SIZE]; // Hash of the compressed frame
-
-} BLTE_FRAME, *PBLTE_FRAME;
-
-//-----------------------------------------------------------------------------
// Local functions
-TCascFile * IsValidFileHandle(HANDLE hFile); // In CascOpenFile.cpp
-
static int EnsureDataStreamIsOpen(TCascFile * hf)
{
TCascStorage * hs = hf->hs;
TFileStream * pStream = NULL;
+ ULONGLONG EncodedSize = 0;
TCHAR * szDataFile;
- TCHAR szPlainName[0x40];
+ TCHAR szCachePath[MAX_PATH];
+ TCHAR szPlainName[0x80];
+ int nError;
- // If the file is not open yet, do it
- if(hs->DataFileArray[hf->ArchiveIndex] == NULL)
+ // If the file is available locally, we rely on data files.
+ // If not, we download the file and open the stream
+ if(hf->pCKeyEntry->Flags & CASC_CE_FILE_IS_LOCAL)
{
- // Prepare the name of the data file
- _stprintf(szPlainName, _T("data.%03u"), hf->ArchiveIndex);
- szDataFile = CombinePath(hs->szIndexPath, szPlainName);
+ // If the file is not open yet, do it
+ if(hs->DataFiles[hf->ArchiveIndex] == NULL)
+ {
+ // Prepare the name of the data file
+ CascStrPrintf(szPlainName, _countof(szPlainName), _T("data.%03u"), hf->ArchiveIndex);
+ szDataFile = CombinePath(hs->szIndexPath, szPlainName);
- // Open the data file
- if(szDataFile != NULL)
+ // Open the data file
+ if(szDataFile != NULL)
+ {
+ // Open the data stream with read+write sharing to prevent Battle.net agent
+ // detecting a corruption and redownloading the entire package
+ pStream = FileStream_OpenFile(szDataFile, STREAM_FLAG_READ_ONLY | STREAM_FLAG_WRITE_SHARE | STREAM_PROVIDER_FLAT | STREAM_FLAG_FILL_MISSING | BASE_PROVIDER_FILE);
+ hs->DataFiles[hf->ArchiveIndex] = pStream;
+ CASC_FREE(szDataFile);
+ }
+ }
+
+ // Return error or success
+ hf->pStream = hs->DataFiles[hf->ArchiveIndex];
+ return (hf->pStream != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND;
+ }
+ else
+ {
+ if(hf->bDownloadFileIf)
{
- // Open the stream
- pStream = FileStream_OpenFile(szDataFile, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
- hs->DataFileArray[hf->ArchiveIndex] = pStream;
- CASC_FREE(szDataFile);
+ // Create the local folder path and download the file from CDN
+ nError = DownloadFileFromCDN(hf->hs, _T("data"), hf->pCKeyEntry->EKey, NULL, szCachePath, _countof(szCachePath));
+ if(nError == ERROR_SUCCESS)
+ {
+ hf->pStream = FileStream_OpenFile(szCachePath, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT);
+ if(hf->pStream != NULL)
+ {
+ // Supply the file size, if unknown yet
+ if(hf->EncodedSize == CASC_INVALID_SIZE)
+ {
+ FileStream_GetSize(hf->pStream, &EncodedSize);
+ hf->pCKeyEntry->EncodedSize = (DWORD)EncodedSize;
+ hf->EncodedSize = (DWORD)EncodedSize;
+ }
+
+ hf->bLocalFileStream = true;
+ return ERROR_SUCCESS;
+ }
+ }
}
+
+ return ERROR_FILE_OFFLINE;
}
+}
- // Return error or success
- hf->pStream = hs->DataFileArray[hf->ArchiveIndex];
- return (hf->pStream != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND;
+#ifdef _DEBUG
+static unsigned int table_16C57A8[0x10] =
+{
+ 0x049396B8, 0x72A82A9B, 0xEE626CCA, 0x9917754F,
+ 0x15DE40B1, 0xF5A8A9B6, 0x421EAC7E, 0xA9D55C9A,
+ 0x317FD40C, 0x04FAF80D, 0x3D6BE971, 0x52933CFD,
+ 0x27F64B7D, 0xC6F5C11B, 0xD5757E3A, 0x6C388745
+};
+
+// Obtained from Agent.exe v 2.15.0.6296 (d14ec9d9a1b396a42964b05f40ea55f37eae5478d550c07ebb6cb09e50968d62)
+// Note the "Checksum" value probably won't match with older game versions.
+static void VerifyHeaderSpan(PBLTE_ENCODED_HEADER pBlteHeader, ULONGLONG HeaderOffset)
+{
+ LPBYTE pbBlteHeader = (LPBYTE)pBlteHeader;
+ DWORD dwInt32;
+ BYTE EncodedOffset[4] = { 0 };
+ BYTE HashedHeader[4] = { 0 };
+ BYTE JenkinsHash[4];
+ BYTE Checksum[4];
+ size_t i, j;
+
+ // Seems to be hardcoded to zero
+ assert(pBlteHeader->field_15 == 0);
+
+ // Calculate the Jenkins hash and write it to the header
+ dwInt32 = hashlittle(pbBlteHeader, FIELD_OFFSET(BLTE_ENCODED_HEADER, JenkinsHash), 0x3D6BE971);
+ ConvertIntegerToBytes_4_LE(dwInt32, JenkinsHash);
+// assert(memcmp(pBlteHeader->JenkinsHash, JenkinsHash, sizeof(JenkinsHash)) == 0);
+
+ // Encode the lower 32-bits of the offset
+ dwInt32 = (DWORD)(HeaderOffset + FIELD_OFFSET(BLTE_ENCODED_HEADER, Signature));
+ dwInt32 = table_16C57A8[dwInt32 & 0x0F] ^ dwInt32;
+ ConvertIntegerToBytes_4_LE(dwInt32, EncodedOffset);
+
+ // Calculate checksum of the so-far filled structure
+ for (i = 0; i < FIELD_OFFSET(BLTE_ENCODED_HEADER, Checksum); i++)
+ HashedHeader[i & 3] ^= pbBlteHeader[i];
+
+ // XOR the two values together to get the final checksum.
+ for (j = 0; j < 4; j++, i++)
+ Checksum[j] = HashedHeader[i & 3] ^ EncodedOffset[i & 3];
+// assert(memcmp(pBlteHeader->Checksum, Checksum, sizeof(Checksum)) == 0);
}
+#endif
-static int LoadFileFrames(TCascFile * hf)
+static int ParseBlteHeader(TCascFile * hf, ULONGLONG HeaderOffset, LPBYTE pbEncodedBuffer, size_t cbEncodedBuffer, size_t * pcbHeaderSize)
+{
+ PBLTE_ENCODED_HEADER pEncodedHeader = (PBLTE_ENCODED_HEADER)pbEncodedBuffer;
+ PBLTE_HEADER pBlteHeader = (PBLTE_HEADER)pbEncodedBuffer;
+ DWORD ExpectedHeaderSize;
+ DWORD ExHeaderSize = 0;
+ DWORD HeaderSize;
+ DWORD FrameCount = 0;
+
+ CASCLIB_UNUSED(HeaderOffset);
+
+ // On files within storage segments ("data.###"), there is BLTE_ENCODED_HEADER
+ // On local files, there is just PBLTE_HEADER
+ if(ConvertBytesToInteger_4_LE(pBlteHeader->Signature) != BLTE_HEADER_SIGNATURE)
+ {
+ // There must be at least some bytes
+ if (cbEncodedBuffer < FIELD_OFFSET(BLTE_ENCODED_HEADER, MustBe0F))
+ return ERROR_BAD_FORMAT;
+ if (pEncodedHeader->EncodedSize != hf->EncodedSize)
+ return ERROR_BAD_FORMAT;
+
+#ifdef _DEBUG
+ // Not really needed, it's here just for explanation of what the values mean
+ //assert(memcmp(hf->pCKeyEntry->EKey, pEncodedHeader->EKey.Value, MD5_HASH_SIZE) == 0);
+ VerifyHeaderSpan(pEncodedHeader, HeaderOffset);
+#endif
+ // Capture the EKey
+ ExHeaderSize = FIELD_OFFSET(BLTE_ENCODED_HEADER, Signature);
+ pBlteHeader = (PBLTE_HEADER)(pbEncodedBuffer + ExHeaderSize);
+ }
+
+ // Verify the signature
+ if(ConvertBytesToInteger_4_LE(pBlteHeader->Signature) != BLTE_HEADER_SIGNATURE)
+ return ERROR_BAD_FORMAT;
+
+ // Capture the header size. If this is non-zero, then array
+ // of chunk headers follow. Otherwise, the file is just one chunk
+ HeaderSize = ConvertBytesToInteger_4(pBlteHeader->HeaderSize);
+ if (HeaderSize != 0)
+ {
+ if (pBlteHeader->MustBe0F != 0x0F)
+ return ERROR_BAD_FORMAT;
+
+ // Verify the header size
+ FrameCount = ConvertBytesToInteger_3(pBlteHeader->FrameCount);
+ ExpectedHeaderSize = 0x0C + FrameCount * sizeof(BLTE_FRAME);
+ if (ExpectedHeaderSize != HeaderSize)
+ return ERROR_BAD_FORMAT;
+
+ // Give the values
+ pcbHeaderSize[0] = ExHeaderSize + FIELD_OFFSET(BLTE_HEADER, MustBe0F) + sizeof(DWORD);
+ }
+ else
+ {
+ pcbHeaderSize[0] = ExHeaderSize + FIELD_OFFSET(BLTE_HEADER, MustBe0F);
+ }
+
+ // Give the frame count
+ hf->FrameCount = FrameCount;
+ return ERROR_SUCCESS;
+}
+
+static LPBYTE ReadMissingHeaderData(TCascFile * hf, ULONGLONG DataFileOffset, LPBYTE pbEncodedBuffer, size_t cbEncodedBuffer, size_t cbTotalHeaderSize)
+{
+ LPBYTE pbNewBuffer;
+
+ // Reallocate the buffer
+ pbNewBuffer = CASC_REALLOC(BYTE, pbEncodedBuffer, cbTotalHeaderSize);
+ if (pbNewBuffer != NULL)
+ {
+ // Load the missing data
+ DataFileOffset += cbEncodedBuffer;
+ if (FileStream_Read(hf->pStream, &DataFileOffset, pbNewBuffer + cbEncodedBuffer, (DWORD)(cbTotalHeaderSize - cbEncodedBuffer)))
+ {
+ return pbNewBuffer;
+ }
+ }
+
+ // If anything failed, we free the original buffer and return NULL;
+ CASC_FREE(pbEncodedBuffer);
+ return NULL;
+}
+
+static int LoadFileFrames(TCascFile * hf, ULONGLONG DataFileOffset, LPBYTE pbFramePtr, LPBYTE pbFrameEnd, size_t cbHeaderSize)
{
- PBLTE_FRAME pFileFrames;
PBLTE_FRAME pFileFrame;
- ULONGLONG ArchiveFileOffset;
- DWORD FrameOffset = 0;
- DWORD FileSize = 0;
+ DWORD ContentSize = 0;
+ DWORD FileOffset = 0;
int nError = ERROR_SUCCESS;
assert(hf != NULL);
assert(hf->pStream != NULL);
- assert(hf->pFrames != NULL);
+ assert(hf->pFrames == NULL);
- // Allocate frame array
- pFileFrames = pFileFrame = CASC_ALLOC(BLTE_FRAME, hf->FrameCount);
- if(pFileFrames != NULL)
+ if (hf->FrameCount != 0)
{
- // Load the frame array
- ArchiveFileOffset = hf->FramesOffset;
- if(FileStream_Read(hf->pStream, &ArchiveFileOffset, pFileFrames, hf->FrameCount * sizeof(BLTE_FRAME)))
- {
- // Move the raw archive offset
- ArchiveFileOffset += (hf->FrameCount * sizeof(BLTE_FRAME));
+ // Move the raw archive offset
+ DataFileOffset += (hf->FrameCount * sizeof(BLTE_FRAME));
+ // Allocate array of file frames
+ hf->pFrames = CASC_ALLOC(CASC_FILE_FRAME, hf->FrameCount);
+ if (hf->pFrames != NULL)
+ {
// Copy the frames to the file structure
- for(DWORD i = 0; i < hf->FrameCount; i++, pFileFrame++)
+ for (DWORD i = 0; i < hf->FrameCount; i++, pbFramePtr += sizeof(BLTE_FRAME))
{
- hf->pFrames[i].FrameArchiveOffset = (DWORD)ArchiveFileOffset;
- hf->pFrames[i].FrameFileOffset = FrameOffset;
- hf->pFrames[i].CompressedSize = ConvertBytesToInteger_4(pFileFrame->CompressedSize);
- hf->pFrames[i].FrameSize = ConvertBytesToInteger_4(pFileFrame->FrameSize);
- memcpy(hf->pFrames[i].md5, pFileFrame->md5, MD5_HASH_SIZE);
-
- ArchiveFileOffset += hf->pFrames[i].CompressedSize;
- FrameOffset += hf->pFrames[i].FrameSize;
- FileSize += hf->pFrames[i].FrameSize;
+ // Capture the file frame
+ if ((pbFramePtr + sizeof(BLTE_FRAME)) > pbFrameEnd)
+ return ERROR_BAD_FORMAT;
+ pFileFrame = (PBLTE_FRAME)pbFramePtr;
+
+ // Convert the file frame to the native format
+ hf->pFrames[i].DataFileOffset = (DWORD)DataFileOffset;
+ hf->pFrames[i].FileOffset = CASC_INVALID_POS;
+ hf->pFrames[i].EncodedSize = ConvertBytesToInteger_4(pFileFrame->EncodedSize);
+ hf->pFrames[i].ContentSize = ConvertBytesToInteger_4(pFileFrame->ContentSize);
+ hf->pFrames[i].FrameHash = pFileFrame->FrameHash;
+
+ DataFileOffset += hf->pFrames[i].EncodedSize;
+ ContentSize += hf->pFrames[i].ContentSize;
+ FileOffset += hf->pFrames[i].ContentSize;
}
- }
- else
- nError = GetLastError();
- // Note: on ENCODING file, this value is almost always bigger
- // then the real size of ENCODING. We handle this problem
- // by calculating size of the ENCODIG file from its header.
- hf->FileSize = FileSize;
+ // Save the content size of the file
+ if(hf->pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
+ {
+ hf->pCKeyEntry->ContentSize = ContentSize;
+ hf->ContentSize = ContentSize;
+ }
+ }
+ }
+ else
+ {
+ // The content size in the file structure must be valid at this point,
+ // otherwise we don't know the frame content size
+ if (hf->ContentSize == CASC_INVALID_SIZE)
+ {
+ assert(false);
+ return ERROR_CAN_NOT_COMPLETE;
+ }
-#ifdef CASCLIB_TEST
- hf->FileSize_FrameSum = FileSize;
-#endif
+ // Save the number of file frames
+ hf->FrameCount = 1;
- // Free the array
- CASC_FREE(pFileFrames);
+ // Allocate single "dummy" frame
+ hf->pFrames = CASC_ALLOC(CASC_FILE_FRAME, 1);
+ if (hf->pFrames != NULL)
+ {
+ memset(&hf->pFrames->FrameHash, 0, sizeof(CONTENT_KEY));
+ hf->pFrames->DataFileOffset = (DWORD)DataFileOffset;
+ hf->pFrames->FileOffset = CASC_INVALID_POS;
+ hf->pFrames->EncodedSize = (DWORD)(hf->EncodedSize - cbHeaderSize);
+ hf->pFrames->ContentSize = hf->ContentSize;
+ }
}
- else
- nError = ERROR_NOT_ENOUGH_MEMORY;
+ if (hf->pFrames == NULL)
+ nError = ERROR_NOT_ENOUGH_MEMORY;
return nError;
}
-static int EnsureHeaderAreaIsLoaded(TCascFile * hf)
+static int LoadEncodedHeaderAndFileFrames(TCascFile * hf)
{
- TCascStorage * hs = hf->hs;
- ULONGLONG FileOffset = hf->HeaderOffset;
- LPBYTE pbHeaderArea;
- DWORD FileSignature;
- DWORD FileSize;
- BYTE HeaderArea[MAX_HEADER_AREA_SIZE];
- int nError;
-
- // We need the data file to be open
- nError = EnsureDataStreamIsOpen(hf);
- if(nError != ERROR_SUCCESS)
- return nError;
-
- // Make sure that we already know the shift
- // to the begin of file data.
- // Note that older builds of Heroes of the Storm have entries pointing
- // to the beginning of the header area.
- // Newer versions of HOTS have encoding entries pointing directly to
- // the BLTE header
- if(hs->dwFileBeginDelta == 0xFFFFFFFF)
- {
- FileSignature = 0;
- FileOffset = hf->HeaderOffset;
- if(!FileStream_Read(hf->pStream, &FileOffset, &FileSignature, sizeof(DWORD)))
- return ERROR_FILE_CORRUPT;
+ LPBYTE pbEncodedBuffer;
+ size_t cbEncodedBuffer = MAX_ENCODED_HEADER;
+ int nError = ERROR_SUCCESS;
- hs->dwFileBeginDelta = (FileSignature == BLTE_HEADER_SIGNATURE) ? BLTE_HEADER_DELTA : 0;
- }
+ // Should only be called when the file frames are NOT loaded
+ assert(hf->pFrames == NULL);
+ assert(hf->FrameCount == 0);
- // If the file size is not loaded yet, do it
- if(hf->FrameCount == 0)
+ // Allocate the initial buffer for the encoded headers
+ pbEncodedBuffer = CASC_ALLOC(BYTE, MAX_ENCODED_HEADER);
+ if (pbEncodedBuffer != NULL)
{
- // Load the part before BLTE header + header itself
- FileOffset = hf->HeaderOffset - hs->dwFileBeginDelta;
- if(!FileStream_Read(hf->pStream, &FileOffset, HeaderArea, sizeof(HeaderArea)))
- return ERROR_FILE_CORRUPT;
-
- // Copy the MD5 hash of the frame array
- memcpy(hf->FrameArrayHash, HeaderArea, MD5_HASH_SIZE);
- pbHeaderArea = HeaderArea + MD5_HASH_SIZE;
-
- // Copy the file size
- FileSize = ConvertBytesToInteger_4_LE(pbHeaderArea);
- pbHeaderArea += 0x0E;
-
- // Verify the BLTE signature
- if(ConvertBytesToInteger_4_LE(pbHeaderArea) != BLTE_HEADER_SIGNATURE)
- return ERROR_BAD_FORMAT;
- pbHeaderArea += sizeof(DWORD);
+ ULONGLONG ReadOffset = hf->ArchiveOffset;
+ size_t cbTotalHeaderSize;
+ size_t cbHeaderSize = 0;
- // Load the size of the frame headers
- hf->HeaderSize = ConvertBytesToInteger_4(pbHeaderArea);
- if(hf->HeaderSize & 0x80000000)
- return ERROR_BAD_FORMAT;
- pbHeaderArea += sizeof(DWORD);
+ // At this point, we expect encoded size to be known
+ assert(hf->EncodedSize != CASC_INVALID_SIZE);
- // Read the header size
- assert(hs->dwFileBeginDelta <= BLTE_HEADER_DELTA);
- hf->HeaderOffset += (BLTE_HEADER_DELTA - hs->dwFileBeginDelta);
- hf->FrameCount = 1;
+ // Do not read more than encoded size
+ cbEncodedBuffer = CASCLIB_MIN(cbEncodedBuffer, hf->EncodedSize);
- // Retrieve the frame count, if different from 1
- if(hf->HeaderSize != 0)
+ // Load the entire (eventual) header area. This is faster than doing
+ // two read operations in a row. Read as much as possible. If the file is cut,
+ // the FileStream will pad it with zeros
+ if (FileStream_Read(hf->pStream, &ReadOffset, pbEncodedBuffer, (DWORD)cbEncodedBuffer))
{
- // The next byte must be 0x0F
- if(pbHeaderArea[0] != 0x0F)
- return ERROR_BAD_FORMAT;
- pbHeaderArea++;
+ // Parse the BLTE header
+ nError = ParseBlteHeader(hf, ReadOffset, pbEncodedBuffer, cbEncodedBuffer, &cbHeaderSize);
+ if (nError == ERROR_SUCCESS)
+ {
+ // If the headers are larger than the initial read size,
+ // We read the missing data
+ cbTotalHeaderSize = cbHeaderSize + (hf->FrameCount * sizeof(BLTE_FRAME));
+ if (cbTotalHeaderSize > cbEncodedBuffer)
+ {
+ pbEncodedBuffer = ReadMissingHeaderData(hf, ReadOffset, pbEncodedBuffer, cbEncodedBuffer, cbTotalHeaderSize);
+ if (pbEncodedBuffer == NULL)
+ nError = GetLastError();
+ cbEncodedBuffer = cbTotalHeaderSize;
+ }
- // Next three bytes form number of frames
- hf->FrameCount = ConvertBytesToInteger_3(pbHeaderArea);
+ // Load the array of frame headers
+ if (nError == ERROR_SUCCESS)
+ {
+ nError = LoadFileFrames(hf, ReadOffset + cbHeaderSize, pbEncodedBuffer + cbHeaderSize, pbEncodedBuffer + cbEncodedBuffer, cbHeaderSize);
+ }
+ }
+ }
+ else
+ {
+ nError = ERROR_FILE_CORRUPT;
}
-#ifdef CASCLIB_TEST
- hf->FileSize_HdrArea = FileSize;
-#endif
+ // Free the frame buffer
+ CASC_FREE(pbEncodedBuffer);
+ }
+ else
+ {
+ nError = ERROR_NOT_ENOUGH_MEMORY;
}
- return ERROR_SUCCESS;
+ return nError;
}
-static int EnsureFrameHeadersLoaded(TCascFile * hf)
+static int EnsureFileFramesLoaded(TCascFile * hf)
{
- int nError;
-
- // Make sure we have header area loaded
- nError = EnsureHeaderAreaIsLoaded(hf);
- if(nError != ERROR_SUCCESS)
- return nError;
+ int nError = ERROR_SUCCESS;
- // If the frame headers are not loaded yet, do it
+ // If the encoded frames are not loaded, do it now
if(hf->pFrames == NULL)
{
- // Allocate the frame array
- hf->pFrames = CASC_ALLOC(CASC_FILE_FRAME, hf->FrameCount);
- if(hf->pFrames != NULL)
- {
- // Either load the frames from the file or supply them on our own
- if(hf->HeaderSize != 0)
- {
- hf->FramesOffset = hf->HeaderOffset + sizeof(DWORD) + sizeof(DWORD) + sizeof(DWORD);
- nError = LoadFileFrames(hf);
- }
- else
- {
- // Offset of the first frame is right after the file frames
- hf->FramesOffset = hf->HeaderOffset + sizeof(DWORD) + sizeof(DWORD);
-
- hf->pFrames[0].FrameArchiveOffset = hf->FramesOffset;
- hf->pFrames[0].FrameFileOffset = 0;
- hf->pFrames[0].CompressedSize = hf->CompressedSize;
- hf->pFrames[0].FrameSize = hf->FileSize;
- memset(hf->pFrames[0].md5, 0, MD5_HASH_SIZE);
- }
- }
+ // We need the data file to be open
+ nError = EnsureDataStreamIsOpen(hf);
+ if(nError != ERROR_SUCCESS)
+ return nError;
- // Return result
- return (hf->pFrames != NULL) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
+ // Make sure we have header area loaded
+ nError = LoadEncodedHeaderAndFileFrames(hf);
}
- return ERROR_SUCCESS;
+ return nError;
}
-static PCASC_FILE_FRAME FindFileFrame(TCascFile * hf, DWORD FilePointer)
+static int LoadEncodedFrame(TFileStream * pStream, PCASC_FILE_FRAME pFrame, LPBYTE pbEncodedFrame, bool bVerifyIntegrity)
{
- PCASC_FILE_FRAME pFrame = hf->pFrames;
- DWORD FrameBegin;
- DWORD FrameEnd;
-
- // Sanity checks
- assert(hf->pFrames != NULL);
- assert(hf->FrameCount != 0);
+ ULONGLONG FileOffset = pFrame->DataFileOffset;
+ int nError = ERROR_SUCCESS;
- // Find the frame where to read from
- for(DWORD i = 0; i < hf->FrameCount; i++, pFrame++)
+ // Load the encoded frame to memory
+ if(FileStream_Read(pStream, &FileOffset, pbEncodedFrame, pFrame->EncodedSize))
{
- // Does the read request fit into the current frame?
- FrameBegin = pFrame->FrameFileOffset;
- FrameEnd = FrameBegin + pFrame->FrameSize;
- if(FrameBegin <= FilePointer && FilePointer < FrameEnd)
- return pFrame;
+ if (bVerifyIntegrity)
+ {
+ if (!CascVerifyDataBlockHash(pbEncodedFrame, pFrame->EncodedSize, pFrame->FrameHash.Value))
+ nError = ERROR_FILE_CORRUPT;
+ }
+ }
+ else
+ {
+ nError = GetLastError();
}
- // Not found, sorry
- return NULL;
+ return nError;
}
static int ProcessFileFrame(
+ TCascStorage * hs,
LPBYTE pbOutBuffer,
DWORD cbOutBuffer,
LPBYTE pbInBuffer,
DWORD cbInBuffer,
DWORD dwFrameIndex)
{
- LPBYTE pbTempBuffer;
- LPBYTE pbWorkBuffer;
- DWORD cbTempBuffer = CASCLIB_MAX(cbInBuffer, cbOutBuffer);
- DWORD cbWorkBuffer = cbOutBuffer + 1;
+ LPBYTE pbWorkBuffer = NULL;
+ DWORD cbOutBufferExpected = 0;
+ DWORD cbWorkBuffer = 0;
DWORD dwStepCount = 0;
bool bWorkComplete = false;
int nError = ERROR_SUCCESS;
- // Allocate the temporary buffer that will serve as output
- pbWorkBuffer = pbTempBuffer = CASC_ALLOC(BYTE, cbTempBuffer);
- if(pbWorkBuffer == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
// Perform the loop
- for(;;)
+ while(bWorkComplete == false)
{
- // Set the output buffer.
- // Even operations: extract to temporary buffer
- // Odd operations: extract to output buffer
- pbWorkBuffer = (dwStepCount & 0x01) ? pbOutBuffer : pbTempBuffer;
- cbWorkBuffer = (dwStepCount & 0x01) ? cbOutBuffer : cbTempBuffer;
+ // There should never be a 3rd step
+ assert(dwStepCount < 2);
- // Perform the operation specific to the operation ID
+ // Perform the operation specific by the first byte
switch(pbInBuffer[0])
{
case 'E': // Encrypted files
- nError = CascDecrypt(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1, dwFrameIndex);
- bWorkComplete = (nError != ERROR_SUCCESS);
+
+ // The work buffer should not have been allocated by any step
+ assert(pbWorkBuffer == NULL && cbWorkBuffer == 0);
+
+ // Allocate temporary buffer to decrypt into
+ // Example storage: "2016 - WoW/23420", File: "4ee6bc9c6564227f1748abd0b088e950"
+ pbWorkBuffer = CASC_ALLOC(BYTE, cbInBuffer - 1);
+ cbWorkBuffer = cbInBuffer - 1;
+ if(pbWorkBuffer == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Decrypt the stream to the work buffer
+ nError = CascDecrypt(hs, pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1, dwFrameIndex);
+ if(nError != ERROR_SUCCESS)
+ {
+ bWorkComplete = true;
+ break;
+ }
+
+ // When encrypted, there is always one more step after this.
+ // Setup the work buffer as input buffer for the next operation
+ pbInBuffer = pbWorkBuffer;
+ cbInBuffer = cbWorkBuffer;
break;
case 'Z': // ZLIB compressed files
- nError = CascDecompress(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1);
+
+ // If we decompressed less than expected, we simply fill the rest with zeros
+ // Example: INSTALL file from the TACT CASC storage
+ cbOutBufferExpected = cbOutBuffer;
+ nError = CascDecompress(pbOutBuffer, &cbOutBuffer, pbInBuffer + 1, cbInBuffer - 1);
+
+ // We exactly know what the output buffer size will be.
+ // If the uncompressed data is smaller, fill the rest with zeros
+ if(cbOutBuffer < cbOutBufferExpected)
+ memset(pbOutBuffer + cbOutBuffer, 0, (cbOutBufferExpected - cbOutBuffer));
bWorkComplete = true;
break;
case 'N': // Normal stored files
- nError = CascDirectCopy(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1);
+ nError = CascDirectCopy(pbOutBuffer, &cbOutBuffer, pbInBuffer + 1, cbInBuffer - 1);
bWorkComplete = true;
break;
@@ -320,48 +471,131 @@ static int ProcessFileFrame(
break;
}
- // Are we done?
- if(bWorkComplete)
- break;
-
- // Set the input buffer to the work buffer
- pbInBuffer = pbWorkBuffer;
- cbInBuffer = cbWorkBuffer;
+ // Increment the step count
dwStepCount++;
}
- // If the data are currently in the temporary buffer,
- // we need to copy them to output buffer
- if(nError == ERROR_SUCCESS && pbWorkBuffer != pbOutBuffer)
+ // Free the temporary buffer
+ CASC_FREE(pbWorkBuffer);
+ return nError;
+}
+
+static bool GetFileFullInfo(TCascFile * hf, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded)
+{
+ PCASC_FILE_FULL_INFO pFileInfo;
+ PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry;
+ TCascStorage * hs = hf->hs;
+
+ // Verify whether we have enough space in the buffer
+ pFileInfo = (PCASC_FILE_FULL_INFO)ProbeOutputBuffer(pvFileInfo, cbFileInfo, sizeof(CASC_FILE_FULL_INFO), pcbLengthNeeded);
+ if(pFileInfo != NULL)
{
- if(cbWorkBuffer != cbOutBuffer)
- nError = ERROR_INSUFFICIENT_BUFFER;
- memcpy(pbOutBuffer, pbWorkBuffer, cbOutBuffer);
+ // Reset the entire structure
+ CopyMemory16(pFileInfo->CKey, pCKeyEntry->CKey);
+ CopyMemory16(pFileInfo->EKey, pCKeyEntry->EKey);
+ pFileInfo->FileDataId = CASC_INVALID_ID;
+ pFileInfo->LocaleFlags = CASC_INVALID_ID;
+ pFileInfo->ContentFlags = CASC_INVALID_ID;
+
+ // Supply information not depending on root
+ CascStrPrintf(pFileInfo->DataFileName, _countof(pFileInfo->DataFileName), "data.%03u", hf->ArchiveIndex);
+ pFileInfo->StorageOffset = pCKeyEntry->StorageOffset;
+ pFileInfo->SegmentOffset = hf->ArchiveOffset;
+ pFileInfo->FileNameHash = 0;
+ pFileInfo->TagBitMask = pCKeyEntry->TagBitMask;
+ pFileInfo->SegmentIndex = hf->ArchiveIndex;
+ pFileInfo->ContentSize = hf->ContentSize;
+ pFileInfo->EncodedSize = hf->EncodedSize;
+
+ // Supply the root-specific information
+ hs->pRootHandler->GetInfo(pCKeyEntry, pFileInfo);
}
- // Free the temporary buffer
- CASC_FREE(pbTempBuffer);
- return nError;
+ return (pFileInfo != NULL);
}
//-----------------------------------------------------------------------------
// Public functions
+bool WINAPI CascGetFileInfo(HANDLE hFile, CASC_FILE_INFO_CLASS InfoClass, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded)
+{
+ TCascFile * hf;
+ LPBYTE pbOutputValue = NULL;
+ LPBYTE pbInfoValue = NULL;
+ size_t cbInfoValue = 0;
+
+ // Validate the file handle
+ if((hf = TCascFile::IsValid(hFile)) == NULL)
+ {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ // Differentiate between info classes
+ switch(InfoClass)
+ {
+ case CascFileContentKey:
+
+ // Do we have content key at all?
+ if(hf->pCKeyEntry == NULL || (hf->pCKeyEntry->Flags & CASC_CE_HAS_CKEY) == 0)
+ {
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return false;
+ }
+
+ // Give the content key
+ pbInfoValue = hf->pCKeyEntry->CKey;
+ cbInfoValue = CASC_CKEY_SIZE;
+ break;
+
+ case CascFileEncodedKey:
+
+ // Do we have content key at all?
+ if(hf->pCKeyEntry == NULL || (hf->pCKeyEntry->Flags & CASC_CE_HAS_EKEY) == 0)
+ {
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return false;
+ }
+
+ // Give the content key
+ pbInfoValue = hf->pCKeyEntry->EKey;
+ cbInfoValue = CASC_CKEY_SIZE;
+ break;
+
+ case CascFileFullInfo:
+ return GetFileFullInfo(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded);
+
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ // Sanity check
+ assert(pbInfoValue != NULL);
+ assert(cbInfoValue != 0);
+
+ // Give the result
+ pbOutputValue = (LPBYTE)ProbeOutputBuffer(pvFileInfo, cbFileInfo, cbInfoValue, pcbLengthNeeded);
+ if(pbOutputValue != NULL)
+ memcpy(pbOutputValue, pbInfoValue, cbInfoValue);
+ return (pbOutputValue != NULL);
+}
+
//
// THE FILE SIZE PROBLEM
//
// There are members called "FileSize" in many CASC-related structure
// For various files, these variables have different meaning.
//
-// Storage FileName FileSize FrameSum HdrArea IdxEntry EncEntry RootEntry
-// ----------- -------- ---------- -------- -------- -------- -------- ---------
-// HotS(29049) ENCODING 0x0024BA45 - 0x0024b98a 0x0024BA45 0x0024BA45 n/a n/a
-// HotS(29049) ROOT 0x00193340 - 0x00193340 0x0010db65 0x0010db65 0x00193340 n/a
-// HotS(29049) (other) 0x00001080 - 0x00001080 0x000008eb 0x000008eb 0x00001080 0x00001080
-//
-// WoW(18888) ENCODING 0x030d487b - 0x030dee79 0x030d487b 0x030d487b n/a n/a
-// WoW(18888) ROOT 0x016a9800 - n/a 0x0131313d 0x0131313d 0x016a9800 n/a
-// WoW(18888) (other) 0x000007d0 - 0x000007d0 0x00000397 0x00000397 0x000007d0 n/a
+// Storage FileName FileSize FrameSum HdrArea CKeyEntry EKeyEntry RootEntry
+// ----------- -------- ---------- -------- -------- ---------- ---------- ----------
+// HotS(29049) ENCODING 0x0024BA45 - 0x0024b98a 0x0024BA45 n/a 0x0024BA45 n/a
+// HotS(29049) ROOT 0x00193340 - 0x00193340 0x0010db65 0x00193340 0x0010db65 n/a
+// HotS(29049) (other) 0x00001080 - 0x00001080 0x000008eb 0x00001080 0x000008eb 0x00001080
+//
+// WoW(18888) ENCODING 0x030d487b - 0x030dee79 0x030d487b n/a 0x030d487b n/a
+// WoW(18888) ROOT 0x016a9800 - n/a 0x0131313d 0x016a9800 0x0131313d n/a
+// WoW(18888) (other) 0x000007d0 - 0x000007d0 0x00000397 0x000007d0 0x00000397 n/a
//
DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh)
@@ -372,24 +606,32 @@ DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh)
CASCLIB_UNUSED(pdwFileSizeHigh);
// Validate the file handle
- if((hf = IsValidFileHandle(hFile)) == NULL)
+ if((hf = TCascFile::IsValid(hFile)) == NULL)
{
SetLastError(ERROR_INVALID_HANDLE);
return CASC_INVALID_SIZE;
}
- // Make sure that the file header area is loaded
- nError = EnsureFrameHeadersLoaded(hf);
- if(nError != ERROR_SUCCESS)
+ // Someone may have provided file content size.
+ // If yes, do not load the frames, as it's not necessary.
+ if(hf->ContentSize == CASC_INVALID_SIZE)
{
- SetLastError(nError);
- return CASC_INVALID_SIZE;
+ // Make sure that the file header area is loaded
+ nError = EnsureFileFramesLoaded(hf);
+ if(nError != ERROR_SUCCESS)
+ {
+ SetLastError(nError);
+ return CASC_INVALID_SIZE;
+ }
+
+ // The content size should be loaded from the frames
+ assert(hf->ContentSize != CASC_INVALID_SIZE);
}
// Give the file size to the caller
if(pdwFileSizeHigh != NULL)
*pdwFileSizeHigh = 0;
- return hf->FileSize;
+ return hf->ContentSize;
}
DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod)
@@ -400,7 +642,7 @@ DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHig
DWORD dwFilePosHi;
// If the hFile is not a valid file handle, return an error.
- hf = IsValidFileHandle(hFile);
+ hf = TCascFile::IsValid(hFile);
if(hf == NULL)
{
SetLastError(ERROR_INVALID_HANDLE);
@@ -419,7 +661,7 @@ DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHig
break;
case FILE_END:
- FilePosition = hf->FileSize;
+ FilePosition = hf->ContentSize;
break;
default:
@@ -458,16 +700,7 @@ DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHig
bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDWORD pdwBytesRead)
{
- PCASC_FILE_FRAME pFrame = NULL;
- ULONGLONG StreamSize;
- ULONGLONG FileOffset;
TCascFile * hf;
- LPBYTE pbBuffer = (LPBYTE)pvBuffer;
- DWORD dwStartPointer = 0;
- DWORD dwFilePointer = 0;
- DWORD dwEndPointer = 0;
- DWORD dwFrameSize;
- bool bReadResult;
int nError = ERROR_SUCCESS;
// The buffer must be valid
@@ -478,7 +711,7 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW
}
// Validate the file handle
- if((hf = IsValidFileHandle(hFile)) == NULL)
+ if((hf = TCascFile::IsValid(hFile)) == NULL)
{
SetLastError(ERROR_INVALID_HANDLE);
return false;
@@ -487,134 +720,116 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW
// If the file frames are not loaded yet, do it now
if(nError == ERROR_SUCCESS)
{
- nError = EnsureFrameHeadersLoaded(hf);
+ nError = EnsureFileFramesLoaded(hf);
}
// If the file position is at or beyond end of file, do nothing
- if(nError == ERROR_SUCCESS && hf->FilePointer >= hf->FileSize)
+ if(nError == ERROR_SUCCESS)
{
- *pdwBytesRead = 0;
- return true;
+ // Check the starting position
+ if(hf->FilePointer >= hf->ContentSize)
+ {
+ *pdwBytesRead = 0;
+ return true;
+ }
+
+ // Check the ending position
+ if((hf->FilePointer + dwBytesToRead) > hf->ContentSize)
+ {
+ dwBytesToRead = hf->ContentSize - hf->FilePointer;
+ }
}
- // Find the file frame where to read from
+ // Allocate cache buffer for the entire file. This is the fastest approach
+ // (without reallocations). However, this may consume quite a lot of memory
+ // (Storage: "2016 - Starcraft II/45364", file: "3d815f40c0413701aa2bd214070d0062"
+ // needs 0x239a09b3 bytes of memory (~600 MB)
if(nError == ERROR_SUCCESS)
{
- // Get the frame
- pFrame = FindFileFrame(hf, hf->FilePointer);
- if(pFrame == NULL || pFrame->CompressedSize < 1)
- nError = ERROR_FILE_CORRUPT;
+ if(hf->pbFileCache == NULL)
+ {
+ // Allocate buffer
+ hf->pbFileCache = CASC_ALLOC(BYTE, hf->ContentSize);
+ hf->cbFileCache = hf->ContentSize;
+ if(hf->pbFileCache == NULL)
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ }
}
- // Perform the read
+ // Load all frames that are not loaded yet
if(nError == ERROR_SUCCESS)
{
- // If not enough bytes in the file remaining, cut them
- dwStartPointer = dwFilePointer = hf->FilePointer;
- dwEndPointer = dwStartPointer + dwBytesToRead;
- if(dwEndPointer > hf->FileSize)
- dwEndPointer = hf->FileSize;
-
- // Perform block read from each file frame
- while(dwFilePointer < dwEndPointer)
+ PCASC_FILE_FRAME pFrame = hf->pFrames;
+ DWORD StartFrameOffset = 0;
+ DWORD StartReadOffset = hf->FilePointer;
+ DWORD EndReadOffset = hf->FilePointer + dwBytesToRead;
+
+ for(DWORD i = 0; (i < hf->FrameCount) && (nError == ERROR_SUCCESS); i++, pFrame++)
{
- LPBYTE pbFrameData = NULL;
- DWORD dwFrameStart = pFrame->FrameFileOffset;
- DWORD dwFrameEnd = pFrame->FrameFileOffset + pFrame->FrameSize;
+ LPBYTE pbDecodedFrame = hf->pbFileCache + StartFrameOffset;
+ LPBYTE pbEncodedFrame;
+ DWORD EndFrameOffset = StartFrameOffset + pFrame->ContentSize;
- // Shall we populate the cache with a new data?
- if(dwFrameStart != hf->CacheStart || hf->CacheEnd != dwFrameEnd)
+ // Does that frame belong to the range?
+ if(StartReadOffset < EndFrameOffset && EndReadOffset > StartFrameOffset)
{
- // Shall we reallocate the cache buffer?
- if(pFrame->FrameSize > hf->cbFileCache)
- {
- if(hf->pbFileCache != NULL)
- CASC_FREE(hf->pbFileCache);
-
- hf->pbFileCache = CASC_ALLOC(BYTE, pFrame->FrameSize);
- hf->cbFileCache = pFrame->FrameSize;
- }
-
- // We also need to allocate buffer for the raw data
- pbFrameData = CASC_ALLOC(BYTE, pFrame->CompressedSize);
- if(pbFrameData == NULL)
- {
- nError = ERROR_NOT_ENOUGH_MEMORY;
- break;
- }
-
- // Load the raw file data to memory
- FileOffset = pFrame->FrameArchiveOffset;
- bReadResult = FileStream_Read(hf->pStream, &FileOffset, pbFrameData, pFrame->CompressedSize);
-
- // Note: The raw file data size could be less than expected
- // Happened in WoW build 19342 with the ROOT file. MD5 in the frame header
- // is zeroed, which means it should not be checked
- // Frame File: data.029
- // Frame Offs: 0x013ED9F0 size 0x01325B32
- // Frame End: 0x02713522
- // File Size: 0x027134FC
- if(bReadResult == false && GetLastError() == ERROR_HANDLE_EOF && !IsValidMD5(pFrame->md5))
+ // Is the frame already loaded?
+ if (pFrame->FileOffset == CASC_INVALID_POS)
{
- // Get the size of the remaining file
- FileStream_GetSize(hf->pStream, &StreamSize);
- dwFrameSize = (DWORD)(StreamSize - FileOffset);
-
- // If the frame offset is before EOF and frame end is beyond EOF, correct it
- if(FileOffset < StreamSize && dwFrameSize < pFrame->CompressedSize)
+ // Allocate space for the encoded frame
+ pbEncodedFrame = CASC_ALLOC(BYTE, pFrame->EncodedSize);
+ if (pbEncodedFrame != NULL)
{
- memset(pbFrameData + dwFrameSize, 0, (pFrame->CompressedSize - dwFrameSize));
- bReadResult = true;
+ // Load the encoded frame data
+ nError = LoadEncodedFrame(hf->pStream, pFrame, pbEncodedFrame, hf->bVerifyIntegrity);
+ if (nError == ERROR_SUCCESS)
+ {
+ // Decode the frame
+ nError = ProcessFileFrame(hf->hs,
+ pbDecodedFrame,
+ pFrame->ContentSize,
+ pbEncodedFrame,
+ pFrame->EncodedSize,
+ (DWORD)(pFrame - hf->pFrames));
+ if (nError == ERROR_SUCCESS)
+ {
+ // Mark the frame as loaded
+ pFrame->FileOffset = StartFrameOffset;
+ }
+ }
+
+ // Free the frame buffer
+ CASC_FREE(pbEncodedFrame);
}
- }
-
- // If the read result failed, we cannot finish reading it
- if(bReadResult && VerifyDataBlockHash(pbFrameData, pFrame->CompressedSize, pFrame->md5))
- {
- // Convert the source frame to the file cache
- nError = ProcessFileFrame(hf->pbFileCache,
- pFrame->FrameSize,
- pbFrameData,
- pFrame->CompressedSize,
- (DWORD)(pFrame - hf->pFrames));
- if(nError == ERROR_SUCCESS)
+ else
{
- // Set the start and end of the cache
- hf->CacheStart = dwFrameStart;
- hf->CacheEnd = dwFrameEnd;
+ nError = ERROR_NOT_ENOUGH_MEMORY;
}
}
- else
- {
- nError = ERROR_FILE_CORRUPT;
- }
-
- // Free the raw frame data
- CASC_FREE(pbFrameData);
}
- // Copy the decompressed data
- if(dwFrameEnd > dwEndPointer)
- dwFrameEnd = dwEndPointer;
- memcpy(pbBuffer, hf->pbFileCache + (dwFilePointer - dwFrameStart), (dwFrameEnd - dwFilePointer));
- pbBuffer += (dwFrameEnd - dwFilePointer);
-
- // Move pointers
- dwFilePointer = dwFrameEnd;
- pFrame++;
+ // If the frame start is past the read offset, stop the loop
+ if ((StartFrameOffset + pFrame->ContentSize) >= EndReadOffset)
+ break;
+ StartFrameOffset += pFrame->ContentSize;
}
}
- // Update the file position
+ // Now all frames have been loaded into the cache; copy the entire block to the output buffer
if(nError == ERROR_SUCCESS)
{
+ // Copy the entire data
+ memcpy(pvBuffer, hf->pbFileCache + hf->FilePointer, dwBytesToRead);
+ hf->FilePointer += dwBytesToRead;
+
+ // Give the number of bytes read
if(pdwBytesRead != NULL)
- *pdwBytesRead = (dwFilePointer - dwStartPointer);
- hf->FilePointer = dwFilePointer;
+ *pdwBytesRead = dwBytesToRead;
+ return true;
}
-
- if(nError != ERROR_SUCCESS)
+ else
+ {
SetLastError(nError);
- return (nError == ERROR_SUCCESS);
+ return false;
+ }
}
-
diff --git a/dep/CascLib/src/CascRootFile_Diablo3.cpp b/dep/CascLib/src/CascRootFile_Diablo3.cpp
index f695ad978b1..bbe369c7646 100644
--- a/dep/CascLib/src/CascRootFile_Diablo3.cpp
+++ b/dep/CascLib/src/CascRootFile_Diablo3.cpp
@@ -20,61 +20,36 @@
#define DIABLO3_SUBDIR_SIGNATURE 0xEAF1FE87
#define DIABLO3_PACKAGES_SIGNATURE 0xAABB0002
#define DIABLO3_MAX_SUBDIRS 0x20
-
-#define DIABLO3_INVALID_INDEX 0xFFFFFFFF
-#define DIABLO3_INVALID_FILE 0xFFFFFFFF
#define DIABLO3_MAX_ASSETS 70 // Maximum possible number of assets
-#define DIABLO3_MAX_LEVEL0_LENGTH 0x10 // Maximum length of the level-0 directory name
-
-#define INVALID_FILE_INDEX 0xFFFFFFFF
-#define INVALID_ASSET_INDEX 0xFF
-
-#define ENTRY_FLAG_DIRECTORY_ENTRY 0x80 // The file is actually a directory entry
-#define ENTRY_FLAG_PLAIN_NAME 0x01 // If set, the file entry contains offset of the plain file name
-#define ENTRY_FLAG_FULL_NAME 0x02 // If set, the file entry contains offset of the full name
-#define ENTRY_FLAG_FLAGS_MASK 0xF0 // Mask for the entry flags
-#define ENTRY_FLAG_NAME_MASK 0x0F // Mask for the entry file name type
-
-// Values for CASC_FILE_ENTRY::dwFlags
-#define CASC_ENTRY_SHORT_NAME 0x000000001 // If set, the name is in format XXYYplain-name[\sub-index].ext
-#define CASC_ENTRY_HAS_SUBINDEX 0x000000002 // If set, the subitem is present in the file name (i.e. XXYYplain-name\sub-index.ext)
-
-#define SEARCH_PHASE_NAMES 0 // Searching named entry
-#define SEARCH_PHASE_FILE_IDS 1 // Searching filed by ID
-
-// Macro for constructing 64-bit integer from root-index, file-index and sub-index
-// The result value is RRAAAAAAAASSSSSS
-#define MAKE_INDEX64(ri, fi, si) (((ULONGLONG)ri << 0x38) | ((ULONGLONG)fi << 0x18) | ((ULONGLONG)si))
-#define INDEX64_ROOT_INDEX(hash) (DWORD)((hash >> 0x38) & 0x000000FF)
-#define INDEX64_FILE_INDEX(hash) (DWORD)((hash >> 0x18) & 0xFFFFFFFF)
-#define INDEX64_SUB_INDEX(hash) (DWORD)((hash >> 0x00) & 0x00FFFFFF)
+#define DIABLO3_MAX_ROOT_FOLDERS 0x20 // Maximum count of root directory named entries
// On-disk structure for a file given by file number
-typedef struct _DIABLO3_FILEID1_ENTRY
+typedef struct _DIABLO3_ASSET_ENTRY
{
- ENCODING_KEY EncodingKey; // Encoding key for the file
- DWORD FileIndex; // File index
-} DIABLO3_FILEID1_ENTRY, *PDIABLO3_FILEID1_ENTRY;
+ CONTENT_KEY CKey; // Content key for the file
+ DWORD FileIndex; // File index
+} DIABLO3_ASSET_ENTRY, *PDIABLO3_ASSET_ENTRY;
// On-disk structure for a file given by file number and suffix
-typedef struct _DIABLO3_FILEID2_ENTRY
+typedef struct _DIABLO3_ASSETIDX_ENTRY
{
- ENCODING_KEY EncodingKey; // Encoding key for the file
- DWORD FileIndex; // File index
- DWORD SubIndex; // File subindex, like "SoundBank\3D Ambience\0000.smp"
-} DIABLO3_FILEID2_ENTRY, *PDIABLO3_FILEID2_ENTRY;
+ CONTENT_KEY CKey; // Content key for the file
+ DWORD FileIndex; // File index
+ DWORD SubIndex; // File subindex, like "SoundBank\3D Ambience\0000.smp"
+} DIABLO3_ASSETIDX_ENTRY, *PDIABLO3_ASSETIDX_ENTRY;
-// On-disk structure of the named entry
+// In-memory structure of the named entry
typedef struct _DIABLO3_NAMED_ENTRY
{
- ENCODING_KEY EncodingKey; // Encoding key for the file
- BYTE szFileName[1]; // ASCIIZ file name (variable length)
+ PCONTENT_KEY pCKey; // Pointer to the content key
+ const char * szFileName; // Pointer to the zero-terminated file name
+ const char * szFileEnd; // Position of the zero terminator (aka end of the file name)
} DIABLO3_NAMED_ENTRY, *PDIABLO3_NAMED_ENTRY;
// On-disk structure of CoreToc.dat header
typedef struct _DIABLO3_CORE_TOC_HEADER
{
- DWORD EntryCounts[DIABLO3_MAX_ASSETS]; // Array of number of entries (files) for each asset (level-1 directory)
+ DWORD EntryCounts[DIABLO3_MAX_ASSETS]; // Array of number of entries (files) for each asset
DWORD EntryOffsets[DIABLO3_MAX_ASSETS]; // Array of offsets of each DIABLO3_CORE_TOC_ENTRY, relative to data after header
DWORD Unknowns[DIABLO3_MAX_ASSETS]; // Unknown
DWORD Alignment;
@@ -89,33 +64,19 @@ typedef struct _DIABLO3_CORE_TOC_ENTRY
} DIABLO3_CORE_TOC_ENTRY, *PDIABLO3_CORE_TOC_ENTRY;
-// In-memory structure of parsed directory header
-typedef struct _DIABLO3_DIR_HEADER
-{
- LPBYTE pbEntries1;
- LPBYTE pbEntries2;
- LPBYTE pbEntries3;
- DWORD dwEntries1;
- DWORD dwEntries2;
- DWORD dwEntries3;
-} DIABLO3_DIR_HEADER, *PDIABLO3_DIR_HEADER;
-
-// In-memory structure of loaded CoreTOC.dat
-typedef struct _DIABLO3_CORE_TOC
+// In-memory structure of parsed directory data
+typedef struct _DIABLO3_DIRECTORY
{
- DIABLO3_CORE_TOC_HEADER Hdr; // Header of CoreTOC.dat
-
- LPBYTE pbCoreToc; // Content of the CoreTOC.dat file
- DIABLO3_CORE_TOC_ENTRY Entries[1]; // Buffer for storing the entries (variable length)
-
-} DIABLO3_CORE_TOC, *PDIABLO3_CORE_TOC;
-
-// On-disk structure of Packages.dat header
-typedef struct _DIABLO3_PACKAGES_DAT_HEADER
-{
- DWORD Signature;
- DWORD NumberOfNames;
-} DIABLO3_PACKAGES_DAT_HEADER, *PDIABLO3_PACKAGES_DAT_HEADER;
+ LPBYTE pbDirectoryData; // The begin of the directory data block
+ LPBYTE pbDirectoryEnd; // The end of the directory data block
+ LPBYTE pbAssetEntries; // Pointer to asset entries without subitem number. Example: "SoundBank\SoundFile.smp"
+ LPBYTE pbAssetIdxEntries; // Pointer to asset entries with subitem number
+ LPBYTE pbNamedEntries; // Pointer to named entries. These are for files with arbitrary names, and they do not belong to an asset
+ DWORD dwAssetEntries; // Number of asset entries without subitem number
+ DWORD dwAssetIdxEntries;
+ DWORD dwNamedEntries;
+ DWORD dwNodeIndex; // Index of file node for this folder
+} DIABLO3_DIRECTORY, *PDIABLO3_DIRECTORY;
// Structure for conversion DirectoryID -> Directory name
typedef struct _DIABLO3_ASSET_INFO
@@ -126,35 +87,6 @@ typedef struct _DIABLO3_ASSET_INFO
} DIABLO3_ASSET_INFO;
typedef const DIABLO3_ASSET_INFO * PDIABLO3_ASSET_INFO;
-// In-memory structure of a file entry in the linear file list
-typedef struct _CASC_FILE_ENTRY
-{
- ENCODING_KEY EncodingKey; // Encoding key
- ULONGLONG FileNameHash; // Hash of the full file name
- DWORD dwFileName; // Offset of the name (in name's dynamic array)
- DWORD dwFlags; // Entry flags (see CASC_ENTRY_XXXX)
-
- DWORD NameOffset; // Offset of the name (in name's dynamic array)
- USHORT SubIndex; // File\SubFile index
- BYTE AssetIndex; // Asset index (aka directory index)
- BYTE EntryFlags; // Entry flags
-} CASC_FILE_ENTRY, *PCASC_FILE_ENTRY;
-
-//-----------------------------------------------------------------------------
-// Structure definitions for Diablo3 root file
-
-struct TRootHandler_Diablo3 : public TRootHandler
-{
- // Linear global list of all files
- DYNAMIC_ARRAY FileTable;
-
- // Linear global list of names
- DYNAMIC_ARRAY FileNames;
-
- // Global map of FileName -> FileEntry
- PCASC_MAP pRootMap;
-};
-
//-----------------------------------------------------------------------------
// Local variables
@@ -231,969 +163,727 @@ static const DIABLO3_ASSET_INFO Assets[] =
{"Accolade", "aco"}, // 0x42
};
-static const DIABLO3_ASSET_INFO UnknownAsset = {"Unknown", "unk"};
-
#define DIABLO3_ASSET_COUNT (sizeof(Assets) / sizeof(Assets[0]))
//-----------------------------------------------------------------------------
-// Local functions
-
-static PDIABLO3_ASSET_INFO GetAssetInfo(DWORD dwAssetIndex)
-{
- if(dwAssetIndex < DIABLO3_ASSET_COUNT && Assets[dwAssetIndex].szDirectoryName != NULL)
- return &Assets[dwAssetIndex];
- return &UnknownAsset;
-}
-
-static DWORD VerifyNamedFileEntry(LPBYTE pbNamedEntry, LPBYTE pbFileEnd)
-{
- LPBYTE pbFileName = ((PDIABLO3_NAMED_ENTRY)pbNamedEntry)->szFileName;
-
- // Find the end of the name
- while(pbFileName < pbFileEnd && pbFileName[0] != 0)
- pbFileName++;
+// Handler definitions for Diablo3 root file
- // Did we get past the end of the root file?
- if(pbFileName >= pbFileEnd)
- return 0;
- pbFileName++;
-
- // Return the length of the structure
- return (DWORD)(pbFileName - pbNamedEntry);
-}
-
-static char * FindPackageName(
- PCASC_MAP pPackageMap,
- const char * szAssetName,
- const char * szPlainName)
+struct TDiabloRoot : public TFileTreeRoot
{
- char szFileName[MAX_PATH+1];
- size_t nLength;
+ public:
- // Construct the name without extension and find it in the map
- nLength = sprintf(szFileName, "%s\\%s", szAssetName, szPlainName);
- return (char *)Map_FindString(pPackageMap, szFileName, szFileName + nLength);
-}
+ TDiabloRoot() : TFileTreeRoot(0)
+ {
+ memset(RootFolders, 0, sizeof(RootFolders));
+ pFileIndices = NULL;
+ pbCoreTocFile = NULL;
+ pbCoreTocData = NULL;
+ nFileIndices = 0;
+ cbCoreTocFile = 0;
+
+ // Map for searching a real file extension
+ memset(&PackagesMap, 0, sizeof(CASC_MAP));
+ pbPackagesDat = NULL;
+ cbPackagesDat = 0;
+
+ // We have file names and return CKey as result of search
+ dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY);
+ }
-static size_t CreateShortName(
- PCASC_MAP pPackageMap,
- DWORD dwRootIndex, // Level-0-dir: Index of the root subdirectory
- DWORD dwAssetIndex, // Level-1-dir: Index of the asset name
- const char * szPlainName, // Plain name of the file, without extension
- DWORD dwSubIndex,
- char * szBuffer)
-{
- PDIABLO3_ASSET_INFO pAssetInfo = GetAssetInfo(dwAssetIndex);
- const char * szPackageName = NULL;
- const char * szFormat;
- size_t nLength;
-
- // Write the level-0 directory index as 2-digit hexa number
- assert(dwRootIndex < 0x100);
- *szBuffer++ = IntToHexChar[dwRootIndex >> 0x04];
- *szBuffer++ = IntToHexChar[dwRootIndex & 0x0F];
-
- // Write the level-1 directory index as 2-digit hexa number
- assert(dwAssetIndex < 0x100);
- *szBuffer++ = IntToHexChar[dwAssetIndex >> 0x04];
- *szBuffer++ = IntToHexChar[dwAssetIndex & 0x0F];
-
- // Construct the file name with ending "." for extension
- szFormat = (dwSubIndex != DIABLO3_INVALID_INDEX) ? "%s\\%04u." : "%s.";
- nLength = sprintf(szBuffer, szFormat, szPlainName, dwSubIndex);
-
- // Try to fixup the file extension from the package name.
- // File extensions are not predictable because for subitems,
- // they are not always equal to the main items:
- //
- // SoundBank\3D Ambience.sbk
- // SoundBank\3D Ambience\0000.smp
- // SoundBank\3D Ambience\0002.smp
- // ...
- // SoundBank\Angel.sbk
- // SoundBank\Angel\0000.fsb
- // SoundBank\Angel\0002.fsb
- //
- // We use the Base\Data_D3\PC\Misc\Packages.dat for real file extensions, where possible
- //
- if(pPackageMap != NULL)
+ ~TDiabloRoot()
{
- // Retrieve the asset name
- szPackageName = FindPackageName(pPackageMap, pAssetInfo->szDirectoryName, szBuffer);
- if(szPackageName != NULL)
- {
- strcpy(szBuffer, szPackageName + strlen(pAssetInfo->szDirectoryName) + 1);
- nLength = strlen(szBuffer);
- }
+ FreeLoadingStuff();
}
- // If we havent't found the package, we either use the default asset extension or "unk"
- if(szPackageName == NULL)
+ void AppendBackslashToTotalPath(PATH_BUFFER & PathBuffer)
{
- if(dwSubIndex == DIABLO3_INVALID_INDEX)
+ if(PathBuffer.szPtr < PathBuffer.szEnd)
{
- strcpy(szBuffer + nLength, pAssetInfo->szExtension);
- nLength += strlen(pAssetInfo->szExtension);
- }
- else
- {
- strcpy(szBuffer + nLength, "unk");
- nLength += 3;
+ PathBuffer.szPtr[0] = '\\';
+ PathBuffer.szPtr++;
}
}
- // Return the length of the short file name
- return nLength + 4;
-}
-
-static size_t CreateFileName(
- TRootHandler_Diablo3 * pRootHandler,
- const char * szShortName, // Short file name of the file
- char * szBuffer)
-{
- PCASC_FILE_ENTRY pRootEntry;
- const char * szNameLevel0;
- const char * szNameLevel1 = NULL;
- DWORD dwRootIndex0 = 0;
- DWORD dwAssetIndex = 0;
-
- // Retrieve the level-0 and level-1 directory indexes
- ConvertStringToInt08(szShortName+0, &dwRootIndex0);
- ConvertStringToInt08(szShortName+2, &dwAssetIndex);
-
- // Retrieve the name of the level-0 directory (aka root subdirectory)
- pRootEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, dwRootIndex0);
- szNameLevel0 = (char *)Array_ItemAt(&pRootHandler->FileNames, pRootEntry->dwFileName);
-
- // Retrieve the name of the level-1 directory (aka asset name)
- if(dwAssetIndex < DIABLO3_ASSET_COUNT)
- szNameLevel1 = Assets[dwAssetIndex].szDirectoryName;
- if(szNameLevel1 == NULL)
- szNameLevel1 = UnknownAsset.szDirectoryName;
-
- // Copy the rest of the name as-is
- return sprintf(szBuffer, "%s\\%s\\%s", szNameLevel0, szNameLevel1, szShortName + 4);
-}
-
-
-// Creates a map of String -> Pointer
-static PCASC_MAP CreatePackageMap(
- LPBYTE pbPackagesDat,
- LPBYTE pbPackagesEnd)
-{
- PDIABLO3_PACKAGES_DAT_HEADER pDatHeader = (PDIABLO3_PACKAGES_DAT_HEADER)pbPackagesDat;
- PCASC_MAP pPackageMap;
-
- // Get the header
- if((pbPackagesDat + sizeof(DIABLO3_PACKAGES_DAT_HEADER)) >= pbPackagesEnd)
- return NULL;
- pbPackagesDat += sizeof(DIABLO3_PACKAGES_DAT_HEADER);
-
- // Check the signature and name count
- if(pDatHeader->Signature != DIABLO3_PACKAGES_SIGNATURE)
- return NULL;
-
- // Create the map for fast search of the file name
- pPackageMap = Map_Create(pDatHeader->NumberOfNames, KEY_LENGTH_STRING, 0);
- if(pPackageMap != NULL)
+ void AppendPathToTotalPath(PATH_BUFFER & PathBuffer, const char * szFileName, const char * szFileEnd)
{
- char * szFileName = (char *)pbPackagesDat;
+ char * szPathPtr = PathBuffer.szPtr;
+ size_t nLength = (szFileEnd - szFileName);
- // Go as long as there is something
- for(DWORD i = 0; i < pDatHeader->NumberOfNames; i++)
+ // Append the name
+ if((szPathPtr + nLength) < PathBuffer.szEnd)
{
- // Get the file extension
- if((LPBYTE)szFileName >= pbPackagesEnd)
- break;
-
- // Insert the file name to the map. The file extension is not included
- Map_InsertString(pPackageMap, szFileName, true);
- szFileName = szFileName + strlen(szFileName) + 1;
+ memcpy(szPathPtr, szFileName, nLength);
+ szPathPtr += nLength;
}
- }
-
- return pPackageMap;
-}
-
-// Insert an entry with file name as-is
-static int InsertFileEntry(
- TRootHandler_Diablo3 * pRootHandler,
- ENCODING_KEY & EncodingKey,
- const char * szFileName,
- size_t cchFileName)
-{
- PCASC_FILE_ENTRY pFileEntry;
- // We must not allow the file name array to be reallocated.
- // Reallocating the array would cause pointers in TRootHandler_Diablo3::pRootMap
- // become invalid
- if(pRootHandler->FileTable.ItemCount >= pRootHandler->FileTable.ItemCountMax)
- {
- assert(false);
- return ERROR_NOT_ENOUGH_MEMORY;
+ // Append zero terminator
+ if(szPathPtr < PathBuffer.szEnd)
+ szPathPtr[0] = 0;
+ PathBuffer.szPtr = szPathPtr;
}
- // Insert the plain name to the root handler's global name list
- szFileName = (const char *)Array_Insert(&pRootHandler->FileNames, szFileName, cchFileName);
- if(szFileName == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Make sure that we don't exceed the file limit at this phase
- pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1);
- assert(pFileEntry != NULL);
-
- // Store the info into the file entry
- pFileEntry->EncodingKey = EncodingKey;
- pFileEntry->FileNameHash = CalcFileNameHash(szFileName);
- pFileEntry->dwFileName = (DWORD)Array_IndexOf(&pRootHandler->FileNames, szFileName);
- pFileEntry->dwFlags = 0;
-
- // Verify collisions (debug version only)
- assert(Map_FindObject(pRootHandler->pRootMap, &pFileEntry->FileNameHash, NULL) == NULL);
-
- // Calculate the file name hash
- Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash);
-
- // Success
- return ERROR_SUCCESS;
-}
-
-static int ParseDirEntries_FileId1(
- TRootHandler_Diablo3 * pRootHandler,
- LPBYTE pbFileEntries,
- DWORD dwFileEntries,
- DWORD dwRootDirIndex)
-{
- PDIABLO3_FILEID1_ENTRY pEntry = (PDIABLO3_FILEID1_ENTRY)pbFileEntries;
- PCASC_FILE_ENTRY pFileEntry;
-
- // Overflow test
- if((pRootHandler->FileTable.ItemCount + dwFileEntries) >= pRootHandler->FileTable.ItemCountMax)
+ PDIABLO3_ASSET_INFO GetAssetInfo(DWORD dwAssetIndex)
{
- assert(false);
- return ERROR_NOT_ENOUGH_MEMORY;
+ if(dwAssetIndex < DIABLO3_ASSET_COUNT && Assets[dwAssetIndex].szDirectoryName != NULL)
+ return &Assets[dwAssetIndex];
+ return NULL;
}
- // Parse the all ID1 entries in the file
- for(DWORD i = 0; i < dwFileEntries; i++, pEntry++)
+ char * FindPackageName(const char * szAssetName, const char * szPlainName)
{
- // Insert the file entry to the global list
- pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1);
- assert(pFileEntry != NULL);
-
- // Fill the index entry
- pFileEntry->EncodingKey = pEntry->EncodingKey;
- pFileEntry->FileNameHash = MAKE_INDEX64(dwRootDirIndex, pEntry->FileIndex, 0);
- pFileEntry->dwFlags = CASC_ENTRY_SHORT_NAME;
- }
-
- return ERROR_SUCCESS;
-}
+ char szFileName[MAX_PATH+1];
+ size_t nLength;
-static int ParseDirEntries_FileId2(
- TRootHandler_Diablo3 * pRootHandler,
- LPBYTE pbFileEntries,
- DWORD dwFileEntries,
- DWORD dwRootDirIndex)
-{
- PDIABLO3_FILEID2_ENTRY pEntry = (PDIABLO3_FILEID2_ENTRY)pbFileEntries;
- PCASC_FILE_ENTRY pFileEntry;
-
- // Overflow test
- if((pRootHandler->FileTable.ItemCount + dwFileEntries) >= pRootHandler->FileTable.ItemCountMax)
- {
- assert(false);
- return ERROR_NOT_ENOUGH_MEMORY;
+ // Construct the name without extension and find it in the map
+ nLength = CascStrPrintf(szFileName, _countof(szFileName), "%s\\%s", szAssetName, szPlainName);
+ return (char *)PackagesMap.FindString(szFileName, szFileName + nLength);
}
- // Parse the all ID1 entries in the file
- for(DWORD i = 0; i < dwFileEntries; i++, pEntry++)
+ LPBYTE LoadFileToMemory(TCascStorage * hs, const char * szFileName, DWORD * pcbFileData)
{
- // Insert the file entry to the global list
- pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1);
- assert(pFileEntry != NULL);
-
- // Fill the index entry
- pFileEntry->EncodingKey = pEntry->EncodingKey;
- pFileEntry->FileNameHash = MAKE_INDEX64(dwRootDirIndex, pEntry->FileIndex, pEntry->SubIndex);
- pFileEntry->dwFlags = CASC_ENTRY_SHORT_NAME | CASC_ENTRY_HAS_SUBINDEX;
- }
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ LPBYTE pbFileData = NULL;
- return ERROR_SUCCESS;
-}
-
-static int ParseDirEntries_Named(
- TRootHandler_Diablo3 * pRootHandler,
- LPBYTE pbFileEntries,
- LPBYTE pbFileEnd,
- DWORD dwFileEntries,
- DWORD dwRootDirIndex)
-{
- char szFileName[MAX_PATH+1];
- char * szNamePtr = szFileName;
- DWORD cbFileEntry;
- int nError = ERROR_SUCCESS;
+ // Try to find CKey for the file
+ pCKeyEntry = GetFile(hs, szFileName);
+ if(pCKeyEntry != NULL)
+ pbFileData = LoadInternalFileToMemory(hs, pCKeyEntry, pcbFileData);
- // Overflow test
- if((pRootHandler->FileTable.ItemCount + dwFileEntries) >= pRootHandler->FileTable.ItemCountMax)
- {
- assert(false);
- return ERROR_NOT_ENOUGH_MEMORY;
+ return pbFileData;
}
- // If we the file is not in the root directory itself,
- // prepare the prefix for the root directory.
- if(dwRootDirIndex != DIABLO3_INVALID_INDEX)
+ static LPBYTE CaptureDirectoryData(
+ DIABLO3_DIRECTORY & DirHeader,
+ LPBYTE pbDirectory,
+ DWORD cbDirectory)
{
- PCASC_FILE_ENTRY pRootEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, dwRootDirIndex);
- const char * szRootName = (const char *)Array_ItemAt(&pRootHandler->FileNames, pRootEntry->dwFileName);
+ LPBYTE pbDirectoryData = pbDirectory;
+ LPBYTE pbDataEnd = pbDirectory + cbDirectory;
+ DWORD Signature = 0;
+
+ //
+ // Structure of a Diablo3 directory header
+ // 1) Signature (4 bytes)
+ // 2) Number of DIABLO3_ASSET_ENTRY entries (4 bytes)
+ // 3) Array of DIABLO3_ASSET_ENTRY entries
+ // 4) Number of DIABLO3_ASSETIDX_ENTRY entries (4 bytes)
+ // 5) Array of DIABLO3_ASSETIDX_ENTRY entries
+ // 6) Number of DIABLO3_NAMED_ENTRY entries (4 bytes)
+ // 7) Array of DIABLO3_NAMED_ENTRY entries
+ //
+
+ // Prepare the header signature
+ memset(&DirHeader, 0, sizeof(DIABLO3_DIRECTORY));
+
+ // Get the header signature
+ pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &Signature);
+ if((pbDirectory == NULL) || (Signature != CASC_DIABLO3_ROOT_SIGNATURE && Signature != DIABLO3_SUBDIR_SIGNATURE))
+ return NULL;
+
+ // Subdirectories have extra two arrays
+ if(Signature == DIABLO3_SUBDIR_SIGNATURE)
+ {
+ // Capture the number of DIABLO3_ASSET_ENTRY items
+ pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &DirHeader.dwAssetEntries);
+ if(pbDirectory == NULL)
+ return NULL;
+
+ // Capture the array of DIABLO3_ASSET_ENTRY
+ pbDirectory = CaptureArray(pbDirectory, pbDataEnd, &DirHeader.pbAssetEntries, DIABLO3_ASSET_ENTRY, DirHeader.dwAssetEntries);
+ if(pbDirectory == NULL)
+ return NULL;
+
+ // Capture the number of DIABLO3_ASSETIDX_ENTRY items
+ pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &DirHeader.dwAssetIdxEntries);
+ if(pbDirectory == NULL)
+ return NULL;
+
+ // Capture the array of DIABLO3_ASSETIDX_ENTRY
+ pbDirectory = CaptureArray(pbDirectory, pbDataEnd, &DirHeader.pbAssetIdxEntries, DIABLO3_ASSETIDX_ENTRY, DirHeader.dwAssetIdxEntries);
+ if(pbDirectory == NULL)
+ return NULL;
+ }
- // Copy the root directory name
- while(szRootName[0] != 0)
- *szNamePtr++ = *szRootName++;
+ // Capture the number of DIABLO3_NAMED_ENTRY array
+ pbDirectory = CaptureInteger32(pbDirectory, pbDataEnd, &DirHeader.dwNamedEntries);
+ if(pbDirectory == NULL)
+ return NULL;
- // Append the backslash
- *szNamePtr++ = '\\';
- }
+ // Note: Do not capture the array here. We will do that later,
+ // when we will be parsing the directory
+ DirHeader.pbNamedEntries = pbDirectory;
- // Parse the file entry
- while(pbFileEntries < pbFileEnd)
- {
- PDIABLO3_NAMED_ENTRY pNamedEntry = (PDIABLO3_NAMED_ENTRY)pbFileEntries;
- DWORD cchFileName;
-
- // Verify the named entry whether it does not go beyond the EOF
- cbFileEntry = VerifyNamedFileEntry(pbFileEntries, pbFileEnd);
- if(cbFileEntry == 0)
- return ERROR_FILE_CORRUPT;
-
- // Append the file name to the prepared file name
- // This way we obtain the full name and the name lookup
- // will be fully operational
- memcpy(szNamePtr, pNamedEntry->szFileName, (cbFileEntry - sizeof(ENCODING_KEY)));
- cchFileName = (DWORD)((szNamePtr - szFileName) + (cbFileEntry - sizeof(ENCODING_KEY)));
-
- // Insert the named entry to the global file table
- nError = InsertFileEntry(pRootHandler,
- pNamedEntry->EncodingKey,
- szFileName,
- cchFileName);
- if(nError != ERROR_SUCCESS)
- return nError;
-
- // Move the pointer to the next entry
- pbFileEntries += cbFileEntry;
+ // Put the directory range
+ DirHeader.pbDirectoryData = pbDirectoryData;
+ DirHeader.pbDirectoryEnd = pbDirectoryData + cbDirectory;
+ return pbDirectory;
}
- return ERROR_SUCCESS;
-}
-
-static void ResolveFullFileNames(
- TRootHandler_Diablo3 * pRootHandler,
- PDIABLO3_CORE_TOC_ENTRY pCoreTocEntries,
- PCASC_MAP pPackageMap,
- LPBYTE pbCoreTocFile,
- DWORD dwFileIndexes)
-{
- PCASC_FILE_ENTRY pFileEntry;
- char * szPlainName;
- char * szNamePtr;
- size_t nLength;
- DWORD dwRootIndex;
- DWORD dwFileIndex;
- DWORD dwSubIndex;
- char szShortName[MAX_PATH+1];
- char szFullName[MAX_PATH+1];
-
- // Keep compiler happy
- CASCLIB_UNUSED(dwFileIndexes);
-
- // Parse the entire file table
- for(size_t i = 0; i < pRootHandler->FileTable.ItemCount; i++)
+ LPBYTE CaptureCoreTocHeader(
+ PDIABLO3_CORE_TOC_HEADER * PtrHeader,
+ PDWORD PtrMaxIndex,
+ LPBYTE pbDataPtr,
+ LPBYTE pbDataEnd)
{
- // Retrieve the file entry at n-th position
- pFileEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, i);
+ PDIABLO3_CORE_TOC_HEADER pTocHeader = (PDIABLO3_CORE_TOC_HEADER)pbDataPtr;
+ DWORD dwMaxFileIndex = 0;
- // Skip the items that already have full name
- if(pFileEntry->dwFlags & CASC_ENTRY_SHORT_NAME)
- {
- // Retrieve the file index of that file
- dwRootIndex = INDEX64_ROOT_INDEX(pFileEntry->FileNameHash);
- dwFileIndex = INDEX64_FILE_INDEX(pFileEntry->FileNameHash);
- dwSubIndex = (pFileEntry->dwFlags & CASC_ENTRY_HAS_SUBINDEX) ? INDEX64_SUB_INDEX(pFileEntry->FileNameHash) : DIABLO3_INVALID_INDEX;
- assert(dwFileIndex < dwFileIndexes);
-
- // Get the plain name of the file
- szPlainName = (char *)(pbCoreTocFile + pCoreTocEntries[dwFileIndex].NameOffset);
-
- // Create the short file name
- nLength = CreateShortName(pPackageMap,
- dwRootIndex,
- pCoreTocEntries[dwFileIndex].AssetIndex,
- szPlainName,
- dwSubIndex,
- szShortName);
-
- // Insert the short name to the list of the names
- szNamePtr = (char *)Array_Insert(&pRootHandler->FileNames, szShortName, nLength + 1);
- pFileEntry->dwFileName = (DWORD)Array_IndexOf(&pRootHandler->FileNames, szNamePtr);
-
- // Create the full file name
- nLength = CreateFileName(pRootHandler, szShortName, szFullName);
- pFileEntry->FileNameHash = CalcFileNameHash(szFullName);
-
- // Insert the entry to the name map. Use the mapping of FullName -> FileHash
- Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash);
- }
- }
-}
+ // Check the space for header
+ if((pbDataPtr + sizeof(DIABLO3_CORE_TOC_HEADER)) > pbDataEnd)
+ return NULL;
+ pbDataPtr += sizeof(DIABLO3_CORE_TOC_HEADER);
-static LPBYTE LoadFileToMemory(TCascStorage * hs, LPBYTE pbEncodingKey, DWORD * pcbFileData)
-{
- QUERY_KEY EncodingKey;
- LPBYTE pbFileData = NULL;
- HANDLE hFile;
- DWORD cbBytesRead = 0;
- DWORD cbFileData = 0;
-
- // Open the file by encoding key
- EncodingKey.pbData = pbEncodingKey;
- EncodingKey.cbData = MD5_HASH_SIZE;
- if(CascOpenFileByEncodingKey((HANDLE)hs, &EncodingKey, 0, &hFile))
- {
- // Retrieve the file size
- cbFileData = CascGetFileSize(hFile, NULL);
- if(cbFileData > 0)
+ // Verify all asset arrays
+ for(size_t i = 0; i < DIABLO3_MAX_ASSETS; i++)
{
- pbFileData = CASC_ALLOC(BYTE, cbFileData);
- if(pbFileData != NULL)
+ PDIABLO3_CORE_TOC_ENTRY pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbDataPtr + pTocHeader->EntryOffsets[i]);
+ DWORD EntryOffset = pTocHeader->EntryOffsets[i];
+ DWORD EntryCount = pTocHeader->EntryCounts[i];
+
+ // Verify file range
+ if((pbDataPtr + EntryOffset + EntryCount * sizeof(DIABLO3_CORE_TOC_ENTRY)) > pbDataEnd)
+ return NULL;
+
+ // Find out the entry with the maximum index
+ for(DWORD n = 0; n < EntryCount; n++)
{
- CascReadFile(hFile, pbFileData, cbFileData, &cbBytesRead);
+ if(pTocEntry->FileIndex >= dwMaxFileIndex)
+ dwMaxFileIndex = pTocEntry->FileIndex;
+ pTocEntry++;
}
}
- // Close the file
- CascCloseFile(hFile);
+ // Give data and return
+ PtrMaxIndex[0] = dwMaxFileIndex;
+ PtrHeader[0] = pTocHeader;
+ return pbDataPtr;
}
- // Give the file to the caller
- if(pcbFileData != NULL)
- pcbFileData[0] = cbBytesRead;
- return pbFileData;
-}
-
-static LPBYTE LoadFileToMemory(TCascStorage * hs, const char * szFileName, DWORD * pcbFileData)
-{
- LPBYTE pbEncodingKey = NULL;
- LPBYTE pbFileData = NULL;
-
- // Try to find encoding key for the file
- pbEncodingKey = RootHandler_GetKey(hs->pRootHandler, szFileName);
- if(pbEncodingKey != NULL)
- pbFileData = LoadFileToMemory(hs, pbEncodingKey, pcbFileData);
-
- return pbFileData;
-}
-
-static int ParseDirectoryHeader(
- PDIABLO3_DIR_HEADER pDirHeader,
- LPBYTE pbDirFile,
- LPBYTE pbFileEnd)
-{
- DWORD dwSignature = 0;
-
- //
- // Structure of a Diablo3 directory file
- // 1) Signature (4 bytes)
- // 2) Number of DIABLO3_FILEID1_ENTRY entries (4 bytes)
- // 3) Array of DIABLO3_FILEID1_ENTRY entries
- // 4) Number of DIABLO3_FILEID2_ENTRY entries (4 bytes)
- // 5) Array of DIABLO3_FILEID2_ENTRY entries
- // 6) Number of DIABLO3_NAMED_ENTRY entries (4 bytes)
- // 7) Array of DIABLO3_NAMED_ENTRY entries
- //
-
- // Prepare the header signature
- memset(pDirHeader, 0, sizeof(DIABLO3_DIR_HEADER));
-
- // Get the signature
- if((pbDirFile + sizeof(DWORD)) >= pbFileEnd)
- return ERROR_BAD_FORMAT;
- dwSignature = *(PDWORD)pbDirFile;
-
- // Check the signature
- if(dwSignature != CASC_DIABLO3_ROOT_SIGNATURE && dwSignature != DIABLO3_SUBDIR_SIGNATURE)
- return ERROR_BAD_FORMAT;
- pbDirFile += sizeof(DWORD);
-
- // Subdirectories have extra two arrays
- if(dwSignature == DIABLO3_SUBDIR_SIGNATURE)
+ LPBYTE CaptureNamedEntry(
+ LPBYTE pbDataPtr,
+ LPBYTE pbDataEnd,
+ PDIABLO3_NAMED_ENTRY pEntry)
{
- // Get the number of DIABLO3_FILEID1_ENTRY items
- if((pbDirFile + sizeof(DWORD)) >= pbFileEnd)
- return ERROR_BAD_FORMAT;
- pDirHeader->dwEntries1 = *(PDWORD)pbDirFile;
-
- // Get the array of DIABLO3_FILEID1_ENTRY
- pDirHeader->pbEntries1 = (pbDirFile + sizeof(DWORD));
- pbDirFile = pbDirFile + sizeof(DWORD) + pDirHeader->dwEntries1 * sizeof(DIABLO3_FILEID1_ENTRY);
-
- // Get the number of DIABLO3_FILEID2_ENTRY items
- if((pbDirFile + sizeof(DWORD)) >= pbFileEnd)
- return ERROR_BAD_FORMAT;
- pDirHeader->dwEntries2 = *(PDWORD)pbDirFile;
-
- // Get the array of DIABLO3_FILEID2_ENTRY
- pDirHeader->pbEntries2 = (pbDirFile + sizeof(DWORD));
- pbDirFile = pbDirFile + sizeof(DWORD) + pDirHeader->dwEntries2 * sizeof(DIABLO3_FILEID2_ENTRY);
- }
+ // Capture the content key
+ pbDataPtr = CaptureContentKey(pbDataPtr, pbDataEnd, &pEntry->pCKey);
+ if(pbDataPtr == NULL)
+ return NULL;
+
+ // Capture file name. Must be ASCIIZ file name
+ pEntry->szFileName = (const char *)pbDataPtr;
+ while(pbDataPtr < pbDataEnd && pbDataPtr[0] != 0)
+ pbDataPtr++;
+
+ // Did we find a zero char?
+ if(pbDataPtr < pbDataEnd && pbDataPtr[0] == 0)
+ {
+ pEntry->szFileEnd = (const char *)pbDataPtr;
+ return pbDataPtr + 1;
+ }
- // Get the pointer and length DIABLO3_NAMED_ENTRY array
- if((pbDirFile + sizeof(DWORD)) >= pbFileEnd)
- return ERROR_BAD_FORMAT;
- pDirHeader->dwEntries3 = *(PDWORD)pbDirFile;
- pDirHeader->pbEntries3 = (pbDirFile + sizeof(DWORD));
- return ERROR_SUCCESS;
-}
+ return NULL;
+ }
-static DWORD ScanDirectoryFile(
- TCascStorage * hs,
- LPBYTE pbRootFile,
- LPBYTE pbFileEnd)
-{
- PDIABLO3_NAMED_ENTRY pNamedEntry;
- DIABLO3_DIR_HEADER RootHeader;
- DIABLO3_DIR_HEADER DirHeader;
- LPBYTE pbSubDir;
- DWORD dwTotalFileCount;
- DWORD cbNamedEntry;
- DWORD cbSubDir;
- int nError;
-
- // Parse the directory header in order to retrieve the items
- nError = ParseDirectoryHeader(&RootHeader, pbRootFile, pbFileEnd);
- if(nError != ERROR_SUCCESS)
- return 0;
-
- // Add the root directory's entries
- dwTotalFileCount = RootHeader.dwEntries1 + RootHeader.dwEntries2 + RootHeader.dwEntries3;
-
- // Parse the named entries
- for(DWORD i = 0; i < RootHeader.dwEntries3; i++)
+ int LoadDirectoryFile(TCascStorage * hs, DIABLO3_DIRECTORY & DirHeader, PCASC_CKEY_ENTRY pCKeyEntry)
{
- // Get the this named entry
- if((cbNamedEntry = VerifyNamedFileEntry(RootHeader.pbEntries3, pbFileEnd)) == 0)
- return 0;
- pNamedEntry = (PDIABLO3_NAMED_ENTRY)RootHeader.pbEntries3;
- RootHeader.pbEntries3 += cbNamedEntry;
-
- // Load the subdirectory to memory
- pbSubDir = LoadFileToMemory(hs, pNamedEntry->EncodingKey.Value, &cbSubDir);
- if(pbSubDir != NULL)
+ LPBYTE pbData;
+ DWORD cbData = 0;
+
+ // Load the n-th folder
+ pbData = LoadInternalFileToMemory(hs, pCKeyEntry, &cbData);
+ if(pbData && cbData)
{
- // Count the files in the subdirectory
- if(ParseDirectoryHeader(&DirHeader, pbSubDir, pbSubDir + cbSubDir) == ERROR_SUCCESS)
+ if(CaptureDirectoryData(DirHeader, pbData, cbData) == NULL)
{
- dwTotalFileCount += DirHeader.dwEntries1 + DirHeader.dwEntries2 + DirHeader.dwEntries3;
+ // Clear the directory
+ CASC_FREE(pbData);
+ return ERROR_BAD_FORMAT;
}
-
- // Free the subdirectory
- CASC_FREE(pbSubDir);
}
+ return ERROR_SUCCESS;
}
- // Return the total number of entries
- return dwTotalFileCount;
-}
+ bool CreateAssetFileName(
+ PATH_BUFFER & PathBuffer,
+ DWORD FileIndex,
+ DWORD SubIndex)
+ {
+ PDIABLO3_CORE_TOC_ENTRY pTocEntry;
+ PDIABLO3_ASSET_INFO pAssetInfo;
+ const char * szPackageName = NULL;
+ const char * szPlainName;
+ const char * szFormat;
+ char * szPathEnd = PathBuffer.szEnd;
+ char * szPathPtr = PathBuffer.szPtr;
+ size_t nLength = 0;
+
+ // Find and check the entry
+ pTocEntry = pFileIndices + FileIndex;
+ if(pTocEntry->FileIndex == FileIndex)
+ {
+ // Retrieve the asset information
+ pAssetInfo = GetAssetInfo(pTocEntry->AssetIndex);
+
+ // Either use the asset info for getting the folder name or supply "Asset##"
+ if(pAssetInfo != NULL)
+ {
+ CascStrCopy(szPathPtr, (szPathEnd - szPathPtr), pAssetInfo->szDirectoryName);
+ szPathPtr += strlen(szPathPtr);
+ }
+ else
+ {
+ szPathPtr[0] = 'A';
+ szPathPtr[1] = 's';
+ szPathPtr[2] = 's';
+ szPathPtr[3] = 'e';
+ szPathPtr[4] = 't';
+ szPathPtr[5] = (char)('0' + (pTocEntry->AssetIndex / 10));
+ szPathPtr[6] = (char)('0' + (pTocEntry->AssetIndex % 10));
+ szPathPtr += 7;
+ }
-static int ParseDirectoryFile(
- TRootHandler_Diablo3 * pRootHandler,
- LPBYTE pbDirFile,
- LPBYTE pbFileEnd,
- DWORD dwRootDirIndex)
-{
- DIABLO3_DIR_HEADER DirHeader;
- int nError;
+ // Put the backslash
+ if(szPathPtr < PathBuffer.szEnd)
+ *szPathPtr++ = '\\';
+
+ // Construct the file name, up to the extension. Don't include the '.'
+ szPlainName = (const char *)(pbCoreTocData + pTocEntry->NameOffset);
+ szFormat = (SubIndex != CASC_INVALID_INDEX) ? "%s\\%04u" : "%s";
+ nLength = CascStrPrintf(szPathPtr, (szPathEnd - szPathPtr), szFormat, szPlainName, SubIndex);
+
+ // Try to fixup the file extension from the package name.
+ // File extensions are not predictable because for subitems,
+ // they are not always equal to the main items:
+ //
+ // SoundBank\3D Ambience.sbk
+ // SoundBank\3D Ambience\0000.smp
+ // SoundBank\3D Ambience\0002.smp
+ // ...
+ // SoundBank\Angel.sbk
+ // SoundBank\Angel\0000.fsb
+ // SoundBank\Angel\0002.fsb
+ //
+ // We use the Base\Data_D3\PC\Misc\Packages.dat for real file extensions, where possible
+ //
+
+ if(PackagesMap.IsInitialized() && pAssetInfo != NULL)
+ {
+ // Retrieve the asset name
+ szPackageName = FindPackageName(pAssetInfo->szDirectoryName, szPathPtr);
+ if(szPackageName != NULL)
+ {
+ CascStrCopy(PathBuffer.szPtr, (PathBuffer.szEnd - PathBuffer.szPtr), szPackageName);
+ return true;
+ }
+ }
- // Sanity checks
- assert(pRootHandler->FileTable.ItemArray != NULL);
- assert(pRootHandler->FileTable.ItemCount < pRootHandler->FileTable.ItemCountMax);
+ // Use the extension from the AssetInfo, if we have any
+ if(pAssetInfo != NULL && pAssetInfo->szExtension != NULL)
+ {
+ szPathPtr[nLength++] = '.';
+ CascStrCopy(szPathPtr + nLength, (szPathEnd - (szPathPtr + nLength)), pAssetInfo->szExtension);
+ return true;
+ }
- // Parse the directory header in order to retrieve the items
- nError = ParseDirectoryHeader(&DirHeader, pbDirFile, pbFileEnd);
- if(nError != ERROR_SUCCESS)
- return nError;
+ // Otherwise, supply "a##"
+ CascStrPrintf(szPathPtr + nLength, (szPathEnd - (szPathPtr + nLength)), ".a%02u", pTocEntry->AssetIndex);
+ return true;
+ }
- // Process all DIABLO3_FILEID1_ENTRY entries. These are for files
- // belonging to an asset group, without subitem number.
- // Example: "SoundBank\SoundFile.smp"
- // We skip inserting them to the name map, because the names are not known yet
- if(DirHeader.pbEntries1 && DirHeader.dwEntries1)
- {
- assert(dwRootDirIndex != DIABLO3_INVALID_INDEX);
- nError = ParseDirEntries_FileId1(pRootHandler, DirHeader.pbEntries1, DirHeader.dwEntries1, dwRootDirIndex);
- if(nError != ERROR_SUCCESS)
- return nError;
+ return false;
}
- // Parse all DIABLO3_FILEID2_ENTRY entries. These are for files
- // belonging to an asset group, with a subitem number.
- // Example: "SoundBank\SoundFile\0001.smp"
- // We skip inserting them to the name map, because the names are not known yet
- if(DirHeader.pbEntries2 && DirHeader.dwEntries2)
+ // Parse the asset entries
+ int ParseAssetEntries(
+ TCascStorage * hs,
+ DIABLO3_DIRECTORY & Directory,
+ PATH_BUFFER & PathBuffer)
{
- assert(dwRootDirIndex != DIABLO3_INVALID_INDEX);
- nError = ParseDirEntries_FileId2(pRootHandler, DirHeader.pbEntries2, DirHeader.dwEntries2, dwRootDirIndex);
- if(nError != ERROR_SUCCESS)
- return nError;
- }
+ PDIABLO3_ASSET_ENTRY pEntry = (PDIABLO3_ASSET_ENTRY)Directory.pbAssetEntries;
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ DWORD dwEntries = Directory.dwAssetEntries;
+ // Do nothing if there is no entries
+ if(pEntry != NULL && dwEntries != 0)
+ {
+ // Insert all asset entries to the file tree
+ for(DWORD i = 0; i < dwEntries; i++, pEntry++)
+ {
+ pCKeyEntry = FindCKeyEntry_CKey(hs, pEntry->CKey.Value);
+ if(pCKeyEntry != NULL)
+ {
+ // Construct the full path name of the entry
+ if(CreateAssetFileName(PathBuffer, pEntry->FileIndex, CASC_INVALID_INDEX))
+ {
+ // Insert the entry to the file tree
+ FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
+ }
+ }
+ }
+ }
- // Parse all named entries. These are for files with arbitrary names,
- // and they do not belong to an asset.
- if(DirHeader.pbEntries3 && DirHeader.dwEntries3)
- {
- nError = ParseDirEntries_Named(pRootHandler, DirHeader.pbEntries3, pbFileEnd, DirHeader.dwEntries3, dwRootDirIndex);
- if(nError != ERROR_SUCCESS)
- return nError;
+ return ERROR_SUCCESS;
}
- // Give the directory to the caller
- return nError;
-}
-
-static int ParseCoreTOC(
- TRootHandler_Diablo3 * pRootHandler,
- PCASC_MAP pPackageMap,
- LPBYTE pbCoreTocFile,
- LPBYTE pbCoreTocEnd)
-{
- PDIABLO3_CORE_TOC_HEADER pTocHeader;
- PDIABLO3_CORE_TOC_ENTRY pSortedEntries;
- PDIABLO3_CORE_TOC_ENTRY pTocEntry;
- LPBYTE pbCoreTocNames;
- DWORD dwFileIndexes = 0;
- DWORD i;
-
- // Check the space for header
- if((pbCoreTocFile + sizeof(DIABLO3_CORE_TOC_HEADER)) > pbCoreTocEnd)
- return ERROR_FILE_CORRUPT;
- pTocHeader = (PDIABLO3_CORE_TOC_HEADER)pbCoreTocFile;
- pbCoreTocFile += sizeof(DIABLO3_CORE_TOC_HEADER);
-
- // Calculate space needed for allocation
- for(i = 0; i < DIABLO3_MAX_ASSETS; i++)
+ int ParseAssetAndIdxEntries(
+ TCascStorage * hs,
+ DIABLO3_DIRECTORY & Directory,
+ PATH_BUFFER & PathBuffer)
{
- // Get the first entry
- pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbCoreTocFile + pTocHeader->EntryOffsets[i]);
+ PDIABLO3_ASSETIDX_ENTRY pEntry = (PDIABLO3_ASSETIDX_ENTRY)Directory.pbAssetIdxEntries;
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ DWORD dwEntries = Directory.dwAssetIdxEntries;
- // Find out the entry with the maximum index
- for(DWORD n = 0; n < pTocHeader->EntryCounts[i]; n++)
+ // Do nothing if there is no entries
+ if(pEntry != NULL && dwEntries != 0)
{
- if(pTocEntry->FileIndex >= dwFileIndexes)
- dwFileIndexes = pTocEntry->FileIndex + 1;
- pTocEntry++;
+ // Insert all asset entries to the file tree
+ for(DWORD i = 0; i < dwEntries; i++, pEntry++)
+ {
+ pCKeyEntry = FindCKeyEntry_CKey(hs, pEntry->CKey.Value);
+ if(pCKeyEntry != NULL)
+ {
+ // Construct the full path name of the entry
+ if(CreateAssetFileName(PathBuffer, pEntry->FileIndex, pEntry->SubIndex))
+ {
+ // Insert the entry to the file tree
+// fprintf(fp, "%08u %04u %s\n", pEntry->FileIndex, pEntry->SubIndex, PathBuffer.szBegin);
+ FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
+ }
+ }
+ }
}
+
+ return ERROR_SUCCESS;
}
- // Allocate and populate the array of DIABLO3_CORE_TOC_ENTRYs
- pSortedEntries = CASC_ALLOC(DIABLO3_CORE_TOC_ENTRY, dwFileIndexes);
- if(pSortedEntries != NULL)
+ // Parse the named entries of all folders
+ int ParseDirectory_Phase1(
+ TCascStorage * hs,
+ DIABLO3_DIRECTORY & Directory,
+ PATH_BUFFER & PathBuffer,
+ bool bIsRootDirectory)
{
- // Initialize all entries to invalid
- memset(pSortedEntries, 0xFF, dwFileIndexes * sizeof(DIABLO3_CORE_TOC_ENTRY));
+ DIABLO3_NAMED_ENTRY NamedEntry;
+ char * szSavePtr = PathBuffer.szPtr;
+ size_t nFolderIndex = 0;
+ int nError = ERROR_SUCCESS;
- // Populate the linear array with the entries
- for(i = 0; i < DIABLO3_MAX_ASSETS; i++)
+ // Do nothing if there is no named headers
+ if(Directory.pbNamedEntries && Directory.dwNamedEntries)
{
- // Set the pointers
- pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbCoreTocFile + pTocHeader->EntryOffsets[i]);
- pbCoreTocNames = (LPBYTE)(pTocEntry + pTocHeader->EntryCounts[i]);
-
- // Setup the entries
- for(DWORD n = 0; n < pTocHeader->EntryCounts[i]; n++)
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ PCASC_FILE_NODE pFileNode;
+ LPBYTE pbDataPtr = Directory.pbNamedEntries;
+ LPBYTE pbDataEnd = Directory.pbDirectoryEnd;
+ DWORD dwNodeIndex;
+
+ // Parse all entries
+ while(pbDataPtr < pbDataEnd)
{
- pSortedEntries[pTocEntry->FileIndex].AssetIndex = pTocEntry->AssetIndex;
- pSortedEntries[pTocEntry->FileIndex].FileIndex = pTocEntry->FileIndex;
- pSortedEntries[pTocEntry->FileIndex].NameOffset = (DWORD)(pbCoreTocNames - pbCoreTocFile) + pTocEntry->NameOffset;
- pTocEntry++;
+ // Capture the named entry
+ pbDataPtr = CaptureNamedEntry(pbDataPtr, pbDataEnd, &NamedEntry);
+ if(pbDataPtr == NULL)
+ return ERROR_BAD_FORMAT;
+
+ // Append the path fragment to the total path
+ AppendPathToTotalPath(PathBuffer, NamedEntry.szFileName, NamedEntry.szFileEnd);
+
+ // Check whether the file exists in the storage
+ pCKeyEntry = FindCKeyEntry_CKey(hs, NamedEntry.pCKey->Value);
+ if(pCKeyEntry != NULL)
+ {
+ // Create file node belonging to this folder
+ pFileNode = FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
+ dwNodeIndex = (DWORD)FileTree.IndexOf(pFileNode);
+
+ // If we are parsing root folder, we also need to load the data of the sub-folder file
+ if(bIsRootDirectory)
+ {
+ // Mark the node as directory
+ AppendBackslashToTotalPath(PathBuffer);
+ pCKeyEntry->Flags |= CASC_CE_FOLDER_ENTRY;
+ pFileNode->Flags |= CFN_FLAG_FOLDER;
+
+ // Load the sub-directory file
+ nError = LoadDirectoryFile(hs, RootFolders[nFolderIndex], pCKeyEntry);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // Parse the sub-directory file
+ nError = ParseDirectory_Phase1(hs, RootFolders[nFolderIndex], PathBuffer, false);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ // Also save the item pointer and increment the folder index
+ RootFolders[nFolderIndex].dwNodeIndex = dwNodeIndex;
+ nFolderIndex++;
+ }
+
+ // Restore the path pointer
+ PathBuffer.szPtr = szSavePtr;
+ szSavePtr[0] = 0;
+ }
}
}
- // Now use the linear array to resolve the asset indexes and plain names
- ResolveFullFileNames(pRootHandler, pSortedEntries, pPackageMap, pbCoreTocFile, dwFileIndexes);
- CASC_FREE(pSortedEntries);
+ return nError;
}
- return ERROR_SUCCESS;
-}
-
-//-----------------------------------------------------------------------------
-// Implementation of Diablo III root file
-
-static int D3Handler_Insert(TRootHandler_Diablo3 * pRootHandler, const char * szFileName, LPBYTE pbEncodingKey)
-{
- ENCODING_KEY EncodingKey;
- DWORD dwFileIndex;
-
- // Don't let the number of items to overflow
- if(pRootHandler->FileTable.ItemCount >= pRootHandler->FileTable.ItemCountMax)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Insert the item
- EncodingKey = *(PENCODING_KEY)pbEncodingKey;
- dwFileIndex = InsertFileEntry(pRootHandler,
- EncodingKey,
- szFileName,
- strlen(szFileName) + 1);
- return (dwFileIndex != INVALID_FILE_INDEX) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
-}
-
-static LPBYTE D3Handler_Search(TRootHandler_Diablo3 * pRootHandler, TCascSearch * pSearch, PDWORD /* PtrFileSize */, PDWORD /* PtrLocaleFlags */, PDWORD /* PtrFileDataId */)
-{
- PCASC_FILE_ENTRY pFileEntry;
- const char * szSrcName = NULL;
-
- // Are we still inside the root directory range?
- while(pSearch->IndexLevel1 < pRootHandler->FileTable.ItemCount)
+ // Parse the nameless entries of all folders
+ int ParseDirectory_Phase2(TCascStorage * hs)
{
- // Get the n-th directory and the file name
- pFileEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, pSearch->IndexLevel1);
- szSrcName = (char *)Array_ItemAt(&pRootHandler->FileNames, pFileEntry->dwFileName);
+ PATH_BUFFER PathBuffer;
+ char szPathBuffer[MAX_PATH];
- // This is either a full file name or an abbreviated name
- if(pFileEntry->dwFlags & CASC_ENTRY_SHORT_NAME)
+ // Parse each root subdirectory
+ for(size_t i = 0; i < DIABLO3_MAX_ROOT_FOLDERS; i++)
{
- CreateFileName(pRootHandler, szSrcName, pSearch->szFileName);
- }
- else
- {
- strcpy(pSearch->szFileName, szSrcName);
+ // Is this root folder loaded?
+ if(RootFolders[i].pbDirectoryData != NULL)
+ {
+ PathBuffer.szBegin = szPathBuffer;
+ PathBuffer.szPtr = szPathBuffer;
+ PathBuffer.szEnd = szPathBuffer + MAX_PATH - 1;
+ szPathBuffer[0] = 0;
+
+ // Retrieve the parent name
+ if(RootFolders[i].dwNodeIndex != 0)
+ {
+ FileTree.PathAt(szPathBuffer, MAX_PATH, RootFolders[i].dwNodeIndex);
+ PathBuffer.szPtr = PathBuffer.szBegin + strlen(szPathBuffer);
+ }
+
+ // Array of DIABLO3_ASSET_ENTRY entries.
+ // These are for files belonging to an asset, without subitem number.
+ // Example: "SoundBank\SoundFile.smp"
+ ParseAssetEntries(hs, RootFolders[i], PathBuffer);
+
+ // Array of DIABLO3_ASSETIDX_ENTRY entries.
+ // These are for files belonging to an asset, with a subitem number.
+ // Example: "SoundBank\SoundFile\0001.smp"
+ ParseAssetAndIdxEntries(hs, RootFolders[i], PathBuffer);
+ }
}
- // Prepare for the next search
- pSearch->IndexLevel1++;
- return pFileEntry->EncodingKey.Value;
+ return ERROR_SUCCESS;
}
- // No more entries
- return NULL;
-}
+ // Creates an array of DIABLO3_CORE_TOC_ENTRY entries indexed by FileIndex
+ // Used as lookup table when we have FileIndex and need Asset+PlainName
+ int CreateMapOfFileIndices(TCascStorage * hs, const char * szFileName)
+ {
+ PDIABLO3_CORE_TOC_HEADER pTocHeader = NULL;
+ LPBYTE pbCoreTocPtr = pbCoreTocFile;
+ DWORD dwMaxFileIndex = 0;
+ int nError = ERROR_CAN_NOT_COMPLETE;
-static void D3Handler_EndSearch(TRootHandler_Diablo3 * /* pRootHandler */, TCascSearch * /* pSearch */)
-{
- // Do nothing
-}
+ // Load the entire file to memory
+ pbCoreTocFile = pbCoreTocPtr = LoadFileToMemory(hs, szFileName, &cbCoreTocFile);
+ if(pbCoreTocFile && cbCoreTocFile)
+ {
+ LPBYTE pbCoreTocEnd = pbCoreTocFile + cbCoreTocFile;
-static LPBYTE D3Handler_GetKey(TRootHandler_Diablo3 * pRootHandler, const char * szFileName)
-{
- PCASC_FILE_ENTRY pFileEntry;
- ULONGLONG FileNameHash = CalcFileNameHash(szFileName);
+ // Capture the header
+ if((pbCoreTocPtr = CaptureCoreTocHeader(&pTocHeader, &dwMaxFileIndex, pbCoreTocPtr, pbCoreTocEnd)) == NULL)
+ return ERROR_BAD_FORMAT;
- // Find the file in the name table
- pFileEntry = (PCASC_FILE_ENTRY)Map_FindObject(pRootHandler->pRootMap, &FileNameHash, NULL);
- return (pFileEntry != NULL) ? pFileEntry->EncodingKey.Value : NULL;
-}
+ // If there are no indices, return NULL
+ if(dwMaxFileIndex == 0)
+ return ERROR_SUCCESS;
-static DWORD D3Handler_GetFileId(TRootHandler_Diablo3 * /* pRootHandler */, const char * /* szFileName */)
-{
- // Not implemented for D3
- return 0;
-}
+ // Allocate and populate the array of DIABLO3_CORE_TOC_ENTRYs
+ pFileIndices = CASC_ALLOC(DIABLO3_CORE_TOC_ENTRY, dwMaxFileIndex + 1);
+ if(pFileIndices != NULL)
+ {
+ // Initialize all entries to invalid
+ memset(pFileIndices, 0xFF, (dwMaxFileIndex + 1) * sizeof(DIABLO3_CORE_TOC_ENTRY));
+
+ // Populate the linear array with the file indices
+ for(size_t i = 0; i < DIABLO3_MAX_ASSETS; i++)
+ {
+ PDIABLO3_CORE_TOC_ENTRY pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbCoreTocPtr + pTocHeader->EntryOffsets[i]);
+ LPBYTE pbCoreTocNames = (LPBYTE)(pTocEntry + pTocHeader->EntryCounts[i]);
+
+ // Setup the entries
+ for(DWORD n = 0; n < pTocHeader->EntryCounts[i]; n++)
+ {
+ DWORD dwFileIndex = pTocEntry->FileIndex;
+
+ pFileIndices[dwFileIndex].AssetIndex = pTocEntry->AssetIndex;
+ pFileIndices[dwFileIndex].FileIndex = pTocEntry->FileIndex;
+ pFileIndices[dwFileIndex].NameOffset = (DWORD)(pbCoreTocNames - pbCoreTocPtr) + pTocEntry->NameOffset;
+ pTocEntry++;
+ }
+ }
+
+ // Save the file to the root handler
+ pbCoreTocData = pbCoreTocPtr;
+ nFileIndices = dwMaxFileIndex;
+ nError = ERROR_SUCCESS;
+ }
+ }
+ return nError;
+ }
-static void D3Handler_Close(TRootHandler_Diablo3 * pRootHandler)
-{
- if(pRootHandler != NULL)
+ // Packages.dat contains a list of full file names (without locale prefix).
+ // They are not sorted, nor they correspond to file IDs.
+ // Does the sort order mean something? Perhaps we could use them as listfile?
+ int CreateMapOfRealNames(TCascStorage * hs, const char * szFileName)
{
- // Free the file map
- Map_Free(pRootHandler->pRootMap);
+ DWORD Signature = 0;
+ DWORD NumberOfNames = 0;
- // Free the array of the file entries and file names
- Array_Free(&pRootHandler->FileTable);
- Array_Free(&pRootHandler->FileNames);
+ // Load the entire file to memory
+ pbPackagesDat = LoadFileToMemory(hs, szFileName, &cbPackagesDat);
+ if(pbPackagesDat && cbPackagesDat)
+ {
+ LPBYTE pbPackagesPtr = pbPackagesDat;
+ LPBYTE pbPackagesEnd = pbPackagesDat + cbPackagesDat;
+/*
+ LPBYTE pbPackagesPtr = pbPackagesDat + 8;
+ FILE * fp = fopen("E:\\Packages.dat", "wt");
+ if(fp != NULL)
+ {
+ while(pbPackagesPtr < pbPackagesEnd)
+ {
+ fprintf(fp, "%s\n", pbPackagesPtr);
+ pbPackagesPtr = pbPackagesPtr + strlen((char *)pbPackagesPtr) + 1;
+ }
+ fclose(fp);
+ }
+*/
+ // Get the header. There is just Signature + NumberOfNames
+ if((pbPackagesPtr = CaptureInteger32(pbPackagesPtr, pbPackagesEnd, &Signature)) == NULL)
+ return ERROR_BAD_FORMAT;
+ if((pbPackagesPtr = CaptureInteger32(pbPackagesPtr, pbPackagesEnd, &NumberOfNames)) == NULL)
+ return ERROR_BAD_FORMAT;
+ if(Signature != DIABLO3_PACKAGES_SIGNATURE || NumberOfNames == 0)
+ return ERROR_BAD_FORMAT;
+
+ // Create the map for fast search of the file name
+ if(PackagesMap.Create(NumberOfNames, 0, 0, KeyIsString) == ERROR_SUCCESS)
+ {
+ const char * szPackageName = (const char *)pbPackagesPtr;
+
+ // Go as long as there is something
+ for(DWORD i = 0; i < NumberOfNames; i++)
+ {
+ // Get the file extension
+ if((LPBYTE)szPackageName >= pbPackagesEnd)
+ break;
+
+ // Insert the file name to the map. The file extension is not included
+ PackagesMap.InsertString(szPackageName, true);
+ szPackageName = szPackageName + strlen(szPackageName) + 1;
+ }
+ }
+ }
- // Free the root file itself
- CASC_FREE(pRootHandler);
+ return ERROR_SUCCESS;
}
-}
-/*
-static void DumpRootFile(TDumpContext * dc, LPBYTE pbFileData, LPBYTE pbFileDataEnd)
-{
- char szMD5Buffer[MD5_STRING_SIZE+1];
- DWORD dwSignature;
- DWORD dwItemCount;
- DWORD i;
-
- dwSignature = *(PDWORD)pbFileData;
- if(dwSignature != CASC_DIABLO3_SUBDIR_SIGNATURE)
- return;
- pbFileData += sizeof(DWORD);
-
- // Dump items that contain EncodingKey + AssetId
- dwItemCount = *(PDWORD)pbFileData;
- pbFileData += sizeof(DWORD);
- for(i = 0; i < dwItemCount; i++)
+ int Load(TCascStorage * hs, DIABLO3_DIRECTORY & RootDirectory)
{
- PCASC_DIABLO3_ASSET_ENTRY pEntry = (PCASC_DIABLO3_ASSET_ENTRY)pbFileData;
+ PATH_BUFFER PathBuffer;
+ char szPathBuffer[MAX_PATH];
+ int nError;
+
+ // Initialize path buffer and go parse the directory
+ PathBuffer.szBegin = szPathBuffer;
+ PathBuffer.szPtr = szPathBuffer;
+ PathBuffer.szEnd = szPathBuffer + MAX_PATH;
+ szPathBuffer[0] = 0;
+
+ // Always parse the named entries first. They always point to a file.
+ // These are entries with arbitrary names, and they do not belong to an asset
+ nError = ParseDirectory_Phase1(hs, RootDirectory, PathBuffer, true);
+ if(nError == ERROR_SUCCESS)
+ {
+ // The asset entries in the ROOT file don't contain file names, but indices.
+ // To convert a file index to a file name, we need to load and parse the "Base\\CoreTOC.dat" file.
+ nError = CreateMapOfFileIndices(hs, "Base\\CoreTOC.dat");
+ if(nError == ERROR_SUCCESS)
+ {
+ // The file "Base\Data_D3\PC\Misc\Packages.dat" contains the file names
+ // (without level-0 and level-1 directory).
+ // We can use these names for supplying the missing extensions
+ CreateMapOfRealNames(hs, "Base\\Data_D3\\PC\\Misc\\Packages.dat");
+
+ // Now parse all folders and resolve the full names
+ ParseDirectory_Phase2(hs);
+ }
- if((pbFileData + sizeof(*pEntry)) > pbFileDataEnd)
- return;
- pbFileData += sizeof(*pEntry);
+ // Free all stuff that was used during loading of the ROOT file
+ FreeLoadingStuff();
+ }
- dump_print(dc, "%s %08X\n", StringFromMD5(pEntry->EncodingKey, szMD5Buffer), pEntry->AssetId);
+ return nError;
}
- // Terminate with two newlines
- dump_print(dc, "\n");
-
- // Dump items that contain EncodingKey + AssetId + FileNumber
- dwItemCount = *(PDWORD)pbFileData;
- pbFileData += sizeof(DWORD);
- for(i = 0; i < dwItemCount; i++)
+ void FreeLoadingStuff()
{
- PCASC_DIABLO3_ASSET_ENTRY2 pEntry = (PCASC_DIABLO3_ASSET_ENTRY2)pbFileData;
+ // Free the captured root sub-directories
+ for(size_t i = 0; i < DIABLO3_MAX_SUBDIRS; i++)
+ CASC_FREE(RootFolders[i].pbDirectoryData);
- if((pbFileData + sizeof(*pEntry)) > pbFileDataEnd)
- return;
- pbFileData += sizeof(*pEntry);
+ // Free the package map
+ PackagesMap.Free();
- dump_print(dc, "%s %08X %08X\n", StringFromMD5((LPBYTE)pEntry->EncodingKey, szMD5Buffer), pEntry->AssetId, pEntry->FileNumber);
- }
-
- // Terminate with two newlines
- dump_print(dc, "\n");
+ // Free the array of file indices
+ CASC_FREE(pFileIndices);
- // Dump items that contain EncodingKey + FileName
- dwItemCount = *(PDWORD)pbFileData;
- pbFileData += sizeof(DWORD);
- for(i = 0; i < dwItemCount; i++)
- {
- PDIABLO3_NAMED_ENTRY pEntry = (PDIABLO3_NAMED_ENTRY)pbFileData;
- DWORD dwEntrySize = VerifyNamedFileEntry(pbFileData, pbFileDataEnd);
+ // Free the loaded CoreTOC.dat file
+ CASC_FREE(pbCoreTocFile);
- if((pbFileData + dwEntrySize) > pbFileDataEnd)
- return;
- pbFileData += dwEntrySize;
-
- dump_print(dc, "%s %s\n", StringFromMD5((LPBYTE)pEntry->EncodingKey, szMD5Buffer), pEntry->szFileName);
+ // Free the loaded Packages.dat file
+ CASC_FREE(pbPackagesDat);
}
- dump_print(dc, "\n\n");
-}
-*/
+ // Array of root directory subdirectories
+ DIABLO3_DIRECTORY RootFolders[DIABLO3_MAX_ROOT_FOLDERS];
+
+ // Array of DIABLO3_TOC_ENTRY structures, sorted by the file index
+ // Used for converting FileIndex -> Asset+PlainName during loading
+ PDIABLO3_CORE_TOC_ENTRY pFileIndices;
+ LPBYTE pbCoreTocFile;
+ LPBYTE pbCoreTocData;
+ size_t nFileIndices;
+ DWORD cbCoreTocFile;
+
+ // Map for searching a real file extension
+ CASC_MAP PackagesMap;
+ LPBYTE pbPackagesDat;
+ DWORD cbPackagesDat;
+};
+
//-----------------------------------------------------------------------------
// Public functions
int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
{
- TRootHandler_Diablo3 * pRootHandler;
- PCASC_MAP pPackageMap = NULL;
- LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
- LPBYTE pbPackagesDat = NULL;
- DWORD dwTotalFileCount;
- DWORD cbPackagesDat = 0;
- int nError;
-
- // Allocate the root handler object
- hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_Diablo3, 1);
- if(pRootHandler == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Fill-in the handler functions
- memset(pRootHandler, 0, sizeof(TRootHandler_Diablo3));
- pRootHandler->Insert = (ROOT_INSERT)D3Handler_Insert;
- pRootHandler->Search = (ROOT_SEARCH)D3Handler_Search;
- pRootHandler->EndSearch = (ROOT_ENDSEARCH)D3Handler_EndSearch;
- pRootHandler->GetKey = (ROOT_GETKEY)D3Handler_GetKey;
- pRootHandler->Close = (ROOT_CLOSE)D3Handler_Close;
- pRootHandler->GetFileId = (ROOT_GETFILEID)D3Handler_GetFileId;
-
- // Fill-in the flags
- pRootHandler->dwRootFlags |= ROOT_FLAG_HAS_NAMES;
-
- // Scan the total number of files in the root directories
- // Reserve space for extra files
- dwTotalFileCount = ScanDirectoryFile(hs, pbRootFile, pbRootFileEnd);
- if(dwTotalFileCount == 0)
- return ERROR_FILE_CORRUPT;
- dwTotalFileCount += CASC_EXTRA_FILES;
-
- // Allocate the global linear file table
- // Note: This is about 18 MB of memory for Diablo III PTR build 30013
- nError = Array_Create(&pRootHandler->FileTable, CASC_FILE_ENTRY, dwTotalFileCount);
- if(nError != ERROR_SUCCESS)
- return nError;
+ TDiabloRoot * pRootHandler = NULL;
+ DIABLO3_DIRECTORY RootDirectory;
+ int nError = ERROR_BAD_FORMAT;
- // Allocate global buffer for file names.
- // The size of the buffer was taken from Diablo III build 30013
- nError = Array_Create(&pRootHandler->FileNames, char, 0x01000000);
- if(nError != ERROR_SUCCESS)
- return nError;
-
- // Create map of ROOT_ENTRY -> FileEntry
- pRootHandler->pRootMap = Map_Create(dwTotalFileCount, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_ENTRY, FileNameHash));
- if(pRootHandler->pRootMap == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Parse the ROOT file and insert all entries in the file table
- nError = ParseDirectoryFile(pRootHandler, pbRootFile, pbRootFileEnd, DIABLO3_INVALID_INDEX);
- if(nError == ERROR_SUCCESS)
+ // Verify the header of the ROOT file
+ if(TDiabloRoot::CaptureDirectoryData(RootDirectory, pbRootFile, cbRootFile) != NULL)
{
- size_t dwRootEntries = pRootHandler->FileTable.ItemCount;
-
- // We expect the number of level-0 to be less than maximum
- assert(dwRootEntries < DIABLO3_MAX_SUBDIRS);
-
- // Now parse the all root items and load them
- for(DWORD i = 0; i < dwRootEntries; i++)
+ // Allocate the root handler object
+ pRootHandler = new TDiabloRoot();
+ if(pRootHandler != NULL)
{
- PCASC_FILE_ENTRY pRootEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, i);
-
- // Load the entire file to memory
- pbRootFile = LoadFileToMemory(hs, pRootEntry->EncodingKey.Value, &cbRootFile);
- if(pbRootFile != NULL)
+ // Load the root directory. If load failed, we free the object
+ nError = pRootHandler->Load(hs, RootDirectory);
+ if(nError != ERROR_SUCCESS)
{
- nError = ParseDirectoryFile(pRootHandler, pbRootFile, pbRootFile + cbRootFile, i);
- CASC_FREE(pbRootFile);
+ delete pRootHandler;
+ pRootHandler = NULL;
}
}
}
- // Note: The file "Base\Data_D3\PC\Misc\Packages.dat" contains the names
- // of the files (without level-0 and level-1 directory). We can use these
- // names for supplying the missing extensions
- if(nError == ERROR_SUCCESS)
- {
- // Load the entire file to memory
- pbPackagesDat = LoadFileToMemory(hs, "Base\\Data_D3\\PC\\Misc\\Packages.dat", &cbPackagesDat);
- if(pbPackagesDat != NULL)
- {
- pPackageMap = CreatePackageMap(pbPackagesDat, pbPackagesDat + cbPackagesDat);
- }
- }
-
- // Vast majorify of files at this moment don't have names.
- // We can load the Base\CoreTOC.dat file in order
- // to get directory asset indexes, file names and extensions
- if(nError == ERROR_SUCCESS)
- {
- LPBYTE pbCoreTOC;
- DWORD cbCoreTOC = 0;
-
- // Load the entire file to memory
- pbCoreTOC = LoadFileToMemory(hs, "Base\\CoreTOC.dat", &cbCoreTOC);
- if(pbCoreTOC != NULL)
- {
- ParseCoreTOC(pRootHandler, pPackageMap, pbCoreTOC, pbCoreTOC + cbCoreTOC);
- CASC_FREE(pbCoreTOC);
- }
- }
-
- // Free the packages map
- if(pPackageMap != NULL)
- Map_Free(pPackageMap);
- if(pbPackagesDat != NULL)
- CASC_FREE(pbPackagesDat);
+ // Assign the root directory (or NULL) and return error
+ hs->pRootHandler = pRootHandler;
return nError;
}
diff --git a/dep/CascLib/src/CascRootFile_Install.cpp b/dep/CascLib/src/CascRootFile_Install.cpp
new file mode 100644
index 00000000000..a0b00f73168
--- /dev/null
+++ b/dep/CascLib/src/CascRootFile_Install.cpp
@@ -0,0 +1,121 @@
+/*****************************************************************************/
+/* CascRootFile_Install.cpp Copyright (c) Ladislav Zezula 2018 */
+/*---------------------------------------------------------------------------*/
+/* Support for ROOT handler based on INSTALL manifest */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 21.05.19 1.00 Lad The first version of CascRootFile_Install.cpp */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "CascLib.h"
+#include "CascCommon.h"
+
+//-----------------------------------------------------------------------------
+// Handler definitions for Starcraft I root file
+
+struct TRootHandler_Install : public TFileTreeRoot
+{
+ public:
+
+ TRootHandler_Install() : TFileTreeRoot(0)
+ {
+ // We have file names and return CKey as result of search
+ dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY);
+ }
+
+ static int CaptureInstallHeader(CASC_INSTALL_HEADER & InHeader, LPBYTE pbFileData, size_t cbFileData)
+ {
+ PFILE_INSTALL_HEADER pFileHeader = (PFILE_INSTALL_HEADER)pbFileData;
+
+ // Check the signature ('DL') and version
+ if (cbFileData < sizeof(FILE_INSTALL_HEADER) || pFileHeader->Magic != FILE_MAGIC_INSTALL || pFileHeader->Version != 1)
+ return ERROR_BAD_FORMAT;
+
+ // Note that we don't support CKey sizes greater than 0x10 in the INSTALL file
+ if (pFileHeader->EKeyLength > MD5_HASH_SIZE)
+ return ERROR_BAD_FORMAT;
+
+ // Capture the header version 1
+ memset(&InHeader, 0, sizeof(CASC_INSTALL_HEADER));
+ InHeader.Magic = pFileHeader->Magic;
+ InHeader.Version = pFileHeader->Version;
+ InHeader.EKeyLength = pFileHeader->EKeyLength;
+ InHeader.TagCount = ConvertBytesToInteger_2(pFileHeader->TagCount);
+ InHeader.EntryCount = ConvertBytesToInteger_4(pFileHeader->EntryCount);
+ InHeader.HeaderLength = sizeof(FILE_INSTALL_HEADER);
+ return ERROR_SUCCESS;
+ }
+
+ int Load(TCascStorage * hs, CASC_INSTALL_HEADER InHeader, LPBYTE pbInstallFile, LPBYTE pbInstallEnd)
+ {
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ const char * szString;
+ size_t nBitmapLength;
+ size_t nFileCount = InHeader.EntryCount;
+
+ // Skip the header
+ pbInstallFile += InHeader.HeaderLength;
+
+ // Skip the tags
+ for (DWORD i = 0; i < InHeader.TagCount; i++)
+ {
+ szString = (const char *)pbInstallFile;
+ nBitmapLength = GetTagBitmapLength(pbInstallFile, pbInstallEnd, InHeader.EntryCount);
+ pbInstallFile = pbInstallFile + strlen(szString) + 1 + sizeof(USHORT) + nBitmapLength;
+ }
+
+ // Load the names and insert them to the root handler
+ while(nFileCount > 0 && pbInstallFile < pbInstallEnd)
+ {
+ // File Name and CKey
+ szString = (const char *)pbInstallFile;
+ pbInstallFile += strlen(szString) + 1;
+
+ // Verify whether it is a known entry
+ pCKeyEntry = FindCKeyEntry_CKey(hs, pbInstallFile);
+ pbInstallFile += MD5_HASH_SIZE + sizeof(DWORD);
+
+ // Insert the FileName+CKey to the file tree
+ if (pCKeyEntry != NULL)
+ FileTree.InsertByName(pCKeyEntry, szString);
+ nFileCount--;
+ }
+
+ return ERROR_SUCCESS;
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+//
+// Starcraft ROOT file is a text file with the following format:
+// HD2/portraits/NBluCrit/NLCFID01.webm|c2795b120592355d45eba9cdc37f691e
+// locales/enUS/Assets/campaign/EXPZerg/Zerg08/staredit/wav/zovtra01.ogg|316b0274bf2dabaa8db60c3ff1270c85
+// locales/zhCN/Assets/sound/terran/ghost/tghdth01.wav|6637ed776bd22089e083b8b0b2c0374c
+//
+
+int RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbInstallFile, DWORD cbInstallFile)
+{
+ CASC_INSTALL_HEADER InHeader;
+ TRootHandler_Install * pRootHandler = NULL;
+ int nError = ERROR_BAD_FORMAT;
+
+ // Capture the header of the DOWNLOAD file
+ nError = TRootHandler_Install::CaptureInstallHeader(InHeader, pbInstallFile, cbInstallFile);
+ if (nError == ERROR_SUCCESS)
+ {
+ // Allocate the root handler object
+ pRootHandler = new TRootHandler_Install();
+ if (pRootHandler != NULL)
+ {
+ // Parse the entire install manifest
+ nError = pRootHandler->Load(hs, InHeader, pbInstallFile, pbInstallFile + cbInstallFile);
+ hs->pRootHandler = pRootHandler;
+ }
+ }
+
+ return nError;
+}
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)
diff --git a/dep/CascLib/src/CascRootFile_OW.cpp b/dep/CascLib/src/CascRootFile_OW.cpp
new file mode 100644
index 00000000000..24f5af81b34
--- /dev/null
+++ b/dep/CascLib/src/CascRootFile_OW.cpp
@@ -0,0 +1,605 @@
+/*****************************************************************************/
+/* CascRootFile_Text.cpp Copyright (c) Ladislav Zezula 2017 */
+/*---------------------------------------------------------------------------*/
+/* Support for loading ROOT files in plain text */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 28.10.15 1.00 Lad The first version of CascRootFile_Text.cpp */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "CascLib.h"
+#include "CascCommon.h"
+
+//-----------------------------------------------------------------------------
+// Structure definitions for CMF files
+
+#define MAX_LINE_ELEMENTS 8
+
+typedef struct _CMF_HEADER_V3
+{
+ DWORD BuildVersion;
+ DWORD Unknown0;
+ DWORD Unknown1;
+ DWORD Unknown2;
+ DWORD Unknown3;
+ DWORD DataCount;
+ DWORD Unknown4;
+ DWORD EntryCount;
+ DWORD Magic;
+} CMF_HEADER_V3, *PCMF_HEADER_V3;
+
+typedef struct _CMF_HEADER_V2
+{
+ DWORD BuildVersion;
+ DWORD Unknown0;
+ DWORD Unknown1;
+ DWORD Unknown2;
+ DWORD DataCount;
+ DWORD Unknown3;
+ DWORD EntryCount;
+ DWORD Magic;
+} CMF_HEADER_V2, *PCMF_HEADER_V2;
+
+typedef struct _CMF_HEADER_V1
+{
+ DWORD BuildVersion;
+ DWORD Unknown0;
+ DWORD DataCount;
+ DWORD Unknown1;
+ DWORD EntryCount;
+ DWORD Magic;
+} CMF_HEADER_V1, *PCMF_HEADER_V1;
+
+//-----------------------------------------------------------------------------
+// Structure definitions for APM files
+
+// In-memory format
+typedef struct _APM_ENTRY
+{
+ DWORD Index;
+ ULONGLONG HashA;
+ ULONGLONG HashB;
+} APM_ENTRY, *PAPM_ENTRY;
+
+// On-disk format, size = 0x14
+typedef struct _APM_ENTRY_V2
+{
+ DWORD Index;
+ DWORD HashA_Lo; // Must split the hashes in order to make this structure properly aligned
+ DWORD HashA_Hi;
+ DWORD HashB_Lo;
+ DWORD HashB_Hi;
+} APM_ENTRY_V2, *PAPM_ENTRY_V2;
+
+// On-disk format, size = 0x0C
+typedef struct _APM_ENTRY_V1
+{
+ DWORD Index;
+ DWORD HashA_Lo; // Must split the hashes in order to make this structure properly aligned
+ DWORD HashA_Hi;
+} APM_ENTRY_V1, *PAPM_ENTRY_V1;
+
+// In-memory format
+typedef struct _APM_PACKAGE_ENTRY
+{
+ ULONGLONG PackageGUID; // 077 file
+ ULONGLONG Unknown1;
+ DWORD Unknown2;
+ DWORD Unknown3;
+ ULONGLONG Unknown4;
+} APM_PACKAGE_ENTRY, *PAPM_PACKAGE_ENTRY;
+
+// On-disk format
+typedef struct _APM_PACKAGE_ENTRY_V2
+{
+ ULONGLONG PackageGUID; // 077 file
+ ULONGLONG Unknown1;
+ DWORD Unknown2;
+ DWORD Unknown3;
+ ULONGLONG Unknown4;
+} APM_PACKAGE_ENTRY_V2, *PAPM_PACKAGE_ENTRY_V2;
+
+// On-disk format
+typedef struct _APM_PACKAGE_ENTRY_V1
+{
+ ULONGLONG EntryPointGUID; // virtual most likely
+ ULONGLONG PrimaryGUID; // real
+ ULONGLONG SecondaryGUID; // real
+ ULONGLONG Key; // encryption
+ ULONGLONG PackageGUID; // 077 file
+ ULONGLONG Unknown1;
+ DWORD Unknown2;
+} APM_PACKAGE_ENTRY_V1, *PAPM_PACKAGE_ENTRY_V1;
+
+typedef struct _APM_HEADER_V3
+{
+ ULONGLONG BuildNumber; // Build number of the game
+ ULONGLONG ZeroValue1;
+ DWORD ZeroValue2;
+ DWORD PackageCount;
+ DWORD ZeroValue3;
+ DWORD EntryCount;
+ DWORD Checksum;
+
+ // Followed by the array of APM_ENTRY (count is in "EntryCount")
+ // Followed by the array of APM_PACKAGE (count is in "PackageCount")
+
+} APM_HEADER_V3, *PAPM_HEADER_V3;
+
+typedef struct _APM_HEADER_V2
+{
+ ULONGLONG BuildNumber; // Build number of the game
+ ULONGLONG ZeroValue1;
+ DWORD PackageCount;
+ DWORD ZeroValue2;
+ DWORD EntryCount;
+ DWORD Checksum;
+
+ // Followed by the array of APM_ENTRY (count is in "EntryCount")
+ // Followed by the array of APM_PACKAGE (count is in "PackageCount")
+
+} APM_HEADER_V2, *PAPM_HEADER_V2;
+
+typedef struct _APM_HEADER_V1
+{
+ ULONGLONG BuildNumber; // Build number of the game
+ DWORD BuildVersion;
+ DWORD PackageCount;
+ DWORD EntryCount;
+ DWORD Checksum;
+
+ // Followed by the array of APM_ENTRY (count is in "EntryCount")
+ // Followed by the array of APM_PACKAGE (count is in "PackageCount")
+
+} APM_HEADER_V1, *PAPM_HEADER_V1;
+
+//-----------------------------------------------------------------------------
+// Handler classes
+
+/*
+struct TCmfFile
+{
+ TCmfFile()
+ {
+ memset(this, 0, sizeof(TCmfFile));
+ }
+
+ LPBYTE CaptureHeader(LPBYTE pbCmfData, LPBYTE pbCmfEnd)
+ {
+ DWORD BuildNumber = *(PDWORD)pbCmfData;
+
+ // Check the newest header version
+ if(BuildNumber >= 45104 && BuildNumber != 45214)
+ {
+ PCMF_HEADER_V3 pHeader3 = (PCMF_HEADER_V3)pbCmfData;
+
+ if ((LPBYTE)(pHeader3 + 1) > pbCmfEnd)
+ return NULL;
+
+ BuildVersion = pHeader3->BuildVersion;
+ DataCount = pHeader3->DataCount;
+ EntryCount = pHeader3->EntryCount;
+ Magic = pHeader3->Magic;
+ return (LPBYTE)(pHeader3 + 1);
+ }
+
+ else if(BuildNumber >= 39028)
+ {
+ // TODO
+ assert(false);
+ return NULL;
+ }
+
+ else
+ {
+ // TODO
+ assert(false);
+ return NULL;
+ }
+ }
+
+ DWORD BuildVersion;
+ DWORD DataCount;
+ DWORD EntryCount;
+ DWORD Magic;
+};
+
+struct TApmFile
+{
+ TApmFile()
+ {
+ memset(this, 0, sizeof(TApmFile));
+ }
+
+ ~TApmFile()
+ {
+ CASC_FREE(pApmPackages);
+ CASC_FREE(pApmEntries);
+ }
+
+ LPBYTE CaptureHeader(LPBYTE pbApmData, LPBYTE pbApmEnd)
+ {
+ // Check the data size for the largest possible header size
+ if((pbApmData + sizeof(APM_HEADER_V3)) < pbApmEnd)
+ {
+ // Try the version 3
+ PAPM_HEADER_V3 pApmFile3 = (PAPM_HEADER_V3)(pbApmData);
+ if(pApmFile3->ZeroValue1 == 0 && pApmFile3->ZeroValue2 == 0 && pApmFile3->PackageCount && pApmFile3->EntryCount && pApmFile3->Checksum)
+ {
+ BuildNumber = pApmFile3->BuildNumber;
+ PackageCount = pApmFile3->PackageCount;
+ EntryCount = pApmFile3->EntryCount;
+ Checksum = pApmFile3->Checksum;
+ return pbApmData + 0x24;
+ }
+
+ // Try the version 2
+ PAPM_HEADER_V2 pApmFile2 = (PAPM_HEADER_V2)(pbApmData);
+ if(pApmFile2->ZeroValue1 == 0 && pApmFile2->PackageCount && pApmFile2->EntryCount && pApmFile2->Checksum)
+ {
+ BuildNumber = pApmFile2->BuildNumber;
+ PackageCount = pApmFile2->PackageCount;
+ EntryCount = pApmFile2->EntryCount;
+ Checksum = pApmFile2->Checksum;
+ return pbApmData + 0x20;
+ }
+
+ // Try the version 1 (build 24919)
+ PAPM_HEADER_V1 pApmHeader1 = (PAPM_HEADER_V1)(pbApmData);
+ if(pApmHeader1->BuildVersion != 0 && pApmHeader1->PackageCount && pApmHeader1->EntryCount && pApmHeader1->Checksum)
+ {
+ BuildNumber = pApmHeader1->BuildNumber;
+ PackageCount = pApmHeader1->PackageCount;
+ EntryCount = pApmHeader1->EntryCount;
+ Checksum = pApmHeader1->Checksum;
+ return pbApmData + 0x18;
+ }
+ }
+
+ return NULL;
+ }
+
+ LPBYTE CaptureArrayOfEntries(LPBYTE pbArrayOfEntries, LPBYTE pbApmEnd)
+ {
+ // Allocate array of entries
+ pApmEntries = CASC_ALLOC(APM_ENTRY, EntryCount);
+ if(pApmEntries != NULL)
+ {
+ // The newest format
+ if(BuildNumber > 45104 && BuildNumber != 45214)
+ {
+ PAPM_ENTRY_V2 pEntry2 = (PAPM_ENTRY_V2)pbArrayOfEntries;
+ LPBYTE pbEntriesEnd = (LPBYTE)(pEntry2 + EntryCount);
+
+ if(pbEntriesEnd <= pbApmEnd)
+ {
+ for(DWORD i = 0; i < EntryCount; i++)
+ {
+ pApmEntries[i].Index = pEntry2->Index;
+ pApmEntries[i].HashA = MAKE_OFFSET64(pEntry2->HashA_Hi, pEntry2->HashA_Lo);
+ pApmEntries[i].HashB = MAKE_OFFSET64(pEntry2->HashB_Hi, pEntry2->HashB_Lo);
+ }
+
+ return pbEntriesEnd;
+ }
+ }
+ else
+ {
+ PAPM_ENTRY_V1 pEntry1 = (PAPM_ENTRY_V1)pbArrayOfEntries;
+ LPBYTE pbEntriesEnd = (LPBYTE)(pEntry1 + EntryCount);
+
+ if(pbEntriesEnd <= pbApmEnd)
+ {
+ for(DWORD i = 0; i < EntryCount; i++)
+ {
+ pApmEntries[i].Index = pEntry1->Index;
+ pApmEntries[i].HashA = MAKE_OFFSET64(pEntry1->HashA_Hi, pEntry1->HashA_Lo);
+ pApmEntries[i].HashB = 0;
+ }
+
+ return pbEntriesEnd;
+ }
+ }
+ }
+
+ return NULL;
+ }
+
+ LPBYTE CapturePackageEntries(LPBYTE pbArrayOfEntries, LPBYTE pbApmEnd)
+ {
+ // Allocate array of entries
+ pApmPackages = CASC_ALLOC(APM_PACKAGE_ENTRY, PackageCount);
+ if(pApmPackages != NULL)
+ {
+ // Zero the entire array
+ memset(pApmPackages, 0, PackageCount * sizeof(APM_PACKAGE_ENTRY));
+
+ // The newest format
+ if(BuildNumber > 45104 && BuildNumber != 45214)
+ {
+ PAPM_PACKAGE_ENTRY_V2 pEntry2 = (PAPM_PACKAGE_ENTRY_V2)pbArrayOfEntries;
+ LPBYTE pbEntriesEnd = (LPBYTE)(pEntry2 + PackageCount);
+
+ if(pbEntriesEnd <= pbApmEnd)
+ {
+ for(DWORD i = 0; i < PackageCount; i++)
+ {
+ pApmPackages[i].PackageGUID = pEntry2[i].PackageGUID;
+ pApmPackages[i].Unknown1 = pEntry2[i].Unknown1;
+ pApmPackages[i].Unknown2 = pEntry2[i].Unknown2;
+ pApmPackages[i].Unknown3 = pEntry2[i].Unknown3;
+ pApmPackages[i].Unknown4 = pEntry2[i].Unknown4;
+ }
+
+ return pbEntriesEnd;
+ }
+ }
+ else
+ {
+ PAPM_PACKAGE_ENTRY_V1 pEntry1 = (PAPM_PACKAGE_ENTRY_V1)pbArrayOfEntries;
+ LPBYTE pbEntriesEnd = (LPBYTE)(pEntry1 + PackageCount);
+
+ if(pbEntriesEnd <= pbApmEnd)
+ {
+ for(DWORD i = 0; i < PackageCount; i++)
+ {
+ // TODO!!!
+ pApmPackages[i].PackageGUID = pEntry1->PackageGUID;
+ }
+
+ return pbEntriesEnd;
+ }
+ }
+ }
+
+ return NULL;
+ }
+
+ PAPM_ENTRY pApmEntries;
+ PAPM_PACKAGE_ENTRY pApmPackages;
+ ULONGLONG BuildNumber;
+ DWORD PackageCount;
+ DWORD EntryCount;
+ DWORD Checksum;
+ size_t HeaderSize;
+
+ // Followed by the array of APM_ENTRY (count is in "EntryCount")
+ // Followed by the array of APM_PACKAGE (count is in "PackageCount")
+
+};
+*/
+
+//-----------------------------------------------------------------------------
+// Handler definition for OVERWATCH root file
+
+//
+// -------------------------------------
+// Overwatch ROOT file (build 24919):
+// -------------------------------------
+// #MD5|CHUNK_ID|FILENAME|INSTALLPATH
+// FE3AD8A77EEF77B383DF4929AED816FD|0|RetailClient/GameClientApp.exe|GameClientApp.exe
+// 5EDDEFECA544B6472C5CD52BE63BC02F|0|RetailClient/Overwatch Launcher.exe|Overwatch Launcher.exe
+// 6DE09F0A67F33F874F2DD8E2AA3B7AAC|0|RetailClient/ca-bundle.crt|ca-bundle.crt
+// 99FE9EB6A4BB20209202F8C7884859D9|0|RetailClient/ortp_x64.dll|ortp_x64.dll
+//
+// -------------------------------------
+// Overwatch ROOT file (build 47161):
+// -------------------------------------
+// #FILEID|MD5|CHUNK_ID|PRIORITY|MPRIORITY|FILENAME|INSTALLPATH
+// RetailClient/Overwatch.exe|807F96661280C07E762A8C129FEBDA6F|0|0|255|RetailClient/Overwatch.exe|Overwatch.exe
+// RetailClient/Overwatch Launcher.exe|5EDDEFECA544B6472C5CD52BE63BC02F|0|0|255|RetailClient/Overwatch Launcher.exe|Overwatch Launcher.exe
+// RetailClient/ortp_x64.dll|7D1B5DEC267480F3E8DAD6B95143A59C|0|0|255|RetailClient/ortp_x64.dll|ortp_x64.dll
+//
+
+struct TRootHandler_OW : public TFileTreeRoot
+{
+ TRootHandler_OW() : TFileTreeRoot(0)
+ {
+ // We have file names and return CKey as result of search
+ dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY);
+ }
+/*
+ bool IsManifestFolderName(const char * szFileName, const char * szManifestFolder, size_t nLength)
+ {
+ if(!_strnicmp(szFileName, szManifestFolder, nLength))
+ {
+ return (szFileName[nLength] == '\\' || szFileName[nLength] == '/');
+ }
+ return false;
+ }
+
+ bool IsApmFileName(const char * szFileName)
+ {
+ const char * szExtension;
+
+ if(IsManifestFolderName(szFileName, "Manifest", 8) || IsManifestFolderName(szFileName, "TactManifest", 12))
+ {
+ szExtension = GetFileExtension(szFileName);
+ if(!_stricmp(szExtension, ".apm"))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ int LoadApmFile(TCascStorage * hs, CONTENT_KEY & CKey, const char * szFileName)
+ {
+ TApmFile ApmFile;
+ LPBYTE pbApmData;
+ DWORD cbApmData = 0;
+ int nError = ERROR_BAD_FORMAT;
+
+ pbApmData = LoadInternalFileToMemory(hs, CKey.Value, CASC_OPEN_BY_CKEY, &cbApmData);
+ if(pbApmData != NULL)
+ {
+ LPBYTE pbApmEnd = pbApmData + cbApmData;
+ LPBYTE pbApmPtr = pbApmData;
+
+ pbApmPtr = ApmFile.CaptureHeader(pbApmPtr, pbApmEnd);
+ if(pbApmPtr == NULL)
+ return ERROR_BAD_FORMAT;
+
+ // Read the array of entries
+ pbApmPtr = ApmFile.CaptureArrayOfEntries(pbApmPtr, pbApmEnd);
+ if(pbApmPtr == NULL)
+ return ERROR_BAD_FORMAT;
+
+ // Read the array of package entries
+ pbApmPtr = ApmFile.CapturePackageEntries(pbApmPtr, pbApmEnd);
+ if(pbApmPtr == NULL)
+ return ERROR_BAD_FORMAT;
+
+ CASC_FREE(pbApmData);
+ }
+
+ return nError;
+ }
+
+ static int LoadCmfFile(TCascStorage * hs, CONTENT_KEY & CKey, const char * szFileName)
+ {
+ TCmfFile CmfFile;
+ LPBYTE pbCmfData;
+ DWORD cbCmfData = 0;
+
+ int nError = ERROR_BAD_FORMAT;
+
+ pbCmfData = LoadInternalFileToMemory(hs, CKey.Value, CASC_OPEN_BY_CKEY, &cbCmfData);
+ if(pbCmfData != NULL)
+ {
+ LPBYTE pbCmfEnd = pbCmfData + cbCmfData;
+ LPBYTE pbCmfPtr = pbCmfData;
+
+ // Capture the CMF header
+ pbCmfPtr = CmfFile.CaptureHeader(pbCmfPtr, pbCmfEnd);
+ if(pbCmfPtr == NULL)
+ return ERROR_BAD_FORMAT;
+
+// if(CmfFile.Magic >= 0x636D6614)
+// DecryptCmfFile(
+
+ CASC_FREE(pbCmfData);
+ }
+
+ return nError;
+ }
+*/
+ int Load(TCascStorage * hs, CASC_CSV & Csv, size_t nFileNameIndex, size_t nCKeyIndex)
+ {
+ PCASC_CKEY_ENTRY pCKeyEntry;
+// size_t ApmFiles[0x80];
+// size_t nApmFiles = 0;
+ BYTE CKey[MD5_HASH_SIZE];
+
+ CASCLIB_UNUSED(hs);
+
+ // Keep loading every line until there is something
+ while(Csv.LoadNextLine())
+ {
+ const CASC_CSV_COLUMN & FileName = Csv[CSV_ZERO][nFileNameIndex];
+ const CASC_CSV_COLUMN & CKeyStr = Csv[CSV_ZERO][nCKeyIndex];
+
+ // Retrieve the file name and the content key
+ if(FileName.szValue && CKeyStr.szValue && CKeyStr.nLength == MD5_STRING_SIZE)
+ {
+ // Convert the string CKey to binary
+ if(ConvertStringToBinary(CKeyStr.szValue, MD5_STRING_SIZE, CKey) == ERROR_SUCCESS)
+ {
+ // Find the item in the tree
+ if((pCKeyEntry = FindCKeyEntry_CKey(hs, CKey)) != NULL)
+ {
+ // Insert the file name and the CKey into the tree
+ FileTree.InsertByName(pCKeyEntry, FileName.szValue);
+
+ // If the file name is actually an asset, we need to parse that asset and load files in it
+// if(IsApmFileName(szFileName))
+// {
+// ApmFiles[nApmFiles++] = FileTree_IndexOf(&pRootHandler->FileTree, pFileNode1);
+// }
+ }
+ }
+ }
+ }
+/*
+ // Load all CMF+APM files
+ if(nError == ERROR_SUCCESS)
+ {
+ for(size_t i = 0; i < nApmFiles; i++)
+ {
+ char szApmFile[MAX_PATH + 1];
+ char szCmfFile[MAX_PATH + 1];
+
+ // Get the n-th item and its name
+ pFileNode1 = (PCASC_FILE_NODE)FileTree_PathAt(&pRootHandler->FileTree, szApmFile, MAX_PATH, ApmFiles[i]);
+ if(pFileNode1 == NULL)
+ break;
+
+ if(strcmp(szApmFile, "TactManifest\\Win_SPWin_RDEV_LenUS_EExt.apm"))
+ continue;
+
+ // Get the name of thew CMF file
+ CascStrCopy(szCmfFile, _countof(szCmfFile), szApmFile);
+ CascStrCopy((char *)GetFileExtension(szCmfFile), 5, ".cmf");
+ pFileNode2 = (PCASC_FILE_NODE)FileTree_Find(&pRootHandler->FileTree, szCmfFile);
+ if(pFileNode2 == NULL)
+ break;
+
+ // Create the map of CMF entries
+ nError = LoadCmfFile(hs, pFileNode2->CKey, szCmfFile);
+ if(nError != ERROR_SUCCESS)
+ break;
+
+ }
+ }
+*/
+ return ERROR_SUCCESS;
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+// TODO: There is way more files in the Overwatch CASC storage than present in the ROOT file.
+int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
+{
+ TRootHandler_OW * pRootHandler = NULL;
+ CASC_CSV Csv(0, true);
+ size_t Indices[2];
+ int nError;
+
+ // Load the ROOT file
+ nError = Csv.Load(pbRootFile, cbRootFile);
+ if(nError == ERROR_SUCCESS)
+ {
+ // Retrieve the indices of the file name and MD5 columns
+ Indices[0] = Csv.GetColumnIndex("FILENAME");
+ Indices[1] = Csv.GetColumnIndex("MD5");
+
+ // If both indices were found OK, then load the root file
+ if(Indices[0] != CSV_INVALID_INDEX && Indices[1] != CSV_INVALID_INDEX)
+ {
+ pRootHandler = new TRootHandler_OW();
+ if (pRootHandler != NULL)
+ {
+ // Load the root directory. If load failed, we free the object
+ nError = pRootHandler->Load(hs, Csv, Indices[0], Indices[1]);
+ if (nError != ERROR_SUCCESS)
+ {
+ delete pRootHandler;
+ pRootHandler = NULL;
+ }
+ }
+ }
+ else
+ {
+ nError = ERROR_BAD_FORMAT;
+ }
+ }
+
+ // Assign the root directory (or NULL) and return error
+ hs->pRootHandler = pRootHandler;
+ return nError;
+}
diff --git a/dep/CascLib/src/CascRootFile_Ovr.cpp b/dep/CascLib/src/CascRootFile_Ovr.cpp
deleted file mode 100644
index 05ce13bd67a..00000000000
--- a/dep/CascLib/src/CascRootFile_Ovr.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-/*****************************************************************************/
-/* CascRootFile_Ovr.cpp Copyright (c) Ladislav Zezula 2015 */
-/*---------------------------------------------------------------------------*/
-/* Support for loading Overwatch ROOT file */
-/*---------------------------------------------------------------------------*/
-/* Date Ver Who Comment */
-/* -------- ---- --- ------- */
-/* 28.10.15 1.00 Lad The first version of CascRootFile_Ovr.cpp */
-/*****************************************************************************/
-
-#define __CASCLIB_SELF__
-#include "CascLib.h"
-#include "CascCommon.h"
-
-//-----------------------------------------------------------------------------
-// Structure definitions for Overwatch root file
-
-typedef struct _CASC_FILE_ENTRY
-{
- ENCODING_KEY EncodingKey; // Encoding key
- ULONGLONG FileNameHash; // File name hash
- DWORD dwFileName; // Offset of the file name in the name cache
-} CASC_FILE_ENTRY, *PCASC_FILE_ENTRY;
-
-struct TRootHandler_Ovr : public TRootHandler
-{
- // Linear global list of file entries
- DYNAMIC_ARRAY FileTable;
-
- // Linear global list of names
- DYNAMIC_ARRAY FileNames;
-
- // Global map of FileName -> FileEntry
- PCASC_MAP pRootMap;
-};
-
-//-----------------------------------------------------------------------------
-// Local functions
-
-static int InsertFileEntry(
- TRootHandler_Ovr * pRootHandler,
- const char * szFileName,
- LPBYTE pbEncodingKey)
-{
- PCASC_FILE_ENTRY pFileEntry;
- size_t nLength = strlen(szFileName);
-
- // Attempt to insert the file name to the global buffer
- szFileName = (char *)Array_Insert(&pRootHandler->FileNames, szFileName, nLength + 1);
- if(szFileName == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Attempt to insert the entry to the array of file entries
- pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1);
- if(pFileEntry == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Fill the file entry
- pFileEntry->EncodingKey = *(PENCODING_KEY)pbEncodingKey;
- pFileEntry->FileNameHash = CalcFileNameHash(szFileName);
- pFileEntry->dwFileName = (DWORD)Array_IndexOf(&pRootHandler->FileNames, szFileName);
-
- // Insert the file entry to the map
- assert(Map_FindObject(pRootHandler->pRootMap, &pFileEntry->FileNameHash, NULL) == NULL);
- Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash);
- return ERROR_SUCCESS;
-}
-
-//-----------------------------------------------------------------------------
-// Implementation of Overwatch root file
-
-static int OvrHandler_Insert(
- TRootHandler_Ovr * pRootHandler,
- const char * szFileName,
- LPBYTE pbEncodingKey)
-{
- return InsertFileEntry(pRootHandler, szFileName, pbEncodingKey);
-}
-
-static LPBYTE OvrHandler_Search(TRootHandler_Ovr * pRootHandler, TCascSearch * pSearch, PDWORD /* PtrFileSize */, PDWORD /* PtrLocaleFlags */, PDWORD /* PtrFileDataId */)
-{
- PCASC_FILE_ENTRY pFileEntry;
-
- // Are we still inside the root directory range?
- while(pSearch->IndexLevel1 < pRootHandler->FileTable.ItemCount)
- {
- // Retrieve the file item
- pFileEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, pSearch->IndexLevel1);
- strcpy(pSearch->szFileName, (char *)Array_ItemAt(&pRootHandler->FileNames, pFileEntry->dwFileName));
-
- // Prepare the pointer to the next search
- pSearch->IndexLevel1++;
- return pFileEntry->EncodingKey.Value;
- }
-
- // No more entries
- return NULL;
-}
-
-static void OvrHandler_EndSearch(TRootHandler_Ovr * /* pRootHandler */, TCascSearch * /* pSearch */)
-{
- // Do nothing
-}
-
-static LPBYTE OvrHandler_GetKey(TRootHandler_Ovr * pRootHandler, const char * szFileName)
-{
- ULONGLONG FileNameHash = CalcFileNameHash(szFileName);
-
- return (LPBYTE)Map_FindObject(pRootHandler->pRootMap, &FileNameHash, NULL);
-}
-
-static DWORD OvrHandler_GetFileId(TRootHandler_Ovr * /* pRootHandler */, const char * /* szFileName */)
-{
- // Not implemented for Overwatch
- return 0;
-}
-
-static void OvrHandler_Close(TRootHandler_Ovr * pRootHandler)
-{
- if(pRootHandler != NULL)
- {
- // Free the file map
- if(pRootHandler->pRootMap)
- Map_Free(pRootHandler->pRootMap);
- pRootHandler->pRootMap = NULL;
-
- // Free the array of the file names and file items
- Array_Free(&pRootHandler->FileTable);
- Array_Free(&pRootHandler->FileNames);
-
- // Free the root file itself
- CASC_FREE(pRootHandler);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Public functions
-
-int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
-{
- TRootHandler_Ovr * pRootHandler;
- ENCODING_KEY KeyBuffer;
- QUERY_KEY EncodingKey = {KeyBuffer.Value, MD5_HASH_SIZE};
- void * pTextFile;
- size_t nLength;
- char szOneLine[0x200];
- char szFileName[MAX_PATH+1];
- DWORD dwFileCountMax = (DWORD)hs->pEncodingMap->TableSize;
- int nFileNameIndex;
- int nError = ERROR_SUCCESS;
-
- // Allocate the root handler object
- hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_Ovr, 1);
- if(pRootHandler == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Fill-in the handler functions
- memset(pRootHandler, 0, sizeof(TRootHandler_Ovr));
- pRootHandler->Insert = (ROOT_INSERT)OvrHandler_Insert;
- pRootHandler->Search = (ROOT_SEARCH)OvrHandler_Search;
- pRootHandler->EndSearch = (ROOT_ENDSEARCH)OvrHandler_EndSearch;
- pRootHandler->GetKey = (ROOT_GETKEY)OvrHandler_GetKey;
- pRootHandler->Close = (ROOT_CLOSE)OvrHandler_Close;
- pRootHandler->GetFileId = (ROOT_GETFILEID)OvrHandler_GetFileId;
-
- // Fill-in the flags
- pRootHandler->dwRootFlags |= ROOT_FLAG_HAS_NAMES;
-
- // Allocate the linear array of file entries
- nError = Array_Create(&pRootHandler->FileTable, CASC_FILE_ENTRY, 0x10000);
- if(nError != ERROR_SUCCESS)
- return nError;
-
- // Allocate the buffer for the file names
- nError = Array_Create(&pRootHandler->FileNames, char, 0x10000);
- if(nError != ERROR_SUCCESS)
- return nError;
-
- // Create map of ROOT_ENTRY -> FileEntry
- pRootHandler->pRootMap = Map_Create(dwFileCountMax, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_ENTRY, FileNameHash));
- if(pRootHandler->pRootMap == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Parse the ROOT file
- pTextFile = ListFile_FromBuffer(pbRootFile, cbRootFile);
- if(pTextFile != NULL)
- {
- // Get the initial line, containing variable names
- nLength = ListFile_GetNextLine(pTextFile, szOneLine, _maxchars(szOneLine));
-
- // Determine the index of the "FILENAME" variable
- nError = GetRootVariableIndex(szOneLine, szOneLine + nLength, "FILENAME", &nFileNameIndex);
- if(nError == ERROR_SUCCESS)
- {
- // Parse the next lines
- while((nLength = ListFile_GetNextLine(pTextFile, szOneLine, _maxchars(szOneLine))) > 0)
- {
- // Parse the line
- nError = ParseRootFileLine(szOneLine, szOneLine + nLength, nFileNameIndex, &EncodingKey, szFileName, _maxchars(szFileName));
- if(nError == ERROR_SUCCESS)
- {
- InsertFileEntry(pRootHandler, szFileName, KeyBuffer.Value);
- }
- }
- }
-
- // Free the listfile
- ListFile_Free(pTextFile);
- }
-
- // Succeeded
- return nError;
-}
diff --git a/dep/CascLib/src/CascRootFile_SC1.cpp b/dep/CascLib/src/CascRootFile_SC1.cpp
deleted file mode 100644
index a795cfd8d96..00000000000
--- a/dep/CascLib/src/CascRootFile_SC1.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/*****************************************************************************/
-/* CascRootFile_SC1.cpp Copyright (c) Ladislav Zezula 2017 */
-/*---------------------------------------------------------------------------*/
-/* Support for loading Starcraft 1 ROOT file */
-/*---------------------------------------------------------------------------*/
-/* Date Ver Who Comment */
-/* -------- ---- --- ------- */
-/* 28.10.15 1.00 Lad The first version of CascRootFile_SC1.cpp */
-/*****************************************************************************/
-
-#define __CASCLIB_SELF__
-#include "CascLib.h"
-#include "CascCommon.h"
-
-//-----------------------------------------------------------------------------
-// Structure definitions for Overwatch root file
-
-typedef struct _CASC_FILE_ENTRY
-{
- ENCODING_KEY EncodingKey; // Encoding key
- ULONGLONG FileNameHash; // File name hash
- DWORD dwFileName; // Offset of the file name in the name cache
-} CASC_FILE_ENTRY, *PCASC_FILE_ENTRY;
-
-struct TRootHandler_SC1 : public TRootHandler
-{
- // Linear global list of file entries
- DYNAMIC_ARRAY FileTable;
-
- // Linear global list of names
- DYNAMIC_ARRAY FileNames;
-
- // Global map of FileName -> FileEntry
- PCASC_MAP pRootMap;
-};
-
-//-----------------------------------------------------------------------------
-// Local functions
-
-static int InsertFileEntry(
- TRootHandler_SC1 * pRootHandler,
- const char * szFileName,
- LPBYTE pbEncodingKey)
-{
- PCASC_FILE_ENTRY pFileEntry;
- size_t nLength = strlen(szFileName);
-
- // Attempt to insert the file name to the global buffer
- szFileName = (char *)Array_Insert(&pRootHandler->FileNames, szFileName, nLength + 1);
- if(szFileName == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Attempt to insert the entry to the array of file entries
- pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1);
- if(pFileEntry == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Fill the file entry
- pFileEntry->EncodingKey = *(PENCODING_KEY)pbEncodingKey;
- pFileEntry->FileNameHash = CalcFileNameHash(szFileName);
- pFileEntry->dwFileName = (DWORD)Array_IndexOf(&pRootHandler->FileNames, szFileName);
-
- // Insert the file entry to the map
- assert(Map_FindObject(pRootHandler->pRootMap, &pFileEntry->FileNameHash, NULL) == NULL);
- Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash);
- return ERROR_SUCCESS;
-}
-
-//-----------------------------------------------------------------------------
-// Implementation of Overwatch root file
-
-static int SC1Handler_Insert(
- TRootHandler_SC1 * pRootHandler,
- const char * szFileName,
- LPBYTE pbEncodingKey)
-{
- return InsertFileEntry(pRootHandler, szFileName, pbEncodingKey);
-}
-
-static LPBYTE SC1Handler_Search(TRootHandler_SC1 * pRootHandler, TCascSearch * pSearch, PDWORD /* PtrFileSize */, PDWORD /* PtrLocaleFlags */, PDWORD /* PtrFileDataId */)
-{
- PCASC_FILE_ENTRY pFileEntry;
-
- // Are we still inside the root directory range?
- while(pSearch->IndexLevel1 < pRootHandler->FileTable.ItemCount)
- {
- // Retrieve the file item
- pFileEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, pSearch->IndexLevel1);
-
- // Prepare the pointer to the next search
- pSearch->IndexLevel1++;
-
- char *filename = (char *)Array_ItemAt(&pRootHandler->FileNames, pFileEntry->dwFileName);
- if (CheckWildCard(filename, pSearch->szMask)) {
- strcpy(pSearch->szFileName, filename);
- return pFileEntry->EncodingKey.Value;
- }
- }
-
- // No more entries
- return NULL;
-}
-
-static void SC1Handler_EndSearch(TRootHandler_SC1 * /* pRootHandler */, TCascSearch * /* pSearch */)
-{
- // Do nothing
-}
-
-static LPBYTE SC1Handler_GetKey(TRootHandler_SC1 * pRootHandler, const char * szFileName)
-{
- ULONGLONG FileNameHash = CalcFileNameHash(szFileName);
-
- return (LPBYTE)Map_FindObject(pRootHandler->pRootMap, &FileNameHash, NULL);
-}
-
-static DWORD SC1Handler_GetFileId(TRootHandler_SC1 * /* pRootHandler */, const char * /* szFileName */)
-{
- // Not implemented for Overwatch
- return 0;
-}
-
-static void SC1Handler_Close(TRootHandler_SC1 * pRootHandler)
-{
- if(pRootHandler != NULL)
- {
- // Free the file map
- if(pRootHandler->pRootMap)
- Map_Free(pRootHandler->pRootMap);
- pRootHandler->pRootMap = NULL;
-
- // Free the array of the file names and file items
- Array_Free(&pRootHandler->FileTable);
- Array_Free(&pRootHandler->FileNames);
-
- // Free the root file itself
- CASC_FREE(pRootHandler);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Public functions
-
-int RootHandler_CreateSC1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
-{
- TRootHandler_SC1 * pRootHandler;
- ENCODING_KEY KeyBuffer;
- QUERY_KEY EncodingKey = {KeyBuffer.Value, MD5_HASH_SIZE};
- void * pTextFile;
- size_t nLength;
- char szOneLine[0x200];
- DWORD dwFileCountMax = (DWORD)hs->pEncodingMap->TableSize;
- int nError = ERROR_SUCCESS;
-
- // Allocate the root handler object
- hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_SC1, 1);
- if(pRootHandler == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Fill-in the handler functions
- memset(pRootHandler, 0, sizeof(TRootHandler_SC1));
- pRootHandler->Insert = (ROOT_INSERT)SC1Handler_Insert;
- pRootHandler->Search = (ROOT_SEARCH)SC1Handler_Search;
- pRootHandler->EndSearch = (ROOT_ENDSEARCH)SC1Handler_EndSearch;
- pRootHandler->GetKey = (ROOT_GETKEY)SC1Handler_GetKey;
- pRootHandler->Close = (ROOT_CLOSE)SC1Handler_Close;
- pRootHandler->GetFileId = (ROOT_GETFILEID)SC1Handler_GetFileId;
-
- // Fill-in the flags
- pRootHandler->dwRootFlags |= ROOT_FLAG_HAS_NAMES;
-
- // Allocate the linear array of file entries
- nError = Array_Create(&pRootHandler->FileTable, CASC_FILE_ENTRY, 0x10000);
- if(nError != ERROR_SUCCESS)
- return nError;
-
- // Allocate the buffer for the file names
- nError = Array_Create(&pRootHandler->FileNames, char, 0x10000);
- if(nError != ERROR_SUCCESS)
- return nError;
-
- // Create map of ROOT_ENTRY -> FileEntry
- pRootHandler->pRootMap = Map_Create(dwFileCountMax, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_ENTRY, FileNameHash));
- if(pRootHandler->pRootMap == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Parse the ROOT file
- pTextFile = ListFile_FromBuffer(pbRootFile, cbRootFile);
- if(pTextFile != NULL)
- {
- // Parse the next lines
- while((nLength = ListFile_GetNextLine(pTextFile, szOneLine, _maxchars(szOneLine))) > 0)
- {
- LPSTR szEncodingKey;
- BYTE EncodingKey[MD5_HASH_SIZE];
-
- szEncodingKey = strchr(szOneLine, _T('|'));
- if(szEncodingKey != NULL)
- {
- // Split the name and encoding key
- *szEncodingKey++ = 0;
-
- // Insert the entry to the map
- ConvertStringToBinary(szEncodingKey, MD5_STRING_SIZE, EncodingKey);
- InsertFileEntry(pRootHandler, szOneLine, EncodingKey);
- }
- }
-
- // Free the listfile
- ListFile_Free(pTextFile);
- }
-
- // Succeeded
- return nError;
-}
diff --git a/dep/CascLib/src/CascRootFile_TVFS.cpp b/dep/CascLib/src/CascRootFile_TVFS.cpp
new file mode 100644
index 00000000000..b115a215037
--- /dev/null
+++ b/dep/CascLib/src/CascRootFile_TVFS.cpp
@@ -0,0 +1,634 @@
+/*****************************************************************************/
+/* CascRootFile_TVFS.cpp Copyright (c) Ladislav Zezula 2018 */
+/*---------------------------------------------------------------------------*/
+/* ROOT handler for TACT VFS manifest format (root) */
+/* Note: TACT = Trusted Application Content Transfer */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 24.05.18 1.00 Lad The first version of CascRootFile_TVFS.cpp */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "CascLib.h"
+#include "CascCommon.h"
+
+//-----------------------------------------------------------------------------
+// Local defines
+
+#define TVFS_FLAG_INCLUDE_CKEY 0x0001 // Include C-key in content file record
+#define TVFS_FLAG_WRITE_SUPPORT 0x0002 // Write support. Include a table of encoding specifiers. This is required for writing files to the underlying storage. This bit is implied by the patch-support bit
+#define TVFS_FLAG_PATCH_SUPPORT 0x0004 // Patch support. Include patch records in the content file records.
+#define TVFS_FLAG_LOWERCASE_MANIFEST 0x0008 // Lowercase manifest. All paths in the path table have been converted to ASCII lowercase (i.e. [A-Z] converted to [a-z])
+
+#define TVFS_PTE_PATH_SEPARATOR_PRE 0x0001 // There is path separator before the name
+#define TVFS_PTE_PATH_SEPARATOR_POST 0x0002 // There is path separator after the name
+#define TVFS_PTE_NODE_VALUE 0x0004 // The NodeValue in path table entry is valid
+
+#define TVFS_FOLDER_NODE 0x80000000 // Highest bit is set if a file node is a folder
+#define TVFS_FOLDER_SIZE_MASK 0x7FFFFFFF // Mask to get length of the folder
+
+//-----------------------------------------------------------------------------
+// Local structures
+
+// In-memory layout of the TVFS file header
+typedef struct _TVFS_DIRECTORY_HEADER
+{
+ DWORD Signature; // Must be CASC_TVFS_ROOT_SIGNATURE
+ BYTE FormatVersion; // Version of the format. Should be 1.
+ BYTE HeaderSize; // Size of the header, in bytes
+ BYTE EKeySize; // Size of an E-Key. TACT uses 9-byte E-keys
+ BYTE PatchKeySize; // Size of a patch key. TACT uses 9-byte P-keys
+ DWORD Flags; // Flags. See TVFS_FLAG_XXX
+
+ // Followed by the offset table (variable length)
+ DWORD PathTableOffset; // Offset of the path table
+ DWORD PathTableSize; // Size of the path table
+ DWORD VfsTableOffset; // Offset of the VFS table
+ DWORD VfsTableSize; // Size of the VFS table
+ DWORD CftTableOffset; // Offset of the container file table
+ DWORD CftTableSize; // Size of the container file table
+ USHORT MaxDepth; // The maximum depth of the path prefix tree stored in the path table
+ DWORD EstTableOffset; // The offset of the encoding specifier table. Only if the write-support bit is set in the header flag
+ DWORD EstTableSize; // The size of the encoding specifier table. Only if the write-support bit is set in the header flag
+
+ DWORD CftOffsSize; // Byte length of the offset in the Content File Table entry
+ DWORD EstOffsSize; // Byte length of the offset in the Encoding Specifier Table entry
+
+ LPBYTE pbDirectoryData; // Pointer to the begin of directory data
+ LPBYTE pbDirectoryEnd; // Pointer to the end of directory data
+
+// LPBYTE pbPathFileTable; // Begin and end of the path table
+// LPBYTE pbPathTableEnd;
+
+// LPBYTE pbVfsFileTable; // Begin and end of the VFS file table
+// LPBYTE pbVfsTableEnd;
+
+// LPBYTE pbCftFileTable; // Begin and end of the content file table
+// LPBYTE pbCftTableEnd;
+
+} TVFS_DIRECTORY_HEADER, *PTVFS_DIRECTORY_HEADER;
+
+/*
+// Minimum size of a valid path table entry. 1 byte + 1-byte name + 1 byte + DWORD
+#define TVFS_HEADER_LENGTH FIELD_OFFSET(TVFS_DIRECTORY_HEADER, CftOffsSize)
+
+// Minimum size of a valid path table entry. 1 byte + 1-byte name + 1 byte + DWORD
+#define TVFS_MIN_PATH_ENTRY (1 + 1 + 1 + sizeof(DWORD))
+
+// Minimum size of the VFS entry (SpanCount + FileOffset + SpanLength + CftOffset)
+#define TVFS_MIN_VFS_ENTRY (1 + sizeof(DWORD) + sizeof(DWORD) + 1)
+
+// Minimum size of the Content File Table entry (CASC_EKEY_SIZE + EncodedSize + ContentSize)
+#define TVFS_MIN_CFT_ENTRY (CASC_EKEY_SIZE + sizeof(DWORD) + sizeof(DWORD))
+
+// Minimum size of the TVFS folder data
+#define TVFS_MIN_FILE_SIZE (TVFS_HEADER_LENGTH + TVFS_MIN_PATH_ENTRY + TVFS_MIN_VFS_ENTRY + TVFS_MIN_CFT_ENTRY)
+
+// Maximum estimated file table. Empirically set to 8 MB, increase if needed.
+#define TVFS_MAX_FILE_SIZE 0x00800000
+*/
+
+// In-memory layout of the path table entry
+typedef struct _TVFS_PATH_TABLE_ENTRY
+{
+ LPBYTE pbNamePtr; // Pointer to the begin of the node name
+ LPBYTE pbNameEnd; // Pointer to the end of the file name
+ DWORD NodeFlags; // TVFS_PTE_XXX
+ DWORD NodeValue; // Node value
+} TVFS_PATH_TABLE_ENTRY, *PTVFS_PATH_TABLE_ENTRY;
+
+//-----------------------------------------------------------------------------
+// Handler definition for TVFS root file
+
+//static FILE * fp = NULL;
+
+// Structure for the root handler
+struct TRootHandler_TVFS : public TFileTreeRoot
+{
+ public:
+
+ TRootHandler_TVFS() : TFileTreeRoot(0)
+ {
+ // TVFS supports file names, but DOESN'T support CKeys.
+ dwFeatures |= CASC_FEATURE_FILE_NAMES;
+ dwNestLevel = 0;
+ }
+
+ // Returns size of "container file table offset" fiels in the VFS.
+ // - If the container file table is larger than 0xffffff bytes, it's 4 bytes
+ // - If the container file table is larger than 0xffff bytes, it's 3 bytes
+ // - If the container file table is larger than 0xff bytes, it's 2 bytes
+ // - If the container file table is smaller than 0xff bytes, it's 1 byte
+ static DWORD GetOffsetFieldSize(DWORD dwTableSize)
+ {
+ if(dwTableSize > 0xffffff)
+ return 4;
+ if(dwTableSize > 0xffff)
+ return 3;
+ if(dwTableSize > 0xff)
+ return 2;
+ return 1;
+ }
+
+ bool PathBuffer_AddChar(PATH_BUFFER & PathBuffer, char chOneChar)
+ {
+ if(PathBuffer.szPtr >= PathBuffer.szEnd)
+ return false;
+
+ *PathBuffer.szPtr++ = chOneChar;
+ *PathBuffer.szPtr = 0;
+ return true;
+ }
+
+ bool PathBuffer_AddString(PATH_BUFFER & PathBuffer, LPBYTE pbNamePtr, LPBYTE pbNameEnd)
+ {
+ size_t nLength = (pbNameEnd - pbNamePtr);
+
+ // Check whether we have enough space
+ if ((PathBuffer.szPtr + nLength) > PathBuffer.szEnd)
+ return false;
+
+ // Copy the node name
+ memcpy(PathBuffer.szPtr, pbNamePtr, nLength);
+ PathBuffer.szPtr += nLength;
+ return true;
+ }
+
+ bool PathBuffer_AppendNode(PATH_BUFFER & PathBuffer, TVFS_PATH_TABLE_ENTRY & PathEntry)
+ {
+ // Append the prefix separator, if needed
+ if (PathEntry.NodeFlags & TVFS_PTE_PATH_SEPARATOR_PRE)
+ PathBuffer_AddChar(PathBuffer, '/');
+
+ // Append the name fragment, if any
+ if (PathEntry.pbNameEnd > PathEntry.pbNamePtr)
+ PathBuffer_AddString(PathBuffer, PathEntry.pbNamePtr, PathEntry.pbNameEnd);
+
+ // Append the postfix separator, if needed
+ if (PathEntry.NodeFlags & TVFS_PTE_PATH_SEPARATOR_POST)
+ PathBuffer_AddChar(PathBuffer, '/');
+
+ // Always end the buffer with zero
+ PathBuffer.szPtr[0] = 0;
+ return true;
+ }
+
+ static int CaptureDirectoryHeader(TVFS_DIRECTORY_HEADER & DirHeader, LPBYTE pbDataPtr, LPBYTE pbDataEnd)
+ {
+ // Fill the header structure with zeros
+ memset(&DirHeader, 0, sizeof(TVFS_DIRECTORY_HEADER));
+ DirHeader.pbDirectoryData = pbDataPtr;
+ DirHeader.pbDirectoryEnd = pbDataEnd;
+
+ // Capture the signature
+ pbDataPtr = CaptureInteger32(pbDataPtr, pbDataEnd, &DirHeader.Signature);
+ if(pbDataPtr == NULL || DirHeader.Signature != CASC_TVFS_ROOT_SIGNATURE)
+ return ERROR_BAD_FORMAT;
+
+ // Capture the other four integers
+ pbDataPtr = CaptureByteArray(pbDataPtr, pbDataEnd, 4, &DirHeader.FormatVersion);
+ if(pbDataPtr == NULL || DirHeader.FormatVersion != 1 || DirHeader.EKeySize != 9 || DirHeader.PatchKeySize != 9 || DirHeader.HeaderSize < 8)
+ return ERROR_BAD_FORMAT;
+
+ // Capture the rest
+ pbDataPtr = CaptureByteArray(pbDataPtr, pbDataEnd, DirHeader.HeaderSize - FIELD_OFFSET(TVFS_DIRECTORY_HEADER, Flags), (LPBYTE)(&DirHeader.Flags));
+ if(pbDataPtr == NULL)
+ return ERROR_BAD_FORMAT;
+
+ // Swap the header values
+ DirHeader.Flags = ConvertBytesToInteger_4_LE((LPBYTE)(&DirHeader.Flags));
+
+ // Swap the offset table values
+ DirHeader.PathTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.PathTableOffset));
+ DirHeader.PathTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.PathTableSize));
+ DirHeader.VfsTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.VfsTableOffset));
+ DirHeader.VfsTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.VfsTableSize));
+ DirHeader.CftTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.CftTableOffset));
+ DirHeader.CftTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.CftTableSize));
+ DirHeader.MaxDepth = (USHORT)ConvertBytesToInteger_2((LPBYTE)(&DirHeader.MaxDepth));
+ DirHeader.EstTableOffset = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.EstTableOffset));
+ DirHeader.EstTableSize = ConvertBytesToInteger_4((LPBYTE)(&DirHeader.EstTableSize));
+
+ // Determine size of file table offsets
+ DirHeader.CftOffsSize = GetOffsetFieldSize(DirHeader.CftTableSize);
+ DirHeader.EstOffsSize = GetOffsetFieldSize(DirHeader.EstTableSize);
+
+ // Capture the path table
+// DirHeader.pbPathFileTable = pbDirectory + DirHeader.PathTableOffset;
+// DirHeader.pbPathTableEnd = pbDirectory + DirHeader.PathTableOffset + DirHeader.PathTableSize;
+// if(DirHeader.pbPathTableEnd > pbDataEnd)
+// return ERROR_BAD_FORMAT;
+
+ // Capture the VFS file table
+// DirHeader.pbVfsFileTable = pbDirectory + DirHeader.VfsTableOffset;
+// DirHeader.pbVfsTableEnd = pbDirectory + DirHeader.VfsTableOffset + DirHeader.VfsTableSize;
+// if(DirHeader.pbVfsTableEnd > pbDataEnd)
+// return ERROR_BAD_FORMAT;
+
+ // Capture the container file table
+// DirHeader.pbCftFileTable = pbDirectory + DirHeader.CftTableOffset;
+// DirHeader.pbCftTableEnd = pbDirectory + DirHeader.CftTableOffset + DirHeader.CftTableSize;
+// if(DirHeader.pbCftTableEnd > pbDataEnd)
+// return ERROR_BAD_FORMAT;
+
+ return ERROR_SUCCESS;
+ }
+
+ int CaptureVfsSpanEntries(TVFS_DIRECTORY_HEADER & DirHeader, PENCODED_KEY pEKey, PDWORD PtrSpanSize, DWORD dwVfsOffset)
+ {
+ LPBYTE pbVfsFileTable = DirHeader.pbDirectoryData + DirHeader.VfsTableOffset;
+ LPBYTE pbVfsFileEntry = pbVfsFileTable + dwVfsOffset;
+ LPBYTE pbVfsFileEnd = pbVfsFileTable + DirHeader.VfsTableSize;
+ LPBYTE pbCftFileTable;
+ LPBYTE pbCftFileEntry;
+ LPBYTE pbCftFileEnd;
+ LPBYTE pbTemp = NULL;
+ size_t ItemSize = sizeof(DWORD) + sizeof(DWORD) + DirHeader.CftOffsSize;
+ DWORD dwCftOffset;
+ DWORD dwSpanCount;
+ DWORD dwSpanSize;
+
+ // Get the number of span entries
+ if(!(pbVfsFileTable <= pbVfsFileEntry && pbVfsFileEntry <= pbVfsFileEnd))
+ return ERROR_INVALID_PARAMETER;
+ dwSpanCount = *pbVfsFileEntry++;
+
+ // 1 - 224 = valid file, 225-254 = other file, 255 = deleted file
+ // We will ignore all files with unsupported span count
+ if(dwSpanCount == 0 || dwSpanCount > 224)
+ return ERROR_BAD_FORMAT;
+
+ // So far we've only saw entries with 1 span.
+ // Need to test files with multiple spans. Ignore such files for now.
+ assert(dwSpanCount == 1);
+
+ // Capture the array of span items
+ if(CaptureArray_(pbVfsFileEntry, pbVfsFileEnd, &pbTemp, ItemSize, dwSpanCount) == NULL)
+ return ERROR_BAD_FORMAT;
+
+ //
+ // Structure of the span entry:
+ // (4bytes): Offset into the referenced file (big endian)
+ // (4bytes): Size of the span (big endian)
+ // (?bytes): Offset into Container File Table. Length depends on container file table size
+ //
+
+ // Get the offset to the Container File Table
+ dwCftOffset = ConvertBytesToInteger_X(pbVfsFileEntry + sizeof(DWORD) + sizeof(DWORD), DirHeader.CftOffsSize);
+ dwSpanSize = ConvertBytesToInteger_4(pbVfsFileEntry + sizeof(DWORD));
+
+ // Go to the Container File Table and fetch the EKey from there
+ pbCftFileTable = DirHeader.pbDirectoryData + DirHeader.CftTableOffset;
+ pbCftFileEntry = pbCftFileTable + dwCftOffset;
+ pbCftFileEnd = pbCftFileTable + DirHeader.CftTableSize;
+ if((pbCftFileEntry + DirHeader.EKeySize) > pbCftFileEnd)
+ return ERROR_BAD_FORMAT;
+
+ // Give the pointer to the EKey
+ memcpy(pEKey->Value, pbCftFileEntry, DirHeader.EKeySize);
+ PtrSpanSize[0] = dwSpanSize;
+ return ERROR_SUCCESS;
+ }
+
+ //
+ // Structure of the path table entry:
+ // (1byte) 0x00 (optional) - means that there will be prefix path separator
+ // (1byte) File name length
+ // (?byte) File name
+ // (1byte) 0x00 (optional) - means that there will be postfix path separator
+ // (1byte) 0xFF (optional) - node value identifier
+ // (4byte) - node value
+ //
+ // Note: The path "data\archive\maps\file.bmp" could be cut into nodes like:
+ // data\0 (or data with subdirectory)
+ // arc
+ // hive\0
+ // maps\0 (or folder data)
+ // file.bmp
+ //
+
+ LPBYTE CapturePathEntry(TVFS_PATH_TABLE_ENTRY & PathEntry, LPBYTE pbPathTablePtr, LPBYTE pbPathTableEnd)
+ {
+ // Reset the path entry structure
+ PathEntry.pbNamePtr = pbPathTablePtr;
+ PathEntry.pbNameEnd = pbPathTablePtr;
+ PathEntry.NodeFlags = 0;
+ PathEntry.NodeValue = 0;
+
+ // Zero before the name means prefix path separator
+ if (pbPathTablePtr < pbPathTableEnd && pbPathTablePtr[0] == 0)
+ {
+ PathEntry.NodeFlags |= TVFS_PTE_PATH_SEPARATOR_PRE;
+ pbPathTablePtr++;
+ }
+
+ // Capture the length of the name fragment
+ if (pbPathTablePtr < pbPathTableEnd && pbPathTablePtr[0] != 0xFF)
+ {
+ // Capture length of the name fragment
+ size_t nLength = *pbPathTablePtr++;
+
+ if ((pbPathTablePtr + nLength) > pbPathTableEnd)
+ return NULL;
+ PathEntry.pbNamePtr = pbPathTablePtr;
+ PathEntry.pbNameEnd = pbPathTablePtr + nLength;
+ pbPathTablePtr += nLength;
+ }
+
+ // Zero after the name means postfix path separator
+ if (pbPathTablePtr < pbPathTableEnd && pbPathTablePtr[0] == 0)
+ {
+ PathEntry.NodeFlags |= TVFS_PTE_PATH_SEPARATOR_POST;
+ pbPathTablePtr++;
+ }
+
+ if (pbPathTablePtr < pbPathTableEnd)
+ {
+ // Check for node value
+ if (pbPathTablePtr[0] == 0xFF)
+ {
+ if ((pbPathTablePtr + 1 + sizeof(DWORD)) > pbPathTableEnd)
+ return NULL;
+ PathEntry.NodeValue = ConvertBytesToInteger_4(pbPathTablePtr + 1);
+ PathEntry.NodeFlags |= TVFS_PTE_NODE_VALUE;
+ pbPathTablePtr = pbPathTablePtr + 1 + sizeof(DWORD);
+ }
+
+ // Non-0xFF after the name means path separator after
+ else
+ {
+ PathEntry.NodeFlags |= TVFS_PTE_PATH_SEPARATOR_POST;
+ assert(pbPathTablePtr[0] != 0);
+ }
+ }
+
+ return pbPathTablePtr;
+ }
+
+ bool IsVfsFileEKey(TCascStorage * hs, ENCODED_KEY & EKey, size_t EKeyLength)
+ {
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ size_t ItemCount = hs->VfsRootList.ItemCount();
+
+ // Search the array
+ for (size_t i = 0; i < ItemCount; i++)
+ {
+ pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i);
+ if (pCKeyEntry != NULL)
+ {
+ if (!memcmp(pCKeyEntry->EKey, EKey.Value, EKeyLength))
+ return true;
+ }
+ }
+
+ // Not found in the VFS list
+ return false;
+ }
+
+ // This function verifies whether a file is actually a sub-directory.
+ // If yes, it contains just another "TVFS" virtual file system, just like the ROOT file.
+ int IsVfsSubDirectory(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, TVFS_DIRECTORY_HEADER & SubHeader, ENCODED_KEY & EKey, DWORD dwFileSize)
+ {
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ LPBYTE pbVfsData = NULL;
+ DWORD cbVfsData = dwFileSize;
+ int nError = ERROR_BAD_FORMAT;
+
+ // Verify whether the EKey is in the list of VFS root files
+ if(IsVfsFileEKey(hs, EKey, DirHeader.EKeySize))
+ {
+ // Locate the CKey entry
+ if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey.Value)) != NULL)
+ {
+ // Load the entire file into memory
+ pbVfsData = LoadInternalFileToMemory(hs, pCKeyEntry, &cbVfsData);
+ if (pbVfsData && cbVfsData)
+ {
+ // Capture the file folder. This also serves as test
+ nError = CaptureDirectoryHeader(SubHeader, pbVfsData, pbVfsData + cbVfsData);
+ if (nError == ERROR_SUCCESS)
+ return nError;
+
+ // Clear the captured header
+ memset(&SubHeader, 0, sizeof(TVFS_DIRECTORY_HEADER));
+ CASC_FREE(pbVfsData);
+ }
+ }
+ }
+
+ return nError;
+ }
+
+ void InsertRootVfsEntry(TCascStorage * hs, LPBYTE pbCKey, const char * szFormat, size_t nIndex)
+ {
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ char szFileName[0x20];
+
+ // The CKey entry must exist
+ if((pCKeyEntry = FindCKeyEntry_CKey(hs, pbCKey)) != NULL)
+ {
+ CascStrPrintf(szFileName, _countof(szFileName), szFormat, nIndex);
+ Insert(szFileName, pCKeyEntry);
+ }
+ }
+
+ DWORD ParsePathFileTable(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, PATH_BUFFER & PathBuffer, LPBYTE pbPathTablePtr, LPBYTE pbPathTableEnd)
+ {
+ TVFS_DIRECTORY_HEADER SubHeader;
+ TVFS_PATH_TABLE_ENTRY PathEntry;
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ ENCODED_KEY EKey;
+ char * szSavePathPtr = PathBuffer.szPtr;
+ DWORD dwSpanSize = 0;
+ int nError;
+
+ // Prepare the EKey structure to be filled with zeros
+ memset(&EKey, 0, sizeof(ENCODED_KEY));
+
+ // Parse the file table
+ while(pbPathTablePtr < pbPathTableEnd)
+ {
+ // Capture the single path table entry
+ pbPathTablePtr = CapturePathEntry(PathEntry, pbPathTablePtr, pbPathTableEnd);
+ if(pbPathTablePtr == NULL)
+ return ERROR_BAD_FORMAT;
+
+ // Append the node name to the total path. Also add backslash, if it's a folder
+ PathBuffer_AppendNode(PathBuffer, PathEntry);
+
+ // Folder component
+ if (PathEntry.NodeFlags & TVFS_PTE_NODE_VALUE)
+ {
+ // If the TVFS_FOLDER_NODE is set, then the path node is a directory,
+ // with its data immediately following the path node. Lower 31 bits of NodeValue
+ // contain the length of the directory (including the NodeValue!)
+ if (PathEntry.NodeValue & TVFS_FOLDER_NODE)
+ {
+ LPBYTE pbDirectoryEnd = pbPathTablePtr + (PathEntry.NodeValue & TVFS_FOLDER_SIZE_MASK) - sizeof(DWORD);
+
+ // Check the available data
+ assert((PathEntry.NodeValue & TVFS_FOLDER_SIZE_MASK) >= sizeof(DWORD));
+
+ // Recursively call the folder parser on the same file
+ nError = ParsePathFileTable(hs, DirHeader, PathBuffer, pbPathTablePtr, pbDirectoryEnd);
+ if (nError != ERROR_SUCCESS)
+ return nError;
+
+ // Skip the directory data
+ pbPathTablePtr = pbDirectoryEnd;
+ }
+ else
+ {
+ // Capture the VFS and Container Table Entry in order to get the file EKey
+ nError = CaptureVfsSpanEntries(DirHeader, &EKey, &dwSpanSize, PathEntry.NodeValue);
+ if (nError != ERROR_SUCCESS)
+ return nError;
+
+ // We need to check whether this is another TVFS directory file
+ if (IsVfsSubDirectory(hs, DirHeader, SubHeader, EKey, dwSpanSize) == ERROR_SUCCESS)
+ {
+ // Add colon (':')
+ PathBuffer_AddChar(PathBuffer, ':');
+
+ // Insert the file to the file tree
+ if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey.Value)) != NULL)
+ {
+ // The file content size should already be there
+ assert(pCKeyEntry->ContentSize == dwSpanSize);
+ FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
+ }
+
+ ParseDirectoryData(hs, SubHeader, PathBuffer);
+ CASC_FREE(SubHeader.pbDirectoryData);
+ }
+ else
+ {
+// if (fp != NULL)
+// {
+// fwrite(PathBuffer.szBegin, 1, (PathBuffer.szPtr - PathBuffer.szBegin), fp);
+// fprintf(fp, "\n");
+// }
+
+ // Insert the file to the file tree
+ if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey.Value)) != NULL)
+ {
+ // If the file content is not there, supply it now
+ if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
+ pCKeyEntry->ContentSize = dwSpanSize;
+ FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
+ }
+ }
+ }
+
+ // Reset the position of the path buffer
+ PathBuffer.szPtr = szSavePathPtr;
+ PathBuffer.szPtr[0] = 0;
+ }
+ }
+
+ // Return the total number of entries
+ return ERROR_SUCCESS;
+ }
+
+ int ParseDirectoryData(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, PATH_BUFFER & PathBuffer)
+ {
+ LPBYTE pbRootDirectory = DirHeader.pbDirectoryData + DirHeader.PathTableOffset;
+ LPBYTE pbRootDirPtr = pbRootDirectory;
+ LPBYTE pbRootDirEnd = pbRootDirPtr + DirHeader.PathTableSize;
+ DWORD dwNodeValue = 0;
+
+ // Most usually, there is a root directory in the folder
+ if((pbRootDirPtr + 1 + sizeof(DWORD)) < pbRootDirEnd)
+ {
+ //
+ // The structure of the root directory
+ // -----------------------------------
+ // 1byte 0xFF
+ // 4bytes NodeValue (BigEndian). The most significant bit is set
+ // - Lower 31 bits contain length of the directory data, including NodeValue
+ //
+
+ if(pbRootDirPtr[0] == 0xFF)
+ {
+ // Get the NodeValue and check its highest bit
+ if(CaptureInteger32_BE(pbRootDirPtr + 1, pbRootDirEnd, &dwNodeValue) == NULL || (dwNodeValue & TVFS_FOLDER_NODE) == 0)
+ return ERROR_BAD_FORMAT;
+
+ // Get the range of the root directory
+ pbRootDirEnd = pbRootDirPtr + 1 + (dwNodeValue & TVFS_FOLDER_SIZE_MASK);
+ pbRootDirPtr = pbRootDirPtr + 1 + sizeof(DWORD);
+
+ // Check the directory
+ if(pbRootDirEnd > (pbRootDirectory + DirHeader.PathTableSize))
+ return ERROR_BAD_FORMAT;
+ }
+ }
+
+ // Now go parse the path file table
+ return ParsePathFileTable(hs, DirHeader, PathBuffer, pbRootDirPtr, pbRootDirEnd);
+ }
+
+ int Load(TCascStorage * hs, TVFS_DIRECTORY_HEADER & RootHeader)
+ {
+// PCASC_CKEY_ENTRY pCKeyEntry;
+ PATH_BUFFER PathBuffer;
+ char szPathBuffer[MAX_PATH];
+
+ // Initialize the path buffer
+ memset(szPathBuffer, 0, sizeof(szPathBuffer));
+ PathBuffer.szBegin =
+ PathBuffer.szPtr = szPathBuffer;
+ PathBuffer.szEnd = szPathBuffer + MAX_PATH;
+
+ // Save the length of the key
+ FileTree.SetKeyLength(RootHeader.EKeySize);
+
+ // Insert the main VFS root file as named entry
+ InsertRootVfsEntry(hs, hs->VfsRoot.CKey, "vfs-root", 0);
+
+ // Insert all VFS roots folders as files
+ //for(size_t i = 0; i < hs->VfsRootList.ItemCount(); i++)
+ //{
+ // pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i);
+ // InsertRootVfsEntry(hs, pCKeyEntry->CKey, "vfs-%u", i+1);
+ //}
+
+ // Parse the entire directory data
+ return ParseDirectoryData(hs, RootHeader, PathBuffer);
+ }
+
+ DWORD dwNestLevel;
+};
+
+//-----------------------------------------------------------------------------
+// Public functions - TVFS root
+
+int RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
+{
+ TRootHandler_TVFS * pRootHandler = NULL;
+ TVFS_DIRECTORY_HEADER RootHeader;
+ int nError;
+
+ // Capture the entire root directory
+ nError = TRootHandler_TVFS::CaptureDirectoryHeader(RootHeader, pbRootFile, pbRootFile + cbRootFile);
+ if(nError == ERROR_SUCCESS)
+ {
+ // Allocate the root handler object
+ pRootHandler = new TRootHandler_TVFS();
+ if(pRootHandler != NULL)
+ {
+ // Load the root directory. If load failed, we free the object
+ nError = pRootHandler->Load(hs, RootHeader);
+ if(nError != ERROR_SUCCESS)
+ {
+ delete pRootHandler;
+ pRootHandler = NULL;
+ }
+ }
+ }
+
+ // Assign the root directory (or NULL) and return error
+ hs->pRootHandler = pRootHandler;
+ return nError;
+}
diff --git a/dep/CascLib/src/CascRootFile_Text.cpp b/dep/CascLib/src/CascRootFile_Text.cpp
new file mode 100644
index 00000000000..e78b0bf086f
--- /dev/null
+++ b/dep/CascLib/src/CascRootFile_Text.cpp
@@ -0,0 +1,121 @@
+/*****************************************************************************/
+/* CascRootFile_Text.cpp Copyright (c) Ladislav Zezula 2017 */
+/*---------------------------------------------------------------------------*/
+/* Support for generic ROOT handler with mapping of FileName -> CKey */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 28.10.15 1.00 Lad The first version of CascRootFile_Text.cpp */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "CascLib.h"
+#include "CascCommon.h"
+
+//-----------------------------------------------------------------------------
+// Handler definitions for Starcraft I root file
+
+struct TRootHandler_SC1 : public TFileTreeRoot
+{
+ public:
+
+ TRootHandler_SC1() : TFileTreeRoot(0)
+ {
+ // We have file names and return CKey as result of search
+ dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY);
+ }
+
+ static bool IsRootFile(LPBYTE pbRootFile, DWORD cbRootFile)
+ {
+ CASC_CSV Csv(1, false);
+ size_t nColumns;
+ bool bResult = false;
+
+ // Get the first line from the listfile
+ if(Csv.Load(pbRootFile, cbRootFile) == ERROR_SUCCESS)
+ {
+ // There must be 2 or 3 elements
+ nColumns = Csv[CSV_ZERO].GetColumnCount();
+ if (nColumns == 2 || nColumns == 3)
+ {
+ const CASC_CSV_COLUMN & FileName = Csv[CSV_ZERO][CSV_ZERO];
+ const CASC_CSV_COLUMN & CKeyStr = Csv[CSV_ZERO][1];
+
+ bResult = (FileName.szValue && CKeyStr.szValue && CKeyStr.nLength == MD5_STRING_SIZE);
+ }
+ }
+
+ // We need to reset the listfile to the begin position
+ return bResult;
+ }
+
+ int Load(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
+ {
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ CASC_CSV Csv(0, false);
+ BYTE CKey[MD5_HASH_SIZE];
+ int nError;
+
+ // Parse the ROOT file first in order to see whether we have the correct format
+ nError = Csv.Load(pbRootFile, cbRootFile);
+ if(nError == ERROR_SUCCESS)
+ {
+ // Parse all lines
+ while(Csv.LoadNextLine())
+ {
+ const CASC_CSV_COLUMN & FileName = Csv[CSV_ZERO][CSV_ZERO];
+ const CASC_CSV_COLUMN & CKeyStr = Csv[CSV_ZERO][1];
+
+ // Convert the CKey to binary
+ if(ConvertStringToBinary(CKeyStr.szValue, MD5_STRING_SIZE, CKey) == ERROR_SUCCESS)
+ {
+ // Verify whether it is a known entry
+ if((pCKeyEntry = FindCKeyEntry_CKey(hs, CKey)) != NULL)
+ {
+ // Insert the FileName+CKey to the file tree
+ FileTree.InsertByName(pCKeyEntry, FileName.szValue);
+ }
+ }
+ }
+ }
+
+ return nError;
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+//
+// Starcraft ROOT file is a text file with the following format:
+// HD2/portraits/NBluCrit/NLCFID01.webm|c2795b120592355d45eba9cdc37f691e
+// locales/enUS/Assets/campaign/EXPZerg/Zerg08/staredit/wav/zovtra01.ogg|316b0274bf2dabaa8db60c3ff1270c85
+// locales/zhCN/Assets/sound/terran/ghost/tghdth01.wav|6637ed776bd22089e083b8b0b2c0374c
+//
+
+int RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
+{
+ TRootHandler_SC1 * pRootHandler = NULL;
+ int nError = ERROR_BAD_FORMAT;
+
+ // Verify whether this looks like a Starcraft I root file
+ if(TRootHandler_SC1::IsRootFile(pbRootFile, cbRootFile))
+ {
+ // Allocate the root handler object
+ pRootHandler = new TRootHandler_SC1();
+ if(pRootHandler != NULL)
+ {
+ // Load the root directory. If load failed, we free the object
+ nError = pRootHandler->Load(hs, pbRootFile, cbRootFile);
+ if(nError != ERROR_SUCCESS)
+ {
+ delete pRootHandler;
+ pRootHandler = NULL;
+ }
+ }
+ }
+
+ // Assign the root directory (or NULL) and return error
+ hs->pRootHandler = pRootHandler;
+ return nError;
+}
diff --git a/dep/CascLib/src/CascRootFile_WoW.cpp b/dep/CascLib/src/CascRootFile_WoW.cpp
new file mode 100644
index 00000000000..780220bbe40
--- /dev/null
+++ b/dep/CascLib/src/CascRootFile_WoW.cpp
@@ -0,0 +1,481 @@
+/*****************************************************************************/
+/* CascRootFile_WoW.cpp Copyright (c) Ladislav Zezula 2014 */
+/*---------------------------------------------------------------------------*/
+/* Storage functions for CASC */
+/* Note: WoW offsets refer to WoW.exe 6.0.3.19116 (32-bit) */
+/* SHA1: c10e9ffb7d040a37a356b96042657e1a0c95c0dd */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 29.04.14 1.00 Lad The first version of CascRootFile_WoW.cpp */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "CascLib.h"
+#include "CascCommon.h"
+
+//-----------------------------------------------------------------------------
+// Local structures
+
+#define ROOT_SEARCH_PHASE_INITIALIZING 0
+#define ROOT_SEARCH_PHASE_LISTFILE 1
+#define ROOT_SEARCH_PHASE_NAMELESS 2
+#define ROOT_SEARCH_PHASE_FINISHED 3
+
+// Known dwRegion values returned from sub_661316 (7.0.3.22210 x86 win), also referred by lua GetCurrentRegion
+#define WOW_REGION_US 0x01
+#define WOW_REGION_KR 0x02
+#define WOW_REGION_EU 0x03
+#define WOW_REGION_TW 0x04
+#define WOW_REGION_CN 0x05
+
+typedef enum _ROOT_FORMAT
+{
+ RootFormatWoW6x, // WoW 6.x - 8.1.x
+ RootFormatWoW82 // WoW 8.2 or newer
+} ROOT_FORMAT, *PROOT_FORMAT;
+
+// ROOT file header, since 8.2
+typedef struct _FILE_ROOT_HEADER_82
+{
+ DWORD Signature; // Must be CASC_WOW82_ROOT_SIGNATURE
+ DWORD TotalFiles;
+ DWORD FilesWithNameHash;
+} FILE_ROOT_HEADER_82, *PFILE_ROOT_HEADER_82;
+
+// On-disk version of root group. A root group contains a group of file
+// with the same locale and file flags
+typedef struct _FILE_ROOT_GROUP_HEADER
+{
+ DWORD NumberOfFiles; // Number of entries
+ DWORD ContentFlags;
+ DWORD LocaleFlags; // File locale mask (CASC_LOCALE_XXX)
+
+ // Followed by a block of file data IDs (count: NumberOfFiles)
+ // Followed by the MD5 and file name hash (count: NumberOfFiles)
+
+} FILE_ROOT_GROUP_HEADER, *PFILE_ROOT_GROUP_HEADER;
+
+// On-disk version of root entry. Only present in versions 6.x - 8.1.xx
+// Each root entry represents one file in the CASC storage
+// In WoW 8.2 and newer, CKey and FileNameHash are split into separate arrays
+// and FileNameHash is optional
+typedef struct _FILE_ROOT_ENTRY
+{
+ CONTENT_KEY CKey; // MD5 of the file
+ ULONGLONG FileNameHash; // Jenkins hash of the file name
+
+} FILE_ROOT_ENTRY, *PFILE_ROOT_ENTRY;
+
+typedef struct _FILE_ROOT_GROUP
+{
+ FILE_ROOT_GROUP_HEADER Header;
+ PDWORD FileDataIds; // Pointer to the array of File Data IDs
+
+ PFILE_ROOT_ENTRY pRootEntries; // Valid for WoW 6.x - 8.1.x
+ PCONTENT_KEY pCKeyEntries; // Valid for WoW 8.2 or newer
+ PULONGLONG pHashes; // Valid for WoW 8.2 or newer (optional)
+
+} FILE_ROOT_GROUP, *PFILE_ROOT_GROUP;
+
+//-----------------------------------------------------------------------------
+// TRootHandler_WoW interface / implementation
+
+#define FTREE_FLAGS_WOW (FTREE_FLAG_USE_DATA_ID | FTREE_FLAG_USE_LOCALE_FLAGS | FTREE_FLAG_USE_CONTENT_FLAGS)
+
+struct TRootHandler_WoW : public TFileTreeRoot
+{
+ public:
+
+ TRootHandler_WoW(ROOT_FORMAT RFormat, DWORD HashlessFileCount) : TFileTreeRoot(FTREE_FLAGS_WOW)
+ {
+ // Turn off the "we know file names" bit
+ FileCounterHashless = HashlessFileCount;
+ FileCounter = 0;
+ RootFormat = RFormat;
+
+ // Update the flags based on format
+ switch(RootFormat)
+ {
+ case RootFormatWoW6x:
+ dwFeatures |= CASC_FEATURE_ROOT_CKEY | CASC_FEATURE_FNAME_HASHES | CASC_FEATURE_FILE_DATA_IDS | CASC_FEATURE_LOCALE_FLAGS | CASC_FEATURE_CONTENT_FLAGS;
+ break;
+
+ case RootFormatWoW82:
+ dwFeatures |= CASC_FEATURE_ROOT_CKEY | CASC_FEATURE_FNAME_HASHES_OPTIONAL | CASC_FEATURE_FILE_DATA_IDS | CASC_FEATURE_LOCALE_FLAGS | CASC_FEATURE_CONTENT_FLAGS;
+ break;
+ }
+ }
+
+ static LPBYTE CaptureRootHeader(FILE_ROOT_HEADER_82 & RootHeader, LPBYTE pbRootPtr, LPBYTE pbRootEnd)
+ {
+ // Validate the root file header
+ if((pbRootPtr + sizeof(FILE_ROOT_HEADER_82)) >= pbRootEnd)
+ return NULL;
+ memcpy(&RootHeader, pbRootPtr, sizeof(FILE_ROOT_HEADER_82));
+
+ // Verify the root file header
+ if(RootHeader.Signature != CASC_WOW82_ROOT_SIGNATURE)
+ return NULL;
+ if(RootHeader.FilesWithNameHash > RootHeader.TotalFiles)
+ return NULL;
+
+ return pbRootPtr + sizeof(FILE_ROOT_HEADER_82);
+ }
+
+ LPBYTE CaptureRootGroup(FILE_ROOT_GROUP & RootGroup, LPBYTE pbRootPtr, LPBYTE pbRootEnd)
+ {
+ // Reset the entire root group structure
+ memset(&RootGroup, 0, sizeof(FILE_ROOT_GROUP));
+
+ // Validate the locale block header
+ if((pbRootPtr + sizeof(FILE_ROOT_GROUP_HEADER)) >= pbRootEnd)
+ return NULL;
+ memcpy(&RootGroup.Header, pbRootPtr, sizeof(FILE_ROOT_GROUP_HEADER));
+ pbRootPtr = pbRootPtr + sizeof(FILE_ROOT_GROUP_HEADER);
+
+ // Validate the array of file data IDs
+ if((pbRootPtr + (sizeof(DWORD) * RootGroup.Header.NumberOfFiles)) >= pbRootEnd)
+ return NULL;
+ RootGroup.FileDataIds = (PDWORD)pbRootPtr;
+ pbRootPtr = pbRootPtr + (sizeof(DWORD) * RootGroup.Header.NumberOfFiles);
+
+ // Add the number of files in this block to the number of files loaded
+ FileCounter += RootGroup.Header.NumberOfFiles;
+
+ // Validate the array of root entries
+ switch(RootFormat)
+ {
+ case RootFormatWoW6x:
+ if((pbRootPtr + (sizeof(FILE_ROOT_ENTRY) * RootGroup.Header.NumberOfFiles)) > pbRootEnd)
+ return NULL;
+ RootGroup.pRootEntries = (PFILE_ROOT_ENTRY)pbRootPtr;
+
+ // Return the position of the next block
+ return pbRootPtr + (sizeof(FILE_ROOT_ENTRY) * RootGroup.Header.NumberOfFiles);
+
+ case RootFormatWoW82:
+
+ // Verify the position of array of CONTENT_KEY
+ if((pbRootPtr + (sizeof(CONTENT_KEY) * RootGroup.Header.NumberOfFiles)) > pbRootEnd)
+ return NULL;
+ RootGroup.pCKeyEntries = (PCONTENT_KEY)pbRootPtr;
+ pbRootPtr = pbRootPtr + (sizeof(CONTENT_KEY) * RootGroup.Header.NumberOfFiles);
+
+ // Also include array of file hashes
+ if(FileCounter > FileCounterHashless)
+ {
+ if((pbRootPtr + (sizeof(ULONGLONG) * RootGroup.Header.NumberOfFiles)) > pbRootEnd)
+ return NULL;
+ RootGroup.pHashes = (PULONGLONG)pbRootPtr;
+ pbRootPtr = pbRootPtr + (sizeof(ULONGLONG) * RootGroup.Header.NumberOfFiles);
+ }
+
+ return pbRootPtr;
+
+ default:
+ return NULL;
+ }
+ }
+
+ int ParseWowRootFile_AddFiles_6x(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup)
+ {
+ PFILE_ROOT_ENTRY pRootEntry = RootGroup.pRootEntries;
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ DWORD FileDataId = 0;
+
+ // Sanity check
+ assert(RootGroup.pRootEntries != NULL);
+
+ // WoW.exe (build 19116): Blocks with zero files are skipped
+ for(DWORD i = 0; i < RootGroup.Header.NumberOfFiles; i++, pRootEntry++)
+ {
+ // Set the file data ID
+ FileDataId = FileDataId + RootGroup.FileDataIds[i];
+// BREAKIF(FileDataId == 2823765);
+
+ // Find the item in the central storage. Insert it to the tree
+ if((pCKeyEntry = FindCKeyEntry_CKey(hs, pRootEntry->CKey.Value)) != NULL)
+ {
+ if(pRootEntry->FileNameHash != 0)
+ {
+ FileTree.InsertByHash(pCKeyEntry, pRootEntry->FileNameHash, FileDataId, RootGroup.Header.LocaleFlags, RootGroup.Header.ContentFlags);
+ }
+ else
+ {
+ FileTree.InsertById(pCKeyEntry, FileDataId, RootGroup.Header.LocaleFlags, RootGroup.Header.ContentFlags);
+ }
+ }
+
+ // Update the file data ID
+ assert((FileDataId + 1) > FileDataId);
+ FileDataId++;
+ }
+
+ return ERROR_SUCCESS;
+ }
+
+ int ParseWowRootFile_AddFiles_82(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup)
+ {
+ PCASC_CKEY_ENTRY pCKeyEntry;
+ PCONTENT_KEY pCKey = RootGroup.pCKeyEntries;
+ DWORD FileDataId = 0;
+
+ // Sanity check
+ assert(RootGroup.pCKeyEntries != NULL);
+
+ // WoW.exe (build 19116): Blocks with zero files are skipped
+ for(DWORD i = 0; i < RootGroup.Header.NumberOfFiles; i++, pCKey++)
+ {
+ // Set the file data ID
+ FileDataId = FileDataId + RootGroup.FileDataIds[i];
+
+ // Find the item in the central storage. Insert it to the tree
+ if((pCKeyEntry = FindCKeyEntry_CKey(hs, pCKey->Value)) != NULL)
+ {
+ // If we know the file name hash, we're gonna insert it by hash AND file data id.
+ // If we don't know the hash, we're gonna insert it just by file data id.
+ if(RootGroup.pHashes != NULL && RootGroup.pHashes[i] != 0)
+ {
+ FileTree.InsertByHash(pCKeyEntry, RootGroup.pHashes[i], FileDataId, RootGroup.Header.LocaleFlags, RootGroup.Header.ContentFlags);
+ }
+ else
+ {
+ FileTree.InsertById(pCKeyEntry, FileDataId, RootGroup.Header.LocaleFlags, RootGroup.Header.ContentFlags);
+ }
+ }
+
+ // Update the file data ID
+ assert((FileDataId + 1) > FileDataId);
+ FileDataId++;
+ }
+
+ return ERROR_SUCCESS;
+ }
+
+ int ParseWowRootFile_Level2(
+ TCascStorage * hs,
+ LPBYTE pbRootPtr,
+ LPBYTE pbRootEnd,
+ DWORD dwLocaleMask,
+ BYTE bOverrideLowViolence,
+ BYTE bAudioLocale)
+ {
+ FILE_ROOT_GROUP RootBlock;
+
+ // Reset the total file counter
+ FileCounter = 0;
+
+ // Now parse the root file
+ while(pbRootPtr < pbRootEnd)
+ {
+ // Validate the file locale block
+ pbRootPtr = CaptureRootGroup(RootBlock, pbRootPtr, pbRootEnd);
+ if(pbRootPtr == NULL)
+ return ERROR_BAD_FORMAT;
+
+ // WoW.exe (build 19116): Entries with flag 0x100 set are skipped
+ if(RootBlock.Header.ContentFlags & CASC_CFLAG_DONT_LOAD)
+ continue;
+
+ // WoW.exe (build 19116): Entries with flag 0x80 set are skipped if overrideArchive CVAR is set to FALSE (which is by default in non-chinese clients)
+ if((RootBlock.Header.ContentFlags & CASC_CFLAG_LOW_VIOLENCE) && bOverrideLowViolence == 0)
+ continue;
+
+ // WoW.exe (build 19116): Entries with (flags >> 0x1F) not equal to bAudioLocale are skipped
+ if((RootBlock.Header.ContentFlags >> 0x1F) != bAudioLocale)
+ continue;
+
+ // WoW.exe (build 19116): Locales other than defined mask are skipped too
+ if((RootBlock.Header.LocaleFlags & dwLocaleMask) == 0)
+ continue;
+
+ // Now call the custom function
+ switch(RootFormat)
+ {
+ case RootFormatWoW82:
+ ParseWowRootFile_AddFiles_82(hs, RootBlock);
+ break;
+
+ case RootFormatWoW6x:
+ ParseWowRootFile_AddFiles_6x(hs, RootBlock);
+ break;
+
+ default:
+ return ERROR_NOT_SUPPORTED;
+ }
+ }
+
+ return ERROR_SUCCESS;
+ }
+
+ /*
+ #define CASC_LOCALE_BIT_ENUS 0x01
+ #define CASC_LOCALE_BIT_KOKR 0x02
+ #define CASC_LOCALE_BIT_RESERVED 0x03
+ #define CASC_LOCALE_BIT_FRFR 0x04
+ #define CASC_LOCALE_BIT_DEDE 0x05
+ #define CASC_LOCALE_BIT_ZHCN 0x06
+ #define CASC_LOCALE_BIT_ESES 0x07
+ #define CASC_LOCALE_BIT_ZHTW 0x08
+ #define CASC_LOCALE_BIT_ENGB 0x09
+ #define CASC_LOCALE_BIT_ENCN 0x0A
+ #define CASC_LOCALE_BIT_ENTW 0x0B
+ #define CASC_LOCALE_BIT_ESMX 0x0C
+ #define CASC_LOCALE_BIT_RURU 0x0D
+ #define CASC_LOCALE_BIT_PTBR 0x0E
+ #define CASC_LOCALE_BIT_ITIT 0x0F
+ #define CASC_LOCALE_BIT_PTPT 0x10
+
+ // dwLocale is obtained from a WOW_LOCALE_* to CASC_LOCALE_BIT_* mapping (sub_6615D0 in 7.0.3.22210 x86 win)
+ // because (ENUS, ENGB) and (PTBR, PTPT) pairs share the same value on WOW_LOCALE_* enum
+ // dwRegion is used to distinguish them
+ if(dwRegion == WOW_REGION_EU)
+ {
+ // Is this english version of WoW?
+ if(dwLocale == CASC_LOCALE_BIT_ENUS)
+ {
+ LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, CASC_LOCALE_ENGB, bOverrideArchive, bAudioLocale);
+ LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, CASC_LOCALE_ENUS, bOverrideArchive, bAudioLocale);
+ return ERROR_SUCCESS;
+ }
+
+ // Is this portuguese version of WoW?
+ if(dwLocale == CASC_LOCALE_BIT_PTBR)
+ {
+ LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, CASC_LOCALE_PTPT, bOverrideArchive, bAudioLocale);
+ LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, CASC_LOCALE_PTBR, bOverrideArchive, bAudioLocale);
+ }
+ }
+ else
+ LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, (1 << dwLocale), bOverrideArchive, bAudioLocale);
+ */
+
+ int ParseWowRootFile_Level1(
+ TCascStorage * hs,
+ LPBYTE pbRootPtr,
+ LPBYTE pbRootEnd,
+ DWORD dwLocaleMask,
+ BYTE bAudioLocale)
+ {
+ int nError;
+
+ // Load the locale as-is
+ nError = ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, dwLocaleMask, false, bAudioLocale);
+ if (nError != ERROR_SUCCESS)
+ return nError;
+
+ // If we wanted enGB, we also load enUS for the missing files
+ if(dwLocaleMask == CASC_LOCALE_ENGB)
+ ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, CASC_LOCALE_ENUS, false, bAudioLocale);
+
+ if(dwLocaleMask == CASC_LOCALE_PTPT)
+ ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, CASC_LOCALE_PTBR, false, bAudioLocale);
+
+ return ERROR_SUCCESS;
+ }
+
+ // WoW.exe: 004146C7 (BuildManifest::Load)
+ int Load(TCascStorage * hs, LPBYTE pbRootPtr, LPBYTE pbRootEnd, DWORD dwLocaleMask)
+ {
+ int nError;
+
+ nError = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 0);
+ if (nError == ERROR_SUCCESS)
+ nError = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 1);
+
+ return nError;
+ }
+
+ // Search for files
+ PCASC_CKEY_ENTRY Search(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
+ {
+ // If we have a listfile, we'll feed the listfile entries to the file tree
+ if(pSearch->pCache != NULL && pSearch->bListFileUsed == false)
+ {
+ PCASC_FILE_NODE pFileNode;
+ ULONGLONG FileNameHash;
+ DWORD FileDataId = CASC_INVALID_ID;
+ char szFileName[MAX_PATH];
+
+ if(RootFormat == RootFormatWoW82)
+ {
+ // Keep going through the listfile
+ while(ListFile_GetNext(pSearch->pCache, szFileName, _countof(szFileName), &FileDataId))
+ {
+ // Try to find the file node by file data id
+ pFileNode = FileTree.FindById(FileDataId);
+ if(pFileNode != NULL && pFileNode->NameLength == 0)
+ {
+ FileTree.SetNodeFileName(pFileNode, szFileName);
+ }
+ }
+ }
+ else
+ {
+ // Keep going through the listfile
+ while(ListFile_GetNextLine(pSearch->pCache, szFileName, MAX_PATH))
+ {
+ // Calculate the hash of the file name
+ FileNameHash = CalcFileNameHash(szFileName);
+
+ // Try to find the file node by file name hash
+ pFileNode = FileTree.Find(FileNameHash);
+ if(pFileNode != NULL && pFileNode->NameLength == 0)
+ {
+ FileTree.SetNodeFileName(pFileNode, szFileName);
+ }
+ }
+ }
+ pSearch->bListFileUsed = true;
+ }
+
+ // Let the file tree root give us the file names
+ return TFileTreeRoot::Search(pSearch, pFindData);
+ }
+
+ ROOT_FORMAT RootFormat; // Root file format
+ DWORD FileCounterHashless; // Number of files for which we don't have hash. Meaningless for WoW before 8.2
+ DWORD FileCounter; // Counter of loaded files. Only used during loading of ROOT file
+};
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+int RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask)
+{
+ TRootHandler_WoW * pRootHandler = NULL;
+ FILE_ROOT_HEADER_82 RootHeader;
+ ROOT_FORMAT RootFormat = RootFormatWoW6x;
+ LPBYTE pbRootEnd = pbRootFile + cbRootFile;
+ LPBYTE pbRootPtr;
+ DWORD FileCounterHashless = 0;
+ int nError = ERROR_BAD_FORMAT;
+
+ // Check for the new format (World of Warcraft 8.2, build 30170)
+ pbRootPtr = TRootHandler_WoW::CaptureRootHeader(RootHeader, pbRootFile, pbRootEnd);
+ if(pbRootPtr != NULL)
+ {
+ FileCounterHashless = RootHeader.TotalFiles - RootHeader.FilesWithNameHash;
+ RootFormat = RootFormatWoW82;
+ pbRootFile = pbRootPtr;
+ }
+
+ // Create the WOW handler
+ pRootHandler = new TRootHandler_WoW(RootFormat, FileCounterHashless);
+ if(pRootHandler != NULL)
+ {
+ // Load the root directory. If load failed, we free the object
+ nError = pRootHandler->Load(hs, pbRootFile, pbRootEnd, dwLocaleMask);
+ if(nError != ERROR_SUCCESS)
+ {
+ delete pRootHandler;
+ pRootHandler = NULL;
+ }
+ }
+
+ // Assign the root directory (or NULL) and return error
+ hs->pRootHandler = pRootHandler;
+ return nError;
+}
+
diff --git a/dep/CascLib/src/CascRootFile_WoW6.cpp b/dep/CascLib/src/CascRootFile_WoW6.cpp
deleted file mode 100644
index 74805d07620..00000000000
--- a/dep/CascLib/src/CascRootFile_WoW6.cpp
+++ /dev/null
@@ -1,595 +0,0 @@
-/*****************************************************************************/
-/* CascOpenStorage.cpp Copyright (c) Ladislav Zezula 2014 */
-/*---------------------------------------------------------------------------*/
-/* Storage functions for CASC */
-/* Note: WoW6 offsets refer to WoW.exe 6.0.3.19116 (32-bit) */
-/* SHA1: c10e9ffb7d040a37a356b96042657e1a0c95c0dd */
-/*---------------------------------------------------------------------------*/
-/* Date Ver Who Comment */
-/* -------- ---- --- ------- */
-/* 29.04.14 1.00 Lad The first version of CascOpenStorage.cpp */
-/*****************************************************************************/
-
-#define __CASCLIB_SELF__
-#include "CascLib.h"
-#include "CascCommon.h"
-
-//-----------------------------------------------------------------------------
-// Local structures
-
-#define ROOT_SEARCH_PHASE_INITIALIZING 0
-#define ROOT_SEARCH_PHASE_LISTFILE 1
-#define ROOT_SEARCH_PHASE_NAMELESS 2
-#define ROOT_SEARCH_PHASE_FINISHED 2
-
-// On-disk version of locale block
-typedef struct _FILE_LOCALE_BLOCK
-{
- DWORD NumberOfFiles; // Number of entries
- DWORD Flags;
- DWORD Locales; // File locale mask (CASC_LOCALE_XXX)
-
- // Followed by a block of 32-bit integers (count: NumberOfFiles)
- // Followed by the MD5 and file name hash (count: NumberOfFiles)
-
-} FILE_LOCALE_BLOCK, *PFILE_LOCALE_BLOCK;
-
-// On-disk version of root entry
-typedef struct _FILE_ROOT_ENTRY
-{
- ENCODING_KEY EncodingKey; // MD5 of the file
- ULONGLONG FileNameHash; // Jenkins hash of the file name
-
-} FILE_ROOT_ENTRY, *PFILE_ROOT_ENTRY;
-
-
-typedef struct _CASC_ROOT_BLOCK
-{
- PFILE_LOCALE_BLOCK pLocaleBlockHdr; // Pointer to the locale block
- PDWORD FileDataIds; // Pointer to the array of File Data IDs
- PFILE_ROOT_ENTRY pRootEntries;
-
-} CASC_ROOT_BLOCK, *PCASC_ROOT_BLOCK;
-
-// Root file entry for CASC storages without MNDX root file (World of Warcraft 6.0+)
-// Does not match to the in-file structure of the root entry
-typedef struct _CASC_FILE_ENTRY
-{
- ENCODING_KEY EncodingKey; // File encoding key (MD5)
- ULONGLONG FileNameHash; // Jenkins hash of the file name
- DWORD FileDataId; // File Data Index
- DWORD Locales; // Locale flags of the file
-
-} CASC_FILE_ENTRY, *PCASC_FILE_ENTRY;
-
-struct TRootHandler_WoW6 : public TRootHandler
-{
- // Linear global list of file entries
- DYNAMIC_ARRAY FileTable;
- DYNAMIC_ARRAY FileDataIdLookupTable;
-
- // Global map of FileName -> FileEntry
- PCASC_MAP pRootMap;
-
- // For counting files
- DWORD dwTotalFileCount;
-};
-
-// Prototype for root file parsing routine
-typedef int (*PARSE_ROOT)(TRootHandler_WoW6 * pRootHandler, PCASC_ROOT_BLOCK pBlockInfo);
-
-//-----------------------------------------------------------------------------
-// Local functions
-
-static bool IsFileDataIdName(const char * szFileName)
-{
- BYTE BinaryValue[4];
-
- // File name must begin with "File", case insensitive
- if(AsciiToUpperTable_BkSlash[szFileName[0]] == 'F' &&
- AsciiToUpperTable_BkSlash[szFileName[1]] == 'I' &&
- AsciiToUpperTable_BkSlash[szFileName[2]] == 'L' &&
- AsciiToUpperTable_BkSlash[szFileName[3]] == 'E')
- {
- // Then, 8 hexadecimal digits must follow
- if(ConvertStringToBinary(szFileName + 4, 8, BinaryValue) == ERROR_SUCCESS)
- {
- // Must be followed by an extension or end-of-string
- return (szFileName[0x0C] == 0 || szFileName[0x0C] == '.');
- }
- }
-
- return false;
-}
-
-static int FileDataIdCompare(const void *, const void * pvFile1, const void * pvFile2)
-{
- return ((PCASC_FILE_ENTRY)pvFile1)->FileDataId - ((PCASC_FILE_ENTRY)pvFile2)->FileDataId;
-}
-
-// Search by FileDataId
-PCASC_FILE_ENTRY FindRootEntry(DYNAMIC_ARRAY & FileTable, DWORD FileDataId)
-{
- PCASC_FILE_ENTRY* pStartEntry = (PCASC_FILE_ENTRY*)FileTable.ItemArray;
- PCASC_FILE_ENTRY* pMidlEntry;
- PCASC_FILE_ENTRY* pEndEntry = pStartEntry + FileTable.ItemCount - 1;
- int nResult;
-
- // Perform binary search on the table
- while(pStartEntry < pEndEntry)
- {
- // Calculate the middle of the interval
- pMidlEntry = pStartEntry + ((pEndEntry - pStartEntry) / 2);
-
- // Did we find it?
- nResult = (int)FileDataId - (int)(*pMidlEntry)->FileDataId;
- if(nResult == 0)
- return *pMidlEntry;
-
- // Move the interval to the left or right
- (nResult < 0) ? pEndEntry = pMidlEntry : pStartEntry = pMidlEntry + 1;
- }
-
- return NULL;
-}
-
-// Search by file name hash
-// Also used in CascSearchFile
-PCASC_FILE_ENTRY FindRootEntry(PCASC_MAP pRootMap, const char * szFileName, DWORD * PtrTableIndex)
-{
- // Calculate the HASH value of the normalized file name
- ULONGLONG FileNameHash = CalcFileNameHash(szFileName);
-
- // Perform the hash search
- return (PCASC_FILE_ENTRY)Map_FindObject(pRootMap, &FileNameHash, PtrTableIndex);
-}
-
-LPBYTE VerifyLocaleBlock(PCASC_ROOT_BLOCK pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd)
-{
- // Validate the file locale block
- pBlockInfo->pLocaleBlockHdr = (PFILE_LOCALE_BLOCK)pbFilePointer;
- pbFilePointer = (LPBYTE)(pBlockInfo->pLocaleBlockHdr + 1);
- if(pbFilePointer > pbFileEnd)
- return NULL;
-
- // Validate the array of 32-bit integers
- pBlockInfo->FileDataIds = (PDWORD)pbFilePointer;
- pbFilePointer = (LPBYTE)(pBlockInfo->FileDataIds + pBlockInfo->pLocaleBlockHdr->NumberOfFiles);
- if(pbFilePointer > pbFileEnd)
- return NULL;
-
- // Validate the array of root entries
- pBlockInfo->pRootEntries = (PFILE_ROOT_ENTRY)pbFilePointer;
- pbFilePointer = (LPBYTE)(pBlockInfo->pRootEntries + pBlockInfo->pLocaleBlockHdr->NumberOfFiles);
- if(pbFilePointer > pbFileEnd)
- return NULL;
-
- // Return the position of the next block
- return pbFilePointer;
-}
-
-static int ParseRoot_CountFiles(
- TRootHandler_WoW6 * pRootHandler,
- PCASC_ROOT_BLOCK pRootBlock)
-{
- // Add the file count to the total file count
- pRootHandler->dwTotalFileCount += pRootBlock->pLocaleBlockHdr->NumberOfFiles;
- return ERROR_SUCCESS;
-}
-
-static int ParseRoot_AddRootEntries(
- TRootHandler_WoW6 * pRootHandler,
- PCASC_ROOT_BLOCK pRootBlock)
-{
- PCASC_FILE_ENTRY pFileEntry;
- DWORD dwFileDataId = 0;
-
- // Sanity checks
- assert(pRootHandler->FileTable.ItemArray != NULL);
- assert(pRootHandler->FileTable.ItemCountMax != 0);
- assert(pRootHandler->FileDataIdLookupTable.ItemArray != NULL);
- assert(pRootHandler->FileDataIdLookupTable.ItemCountMax != 0);
-
- // WoW.exe (build 19116): Blocks with zero files are skipped
- for(DWORD i = 0; i < pRootBlock->pLocaleBlockHdr->NumberOfFiles; i++)
- {
- // Create new entry, with overflow check
- if(pRootHandler->FileTable.ItemCount >= pRootHandler->FileTable.ItemCountMax)
- return ERROR_INSUFFICIENT_BUFFER;
- pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1);
-
- if (pRootHandler->FileDataIdLookupTable.ItemCount >= pRootHandler->FileDataIdLookupTable.ItemCountMax)
- return ERROR_INSUFFICIENT_BUFFER;
- Array_Insert(&pRootHandler->FileDataIdLookupTable, &pFileEntry, 1);
-
- // (004147A3) Prepare the CASC_FILE_ENTRY structure
- pFileEntry->FileNameHash = pRootBlock->pRootEntries[i].FileNameHash;
- pFileEntry->FileDataId = dwFileDataId + pRootBlock->FileDataIds[i];
- pFileEntry->Locales = pRootBlock->pLocaleBlockHdr->Locales;
- pFileEntry->EncodingKey = pRootBlock->pRootEntries[i].EncodingKey;
-
- // Also, insert the entry to the map
- Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash);
-
- // Update the local File Data Id
- assert((pFileEntry->FileDataId + 1) > pFileEntry->FileDataId);
- dwFileDataId = pFileEntry->FileDataId + 1;
-
- // Move to the next root entry
- pFileEntry++;
- }
-
- return ERROR_SUCCESS;
-}
-
-static int ParseWowRootFileInternal(
- TRootHandler_WoW6 * pRootHandler,
- PARSE_ROOT pfnParseRoot,
- LPBYTE pbRootFile,
- LPBYTE pbRootFileEnd,
- DWORD dwLocaleMask,
- BYTE bOverrideArchive,
- BYTE bAudioLocale)
-{
- CASC_ROOT_BLOCK RootBlock;
-
- // Now parse the root file
- while(pbRootFile < pbRootFileEnd)
- {
- // Validate the file locale block
- pbRootFile = VerifyLocaleBlock(&RootBlock, pbRootFile, pbRootFileEnd);
- if(pbRootFile == NULL)
- break;
-
- // WoW.exe (build 19116): Entries with flag 0x100 set are skipped
- if(RootBlock.pLocaleBlockHdr->Flags & 0x100)
- continue;
-
- // WoW.exe (build 19116): Entries with flag 0x80 set are skipped if overrideArchive CVAR is set to FALSE (which is by default in non-chinese clients)
- if((RootBlock.pLocaleBlockHdr->Flags & 0x80) && bOverrideArchive == 0)
- continue;
-
- // WoW.exe (build 19116): Entries with (flags >> 0x1F) not equal to bAudioLocale are skipped
- if((RootBlock.pLocaleBlockHdr->Flags >> 0x1F) != bAudioLocale)
- continue;
-
- // WoW.exe (build 19116): Locales other than defined mask are skipped too
- if((RootBlock.pLocaleBlockHdr->Locales & dwLocaleMask) == 0)
- continue;
-
- // Now call the custom function
- pfnParseRoot(pRootHandler, &RootBlock);
- }
-
- return ERROR_SUCCESS;
-}
-
-/*
-// known dwRegion values returned from sub_661316 (7.0.3.22210 x86 win), also referred by lua GetCurrentRegion
-#define WOW_REGION_US 0x01
-#define WOW_REGION_KR 0x02
-#define WOW_REGION_EU 0x03
-#define WOW_REGION_TW 0x04
-#define WOW_REGION_CN 0x05
-
-#define WOW_LOCALE_ENUS 0x00
-#define WOW_LOCALE_KOKR 0x01
-#define WOW_LOCALE_FRFR 0x02
-#define WOW_LOCALE_DEDE 0x03
-#define WOW_LOCALE_ZHCN 0x04
-#define WOW_LOCALE_ZHTW 0x05
-#define WOW_LOCALE_ESES 0x06
-#define WOW_LOCALE_ESMX 0x07
-#define WOW_LOCALE_RURU 0x08
-#define WOW_LOCALE_PTBR 0x0A
-#define WOW_LOCALE_ITIT 0x0B
-
- // dwLocale is obtained from a WOW_LOCALE_* to CASC_LOCALE_BIT_* mapping (sub_6615D0 in 7.0.3.22210 x86 win)
- // because (ENUS, ENGB) and (PTBR, PTPT) pairs share the same value on WOW_LOCALE_* enum
- // dwRegion is used to distinguish them
- if(dwRegion == WOW_REGION_EU)
- {
- // Is this english version of WoW?
- if(dwLocale == CASC_LOCALE_BIT_ENUS)
- {
- LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENGB, bOverrideArchive, bAudioLocale);
- LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENUS, bOverrideArchive, bAudioLocale);
- return ERROR_SUCCESS;
- }
-
- // Is this portuguese version of WoW?
- if(dwLocale == CASC_LOCALE_BIT_PTBR)
- {
- LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTPT, bOverrideArchive, bAudioLocale);
- LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTBR, bOverrideArchive, bAudioLocale);
- }
- }
- else
- LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, (1 << dwLocale), bOverrideArchive, bAudioLocale);
-*/
-
-static int ParseWowRootFile2(
- TRootHandler_WoW6 * pRootHandler,
- PARSE_ROOT pfnParseRoot,
- LPBYTE pbRootFile,
- LPBYTE pbRootFileEnd,
- DWORD dwLocaleMask,
- BYTE bAudioLocale)
-{
- // Load the locale as-is
- ParseWowRootFileInternal(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, dwLocaleMask, false, bAudioLocale);
-
- // If we wanted enGB, we also load enUS for the missing files
- if(dwLocaleMask == CASC_LOCALE_ENGB)
- ParseWowRootFileInternal(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, CASC_LOCALE_ENUS, false, bAudioLocale);
-
- if(dwLocaleMask == CASC_LOCALE_PTPT)
- ParseWowRootFileInternal(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, CASC_LOCALE_PTBR, false, bAudioLocale);
-
- return ERROR_SUCCESS;
-}
-
-// WoW.exe: 004146C7 (BuildManifest::Load)
-static int ParseWowRootFile(
- TRootHandler_WoW6 * pRootHandler,
- PARSE_ROOT pfnParseRoot,
- LPBYTE pbRootFile,
- LPBYTE pbRootFileEnd,
- DWORD dwLocaleMask)
-{
- ParseWowRootFile2(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, dwLocaleMask, 0);
- ParseWowRootFile2(pRootHandler, pfnParseRoot, pbRootFile, pbRootFileEnd, dwLocaleMask, 1);
- return ERROR_SUCCESS;
-}
-
-//-----------------------------------------------------------------------------
-// Implementation of WoW6 root file
-
-static int WowHandler_Insert(
- TRootHandler_WoW6 * pRootHandler,
- const char * szFileName,
- LPBYTE pbEncodingKey)
-{
- PCASC_FILE_ENTRY pFileEntry;
- DWORD FileDataId = 0;
-
- // Don't let the number of items to overflow
- if(pRootHandler->FileTable.ItemCount >= pRootHandler->FileTable.ItemCountMax)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- if (pRootHandler->FileDataIdLookupTable.ItemCount >= pRootHandler->FileDataIdLookupTable.ItemCountMax)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Insert the item to the linear file list
- pFileEntry = (PCASC_FILE_ENTRY)Array_Insert(&pRootHandler->FileTable, NULL, 1);
- if(pFileEntry != NULL)
- {
- Array_Insert(&pRootHandler->FileDataIdLookupTable, &pFileEntry, 1);
-
- // Get the file data ID of the previous item (0 if this is the first one)
- if(pRootHandler->FileTable.ItemCount > 1)
- FileDataId = pFileEntry[-1].FileDataId;
-
- // Fill-in the new entry
- pFileEntry->EncodingKey = *(PENCODING_KEY)pbEncodingKey;
- pFileEntry->FileNameHash = CalcFileNameHash(szFileName);
- pFileEntry->FileDataId = FileDataId + 1;
- pFileEntry->Locales = CASC_LOCALE_ALL;
-
- // Verify collisions (debug version only)
- assert(Map_FindObject(pRootHandler->pRootMap, &pFileEntry->FileNameHash, NULL) == NULL);
-
- // Insert the entry to the map
- Map_InsertObject(pRootHandler->pRootMap, pFileEntry, &pFileEntry->FileNameHash);
- }
-
- return ERROR_SUCCESS;
-}
-
-static LPBYTE WowHandler_Search(
- TRootHandler_WoW6 * pRootHandler,
- TCascSearch * pSearch,
- PDWORD /* PtrFileSize */,
- PDWORD PtrLocaleFlags,
- PDWORD PtrFileDataId)
-{
- PCASC_FILE_ENTRY pFileEntry;
-
- // Only if we have a listfile
- if(pSearch->pCache != NULL)
- {
- // Keep going through the listfile
- while(ListFile_GetNext(pSearch->pCache, pSearch->szMask, pSearch->szFileName, MAX_PATH))
- {
- // Find the root entry
- pFileEntry = FindRootEntry(pRootHandler->pRootMap, pSearch->szFileName, NULL);
- if(pFileEntry != NULL)
- {
- // Give the caller the locale mask
- if(PtrLocaleFlags != NULL)
- PtrLocaleFlags[0] = pFileEntry->Locales;
- if(PtrFileDataId != NULL)
- PtrFileDataId[0] = pFileEntry->FileDataId;
- return pFileEntry->EncodingKey.Value;
- }
- }
- }
-
- // No more files
- return NULL;
-}
-
-static LPBYTE WowHandler_GetKey(TRootHandler_WoW6 * pRootHandler, const char * szFileName)
-{
- PCASC_FILE_ENTRY pFileEntry;
- DWORD FileDataId;
- BYTE FileDataIdLE[4];
-
- // Open by FileDataId. The file name must be as following:
- // File########.unk, where '#' are hexa-decimal numbers (case insensitive).
- // Extension is ignored in that case
- if(IsFileDataIdName(szFileName))
- {
- ConvertStringToBinary(szFileName + 4, 8, FileDataIdLE);
- FileDataId = ConvertBytesToInteger_4(FileDataIdLE);
-
- pFileEntry = FindRootEntry(pRootHandler->FileDataIdLookupTable, FileDataId);
- }
- else
- {
- // Find by the file name hash
- pFileEntry = FindRootEntry(pRootHandler->pRootMap, szFileName, NULL);
- }
-
- return (pFileEntry != NULL) ? pFileEntry->EncodingKey.Value : NULL;
-}
-
-static void WowHandler_EndSearch(TRootHandler_WoW6 * /* pRootHandler */, TCascSearch * pSearch)
-{
- if(pSearch->pRootContext != NULL)
- CASC_FREE(pSearch->pRootContext);
- pSearch->pRootContext = NULL;
-}
-
-static DWORD WowHandler_GetFileId(TRootHandler_WoW6 * pRootHandler, const char * szFileName)
-{
- PCASC_FILE_ENTRY pFileEntry;
-
- // Find by the file name hash
- pFileEntry = FindRootEntry(pRootHandler->pRootMap, szFileName, NULL);
- return (pFileEntry != NULL) ? pFileEntry->FileDataId : 0;
-}
-
-static void WowHandler_Close(TRootHandler_WoW6 * pRootHandler)
-{
- if(pRootHandler != NULL)
- {
- Array_Free(&pRootHandler->FileTable);
- Array_Free(&pRootHandler->FileDataIdLookupTable);
- Map_Free(pRootHandler->pRootMap);
- CASC_FREE(pRootHandler);
- }
-}
-
-#ifdef _DEBUG
-static void TRootHandlerWoW6_Dump(
- TCascStorage * hs,
- TDumpContext * dc, // Pointer to an opened file
- LPBYTE pbRootFile,
- DWORD cbRootFile,
- const TCHAR * szListFile,
- int nDumpLevel)
-{
- PCASC_ENCODING_ENTRY pEncodingEntry;
- CASC_ROOT_BLOCK BlockInfo;
- PLISTFILE_MAP pListMap;
- QUERY_KEY EncodingKey;
- LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
- LPBYTE pbFilePointer;
- char szOneLine[0x100];
- DWORD i;
-
- // Create the listfile map
- pListMap = ListFile_CreateMap(szListFile);
-
- // Dump the root entries
- for(pbFilePointer = pbRootFile; pbFilePointer <= pbRootFileEnd; )
- {
- // Validate the root block
- pbFilePointer = VerifyLocaleBlock(&BlockInfo, pbFilePointer, pbRootFileEnd);
- if(pbFilePointer == NULL)
- break;
-
- // Dump the locale block
- dump_print(dc, "Flags: %08X Locales: %08X NumberOfFiles: %u\n"
- "=========================================================\n",
- BlockInfo.pLocaleBlockHdr->Flags,
- BlockInfo.pLocaleBlockHdr->Locales,
- BlockInfo.pLocaleBlockHdr->NumberOfFiles);
-
- // Dump the hashes and encoding keys
- for(i = 0; i < BlockInfo.pLocaleBlockHdr->NumberOfFiles; i++)
- {
- // Dump the entry
- dump_print(dc, "%08X %08X-%08X %s %s\n",
- (DWORD)(BlockInfo.FileDataIds[i]),
- (DWORD)(BlockInfo.pRootEntries[i].FileNameHash >> 0x20),
- (DWORD)(BlockInfo.pRootEntries[i].FileNameHash),
- StringFromMD5(BlockInfo.pRootEntries[i].EncodingKey.Value, szOneLine),
- ListFile_FindName(pListMap, BlockInfo.pRootEntries[i].FileNameHash));
-
- // Find the encoding entry in the encoding table
- if(nDumpLevel >= DUMP_LEVEL_ENCODING_FILE)
- {
- EncodingKey.pbData = BlockInfo.pRootEntries[i].EncodingKey.Value;
- EncodingKey.cbData = MD5_HASH_SIZE;
- pEncodingEntry = FindEncodingEntry(hs, &EncodingKey, NULL);
- CascDumpEncodingEntry(hs, dc, pEncodingEntry, nDumpLevel);
- }
- }
-
- // Put extra newline
- dump_print(dc, "\n");
- }
-
- ListFile_FreeMap(pListMap);
-}
-#endif
-
-//-----------------------------------------------------------------------------
-// Public functions
-
-int RootHandler_CreateWoW6(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask)
-{
- TRootHandler_WoW6 * pRootHandler;
- LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
- int nError;
-
- // Verify the size
- if(pbRootFile == NULL || cbRootFile <= sizeof(PFILE_LOCALE_BLOCK))
- nError = ERROR_FILE_CORRUPT;
-
- // Allocate the root handler object
- hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_WoW6, 1);
- if(pRootHandler == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Fill-in the handler functions
- memset(pRootHandler, 0, sizeof(TRootHandler_WoW6));
- pRootHandler->Insert = (ROOT_INSERT)WowHandler_Insert;
- pRootHandler->Search = (ROOT_SEARCH)WowHandler_Search;
- pRootHandler->EndSearch = (ROOT_ENDSEARCH)WowHandler_EndSearch;
- pRootHandler->GetKey = (ROOT_GETKEY)WowHandler_GetKey;
- pRootHandler->Close = (ROOT_CLOSE)WowHandler_Close;
- pRootHandler->GetFileId = (ROOT_GETFILEID)WowHandler_GetFileId;
-
-#ifdef _DEBUG
- pRootHandler->Dump = TRootHandlerWoW6_Dump; // Support for ROOT file dump
-#endif // _DEBUG
-
- // Count the files that are going to be loaded
- ParseWowRootFile(pRootHandler, ParseRoot_CountFiles, pbRootFile, pbRootFileEnd, dwLocaleMask);
- pRootHandler->dwTotalFileCount += CASC_EXTRA_FILES;
-
- // Create linear table that will contain all root items
- nError = Array_Create(&pRootHandler->FileTable, CASC_FILE_ENTRY, pRootHandler->dwTotalFileCount);
- if(nError != ERROR_SUCCESS)
- return nError;
-
- // Create sorted table that will contain all root items to lookup by FileDataId
- nError = Array_Create(&pRootHandler->FileDataIdLookupTable, PCASC_FILE_ENTRY, pRootHandler->dwTotalFileCount);
- if (nError != ERROR_SUCCESS)
- return nError;
-
- // Create the map of FileHash ->FileEntry
- pRootHandler->pRootMap = Map_Create(pRootHandler->dwTotalFileCount, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_ENTRY, FileNameHash));
- if(pRootHandler->pRootMap == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- // Parse the root file again and insert all files to the map
- ParseWowRootFile(pRootHandler, ParseRoot_AddRootEntries, pbRootFile, pbRootFileEnd, dwLocaleMask);
-
- // Sort entries by FileDataId for searches
- qsort_pointer_array((void**)pRootHandler->FileDataIdLookupTable.ItemArray, pRootHandler->FileDataIdLookupTable.ItemCount, &FileDataIdCompare, NULL);
- return ERROR_SUCCESS;
-}
diff --git a/dep/CascLib/src/CascStructs.h b/dep/CascLib/src/CascStructs.h
new file mode 100644
index 00000000000..f67da810c9e
--- /dev/null
+++ b/dep/CascLib/src/CascStructs.h
@@ -0,0 +1,255 @@
+/*****************************************************************************/
+/* CascStructs.h Copyright (c) Ladislav Zezula 2019 */
+/*---------------------------------------------------------------------------*/
+/* On-disk structures for CASC storages */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 28.04.19 1.00 Lad The first version of CascStructs.h */
+/*****************************************************************************/
+
+#ifndef __CASC_STRUCTS_H__
+#define __CASC_STRUCTS_H__
+
+//-----------------------------------------------------------------------------
+// Common definitions
+
+#define CASC_INDEX_COUNT 0x10 // Number of index files
+#define CASC_CKEY_SIZE 0x10 // Size of the content key
+#define CASC_EKEY_SIZE 0x09 // Size of the encoded key
+#define CASC_MAX_DATA_FILES 0x100 // Maximum number of data files
+
+//-----------------------------------------------------------------------------
+// The index files structures
+
+#define FILE_INDEX_PAGE_SIZE 0x200 // Number of bytes in one page of EKey items
+
+// Structure describing the 32-bit block size and 32-bit Jenkins hash of the block
+typedef struct _BLOCK_SIZE_AND_HASH
+{
+ DWORD cbBlockSize;
+ DWORD dwBlockHash;
+
+} BLOCK_SIZE_AND_HASH, *PBLOCK_SIZE_AND_HASH;
+
+// Checksum describing a block in the index file (v2)
+typedef struct _FILE_INDEX_GUARDED_BLOCK
+{
+ DWORD BlockSize;
+ DWORD BlockHash;
+
+} FILE_INDEX_GUARDED_BLOCK, *PFILE_INDEX_GUARDED_BLOCK;
+
+// Structure of the header of the index files version 1
+typedef struct _FILE_INDEX_HEADER_V1
+{
+ USHORT IndexVersion; // Must be 0x05
+ BYTE BucketIndex; // The bucket index of this file; should be the same as the first byte of the hex filename.
+ BYTE align_3;
+ DWORD field_4;
+ ULONGLONG field_8;
+ ULONGLONG SegmentSize; // Size of one data segment (aka data.### file)
+ BYTE EncodedSizeLength; // Length, in bytes, of the EncodedSize in the EKey entry
+ BYTE StorageOffsetLength; // Length, in bytes, of the StorageOffset field in the EKey entry
+ BYTE EKeyLength; // Length of the encoded key (bytes)
+ BYTE FileOffsetBits; // Number of bits of the archive file offset in StorageOffset field. Rest is data segment index
+ DWORD EKeyCount1;
+ DWORD EKeyCount2;
+ DWORD KeysHash1;
+ DWORD KeysHash2;
+ DWORD HeaderHash;
+} FILE_INDEX_HEADER_V1, *PFILE_INDEX_HEADER_V1;
+
+typedef struct _FILE_INDEX_HEADER_V2
+{
+ USHORT IndexVersion; // Must be 0x07
+ BYTE BucketIndex; // The bucket index of this file; should be the same as the first byte of the hex filename.
+ BYTE ExtraBytes; // Unknown; must be 0
+ BYTE EncodedSizeLength; // Length, in bytes, of the EncodedSize in the EKey entry
+ BYTE StorageOffsetLength; // Length, in bytes, of the StorageOffset field in the EKey entry
+ BYTE EKeyLength; // Length of the encoded key (bytes)
+ BYTE FileOffsetBits; // Number of bits of the archive file offset in StorageOffset field. Rest is data segment index
+ ULONGLONG SegmentSize; // Size of one data segment (aka data.### file)
+
+} FILE_INDEX_HEADER_V2, *PFILE_INDEX_HEADER_V2;
+
+// The EKey entry from the ".idx" files. Note that the lengths are optional and controlled by the FILE_INDEX_HEADER_Vx
+typedef struct _FILE_EKEY_ENTRY
+{
+ BYTE EKey[CASC_EKEY_SIZE]; // The first 9 bytes of the encoded key
+ BYTE FileOffsetBE[5]; // Index of data file and offset within (big endian).
+ BYTE EncodedSize[4]; // Encoded size (big endian). This is the size of encoded header, all file frame headers and all file frames
+} FILE_EKEY_ENTRY, *PFILE_EKEY_ENTRY;
+
+//-----------------------------------------------------------------------------
+// The archive index (md5.index) files structures
+// https://wowdev.wiki/TACT#CDN_File_Organization
+
+template <int CHKSUM_LENGTH>
+struct FILE_INDEX_FOOTER
+{
+ BYTE TocHash[MD5_HASH_SIZE]; // Client tries to read with 0x10, then backs off in size when smaller
+ BYTE Version; // Version of the index header
+ BYTE Reserved[2]; // Length, in bytes, of the file offset field
+ BYTE PageSizeKB; // Length, in kilobytes, of the index page
+ BYTE OffsetBytes; // Normally 4 for archive indices, 6 for group indices, and 0 for loose file indices
+ BYTE SizeBytes; // Normally 4
+ BYTE EKeySizeBytes; // Normally 16
+ BYTE FooterHashBytes; // Normally 8, <= 0x10
+ BYTE ElementCount[4]; // BigEndian in _old_ versions (e.g. 18179)
+ BYTE FooterHash[CHKSUM_LENGTH];
+};
+
+//-----------------------------------------------------------------------------
+// The ENCODING manifest structures
+//
+// The ENCODING file is in the form of:
+// * File header. Fixed length.
+// * Encoding Specification (ESpec) in the string form. Length is stored in FILE_ENCODING_HEADER::ESpecBlockSize
+// https://wowdev.wiki/CASC#Encoding
+// https://wowdev.wiki/TACT#Encoding_table
+//
+
+#define FILE_MAGIC_ENCODING 0x4E45 // 'EN'
+
+// File header of the ENCODING manifest
+typedef struct _FILE_ENCODING_HEADER
+{
+ USHORT Magic; // FILE_MAGIC_ENCODING ('EN')
+ BYTE Version; // Expected to be 1 by CascLib
+ BYTE CKeyLength; // The content key length in ENCODING file. Usually 0x10
+ BYTE EKeyLength; // The encoded key length in ENCODING file. Usually 0x10
+ BYTE CKeyPageSize[2]; // Size of the CKey page, in KB (big-endian)
+ BYTE EKeyPageSize[2]; // Size of the EKey page, in KB (big-endian)
+ BYTE CKeyPageCount[4]; // Number of CKey pages in the table (big endian)
+ BYTE EKeyPageCount[4]; // Number of EKey pages in the table (big endian)
+ BYTE field_11; // Asserted to be zero by the agent
+ BYTE ESpecBlockSize[4]; // Size of the ESpec string block
+
+} FILE_ENCODING_HEADER, *PFILE_ENCODING_HEADER;
+
+// Page header of the ENCODING manifest
+typedef struct _FILE_CKEY_PAGE
+{
+ BYTE FirstKey[MD5_HASH_SIZE]; // The first CKey/EKey in the segment
+ BYTE SegmentHash[MD5_HASH_SIZE]; // MD5 hash of the entire segment
+
+} FILE_CKEY_PAGE, *PFILE_CKEY_PAGE;
+
+// Single entry in the page
+typedef struct _FILE_CKEY_ENTRY
+{
+ USHORT EKeyCount; // Number of EKeys
+ BYTE ContentSize[4]; // Content file size (big endian)
+ BYTE CKey[CASC_CKEY_SIZE]; // Content key. This is MD5 of the file content
+ BYTE EKey[CASC_CKEY_SIZE]; // Encoded key. This is (trimmed) MD5 hash of the file header, containing MD5 hashes of all the logical blocks of the file
+
+} FILE_CKEY_ENTRY, *PFILE_CKEY_ENTRY;
+
+typedef struct _FILE_ESPEC_ENTRY
+{
+ BYTE ESpecKey[MD5_HASH_SIZE]; // The ESpec key of the file
+ BYTE ESpecIndexBE[4]; // Index of ESPEC entry, assuming zero-terminated strings (big endian)
+ BYTE FileSizeBE[5]; // Size of the encoded version of the file (big endian)
+
+} FILE_ESPEC_ENTRY, *PFILE_ESPEC_ENTRY;
+
+//-----------------------------------------------------------------------------
+// The DOWNLOAD manifest structures
+//
+// See https://wowdev.wiki/TACT#Download_manifest
+//
+
+#define FILE_MAGIC_DOWNLOAD 0x4C44 // 'DL'
+
+// File header of the DOWNLOAD manifest
+typedef struct _FILE_DOWNLOAD_HEADER
+{
+ USHORT Magic; // FILE_MAGIC_DOWNLOAD ('DL')
+ BYTE Version; // Expected to be 1 by CascLib
+ BYTE EKeyLength; // The content key length in DOWNLOAD file. Expected to be 0x10
+ BYTE EntryHasChecksum; // If nonzero, then the entry has checksum.
+ BYTE EntryCount[4]; // Number of entries (big-endian)
+ BYTE TagCount[2]; // Number of tag entries (big endian)
+
+ // Version 2 or newer
+ BYTE FlagByteSize; // Number of flag bytes
+
+ // Verion 3 or newer
+ BYTE BasePriority;
+ BYTE Unknown2[3];
+
+} FILE_DOWNLOAD_HEADER, *PFILE_DOWNLOAD_HEADER;
+
+typedef struct _FILE_DOWNLOAD_ENTRY
+{
+ BYTE EKey[MD5_HASH_SIZE]; // Encoding key (variable length)
+ BYTE FileSize[5]; // File size
+ BYTE Priority;
+
+ // DWORD Checksum [optional]
+ // BYTE Flags;
+
+} FILE_DOWNLOAD_ENTRY, *PFILE_DOWNLOAD_ENTRY;
+
+//-----------------------------------------------------------------------------
+// The INSTALL manifest structures
+//
+// See https://wowdev.wiki/TACT#Install_manifest
+//
+
+#define FILE_MAGIC_INSTALL 0x4E49 // 'IN'
+
+// File header of the INSTALL manifest
+typedef struct _FILE_INSTALL_HEADER
+{
+ USHORT Magic; // FILE_MAGIC_INSTALL ('DL')
+ BYTE Version; // Expected to be 1 by CascLib
+ BYTE EKeyLength; // The content key length in INSTALL file. Expected to be 0x10
+ BYTE TagCount[2]; // Number of tag entries (big endian)
+ BYTE EntryCount[4]; // Number of entries (big-endian)
+
+} FILE_INSTALL_HEADER, *PFILE_INSTALL_HEADER;
+
+//-----------------------------------------------------------------------------
+// Data file structures
+
+#define BLTE_HEADER_SIGNATURE 0x45544C42 // 'BLTE' header in the data files
+#define BLTE_HEADER_DELTA 0x1E // Distance of BLTE header from begin of the header area
+#define MAX_ENCODED_HEADER 0x1000 // Starting size for the frame headers
+
+typedef struct _BLTE_HEADER
+{
+ BYTE Signature[4]; // Must be "BLTE"
+ BYTE HeaderSize[4]; // Header size in bytes (big endian)
+ BYTE MustBe0F; // Must be 0x0F. Optional, only if HeaderSize != 0
+ BYTE FrameCount[3]; // Frame count (big endian). Optional, only if HeaderSize != 0
+} BLTE_HEADER, *PBLTE_HEADER;
+
+typedef struct _BLTE_ENCODED_HEADER
+{
+ // Header span.
+ ENCODED_KEY EKey; // Encoded key of the data beginning with "BLTE" (byte-reversed)
+ DWORD EncodedSize; // Encoded size of the data data beginning with "BLTE" (little endian)
+ BYTE field_14; // Seems to be 1 if the header span has no data
+ BYTE field_15; // Hardcoded to zero (Agent.exe 2.15.0.6296: 01370000->0148E2AA)
+ BYTE JenkinsHash[4]; // Jenkins hash (hashlittle2) of the preceding fields (EKey + EncodedSize + field_14 + field_15) (little endian)
+ BYTE Checksum[4]; // Checksum of the previous part. See "VerifyHeaderSpan()" for more information.
+
+ // BLTE header. Always present.
+ BYTE Signature[4]; // Must be "BLTE"
+ BYTE HeaderSize[4]; // Header size in bytes (big endian)
+ BYTE MustBe0F; // Must be 0x0F. Optional, only if HeaderSize != 0
+ BYTE FrameCount[3]; // Frame count (big endian). Optional, only if HeaderSize != 0
+} BLTE_ENCODED_HEADER, *PBLTE_ENCODED_HEADER;
+
+typedef struct _BLTE_FRAME
+{
+ BYTE EncodedSize[4]; // Encoded frame size (big endian)
+ BYTE ContentSize[4]; // Content frame size (big endian)
+ CONTENT_KEY FrameHash; // Hash of the encoded frame
+
+} BLTE_FRAME, *PBLTE_FRAME;
+
+#endif // __CASC_STRUCTS_H__
+
diff --git a/dep/CascLib/src/DllMain.c b/dep/CascLib/src/DllMain.c
new file mode 100644
index 00000000000..f7d0ce3063d
--- /dev/null
+++ b/dep/CascLib/src/DllMain.c
@@ -0,0 +1,24 @@
+/*****************************************************************************/
+/* DllMain.c Copyright (c) Ladislav Zezula 2015 */
+/*---------------------------------------------------------------------------*/
+/* Description: DllMain for the CascLib.dll library */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 26.10.15 1.00 Lad The first version of DllMain.c */
+/*****************************************************************************/
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+//-----------------------------------------------------------------------------
+// DllMain
+
+BOOL WINAPI DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved)
+{
+ UNREFERENCED_PARAMETER(hInst);
+ UNREFERENCED_PARAMETER(dwReason);
+ UNREFERENCED_PARAMETER(lpReserved);
+
+ return TRUE;
+}
diff --git a/dep/CascLib/src/common/Array.h b/dep/CascLib/src/common/Array.h
new file mode 100644
index 00000000000..1dc96b7be8e
--- /dev/null
+++ b/dep/CascLib/src/common/Array.h
@@ -0,0 +1,208 @@
+/*****************************************************************************/
+/* Array.h Copyright (c) Ladislav Zezula 2015 */
+/*---------------------------------------------------------------------------*/
+/* Common array implementation */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 30.10.15 1.00 Lad The first version of DynamicArray.h */
+/* 10.08.18 1.00 Lad CLASS-ified, renamed to Array.h */
+/*****************************************************************************/
+
+#ifndef __CASC_ARRAY_H__
+#define __CASC_ARRAY_H__
+
+//-----------------------------------------------------------------------------
+// Structures
+
+class CASC_ARRAY
+{
+ public:
+
+ CASC_ARRAY()
+ {
+ m_pItemArray = NULL;
+ m_ItemCountMax = 0;
+ m_ItemCount = 0;
+ m_ItemSize = 0;
+ }
+
+ ~CASC_ARRAY()
+ {
+ Free();
+ }
+
+ // Creates an array with a custom element type
+ template<typename TYPE>
+ int Create(size_t ItemCountMax)
+ {
+ return Create(sizeof(TYPE), ItemCountMax);
+ }
+
+ // Creates an array with a custom element size
+ int Create(size_t ItemSize, size_t ItemCountMax)
+ {
+ // Create the array
+ if ((m_pItemArray = CASC_ALLOC(BYTE, ItemSize * ItemCountMax)) == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ m_ItemCountMax = ItemCountMax;
+ m_ItemCount = 0;
+ m_ItemSize = ItemSize;
+ return ERROR_SUCCESS;
+ }
+
+ // Inserts one or more items; returns pointer to the first inserted item
+ void * Insert(size_t NewItemCount)
+ {
+ void * pNewItems;
+
+ // Try to enlarge the buffer, if needed
+ if (!EnlargeArray(m_ItemCount + NewItemCount))
+ return NULL;
+ pNewItems = m_pItemArray + (m_ItemCount * m_ItemSize);
+
+ // Increment the size of the array
+ m_ItemCount += NewItemCount;
+
+ // Return pointer to the new item
+ return pNewItems;
+ }
+
+ // Inserts one or more items; returns pointer to the first inserted item
+ void * Insert(const void * NewItems, size_t NewItemCount)
+ {
+ void * pNewItem = Insert(NewItemCount);
+
+ // Copy the item(s) to the array, if any
+ if (pNewItem && NewItems)
+ memcpy(pNewItem, NewItems, (NewItemCount * m_ItemSize));
+ return pNewItem;
+ }
+
+ // Returns an item at a given index
+ void * ItemAt(size_t ItemIndex)
+ {
+ return (ItemIndex < m_ItemCount) ? (m_pItemArray + (ItemIndex * m_ItemSize)) : NULL;
+ }
+
+ void * LastItem()
+ {
+ return m_pItemArray + (m_ItemCount * m_ItemSize);
+ }
+
+ // Inserts an item at a given index. If there is not enough items in the array,
+ // the array will be enlarged. Should any gaps to be created, the function will zero them
+ void * InsertAt(size_t ItemIndex)
+ {
+ LPBYTE pbLastItem;
+ LPBYTE pbNewItem;
+
+ // Make sure we have array large enough
+ if(!EnlargeArray(ItemIndex + 1))
+ return NULL;
+
+ // Get the items range
+ pbLastItem = m_pItemArray + (m_ItemCount * m_ItemSize);
+ pbNewItem = m_pItemArray + (ItemIndex * m_ItemSize);
+ m_ItemCount = CASCLIB_MAX(m_ItemCount, ItemIndex+1);
+
+ // If we inserted an item past the current end, we need to clear the items in-between
+ if (pbNewItem > pbLastItem)
+ {
+ memset(pbLastItem, 0, (pbNewItem - pbLastItem));
+ m_ItemCount = ItemIndex + 1;
+ }
+
+ return pbNewItem;
+ }
+
+ // Returns index of an item
+ size_t IndexOf(const void * pItem)
+ {
+ LPBYTE pbItem = (LPBYTE)pItem;
+
+ assert((m_pItemArray <= pbItem) && (pbItem <= m_pItemArray + (m_ItemCount * m_ItemSize)));
+ assert(((pbItem - m_pItemArray) % m_ItemSize) == 0);
+
+ return ((pbItem - m_pItemArray) / m_ItemSize);
+ }
+
+ void * ItemArray()
+ {
+ return m_pItemArray;
+ }
+
+ size_t ItemCount()
+ {
+ return m_ItemCount;
+ }
+
+ size_t ItemCountMax()
+ {
+ return m_ItemCountMax;
+ }
+
+ size_t ItemSize()
+ {
+ return m_ItemSize;
+ }
+
+ bool IsInitialized()
+ {
+ return (m_pItemArray && m_ItemCountMax);
+ }
+
+ // Invalidates the entire array, but keeps memory allocated
+ void Reset()
+ {
+ memset(m_pItemArray, 0, m_ItemCountMax * m_ItemSize);
+ m_ItemCount = 0;
+ }
+
+ // Frees the array
+ void Free()
+ {
+ CASC_FREE(m_pItemArray);
+ m_ItemCountMax = m_ItemCount = m_ItemSize = 0;
+ }
+
+ protected:
+
+ bool EnlargeArray(size_t NewItemCount)
+ {
+ LPBYTE NewItemArray;
+ size_t ItemCountMax;
+
+ // We expect the array to be already allocated
+ assert(m_pItemArray != NULL);
+ assert(m_ItemCountMax != 0);
+
+ // Shall we enlarge the table?
+ if (NewItemCount > m_ItemCountMax)
+ {
+ // Calculate new table size
+ ItemCountMax = m_ItemCountMax;
+ while (ItemCountMax < NewItemCount)
+ ItemCountMax = ItemCountMax << 1;
+
+ // Allocate new table
+ NewItemArray = CASC_REALLOC(BYTE, m_pItemArray, (ItemCountMax * m_ItemSize));
+ if (NewItemArray == NULL)
+ return false;
+
+ // Set the new table size
+ m_ItemCountMax = ItemCountMax;
+ m_pItemArray = NewItemArray;
+ }
+
+ return true;
+ }
+
+ LPBYTE m_pItemArray; // Pointer to item array
+ size_t m_ItemCountMax; // Maximum item count
+ size_t m_ItemCount; // Current item count
+ size_t m_ItemSize; // Size of an item
+};
+
+#endif // __CASC_ARRAY__
diff --git a/dep/CascLib/src/common/Common.cpp b/dep/CascLib/src/common/Common.cpp
index a0455b7c659..d545f52f84a 100644
--- a/dep/CascLib/src/common/Common.cpp
+++ b/dep/CascLib/src/common/Common.cpp
@@ -65,40 +65,230 @@ unsigned char IntToHexChar[] = "0123456789abcdef";
// GetLastError/SetLastError support for non-Windows platform
#ifndef PLATFORM_WINDOWS
-static int nLastError = ERROR_SUCCESS;
+static DWORD dwLastError = ERROR_SUCCESS;
-int GetLastError()
+DWORD GetLastError()
{
- return nLastError;
+ return dwLastError;
}
-void SetLastError(int nError)
+void SetLastError(DWORD dwErrCode)
{
- nLastError = nError;
+ dwLastError = dwErrCode;
}
#endif
//-----------------------------------------------------------------------------
-// String manipulation
+// Overloaded "new" and "delete" operators
-void CopyString(char * szTarget, const char * szSource, size_t cchLength)
+void * operator new(size_t size)
{
- memcpy(szTarget, szSource, cchLength);
- szTarget[cchLength] = 0;
+ return CASC_ALLOC(BYTE, size);
}
-void CopyString(wchar_t * szTarget, const char * szSource, size_t cchLength)
+void * operator new[](size_t size)
{
- mbstowcs(szTarget, szSource, cchLength);
- szTarget[cchLength] = 0;
+ return CASC_ALLOC(BYTE, size);
}
-void CopyString(char * szTarget, const wchar_t * szSource, size_t cchLength)
+void operator delete(void * ptr)
{
- wcstombs(szTarget, szSource, cchLength);
- szTarget[cchLength] = 0;
+ CASC_FREE(ptr);
}
+void operator delete[](void * ptr)
+{
+ CASC_FREE(ptr);
+}
+
+// For some reason, VS2015 needs this
+void operator delete(void * ptr, size_t)
+{
+ CASC_FREE(ptr);
+}
+
+//-----------------------------------------------------------------------------
+// Linear data stream manipulation
+
+LPBYTE CaptureInteger32(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue)
+{
+ // Is there enough data?
+ if((pbDataPtr + sizeof(DWORD)) > pbDataEnd)
+ return NULL;
+
+ // Give data
+ PtrValue[0] = *(PDWORD)pbDataPtr;
+
+ // Return the pointer to data following after the integer
+ return pbDataPtr + sizeof(DWORD);
+}
+
+LPBYTE CaptureInteger32_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue)
+{
+ // Is there enough data?
+ if((pbDataPtr + sizeof(DWORD)) > pbDataEnd)
+ return NULL;
+
+ // Convert data from Little endian to
+ PtrValue[0] = ConvertBytesToInteger_4(pbDataPtr);
+
+ // Return the pointer to data following after the integer
+ return pbDataPtr + sizeof(DWORD);
+}
+
+LPBYTE CaptureByteArray(LPBYTE pbDataPtr, LPBYTE pbDataEnd, size_t nLength, LPBYTE pbOutput)
+{
+ // Is there enough data?
+ if((pbDataPtr + nLength) > pbDataEnd)
+ return NULL;
+
+ // Give data
+ memcpy(pbOutput, pbDataPtr, nLength);
+
+ // Return the pointer to data following after the integer
+ return pbDataPtr + nLength;
+}
+
+LPBYTE CaptureContentKey(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PCONTENT_KEY * PtrCKey)
+{
+ // Is there enough data?
+ if((pbDataPtr + sizeof(CONTENT_KEY)) > pbDataEnd)
+ return NULL;
+
+ // Give data
+ PtrCKey[0] = (PCONTENT_KEY)pbDataPtr;
+
+ // Return the pointer to data following after the integer
+ return pbDataPtr + sizeof(CONTENT_KEY);
+}
+
+LPBYTE CaptureArray_(LPBYTE pbDataPtr, LPBYTE pbDataEnd, LPBYTE * PtrArray, size_t ItemSize, size_t ItemCount)
+{
+ size_t ArraySize = ItemSize * ItemCount;
+
+ // Is there enough data?
+ if((pbDataPtr + ArraySize) > pbDataEnd)
+ return NULL;
+
+ // Give data
+ PtrArray[0] = pbDataPtr;
+
+ // Return the pointer to data following after the array
+ return pbDataPtr + ArraySize;
+}
+
+//-----------------------------------------------------------------------------
+// String copying and conversion
+
+void CascStrCopy(char * szTarget, size_t cchTarget, const char * szSource, size_t cchSource)
+{
+ size_t cchToCopy;
+
+ if (cchTarget > 0)
+ {
+ // Make sure we know the length
+ if (cchSource == -1)
+ cchSource = strlen(szSource);
+ cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource);
+
+ // Copy the string
+ memcpy(szTarget, szSource, cchToCopy);
+ szTarget[cchToCopy] = 0;
+ }
+}
+
+void CascStrCopy(char * szTarget, size_t cchTarget, const wchar_t * szSource, size_t cchSource)
+{
+ size_t cchToCopy;
+
+ if (cchTarget > 0)
+ {
+ // Make sure we know the length
+ if (cchSource == -1)
+ cchSource = wcslen(szSource);
+ cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource);
+
+ wcstombs(szTarget, szSource, cchToCopy);
+ szTarget[cchToCopy] = 0;
+ }
+}
+
+void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const char * szSource, size_t cchSource)
+{
+ size_t cchToCopy;
+
+ if (cchTarget > 0)
+ {
+ // Make sure we know the length
+ if (cchSource == -1)
+ cchSource = strlen(szSource);
+ cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource);
+
+ mbstowcs(szTarget, szSource, cchToCopy);
+ szTarget[cchToCopy] = 0;
+ }
+}
+
+void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const wchar_t * szSource, size_t cchSource)
+{
+ size_t cchToCopy;
+
+ if (cchTarget > 0)
+ {
+ // Make sure we know the length
+ if (cchSource == -1)
+ cchSource = wcslen(szSource);
+ cchToCopy = CASCLIB_MIN((cchTarget - 1), cchSource);
+
+ memcpy(szTarget, szSource, cchToCopy * sizeof(wchar_t));
+ szTarget[cchToCopy] = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Safe version of s(w)printf
+
+size_t CascStrPrintf(char * buffer, size_t nCount, const char * format, ...)
+{
+ char * buffend;
+ va_list argList;
+
+ // Start the argument list
+ va_start(argList, format);
+
+#ifdef PLATFORM_WINDOWS
+ StringCchVPrintfExA(buffer, nCount, &buffend, NULL, 0, format, argList);
+#else
+ buffend = buffer + vsnprintf(buffer, nCount, format, argList);
+#endif
+
+ // End the argument list
+ va_end(argList);
+ return (buffend - buffer);
+}
+
+size_t CascStrPrintf(wchar_t * buffer, size_t nCount, const wchar_t * format, ...)
+{
+ wchar_t * buffend;
+ va_list argList;
+
+ // Start the argument list
+ va_start(argList, format);
+
+#ifdef PLATFORM_WINDOWS
+ StringCchVPrintfExW(buffer, nCount, &buffend, NULL, 0, format, argList);
+#else
+ buffend = buffer + vswprintf(buffer, nCount, format, argList);
+#endif
+
+ // End the argument list
+ va_end(argList);
+ return (buffend - buffer);
+}
+
+//-----------------------------------------------------------------------------
+// String allocation
+
char * CascNewStr(const char * szString, size_t nCharsToReserve)
{
char * szNewString = NULL;
@@ -137,102 +327,144 @@ wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve)
return szNewString;
}
-TCHAR * CascNewStrFromAnsi(const char * szBegin, const char * szEnd)
+template <typename XCHAR>
+TCHAR * AppendPathFragment(TCHAR * szBuffer, TCHAR * szBufferEnd, const XCHAR * szPath, char chSeparator, bool bFirstFragment = false)
{
- TCHAR * szNewString = NULL;
-
- // Only if the entry is valid
- if(szBegin != NULL && szEnd > szBegin)
+ // The "Path" must not be empty
+ if(szPath && szPath[0])
{
- // Allocate and copy the string
- szNewString = CASC_ALLOC(TCHAR, (szEnd - szBegin + 1));
- if(szNewString != NULL)
- CopyString(szNewString, szBegin, (szEnd - szBegin));
+ // Append the path separator after the first fragment
+ if(szBuffer < szBufferEnd && bFirstFragment == false)
+ {
+ if(szBuffer[-1] != chSeparator)
+ {
+ *szBuffer++ = chSeparator;
+ }
+ }
+
+ // Copy the sub path
+ while(szBuffer < szBufferEnd && szPath[0] != 0)
+ {
+ // If there is a path separator, we skip it (all of them) and put single separator there
+ if(szPath[0] == '\\' || szPath[0] == '/')
+ {
+ while(szPath[0] == '\\' || szPath[0] == '/')
+ szPath++;
+ *szBuffer++ = chSeparator;
+ }
+ else
+ {
+ *szBuffer++ = *szPath++;
+ }
+ }
+
+ // Append end of string
+ szBuffer[0] = 0;
}
- // Return the string
- return szNewString;
+ return szBuffer;
}
-TCHAR * CombinePath(const TCHAR * szDirectory, const TCHAR * szSubDir)
+TCHAR * GetLastPathPart(TCHAR * szWorkPath)
{
- TCHAR * szFullPath = NULL;
- TCHAR * szPathPtr;
- size_t nLength1 = 0;
- size_t nLength2 = 0;
+ size_t nLength = _tcslen(szWorkPath);
- // Calculate lengths of each part
- if(szDirectory != NULL)
- {
- // Get the length of the directory
- nLength1 = _tcslen(szDirectory);
+ // Go one character back
+ if(nLength > 0)
+ nLength--;
- // Cut all ending backslashes
- while(nLength1 > 0 && (szDirectory[nLength1 - 1] == _T('\\') || szDirectory[nLength1 - 1] == _T('/')))
- nLength1--;
- }
+ // Cut ending (back)slashes, if any
+ while(nLength > 0 && (szWorkPath[nLength] == _T('\\') || szWorkPath[nLength] == _T('/')))
+ nLength--;
- if(szSubDir != NULL)
+ // Cut the last path part
+ while(nLength > 0)
{
- // Cut all leading backslashes
- while(szSubDir[0] == _T(PATH_SEPARATOR))
- szSubDir++;
-
- // Get the length of the subdir
- nLength2 = _tcslen(szSubDir);
-
- // Cut all ending backslashes
- while(nLength2 > 0 && szSubDir[nLength2 - 1] == _T(PATH_SEPARATOR))
- nLength2--;
- }
-
- // Allocate space for the full path
- szFullPath = szPathPtr = CASC_ALLOC(TCHAR, nLength1 + nLength2 + 2);
- if(szFullPath != NULL)
- {
- // Copy the directory
- if(szDirectory != NULL && nLength1 != 0)
+ // End of path?
+ if(szWorkPath[nLength] == _T('\\') || szWorkPath[nLength] == _T('/'))
{
- memcpy(szPathPtr, szDirectory, (nLength1 * sizeof(TCHAR)));
- szPathPtr += nLength1;
+ return szWorkPath + nLength;
}
- // Copy the sub-directory
- if(szSubDir != NULL && nLength2 != 0)
- {
- // Append backslash to the previous one
- if(szPathPtr > szFullPath)
- *szPathPtr++ = _T(PATH_SEPARATOR);
+ // Go one character back
+ nLength--;
+ }
- // Copy the string
- memcpy(szPathPtr, szSubDir, (nLength2 * sizeof(TCHAR)));
- szPathPtr += nLength2;
- }
+ return NULL;
+}
- // Terminate the string
- szPathPtr[0] = 0;
- }
+bool CutLastPathPart(TCHAR * szWorkPath)
+{
+ // Get the last part of the path
+ szWorkPath = GetLastPathPart(szWorkPath);
+ if(szWorkPath == NULL)
+ return false;
- return szFullPath;
+ szWorkPath[0] = 0;
+ return true;
}
-TCHAR * CombinePathAndString(const TCHAR * szPath, const char * szString, size_t nLength)
+TCHAR * CombinePath(const TCHAR * szDirectory, const TCHAR * szSubDir)
{
+ TCHAR * szFullPathEnd;
TCHAR * szFullPath = NULL;
- TCHAR * szSubDir;
+ TCHAR * szPathPtr;
+ size_t nLength1 = (szDirectory != NULL) ? _tcslen(szDirectory) : 0;
+ size_t nLength2 = (szSubDir != NULL) ? _tcslen(szSubDir) : 0;
- // Create the subdir string
- szSubDir = CASC_ALLOC(TCHAR, nLength + 1);
- if(szSubDir != NULL)
+ // Allocate the entire buffer
+ szFullPath = szPathPtr = CASC_ALLOC(TCHAR, nLength1 + nLength2 + 2);
+ szFullPathEnd = szFullPath + nLength1 + nLength2 + 1;
+ if(szFullPath != NULL)
{
- CopyString(szSubDir, szString, nLength);
- szFullPath = CombinePath(szPath, szSubDir);
- CASC_FREE(szSubDir);
+ szPathPtr = AppendPathFragment(szPathPtr, szFullPathEnd, szDirectory, PATH_SEP_CHAR, true);
+ szPathPtr = AppendPathFragment(szPathPtr, szFullPathEnd, szSubDir, PATH_SEP_CHAR);
}
return szFullPath;
}
+size_t CombineFilePath(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szPath, const TCHAR * szSubPath1, const TCHAR * szSubPath2)
+{
+ TCHAR * szSaveBuffer = szBuffer;
+ TCHAR * szBufferEnd = szBuffer + nMaxChars - 1;
+
+ // Append all three parts and return length
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szPath, PATH_SEP_CHAR, true);
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szSubPath1, PATH_SEP_CHAR);
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szSubPath2, PATH_SEP_CHAR);
+ return (szBuffer - szSaveBuffer);
+}
+
+size_t CombineUrlPath(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szHost, const TCHAR * szSubPath1, const TCHAR * szSubPath2)
+{
+ TCHAR * szSaveBuffer = szBuffer;
+ TCHAR * szBufferEnd = szBuffer + nMaxChars - 1;
+
+ // Append all three parts and return length
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szHost, '/', true);
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szSubPath1, '/');
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szSubPath2, '/');
+ return (szBuffer - szSaveBuffer);
+}
+
+size_t CreateCascSubdirectoryName(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szSubDir, const TCHAR * szExtension, LPBYTE pbEKey)
+{
+ TCHAR * szSaveBuffer = szBuffer;
+ TCHAR * szBufferEnd = szBuffer + nMaxChars - 1;
+ char szHashSubPath[0x80];
+ char szHashText[MD5_STRING_SIZE+1];
+
+ // Prepare the subpath
+ StringFromBinary(pbEKey, MD5_HASH_SIZE, szHashText);
+ CascStrPrintf(szHashSubPath, _countof(szHashSubPath), "%02x/%02x/%s", pbEKey[0], pbEKey[1], szHashText);
+
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szSubDir, '/', true);
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szHashSubPath, '/');
+ szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szExtension, '/', true);
+ return (szBuffer - szSaveBuffer);
+}
+
size_t NormalizeFileName(const unsigned char * NormTable, char * szNormName, const char * szFileName, size_t cchMaxChars)
{
char * szNormNameEnd = szNormName + cchMaxChars;
@@ -257,19 +489,26 @@ size_t NormalizeFileName_LowerSlash(char * szNormName, const char * szFileName,
return NormalizeFileName(AsciiToLowerTable_Slash, szNormName, szFileName, cchMaxChars);
}
-ULONGLONG CalcFileNameHash(const char * szFileName)
+ULONGLONG CalcNormNameHash(const char * szNormName, size_t nLength)
{
- char szNormName[MAX_PATH+1];
uint32_t dwHashHigh = 0;
uint32_t dwHashLow = 0;
+
+ // Calculate the HASH value of the normalized file name
+ hashlittle2(szNormName, nLength, &dwHashHigh, &dwHashLow);
+ return ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow;
+}
+
+ULONGLONG CalcFileNameHash(const char * szFileName)
+{
+ char szNormName[MAX_PATH+1];
size_t nLength;
// Normalize the file name - convert to uppercase, slashes to backslashes
nLength = NormalizeFileName_UpperBkSlash(szNormName, szFileName, MAX_PATH);
- // Calculate the HASH value of the normalized file name
- hashlittle2(szNormName, nLength, &dwHashHigh, &dwHashLow);
- return ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow;
+ // Calculate hash from the normalized name
+ return CalcNormNameHash(szNormName, nLength);
}
int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue)
@@ -373,13 +612,16 @@ char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer)
{
char * szSaveBuffer = szBuffer;
- // Convert the string to the array of MD5
- // Copy the blob data as text
- for(size_t i = 0; i < cbBinary; i++)
+ // Verify the binary pointer
+ if(pbBinary && cbBinary)
{
- *szBuffer++ = IntToHexChar[pbBinary[0] >> 0x04];
- *szBuffer++ = IntToHexChar[pbBinary[0] & 0x0F];
- pbBinary++;
+ // Convert the string to the array of MD5
+ // Copy the blob data as text
+ for(size_t i = 0; i < cbBinary; i++)
+ {
+ *szBuffer++ = IntToHexChar[pbBinary[i] >> 0x04];
+ *szBuffer++ = IntToHexChar[pbBinary[i] & 0x0F];
+ }
}
// Terminate the string
@@ -395,39 +637,51 @@ char * StringFromMD5(LPBYTE md5, char * szBuffer)
//-----------------------------------------------------------------------------
// File name utilities
-const wchar_t * GetPlainFileName(const wchar_t * szFileName)
+bool IsFileDataIdName(const char * szFileName, DWORD & FileDataId)
{
- const wchar_t * szPlainName = szFileName;
+ BYTE BinaryValue[4];
- while(*szFileName != 0)
+ // File name must begin with "File", case insensitive
+ if(AsciiToUpperTable_BkSlash[szFileName[0]] == 'F' &&
+ AsciiToUpperTable_BkSlash[szFileName[1]] == 'I' &&
+ AsciiToUpperTable_BkSlash[szFileName[2]] == 'L' &&
+ AsciiToUpperTable_BkSlash[szFileName[3]] == 'E')
{
- if(*szFileName == '\\' || *szFileName == '/')
- szPlainName = szFileName + 1;
- szFileName++;
+ // Then, 8 hexadecimal digits must follow
+ if(ConvertStringToBinary(szFileName + 4, 8, BinaryValue) == ERROR_SUCCESS)
+ {
+ // Must be followed by an extension or end-of-string
+ if(szFileName[0x0C] == 0 || szFileName[0x0C] == '.')
+ {
+ FileDataId = ConvertBytesToInteger_4(BinaryValue);
+ return (FileDataId != CASC_INVALID_ID);
+ }
+ }
}
- return szPlainName;
+ return false;
}
-const char * GetPlainFileName(const char * szFileName)
+bool IsFileCKeyEKeyName(const char * szFileName, LPBYTE PtrKeyBuffer)
{
- const char * szPlainName = szFileName;
+ size_t nLength = strlen(szFileName);
- while(*szFileName != 0)
+ if(nLength == MD5_STRING_SIZE)
{
- if(*szFileName == '\\' || *szFileName == '/')
- szPlainName = szFileName + 1;
- szFileName++;
+ if(ConvertStringToBinary(szFileName, MD5_STRING_SIZE, PtrKeyBuffer) == ERROR_SUCCESS)
+ {
+ return true;
+ }
}
- return szPlainName;
+ return false;
}
-bool CheckWildCard(const char * szString, const char * szWildCard)
+bool CascCheckWildCard(const char * szString, const char * szWildCard)
{
const char * szWildCardPtr;
- for(;;)
+ while(szWildCard && szWildCard[0])
{
// If there is '?' in the wildcard, we skip one char
while(szWildCard[0] == '?')
@@ -455,7 +709,7 @@ bool CheckWildCard(const char * szString, const char * szWildCard)
if(AsciiToUpperTable_BkSlash[szWildCardPtr[0]] == AsciiToUpperTable_BkSlash[szString[0]])
{
- if(CheckWildCard(szString, szWildCardPtr))
+ if(CascCheckWildCard(szString, szWildCardPtr))
return true;
}
}
@@ -476,244 +730,43 @@ bool CheckWildCard(const char * szString, const char * szWildCard)
return (szString[0] == 0) ? true : false;
}
}
+ return true;
}
//-----------------------------------------------------------------------------
// Hashing functions
-bool IsValidMD5(LPBYTE pbMd5)
+bool CascIsValidMD5(LPBYTE pbMd5)
{
- BYTE BitSummary = 0;
+ PDWORD Int32Array = (PDWORD)pbMd5;
- // The MD5 is considered invalid of it is zeroed
- BitSummary |= pbMd5[0x00] | pbMd5[0x01] | pbMd5[0x02] | pbMd5[0x03] | pbMd5[0x04] | pbMd5[0x05] | pbMd5[0x06] | pbMd5[0x07];
- BitSummary |= pbMd5[0x08] | pbMd5[0x09] | pbMd5[0x0A] | pbMd5[0x0B] | pbMd5[0x0C] | pbMd5[0x0D] | pbMd5[0x0E] | pbMd5[0x0F];
- return (BitSummary != 0);
+ // The MD5 is considered invalid if it is zeroed
+ return (Int32Array[0] | Int32Array[1] | Int32Array[2] | Int32Array[3]) ? true : false;
}
-bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5)
+bool CascVerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5)
{
- hash_state md5_state;
+ MD5_CTX md5_ctx;
BYTE md5_digest[MD5_HASH_SIZE];
// Don't verify the block if the MD5 is not valid.
- if(!IsValidMD5(expected_md5))
+ if(!CascIsValidMD5(expected_md5))
return true;
// Calculate the MD5 of the data block
- md5_init(&md5_state);
- md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock);
- md5_done(&md5_state, md5_digest);
+ MD5_Init(&md5_ctx);
+ MD5_Update(&md5_ctx, pvDataBlock, cbDataBlock);
+ MD5_Final(md5_digest, &md5_ctx);
// Does the MD5's match?
return (memcmp(md5_digest, expected_md5, MD5_HASH_SIZE) == 0);
}
-void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash)
-{
- hash_state md5_state;
-
- md5_init(&md5_state);
- md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock);
- md5_done(&md5_state, md5_hash);
-}
-
-//-----------------------------------------------------------------------------
-// We have our own qsort implementation, optimized for using array of pointers
-
-#define STKSIZ (8*sizeof(void*) - 2)
-
-#define SWAP_ENTRIES(index1, index2) \
-{ \
- temp = base[index1]; \
- base[index1] = base[index2]; \
- base[index2] = temp; \
-}
-
-void qsort_pointer_array(void ** base, size_t num, int (*compare)(const void *, const void *, const void *), const void * context)
+void CascCalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash)
{
- size_t lo, hi; /* ends of sub-array currently sorting */
- size_t mid; /* points to middle of subarray */
- size_t loguy, higuy; /* traveling pointers for partition step */
- size_t size; /* size of the sub-array */
- size_t lostk[STKSIZ], histk[STKSIZ];
- void * temp;
- int stkptr; /* stack for saving sub-array to be processed */
-
- /* validation section */
- assert(base != NULL);
- assert(compare != NULL);
-
- if (num < 2)
- return; /* nothing to do */
-
- stkptr = 0; /* initialize stack */
-
- lo = 0;
- hi = (num-1); /* initialize limits */
-
- /* this entry point is for pseudo-recursion calling: setting
- lo and hi and jumping to here is like recursion, but stkptr is
- preserved, locals aren't, so we preserve stuff on the stack */
-recurse:
-
- size = (hi - lo) + 1; /* number of el's to sort */
-
- /* First we pick a partitioning element. The efficiency of the
- algorithm demands that we find one that is approximately the median
- of the values, but also that we select one fast. We choose the
- median of the first, middle, and last elements, to avoid bad
- performance in the face of already sorted data, or data that is made
- up of multiple sorted runs appended together. Testing shows that a
- median-of-three algorithm provides better performance than simply
- picking the middle element for the latter case. */
-
- mid = lo + (size / 2); /* find middle element */
-
- /* Sort the first, middle, last elements into order */
- if (compare(context, base[lo], base[mid]) > 0) {
- SWAP_ENTRIES(lo, mid);
- }
- if (compare(context, base[lo], base[hi]) > 0) {
- SWAP_ENTRIES(lo, hi);
- }
- if (compare(context, base[mid], base[hi]) > 0) {
- SWAP_ENTRIES(mid, hi);
- }
-
- /* We now wish to partition the array into three pieces, one consisting
- of elements <= partition element, one of elements equal to the
- partition element, and one of elements > than it. This is done
- below; comments indicate conditions established at every step. */
-
- loguy = lo;
- higuy = hi;
-
- /* Note that higuy decreases and loguy increases on every iteration,
- so loop must terminate. */
- for (;;) {
- /* lo <= loguy < hi, lo < higuy <= hi,
- A[i] <= A[mid] for lo <= i <= loguy,
- A[i] > A[mid] for higuy <= i < hi,
- A[hi] >= A[mid] */
-
- /* The doubled loop is to avoid calling comp(mid,mid), since some
- existing comparison funcs don't work when passed the same
- value for both pointers. */
-
- if (mid > loguy) {
- do {
- loguy ++;
- } while (loguy < mid && compare(context, base[loguy], base[mid]) <= 0);
- }
- if (mid <= loguy) {
- do {
- loguy ++;
- } while (loguy <= hi && compare(context, base[loguy], base[mid]) <= 0);
- }
-
- /* lo < loguy <= hi+1, A[i] <= A[mid] for lo <= i < loguy,
- either loguy > hi or A[loguy] > A[mid] */
-
- do {
- higuy --;
- } while (higuy > mid && compare(context, base[higuy], base[mid]) > 0);
-
- /* lo <= higuy < hi, A[i] > A[mid] for higuy < i < hi,
- either higuy == lo or A[higuy] <= A[mid] */
-
- if (higuy < loguy)
- break;
-
- /* if loguy > hi or higuy == lo, then we would have exited, so
- A[loguy] > A[mid], A[higuy] <= A[mid],
- loguy <= hi, higuy > lo */
-
- SWAP_ENTRIES(loguy, higuy);
-
- /* If the partition element was moved, follow it. Only need
- to check for mid == higuy, since before the swap,
- A[loguy] > A[mid] implies loguy != mid. */
-
- if (mid == higuy)
- mid = loguy;
-
- /* A[loguy] <= A[mid], A[higuy] > A[mid]; so condition at top
- of loop is re-established */
- }
+ MD5_CTX md5_ctx;
- /* A[i] <= A[mid] for lo <= i < loguy,
- A[i] > A[mid] for higuy < i < hi,
- A[hi] >= A[mid]
- higuy < loguy
- implying:
- higuy == loguy-1
- or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] */
-
- /* Find adjacent elements equal to the partition element. The
- doubled loop is to avoid calling comp(mid,mid), since some
- existing comparison funcs don't work when passed the same value
- for both pointers. */
-
- higuy ++;
- if (mid < higuy) {
- do {
- higuy --;
- } while (higuy > mid && compare(context, base[higuy], base[mid]) == 0);
- }
- if (mid >= higuy) {
- do {
- higuy --;
- } while (higuy > lo && compare(context, base[higuy], base[mid]) == 0);
- }
-
- /* OK, now we have the following:
- higuy < loguy
- lo <= higuy <= hi
- A[i] <= A[mid] for lo <= i <= higuy
- A[i] == A[mid] for higuy < i < loguy
- A[i] > A[mid] for loguy <= i < hi
- A[hi] >= A[mid] */
-
- /* We've finished the partition, now we want to sort the subarrays
- [lo, higuy] and [loguy, hi].
- We do the smaller one first to minimize stack usage.
- We only sort arrays of length 2 or more.*/
-
- if ( higuy - lo >= hi - loguy ) {
- if (lo < higuy) {
- lostk[stkptr] = lo;
- histk[stkptr] = higuy;
- ++stkptr;
- } /* save big recursion for later */
-
- if (loguy < hi) {
- lo = loguy;
- goto recurse; /* do small recursion */
- }
- }
- else {
- if (loguy < hi) {
- lostk[stkptr] = loguy;
- histk[stkptr] = hi;
- ++stkptr; /* save big recursion for later */
- }
-
- if (lo < higuy) {
- hi = higuy;
- goto recurse; /* do small recursion */
- }
- }
-
- /* We have sorted the array, except for any pending sorts on the stack.
- Check if there are any, and do them. */
-
- --stkptr;
- if (stkptr >= 0) {
- lo = lostk[stkptr];
- hi = histk[stkptr];
- goto recurse; /* pop subarray from stack */
- }
- else
- return; /* all subarrays done */
+ MD5_Init(&md5_ctx);
+ MD5_Update(&md5_ctx, pvDataBlock, cbDataBlock);
+ MD5_Final(md5_hash, &md5_ctx);
}
diff --git a/dep/CascLib/src/common/Common.h b/dep/CascLib/src/common/Common.h
index 484276a3bd7..c7f189b91ac 100644
--- a/dep/CascLib/src/common/Common.h
+++ b/dep/CascLib/src/common/Common.h
@@ -21,6 +21,76 @@
#define ALIGN_TO_SIZE(x, a) (((x) + (a)-1) & ~((a)-1))
#endif
+// Prevent problems with CRT "min" and "max" functions,
+// as they are not defined on all platforms
+#define CASCLIB_MIN(a, b) ((a < b) ? a : b)
+#define CASCLIB_MAX(a, b) ((a > b) ? a : b)
+#define CASCLIB_UNUSED(p) ((void)(p))
+
+//-----------------------------------------------------------------------------
+// Common structures
+
+// Structure for static content key (CKey) and encoded key (EKey)
+// The CKey is a MD5 hash of the file data.
+// The EKey is (shortened) MD5 hash of the file header, which contains MD5 hashes of all the logical blocks of the file.
+typedef struct _CONTENT_KEY
+{
+ BYTE Value[MD5_HASH_SIZE]; // MD5 of the file
+
+} CONTENT_KEY, *PCONTENT_KEY, ENCODED_KEY, *PENCODED_KEY;
+
+// Helper structure for merging file paths
+typedef struct _PATH_BUFFER
+{
+ char * szBegin;
+ char * szPtr;
+ char * szEnd;
+} PATH_BUFFER, *PPATH_BUFFER;
+
+//-----------------------------------------------------------------------------
+// Basic structure used by all CascLib objects to describe a single entry
+// in the CASC storage. Each entry represents one physical file
+// in the storage. Note that the file may be present under several file names.
+
+// Flags for CASC_CKEY_ENTRY::Flags
+#define CASC_CE_FILE_IS_LOCAL 0x00000001 // The file is available locally. Keep this flag to have value of 1
+#define CASC_CE_HAS_CKEY 0x00000002 // The CKey is present in the entry
+#define CASC_CE_HAS_EKEY 0x00000004 // The EKey is present, at least partial one
+#define CASC_CE_HAS_EKEY_PARTIAL 0x00000008 // The EKey is only partial, padded by zeros. Always used with CASC_CE_HAS_EKEY
+#define CASC_CE_IN_ENCODING 0x00000010 // Present in the ENCODING manifest
+#define CASC_CE_IN_DOWNLOAD 0x00000020 // Present in the DOWNLOAD manifest
+#define CASC_CE_FOLDER_ENTRY 0x00000040 // This CKey entry is a folder
+
+// In-memory representation of a single entry.
+struct CASC_CKEY_ENTRY
+{
+ CASC_CKEY_ENTRY()
+ {
+ Init();
+ }
+
+ void Init(void)
+ {
+ memset(this, 0, sizeof(CASC_CKEY_ENTRY));
+ StorageOffset = CASC_INVALID_OFFS64;
+ EncodedSize = CASC_INVALID_SIZE;
+ ContentSize = CASC_INVALID_SIZE;
+ }
+
+ BYTE CKey[MD5_HASH_SIZE]; // Content key of the full length
+ BYTE EKey[MD5_HASH_SIZE]; // Encoded key of the full length
+ ULONGLONG StorageOffset; // Linear offset over the entire storage. 0 if not present
+ ULONGLONG TagBitMask; // Bitmap for the tags. 0 ig tags are not supported
+ DWORD EncodedSize; // Encoded size of the file. 0 if not supported
+ DWORD ContentSize; // Content size of the file. 0 if not supported
+ DWORD Flags; // See CASC_CE_XXX
+ USHORT RefCount; // This is the number of file names referencing this entry
+ BYTE Priority; // Download priority
+ BYTE Alignment;
+
+};
+typedef CASC_CKEY_ENTRY *PCASC_CKEY_ENTRY;
+
//-----------------------------------------------------------------------------
// Conversion tables
@@ -29,23 +99,198 @@ extern unsigned char AsciiToUpperTable_BkSlash[256];
extern unsigned char IntToHexChar[];
//-----------------------------------------------------------------------------
-// String manipulation
+// Memory management
+//
+// We use our own macros for allocating/freeing memory. If you want
+// to redefine them, please keep the following rules:
+//
+// - The memory allocation must return NULL if not enough memory
+// (i.e not to throw exception)
+// - The allocating function does not need to fill the allocated buffer with zeros
+// - The reallocating function must support NULL as the previous block
+// - Memory freeing function must check for NULL pointer and do nothing if so
+//
+
+#define CASC_REALLOC(type, ptr, count) (type *)realloc(ptr, (count) * sizeof(type))
+#define CASC_ALLOC(type, count) (type *)malloc((count) * sizeof(type))
+
+template <typename T>
+void CASC_FREE(T *& ptr)
+{
+ if (ptr != NULL)
+ free(ptr);
+ ptr = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Overloaded "new" and "delete" operators
+
+void * operator new(size_t size);
+void * operator new[](size_t size);
+void operator delete(void * ptr);
+void operator delete[](void * ptr);
+void operator delete(void * ptr, size_t); // For some reason, VS2015 needs this
+
+//-----------------------------------------------------------------------------
+// Big endian number manipulation
+
+inline DWORD ConvertBytesToInteger_2(LPBYTE ValueAsBytes)
+{
+ USHORT Value = 0;
+
+ Value = (Value << 0x08) | ValueAsBytes[0];
+ Value = (Value << 0x08) | ValueAsBytes[1];
+
+ return Value;
+}
+
+inline DWORD ConvertBytesToInteger_3(LPBYTE ValueAsBytes)
+{
+ DWORD Value = 0;
+
+ Value = (Value << 0x08) | ValueAsBytes[0];
+ Value = (Value << 0x08) | ValueAsBytes[1];
+ Value = (Value << 0x08) | ValueAsBytes[2];
+
+ return Value;
+}
+
+inline DWORD ConvertBytesToInteger_4(LPBYTE ValueAsBytes)
+{
+ DWORD Value = 0;
+
+ Value = (Value << 0x08) | ValueAsBytes[0];
+ Value = (Value << 0x08) | ValueAsBytes[1];
+ Value = (Value << 0x08) | ValueAsBytes[2];
+ Value = (Value << 0x08) | ValueAsBytes[3];
+
+ return Value;
+}
+
+// Converts the variable-size big-endian into integer
+inline DWORD ConvertBytesToInteger_X(LPBYTE ValueAsBytes, DWORD dwByteSize)
+{
+ DWORD Value = 0;
+
+ if(dwByteSize > 0)
+ Value = (Value << 0x08) | ValueAsBytes[0];
+ if(dwByteSize > 1)
+ Value = (Value << 0x08) | ValueAsBytes[1];
+ if(dwByteSize > 2)
+ Value = (Value << 0x08) | ValueAsBytes[2];
+ if(dwByteSize > 3)
+ Value = (Value << 0x08) | ValueAsBytes[3];
+
+ return Value;
+}
+
+inline DWORD ConvertBytesToInteger_4_LE(LPBYTE ValueAsBytes)
+{
+ DWORD Value = 0;
+
+ Value = (Value << 0x08) | ValueAsBytes[3];
+ Value = (Value << 0x08) | ValueAsBytes[2];
+ Value = (Value << 0x08) | ValueAsBytes[1];
+ Value = (Value << 0x08) | ValueAsBytes[0];
+
+ return Value;
+}
+
+// Read the 40-bit big-endian offset into ULONGLONG
+inline ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes)
+{
+ ULONGLONG Value = 0;
+
+ Value = (Value << 0x08) | ValueAsBytes[0];
+ Value = (Value << 0x08) | ValueAsBytes[1];
+ Value = (Value << 0x08) | ValueAsBytes[2];
+ Value = (Value << 0x08) | ValueAsBytes[3];
+ Value = (Value << 0x08) | ValueAsBytes[4];
+
+ return Value;
+}
+
+inline void ConvertIntegerToBytes_4(DWORD Value, LPBYTE ValueAsBytes)
+{
+ ValueAsBytes[0] = (Value >> 0x18) & 0xFF;
+ ValueAsBytes[1] = (Value >> 0x10) & 0xFF;
+ ValueAsBytes[2] = (Value >> 0x08) & 0xFF;
+ ValueAsBytes[3] = (Value >> 0x00) & 0xFF;
+}
+
+inline void ConvertIntegerToBytes_4_LE(DWORD Value, LPBYTE ValueAsBytes)
+{
+ ValueAsBytes[0] = (Value >> 0x00) & 0xFF;
+ ValueAsBytes[1] = (Value >> 0x08) & 0xFF;
+ ValueAsBytes[2] = (Value >> 0x10) & 0xFF;
+ ValueAsBytes[3] = (Value >> 0x18) & 0xFF;
+}
+
+// Faster than memset(Buffer, 0, 0x10)
+inline void ZeroMemory16(void * Buffer)
+{
+ PDWORD PtrBuffer = (PDWORD)Buffer;
+
+ PtrBuffer[0] = 0;
+ PtrBuffer[1] = 0;
+ PtrBuffer[2] = 0;
+ PtrBuffer[3] = 0;
+}
+
+// Faster than memcpy(Target, Source, 0x10)
+inline void CopyMemory16(void * Target, void * Source)
+{
+ PDWORD PtrTarget = (PDWORD)Target;
+ PDWORD PtrSource = (PDWORD)Source;
+
+ PtrTarget[0] = PtrSource[0];
+ PtrTarget[1] = PtrSource[1];
+ PtrTarget[2] = PtrSource[2];
+ PtrTarget[3] = PtrSource[3];
+}
+
+//-----------------------------------------------------------------------------
+// Linear data stream manipulation
-void CopyString(char * szTarget, const char * szSource, size_t cchLength);
-void CopyString(wchar_t * szTarget, const char * szSource, size_t cchLength);
-void CopyString(char * szTarget, const wchar_t * szSource, size_t cchLength);
+LPBYTE CaptureInteger32(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue);
+LPBYTE CaptureInteger32_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue);
+LPBYTE CaptureByteArray(LPBYTE pbDataPtr, LPBYTE pbDataEnd, size_t nLength, LPBYTE pbOutput);
+LPBYTE CaptureContentKey(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PCONTENT_KEY * PtrCKey);
+LPBYTE CaptureArray_(LPBYTE pbDataPtr, LPBYTE pbDataEnd, LPBYTE * PtrArray, size_t ItemSize, size_t ItemCount);
-char * CascNewStr(const char * szString, size_t nCharsToReserve);
-wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve);
+#define CaptureArray(pbDataPtr, pbDataEnd, PtrArray, type, count) CaptureArray_(pbDataPtr, pbDataEnd, PtrArray, sizeof(type), count)
-TCHAR * CascNewStrFromAnsi(const char * szBegin, const char * szEnd);
+//-----------------------------------------------------------------------------
+// String copying and conversion
+
+void CascStrCopy(char * szTarget, size_t cchTarget, const char * szSource, size_t cchSource = -1);
+void CascStrCopy(char * szTarget, size_t cchTarget, const wchar_t * szSource, size_t cchSource = -1);
+void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const char * szSource, size_t cchSource = -1);
+void CascStrCopy(wchar_t * szTarget, size_t cchTarget, const wchar_t * szSource, size_t cchSource = -1);
+
+//-----------------------------------------------------------------------------
+// Safe version of s(w)printf
+
+size_t CascStrPrintf(char * buffer, size_t nCount, const char * format, ...);
+size_t CascStrPrintf(wchar_t * buffer, size_t nCount, const wchar_t * format, ...);
+
+//-----------------------------------------------------------------------------
+// String allocation
+
+char * CascNewStr(const char * szString, size_t nCharsToReserve = 0);
+wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve = 0);
TCHAR * CombinePath(const TCHAR * szPath, const TCHAR * szSubDir);
-TCHAR * CombinePathAndString(const TCHAR * szPath, const char * szString, size_t nLength);
+size_t CombineFilePath(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szPath, const TCHAR * szSubPath1, const TCHAR * szSubPath2 = NULL);
+size_t CombineUrlPath(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szHost, const TCHAR * szSubPath1, const TCHAR * szSubPath2 = NULL);
+TCHAR * GetLastPathPart(TCHAR * szWorkPath);
+bool CutLastPathPart(TCHAR * szWorkPath);
+size_t CreateCascSubdirectoryName(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szSubDir, const TCHAR * szExtension, LPBYTE pbEKey);
size_t NormalizeFileName_UpperBkSlash(char * szNormName, const char * szFileName, size_t cchMaxChars);
size_t NormalizeFileName_LowerSlash(char * szNormName, const char * szFileName, size_t cchMaxChars);
+ULONGLONG CalcNormNameHash(const char * szNormName, size_t nLength);
ULONGLONG CalcFileNameHash(const char * szFileName);
int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue);
@@ -56,20 +301,81 @@ char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer);
char * StringFromMD5(LPBYTE md5, char * szBuffer);
//-----------------------------------------------------------------------------
+// Structure query key
+
+struct QUERY_KEY
+{
+ QUERY_KEY()
+ {
+ pbData = NULL;
+ cbData = 0;
+ }
+
+ ~QUERY_KEY()
+ {
+ CASC_FREE(pbData);
+ cbData = 0;
+ }
+
+ LPBYTE pbData;
+ size_t cbData;
+};
+typedef QUERY_KEY *PQUERY_KEY;
+
+//-----------------------------------------------------------------------------
// File name utilities
-bool CheckWildCard(const char * szString, const char * szWildCard);
-const wchar_t * GetPlainFileName(const wchar_t * szFileName);
-const char * GetPlainFileName(const char * szFileName);
+// Retrieves the pointer to plain name
+template <typename XCHAR>
+const XCHAR * GetPlainFileName(const XCHAR * szFileName)
+{
+ const XCHAR * szPlainName = szFileName;
+
+ while(*szFileName != 0)
+ {
+ if(*szFileName == '\\' || *szFileName == '/')
+ szPlainName = szFileName + 1;
+ szFileName++;
+ }
+
+ return szPlainName;
+}
+
+// Retrieves the pointer to file extension
+template <typename XCHAR>
+const XCHAR * GetFileExtension(const XCHAR * szFileName)
+{
+ const XCHAR * szExtension = NULL;
+
+ // We need to start searching from the plain name
+ // Avoid: C:\$RECYCLE.BIN\File.ext
+ szFileName = GetPlainFileName(szFileName);
+
+ // Find the last dot in the plain file name
+ while(szFileName[0] != 0)
+ {
+ if(szFileName[0] == '.')
+ szExtension = szFileName;
+ szFileName++;
+ }
+
+ // If not found, return the end of the file name
+ return (XCHAR *)((szExtension != NULL) ? szExtension : szFileName);
+}
+
+bool IsFileDataIdName(const char * szFileName, DWORD & FileDataId);
+bool IsFileCKeyEKeyName(const char * szFileName, LPBYTE PtrKeyBuffer);
+
+bool CascCheckWildCard(const char * szString, const char * szWildCard);
//-----------------------------------------------------------------------------
// Hashing functions
ULONGLONG HashStringJenkins(const char * szFileName);
-bool IsValidMD5(LPBYTE pbMd5);
-void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash);
-bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5);
+bool CascIsValidMD5(LPBYTE pbMd5);
+void CascCalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash);
+bool CascVerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5);
//-----------------------------------------------------------------------------
// Scanning a directory
diff --git a/dep/CascLib/src/common/Csv.cpp b/dep/CascLib/src/common/Csv.cpp
new file mode 100644
index 00000000000..24b654daa86
--- /dev/null
+++ b/dep/CascLib/src/common/Csv.cpp
@@ -0,0 +1,370 @@
+/*****************************************************************************/
+/* Csv.cpp Copyright (c) Ladislav Zezula 2019 */
+/*---------------------------------------------------------------------------*/
+/* Implementation for the CSV handler class */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 24.05.19 1.00 Lad Created */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "../CascLib.h"
+#include "../CascCommon.h"
+
+//-----------------------------------------------------------------------------
+// Local variables
+
+static const CASC_CSV_LINE NullLine;
+#define NullColumn NullLine.Columns[0];
+
+//-----------------------------------------------------------------------------
+// Local functions
+
+static char * NextLine(char * szLine)
+{
+ // Find the end of the line
+ while(szLine[0] != 0 && szLine[0] != 0x0A && szLine[0] != 0x0D)
+ szLine++;
+
+ // Terminate the line
+ while(szLine[0] == 0x0A || szLine[0] == 0x0D)
+ *szLine++ = 0;
+
+ // If there's an end-of-string, it's over
+ return (szLine[0] != 0) ? szLine : NULL;
+}
+
+static char * NextColumn(char * szColumn)
+{
+ // Find the end of the column
+ while(szColumn[0] != 0 && szColumn[0] != '|')
+ szColumn++;
+
+ // Terminate the column
+ if (szColumn[0] == '|')
+ {
+ *szColumn++ = 0;
+ return szColumn;
+ }
+
+ // If there's an end-of-string, it's over
+ return NULL;
+}
+
+static size_t CalcHashValue(const char * szString)
+{
+ size_t dwHash = 0x7EEE7EEE;
+
+ // Hash the string itself
+ while(szString[0] != 0)
+ {
+ dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ szString[0];
+ szString++;
+ }
+
+ // Return the hash limited by the table size
+ return dwHash;
+}
+
+static BYTE HashToIndex(size_t dwHashValue)
+{
+ return (BYTE)(dwHashValue & (CSV_HASH_TABLE_SIZE - 1));
+}
+
+//-----------------------------------------------------------------------------
+// Class for CSV line
+
+CASC_CSV_LINE::CASC_CSV_LINE()
+{
+ m_pParent = NULL;
+ m_nColumns = 0;
+}
+
+CASC_CSV_LINE::~CASC_CSV_LINE()
+{}
+
+bool CASC_CSV_LINE::SetLine(CASC_CSV * pParent, char * szCsvLine)
+{
+ char * szCsvColumn;
+ size_t nHdrColumns = 0;
+ size_t nColumns = 0;
+
+ // Reset the number of column to zero
+ m_pParent = pParent;
+ m_nColumns = 0;
+
+ // Parse each column
+ while(szCsvLine != NULL && nColumns < CSV_MAX_COLUMNS)
+ {
+ // Get current line and the next one
+ szCsvColumn = szCsvLine;
+ szCsvLine = NextColumn(szCsvLine);
+
+ // Save the current line
+ Columns[nColumns].szValue = szCsvColumn;
+ Columns[nColumns].nLength = strlen(szCsvColumn);
+ nColumns++;
+ }
+
+ // Columns overflow
+ if(nColumns >= CSV_MAX_COLUMNS)
+ {
+ assert(false);
+ return false;
+ }
+
+ // If the parent has header lines, then the number of columns must match
+ // In the case of mismatched column count, ignore the line
+ nHdrColumns = pParent->GetHeaderColumns();
+ if(nHdrColumns != 0 && nColumns != nHdrColumns)
+ return false;
+
+ // All OK
+ m_nColumns = nColumns;
+ return true;
+}
+
+const CASC_CSV_COLUMN & CASC_CSV_LINE::operator[](const char * szColumnName) const
+{
+ size_t nIndex;
+
+ // The column must have a hash table
+ if(m_pParent != NULL)
+ {
+ nIndex = m_pParent->GetColumnIndex(szColumnName);
+ if(nIndex != CSV_INVALID_INDEX && nIndex < m_nColumns)
+ {
+ return Columns[nIndex];
+ }
+ }
+
+ return NullColumn;
+}
+
+const CASC_CSV_COLUMN & CASC_CSV_LINE::operator[](size_t nIndex) const
+{
+ return (nIndex < m_nColumns) ? Columns[nIndex] : NullColumn;
+}
+
+//-----------------------------------------------------------------------------
+// Class for CSV object
+
+CASC_CSV::CASC_CSV(size_t nLinesMax, bool bHasHeader)
+{
+ // Initialize the class variables
+ memset(HashTable, 0xFF, sizeof(HashTable));
+ m_szCsvFile = NULL;
+ m_szCsvPtr = NULL;
+ m_nCsvFile = 0;
+ m_nLines = 0;
+ m_bHasHeader = bHasHeader;
+
+ // Nonzero number of lines means a finite CSV handler. The CSV will be loaded at once.
+ // Zero means that the user needs to call LoadNextLine() and then the line data
+ if(nLinesMax != 0)
+ {
+ m_pLines = new CASC_CSV_LINE[nLinesMax];
+ m_nLinesMax = nLinesMax;
+ m_bHasAllLines = true;
+ }
+ else
+ {
+ m_pLines = new CASC_CSV_LINE[1];
+ m_nLinesMax = 1;
+ m_bHasAllLines = false;
+ }
+}
+
+CASC_CSV::~CASC_CSV()
+{
+ if(m_pLines != NULL)
+ delete[] m_pLines;
+ if(m_szCsvFile != NULL)
+ delete [] m_szCsvFile;
+ m_szCsvFile = NULL;
+}
+
+int CASC_CSV::Load(const TCHAR * szFileName)
+{
+ DWORD cbFileData = 0;
+ int nError = ERROR_SUCCESS;
+
+ m_szCsvFile = (char *)LoadFileToMemory(szFileName, &cbFileData);
+ if (m_szCsvFile != NULL)
+ {
+ // There is one extra byte reserved by LoadFileToMemory, so we can put zero there
+ m_szCsvFile[cbFileData] = 0;
+ m_szCsvPtr = m_szCsvFile;
+ m_nCsvFile = cbFileData;
+
+ // Parse the data
+ nError = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
+ }
+ else
+ {
+ nError = GetLastError();
+ }
+
+ return nError;
+}
+
+int CASC_CSV::Load(LPBYTE pbData, size_t cbData)
+{
+ int nError = ERROR_NOT_ENOUGH_MEMORY;
+
+ m_szCsvFile = new char[cbData + 1];
+ if (m_szCsvFile != NULL)
+ {
+ // Copy the entire data and terminate them with zero
+ memcpy(m_szCsvFile, pbData, cbData);
+ m_szCsvFile[cbData] = 0;
+ m_szCsvPtr = m_szCsvFile;
+ m_nCsvFile = cbData;
+
+ // Parse the data
+ nError = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
+ }
+
+ return nError;
+}
+
+bool CASC_CSV::LoadNextLine()
+{
+ bool bResult = false;
+
+ // Only endless CSV handler can do this
+ if(m_bHasAllLines == false)
+ {
+ // A few checks
+ assert(m_pLines != NULL);
+ assert(m_nLinesMax == 1);
+
+ // Reset the current line and load it
+ bResult = LoadNextLine(m_pLines[0]);
+ m_nLines = (bResult) ? 1 : 0;
+ }
+
+ return bResult;
+}
+
+bool CASC_CSV::InitializeHashTable()
+{
+ // Create the hash table of HeaderText -> ColumnIndex
+ for(size_t i = 0; i < Header.GetColumnCount(); i++)
+ {
+ // Calculate the start slot and the current slot
+ size_t nStartIndex = HashToIndex(CalcHashValue(Header[i].szValue));
+ size_t nHashIndex = nStartIndex;
+
+ // Go as long as there is not a free space
+ while(HashTable[nHashIndex] != 0xFF)
+ {
+ nHashIndex = HashToIndex(nHashIndex + 1);
+ }
+
+ // Set the slot
+ HashTable[nHashIndex] = (BYTE)i;
+ }
+
+ return true;
+}
+
+bool CASC_CSV::LoadNextLine(CASC_CSV_LINE & Line)
+{
+ // Overwatch ROOT's header begins with "#"
+ if(m_szCsvPtr == m_szCsvFile && m_szCsvPtr[0] == '#')
+ m_szCsvPtr++;
+
+ // Parse the entire line
+ while(m_szCsvPtr != NULL && m_szCsvPtr[0] != 0)
+ {
+ char * szCsvLine = m_szCsvPtr;
+
+ // Get the next line
+ m_szCsvPtr = NextLine(m_szCsvPtr);
+
+ // Initialize the line
+ if (Line.SetLine(this, szCsvLine))
+ return true;
+ }
+
+ // End-of-file found
+ return false;
+}
+
+bool CASC_CSV::ParseCsvData()
+{
+ // Sanity checks
+ assert(m_nLines == 0);
+
+ // If we have header, then parse it and set the pointer to the next line
+ if(m_bHasHeader)
+ {
+ // Load the current line to the header
+ if (!LoadNextLine(Header))
+ return false;
+
+ // Initialize the hash table
+ if(!InitializeHashTable())
+ return false;
+ }
+
+ // Are we supposed to load all lines?
+ if(m_bHasAllLines)
+ {
+ // Parse each line
+ for(size_t i = 0; i < m_nLinesMax; i++)
+ {
+ if(!LoadNextLine(m_pLines[i]))
+ break;
+ m_nLines++;
+ }
+ }
+
+ return true;
+}
+
+const CASC_CSV_COLUMN & CASC_CSV::operator[](const char * szColumnName) const
+{
+ if (m_pLines == NULL || m_nLines == 0)
+ return NullColumn;
+ return m_pLines[0][GetColumnIndex(szColumnName)];
+}
+
+const CASC_CSV_LINE & CASC_CSV::operator[](size_t nIndex) const
+{
+ if (m_pLines == NULL || nIndex >= m_nLines)
+ return NullLine;
+ return m_pLines[nIndex];
+}
+
+size_t CASC_CSV::GetHeaderColumns() const
+{
+ return (m_bHasHeader) ? Header.GetColumnCount() : 0;
+}
+
+size_t CASC_CSV::GetColumnIndex(const char * szColumnName) const
+{
+ if(m_bHasHeader)
+ {
+ // Calculate the start slot and the current slot
+ size_t nStartIndex = HashToIndex(CalcHashValue(szColumnName));
+ size_t nHashIndex = nStartIndex;
+ size_t nIndex;
+
+ // Go as long as there is not a free space
+ while((nIndex = HashTable[nHashIndex]) != 0xFF)
+ {
+ // Compare the string
+ if(!strcmp(Header[nIndex].szValue, szColumnName))
+ return nIndex;
+
+ // Move to the next column
+ nHashIndex = HashToIndex(nHashIndex + 1);
+ }
+ }
+
+ return CSV_INVALID_INDEX;
+}
+
diff --git a/dep/CascLib/src/common/Csv.h b/dep/CascLib/src/common/Csv.h
new file mode 100644
index 00000000000..2138255e74e
--- /dev/null
+++ b/dep/CascLib/src/common/Csv.h
@@ -0,0 +1,105 @@
+/*****************************************************************************/
+/* Csv.h Copyright (c) Ladislav Zezula 2019 */
+/*---------------------------------------------------------------------------*/
+/* Interface for the CSV handler class */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 24.05.19 1.00 Lad Created */
+/*****************************************************************************/
+
+#ifndef __CSV_H__
+#define __CSV_H__
+
+//-----------------------------------------------------------------------------
+// Defines
+
+#define CSV_INVALID_INDEX ((size_t)(-1))
+#define CSV_ZERO ((size_t)(0)) // Use Csv[0][CSV_ZERO] instead of ambiguous Csv[0][0]
+#define CSV_MAX_COLUMNS 0x10
+#define CSV_HASH_TABLE_SIZE 0x80
+
+//-----------------------------------------------------------------------------
+// Class for CSV line
+
+class CASC_CSV;
+
+struct CASC_CSV_COLUMN
+{
+ CASC_CSV_COLUMN()
+ {
+ szValue = NULL;
+ nLength = 0;
+ }
+
+ const char * szValue;
+ size_t nLength;
+};
+
+class CASC_CSV_LINE
+{
+ public:
+
+ CASC_CSV_LINE();
+ ~CASC_CSV_LINE();
+
+ bool SetLine(CASC_CSV * pParent, char * szLine);
+
+ const CASC_CSV_COLUMN & operator[](const char * szColumnName) const;
+ const CASC_CSV_COLUMN & operator[](size_t nIndex) const;
+
+ size_t GetColumnCount() const
+ {
+ return m_nColumns;
+ }
+
+ protected:
+
+ friend class CASC_CSV;
+
+ CASC_CSV * m_pParent;
+ CASC_CSV_COLUMN Columns[CSV_MAX_COLUMNS];
+ size_t m_nColumns;
+};
+
+class CASC_CSV
+{
+ public:
+
+ CASC_CSV(size_t nLinesMax, bool bHasHeader);
+ ~CASC_CSV();
+
+ int Load(LPBYTE pbData, size_t cbData);
+ int Load(const TCHAR * szFileName);
+ bool LoadNextLine();
+
+ const CASC_CSV_COLUMN & operator[](const char * szColumnName) const;
+ const CASC_CSV_LINE & operator[](size_t nIndex) const;
+
+ size_t GetHeaderColumns() const;
+ size_t GetColumnIndex(const char * szColumnName) const;
+
+ size_t GetLineCount() const
+ {
+ return m_nLines;
+ }
+
+ protected:
+
+ bool InitializeHashTable();
+ bool LoadNextLine(CASC_CSV_LINE & Line);
+ bool ParseCsvData();
+
+ CASC_CSV_LINE * m_pLines;
+ CASC_CSV_LINE Header;
+ BYTE HashTable[CSV_HASH_TABLE_SIZE];
+ char * m_szCsvFile;
+ char * m_szCsvPtr;
+ size_t m_nCsvFile;
+ size_t m_nLinesMax;
+ size_t m_nLines;
+ bool m_bHasHeader;
+ bool m_bHasAllLines;
+};
+
+#endif // __CSV_H__
diff --git a/dep/CascLib/src/common/Directory.cpp b/dep/CascLib/src/common/Directory.cpp
index c4091095859..463881f3f9c 100644
--- a/dep/CascLib/src/common/Directory.cpp
+++ b/dep/CascLib/src/common/Directory.cpp
@@ -38,6 +38,20 @@ bool DirectoryExists(const TCHAR * szDirectory)
return false;
}
+bool MakeDirectory(const TCHAR * szDirectory)
+{
+#ifdef PLATFORM_WINDOWS
+
+ BOOL bResult = CreateDirectory(szDirectory, NULL);
+ return (bResult) ? true : false;
+
+#else
+
+ return (mkdir(szDirectory, 0755) == 0);
+
+#endif
+}
+
int ScanIndexDirectory(
const TCHAR * szIndexPath,
INDEX_FILE_FOUND pfnOnFileFound,
diff --git a/dep/CascLib/src/common/Directory.h b/dep/CascLib/src/common/Directory.h
index ae97dca3d76..739f0612e54 100644
--- a/dep/CascLib/src/common/Directory.h
+++ b/dep/CascLib/src/common/Directory.h
@@ -18,6 +18,8 @@ typedef bool (*INDEX_FILE_FOUND)(const TCHAR * szFileName, PDWORD IndexArray, PD
bool DirectoryExists(const TCHAR * szDirectory);
+bool MakeDirectory(const TCHAR * szDirectory);
+
int ScanIndexDirectory(
const TCHAR * szIndexPath,
INDEX_FILE_FOUND pfnOnFileFound,
diff --git a/dep/CascLib/src/common/DumpContext.cpp b/dep/CascLib/src/common/DumpContext.cpp
deleted file mode 100644
index 8783ff201ba..00000000000
--- a/dep/CascLib/src/common/DumpContext.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*****************************************************************************/
-/* DumpContext.cpp Copyright (c) Ladislav Zezula 2015 */
-/*---------------------------------------------------------------------------*/
-/* Implementation of dump context */
-/*---------------------------------------------------------------------------*/
-/* Date Ver Who Comment */
-/* -------- ---- --- ------- */
-/* 16.03.15 1.00 Lad Created */
-/*****************************************************************************/
-
-#define __CASCLIB_SELF__
-#include "../CascLib.h"
-#include "../CascCommon.h"
-
-//-----------------------------------------------------------------------------
-// Local functions
-
-static TCHAR * FormatFileName(TCascStorage * hs, const TCHAR * szNameFormat)
-{
- TCHAR * szFileName;
- TCHAR * szSrc;
- TCHAR * szTrg;
-
- // Create copy of the file name
- szFileName = szSrc = szTrg = CascNewStr(szNameFormat, 0);
- if(szFileName != NULL)
- {
- // Format the file name
- while(szSrc[0] != 0)
- {
- if(szSrc[0] == _T('%'))
- {
- // Replace "%build%" with a build number
- if(!_tcsncmp(szSrc, _T("%build%"), 7))
- {
- szTrg += _stprintf(szTrg, _T("%u"), hs->dwBuildNumber);
- szSrc += 7;
- continue;
- }
- }
-
- // Just copy the character
- *szTrg++ = *szSrc++;
- }
-
- // Terminate the target file name
- szTrg[0] = 0;
- }
-
- return szFileName;
-}
-
-//-----------------------------------------------------------------------------
-// Public functions
-
-TDumpContext * CreateDumpContext(TCascStorage * hs, const TCHAR * szNameFormat)
-{
- TDumpContext * dc;
- TCHAR * szFileName;
-
- // Validate the storage handle
- if(hs != NULL)
- {
- // Calculate the number of bytes needed for dump context
- dc = CASC_ALLOC(TDumpContext, 1);
- if(dc != NULL)
- {
- // Initialize the dump context
- memset(dc, 0, sizeof(TDumpContext));
-
- // Format the real file name
- szFileName = FormatFileName(hs, szNameFormat);
- if(szFileName != NULL)
- {
- // Create the file
- dc->pStream = FileStream_CreateFile(szFileName, 0);
- if(dc->pStream != NULL)
- {
- // Initialize buffers
- dc->pbBufferBegin =
- dc->pbBufferPtr = dc->DumpBuffer;
- dc->pbBufferEnd = dc->DumpBuffer + CASC_DUMP_BUFFER_SIZE;
-
- // Success
- CASC_FREE(szFileName);
- return dc;
- }
-
- // File create failed --> delete the file name
- CASC_FREE(szFileName);
- }
-
- // Free the dump context
- CASC_FREE(dc);
- }
- }
-
- return NULL;
-}
-
-int dump_print(TDumpContext * dc, const char * szFormat, ...)
-{
- va_list argList;
- char szBuffer[0x200];
- int nLength = 0;
-
- // Only if the dump context is valid
- if(dc != NULL)
- {
- // Print the buffer using sprintf
- va_start(argList, szFormat);
- nLength = vsprintf(szBuffer, szFormat, argList);
- assert(nLength < 0x200);
- va_end(argList);
-
- // Copy the string. Replace "\n" with "\n\r"
- for(int i = 0; i < nLength; i++)
- {
- // Flush the buffer, if needed
- if((dc->pbBufferPtr + 2) >= dc->pbBufferEnd)
- {
- FileStream_Write(dc->pStream, NULL, dc->pbBufferBegin, (DWORD)(dc->pbBufferPtr - dc->pbBufferBegin));
- dc->pbBufferPtr = dc->pbBufferBegin;
- }
-
- // Copy the char
- if(szBuffer[i] == 0x0A)
- *dc->pbBufferPtr++ = 0x0D;
- *dc->pbBufferPtr++ = szBuffer[i];
- }
- }
-
- return nLength;
-}
-
-int dump_close(TDumpContext * dc)
-{
- // Only if the dump context is valid
- if(dc != NULL)
- {
- // Flush the dump context if there are some data
- if(dc->pbBufferPtr > dc->pbBufferBegin)
- FileStream_Write(dc->pStream, NULL, dc->pbBufferBegin, (DWORD)(dc->pbBufferPtr - dc->pbBufferBegin));
- dc->pbBufferPtr = dc->pbBufferBegin;
-
- // Free the file stream and the entire context
- if(dc->pStream != NULL)
- FileStream_Close(dc->pStream);
- CASC_FREE(dc);
- }
-
- return 0;
-}
diff --git a/dep/CascLib/src/common/DumpContext.h b/dep/CascLib/src/common/DumpContext.h
deleted file mode 100644
index 6f725f5b942..00000000000
--- a/dep/CascLib/src/common/DumpContext.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*****************************************************************************/
-/* DumpContext.h Copyright (c) Ladislav Zezula 2015 */
-/*---------------------------------------------------------------------------*/
-/* Interface for TDumpContext */
-/*---------------------------------------------------------------------------*/
-/* Date Ver Who Comment */
-/* -------- ---- --- ------- */
-/* 16.03.15 1.00 Lad Created */
-/*****************************************************************************/
-
-#ifndef __DUMP_CONTEXT_H__
-#define __DUMP_CONTEXT_H__
-
-//-----------------------------------------------------------------------------
-// Defines
-
-// Size of the buffer for the dump context
-#define CASC_DUMP_BUFFER_SIZE 0x10000
-
-// Structure for dump context
-struct TDumpContext
-{
- TFileStream * pStream; // Pointer to the open stream
- LPBYTE pbBufferBegin; // Begin of the dump buffer
- LPBYTE pbBufferPtr; // Current dump buffer pointer
- LPBYTE pbBufferEnd; // End of the dump buffer
-
- BYTE DumpBuffer[CASC_DUMP_BUFFER_SIZE]; // Dump buffer
-};
-
-//-----------------------------------------------------------------------------
-// Dump context functions
-
-TDumpContext * CreateDumpContext(struct _TCascStorage * hs, const TCHAR * szNameFormat);
-int dump_print(TDumpContext * dc, const char * szFormat, ...);
-int dump_close(TDumpContext * dc);
-
-#endif // __DUMP_CONTEXT_H__
diff --git a/dep/CascLib/src/common/DynamicArray.cpp b/dep/CascLib/src/common/DynamicArray.cpp
deleted file mode 100644
index e744fbe3912..00000000000
--- a/dep/CascLib/src/common/DynamicArray.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*****************************************************************************/
-/* DynamicArray.cpp Copyright (c) Ladislav Zezula 2015 */
-/*---------------------------------------------------------------------------*/
-/* Description: */
-/*---------------------------------------------------------------------------*/
-/* Date Ver Who Comment */
-/* -------- ---- --- ------- */
-/* 30.10.15 1.00 Lad The first version of DynamicArray.cpp */
-/*****************************************************************************/
-
-#define __CASCLIB_SELF__
-#include "../CascLib.h"
-#include "../CascCommon.h"
-
-//-----------------------------------------------------------------------------
-// Local functions
-
-static bool EnlargeArray(PDYNAMIC_ARRAY pArray, size_t NewItemCount)
-{
- char * NewItemArray;
- size_t ItemCountMax;
-
- // We expect the array to be already allocated
- assert(pArray->ItemArray != NULL);
- assert(pArray->ItemCountMax != 0);
-
- // Shall we enlarge the table?
- if(NewItemCount > pArray->ItemCountMax)
- {
- // Calculate new table size
- ItemCountMax = pArray->ItemCountMax;
- while(ItemCountMax < NewItemCount)
- ItemCountMax = ItemCountMax << 1;
-
- // Allocate new table
- NewItemArray = CASC_REALLOC(char, pArray->ItemArray, pArray->ItemSize * ItemCountMax);
- if(NewItemArray == NULL)
- return false;
-
- // Set the new table size
- pArray->ItemCountMax = ItemCountMax;
- pArray->ItemArray = NewItemArray;
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Public functions
-
-int Array_Create_(PDYNAMIC_ARRAY pArray, size_t ItemSize, size_t ItemCountMax)
-{
- pArray->ItemArray = CASC_ALLOC(char, (ItemSize * ItemCountMax));
- if(pArray->ItemArray == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- pArray->ItemCountMax = ItemCountMax;
- pArray->ItemCount = 0;
- pArray->ItemSize = ItemSize;
- return ERROR_SUCCESS;
-}
-
-void * Array_Insert(PDYNAMIC_ARRAY pArray, const void * NewItems, size_t NewItemCount)
-{
- char * NewItemPtr;
-
- // Try to enlarge the buffer, if needed
- if(!EnlargeArray(pArray, pArray->ItemCount + NewItemCount))
- return NULL;
- NewItemPtr = pArray->ItemArray + (pArray->ItemCount * pArray->ItemSize);
-
- // Copy the old item(s), if any
- if(NewItems != NULL)
- memcpy(NewItemPtr, NewItems, (NewItemCount * pArray->ItemSize));
-
- // Increment the size of the array
- pArray->ItemCount += NewItemCount;
- return NewItemPtr;
-}
-
-void * Array_ItemAt(PDYNAMIC_ARRAY pArray, size_t ItemIndex)
-{
- assert(ItemIndex < pArray->ItemCount);
- return pArray->ItemArray + (ItemIndex * pArray->ItemSize);
-}
-
-size_t Array_IndexOf(PDYNAMIC_ARRAY pArray, const void * ArrayPtr)
-{
- char * ArrayItem = (char *)ArrayPtr;
-
- assert(pArray->ItemArray <= ArrayItem && ArrayItem <= pArray->ItemArray + (pArray->ItemCount * pArray->ItemSize));
- return ((ArrayItem - pArray->ItemArray) / pArray->ItemSize);
-}
-
-void Array_Free(PDYNAMIC_ARRAY pArray)
-{
- if(pArray != NULL && pArray->ItemArray != NULL)
- {
- CASC_FREE(pArray->ItemArray);
- }
-}
diff --git a/dep/CascLib/src/common/DynamicArray.h b/dep/CascLib/src/common/DynamicArray.h
deleted file mode 100644
index 11cefacdcc4..00000000000
--- a/dep/CascLib/src/common/DynamicArray.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*****************************************************************************/
-/* DynamicArray.h Copyright (c) Ladislav Zezula 2015 */
-/*---------------------------------------------------------------------------*/
-/* Common array implementation */
-/*---------------------------------------------------------------------------*/
-/* Date Ver Who Comment */
-/* -------- ---- --- ------- */
-/* 30.10.15 1.00 Lad The first version of DynamicArray.h */
-/*****************************************************************************/
-
-#ifndef __DYNAMIC_ARRAY_H__
-#define __DYNAMIC_ARRAY_H__
-
-//-----------------------------------------------------------------------------
-// Structures
-
-typedef struct _DYNAMIC_ARRAY
-{
- char * ItemArray; // Pointer to items
- size_t ItemCountMax; // Current number of items
- size_t ItemCount; // Total size of the buffer
- size_t ItemSize; // Size of the single item
-
-} DYNAMIC_ARRAY, *PDYNAMIC_ARRAY;
-
-//-----------------------------------------------------------------------------
-// Functions for managing the array
-
-int Array_Create_(PDYNAMIC_ARRAY pArray, size_t ItemSize, size_t ItemCountMax);
-void * Array_Insert(PDYNAMIC_ARRAY pArray, const void * NewItems, size_t NewItemCount);
-void * Array_ItemAt(PDYNAMIC_ARRAY pArray, size_t ItemIndex);
-size_t Array_IndexOf(PDYNAMIC_ARRAY pArray, const void * ArrayPtr);
-void Array_Free(PDYNAMIC_ARRAY pArray);
-
-#define Array_Create(pArray, type, ItemCountMax) Array_Create_(pArray, sizeof(type), ItemCountMax)
-
-#endif // __DYNAMIC_ARRAY__
diff --git a/dep/CascLib/src/common/FileStream.cpp b/dep/CascLib/src/common/FileStream.cpp
index 22d7fceb4c2..845e793a6fa 100644
--- a/dep/CascLib/src/common/FileStream.cpp
+++ b/dep/CascLib/src/common/FileStream.cpp
@@ -219,10 +219,24 @@ static bool BaseFile_Read(
#endif
// Increment the current file position by number of bytes read
- // If the number of bytes read doesn't match to required amount, return false
pStream->Base.File.FilePos = ByteOffset + dwBytesRead;
- if(dwBytesRead != dwBytesToRead)
- SetLastError(ERROR_HANDLE_EOF);
+
+ // If the number of bytes read doesn't match to required amount, return false
+ // However, Blizzard's CASC handlers read encoded data so that if less than expected
+ // was read, then they fill the rest with zeros
+ if(dwBytesRead < dwBytesToRead)
+ {
+ if(pStream->dwFlags & STREAM_FLAG_FILL_MISSING)
+ {
+ memset((LPBYTE)pvBuffer + dwBytesRead, 0, (dwBytesToRead - dwBytesRead));
+ dwBytesRead = dwBytesToRead;
+ }
+ else
+ {
+ SetLastError(ERROR_HANDLE_EOF);
+ }
+ }
+
return (dwBytesRead == dwBytesToRead);
}
@@ -597,22 +611,26 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
{
#ifdef PLATFORM_WINDOWS
+ INTERNET_PORT ServerPort = INTERNET_DEFAULT_HTTP_PORT;
HINTERNET hRequest;
DWORD dwTemp = 0;
bool bFileAvailable = false;
int nError = ERROR_SUCCESS;
- // Keep compiler happy
- dwStreamFlags = dwStreamFlags;
+ // Check alternate ports
+ if(dwStreamFlags & STREAM_FLAG_USE_PORT_1119)
+ {
+ ServerPort = 1119;
+ }
- // Don't connect to the internet
+ // Don't download if we are not connected to the internet
if(!InternetGetConnectedState(&dwTemp, 0))
nError = GetLastError();
// Initiate the connection to the internet
if(nError == ERROR_SUCCESS)
{
- pStream->Base.Http.hInternet = InternetOpen(_T("CascLib HTTP archive reader"),
+ pStream->Base.Http.hInternet = InternetOpen(_T("agent/2.17.2.6700"),
INTERNET_OPEN_TYPE_PRECONFIG,
NULL,
NULL,
@@ -631,7 +649,7 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
szFileName = BaseHttp_ExtractServerName(szFileName, szServerName);
pStream->Base.Http.hConnect = InternetConnect(pStream->Base.Http.hInternet,
szServerName,
- INTERNET_DEFAULT_HTTP_PORT,
+ ServerPort,
NULL,
NULL,
INTERNET_SERVICE_HTTP,
@@ -651,26 +669,41 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
if(HttpSendRequest(hRequest, NULL, 0, NULL, 0))
{
ULONGLONG FileTime = 0;
+ LPTSTR szEndPtr;
+ TCHAR szStatusCode[0x10];
+ DWORD dwStatusCode = 404;
DWORD dwFileSize = 0;
- DWORD dwDataSize;
+ DWORD dwDataSize = sizeof(szStatusCode);
DWORD dwIndex = 0;
- // Check if the archive has Last Modified field
- dwDataSize = sizeof(ULONGLONG);
- if(HttpQueryInfo(hRequest, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, &FileTime, &dwDataSize, &dwIndex))
- pStream->Base.Http.FileTime = FileTime;
+ // Check whether the file exists
+ if (HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, szStatusCode, &dwDataSize, &dwIndex))
+ dwStatusCode = _tcstoul(szStatusCode, &szEndPtr, 10);
- // Verify if the server supports random access
- dwDataSize = sizeof(DWORD);
- if(HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwFileSize, &dwDataSize, &dwIndex))
+ // Copare the statuc code
+ if (dwStatusCode == 200)
{
- if(dwFileSize != 0)
+ // Check if the archive has Last Modified field
+ dwDataSize = sizeof(ULONGLONG);
+ if (HttpQueryInfo(hRequest, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, &FileTime, &dwDataSize, &dwIndex))
+ pStream->Base.Http.FileTime = FileTime;
+
+ // Verify if the server supports random access
+ dwDataSize = sizeof(DWORD);
+ if (HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwFileSize, &dwDataSize, &dwIndex))
{
- pStream->Base.Http.FileSize = dwFileSize;
- pStream->Base.Http.FilePos = 0;
- bFileAvailable = true;
+ if (dwFileSize != 0)
+ {
+ pStream->Base.Http.FileSize = dwFileSize;
+ pStream->Base.Http.FilePos = 0;
+ bFileAvailable = true;
+ }
}
}
+ else
+ {
+ nError = ERROR_FILE_NOT_FOUND;
+ }
}
InternetCloseHandle(hRequest);
}
@@ -681,6 +714,7 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
if(bFileAvailable == false)
{
pStream->BaseClose(pStream);
+ SetLastError(nError);
return false;
}
@@ -723,7 +757,7 @@ static bool BaseHttp_Read(
{
// Add range request to the HTTP headers
// http://www.clevercomponents.com/articles/article015/resuming.asp
- _stprintf(szRangeRequest, _T("Range: bytes=%u-%u"), (unsigned int)dwStartOffset, (unsigned int)dwEndOffset);
+ CascStrPrintf(szRangeRequest, _countof(szRangeRequest), _T("Range: bytes=%u-%u"), (unsigned int)dwStartOffset, (unsigned int)dwEndOffset);
HttpAddRequestHeaders(hRequest, szRangeRequest, 0xFFFFFFFF, HTTP_ADDREQ_FLAG_ADD_IF_NEW);
// Send the request to the server
@@ -963,9 +997,7 @@ static bool BlockStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset)
static void BlockStream_Close(TBlockStream * pStream)
{
// Free the data map, if any
- if(pStream->FileBitmap != NULL)
- CASC_FREE(pStream->FileBitmap);
- pStream->FileBitmap = NULL;
+ CASC_FREE(pStream->FileBitmap);
// Call the base class for closing the stream
pStream->BaseClose(pStream);
@@ -1026,7 +1058,7 @@ static TFileStream * AllocateFileStream(
if(pStream != NULL)
{
// Zero the entire structure
- memset(pStream, 0, StreamSize);
+ memset(pStream, 0, StreamSize + FileNameSize + sizeof(TCHAR));
pStream->pMaster = pMaster;
pStream->dwFlags = dwStreamFlags;
@@ -1344,7 +1376,10 @@ static TFileStream * FlatStream_Open(const TCHAR * szFileName, DWORD dwStreamFla
// Create new empty stream
pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags);
if(pStream == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
+ }
// Do we have a master stream?
if(pStream->pMaster != NULL)
@@ -1661,7 +1696,7 @@ static void PartStream_Close(TBlockStream * pStream)
// Make sure that the header is properly BSWAPed
BSWAP_ARRAY32_UNSIGNED(&PartHeader, sizeof(PART_FILE_HEADER));
- sprintf(PartHeader.GameBuildNumber, "%u", (unsigned int)pStream->BuildNumber);
+ CascStrPrintf(PartHeader.GameBuildNumber, _countof(PartHeader.GameBuildNumber), "%u", (unsigned int)pStream->BuildNumber);
// Write the part header
pStream->BaseWrite(pStream, &ByteOffset, &PartHeader, sizeof(PART_FILE_HEADER));
@@ -1755,7 +1790,6 @@ static bool PartStream_CreateMirror(TBlockStream * pStream)
return true;
}
-
static TFileStream * PartStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)
{
TBlockStream * pStream;
@@ -2193,9 +2227,7 @@ static void Block4Stream_Close(TBlockStream * pStream)
}
// Free the data map, if any
- if(pStream->FileBitmap != NULL)
- CASC_FREE(pStream->FileBitmap);
- pStream->FileBitmap = NULL;
+ CASC_FREE(pStream->FileBitmap);
// Do not call the BaseClose function,
// we closed all handles already
@@ -2246,7 +2278,7 @@ static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamF
for(int nSuffix = 0; nSuffix < 30; nSuffix++)
{
// Open the n-th file
- _stprintf(szNameBuff, _T("%s.%u"), pStream->szFileName, nSuffix);
+ CascStrPrintf(szNameBuff, (nNameLength + 4), _T("%s.%u"), pStream->szFileName, nSuffix);
if(!pStream->BaseOpen(pStream, szNameBuff, dwBaseFlags))
break;
@@ -2368,7 +2400,6 @@ TFileStream * FileStream_CreateFile(
// File create failed, delete the stream
CASC_FREE(pStream);
- pStream = NULL;
}
// Return the stream
diff --git a/dep/CascLib/src/common/FileStream.h b/dep/CascLib/src/common/FileStream.h
index 1c72619e393..a2b0c5b9f7d 100644
--- a/dep/CascLib/src/common/FileStream.h
+++ b/dep/CascLib/src/common/FileStream.h
@@ -28,6 +28,8 @@
#define STREAM_FLAG_READ_ONLY 0x00000100 // Stream is read only
#define STREAM_FLAG_WRITE_SHARE 0x00000200 // Allow write sharing when open for write
#define STREAM_FLAG_USE_BITMAP 0x00000400 // If the file has a file bitmap, load it and use it
+#define STREAM_FLAG_FILL_MISSING 0x00000800 // If less than expected was read from the file, fill the missing part with zeros
+#define STREAM_FLAG_USE_PORT_1119 0x00001000 // For HTTP streams, use port 1119
#define STREAM_OPTIONS_MASK 0x0000FF00 // Mask for stream options
#define STREAM_PROVIDERS_MASK 0x000000FF // Mask to get stream providers
@@ -101,6 +103,12 @@ typedef void (*BLOCK_SAVEMAP)(
struct TFileStream * pStream // Pointer to a block-oriented stream
);
+typedef void (WINAPI * STREAM_DOWNLOAD_CALLBACK)(
+ void * pvUserData,
+ ULONGLONG ByteOffset,
+ DWORD dwTotalBytes
+ );
+
//-----------------------------------------------------------------------------
// Local structures - partial file structure and bitmap footer
diff --git a/dep/CascLib/src/common/FileTree.cpp b/dep/CascLib/src/common/FileTree.cpp
new file mode 100644
index 00000000000..f753c25f263
--- /dev/null
+++ b/dep/CascLib/src/common/FileTree.cpp
@@ -0,0 +1,684 @@
+/*****************************************************************************/
+/* FileTree.cpp Copyright (c) Ladislav Zezula 2018 */
+/*---------------------------------------------------------------------------*/
+/* Common implementation of a file tree object for various ROOt file formats */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 29.05.18 1.00 Lad The first version of FileTree.cpp */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "../CascLib.h"
+#include "../CascCommon.h"
+
+//-----------------------------------------------------------------------------
+// Local defines
+
+#define START_ITEM_COUNT 0x4000
+
+inline DWORD GET_NODE_INT32(void * node, size_t offset)
+{
+ PDWORD PtrValue = (PDWORD)((LPBYTE)node + offset);
+
+ return PtrValue[0];
+}
+
+inline void SET_NODE_INT32(void * node, size_t offset, DWORD value)
+{
+ PDWORD PtrValue = (PDWORD)((LPBYTE)node + offset);
+
+ PtrValue[0] = value;
+}
+/*
+static bool CompareFileNode(void * pvObject, void * pvUserData)
+{
+ PCASC_COMPARE_CONTEXT pCtx = (PCASC_COMPARE_CONTEXT)pvUserData;
+ PCASC_FILE_TREE pFileTree = (PCASC_FILE_TREE)pCtx->pThis;
+ PCASC_FILE_NODE pFileNode = (PCASC_FILE_NODE)pvObject;
+ char szFullPath[MAX_PATH];
+
+ // First of all, the name hash must match
+ if(pFileNode->FileNameHash == pCtx->FileNameHash)
+ {
+ // Then also compare the full path name
+ pFileTree->PathAt(szFullPath, _countof(szFullPath), pFileNode);
+ if(!_stricmp(szFullPath, pCtx->szFileName))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+*/
+//-----------------------------------------------------------------------------
+// Protected functions
+
+// Inserts a new file node to the file tree.
+// If the pointer to file node array changes, the function also rebuilds all maps
+PCASC_FILE_NODE CASC_FILE_TREE::InsertNew(PCASC_CKEY_ENTRY pCKeyEntry)
+{
+ PCASC_FILE_NODE pFileNode;
+
+ // Create a brand new node
+ pFileNode = InsertNew();
+ if(pFileNode != NULL)
+ {
+ // Initialize the file node's CKeyEntry
+ pFileNode->pCKeyEntry = pCKeyEntry;
+
+ // Don't insert the node into any of the arrays here.
+ // That is the caller's responsibility
+ }
+
+ return pFileNode;
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::InsertNew()
+{
+ PCASC_FILE_NODE pFileNode;
+ void * SaveItemArray = NodeTable.ItemArray(); // We need to save the array pointers. If it changes, we must rebuild both maps
+
+ // Create a brand new node
+ pFileNode = (PCASC_FILE_NODE)NodeTable.Insert(1);
+ if(pFileNode != NULL)
+ {
+ // Initialize the file node
+ pFileNode->FileNameHash = 0;
+ pFileNode->pCKeyEntry = NULL;
+ pFileNode->Parent = 0;
+ pFileNode->NameIndex = 0;
+ pFileNode->NameLength = 0;
+ pFileNode->Flags = 0;
+
+ // We need to supply a file data id for the new entry, otherwise the rebuilding function
+ // will use the uninitialized one
+ SetExtras(pFileNode, CASC_INVALID_ID, CASC_INVALID_ID, CASC_INVALID_ID);
+
+ // If the array pointer changed or we are close to the size of the array, we need to rebuild the maps
+ if(NodeTable.ItemArray() != SaveItemArray || (NodeTable.ItemCount() * 3 / 2) > NameMap.HashTableSize())
+ {
+ // Rebuild both maps. Note that rebuilding also inserts all items to the maps, so no need to insert them here
+ if(!RebuildNameMaps())
+ {
+ pFileNode = NULL;
+ assert(false);
+ }
+ }
+ }
+
+ return pFileNode;
+}
+
+// Insert the node to the map of FileNameHash -> CASC_FILE_NODE
+bool CASC_FILE_TREE::InsertToHashTable(PCASC_FILE_NODE pFileNode)
+{
+ bool bResult = false;
+
+ // Insert the file node to the table
+ if(pFileNode->FileNameHash != 0)
+ bResult = NameMap.InsertObject(pFileNode, &pFileNode->FileNameHash);
+ return bResult;
+}
+
+// Inserts the file node to the array of file data ids
+bool CASC_FILE_TREE::InsertToIdTable(PCASC_FILE_NODE pFileNode)
+{
+ PCASC_FILE_NODE * RefElement;
+ DWORD FileDataId = CASC_INVALID_ID;
+
+ if(FileDataIds.IsInitialized())
+ {
+ // Retrieve the file data id
+ GetExtras(pFileNode, &FileDataId, NULL, NULL);
+ if(FileDataId != CASC_INVALID_ID)
+ {
+ // Sanity check
+ assert(FileDataId < 0x10000000);
+
+ // Insert the element to the array
+ RefElement = (PCASC_FILE_NODE *)FileDataIds.InsertAt(FileDataId);
+ if(RefElement != NULL)
+ {
+ RefElement[0] = pFileNode;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool CASC_FILE_TREE::SetNodePlainName(PCASC_FILE_NODE pFileNode, const char * szPlainName, const char * szPlainNameEnd)
+{
+ char * szNodeName;
+ size_t nLength = (szPlainNameEnd - szPlainName);
+
+ // Insert all chars to the name array
+ szNodeName = (char *)NameTable.Insert(nLength);
+ if(szNodeName != NULL)
+ {
+ // Copy the plain name to the node. Do not include the string terminator
+ memcpy(szNodeName, szPlainName, nLength);
+
+ // Supply the file name to the file node
+ pFileNode->NameIndex = (DWORD)NameTable.IndexOf(szNodeName);
+ pFileNode->NameLength = (USHORT)nLength;
+ return true;
+ }
+
+ return false;
+}
+
+bool CASC_FILE_TREE::SetKeyLength(DWORD aKeyLength)
+{
+ if(aKeyLength > MD5_HASH_SIZE)
+ return false;
+ KeyLength = aKeyLength;
+ return true;
+}
+
+DWORD CASC_FILE_TREE::GetNextFileDataId()
+{
+ if(FileDataIds.IsInitialized())
+ return (DWORD)(FileDataIds.ItemCount() + 1);
+ return CASC_INVALID_ID;
+}
+
+bool CASC_FILE_TREE::RebuildNameMaps()
+{
+ PCASC_FILE_NODE pFileNode;
+ size_t nMaxItems = NodeTable.ItemCountMax();
+
+ // Free the map of "FullName -> CASC_FILE_NODE"
+ NameMap.Free();
+
+ // Create new map map "FullName -> CASC_FILE_NODE"
+ if(NameMap.Create(nMaxItems, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_NODE, FileNameHash)) != ERROR_SUCCESS)
+ return false;
+
+ // Reset the entire array, but keep the buffer allocated
+ FileDataIds.Reset();
+
+ // Parse all items and insert them to the map
+ for(size_t i = 0; i < NodeTable.ItemCount(); i++)
+ {
+ // Retrieve the n-th object
+ pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(i);
+ if(pFileNode != NULL)
+ {
+ // Insert it to the map "FileNameHash -> CASC_FILE_NODE"
+ if(pFileNode->FileNameHash != 0)
+ InsertToHashTable(pFileNode);
+
+ // Insert it to the array "FileDataId -> CASC_FILE_NODE"
+ if(FileDataIds.IsInitialized())
+ InsertToIdTable(pFileNode);
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+int CASC_FILE_TREE::Create(DWORD Flags)
+{
+ PCASC_FILE_NODE pRootNode;
+ size_t FileNodeSize = FIELD_OFFSET(CASC_FILE_NODE, ExtraValues);
+ int nError;
+
+ // Initialize the file tree
+ memset(this, 0, sizeof(CASC_FILE_TREE));
+ KeyLength = MD5_HASH_SIZE;
+
+ // Shall we use the data ID in the tree node?
+ if(Flags & FTREE_FLAG_USE_DATA_ID)
+ {
+ // Set the offset of the file data id in the entry
+ FileDataIdOffset = FileNodeSize;
+ FileNodeSize += sizeof(DWORD);
+
+ // Create the array for FileDataId -> CASC_FILE_NODE
+ nError = FileDataIds.Create<PCASC_FILE_NODE>(START_ITEM_COUNT);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ }
+
+ // Shall we use the locale ID in the tree node?
+ if(Flags & FTREE_FLAG_USE_LOCALE_FLAGS)
+ {
+ LocaleFlagsOffset = FileNodeSize;
+ FileNodeSize += sizeof(DWORD);
+ }
+
+ if(Flags & FTREE_FLAG_USE_CONTENT_FLAGS)
+ {
+ ContentFlagsOffset = FileNodeSize;
+ FileNodeSize += sizeof(DWORD);
+ }
+
+ // Align the file node size to 8 bytes
+ FileNodeSize = ALIGN_TO_SIZE(FileNodeSize, 8);
+
+ // Initialize the dynamic array
+ nError = NodeTable.Create(FileNodeSize, START_ITEM_COUNT);
+ if(nError == ERROR_SUCCESS)
+ {
+ // Create the dynamic array that will hold the node names
+ nError = NameTable.Create<char>(START_ITEM_COUNT);
+ if(nError == ERROR_SUCCESS)
+ {
+ // Insert the first "root" node, without name
+ pRootNode = (PCASC_FILE_NODE)NodeTable.Insert(1);
+ if(pRootNode != NULL)
+ {
+ // Initialize the node
+ memset(pRootNode, 0, NodeTable.ItemSize());
+ pRootNode->Parent = CASC_INVALID_INDEX;
+ pRootNode->NameIndex = CASC_INVALID_INDEX;
+ pRootNode->Flags = CFN_FLAG_FOLDER;
+ SetExtras(pRootNode, CASC_INVALID_ID, CASC_INVALID_ID, CASC_INVALID_ID);
+ }
+ }
+ }
+
+ // Create both maps
+ if(!RebuildNameMaps())
+ nError = ERROR_NOT_ENOUGH_MEMORY;
+ return nError;
+}
+
+void CASC_FILE_TREE::Free()
+{
+ // Free both arrays
+ NodeTable.Free();
+ NameTable.Free();
+ FileDataIds.Free();
+
+ // Free the name map
+ NameMap.Free();
+
+ // Zero the object
+ memset(this, 0, sizeof(CASC_FILE_TREE));
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const char * szFileName, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
+{
+ CASC_COMPARE_CONTEXT CmpCtx;
+ PCASC_FILE_NODE pFileNode;
+
+ // Sanity checks
+ assert(szFileName != NULL && szFileName[0] != 0);
+ assert(pCKeyEntry != NULL);
+
+ // Calculate the file name hash
+ CmpCtx.FileNameHash = CalcFileNameHash(szFileName);
+// CmpCtx.szFileName = szFileName;
+// CmpCtx.pThis = this;
+
+ // Do nothing if the file name is there already.
+// pFileNode = (PCASC_FILE_NODE)NameMap.FindObjectEx(CompareFileNode, &CmpCtx);
+ pFileNode = (PCASC_FILE_NODE)NameMap.FindObject(&CmpCtx.FileNameHash);
+ if(pFileNode == NULL)
+ {
+ // Insert new item
+ pFileNode = InsertNew(pCKeyEntry);
+ if(pFileNode != NULL)
+ {
+ // Supply the name hash
+ pFileNode->FileNameHash = CmpCtx.FileNameHash;
+
+ // Set the file data id and the extra values
+ SetExtras(pFileNode, FileDataId, LocaleFlags, ContentFlags);
+
+ // Insert the file node to the hash map
+ InsertToHashTable(pFileNode);
+
+ // Also make sure that it's in the file data id table, if the table is initialized
+ InsertToIdTable(pFileNode);
+
+ // Set the file name of the new file node. This also increments the number of references
+ SetNodeFileName(pFileNode, szFileName);
+
+ // If we created a new node, we need to increment the reference count
+ assert(pCKeyEntry->RefCount != 0xFFFF);
+ pCKeyEntry->RefCount++;
+ }
+ }
+
+ return pFileNode;
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::InsertByHash(PCASC_CKEY_ENTRY pCKeyEntry, ULONGLONG FileNameHash, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
+{
+ PCASC_FILE_NODE pFileNode;
+
+ // Sanity checks
+ assert(FileDataIds.IsInitialized());
+ assert(FileDataId != CASC_INVALID_ID);
+ assert(FileNameHash != 0);
+ assert(pCKeyEntry != NULL);
+
+ // Insert the node to the tree by file data id
+ pFileNode = InsertById(pCKeyEntry, FileDataId, LocaleFlags, ContentFlags);
+ if(pFileNode != NULL)
+ {
+ // Supply the name hash
+ pFileNode->FileNameHash = FileNameHash;
+
+ // Insert the file node to the hash map
+ InsertToHashTable(pFileNode);
+ }
+
+ return pFileNode;
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::InsertById(PCASC_CKEY_ENTRY pCKeyEntry, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
+{
+ PCASC_FILE_NODE pFileNode;
+
+ // Sanity checks
+ assert(FileDataIds.IsInitialized());
+ assert(FileDataId != CASC_INVALID_ID);
+ assert(pCKeyEntry != NULL);
+
+ // Check whether the file data id exists in the array of file data ids
+ if((pFileNode = FindById(FileDataId)) == NULL)
+ {
+ // Insert the new file node
+ pFileNode = InsertNew(pCKeyEntry);
+ if(pFileNode != NULL)
+ {
+ // Set the file data id and the extra values
+ SetExtras(pFileNode, FileDataId, LocaleFlags, ContentFlags);
+
+ // Insert the file node to the FileDataId array
+ InsertToIdTable(pFileNode);
+
+ // Increment the number of references
+ pCKeyEntry->RefCount++;
+ }
+ }
+
+ // Return the new or old node
+ return pFileNode;
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::ItemAt(size_t nItemIndex)
+{
+ return (PCASC_FILE_NODE)NodeTable.ItemAt(nItemIndex);
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::PathAt(char * szBuffer, size_t cchBuffer, size_t nItemIndex)
+{
+ PCASC_FILE_NODE pFileNode = NULL;
+
+ // If we have FileDataId, then we need to enumerate the files by FileDataId
+ if(FileDataIds.IsInitialized())
+ pFileNode = *(PCASC_FILE_NODE *)FileDataIds.ItemAt(nItemIndex);
+ else
+ pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nItemIndex);
+
+ // Construct the entire path
+ PathAt(szBuffer, cchBuffer, pFileNode);
+ return pFileNode;
+}
+
+size_t CASC_FILE_TREE::PathAt(char * szBuffer, size_t cchBuffer, PCASC_FILE_NODE pFileNode)
+{
+ PCASC_FILE_NODE pParentNode;
+ const char * szNamePtr;
+ char * szSaveBuffer = szBuffer;
+ char * szBufferEnd = szBuffer + cchBuffer - 1;
+
+ if(pFileNode != NULL && pFileNode->Parent != CASC_INVALID_INDEX)
+ {
+ // Copy all parents
+ pParentNode = (PCASC_FILE_NODE)NodeTable.ItemAt(pFileNode->Parent);
+ if(pParentNode != NULL)
+ {
+ // Query the parent and move the buffer
+ szBuffer = szBuffer + PathAt(szBuffer, cchBuffer, pParentNode);
+ }
+
+ // Retrieve the node name
+ szNamePtr = (const char *)NameTable.ItemAt(pFileNode->NameIndex);
+
+ // Check whether we have enough space
+ if((szBuffer + pFileNode->NameLength) < szBufferEnd)
+ {
+ // Copy the path part
+ memcpy(szBuffer, szNamePtr, pFileNode->NameLength);
+ szBuffer += pFileNode->NameLength;
+
+ // Append backslash
+ if((pFileNode->Flags & CFN_FLAG_FOLDER) && ((szBuffer + 1) < szBufferEnd))
+ {
+ *szBuffer++ = (pFileNode->Flags & CFN_FLAG_MOUNT_POINT) ? ':' : '\\';
+ }
+ }
+ }
+
+ // Terminate buffer with zero
+ szBuffer[0] = 0;
+
+ // Return length of the copied string
+ return (szBuffer - szSaveBuffer);
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::Find(const char * szFullPath, DWORD FileDataId, PCASC_FIND_DATA pFindData)
+{
+ PCASC_FILE_NODE pFileNode = NULL;
+ ULONGLONG FileNameHash;
+
+ // Can we search by FileDataId?
+ if(FileDataIds.IsInitialized() && (FileDataId != CASC_INVALID_ID || IsFileDataIdName(szFullPath, FileDataId)))
+ {
+ pFileNode = FindById(FileDataId);
+ }
+ else
+ {
+ if(szFullPath != NULL && szFullPath[0] != 0)
+ {
+ FileNameHash = CalcFileNameHash(szFullPath);
+ pFileNode = (PCASC_FILE_NODE)NameMap.FindObject(&FileNameHash);
+ }
+ }
+
+ // Did we find anything?
+ if(pFileNode != NULL && pFindData != NULL)
+ {
+ GetExtras(pFileNode, &pFindData->dwFileDataId, &pFindData->dwLocaleFlags, &pFindData->dwContentFlags);
+ pFindData->bCanOpenByName = (pFileNode->FileNameHash != 0);
+ pFindData->bCanOpenByDataId = (FileDataIdOffset != 0);
+ }
+
+ return pFileNode;
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::Find(PCASC_CKEY_ENTRY pCKeyEntry)
+{
+ PCASC_FILE_NODE pFileNode;
+
+ for(size_t i = 0; i < NodeTable.ItemCount(); i++)
+ {
+ pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(i);
+ if((pFileNode->Flags & (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT)) == 0)
+ {
+ if(pFileNode->pCKeyEntry == pCKeyEntry)
+ return pFileNode;
+ }
+ }
+
+ return NULL;
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::Find(ULONGLONG FileNameHash)
+{
+ return (PCASC_FILE_NODE)NameMap.FindObject(&FileNameHash);
+}
+
+PCASC_FILE_NODE CASC_FILE_TREE::FindById(DWORD FileDataId)
+{
+ PCASC_FILE_NODE * RefElement;
+ PCASC_FILE_NODE pFileNode = NULL;
+
+ if(FileDataId != CASC_INVALID_ID && FileDataIds.IsInitialized())
+ {
+ // Insert the element to the array
+ RefElement = (PCASC_FILE_NODE *)FileDataIds.ItemAt(FileDataId);
+ if(RefElement != NULL)
+ {
+ pFileNode = RefElement[0];
+ }
+ }
+
+ return pFileNode;
+}
+
+bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szFileName)
+{
+ ULONGLONG FileNameHash = 0;
+ PCASC_FILE_NODE pFolderNode = NULL;
+ const char * szNodeBegin = szFileName;
+ char szPathBuffer[MAX_PATH+1];
+ size_t nFileNode = NodeTable.IndexOf(pFileNode);
+ size_t i;
+ DWORD Parent = 0;
+
+ // Sanity checks
+ assert(szFileName != NULL && szFileName[0] != 0);
+ assert(pFileNode->pCKeyEntry != NULL);
+
+ // Traverse the entire path. For each subfolder, we insert an appropriate fake entry
+ for(i = 0; szFileName[i] != 0; i++)
+ {
+ char chOneChar = szFileName[i];
+
+ // Is there a path separator?
+ // Note: Warcraft III paths may contain "mount points".
+ // Example: "frFR-War3Local.mpq:Maps/FrozenThrone/Campaign/NightElfX06Interlude.w3x:war3map.j"
+ if(chOneChar == '\\' || chOneChar == '/' || chOneChar == ':')
+ {
+ // Calculate hash of the file name up to the end of the node name
+ FileNameHash = CalcNormNameHash(szPathBuffer, i);
+
+ // If the entry is not there yet, create new one
+ if((pFolderNode = Find(FileNameHash)) == NULL)
+ {
+ // Insert new entry to the tree
+ pFolderNode = InsertNew();
+ if(pFolderNode == NULL)
+ return false;
+
+ // Populate the file entry
+ pFolderNode->FileNameHash = FileNameHash;
+ pFolderNode->Parent = Parent;
+ pFolderNode->Flags |= (chOneChar == ':') ? CFN_FLAG_MOUNT_POINT : 0;
+ pFolderNode->Flags |= CFN_FLAG_FOLDER;
+
+ // Set the node sub name to the node
+ SetNodePlainName(pFolderNode, szNodeBegin, szFileName + i);
+
+ // Insert the entry to the name map
+ InsertToHashTable(pFolderNode);
+ }
+
+ // Move the parent to the current node
+ Parent = (DWORD)NodeTable.IndexOf(pFolderNode);
+
+ // Move the begin of the node after the separator
+ szNodeBegin = szFileName + i + 1;
+ }
+
+ // Copy the next character, even if it was slash/backslash before
+ szPathBuffer[i] = AsciiToUpperTable_BkSlash[chOneChar];
+ }
+
+ // If anything left, this is gonna be our node name
+ if(szNodeBegin < szFileName + i)
+ {
+ // We need to reset the file node pointer, as the file node table might have changed
+ pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nFileNode);
+
+ SetNodePlainName(pFileNode, szNodeBegin, szFileName + i);
+ pFileNode->Parent = Parent;
+ }
+ return true;
+}
+
+size_t CASC_FILE_TREE::GetMaxFileIndex()
+{
+ if(FileDataIds.IsInitialized())
+ {
+ return FileDataIds.ItemCount();
+ }
+ else
+ {
+ return NodeTable.ItemCount();
+ }
+}
+
+size_t CASC_FILE_TREE::GetCount()
+{
+ return NodeTable.ItemCount();
+}
+
+size_t CASC_FILE_TREE::IndexOf(PCASC_FILE_NODE pFileNode)
+{
+ return NodeTable.IndexOf(pFileNode);
+}
+
+void CASC_FILE_TREE::GetExtras(PCASC_FILE_NODE pFileNode, PDWORD PtrFileDataId, PDWORD PtrLocaleFlags, PDWORD PtrContentFlags)
+{
+ DWORD FileDataId = CASC_INVALID_ID;
+ DWORD LocaleFlags = CASC_INVALID_ID;
+ DWORD ContentFlags = CASC_INVALID_ID;
+
+ // Retrieve the data ID, if supported
+ if(PtrFileDataId != NULL)
+ {
+ if(FileDataIdOffset != 0)
+ FileDataId = GET_NODE_INT32(pFileNode, FileDataIdOffset);
+ PtrFileDataId[0] = FileDataId;
+ }
+
+ // Retrieve the locale ID, if supported
+ if(PtrLocaleFlags != NULL)
+ {
+ if(LocaleFlagsOffset != 0)
+ LocaleFlags = GET_NODE_INT32(pFileNode, LocaleFlagsOffset);
+ PtrLocaleFlags[0] = LocaleFlags;
+ }
+
+ if(PtrContentFlags != NULL)
+ {
+ if(ContentFlagsOffset != 0)
+ ContentFlags = GET_NODE_INT32(pFileNode, ContentFlagsOffset);
+ PtrContentFlags[0] = ContentFlags;
+ }
+}
+
+void CASC_FILE_TREE::SetExtras(PCASC_FILE_NODE pFileNode, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
+{
+ // Set the file data ID, if supported
+ if(FileDataIdOffset != 0)
+ {
+ SET_NODE_INT32(pFileNode, FileDataIdOffset, FileDataId);
+ }
+
+ // Set the locale ID, if supported
+ if(LocaleFlagsOffset != 0)
+ {
+ SET_NODE_INT32(pFileNode, LocaleFlagsOffset, LocaleFlags);
+ }
+
+ // Set the locale ID, if supported
+ if(ContentFlagsOffset != 0)
+ {
+ SET_NODE_INT32(pFileNode, ContentFlagsOffset, ContentFlags);
+ }
+}
diff --git a/dep/CascLib/src/common/FileTree.h b/dep/CascLib/src/common/FileTree.h
new file mode 100644
index 00000000000..904da6c4c46
--- /dev/null
+++ b/dep/CascLib/src/common/FileTree.h
@@ -0,0 +1,116 @@
+/*****************************************************************************/
+/* FileTree.h Copyright (c) Ladislav Zezula 2018 */
+/*---------------------------------------------------------------------------*/
+/* Common implementation of a file tree object for various ROOt file formats */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 29.05.18 1.00 Lad The first version of FileTree.h */
+/*****************************************************************************/
+
+#ifndef __FILETREE_H__
+#define __FILETREE_H__
+
+//-----------------------------------------------------------------------------
+// Structures
+
+#define FTREE_FLAG_USE_DATA_ID 0x0001 // The FILE_NODE also contains file data ID
+#define FTREE_FLAG_USE_LOCALE_FLAGS 0x0002 // The FILE_NODE also contains file locale flags
+#define FTREE_FLAG_USE_CONTENT_FLAGS 0x0004 // The FILE_NODE also contains content flags
+
+#define CFN_FLAG_FOLDER 0x0001 // This item is a folder
+#define CFN_FLAG_MOUNT_POINT 0x0002 // This item is a mount point.
+
+// Common structure for holding a single folder/file node
+typedef struct _CASC_FILE_NODE
+{
+ ULONGLONG FileNameHash; // Jenkins hash of the normalized file name (uppercase, backslashes)
+ PCASC_CKEY_ENTRY pCKeyEntry; // Pointer to the CKey entry.
+ DWORD Parent; // The index of a parent directory. If CASC_INVALID_INDEX, then this is the root item
+ DWORD NameIndex; // Index of the node name. If CASC_INVALID_INDEX, then this node has no name
+ USHORT NameLength; // Length of the node name (without the zero terminator)
+ USHORT Flags; // See CFE_FLAG_XXX
+
+ DWORD ExtraValues[4]; // FileDataId: Only if FTREE_FLAG_USE_DATA_ID specified at create
+ // LocaleFlags: Only if FTREE_FLAG_USE_LOCALE_FLAGS specified at create
+ // ContentFlags: Only if FTREE_FLAG_USE_CONTENT_FLAGS specified at create
+} CASC_FILE_NODE, *PCASC_FILE_NODE;
+
+// Common structure for comparing a file node
+typedef struct _CASC_COMPARE_CONTEXT
+{
+ ULONGLONG FileNameHash;
+ const char * szFileName;
+ void * pThis;
+} CASC_COMPARE_CONTEXT, *PCASC_COMPARE_CONTEXT;
+
+// Main structure for the file tree
+class CASC_FILE_TREE
+{
+ public:
+
+ // Initializes/destroys the entire tree
+ int Create(DWORD Flags = 0);
+ void Free();
+
+ // Inserts a new node to the tree; either with name or nameless
+ PCASC_FILE_NODE InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const char * szFileName, DWORD FileDataId = CASC_INVALID_ID, DWORD LocaleFlags = CASC_INVALID_ID, DWORD ContentFlags = CASC_INVALID_ID);
+ PCASC_FILE_NODE InsertByHash(PCASC_CKEY_ENTRY pCKeyEntry, ULONGLONG FileNameHash, DWORD FileDataId, DWORD LocaleFlags = CASC_INVALID_ID, DWORD ContentFlags = CASC_INVALID_ID);
+ PCASC_FILE_NODE InsertById(PCASC_CKEY_ENTRY pCKeyEntry, DWORD FileDataId, DWORD LocaleFlags = CASC_INVALID_ID, DWORD ContentFlags = CASC_INVALID_ID);
+
+ // Returns an item at the given index. The PathAt also builds the full path of the node
+ PCASC_FILE_NODE ItemAt(size_t nItemIndex);
+ PCASC_FILE_NODE PathAt(char * szBuffer, size_t cchBuffer, size_t nItemIndex);
+ size_t PathAt(char * szBuffer, size_t cchBuffer, PCASC_FILE_NODE pFileNode);
+
+ // Finds a file using its full path, FileDataId or CKey/EKey
+ PCASC_FILE_NODE Find(const char * szFullPath, DWORD FileDataId, struct _CASC_FIND_DATA * pFindData);
+ PCASC_FILE_NODE Find(PCASC_CKEY_ENTRY pCKeyEntry);
+ PCASC_FILE_NODE Find(ULONGLONG FileNameHash);
+ PCASC_FILE_NODE FindById(DWORD FileDataId);
+
+ // Assigns a file name to the node
+ bool SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szFileName);
+
+ // Returns the number of items in the tree
+ size_t GetMaxFileIndex();
+ size_t GetCount();
+
+ // Returns the index of an item in the tree
+ size_t IndexOf(PCASC_FILE_NODE pFileNode);
+
+ // Retrieves the extra values from the node (if supported)
+ void GetExtras(PCASC_FILE_NODE pFileNode, PDWORD PtrFileDataId, PDWORD PtrLocaleFlags, PDWORD PtrContentFlags);
+ void SetExtras(PCASC_FILE_NODE pFileNode, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags);
+
+ // Change the length of the key
+ bool SetKeyLength(DWORD KeyLength);
+
+ // Retrieve the maximum FileDataId ever inserted
+ DWORD GetNextFileDataId();
+
+ protected:
+
+ PCASC_FILE_NODE InsertNew(PCASC_CKEY_ENTRY pCKeyEntry);
+ PCASC_FILE_NODE InsertNew();
+ bool InsertToHashTable(PCASC_FILE_NODE pFileNode);
+ bool InsertToIdTable(PCASC_FILE_NODE pFileNode);
+
+ bool SetNodePlainName(PCASC_FILE_NODE pFileNode, const char * szPlainName, const char * szPlainNameEnd);
+ bool RebuildNameMaps();
+
+ CASC_ARRAY NodeTable; // Dynamic array that holds all CASC_FILE_NODEs
+ CASC_ARRAY NameTable; // Dynamic array that holds all node names
+
+ CASC_ARRAY FileDataIds; // Dynamic array that maps FileDataId -> CASC_FILE_NODE
+ CASC_MAP NameMap; // Map of FileNameHash -> CASC_FILE_NODE
+
+ size_t FileDataIdOffset; // If nonzero, this is the offset of the "FileDataId" field in the CASC_FILE_NODE
+ size_t LocaleFlagsOffset; // If nonzero, this is the offset of the "LocaleFlags" field in the CASC_FILE_NODE
+ size_t ContentFlagsOffset; // If nonzero, this is the offset of the "ContentFlags" field in the CASC_FILE_NODE
+ DWORD KeyLength; // Actual length of the key supported by the root handler
+};
+
+typedef CASC_FILE_TREE * PCASC_FILE_TREE;
+
+#endif // __FILETREE_H__
diff --git a/dep/CascLib/src/common/ListFile.cpp b/dep/CascLib/src/common/ListFile.cpp
index dc9a88bf44c..351ea226188 100644
--- a/dep/CascLib/src/common/ListFile.cpp
+++ b/dep/CascLib/src/common/ListFile.cpp
@@ -15,11 +15,14 @@
//-----------------------------------------------------------------------------
// Listfile cache structure
+#define LISTFILE_FLAG_USES_FILEDATAID 0x0001 // A new CSV format, containing FileDataId; FullFileName
+
typedef struct _LISTFILE_CACHE
{
- char * pBegin; // The begin of the listfile cache
- char * pPos; // Current position in the cache
- char * pEnd; // The last character in the file cache
+ char * pBegin; // The begin of the listfile cache
+ char * pPos; // Current position in the cache
+ char * pEnd; // The last character in the file cache
+ DWORD Flags;
// Followed by the cache (variable length)
@@ -28,7 +31,7 @@ typedef struct _LISTFILE_CACHE
//-----------------------------------------------------------------------------
// Creating the listfile cache for the given amount of data
-static PLISTFILE_CACHE CreateListFileCache(DWORD dwFileSize)
+static PLISTFILE_CACHE ListFile_CreateCache(DWORD dwFileSize)
{
PLISTFILE_CACHE pCache;
@@ -40,12 +43,81 @@ static PLISTFILE_CACHE CreateListFileCache(DWORD dwFileSize)
pCache->pBegin =
pCache->pPos = (char *)(pCache + 1);
pCache->pEnd = pCache->pBegin + dwFileSize;
+ pCache->Flags = 0;
}
// Return the cache
return pCache;
}
+static char * ListFile_SkipSpaces(PLISTFILE_CACHE pCache)
+{
+ // Skip newlines, spaces, tabs and another non-printable stuff
+ while(pCache->pPos < pCache->pEnd && pCache->pPos[0] <= 0x20)
+ pCache->pPos++;
+
+ // Remember the begin of the line
+ return pCache->pPos;
+}
+
+static void ListFile_CheckFormat(PLISTFILE_CACHE pCache)
+{
+ // Only if the listfile is greatger than 2 MB
+ if((pCache->pEnd - pCache->pBegin) > 0x100000)
+ {
+ char * szPtr = pCache->pBegin;
+ size_t nDigitCount = 0;
+
+ // Calculate the amount of digits
+ while(nDigitCount <= 20 && '0' <= szPtr[nDigitCount] && szPtr[nDigitCount] <= '9')
+ nDigitCount++;
+
+ // There must be a semicolon after
+ if(nDigitCount <= 10 && szPtr[nDigitCount] == ';')
+ {
+ pCache->Flags |= LISTFILE_FLAG_USES_FILEDATAID;
+ }
+ }
+}
+
+static int ListFile_GetFileDataId(PLISTFILE_CACHE pCache, PDWORD PtrFileDataId)
+{
+ char * szLineBegin = ListFile_SkipSpaces(pCache);
+ char * szLineEnd;
+ DWORD dwNewInt32 = 0;
+ DWORD dwInt32 = 0;
+
+ // Set the limit for loading the number
+ szLineEnd = CASCLIB_MIN((szLineBegin + 20), pCache->pEnd);
+
+ // Extract decimal digits from the string
+ while(szLineBegin < szLineEnd && '0' <= szLineBegin[0] && szLineBegin[0] <= '9')
+ {
+ // Check integer overflow
+ dwNewInt32 = (dwInt32 * 10) + (szLineBegin[0] - '0');
+ if(dwNewInt32 < dwInt32)
+ return ERROR_BAD_FORMAT;
+
+ dwInt32 = dwNewInt32;
+ szLineBegin++;
+ }
+
+ // There must still be some space
+ if(szLineBegin < szLineEnd)
+ {
+ // There must be a semicolon after the decimal integer
+ // The decimal integer must be smaller than 10 MB (files)
+ if(szLineBegin[0] != ';' || dwInt32 >= 0xA00000)
+ return ERROR_BAD_FORMAT;
+
+ pCache->pPos = szLineBegin + 1;
+ PtrFileDataId[0] = dwInt32;
+ return ERROR_SUCCESS;
+ }
+
+ return ERROR_NO_MORE_FILES;
+}
+
//-----------------------------------------------------------------------------
// Functions for parsing an external listfile
@@ -65,13 +137,16 @@ void * ListFile_OpenExternal(const TCHAR * szListFile)
{
// Create the in-memory cache for the entire listfile
// The listfile does not have any data loaded yet
- pCache = CreateListFileCache((DWORD)FileSize);
+ pCache = ListFile_CreateCache((DWORD)FileSize);
if(pCache != NULL)
{
- if(!FileStream_Read(pStream, NULL, pCache->pBegin, (DWORD)FileSize))
+ if(FileStream_Read(pStream, NULL, pCache->pBegin, (DWORD)FileSize))
{
- ListFile_Free(pCache);
- pCache = NULL;
+ ListFile_CheckFormat(pCache);
+ }
+ else
+ {
+ CASC_FREE(pCache);
}
}
}
@@ -89,7 +164,7 @@ void * ListFile_FromBuffer(LPBYTE pbBuffer, DWORD cbBuffer)
// Create the in-memory cache for the entire listfile
// The listfile does not have any data loaded yet
- pCache = CreateListFileCache(cbBuffer);
+ pCache = ListFile_CreateCache(cbBuffer);
if(pCache != NULL)
memcpy(pCache->pBegin, pbBuffer, cbBuffer);
@@ -105,7 +180,7 @@ bool ListFile_VerifyMD5(void * pvListFile, LPBYTE pbHashMD5)
assert(pCache->pPos == pCache->pBegin);
// Verify the MD5 hash for the entire block
- return VerifyDataBlockHash(pCache->pBegin, (DWORD)(pCache->pEnd - pCache->pBegin), pbHashMD5);
+ return CascVerifyDataBlockHash(pCache->pBegin, (DWORD)(pCache->pEnd - pCache->pBegin), pbHashMD5);
}
size_t ListFile_GetNextLine(void * pvListFile, const char ** pszLineBegin, const char ** pszLineEnd)
@@ -116,17 +191,15 @@ size_t ListFile_GetNextLine(void * pvListFile, const char ** pszLineBegin, const
char * szLineEnd;
// Skip newlines, spaces, tabs and another non-printable stuff
- while(pCache->pPos < pCache->pEnd && pCache->pPos[0] <= 0x20)
- pCache->pPos++;
-
// Remember the begin of the line
- szLineBegin = pCache->pPos;
+ szLineBegin = ListFile_SkipSpaces(pCache);
// Copy the remaining characters
while(pCache->pPos < pCache->pEnd)
{
// If we have found a newline, stop loading
- if(pCache->pPos[0] == 0x0D || pCache->pPos[0] == 0x0A)
+ // Note: the 0x85 char came from Overwatch build 24919
+ if(pCache->pPos[0] == 0x0A || pCache->pPos[0] == 0x0D || pCache->pPos[0] == 0x85)
break;
// Blizzard listfiles can also contain information about patch:
@@ -169,14 +242,28 @@ size_t ListFile_GetNextLine(void * pvListFile, char * szBuffer, size_t nMaxChars
return nLength;
}
-size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer, size_t nMaxChars)
+size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PDWORD PtrFileDataId)
{
+ PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile;
size_t nLength = 0;
int nError = ERROR_SUCCESS;
// Check for parameters
for(;;)
{
+ DWORD FileDataId = CASC_INVALID_ID;
+
+ // If this is a CSV-format listfile, we need to extract the FileDataId
+ // Lines that contain bogus data, invalid numbers or too big values will be skipped
+ if(pCache->Flags & LISTFILE_FLAG_USES_FILEDATAID)
+ {
+ nError = ListFile_GetFileDataId(pCache, &FileDataId);
+ if(nError == ERROR_NO_MORE_FILES)
+ break;
+ if(nError != ERROR_SUCCESS || FileDataId == CASC_INVALID_ID)
+ continue;
+ }
+
// Read the (next) line
nLength = ListFile_GetNextLine(pvListFile, szBuffer, nMaxChars);
if(nLength == 0)
@@ -185,12 +272,9 @@ size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer,
break;
}
- // If some mask entered, check it
- if(CheckWildCard(szBuffer, szMask))
- {
- nError = ERROR_SUCCESS;
- break;
- }
+ // Give the file data id and return true
+ PtrFileDataId[0] = FileDataId;
+ return nLength;
}
if(nError != ERROR_SUCCESS)
@@ -198,163 +282,22 @@ size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer,
return nLength;
}
-void ListFile_Free(void * pvListFile)
+LPBYTE ListFile_GetData(void * pvListFile, PDWORD PtrDataSize)
{
- if(pvListFile != NULL)
- {
- CASC_FREE(pvListFile);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Functions for creating a listfile map
-
-#define LISTMAP_INITIAL 0x100000
-
-static PLISTFILE_MAP ListMap_Create()
-{
- PLISTFILE_MAP pListMap;
- size_t cbToAllocate;
-
- // Create buffer for the listfile
- // Note that because the listfile is quite big and CASC_REALLOC
- // is a costly operation, we want to have as few reallocs as possible.
- cbToAllocate = sizeof(LISTFILE_MAP) + LISTMAP_INITIAL;
- pListMap = (PLISTFILE_MAP)CASC_ALLOC(BYTE, cbToAllocate);
- if(pListMap != NULL)
- {
- // Fill the listfile buffer
- memset(pListMap, 0, sizeof(LISTFILE_MAP));
- pListMap->cbBufferMax = LISTMAP_INITIAL;
- }
-
- return pListMap;
-}
-
-static PLISTFILE_MAP ListMap_InsertName(PLISTFILE_MAP pListMap, const char * szFileName, size_t nLength)
-{
- PLISTFILE_ENTRY pListEntry;
- size_t cbToAllocate;
- size_t cbEntrySize;
-
- // Make sure there is enough space in the list map
- cbEntrySize = sizeof(LISTFILE_ENTRY) + nLength;
- cbEntrySize = ALIGN_TO_SIZE(cbEntrySize, 8);
- if((pListMap->cbBuffer + cbEntrySize) > pListMap->cbBufferMax)
- {
- cbToAllocate = sizeof(LISTFILE_MAP) + (pListMap->cbBufferMax * 3) / 2;
- pListMap = (PLISTFILE_MAP)CASC_REALLOC(BYTE, pListMap, cbToAllocate);
- if(pListMap == NULL)
- return NULL;
-
- pListMap->cbBufferMax = (pListMap->cbBufferMax * 3) / 2;
- }
-
- // Get the pointer to the first entry
- pListEntry = (PLISTFILE_ENTRY)((LPBYTE)(pListMap + 1) + pListMap->cbBuffer);
- pListEntry->FileNameHash = CalcFileNameHash(szFileName);
- pListEntry->cbEntrySize = (DWORD)cbEntrySize;
-
- // Copy the file name to the entry
- memcpy(pListEntry->szFileName, szFileName, nLength);
- pListEntry->szFileName[nLength] = 0;
-
- // Move the next entry
- pListMap->cbBuffer += cbEntrySize;
- pListMap->nEntries++;
- return pListMap;
-}
-
-static PLISTFILE_MAP ListMap_Finish(PLISTFILE_MAP pListMap)
-{
- PLISTFILE_ENTRY pListEntry;
- PCASC_MAP pMap;
- LPBYTE pbEntry;
-
- // Sanity check
- assert(pListMap->pNameMap == NULL);
-
- // Create the map
- pListMap->pNameMap = pMap = Map_Create((DWORD)pListMap->nEntries, sizeof(ULONGLONG), 0);
- if(pListMap->pNameMap == NULL)
- {
- ListFile_FreeMap(pListMap);
- return NULL;
- }
-
- // Fill the map
- pbEntry = (LPBYTE)(pListMap + 1);
- for(size_t i = 0; i < pListMap->nEntries; i++)
- {
- // Get the listfile entry
- pListEntry = (PLISTFILE_ENTRY)pbEntry;
- pbEntry += pListEntry->cbEntrySize;
-
- // Insert the entry to the map
- Map_InsertObject(pMap, pListEntry, &pListEntry->FileNameHash);
- }
-
- return pListMap;
-}
-
-PLISTFILE_MAP ListFile_CreateMap(const TCHAR * szListFile)
-{
- PLISTFILE_MAP pListMap = NULL;
- void * pvListFile;
- char szFileName[MAX_PATH+1];
- size_t nLength;
+ PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile;
+ LPBYTE pbData = NULL;
+ DWORD cbData = 0;
- // Only if the listfile name has been given
- if(szListFile != NULL)
+ // Get data from the list file cache
+ if (pvListFile != NULL)
{
- // Create map for the listfile
- pListMap = ListMap_Create();
- if(pListMap != NULL)
- {
- // Open the external listfile
- pvListFile = ListFile_OpenExternal(szListFile);
- if(pvListFile != NULL)
- {
- // Go through the entire listfile and insert each name to the map
- while((nLength = ListFile_GetNext(pvListFile, "*", szFileName, MAX_PATH)) != 0)
- {
- // Insert the file name to the map
- pListMap = ListMap_InsertName(pListMap, szFileName, nLength);
- if(pListMap == NULL)
- break;
- }
-
- if(pListMap == NULL)
- {
- // Finish the listfile map
- pListMap = ListMap_Finish(pListMap);
- }
-
- // Free the listfile
- ListFile_Free(pvListFile);
- }
- }
+ pbData = (LPBYTE)pCache->pBegin;
+ cbData = (DWORD)(pCache->pEnd - pCache->pBegin);
}
- // Return the created map
- return pListMap;
-}
-
-const char * ListFile_FindName(PLISTFILE_MAP pListMap, ULONGLONG FileNameHash)
-{
- PLISTFILE_ENTRY pListEntry = NULL;
-
- if(pListMap != NULL)
- pListEntry = (PLISTFILE_ENTRY)Map_FindObject(pListMap->pNameMap, &FileNameHash, NULL);
- return (pListEntry != NULL) ? pListEntry->szFileName : "";
+ // Give the data to the caller
+ if (PtrDataSize != NULL)
+ PtrDataSize[0] = cbData;
+ return pbData;
}
-void ListFile_FreeMap(PLISTFILE_MAP pListMap)
-{
- if(pListMap != NULL)
- {
- if(pListMap->pNameMap != NULL)
- Map_Free(pListMap->pNameMap);
- CASC_FREE(pListMap);
- }
-}
diff --git a/dep/CascLib/src/common/ListFile.h b/dep/CascLib/src/common/ListFile.h
index 84bec3ed751..1333b52c2b5 100644
--- a/dep/CascLib/src/common/ListFile.h
+++ b/dep/CascLib/src/common/ListFile.h
@@ -12,28 +12,6 @@
#define __LISTFILE_H__
//-----------------------------------------------------------------------------
-// Structures
-
-typedef struct _LISTFILE_ENTRY
-{
- ULONGLONG FileNameHash; // Hash of the file name
- DWORD cbEntrySize; // Length of this entry, in bytes
- char szFileName[1]; // File name, aligned to 8-byte boundary
-
-} LISTFILE_ENTRY, *PLISTFILE_ENTRY;
-
-typedef struct _LISTFILE_MAP
-{
- PCASC_MAP pNameMap; // Map of hash-to-name
- size_t cbBufferMax; // Total size of the buffer, in bytes
- size_t cbBuffer; // Current size of the buffer, in bytes
- size_t nEntries; // Number of entries
-
- // First LISTFILE_ENTRY starts here
-
-} LISTFILE_MAP, *PLISTFILE_MAP;
-
-//-----------------------------------------------------------------------------
// Functions for parsing an external listfile
void * ListFile_OpenExternal(const TCHAR * szListFile);
@@ -41,14 +19,7 @@ void * ListFile_FromBuffer(LPBYTE pbBuffer, DWORD cbBuffer);
bool ListFile_VerifyMD5(void * pvListFile, LPBYTE pbHashMD5);
size_t ListFile_GetNextLine(void * pvListFile, const char ** pszLineBegin, const char ** pszLineEnd);
size_t ListFile_GetNextLine(void * pvListFile, char * szBuffer, size_t nMaxChars);
-size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer, size_t nMaxChars);
-void ListFile_Free(void * pvListFile);
-
-//-----------------------------------------------------------------------------
-// Functions for creating a listfile map
-
-PLISTFILE_MAP ListFile_CreateMap(const TCHAR * szListFile);
-const char * ListFile_FindName(PLISTFILE_MAP pListMap, ULONGLONG FileNameHash);
-void ListFile_FreeMap(PLISTFILE_MAP pListMap);
+size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PDWORD PtrFileDataId);
+LPBYTE ListFile_GetData(void * pvListFile, PDWORD PtrDataSize);
#endif // __LISTFILE_H__
diff --git a/dep/CascLib/src/common/Map.cpp b/dep/CascLib/src/common/Map.cpp
deleted file mode 100644
index aa1e2a2ada1..00000000000
--- a/dep/CascLib/src/common/Map.cpp
+++ /dev/null
@@ -1,287 +0,0 @@
-/*****************************************************************************/
-/* Map.cpp Copyright (c) Ladislav Zezula 2014 */
-/*---------------------------------------------------------------------------*/
-/* Implementation of map for CascLib */
-/*---------------------------------------------------------------------------*/
-/* Date Ver Who Comment */
-/* -------- ---- --- ------- */
-/* 10.06.14 1.00 Lad The first version of Map.cpp */
-/*****************************************************************************/
-
-#define __CASCLIB_SELF__
-#include "../CascLib.h"
-#include "../CascCommon.h"
-
-//-----------------------------------------------------------------------------
-// Local functions
-
-// Returns the extension, right after "."
-static const char * String_GetExtension(const char * szString)
-{
- const char * szExtension = strrchr(szString, '.');
- return (szExtension != NULL) ? szExtension + 1 : NULL;
-}
-
-static DWORD CalcHashIndex_Key(PCASC_MAP pMap, void * pvKey)
-{
- LPBYTE pbKey = (LPBYTE)pvKey;
- DWORD dwHash = 0x7EEE7EEE;
-
- // Construct the hash from the first 8 digits
- dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[0];
- dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[1];
- dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[2];
- dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[3];
- dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[4];
- dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[5];
- dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[6];
- dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[7];
-
- // Return the hash limited by the table size
- return (dwHash % pMap->TableSize);
-}
-
-static DWORD CalcHashIndex_String(PCASC_MAP pMap, const char * szString, const char * szStringEnd)
-{
- LPBYTE pbKeyEnd = (LPBYTE)szStringEnd;
- LPBYTE pbKey = (LPBYTE)szString;
- DWORD dwHash = 0x7EEE7EEE;
-
- // Hash the string itself
- while(pbKey < pbKeyEnd)
- {
- dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ AsciiToUpperTable_BkSlash[pbKey[0]];
- pbKey++;
- }
-
- // Return the hash limited by the table size
- return (dwHash % pMap->TableSize);
-}
-
-static bool CompareObject_Key(PCASC_MAP pMap, void * pvObject, void * pvKey)
-{
- LPBYTE pbObjectKey = (LPBYTE)pvObject + pMap->KeyOffset;
-
- return (memcmp(pbObjectKey, pvKey, pMap->KeyLength) == 0);
-}
-
-static bool CompareObject_String(
- PCASC_MAP pMap,
- const char * szExistingString,
- const char * szString,
- const char * szStringEnd)
-{
- // Keep compiler happy
- CASCLIB_UNUSED(pMap);
-
- // Compare the whole part, case insensitive
- while(szString < szStringEnd)
- {
- if(AsciiToUpperTable_BkSlash[*szExistingString] != AsciiToUpperTable_BkSlash[*szString])
- return false;
-
- szExistingString++;
- szString++;
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Public functions
-
-PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwKeyOffset)
-{
- PCASC_MAP pMap;
- size_t cbToAllocate;
- size_t dwTableSize;
-
- // Calculate the size of the table
- dwTableSize = (dwMaxItems * 3 / 2) | 0x01;
-
- // Allocate new map for the objects
- cbToAllocate = sizeof(CASC_MAP) + (dwTableSize * sizeof(void *));
- pMap = (PCASC_MAP)CASC_ALLOC(LPBYTE, cbToAllocate);
- if(pMap != NULL)
- {
- memset(pMap, 0, cbToAllocate);
- pMap->KeyLength = dwKeyLength;
- pMap->TableSize = dwTableSize;
- pMap->KeyOffset = dwKeyOffset;
- }
-
- // Return the allocated map
- return pMap;
-}
-
-size_t Map_EnumObjects(PCASC_MAP pMap, void **ppvArray)
-{
- size_t nIndex = 0;
-
- // Verify pointer to the map
- if(pMap != NULL && ppvArray != NULL)
- {
- // Enumerate all items in main table
- for(size_t i = 0; i < pMap->TableSize; i++)
- {
- // Is that cell valid?
- if(pMap->HashTable[i] != NULL)
- {
- ppvArray[nIndex++] = pMap->HashTable[i];
- }
- }
-
- return pMap->ItemCount;
- }
-
- return 0;
-}
-
-void * Map_FindObject(PCASC_MAP pMap, void * pvKey, PDWORD PtrIndex)
-{
- void * pvObject;
- DWORD dwHashIndex;
-
- // Verify pointer to the map
- if(pMap != NULL)
- {
- // Construct the main index
- dwHashIndex = CalcHashIndex_Key(pMap, pvKey);
- while(pMap->HashTable[dwHashIndex] != NULL)
- {
- // Get the pointer at that position
- pvObject = pMap->HashTable[dwHashIndex];
-
- // Compare the hash
- if(CompareObject_Key(pMap, pvObject, pvKey))
- {
- if(PtrIndex != NULL)
- PtrIndex[0] = dwHashIndex;
- return pvObject;
- }
-
- // Move to the next entry
- dwHashIndex = (dwHashIndex + 1) % pMap->TableSize;
- }
- }
-
- // Not found, sorry
- return NULL;
-}
-
-bool Map_InsertObject(PCASC_MAP pMap, void * pvNewObject, void * pvKey)
-{
- void * pvExistingObject;
- DWORD dwHashIndex;
-
- // Verify pointer to the map
- if(pMap != NULL)
- {
- // Limit check
- if((pMap->ItemCount + 1) >= pMap->TableSize)
- return false;
-
- // Construct the hash index
- dwHashIndex = CalcHashIndex_Key(pMap, pvKey);
- while(pMap->HashTable[dwHashIndex] != NULL)
- {
- // Get the pointer at that position
- pvExistingObject = pMap->HashTable[dwHashIndex];
-
- // Check if hash being inserted conflicts with an existing hash
- if(CompareObject_Key(pMap, pvExistingObject, pvKey))
- return false;
-
- // Move to the next entry
- dwHashIndex = (dwHashIndex + 1) % pMap->TableSize;
- }
-
- // Insert at that position
- pMap->HashTable[dwHashIndex] = pvNewObject;
- pMap->ItemCount++;
- return true;
- }
-
- // Failed
- return false;
-}
-
-bool Map_InsertString(PCASC_MAP pMap, const char * szString, bool bCutExtension)
-{
- const char * szExistingString;
- const char * szStringEnd = NULL;
- DWORD dwHashIndex;
-
- // Verify pointer to the map
- if(pMap != NULL)
- {
- // Limit check
- if((pMap->ItemCount + 1) >= pMap->TableSize)
- return false;
-
- // Retrieve the length of the string without extension
- if(bCutExtension)
- szStringEnd = String_GetExtension(szString);
- if(szStringEnd == NULL)
- szStringEnd = szString + strlen(szString);
-
- // Construct the hash index
- dwHashIndex = CalcHashIndex_String(pMap, szString, szStringEnd);
- while(pMap->HashTable[dwHashIndex] != NULL)
- {
- // Get the pointer at that position
- szExistingString = (const char *)pMap->HashTable[dwHashIndex];
-
- // Check if hash being inserted conflicts with an existing hash
- if(CompareObject_String(pMap, szExistingString, szString, szStringEnd))
- return false;
-
- // Move to the next entry
- dwHashIndex = (dwHashIndex + 1) % pMap->TableSize;
- }
-
- // Insert at that position
- pMap->HashTable[dwHashIndex] = (void *)szString;
- pMap->ItemCount++;
- return true;
- }
-
- // Failed
- return false;
-}
-
-const char * Map_FindString(PCASC_MAP pMap, const char * szString, const char * szStringEnd)
-{
- const char * szExistingString;
- DWORD dwHashIndex;
-
- // Verify pointer to the map
- if(pMap != NULL)
- {
- // Construct the main index
- dwHashIndex = CalcHashIndex_String(pMap, szString, szStringEnd);
- while(pMap->HashTable[dwHashIndex] != NULL)
- {
- // Get the pointer at that position
- szExistingString = (const char *)pMap->HashTable[dwHashIndex];
-
- // Compare the hash
- if(CompareObject_String(pMap, szExistingString, szString, szStringEnd))
- return szExistingString;
-
- // Move to the next entry
- dwHashIndex = (dwHashIndex + 1) % pMap->TableSize;
- }
- }
-
- // Not found, sorry
- return NULL;
-}
-
-void Map_Free(PCASC_MAP pMap)
-{
- if(pMap != NULL)
- {
- CASC_FREE(pMap);
- }
-}
diff --git a/dep/CascLib/src/common/Map.h b/dep/CascLib/src/common/Map.h
index 2b590578105..13799eb0528 100644
--- a/dep/CascLib/src/common/Map.h
+++ b/dep/CascLib/src/common/Map.h
@@ -8,35 +8,355 @@
/* 10.06.14 1.00 Lad The first version of Map.h */
/*****************************************************************************/
-#ifndef __HASHTOPTR_H__
-#define __HASHTOPTR_H__
+#ifndef __CASC_MAP_H__
+#define __CASC_MAP_H__
//-----------------------------------------------------------------------------
// Structures
-#define KEY_LENGTH_STRING 0xFFFFFFFF // Pass this to Map_Create as dwKeyLength when you want map of string->object
+#define MIN_HASH_TABLE_SIZE 0x00000100 // The smallest size of the hash table.
+#define MAX_HASH_TABLE_SIZE 0x00800000 // The largest size of the hash table. Should be enough for any game.
-typedef struct _CASC_MAP
+typedef int (*PFNCOMPAREFUNC)(const void * pvObjectKey, const void * pvKey, size_t nKeyLength);
+typedef DWORD (*PFNHASHFUNC)(void * pvKey, size_t nKeyLength);
+
+typedef enum _KEY_TYPE
+{
+ KeyIsHash, // Use when the key is already a hash. If specified, the map will not hash the key
+ KeyIsArbitrary, // The key is an arbitrary array of bytes. The map will hash it to get a map index
+ KeyIsString // Use when the key is a string
+} KEY_TYPE, *PKEY_TYPE;
+
+#define KEY_LENGTH_STRING 0xFFFFFFFF
+
+//-----------------------------------------------------------------------------
+// Hashing functions
+
+// Used when the key is a hash already - no need to hash it again
+inline DWORD CalcHashValue_Hash(void * pvKey, size_t /* nKeyLength */)
+{
+ // Get the hash directly as value
+ return ConvertBytesToInteger_4((LPBYTE)pvKey);
+}
+
+// Calculates hash value from a key
+inline DWORD CalcHashValue_Key(void * pvKey, size_t nKeyLength)
{
- size_t TableSize;
- size_t ItemCount; // Number of items in the map
- size_t KeyOffset; // How far is the hash from the begin of the structure (in bytes)
- size_t KeyLength; // Length of the hash key
- void * HashTable[1]; // Pointer table
+ LPBYTE pbKey = (LPBYTE)pvKey;
+ DWORD dwHash = 0x7EEE7EEE;
-} CASC_MAP, *PCASC_MAP;
+ // Construct the hash from the key
+ for(DWORD i = 0; i < nKeyLength; i++)
+ dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ pbKey[i];
-typedef bool (*MAP_COMPARE)(PCASC_MAP pMap, void * pvObject, void * pvKey);
+ // Return the hash limited by the table size
+ return dwHash;
+}
+
+// Calculates hash value from a string
+inline DWORD CalcHashValue_String(const char * szString, const char * szStringEnd)
+{
+ LPBYTE pbKeyEnd = (LPBYTE)szStringEnd;
+ LPBYTE pbKey = (LPBYTE)szString;
+ DWORD dwHash = 0x7EEE7EEE;
+
+ // Hash the string itself
+ while(pbKey < pbKeyEnd)
+ {
+ dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ AsciiToUpperTable_BkSlash[pbKey[0]];
+ pbKey++;
+ }
+
+ // Return the hash limited by the table size
+ return dwHash;
+}
//-----------------------------------------------------------------------------
-// Functions
+// Map implementation
+
+class CASC_MAP
+{
+ public:
+
+ CASC_MAP()
+ {
+ PfnCalcHashValue = NULL;
+ m_HashTable = NULL;
+ m_HashTableSize = 0;
+ m_ItemCount = 0;
+ m_KeyOffset = 0;
+ m_KeyLength = 0;
+ m_bKeyIsHash = false;
+ }
+
+ ~CASC_MAP()
+ {
+ Free();
+ }
+
+ int Create(size_t MaxItems, size_t KeyLength, size_t KeyOffset, KEY_TYPE KeyType = KeyIsHash)
+ {
+ // Set the class variables
+ m_KeyLength = CASCLIB_MAX(KeyLength, 8);
+ m_KeyOffset = KeyOffset;
+ m_ItemCount = 0;
+
+ // Setup the hashing function
+ switch(KeyType)
+ {
+ case KeyIsHash:
+ PfnCalcHashValue = CalcHashValue_Hash;
+ break;
+
+ case KeyIsArbitrary:
+ PfnCalcHashValue = CalcHashValue_Key;
+ break;
+
+ case KeyIsString:
+ PfnCalcHashValue = NULL;
+ break;
+
+ default:
+ assert(false);
+ return ERROR_NOT_SUPPORTED;
+ }
+
+ // Calculate the hash table size. Take 133% of the item count and round it up to the next power of two
+ // This will make the hash table indexes somewhat more resilient against count changes and will make
+ // e.g. file order in the file tree more stable.
+ m_HashTableSize = GetNearestPowerOfTwo(MaxItems * 4 / 3);
+ if(m_HashTableSize == 0)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Allocate new map for the objects
+ m_HashTable = (void **)CASC_ALLOC(void *, m_HashTableSize);
+ if(m_HashTable == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Initialize the map object
+ memset(m_HashTable, 0, m_HashTableSize * sizeof(void *));
+ return ERROR_SUCCESS;
+ }
+
+ void * FindObject(void * pvKey, PDWORD PtrIndex = NULL)
+ {
+ void * pvObject;
+ DWORD dwHashIndex;
+
+ // Verify pointer to the map
+ if(m_HashTable != NULL)
+ {
+ // Construct the hash index
+ dwHashIndex = HashToIndex(PfnCalcHashValue(pvKey, m_KeyLength));
+
+ // Search the hash table
+ while((pvObject = m_HashTable[dwHashIndex]) != NULL)
+ {
+ // Compare the hash
+ if(CompareObject_Key(pvObject, pvKey))
+ {
+ if(PtrIndex != NULL)
+ PtrIndex[0] = dwHashIndex;
+ return pvObject;
+ }
+
+ // Move to the next entry
+ dwHashIndex = HashToIndex(dwHashIndex + 1);
+ }
+ }
+
+ // Not found, sorry
+ return NULL;
+ }
+
+ bool InsertObject(void * pvNewObject, void * pvKey)
+ {
+ void * pvExistingObject;
+ DWORD dwHashIndex;
+
+ // Verify pointer to the map
+ if(m_HashTable != NULL)
+ {
+ // Limit check
+ if((m_ItemCount + 1) >= m_HashTableSize)
+ return false;
+
+ // Construct the hash index
+ dwHashIndex = HashToIndex(PfnCalcHashValue(pvKey, m_KeyLength));
+
+ // Search the hash table
+ while((pvExistingObject = m_HashTable[dwHashIndex]) != NULL)
+ {
+ // Check if hash being inserted conflicts with an existing hash
+ if(CompareObject_Key(pvExistingObject, pvKey))
+ return false;
+
+ // Move to the next entry
+ dwHashIndex = HashToIndex(dwHashIndex + 1);
+ }
+
+ // Insert at that position
+ m_HashTable[dwHashIndex] = pvNewObject;
+ m_ItemCount++;
+ return true;
+ }
+
+ // Failed
+ return false;
+ }
+
+ const char * FindString(const char * szString, const char * szStringEnd)
+ {
+ const char * szExistingString;
+ DWORD dwHashIndex;
+
+ // Verify pointer to the map
+ if(m_HashTable != NULL)
+ {
+ // Construct the main index
+ dwHashIndex = HashToIndex(CalcHashValue_String(szString, szStringEnd));
+
+ // Search the hash table
+ while((szExistingString = (const char *)m_HashTable[dwHashIndex]) != NULL)
+ {
+ // Compare the hash
+ if(CompareObject_String(szExistingString, szString, szStringEnd))
+ return szExistingString;
+
+ // Move to the next entry
+ dwHashIndex = HashToIndex(dwHashIndex + 1);
+ }
+ }
+
+ // Not found, sorry
+ return NULL;
+ }
+
+ bool InsertString(const char * szString, bool bCutExtension)
+ {
+ const char * szExistingString;
+ const char * szStringEnd = NULL;
+ DWORD dwHashIndex;
+
+ // Verify pointer to the map
+ if(m_HashTable != NULL)
+ {
+ // Limit check
+ if((m_ItemCount + 1) >= m_HashTableSize)
+ return false;
+
+ // Retrieve the length of the string without extension
+ if(bCutExtension)
+ szStringEnd = GetFileExtension(szString);
+ else
+ szStringEnd = szString + strlen(szString);
+
+ // Construct the hash index
+ dwHashIndex = HashToIndex(CalcHashValue_String(szString, szStringEnd));
+
+ // Search the hash table
+ while((szExistingString = (const char *)m_HashTable[dwHashIndex]) != NULL)
+ {
+ // Check if hash being inserted conflicts with an existing hash
+ if(CompareObject_String(szExistingString, szString, szStringEnd))
+ return false;
+
+ // Move to the next entry
+ dwHashIndex = HashToIndex(dwHashIndex + 1);
+ }
+
+ // Insert at that position
+ m_HashTable[dwHashIndex] = (void *)szString;
+ m_ItemCount++;
+ return true;
+ }
+
+ // Failed
+ return false;
+ }
+
+ void * ItemAt(size_t nIndex)
+ {
+ assert(nIndex < m_HashTableSize);
+ return m_HashTable[nIndex];
+ }
+
+ size_t HashTableSize()
+ {
+ return m_HashTableSize;
+ }
+
+ size_t ItemCount()
+ {
+ return m_ItemCount;
+ }
+
+ bool IsInitialized()
+ {
+ return (m_HashTable && m_HashTableSize);
+ }
+
+ void Free()
+ {
+ PfnCalcHashValue = NULL;
+ CASC_FREE(m_HashTable);
+ m_HashTableSize = 0;
+ }
+
+ protected:
+
+ DWORD HashToIndex(DWORD HashValue)
+ {
+ return HashValue & (m_HashTableSize - 1);
+ }
+
+ bool CompareObject_Key(void * pvObject, void * pvKey)
+ {
+ LPBYTE pbObjectKey = (LPBYTE)pvObject + m_KeyOffset;
+ return (memcmp(pbObjectKey, pvKey, m_KeyLength) == 0);
+ }
+
+ bool CompareObject_String(const char * szExistingString, const char * szString, const char * szStringEnd)
+ {
+ // Compare the whole part, case insensitive
+ while(szString < szStringEnd)
+ {
+ if(AsciiToUpperTable_BkSlash[*szExistingString] != AsciiToUpperTable_BkSlash[*szString])
+ return false;
+
+ szExistingString++;
+ szString++;
+ }
+
+ return true;
+ }
+
+ size_t GetNearestPowerOfTwo(size_t MaxItems)
+ {
+ size_t PowerOfTwo;
+
+ // Round the hash table size up to the nearest power of two
+ for(PowerOfTwo = MIN_HASH_TABLE_SIZE; PowerOfTwo < MAX_HASH_TABLE_SIZE; PowerOfTwo <<= 1)
+ {
+ if(PowerOfTwo > MaxItems)
+ {
+ return PowerOfTwo;
+ }
+ }
+
+ // If the hash table is too big, we cannot create the map
+ assert(false);
+ return 0;
+ }
-PCASC_MAP Map_Create(DWORD dwMaxItems, DWORD dwKeyLength, DWORD dwKeyOffset);
-size_t Map_EnumObjects(PCASC_MAP pMap, void **ppvArray);
-void * Map_FindObject(PCASC_MAP pMap, void * pvKey, PDWORD PtrIndex);
-bool Map_InsertObject(PCASC_MAP pMap, void * pvNewObject, void * pvKey);
-const char * Map_FindString(PCASC_MAP pMap, const char * szString, const char * szStringEnd);
-bool Map_InsertString(PCASC_MAP pMap, const char * szString, bool bCutExtension);
-void Map_Free(PCASC_MAP pMap);
+ PFNHASHFUNC PfnCalcHashValue;
+ void ** m_HashTable; // Hash table
+ size_t m_HashTableSize; // Size of the hash table, in entries. Always a power of two.
+ size_t m_ItemCount; // Number of objects in the map
+ size_t m_KeyOffset; // How far is the hash from the begin of the objects (in bytes)
+ size_t m_KeyLength; // Length of the hash key, in bytes
+ bool m_bKeyIsHash; // If set, then it means that the key is a hash of some sort.
+ // Will improve performance, as we will not hash a hash :-)
+};
-#endif // __HASHTOPTR_H__
+#endif // __CASC_MAP_H__
diff --git a/dep/CascLib/src/common/RootHandler.cpp b/dep/CascLib/src/common/RootHandler.cpp
index 4baeb41a421..89b693f55cb 100644
--- a/dep/CascLib/src/common/RootHandler.cpp
+++ b/dep/CascLib/src/common/RootHandler.cpp
@@ -13,76 +13,105 @@
#include "../CascCommon.h"
//-----------------------------------------------------------------------------
-// Common support
+// Constructor and destructor - TFileTreeRoot
-int RootHandler_Insert(TRootHandler * pRootHandler, const char * szFileName, LPBYTE pbEncodingKey)
+TFileTreeRoot::TFileTreeRoot(DWORD FileTreeFlags) : TRootHandler()
{
- if(pRootHandler == NULL || pRootHandler->Insert == NULL || pbEncodingKey == NULL)
- return ERROR_NOT_SUPPORTED;
+ // Initialize the file tree
+ FileTree.Create(FileTreeFlags);
+}
- return pRootHandler->Insert(pRootHandler, szFileName, pbEncodingKey);
+TFileTreeRoot::~TFileTreeRoot()
+{
+ // Free the file tree
+ FileTree.Free();
+ dwFeatures = 0;
}
-LPBYTE RootHandler_Search(TRootHandler * pRootHandler, struct _TCascSearch * pSearch, PDWORD PtrFileSize, PDWORD PtrLocaleFlags, PDWORD PtrFileDataId)
+//-----------------------------------------------------------------------------
+// Virtual functions - TFileTreeRoot
+
+int TFileTreeRoot::Insert(
+ const char * szFileName,
+ PCASC_CKEY_ENTRY pCKeyEntry)
{
- // Check if the root structure is valid at all
- if(pRootHandler == NULL)
- return NULL;
+ PCASC_FILE_NODE pFileNode;
- return pRootHandler->Search(pRootHandler, pSearch, PtrFileSize, PtrLocaleFlags, PtrFileDataId);
+ pFileNode = FileTree.InsertByName(pCKeyEntry, szFileName, FileTree.GetNextFileDataId());
+ return (pFileNode != NULL) ? ERROR_SUCCESS : ERROR_CAN_NOT_COMPLETE;
}
-void RootHandler_EndSearch(TRootHandler * pRootHandler, struct _TCascSearch * pSearch)
+PCASC_CKEY_ENTRY TFileTreeRoot::GetFile(TCascStorage * /* hs */, const char * szFileName)
{
- // Check if the root structure is valid at all
- if(pRootHandler != NULL)
- {
- pRootHandler->EndSearch(pRootHandler, pSearch);
- }
+ PCASC_FILE_NODE pFileNode;
+ ULONGLONG FileNameHash = CalcFileNameHash(szFileName);
+
+ pFileNode = FileTree.Find(FileNameHash);
+ return (pFileNode != NULL) ? pFileNode->pCKeyEntry : NULL;
}
-LPBYTE RootHandler_GetKey(TRootHandler * pRootHandler, const char * szFileName)
+PCASC_CKEY_ENTRY TFileTreeRoot::GetFile(TCascStorage * /* hs */, DWORD FileDataId)
{
- // Check if the root structure is valid at all
- if(pRootHandler == NULL)
- return NULL;
+ PCASC_FILE_NODE pFileNode;
- return pRootHandler->GetKey(pRootHandler, szFileName);
+ pFileNode = FileTree.FindById(FileDataId);
+ return (pFileNode != NULL) ? pFileNode->pCKeyEntry : NULL;
}
-void RootHandler_Dump(TCascStorage * hs, LPBYTE pbRootHandler, DWORD cbRootHandler, const TCHAR * szNameFormat, const TCHAR * szListFile, int nDumpLevel)
+PCASC_CKEY_ENTRY TFileTreeRoot::Search(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
{
- TDumpContext * dc;
+ PCASC_FILE_NODE pFileNode;
+ size_t nMaxFileIndex = FileTree.GetMaxFileIndex();
- // Only if the ROOT provider suports the dump option
- if(hs->pRootHandler != NULL && hs->pRootHandler->Dump != NULL)
+ // Are we still inside the root directory range?
+ while(pSearch->nFileIndex < nMaxFileIndex)
{
- // Create the dump file
- dc = CreateDumpContext(hs, szNameFormat);
- if(dc != NULL)
+ //BREAKIF(pSearch->nFileIndex >= 2823765);
+
+ // Retrieve the file item
+ pFileNode = FileTree.PathAt(pFindData->szFileName, MAX_PATH, pSearch->nFileIndex++);
+ if(pFileNode != NULL)
{
- // Dump the content and close the file
- hs->pRootHandler->Dump(hs, dc, pbRootHandler, cbRootHandler, szListFile, nDumpLevel);
- dump_close(dc);
+ // Ignore folders and mount points
+ if(!(pFileNode->Flags & CFN_FLAG_FOLDER))
+ {
+ // Check the wildcard
+ if (CascCheckWildCard(pFindData->szFileName, pSearch->szMask))
+ {
+ // Retrieve the extra values (FileDataId, file size and locale flags)
+ FileTree.GetExtras(pFileNode, &pFindData->dwFileDataId, &pFindData->dwLocaleFlags, &pFindData->dwContentFlags);
+
+ // Supply the bCanOpenByDataId variable
+ pFindData->bCanOpenByName = (pFileNode->FileNameHash != 0);
+ pFindData->bCanOpenByDataId = (pFindData->dwFileDataId != CASC_INVALID_ID);
+
+ // Return the found CKey entry
+ return pFileNode->pCKeyEntry;
+ }
+ }
}
}
+
+ // No more entries
+ return NULL;
}
-void RootHandler_Close(TRootHandler * pRootHandler)
+bool TFileTreeRoot::GetInfo(PCASC_CKEY_ENTRY pCKeyEntry, PCASC_FILE_FULL_INFO pFileInfo)
{
- // Check if the root structure is allocated at all
- if(pRootHandler != NULL)
+ PCASC_FILE_NODE pFileNode;
+
+ // Can't do much if the root key is NULL
+ if(pCKeyEntry != NULL)
{
- pRootHandler->Close(pRootHandler);
+ pFileNode = FileTree.Find(pCKeyEntry);
+ if(pFileNode != NULL)
+ {
+ FileTree.GetExtras(pFileNode, &pFileInfo->FileDataId, &pFileInfo->LocaleFlags, &pFileInfo->ContentFlags);
+ pFileInfo->FileNameHash = pFileNode->FileNameHash;
+ return true;
+ }
}
-}
-
-DWORD RootHandler_GetFileId(TRootHandler * pRootHandler, const char * szFileName)
-{
- // Check if the root structure is valid at all
- if(pRootHandler == NULL)
- return 0;
- return pRootHandler->GetFileId(pRootHandler, szFileName);
+ return false;
}
diff --git a/dep/CascLib/src/common/RootHandler.h b/dep/CascLib/src/common/RootHandler.h
index d1b66e501d8..4e56650b034 100644
--- a/dep/CascLib/src/common/RootHandler.h
+++ b/dep/CascLib/src/common/RootHandler.h
@@ -15,82 +15,97 @@
// Defines
#define CASC_MNDX_ROOT_SIGNATURE 0x58444E4D // 'MNDX'
+#define CASC_TVFS_ROOT_SIGNATURE 0x53465654 // 'TVFS'
#define CASC_DIABLO3_ROOT_SIGNATURE 0x8007D0C4
-#define CASC_OVERWATCH_ROOT_SIGNATURE 0x35444D23 // '#MD5'
+#define CASC_WOW82_ROOT_SIGNATURE 0x4D465354 // 'TSFM', WoW since 8.2
-#define ROOT_FLAG_HAS_NAMES 0x00000001 // The root file contains file names
-
-#define DUMP_LEVEL_ROOT_FILE 1 // Dump root file
-#define DUMP_LEVEL_ENCODING_FILE 2 // Dump root file + encoding file
-#define DUMP_LEVEL_INDEX_ENTRIES 3 // Dump root file + encoding file + index entries
+#define DUMP_LEVEL_ROOT_FILE 1 // Dump root file
+#define DUMP_LEVEL_ENCODING_FILE 2 // Dump root file + encoding file
+#define DUMP_LEVEL_INDEX_ENTRIES 3 // Dump root file + encoding file + index entries
//-----------------------------------------------------------------------------
-// Root file function prototypes
-
-typedef int (*ROOT_INSERT)(
- struct TRootHandler * pRootHandler, // Pointer to an initialized root handler
- const char * szFileName, // Pointer to the file name
- LPBYTE pbEncodingKey // Pointer to the encoding key of the file name
- );
-
-typedef LPBYTE (*ROOT_SEARCH)(
- struct TRootHandler * pRootHandler, // Pointer to an initialized root handler
- struct _TCascSearch * pSearch, // Pointer to the initialized search structure
- PDWORD PtrFileSize, // Pointer to receive file size (optional)
- PDWORD PtrLocaleFlags, // Pointer to receive locale flags (optional)
- PDWORD PtrFileDataId // Pointer to FileDataID (optional)
- );
-
-typedef void (*ROOT_ENDSEARCH)(
- struct TRootHandler * pRootHandler, // Pointer to an initialized root handler
- struct _TCascSearch * pSearch // Pointer to the initialized search structure
- );
-
-typedef LPBYTE (*ROOT_GETKEY)(
- struct TRootHandler * pRootHandler, // Pointer to an initialized root handler
- const char * szFileName // Pointer to the name of a file
- );
-
-typedef void (*ROOT_DUMP)(
- struct _TCascStorage * hs, // Pointer to the open storage
- TDumpContext * dc, // Opened dump context
- LPBYTE pbRootHandler, // Pointer to the loaded ROOT file
- DWORD cbRootHandler, // Length of the loaded ROOT file
- const TCHAR * szListFile,
- int nDumpLevel
- );
-
-typedef void (*ROOT_CLOSE)(
- struct TRootHandler * pRootHandler // Pointer to an initialized root handler
- );
-
-typedef DWORD(*ROOT_GETFILEID)(
-struct TRootHandler * pRootHandler, // Pointer to an initialized root handler
- const char * szFileName // Pointer to the name of a file
- );
+// Class for generic root handler
struct TRootHandler
{
- ROOT_INSERT Insert; // Inserts an existing file name
- ROOT_SEARCH Search; // Performs the root file search
- ROOT_ENDSEARCH EndSearch; // Performs cleanup after searching
- ROOT_GETKEY GetKey; // Retrieves encoding key for a file name
- ROOT_DUMP Dump;
- ROOT_CLOSE Close; // Closing the root file
- ROOT_GETFILEID GetFileId; // Returns File Id for a given Filename
-
- DWORD dwRootFlags; // Root flags - see the ROOT_FLAG_XXX
+ public:
+
+ TRootHandler()
+ {
+ dwFeatures = 0;
+ }
+
+ virtual ~TRootHandler()
+ {}
+
+ // Inserts new file name to the root handler
+ // szFileName - Pointer to the file name
+ // pCKeyEntry - Pointer to the CASC_CKEY_ENTRY for the file
+ virtual int Insert(const char * /* szFileName */, PCASC_CKEY_ENTRY /* pCKeyEntry */)
+ {
+ return ERROR_NOT_SUPPORTED;
+ }
+
+ // Searches the file by file name
+ // hs - Pointer to the storage structure
+ // szFileName - Pointer to the file name
+ virtual PCASC_CKEY_ENTRY GetFile(struct TCascStorage * /* hs */, const char * /* szFileName */)
+ {
+ return NULL;
+ }
+
+ // Searches the file by file data id
+ // hs - Pointer to the storage structure
+ // FileDataId - File data id
+ virtual PCASC_CKEY_ENTRY GetFile(struct TCascStorage * /* hs */, DWORD /* FileDataId */)
+ {
+ return NULL;
+ }
+
+ // Performs find-next-file operation
+ // pSearch - Pointer to the initialized search structure
+ // pFindData - Pointer to output structure that will contain the information
+ virtual PCASC_CKEY_ENTRY Search(struct TCascSearch * /* pSearch */, struct _CASC_FIND_DATA * /* pFindData */)
+ {
+ return NULL;
+ }
+
+ // Returns advanced info from the root file entry.
+ // pCKeyEntry - CKey/EKey, depending on which type the root handler provides
+ // pFileInfo - Pointer to CASC_FILE_FULL_INFO structure
+ virtual bool GetInfo(PCASC_CKEY_ENTRY /* pCKeyEntry */, struct _CASC_FILE_FULL_INFO * /* pFileInfo */)
+ {
+ return false;
+ }
+
+ DWORD GetFeatures()
+ {
+ return dwFeatures;
+ }
+
+ protected:
+
+ DWORD dwFeatures; // CASC features. See CASC_FEATURE_XXX
};
//-----------------------------------------------------------------------------
-// Public functions
-
-int RootHandler_Insert(TRootHandler * pRootHandler, const char * szFileName, LPBYTE pbEncodingKey);
-LPBYTE RootHandler_Search(TRootHandler * pRootHandler, struct _TCascSearch * pSearch, PDWORD PtrFileSize, PDWORD PtrLocaleFlags, PDWORD PtrFileDataId);
-void RootHandler_EndSearch(TRootHandler * pRootHandler, struct _TCascSearch * pSearch);
-LPBYTE RootHandler_GetKey(TRootHandler * pRootHandler, const char * szFileName);
-void RootHandler_Dump(struct _TCascStorage * hs, LPBYTE pbRootHandler, DWORD cbRootHandler, const TCHAR * szNameFormat, const TCHAR * szListFile, int nDumpLevel);
-void RootHandler_Close(TRootHandler * pRootHandler);
-DWORD RootHandler_GetFileId(TRootHandler * pRootHandler, const char * szFileName);
+// Class for root handler that has basic mapping of FileName -> CASC_FILE_NODE
+
+struct TFileTreeRoot : public TRootHandler
+{
+ TFileTreeRoot(DWORD FileTreeFlags);
+ virtual ~TFileTreeRoot();
+
+ int Insert(const char * szFileName, PCASC_CKEY_ENTRY pCKeyEntry);
+
+ PCASC_CKEY_ENTRY GetFile(struct TCascStorage * hs, const char * szFileName);
+ PCASC_CKEY_ENTRY GetFile(struct TCascStorage * hs, DWORD FileDataId);
+ PCASC_CKEY_ENTRY Search(struct TCascSearch * pSearch, struct _CASC_FIND_DATA * pFindData);
+ bool GetInfo(PCASC_CKEY_ENTRY pCKeyEntry, struct _CASC_FILE_FULL_INFO * pFileInfo);
+
+ protected:
+
+ CASC_FILE_TREE FileTree;
+};
#endif // __ROOT_HANDLER_H__
diff --git a/dep/CascLib/src/libtomcrypt/src/hashes/hash_memory.c b/dep/CascLib/src/libtomcrypt/src/hashes/hash_memory.c
deleted file mode 100644
index 1daf0bffa1b..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/hashes/hash_memory.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- *
- * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
- */
-#include "../headers/tomcrypt.h"
-
-/**
- @file hash_memory.c
- Hash memory helper, Tom St Denis
-*/
-
-/**
- Hash a block of memory and store the digest.
- @param hash The index of the hash you wish to use
- @param in The data you wish to hash
- @param inlen The length of the data to hash (octets)
- @param out [out] Where to store the digest
- @param outlen [in/out] Max size and resulting size of the digest
- @return CRYPT_OK if successful
-*/
-int hash_memory(int hash, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen)
-{
- hash_state *md;
- int err;
-
- LTC_ARGCHK(in != NULL);
- LTC_ARGCHK(out != NULL);
- LTC_ARGCHK(outlen != NULL);
-
- if ((err = hash_is_valid(hash)) != CRYPT_OK) {
- return err;
- }
-
- if (*outlen < hash_descriptor[hash].hashsize) {
- *outlen = hash_descriptor[hash].hashsize;
- return CRYPT_BUFFER_OVERFLOW;
- }
-
- md = XMALLOC(sizeof(hash_state));
- if (md == NULL) {
- return CRYPT_MEM;
- }
-
- if ((err = hash_descriptor[hash].init(md)) != CRYPT_OK) {
- goto LBL_ERR;
- }
- if ((err = hash_descriptor[hash].process(md, in, inlen)) != CRYPT_OK) {
- goto LBL_ERR;
- }
- err = hash_descriptor[hash].done(md, out);
- *outlen = hash_descriptor[hash].hashsize;
-LBL_ERR:
-#ifdef LTC_CLEAN_STACK
- zeromem(md, sizeof(hash_state));
-#endif
- XFREE(md);
-
- return err;
-}
-
-/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_memory.c,v $ */
-/* $Revision: 1.6 $ */
-/* $Date: 2006/12/28 01:27:23 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/hashes/md5.c b/dep/CascLib/src/libtomcrypt/src/hashes/md5.c
deleted file mode 100644
index e92da256d56..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/hashes/md5.c
+++ /dev/null
@@ -1,368 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- *
- * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
- */
-#include "../headers/tomcrypt.h"
-
-
-/**
- @file md5.c
- LTC_MD5 hash function by Tom St Denis
-*/
-
-#ifdef LTC_MD5
-
-const struct ltc_hash_descriptor md5_desc =
-{
- "md5",
- 3,
- 16,
- 64,
-
- /* OID */
- { 1, 2, 840, 113549, 2, 5, },
- 6,
-
- &md5_init,
- &md5_process,
- &md5_done,
- &md5_test,
- NULL
-};
-
-#define F(x,y,z) (z ^ (x & (y ^ z)))
-#define G(x,y,z) (y ^ (z & (y ^ x)))
-#define H(x,y,z) (x^y^z)
-#define I(x,y,z) (y^(x|(~z)))
-
-#ifdef LTC_SMALL_CODE
-
-#define FF(a,b,c,d,M,s,t) \
- a = (a + F(b,c,d) + M + t); a = ROL(a, s) + b;
-
-#define GG(a,b,c,d,M,s,t) \
- a = (a + G(b,c,d) + M + t); a = ROL(a, s) + b;
-
-#define HH(a,b,c,d,M,s,t) \
- a = (a + H(b,c,d) + M + t); a = ROL(a, s) + b;
-
-#define II(a,b,c,d,M,s,t) \
- a = (a + I(b,c,d) + M + t); a = ROL(a, s) + b;
-
-static const unsigned char Worder[64] = {
- 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
- 1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12,
- 5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2,
- 0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9
-};
-
-static const unsigned char Rorder[64] = {
- 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
- 5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
- 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
- 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
-};
-
-static const ulong32 Korder[64] = {
-0xd76aa478UL, 0xe8c7b756UL, 0x242070dbUL, 0xc1bdceeeUL, 0xf57c0fafUL, 0x4787c62aUL, 0xa8304613UL, 0xfd469501UL,
-0x698098d8UL, 0x8b44f7afUL, 0xffff5bb1UL, 0x895cd7beUL, 0x6b901122UL, 0xfd987193UL, 0xa679438eUL, 0x49b40821UL,
-0xf61e2562UL, 0xc040b340UL, 0x265e5a51UL, 0xe9b6c7aaUL, 0xd62f105dUL, 0x02441453UL, 0xd8a1e681UL, 0xe7d3fbc8UL,
-0x21e1cde6UL, 0xc33707d6UL, 0xf4d50d87UL, 0x455a14edUL, 0xa9e3e905UL, 0xfcefa3f8UL, 0x676f02d9UL, 0x8d2a4c8aUL,
-0xfffa3942UL, 0x8771f681UL, 0x6d9d6122UL, 0xfde5380cUL, 0xa4beea44UL, 0x4bdecfa9UL, 0xf6bb4b60UL, 0xbebfbc70UL,
-0x289b7ec6UL, 0xeaa127faUL, 0xd4ef3085UL, 0x04881d05UL, 0xd9d4d039UL, 0xe6db99e5UL, 0x1fa27cf8UL, 0xc4ac5665UL,
-0xf4292244UL, 0x432aff97UL, 0xab9423a7UL, 0xfc93a039UL, 0x655b59c3UL, 0x8f0ccc92UL, 0xffeff47dUL, 0x85845dd1UL,
-0x6fa87e4fUL, 0xfe2ce6e0UL, 0xa3014314UL, 0x4e0811a1UL, 0xf7537e82UL, 0xbd3af235UL, 0x2ad7d2bbUL, 0xeb86d391UL
-};
-
-#else
-
-#define FF(a,b,c,d,M,s,t) \
- a = (a + F(b,c,d) + M + t); a = ROLc(a, s) + b;
-
-#define GG(a,b,c,d,M,s,t) \
- a = (a + G(b,c,d) + M + t); a = ROLc(a, s) + b;
-
-#define HH(a,b,c,d,M,s,t) \
- a = (a + H(b,c,d) + M + t); a = ROLc(a, s) + b;
-
-#define II(a,b,c,d,M,s,t) \
- a = (a + I(b,c,d) + M + t); a = ROLc(a, s) + b;
-
-
-#endif
-
-#ifdef LTC_CLEAN_STACK
-static int _md5_compress(hash_state *md, unsigned char *buf)
-#else
-static int md5_compress(hash_state *md, unsigned char *buf)
-#endif
-{
- ulong32 i, W[16], a, b, c, d;
-#ifdef LTC_SMALL_CODE
- ulong32 t;
-#endif
-
- /* copy the state into 512-bits into W[0..15] */
- for (i = 0; i < 16; i++) {
- LOAD32L(W[i], buf + (4*i));
- }
-
- /* copy state */
- a = md->md5.state[0];
- b = md->md5.state[1];
- c = md->md5.state[2];
- d = md->md5.state[3];
-
-#ifdef LTC_SMALL_CODE
- for (i = 0; i < 16; ++i) {
- FF(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]);
- t = d; d = c; c = b; b = a; a = t;
- }
-
- for (; i < 32; ++i) {
- GG(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]);
- t = d; d = c; c = b; b = a; a = t;
- }
-
- for (; i < 48; ++i) {
- HH(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]);
- t = d; d = c; c = b; b = a; a = t;
- }
-
- for (; i < 64; ++i) {
- II(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]);
- t = d; d = c; c = b; b = a; a = t;
- }
-
-#else
- FF(a,b,c,d,W[0],7,0xd76aa478UL)
- FF(d,a,b,c,W[1],12,0xe8c7b756UL)
- FF(c,d,a,b,W[2],17,0x242070dbUL)
- FF(b,c,d,a,W[3],22,0xc1bdceeeUL)
- FF(a,b,c,d,W[4],7,0xf57c0fafUL)
- FF(d,a,b,c,W[5],12,0x4787c62aUL)
- FF(c,d,a,b,W[6],17,0xa8304613UL)
- FF(b,c,d,a,W[7],22,0xfd469501UL)
- FF(a,b,c,d,W[8],7,0x698098d8UL)
- FF(d,a,b,c,W[9],12,0x8b44f7afUL)
- FF(c,d,a,b,W[10],17,0xffff5bb1UL)
- FF(b,c,d,a,W[11],22,0x895cd7beUL)
- FF(a,b,c,d,W[12],7,0x6b901122UL)
- FF(d,a,b,c,W[13],12,0xfd987193UL)
- FF(c,d,a,b,W[14],17,0xa679438eUL)
- FF(b,c,d,a,W[15],22,0x49b40821UL)
- GG(a,b,c,d,W[1],5,0xf61e2562UL)
- GG(d,a,b,c,W[6],9,0xc040b340UL)
- GG(c,d,a,b,W[11],14,0x265e5a51UL)
- GG(b,c,d,a,W[0],20,0xe9b6c7aaUL)
- GG(a,b,c,d,W[5],5,0xd62f105dUL)
- GG(d,a,b,c,W[10],9,0x02441453UL)
- GG(c,d,a,b,W[15],14,0xd8a1e681UL)
- GG(b,c,d,a,W[4],20,0xe7d3fbc8UL)
- GG(a,b,c,d,W[9],5,0x21e1cde6UL)
- GG(d,a,b,c,W[14],9,0xc33707d6UL)
- GG(c,d,a,b,W[3],14,0xf4d50d87UL)
- GG(b,c,d,a,W[8],20,0x455a14edUL)
- GG(a,b,c,d,W[13],5,0xa9e3e905UL)
- GG(d,a,b,c,W[2],9,0xfcefa3f8UL)
- GG(c,d,a,b,W[7],14,0x676f02d9UL)
- GG(b,c,d,a,W[12],20,0x8d2a4c8aUL)
- HH(a,b,c,d,W[5],4,0xfffa3942UL)
- HH(d,a,b,c,W[8],11,0x8771f681UL)
- HH(c,d,a,b,W[11],16,0x6d9d6122UL)
- HH(b,c,d,a,W[14],23,0xfde5380cUL)
- HH(a,b,c,d,W[1],4,0xa4beea44UL)
- HH(d,a,b,c,W[4],11,0x4bdecfa9UL)
- HH(c,d,a,b,W[7],16,0xf6bb4b60UL)
- HH(b,c,d,a,W[10],23,0xbebfbc70UL)
- HH(a,b,c,d,W[13],4,0x289b7ec6UL)
- HH(d,a,b,c,W[0],11,0xeaa127faUL)
- HH(c,d,a,b,W[3],16,0xd4ef3085UL)
- HH(b,c,d,a,W[6],23,0x04881d05UL)
- HH(a,b,c,d,W[9],4,0xd9d4d039UL)
- HH(d,a,b,c,W[12],11,0xe6db99e5UL)
- HH(c,d,a,b,W[15],16,0x1fa27cf8UL)
- HH(b,c,d,a,W[2],23,0xc4ac5665UL)
- II(a,b,c,d,W[0],6,0xf4292244UL)
- II(d,a,b,c,W[7],10,0x432aff97UL)
- II(c,d,a,b,W[14],15,0xab9423a7UL)
- II(b,c,d,a,W[5],21,0xfc93a039UL)
- II(a,b,c,d,W[12],6,0x655b59c3UL)
- II(d,a,b,c,W[3],10,0x8f0ccc92UL)
- II(c,d,a,b,W[10],15,0xffeff47dUL)
- II(b,c,d,a,W[1],21,0x85845dd1UL)
- II(a,b,c,d,W[8],6,0x6fa87e4fUL)
- II(d,a,b,c,W[15],10,0xfe2ce6e0UL)
- II(c,d,a,b,W[6],15,0xa3014314UL)
- II(b,c,d,a,W[13],21,0x4e0811a1UL)
- II(a,b,c,d,W[4],6,0xf7537e82UL)
- II(d,a,b,c,W[11],10,0xbd3af235UL)
- II(c,d,a,b,W[2],15,0x2ad7d2bbUL)
- II(b,c,d,a,W[9],21,0xeb86d391UL)
-#endif
-
- md->md5.state[0] = md->md5.state[0] + a;
- md->md5.state[1] = md->md5.state[1] + b;
- md->md5.state[2] = md->md5.state[2] + c;
- md->md5.state[3] = md->md5.state[3] + d;
-
- return CRYPT_OK;
-}
-
-#ifdef LTC_CLEAN_STACK
-static int md5_compress(hash_state *md, unsigned char *buf)
-{
- int err;
- err = _md5_compress(md, buf);
- burn_stack(sizeof(ulong32) * 21);
- return err;
-}
-#endif
-
-/**
- Initialize the hash state
- @param md The hash state you wish to initialize
- @return CRYPT_OK if successful
-*/
-int md5_init(hash_state * md)
-{
- LTC_ARGCHK(md != NULL);
- md->md5.state[0] = 0x67452301UL;
- md->md5.state[1] = 0xefcdab89UL;
- md->md5.state[2] = 0x98badcfeUL;
- md->md5.state[3] = 0x10325476UL;
- md->md5.curlen = 0;
- md->md5.length = 0;
- return CRYPT_OK;
-}
-
-/**
- Process a block of memory though the hash
- @param md The hash state
- @param in The data to hash
- @param inlen The length of the data (octets)
- @return CRYPT_OK if successful
-*/
-HASH_PROCESS(md5_process, md5_compress, md5, 64)
-
-/**
- Terminate the hash to get the digest
- @param md The hash state
- @param out [out] The destination of the hash (16 bytes)
- @return CRYPT_OK if successful
-*/
-int md5_done(hash_state * md, unsigned char *out)
-{
- int i;
-
- LTC_ARGCHK(md != NULL);
- LTC_ARGCHK(out != NULL);
-
- if (md->md5.curlen >= sizeof(md->md5.buf)) {
- return CRYPT_INVALID_ARG;
- }
-
-
- /* increase the length of the message */
- md->md5.length += md->md5.curlen * 8;
-
- /* append the '1' bit */
- md->md5.buf[md->md5.curlen++] = (unsigned char)0x80;
-
- /* if the length is currently above 56 bytes we append zeros
- * then compress. Then we can fall back to padding zeros and length
- * encoding like normal.
- */
- if (md->md5.curlen > 56) {
- while (md->md5.curlen < 64) {
- md->md5.buf[md->md5.curlen++] = (unsigned char)0;
- }
- md5_compress(md, md->md5.buf);
- md->md5.curlen = 0;
- }
-
- /* pad upto 56 bytes of zeroes */
- while (md->md5.curlen < 56) {
- md->md5.buf[md->md5.curlen++] = (unsigned char)0;
- }
-
- /* store length */
- STORE64L(md->md5.length, md->md5.buf+56);
- md5_compress(md, md->md5.buf);
-
- /* copy output */
- for (i = 0; i < 4; i++) {
- STORE32L(md->md5.state[i], out+(4*i));
- }
-#ifdef LTC_CLEAN_STACK
- zeromem(md, sizeof(hash_state));
-#endif
- return CRYPT_OK;
-}
-
-/**
- Self-test the hash
- @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
-*/
-int md5_test(void)
-{
- #ifndef LTC_TEST
- return CRYPT_NOP;
- #else
- static const struct {
- char *msg;
- unsigned char hash[16];
- } tests[] = {
- { "",
- { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
- 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } },
- { "a",
- {0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8,
- 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 } },
- { "abc",
- { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0,
- 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } },
- { "message digest",
- { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d,
- 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 } },
- { "abcdefghijklmnopqrstuvwxyz",
- { 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00,
- 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b } },
- { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
- { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5,
- 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f } },
- { "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
- { 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55,
- 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a } },
- { NULL, { 0 } }
- };
-
- int i;
- unsigned char tmp[16];
- hash_state md;
-
- for (i = 0; tests[i].msg != NULL; i++) {
- md5_init(&md);
- md5_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg));
- md5_done(&md, tmp);
- if (XMEMCMP(tmp, tests[i].hash, 16) != 0) {
- return CRYPT_FAIL_TESTVECTOR;
- }
- }
- return CRYPT_OK;
- #endif
-}
-
-#endif
-
-
-
-/* $Source: /cvs/libtom/libtomcrypt/src/hashes/md5.c,v $ */
-/* $Revision: 1.10 $ */
-/* $Date: 2007/05/12 14:25:28 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt.h
deleted file mode 100644
index 74cdff47549..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt.h
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef TOMCRYPT_H_
-#define TOMCRYPT_H_
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <time.h>
-#include <ctype.h>
-#include <limits.h>
-
-/* use configuration data */
-#include "tomcrypt_custom.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* version */
-#define CRYPT 0x0117
-#define SCRYPT "1.17"
-
-/* max size of either a cipher/hash block or symmetric key [largest of the two] */
-#define MAXBLOCKSIZE 128
-
-/* descriptor table size */
-#define TAB_SIZE 32
-
-/* error codes [will be expanded in future releases] */
-enum {
- CRYPT_OK=0, /* Result OK */
- CRYPT_ERROR, /* Generic Error */
- CRYPT_NOP, /* Not a failure but no operation was performed */
-
- CRYPT_INVALID_KEYSIZE, /* Invalid key size given */
- CRYPT_INVALID_ROUNDS, /* Invalid number of rounds */
- CRYPT_FAIL_TESTVECTOR, /* Algorithm failed test vectors */
-
- CRYPT_BUFFER_OVERFLOW, /* Not enough space for output */
- CRYPT_INVALID_PACKET, /* Invalid input packet given */
-
- CRYPT_INVALID_PRNGSIZE, /* Invalid number of bits for a PRNG */
- CRYPT_ERROR_READPRNG, /* Could not read enough from PRNG */
-
- CRYPT_INVALID_CIPHER, /* Invalid cipher specified */
- CRYPT_INVALID_HASH, /* Invalid hash specified */
- CRYPT_INVALID_PRNG, /* Invalid PRNG specified */
-
- CRYPT_MEM, /* Out of memory */
-
- CRYPT_PK_TYPE_MISMATCH, /* Not equivalent types of PK keys */
- CRYPT_PK_NOT_PRIVATE, /* Requires a private PK key */
-
- CRYPT_INVALID_ARG, /* Generic invalid argument */
- CRYPT_FILE_NOTFOUND, /* File Not Found */
-
- CRYPT_PK_INVALID_TYPE, /* Invalid type of PK key */
- CRYPT_PK_INVALID_SYSTEM,/* Invalid PK system specified */
- CRYPT_PK_DUP, /* Duplicate key already in key ring */
- CRYPT_PK_NOT_FOUND, /* Key not found in keyring */
- CRYPT_PK_INVALID_SIZE, /* Invalid size input for PK parameters */
-
- CRYPT_INVALID_PRIME_SIZE,/* Invalid size of prime requested */
- CRYPT_PK_INVALID_PADDING /* Invalid padding on input */
-};
-
-#include "tomcrypt_cfg.h"
-#include "tomcrypt_macros.h"
-#include "tomcrypt_cipher.h"
-#include "tomcrypt_hash.h"
-#include "tomcrypt_mac.h"
-#include "tomcrypt_prng.h"
-#include "tomcrypt_pk.h"
-#include "tomcrypt_math.h"
-#include "tomcrypt_misc.h"
-#include "tomcrypt_argchk.h"
-#include "tomcrypt_pkcs.h"
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* TOMCRYPT_H_ */
-
-
-/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt.h,v $ */
-/* $Revision: 1.21 $ */
-/* $Date: 2006/12/16 19:34:05 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_argchk.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_argchk.h
deleted file mode 100644
index cfc93ad7ea6..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_argchk.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Defines the LTC_ARGCHK macro used within the library */
-/* ARGTYPE is defined in mycrypt_cfg.h */
-#if ARGTYPE == 0
-
-#include <signal.h>
-
-/* this is the default LibTomCrypt macro */
-void crypt_argchk(char *v, char *s, int d);
-#define LTC_ARGCHK(x) if (!(x)) { crypt_argchk(#x, __FILE__, __LINE__); }
-#define LTC_ARGCHKVD(x) LTC_ARGCHK(x)
-
-#elif ARGTYPE == 1
-
-/* fatal type of error */
-#define LTC_ARGCHK(x) assert((x))
-#define LTC_ARGCHKVD(x) LTC_ARGCHK(x)
-
-#elif ARGTYPE == 2
-
-#define LTC_ARGCHK(x) if (!(x)) { fprintf(stderr, "\nwarning: ARGCHK failed at %s:%d\n", __FILE__, __LINE__); }
-#define LTC_ARGCHKVD(x) LTC_ARGCHK(x)
-
-#elif ARGTYPE == 3
-
-#define LTC_ARGCHK(x)
-#define LTC_ARGCHKVD(x) LTC_ARGCHK(x)
-
-#elif ARGTYPE == 4
-
-#define LTC_ARGCHK(x) if (!(x)) return CRYPT_INVALID_ARG;
-#define LTC_ARGCHKVD(x) if (!(x)) return;
-
-#endif
-
-
-/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_argchk.h,v $ */
-/* $Revision: 1.5 $ */
-/* $Date: 2006/08/27 20:50:21 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cfg.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cfg.h
deleted file mode 100644
index 7feae6e8bdc..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cfg.h
+++ /dev/null
@@ -1,136 +0,0 @@
-/* This is the build config file.
- *
- * With this you can setup what to inlcude/exclude automatically during any build. Just comment
- * out the line that #define's the word for the thing you want to remove. phew!
- */
-
-#ifndef TOMCRYPT_CFG_H
-#define TOMCRYPT_CFG_H
-
-#if defined(_WIN32) || defined(_MSC_VER)
-#define LTC_CALL __cdecl
-#else
-#ifndef LTC_CALL
- #define LTC_CALL
-#endif
-#endif
-
-#ifndef LTC_EXPORT
-#define LTC_EXPORT
-#endif
-
-/* certain platforms use macros for these, making the prototypes broken */
-#ifndef LTC_NO_PROTOTYPES
-
-/* you can change how memory allocation works ... */
-LTC_EXPORT void * LTC_CALL XMALLOC(size_t n);
-LTC_EXPORT void * LTC_CALL XREALLOC(void *p, size_t n);
-LTC_EXPORT void * LTC_CALL XCALLOC(size_t n, size_t s);
-LTC_EXPORT void LTC_CALL XFREE(void *p);
-
-LTC_EXPORT void LTC_CALL XQSORT(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));
-
-
-/* change the clock function too */
-LTC_EXPORT clock_t LTC_CALL XCLOCK(void);
-
-/* various other functions */
-LTC_EXPORT void * LTC_CALL XMEMCPY(void *dest, const void *src, size_t n);
-LTC_EXPORT int LTC_CALL XMEMCMP(const void *s1, const void *s2, size_t n);
-LTC_EXPORT void * LTC_CALL XMEMSET(void *s, int c, size_t n);
-
-LTC_EXPORT int LTC_CALL XSTRCMP(const char *s1, const char *s2);
-
-#endif
-
-/* type of argument checking, 0=default, 1=fatal and 2=error+continue, 3=nothing */
-#ifndef ARGTYPE
- #define ARGTYPE 0
-#endif
-
-/* Controls endianess and size of registers. Leave uncommented to get platform neutral [slower] code
- *
- * Note: in order to use the optimized macros your platform must support unaligned 32 and 64 bit read/writes.
- * The x86 platforms allow this but some others [ARM for instance] do not. On those platforms you **MUST**
- * use the portable [slower] macros.
- */
-
-/* detect x86-32 machines somewhat */
-#if !defined(__STRICT_ANSI__) && (defined(INTEL_CC) || (defined(_MSC_VER) && defined(WIN32)) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__))))
- #define ENDIAN_LITTLE
- #define ENDIAN_32BITWORD
- #define LTC_FAST
- #define LTC_FAST_TYPE unsigned long
-#endif
-
-/* detects MIPS R5900 processors (PS2) */
-#if (defined(__R5900) || defined(R5900) || defined(__R5900__)) && (defined(_mips) || defined(__mips__) || defined(mips))
- #define ENDIAN_LITTLE
- #define ENDIAN_64BITWORD
-#endif
-
-/* detect amd64 */
-#if !defined(__STRICT_ANSI__) && defined(__x86_64__)
- #define ENDIAN_LITTLE
- #define ENDIAN_64BITWORD
- #define LTC_FAST
- #define LTC_FAST_TYPE unsigned long
-#endif
-
-/* detect PPC32 */
-#if !defined(__STRICT_ANSI__) && defined(LTC_PPC32)
- #define ENDIAN_BIG
- #define ENDIAN_32BITWORD
- #define LTC_FAST
- #define LTC_FAST_TYPE unsigned long
-#endif
-
-/* detect sparc and sparc64 */
-#if defined(__sparc__)
- #define ENDIAN_BIG
- #if defined(__arch64__)
- #define ENDIAN_64BITWORD
- #else
- #define ENDIAN_32BITWORD
- #endif
-#endif
-
-
-#ifdef LTC_NO_FAST
- #ifdef LTC_FAST
- #undef LTC_FAST
- #endif
-#endif
-
-/* No asm is a quick way to disable anything "not portable" */
-#ifdef LTC_NO_ASM
- #undef ENDIAN_LITTLE
- #undef ENDIAN_BIG
- #undef ENDIAN_32BITWORD
- #undef ENDIAN_64BITWORD
- #undef LTC_FAST
- #undef LTC_FAST_TYPE
- #define LTC_NO_ROLC
- #define LTC_NO_BSWAP
-#endif
-
-/* #define ENDIAN_LITTLE */
-/* #define ENDIAN_BIG */
-
-/* #define ENDIAN_32BITWORD */
-/* #define ENDIAN_64BITWORD */
-
-#if (defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) && !(defined(ENDIAN_32BITWORD) || defined(ENDIAN_64BITWORD))
- #error You must specify a word size as well as endianess in tomcrypt_cfg.h
-#endif
-
-#if !(defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE))
- #define ENDIAN_NEUTRAL
-#endif
-
-#endif
-
-
-/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cfg.h,v $ */
-/* $Revision: 1.19 $ */
-/* $Date: 2006/12/04 02:19:48 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cipher.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cipher.h
deleted file mode 100644
index bd740bf4a0c..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_cipher.h
+++ /dev/null
@@ -1,891 +0,0 @@
-/* ---- SYMMETRIC KEY STUFF -----
- *
- * We put each of the ciphers scheduled keys in their own structs then we put all of
- * the key formats in one union. This makes the function prototypes easier to use.
- */
-#ifdef LTC_BLOWFISH
-struct blowfish_key {
- ulong32 S[4][256];
- ulong32 K[18];
-};
-#endif
-
-#ifdef LTC_RC5
-struct rc5_key {
- int rounds;
- ulong32 K[50];
-};
-#endif
-
-#ifdef LTC_RC6
-struct rc6_key {
- ulong32 K[44];
-};
-#endif
-
-#ifdef LTC_SAFERP
-struct saferp_key {
- unsigned char K[33][16];
- long rounds;
-};
-#endif
-
-#ifdef LTC_RIJNDAEL
-struct rijndael_key {
- ulong32 eK[60], dK[60];
- int Nr;
-};
-#endif
-
-#ifdef LTC_KSEED
-struct kseed_key {
- ulong32 K[32], dK[32];
-};
-#endif
-
-#ifdef LTC_KASUMI
-struct kasumi_key {
- ulong32 KLi1[8], KLi2[8],
- KOi1[8], KOi2[8], KOi3[8],
- KIi1[8], KIi2[8], KIi3[8];
-};
-#endif
-
-#ifdef LTC_XTEA
-struct xtea_key {
- unsigned long A[32], B[32];
-};
-#endif
-
-#ifdef LTC_TWOFISH
-#ifndef LTC_TWOFISH_SMALL
- struct twofish_key {
- ulong32 S[4][256], K[40];
- };
-#else
- struct twofish_key {
- ulong32 K[40];
- unsigned char S[32], start;
- };
-#endif
-#endif
-
-#ifdef LTC_SAFER
-#define LTC_SAFER_K64_DEFAULT_NOF_ROUNDS 6
-#define LTC_SAFER_K128_DEFAULT_NOF_ROUNDS 10
-#define LTC_SAFER_SK64_DEFAULT_NOF_ROUNDS 8
-#define LTC_SAFER_SK128_DEFAULT_NOF_ROUNDS 10
-#define LTC_SAFER_MAX_NOF_ROUNDS 13
-#define LTC_SAFER_BLOCK_LEN 8
-#define LTC_SAFER_KEY_LEN (1 + LTC_SAFER_BLOCK_LEN * (1 + 2 * LTC_SAFER_MAX_NOF_ROUNDS))
-typedef unsigned char safer_block_t[LTC_SAFER_BLOCK_LEN];
-typedef unsigned char safer_key_t[LTC_SAFER_KEY_LEN];
-struct safer_key { safer_key_t key; };
-#endif
-
-#ifdef LTC_RC2
-struct rc2_key { unsigned xkey[64]; };
-#endif
-
-#ifdef LTC_DES
-struct des_key {
- ulong32 ek[32], dk[32];
-};
-
-struct des3_key {
- ulong32 ek[3][32], dk[3][32];
-};
-#endif
-
-#ifdef LTC_CAST5
-struct cast5_key {
- ulong32 K[32], keylen;
-};
-#endif
-
-#ifdef LTC_NOEKEON
-struct noekeon_key {
- ulong32 K[4], dK[4];
-};
-#endif
-
-#ifdef LTC_SKIPJACK
-struct skipjack_key {
- unsigned char key[10];
-};
-#endif
-
-#ifdef LTC_KHAZAD
-struct khazad_key {
- ulong64 roundKeyEnc[8 + 1];
- ulong64 roundKeyDec[8 + 1];
-};
-#endif
-
-#ifdef LTC_ANUBIS
-struct anubis_key {
- int keyBits;
- int R;
- ulong32 roundKeyEnc[18 + 1][4];
- ulong32 roundKeyDec[18 + 1][4];
-};
-#endif
-
-#ifdef LTC_MULTI2
-struct multi2_key {
- int N;
- ulong32 uk[8];
-};
-#endif
-
-typedef union Symmetric_key {
-#ifdef LTC_DES
- struct des_key des;
- struct des3_key des3;
-#endif
-#ifdef LTC_RC2
- struct rc2_key rc2;
-#endif
-#ifdef LTC_SAFER
- struct safer_key safer;
-#endif
-#ifdef LTC_TWOFISH
- struct twofish_key twofish;
-#endif
-#ifdef LTC_BLOWFISH
- struct blowfish_key blowfish;
-#endif
-#ifdef LTC_RC5
- struct rc5_key rc5;
-#endif
-#ifdef LTC_RC6
- struct rc6_key rc6;
-#endif
-#ifdef LTC_SAFERP
- struct saferp_key saferp;
-#endif
-#ifdef LTC_RIJNDAEL
- struct rijndael_key rijndael;
-#endif
-#ifdef LTC_XTEA
- struct xtea_key xtea;
-#endif
-#ifdef LTC_CAST5
- struct cast5_key cast5;
-#endif
-#ifdef LTC_NOEKEON
- struct noekeon_key noekeon;
-#endif
-#ifdef LTC_SKIPJACK
- struct skipjack_key skipjack;
-#endif
-#ifdef LTC_KHAZAD
- struct khazad_key khazad;
-#endif
-#ifdef LTC_ANUBIS
- struct anubis_key anubis;
-#endif
-#ifdef LTC_KSEED
- struct kseed_key kseed;
-#endif
-#ifdef LTC_KASUMI
- struct kasumi_key kasumi;
-#endif
-#ifdef LTC_MULTI2
- struct multi2_key multi2;
-#endif
- void *data;
-} symmetric_key;
-
-#ifdef LTC_ECB_MODE
-/** A block cipher ECB structure */
-typedef struct {
- /** The index of the cipher chosen */
- int cipher,
- /** The block size of the given cipher */
- blocklen;
- /** The scheduled key */
- symmetric_key key;
-} symmetric_ECB;
-#endif
-
-#ifdef LTC_CFB_MODE
-/** A block cipher CFB structure */
-typedef struct {
- /** The index of the cipher chosen */
- int cipher,
- /** The block size of the given cipher */
- blocklen,
- /** The padding offset */
- padlen;
- /** The current IV */
- unsigned char IV[MAXBLOCKSIZE],
- /** The pad used to encrypt/decrypt */
- pad[MAXBLOCKSIZE];
- /** The scheduled key */
- symmetric_key key;
-} symmetric_CFB;
-#endif
-
-#ifdef LTC_OFB_MODE
-/** A block cipher OFB structure */
-typedef struct {
- /** The index of the cipher chosen */
- int cipher,
- /** The block size of the given cipher */
- blocklen,
- /** The padding offset */
- padlen;
- /** The current IV */
- unsigned char IV[MAXBLOCKSIZE];
- /** The scheduled key */
- symmetric_key key;
-} symmetric_OFB;
-#endif
-
-#ifdef LTC_CBC_MODE
-/** A block cipher CBC structure */
-typedef struct {
- /** The index of the cipher chosen */
- int cipher,
- /** The block size of the given cipher */
- blocklen;
- /** The current IV */
- unsigned char IV[MAXBLOCKSIZE];
- /** The scheduled key */
- symmetric_key key;
-} symmetric_CBC;
-#endif
-
-
-#ifdef LTC_CTR_MODE
-/** A block cipher CTR structure */
-typedef struct {
- /** The index of the cipher chosen */
- int cipher,
- /** The block size of the given cipher */
- blocklen,
- /** The padding offset */
- padlen,
- /** The mode (endianess) of the CTR, 0==little, 1==big */
- mode,
- /** counter width */
- ctrlen;
-
- /** The counter */
- unsigned char ctr[MAXBLOCKSIZE],
- /** The pad used to encrypt/decrypt */
- pad[MAXBLOCKSIZE];
- /** The scheduled key */
- symmetric_key key;
-} symmetric_CTR;
-#endif
-
-
-#ifdef LTC_LRW_MODE
-/** A LRW structure */
-typedef struct {
- /** The index of the cipher chosen (must be a 128-bit block cipher) */
- int cipher;
-
- /** The current IV */
- unsigned char IV[16],
-
- /** the tweak key */
- tweak[16],
-
- /** The current pad, it's the product of the first 15 bytes against the tweak key */
- pad[16];
-
- /** The scheduled symmetric key */
- symmetric_key key;
-
-#ifdef LRW_TABLES
- /** The pre-computed multiplication table */
- unsigned char PC[16][256][16];
-#endif
-} symmetric_LRW;
-#endif
-
-#ifdef LTC_F8_MODE
-/** A block cipher F8 structure */
-typedef struct {
- /** The index of the cipher chosen */
- int cipher,
- /** The block size of the given cipher */
- blocklen,
- /** The padding offset */
- padlen;
- /** The current IV */
- unsigned char IV[MAXBLOCKSIZE],
- MIV[MAXBLOCKSIZE];
- /** Current block count */
- ulong32 blockcnt;
- /** The scheduled key */
- symmetric_key key;
-} symmetric_F8;
-#endif
-
-
-/** cipher descriptor table, last entry has "name == NULL" to mark the end of table */
-extern struct ltc_cipher_descriptor {
- /** name of cipher */
- char *name;
- /** internal ID */
- unsigned char ID;
- /** min keysize (octets) */
- int min_key_length,
- /** max keysize (octets) */
- max_key_length,
- /** block size (octets) */
- block_length,
- /** default number of rounds */
- default_rounds;
- /** Setup the cipher
- @param key The input symmetric key
- @param keylen The length of the input key (octets)
- @param num_rounds The requested number of rounds (0==default)
- @param skey [out] The destination of the scheduled key
- @return CRYPT_OK if successful
- */
- int (*setup)(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
- /** Encrypt a block
- @param pt The plaintext
- @param ct [out] The ciphertext
- @param skey The scheduled key
- @return CRYPT_OK if successful
- */
- int (*ecb_encrypt)(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
- /** Decrypt a block
- @param ct The ciphertext
- @param pt [out] The plaintext
- @param skey The scheduled key
- @return CRYPT_OK if successful
- */
- int (*ecb_decrypt)(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
- /** Test the block cipher
- @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
- */
- int (*test)(void);
-
- /** Terminate the context
- @param skey The scheduled key
- */
- void (*done)(symmetric_key *skey);
-
- /** Determine a key size
- @param keysize [in/out] The size of the key desired and the suggested size
- @return CRYPT_OK if successful
- */
- int (*keysize)(int *keysize);
-
-/** Accelerators **/
- /** Accelerated ECB encryption
- @param pt Plaintext
- @param ct Ciphertext
- @param blocks The number of complete blocks to process
- @param skey The scheduled key context
- @return CRYPT_OK if successful
- */
- int (*accel_ecb_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, symmetric_key *skey);
-
- /** Accelerated ECB decryption
- @param pt Plaintext
- @param ct Ciphertext
- @param blocks The number of complete blocks to process
- @param skey The scheduled key context
- @return CRYPT_OK if successful
- */
- int (*accel_ecb_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, symmetric_key *skey);
-
- /** Accelerated CBC encryption
- @param pt Plaintext
- @param ct Ciphertext
- @param blocks The number of complete blocks to process
- @param IV The initial value (input/output)
- @param skey The scheduled key context
- @return CRYPT_OK if successful
- */
- int (*accel_cbc_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, symmetric_key *skey);
-
- /** Accelerated CBC decryption
- @param pt Plaintext
- @param ct Ciphertext
- @param blocks The number of complete blocks to process
- @param IV The initial value (input/output)
- @param skey The scheduled key context
- @return CRYPT_OK if successful
- */
- int (*accel_cbc_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, symmetric_key *skey);
-
- /** Accelerated CTR encryption
- @param pt Plaintext
- @param ct Ciphertext
- @param blocks The number of complete blocks to process
- @param IV The initial value (input/output)
- @param mode little or big endian counter (mode=0 or mode=1)
- @param skey The scheduled key context
- @return CRYPT_OK if successful
- */
- int (*accel_ctr_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, int mode, symmetric_key *skey);
-
- /** Accelerated LRW
- @param pt Plaintext
- @param ct Ciphertext
- @param blocks The number of complete blocks to process
- @param IV The initial value (input/output)
- @param tweak The LRW tweak
- @param skey The scheduled key context
- @return CRYPT_OK if successful
- */
- int (*accel_lrw_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey);
-
- /** Accelerated LRW
- @param ct Ciphertext
- @param pt Plaintext
- @param blocks The number of complete blocks to process
- @param IV The initial value (input/output)
- @param tweak The LRW tweak
- @param skey The scheduled key context
- @return CRYPT_OK if successful
- */
- int (*accel_lrw_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey);
-
- /** Accelerated CCM packet (one-shot)
- @param key The secret key to use
- @param keylen The length of the secret key (octets)
- @param uskey A previously scheduled key [optional can be NULL]
- @param nonce The session nonce [use once]
- @param noncelen The length of the nonce
- @param header The header for the session
- @param headerlen The length of the header (octets)
- @param pt [out] The plaintext
- @param ptlen The length of the plaintext (octets)
- @param ct [out] The ciphertext
- @param tag [out] The destination tag
- @param taglen [in/out] The max size and resulting size of the authentication tag
- @param direction Encrypt or Decrypt direction (0 or 1)
- @return CRYPT_OK if successful
- */
- int (*accel_ccm_memory)(
- const unsigned char *key, unsigned long keylen,
- symmetric_key *uskey,
- const unsigned char *nonce, unsigned long noncelen,
- const unsigned char *header, unsigned long headerlen,
- unsigned char *pt, unsigned long ptlen,
- unsigned char *ct,
- unsigned char *tag, unsigned long *taglen,
- int direction);
-
- /** Accelerated GCM packet (one shot)
- @param key The secret key
- @param keylen The length of the secret key
- @param IV The initial vector
- @param IVlen The length of the initial vector
- @param adata The additional authentication data (header)
- @param adatalen The length of the adata
- @param pt The plaintext
- @param ptlen The length of the plaintext (ciphertext length is the same)
- @param ct The ciphertext
- @param tag [out] The MAC tag
- @param taglen [in/out] The MAC tag length
- @param direction Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT)
- @return CRYPT_OK on success
- */
- int (*accel_gcm_memory)(
- const unsigned char *key, unsigned long keylen,
- const unsigned char *IV, unsigned long IVlen,
- const unsigned char *adata, unsigned long adatalen,
- unsigned char *pt, unsigned long ptlen,
- unsigned char *ct,
- unsigned char *tag, unsigned long *taglen,
- int direction);
-
- /** Accelerated one shot LTC_OMAC
- @param key The secret key
- @param keylen The key length (octets)
- @param in The message
- @param inlen Length of message (octets)
- @param out [out] Destination for tag
- @param outlen [in/out] Initial and final size of out
- @return CRYPT_OK on success
- */
- int (*omac_memory)(
- const unsigned char *key, unsigned long keylen,
- const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-
- /** Accelerated one shot XCBC
- @param key The secret key
- @param keylen The key length (octets)
- @param in The message
- @param inlen Length of message (octets)
- @param out [out] Destination for tag
- @param outlen [in/out] Initial and final size of out
- @return CRYPT_OK on success
- */
- int (*xcbc_memory)(
- const unsigned char *key, unsigned long keylen,
- const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-
- /** Accelerated one shot F9
- @param key The secret key
- @param keylen The key length (octets)
- @param in The message
- @param inlen Length of message (octets)
- @param out [out] Destination for tag
- @param outlen [in/out] Initial and final size of out
- @return CRYPT_OK on success
- @remark Requires manual padding
- */
- int (*f9_memory)(
- const unsigned char *key, unsigned long keylen,
- const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-} cipher_descriptor[];
-
-#ifdef LTC_BLOWFISH
-int blowfish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int blowfish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int blowfish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int blowfish_test(void);
-void blowfish_done(symmetric_key *skey);
-int blowfish_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor blowfish_desc;
-#endif
-
-#ifdef LTC_RC5
-int rc5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int rc5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int rc5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int rc5_test(void);
-void rc5_done(symmetric_key *skey);
-int rc5_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor rc5_desc;
-#endif
-
-#ifdef LTC_RC6
-int rc6_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int rc6_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int rc6_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int rc6_test(void);
-void rc6_done(symmetric_key *skey);
-int rc6_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor rc6_desc;
-#endif
-
-#ifdef LTC_RC2
-int rc2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int rc2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int rc2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int rc2_test(void);
-void rc2_done(symmetric_key *skey);
-int rc2_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor rc2_desc;
-#endif
-
-#ifdef LTC_SAFERP
-int saferp_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int saferp_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int saferp_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int saferp_test(void);
-void saferp_done(symmetric_key *skey);
-int saferp_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor saferp_desc;
-#endif
-
-#ifdef LTC_SAFER
-int safer_k64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int safer_sk64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int safer_k128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int safer_sk128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int safer_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *key);
-int safer_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *key);
-int safer_k64_test(void);
-int safer_sk64_test(void);
-int safer_sk128_test(void);
-void safer_done(symmetric_key *skey);
-int safer_64_keysize(int *keysize);
-int safer_128_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor safer_k64_desc, safer_k128_desc, safer_sk64_desc, safer_sk128_desc;
-#endif
-
-#ifdef LTC_RIJNDAEL
-
-/* make aes an alias */
-#define aes_setup rijndael_setup
-#define aes_ecb_encrypt rijndael_ecb_encrypt
-#define aes_ecb_decrypt rijndael_ecb_decrypt
-#define aes_test rijndael_test
-#define aes_done rijndael_done
-#define aes_keysize rijndael_keysize
-
-#define aes_enc_setup rijndael_enc_setup
-#define aes_enc_ecb_encrypt rijndael_enc_ecb_encrypt
-#define aes_enc_keysize rijndael_enc_keysize
-
-int rijndael_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int rijndael_test(void);
-void rijndael_done(symmetric_key *skey);
-int rijndael_keysize(int *keysize);
-int rijndael_enc_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int rijndael_enc_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-void rijndael_enc_done(symmetric_key *skey);
-int rijndael_enc_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor rijndael_desc, aes_desc;
-extern const struct ltc_cipher_descriptor rijndael_enc_desc, aes_enc_desc;
-#endif
-
-#ifdef LTC_XTEA
-int xtea_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int xtea_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int xtea_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int xtea_test(void);
-void xtea_done(symmetric_key *skey);
-int xtea_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor xtea_desc;
-#endif
-
-#ifdef LTC_TWOFISH
-int twofish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int twofish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int twofish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int twofish_test(void);
-void twofish_done(symmetric_key *skey);
-int twofish_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor twofish_desc;
-#endif
-
-#ifdef LTC_DES
-int des_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int des_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int des_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int des_test(void);
-void des_done(symmetric_key *skey);
-int des_keysize(int *keysize);
-int des3_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int des3_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int des3_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int des3_test(void);
-void des3_done(symmetric_key *skey);
-int des3_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor des_desc, des3_desc;
-#endif
-
-#ifdef LTC_CAST5
-int cast5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int cast5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int cast5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int cast5_test(void);
-void cast5_done(symmetric_key *skey);
-int cast5_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor cast5_desc;
-#endif
-
-#ifdef LTC_NOEKEON
-int noekeon_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int noekeon_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int noekeon_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int noekeon_test(void);
-void noekeon_done(symmetric_key *skey);
-int noekeon_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor noekeon_desc;
-#endif
-
-#ifdef LTC_SKIPJACK
-int skipjack_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int skipjack_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int skipjack_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int skipjack_test(void);
-void skipjack_done(symmetric_key *skey);
-int skipjack_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor skipjack_desc;
-#endif
-
-#ifdef LTC_KHAZAD
-int khazad_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int khazad_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int khazad_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int khazad_test(void);
-void khazad_done(symmetric_key *skey);
-int khazad_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor khazad_desc;
-#endif
-
-#ifdef LTC_ANUBIS
-int anubis_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int anubis_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int anubis_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int anubis_test(void);
-void anubis_done(symmetric_key *skey);
-int anubis_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor anubis_desc;
-#endif
-
-#ifdef LTC_KSEED
-int kseed_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int kseed_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int kseed_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int kseed_test(void);
-void kseed_done(symmetric_key *skey);
-int kseed_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor kseed_desc;
-#endif
-
-#ifdef LTC_KASUMI
-int kasumi_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int kasumi_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int kasumi_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int kasumi_test(void);
-void kasumi_done(symmetric_key *skey);
-int kasumi_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor kasumi_desc;
-#endif
-
-
-#ifdef LTC_MULTI2
-int multi2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
-int multi2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey);
-int multi2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey);
-int multi2_test(void);
-void multi2_done(symmetric_key *skey);
-int multi2_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor multi2_desc;
-#endif
-
-#ifdef LTC_ECB_MODE
-int ecb_start(int cipher, const unsigned char *key,
- int keylen, int num_rounds, symmetric_ECB *ecb);
-int ecb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_ECB *ecb);
-int ecb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_ECB *ecb);
-int ecb_done(symmetric_ECB *ecb);
-#endif
-
-#ifdef LTC_CFB_MODE
-int cfb_start(int cipher, const unsigned char *IV, const unsigned char *key,
- int keylen, int num_rounds, symmetric_CFB *cfb);
-int cfb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CFB *cfb);
-int cfb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CFB *cfb);
-int cfb_getiv(unsigned char *IV, unsigned long *len, symmetric_CFB *cfb);
-int cfb_setiv(const unsigned char *IV, unsigned long len, symmetric_CFB *cfb);
-int cfb_done(symmetric_CFB *cfb);
-#endif
-
-#ifdef LTC_OFB_MODE
-int ofb_start(int cipher, const unsigned char *IV, const unsigned char *key,
- int keylen, int num_rounds, symmetric_OFB *ofb);
-int ofb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_OFB *ofb);
-int ofb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_OFB *ofb);
-int ofb_getiv(unsigned char *IV, unsigned long *len, symmetric_OFB *ofb);
-int ofb_setiv(const unsigned char *IV, unsigned long len, symmetric_OFB *ofb);
-int ofb_done(symmetric_OFB *ofb);
-#endif
-
-#ifdef LTC_CBC_MODE
-int cbc_start(int cipher, const unsigned char *IV, const unsigned char *key,
- int keylen, int num_rounds, symmetric_CBC *cbc);
-int cbc_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CBC *cbc);
-int cbc_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CBC *cbc);
-int cbc_getiv(unsigned char *IV, unsigned long *len, symmetric_CBC *cbc);
-int cbc_setiv(const unsigned char *IV, unsigned long len, symmetric_CBC *cbc);
-int cbc_done(symmetric_CBC *cbc);
-#endif
-
-#ifdef LTC_CTR_MODE
-
-#define CTR_COUNTER_LITTLE_ENDIAN 0x0000
-#define CTR_COUNTER_BIG_ENDIAN 0x1000
-#define LTC_CTR_RFC3686 0x2000
-
-int ctr_start( int cipher,
- const unsigned char *IV,
- const unsigned char *key, int keylen,
- int num_rounds, int ctr_mode,
- symmetric_CTR *ctr);
-int ctr_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CTR *ctr);
-int ctr_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CTR *ctr);
-int ctr_getiv(unsigned char *IV, unsigned long *len, symmetric_CTR *ctr);
-int ctr_setiv(const unsigned char *IV, unsigned long len, symmetric_CTR *ctr);
-int ctr_done(symmetric_CTR *ctr);
-int ctr_test(void);
-#endif
-
-#ifdef LTC_LRW_MODE
-
-#define LRW_ENCRYPT 0
-#define LRW_DECRYPT 1
-
-int lrw_start( int cipher,
- const unsigned char *IV,
- const unsigned char *key, int keylen,
- const unsigned char *tweak,
- int num_rounds,
- symmetric_LRW *lrw);
-int lrw_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_LRW *lrw);
-int lrw_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_LRW *lrw);
-int lrw_getiv(unsigned char *IV, unsigned long *len, symmetric_LRW *lrw);
-int lrw_setiv(const unsigned char *IV, unsigned long len, symmetric_LRW *lrw);
-int lrw_done(symmetric_LRW *lrw);
-int lrw_test(void);
-
-/* don't call */
-int lrw_process(const unsigned char *pt, unsigned char *ct, unsigned long len, int mode, symmetric_LRW *lrw);
-#endif
-
-#ifdef LTC_F8_MODE
-int f8_start( int cipher, const unsigned char *IV,
- const unsigned char *key, int keylen,
- const unsigned char *salt_key, int skeylen,
- int num_rounds, symmetric_F8 *f8);
-int f8_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_F8 *f8);
-int f8_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_F8 *f8);
-int f8_getiv(unsigned char *IV, unsigned long *len, symmetric_F8 *f8);
-int f8_setiv(const unsigned char *IV, unsigned long len, symmetric_F8 *f8);
-int f8_done(symmetric_F8 *f8);
-int f8_test_mode(void);
-#endif
-
-#ifdef LTC_XTS_MODE
-typedef struct {
- symmetric_key key1, key2;
- int cipher;
-} symmetric_xts;
-
-int xts_start( int cipher,
- const unsigned char *key1,
- const unsigned char *key2,
- unsigned long keylen,
- int num_rounds,
- symmetric_xts *xts);
-
-int xts_encrypt(
- const unsigned char *pt, unsigned long ptlen,
- unsigned char *ct,
- const unsigned char *tweak,
- symmetric_xts *xts);
-int xts_decrypt(
- const unsigned char *ct, unsigned long ptlen,
- unsigned char *pt,
- const unsigned char *tweak,
- symmetric_xts *xts);
-
-void xts_done(symmetric_xts *xts);
-int xts_test(void);
-void xts_mult_x(unsigned char *I);
-#endif
-
-int find_cipher(const char *name);
-int find_cipher_any(const char *name, int blocklen, int keylen);
-int find_cipher_id(unsigned char ID);
-int register_cipher(const struct ltc_cipher_descriptor *cipher);
-int unregister_cipher(const struct ltc_cipher_descriptor *cipher);
-int cipher_is_valid(int idx);
-
-LTC_MUTEX_PROTO(ltc_cipher_mutex)
-
-/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cipher.h,v $ */
-/* $Revision: 1.54 $ */
-/* $Date: 2007/05/12 14:37:41 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_custom.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_custom.h
deleted file mode 100644
index 88ec8f984ab..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_custom.h
+++ /dev/null
@@ -1,424 +0,0 @@
-#ifndef TOMCRYPT_CUSTOM_H_
-#define TOMCRYPT_CUSTOM_H_
-
-#define LTC_NO_CIPHERS
-#define LTC_NO_HASHES
-#define LTC_NO_MACS
-#define LTC_NO_PRNGS
-#define LTC_NO_CURVES
-#define LTC_NO_MODES
-#define LTC_NO_PKCS
-#define LTC_NO_ROLC
-
-#define LTC_SOURCE
-#define LTC_SHA1
-#define LTC_MD5
-#define LTC_DER
-#define LTC_RC4
-
-#define USE_LTM
-#define LTM_DESC
-
-/* macros for various libc functions you can change for embedded targets */
-#ifndef XMALLOC
- #ifdef malloc
- #define LTC_NO_PROTOTYPES
- #endif
-#define XMALLOC LibTomMalloc
-#endif
-#ifndef XREALLOC
- #ifdef realloc
- #define LTC_NO_PROTOTYPES
- #endif
-#define XREALLOC LibTomRealloc
-#endif
-#ifndef XCALLOC
- #ifdef calloc
- #define LTC_NO_PROTOTYPES
- #endif
-#define XCALLOC LibTomCalloc
-#endif
-#ifndef XFREE
- #ifdef free
- #define LTC_NO_PROTOTYPES
- #endif
-#define XFREE LibTomFree
-#endif
-
-#ifndef XMEMSET
- #ifdef memset
- #define LTC_NO_PROTOTYPES
- #endif
-#define XMEMSET memset
-#endif
-#ifndef XMEMCPY
- #ifdef memcpy
- #define LTC_NO_PROTOTYPES
- #endif
-#define XMEMCPY memcpy
-#endif
-#ifndef XMEMCMP
- #ifdef memcmp
- #define LTC_NO_PROTOTYPES
- #endif
-#define XMEMCMP memcmp
-#endif
-#ifndef XSTRCMP
- #ifdef strcmp
- #define LTC_NO_PROTOTYPES
- #endif
-#define XSTRCMP strcmp
-#endif
-
-#ifndef XCLOCK
-#define XCLOCK LibTomClock
-#endif
-#ifndef XCLOCKS_PER_SEC
-#define XCLOCKS_PER_SEC CLOCKS_PER_SEC
-#endif
-
-#ifndef XQSORT
- #ifdef qsort
- #define LTC_NO_PROTOTYPES
- #endif
-#define XQSORT LibTomQsort
-#endif
-
-/* Easy button? */
-#ifdef LTC_EASY
- #define LTC_NO_CIPHERS
- #define LTC_RIJNDAEL
- #define LTC_BLOWFISH
- #define LTC_DES
- #define LTC_CAST5
-
- #define LTC_NO_MODES
- #define LTC_ECB_MODE
- #define LTC_CBC_MODE
- #define LTC_CTR_MODE
-
- #define LTC_NO_HASHES
- #define LTC_SHA1
- #define LTC_SHA512
- #define LTC_SHA384
- #define LTC_SHA256
- #define LTC_SHA224
-
- #define LTC_NO_MACS
- #define LTC_HMAC
- #define LTC_OMAC
- #define LTC_CCM_MODE
-
- #define LTC_NO_PRNGS
- #define LTC_SPRNG
- #define LTC_YARROW
- #define LTC_DEVRANDOM
- #define TRY_URANDOM_FIRST
-
- #define LTC_NO_PK
- #define LTC_MRSA
- #define LTC_MECC
-#endif
-
-/* Use small code where possible */
-/* #define LTC_SMALL_CODE */
-
-/* Enable self-test test vector checking */
-#ifndef LTC_NO_TEST
- #define LTC_TEST
-#endif
-
-/* clean the stack of functions which put private information on stack */
-/* #define LTC_CLEAN_STACK */
-
-/* disable all file related functions */
-/* #define LTC_NO_FILE */
-
-/* disable all forms of ASM */
-/* #define LTC_NO_ASM */
-
-/* disable FAST mode */
-/* #define LTC_NO_FAST */
-
-/* disable BSWAP on x86 */
-/* #define LTC_NO_BSWAP */
-
-/* ---> Symmetric Block Ciphers <--- */
-#ifndef LTC_NO_CIPHERS
-
-#define LTC_BLOWFISH
-#define LTC_RC2
-#define LTC_RC5
-#define LTC_RC6
-#define LTC_SAFERP
-#define LTC_RIJNDAEL
-#define LTC_XTEA
-/* _TABLES tells it to use tables during setup, _SMALL means to use the smaller scheduled key format
- * (saves 4KB of ram), _ALL_TABLES enables all tables during setup */
-#define LTC_TWOFISH
-#ifndef LTC_NO_TABLES
- #define LTC_TWOFISH_TABLES
- /* #define LTC_TWOFISH_ALL_TABLES */
-#else
- #define LTC_TWOFISH_SMALL
-#endif
-/* #define LTC_TWOFISH_SMALL */
-/* LTC_DES includes EDE triple-LTC_DES */
-#define LTC_DES
-#define LTC_CAST5
-#define LTC_NOEKEON
-#define LTC_SKIPJACK
-#define LTC_SAFER
-#define LTC_KHAZAD
-#define LTC_ANUBIS
-#define LTC_ANUBIS_TWEAK
-#define LTC_KSEED
-#define LTC_KASUMI
-
-#endif /* LTC_NO_CIPHERS */
-
-
-/* ---> Block Cipher Modes of Operation <--- */
-#ifndef LTC_NO_MODES
-
-#define LTC_CFB_MODE
-#define LTC_OFB_MODE
-#define LTC_ECB_MODE
-#define LTC_CBC_MODE
-#define LTC_CTR_MODE
-
-/* F8 chaining mode */
-#define LTC_F8_MODE
-
-/* LRW mode */
-#define LTC_LRW_MODE
-#ifndef LTC_NO_TABLES
- /* like GCM mode this will enable 16 8x128 tables [64KB] that make
- * seeking very fast.
- */
- #define LRW_TABLES
-#endif
-
-/* XTS mode */
-#define LTC_XTS_MODE
-
-#endif /* LTC_NO_MODES */
-
-/* ---> One-Way Hash Functions <--- */
-#ifndef LTC_NO_HASHES
-
-#define LTC_CHC_HASH
-#define LTC_WHIRLPOOL
-#define LTC_SHA512
-#define LTC_SHA384
-#define LTC_SHA256
-#define LTC_SHA224
-#define LTC_TIGER
-#define LTC_SHA1
-#define LTC_MD5
-#define LTC_MD4
-#define LTC_MD2
-#define LTC_RIPEMD128
-#define LTC_RIPEMD160
-#define LTC_RIPEMD256
-#define LTC_RIPEMD320
-
-#endif /* LTC_NO_HASHES */
-
-/* ---> MAC functions <--- */
-#ifndef LTC_NO_MACS
-
-#define LTC_HMAC
-#define LTC_OMAC
-#define LTC_PMAC
-#define LTC_XCBC
-#define LTC_F9_MODE
-#define LTC_PELICAN
-
-#if defined(LTC_PELICAN) && !defined(LTC_RIJNDAEL)
- #error Pelican-MAC requires LTC_RIJNDAEL
-#endif
-
-/* ---> Encrypt + Authenticate Modes <--- */
-
-#define LTC_EAX_MODE
-#if defined(LTC_EAX_MODE) && !(defined(LTC_CTR_MODE) && defined(LTC_OMAC))
- #error LTC_EAX_MODE requires CTR and LTC_OMAC mode
-#endif
-
-#define LTC_OCB_MODE
-#define LTC_CCM_MODE
-#define LTC_GCM_MODE
-
-/* Use 64KiB tables */
-#ifndef LTC_NO_TABLES
- #define LTC_GCM_TABLES
-#endif
-
-/* USE SSE2? requires GCC works on x86_32 and x86_64*/
-#ifdef LTC_GCM_TABLES
-/* #define LTC_GCM_TABLES_SSE2 */
-#endif
-
-#endif /* LTC_NO_MACS */
-
-/* Various tidbits of modern neatoness */
-#define LTC_BASE64
-
-/* --> Pseudo Random Number Generators <--- */
-#ifndef LTC_NO_PRNGS
-
-/* Yarrow */
-#define LTC_YARROW
-/* which descriptor of AES to use? */
-/* 0 = rijndael_enc 1 = aes_enc, 2 = rijndael [full], 3 = aes [full] */
-#define LTC_YARROW_AES 0
-
-#if defined(LTC_YARROW) && !defined(LTC_CTR_MODE)
- #error LTC_YARROW requires LTC_CTR_MODE chaining mode to be defined!
-#endif
-
-/* a PRNG that simply reads from an available system source */
-#define LTC_SPRNG
-
-/* The LTC_RC4 stream cipher */
-#define LTC_RC4
-
-/* Fortuna PRNG */
-#define LTC_FORTUNA
-/* reseed every N calls to the read function */
-#define LTC_FORTUNA_WD 10
-/* number of pools (4..32) can save a bit of ram by lowering the count */
-#define LTC_FORTUNA_POOLS 32
-
-/* Greg's LTC_SOBER128 PRNG ;-0 */
-#define LTC_SOBER128
-
-/* the *nix style /dev/random device */
-#define LTC_DEVRANDOM
-/* try /dev/urandom before trying /dev/random */
-#define TRY_URANDOM_FIRST
-
-#endif /* LTC_NO_PRNGS */
-
-/* ---> math provider? <--- */
-#ifndef LTC_NO_MATH
-
-/* LibTomMath */
-#define LTM_LTC_DESC
-
-/* TomsFastMath */
-//#define TFM_LTC_DESC
-
-#endif /* LTC_NO_MATH */
-
-/* ---> Public Key Crypto <--- */
-#ifndef LTC_NO_PK
-
-/* Include RSA support */
-#define LTC_MRSA
-
-/* Include Katja (a Rabin variant like RSA) */
-/* #define MKAT */
-
-/* Digital Signature Algorithm */
-#define LTC_MDSA
-
-/* ECC */
-#define LTC_MECC
-
-/* use Shamir's trick for point mul (speeds up signature verification) */
-#define LTC_ECC_SHAMIR
-
-#if defined(TFM_LTC_DESC) && defined(LTC_MECC)
- #define LTC_MECC_ACCEL
-#endif
-
-/* do we want fixed point ECC */
-/* #define LTC_MECC_FP */
-
-/* Timing Resistant? */
-/* #define LTC_ECC_TIMING_RESISTANT */
-
-#endif /* LTC_NO_PK */
-
-/* LTC_PKCS #1 (RSA) and #5 (Password Handling) stuff */
-#ifndef LTC_NO_PKCS
-
-#define LTC_PKCS_1
-#define LTC_PKCS_5
-
-/* Include ASN.1 DER (required by DSA/RSA) */
-#define LTC_DER
-
-#endif /* LTC_NO_PKCS */
-
-/* cleanup */
-
-#ifdef LTC_MECC
-/* Supported ECC Key Sizes */
-#ifndef LTC_NO_CURVES
- #define ECC112
- #define ECC128
- #define ECC160
- #define ECC192
- #define ECC224
- #define ECC256
- #define ECC384
- #define ECC521
-#endif
-#endif
-
-#if defined(LTC_MECC) || defined(LTC_MRSA) || defined(LTC_MDSA) || defined(MKATJA)
- /* Include the MPI functionality? (required by the PK algorithms) */
- #define MPI
-#endif
-
-#ifdef LTC_MRSA
- #define LTC_PKCS_1
-#endif
-
-#if defined(LTC_DER) && !defined(MPI)
- #error ASN.1 DER requires MPI functionality
-#endif
-
-#if (defined(LTC_MDSA) || defined(LTC_MRSA) || defined(LTC_MECC) || defined(MKATJA)) && !defined(LTC_DER)
- #error PK requires ASN.1 DER functionality, make sure LTC_DER is enabled
-#endif
-
-/* THREAD management */
-#ifdef LTC_PTHREAD
-
-#include <pthread.h>
-
-#define LTC_MUTEX_GLOBAL(x) pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER;
-#define LTC_MUTEX_PROTO(x) extern pthread_mutex_t x;
-#define LTC_MUTEX_TYPE(x) pthread_mutex_t x;
-#define LTC_MUTEX_INIT(x) pthread_mutex_init(x, NULL);
-#define LTC_MUTEX_LOCK(x) pthread_mutex_lock(x);
-#define LTC_MUTEX_UNLOCK(x) pthread_mutex_unlock(x);
-
-#else
-
-/* default no functions */
-#define LTC_MUTEX_GLOBAL(x)
-#define LTC_MUTEX_PROTO(x)
-#define LTC_MUTEX_TYPE(x)
-#define LTC_MUTEX_INIT(x)
-#define LTC_MUTEX_LOCK(x)
-#define LTC_MUTEX_UNLOCK(x)
-
-#endif
-
-/* Debuggers */
-
-/* define this if you use Valgrind, note: it CHANGES the way SOBER-128 and LTC_RC4 work (see the code) */
-/* #define LTC_VALGRIND */
-
-#endif
-
-
-
-/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_custom.h,v $ */
-/* $Revision: 1.73 $ */
-/* $Date: 2007/05/12 14:37:41 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_hash.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_hash.h
deleted file mode 100644
index 18553ebf9da..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_hash.h
+++ /dev/null
@@ -1,378 +0,0 @@
-/* ---- HASH FUNCTIONS ---- */
-#ifdef LTC_SHA512
-struct sha512_state {
- ulong64 length, state[8];
- unsigned long curlen;
- unsigned char buf[128];
-};
-#endif
-
-#ifdef LTC_SHA256
-struct sha256_state {
- ulong64 length;
- ulong32 state[8], curlen;
- unsigned char buf[64];
-};
-#endif
-
-#ifdef LTC_SHA1
-struct sha1_state {
- ulong64 length;
- ulong32 state[5], curlen;
- unsigned char buf[64];
-};
-#endif
-
-#ifdef LTC_MD5
-struct md5_state {
- ulong64 length;
- ulong32 state[4], curlen;
- unsigned char buf[64];
-};
-#endif
-
-#ifdef LTC_MD4
-struct md4_state {
- ulong64 length;
- ulong32 state[4], curlen;
- unsigned char buf[64];
-};
-#endif
-
-#ifdef LTC_TIGER
-struct tiger_state {
- ulong64 state[3], length;
- unsigned long curlen;
- unsigned char buf[64];
-};
-#endif
-
-#ifdef LTC_MD2
-struct md2_state {
- unsigned char chksum[16], X[48], buf[16];
- unsigned long curlen;
-};
-#endif
-
-#ifdef LTC_RIPEMD128
-struct rmd128_state {
- ulong64 length;
- unsigned char buf[64];
- ulong32 curlen, state[4];
-};
-#endif
-
-#ifdef LTC_RIPEMD160
-struct rmd160_state {
- ulong64 length;
- unsigned char buf[64];
- ulong32 curlen, state[5];
-};
-#endif
-
-#ifdef LTC_RIPEMD256
-struct rmd256_state {
- ulong64 length;
- unsigned char buf[64];
- ulong32 curlen, state[8];
-};
-#endif
-
-#ifdef LTC_RIPEMD320
-struct rmd320_state {
- ulong64 length;
- unsigned char buf[64];
- ulong32 curlen, state[10];
-};
-#endif
-
-#ifdef LTC_WHIRLPOOL
-struct whirlpool_state {
- ulong64 length, state[8];
- unsigned char buf[64];
- ulong32 curlen;
-};
-#endif
-
-#ifdef LTC_CHC_HASH
-struct chc_state {
- ulong64 length;
- unsigned char state[MAXBLOCKSIZE], buf[MAXBLOCKSIZE];
- ulong32 curlen;
-};
-#endif
-
-typedef union Hash_state {
-#ifdef LTC_CHC_HASH
- struct chc_state chc;
-#endif
-#ifdef LTC_WHIRLPOOL
- struct whirlpool_state whirlpool;
-#endif
-#ifdef LTC_SHA512
- struct sha512_state sha512;
-#endif
-#ifdef LTC_SHA256
- struct sha256_state sha256;
-#endif
-#ifdef LTC_SHA1
- struct sha1_state sha1;
-#endif
-#ifdef LTC_MD5
- struct md5_state md5;
-#endif
-#ifdef LTC_MD4
- struct md4_state md4;
-#endif
-#ifdef LTC_MD2
- struct md2_state md2;
-#endif
-#ifdef LTC_TIGER
- struct tiger_state tiger;
-#endif
-#ifdef LTC_RIPEMD128
- struct rmd128_state rmd128;
-#endif
-#ifdef LTC_RIPEMD160
- struct rmd160_state rmd160;
-#endif
-#ifdef LTC_RIPEMD256
- struct rmd256_state rmd256;
-#endif
-#ifdef LTC_RIPEMD320
- struct rmd320_state rmd320;
-#endif
- void *data;
-} hash_state;
-
-/** hash descriptor */
-extern struct ltc_hash_descriptor {
- /** name of hash */
- char *name;
- /** internal ID */
- unsigned char ID;
- /** Size of digest in octets */
- unsigned long hashsize;
- /** Input block size in octets */
- unsigned long blocksize;
- /** ASN.1 OID */
- unsigned long OID[16];
- /** Length of DER encoding */
- unsigned long OIDlen;
-
- /** Init a hash state
- @param hash The hash to initialize
- @return CRYPT_OK if successful
- */
- int (*init)(hash_state *hash);
- /** Process a block of data
- @param hash The hash state
- @param in The data to hash
- @param inlen The length of the data (octets)
- @return CRYPT_OK if successful
- */
- int (*process)(hash_state *hash, const unsigned char *in, unsigned long inlen);
- /** Produce the digest and store it
- @param hash The hash state
- @param out [out] The destination of the digest
- @return CRYPT_OK if successful
- */
- int (*done)(hash_state *hash, unsigned char *out);
- /** Self-test
- @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
- */
- int (*test)(void);
-
- /* accelerated hmac callback: if you need to-do multiple packets just use the generic hmac_memory and provide a hash callback */
- int (*hmac_block)(const unsigned char *key, unsigned long keylen,
- const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-
-} hash_descriptor[];
-
-#ifdef LTC_CHC_HASH
-int chc_register(int cipher);
-int chc_init(hash_state * md);
-int chc_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int chc_done(hash_state * md, unsigned char *hash);
-int chc_test(void);
-extern const struct ltc_hash_descriptor chc_desc;
-#endif
-
-#ifdef LTC_WHIRLPOOL
-int whirlpool_init(hash_state * md);
-int whirlpool_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int whirlpool_done(hash_state * md, unsigned char *hash);
-int whirlpool_test(void);
-extern const struct ltc_hash_descriptor whirlpool_desc;
-#endif
-
-#ifdef LTC_SHA512
-int sha512_init(hash_state * md);
-int sha512_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int sha512_done(hash_state * md, unsigned char *hash);
-int sha512_test(void);
-extern const struct ltc_hash_descriptor sha512_desc;
-#endif
-
-#ifdef LTC_SHA384
-#ifndef LTC_SHA512
- #error LTC_SHA512 is required for LTC_SHA384
-#endif
-int sha384_init(hash_state * md);
-#define sha384_process sha512_process
-int sha384_done(hash_state * md, unsigned char *hash);
-int sha384_test(void);
-extern const struct ltc_hash_descriptor sha384_desc;
-#endif
-
-#ifdef LTC_SHA256
-int sha256_init(hash_state * md);
-int sha256_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int sha256_done(hash_state * md, unsigned char *hash);
-int sha256_test(void);
-extern const struct ltc_hash_descriptor sha256_desc;
-
-#ifdef LTC_SHA224
-#ifndef LTC_SHA256
- #error LTC_SHA256 is required for LTC_SHA224
-#endif
-int sha224_init(hash_state * md);
-#define sha224_process sha256_process
-int sha224_done(hash_state * md, unsigned char *hash);
-int sha224_test(void);
-extern const struct ltc_hash_descriptor sha224_desc;
-#endif
-#endif
-
-#ifdef LTC_SHA1
-int sha1_init(hash_state * md);
-int sha1_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int sha1_done(hash_state * md, unsigned char *hash);
-int sha1_test(void);
-extern const struct ltc_hash_descriptor sha1_desc;
-#endif
-
-#ifdef LTC_MD5
-int md5_init(hash_state * md);
-int md5_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int md5_done(hash_state * md, unsigned char *hash);
-int md5_test(void);
-extern const struct ltc_hash_descriptor md5_desc;
-#endif
-
-#ifdef LTC_MD4
-int md4_init(hash_state * md);
-int md4_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int md4_done(hash_state * md, unsigned char *hash);
-int md4_test(void);
-extern const struct ltc_hash_descriptor md4_desc;
-#endif
-
-#ifdef LTC_MD2
-int md2_init(hash_state * md);
-int md2_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int md2_done(hash_state * md, unsigned char *hash);
-int md2_test(void);
-extern const struct ltc_hash_descriptor md2_desc;
-#endif
-
-#ifdef LTC_TIGER
-int tiger_init(hash_state * md);
-int tiger_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int tiger_done(hash_state * md, unsigned char *hash);
-int tiger_test(void);
-extern const struct ltc_hash_descriptor tiger_desc;
-#endif
-
-#ifdef LTC_RIPEMD128
-int rmd128_init(hash_state * md);
-int rmd128_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int rmd128_done(hash_state * md, unsigned char *hash);
-int rmd128_test(void);
-extern const struct ltc_hash_descriptor rmd128_desc;
-#endif
-
-#ifdef LTC_RIPEMD160
-int rmd160_init(hash_state * md);
-int rmd160_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int rmd160_done(hash_state * md, unsigned char *hash);
-int rmd160_test(void);
-extern const struct ltc_hash_descriptor rmd160_desc;
-#endif
-
-#ifdef LTC_RIPEMD256
-int rmd256_init(hash_state * md);
-int rmd256_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int rmd256_done(hash_state * md, unsigned char *hash);
-int rmd256_test(void);
-extern const struct ltc_hash_descriptor rmd256_desc;
-#endif
-
-#ifdef LTC_RIPEMD320
-int rmd320_init(hash_state * md);
-int rmd320_process(hash_state * md, const unsigned char *in, unsigned long inlen);
-int rmd320_done(hash_state * md, unsigned char *hash);
-int rmd320_test(void);
-extern const struct ltc_hash_descriptor rmd320_desc;
-#endif
-
-
-int find_hash(const char *name);
-int find_hash_id(unsigned char ID);
-int find_hash_oid(const unsigned long *ID, unsigned long IDlen);
-int find_hash_any(const char *name, int digestlen);
-int register_hash(const struct ltc_hash_descriptor *hash);
-int unregister_hash(const struct ltc_hash_descriptor *hash);
-int hash_is_valid(int idx);
-
-LTC_MUTEX_PROTO(ltc_hash_mutex)
-
-int hash_memory(int hash,
- const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-int hash_memory_multi(int hash, unsigned char *out, unsigned long *outlen,
- const unsigned char *in, unsigned long inlen, ...);
-int hash_filehandle(int hash, FILE *in, unsigned char *out, unsigned long *outlen);
-int hash_file(int hash, const char *fname, unsigned char *out, unsigned long *outlen);
-
-/* a simple macro for making hash "process" functions */
-#define HASH_PROCESS(func_name, compress_name, state_var, block_size) \
-int func_name (hash_state * md, const unsigned char *in, unsigned long inlen) \
-{ \
- unsigned long n; \
- int err; \
- LTC_ARGCHK(md != NULL); \
- LTC_ARGCHK(in != NULL); \
- if (md-> state_var .curlen > sizeof(md-> state_var .buf)) { \
- return CRYPT_INVALID_ARG; \
- } \
- while (inlen > 0) { \
- if (md-> state_var .curlen == 0 && inlen >= block_size) { \
- if ((err = compress_name (md, (unsigned char *)in)) != CRYPT_OK) { \
- return err; \
- } \
- md-> state_var .length += block_size * 8; \
- in += block_size; \
- inlen -= block_size; \
- } else { \
- n = MIN(inlen, (block_size - md-> state_var .curlen)); \
- memcpy(md-> state_var .buf + md-> state_var.curlen, in, (size_t)n); \
- md-> state_var .curlen += n; \
- in += n; \
- inlen -= n; \
- if (md-> state_var .curlen == block_size) { \
- if ((err = compress_name (md, md-> state_var .buf)) != CRYPT_OK) { \
- return err; \
- } \
- md-> state_var .length += 8*block_size; \
- md-> state_var .curlen = 0; \
- } \
- } \
- } \
- return CRYPT_OK; \
-}
-
-/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_hash.h,v $ */
-/* $Revision: 1.22 $ */
-/* $Date: 2007/05/12 14:32:35 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_mac.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_mac.h
deleted file mode 100644
index 7ad9516bd29..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_mac.h
+++ /dev/null
@@ -1,384 +0,0 @@
-#ifdef LTC_HMAC
-typedef struct Hmac_state {
- hash_state md;
- int hash;
- hash_state hashstate;
- unsigned char *key;
-} hmac_state;
-
-int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen);
-int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen);
-int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen);
-int hmac_test(void);
-int hmac_memory(int hash,
- const unsigned char *key, unsigned long keylen,
- const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-int hmac_memory_multi(int hash,
- const unsigned char *key, unsigned long keylen,
- unsigned char *out, unsigned long *outlen,
- const unsigned char *in, unsigned long inlen, ...);
-int hmac_file(int hash, const char *fname, const unsigned char *key,
- unsigned long keylen,
- unsigned char *dst, unsigned long *dstlen);
-#endif
-
-#ifdef LTC_OMAC
-
-typedef struct {
- int cipher_idx,
- buflen,
- blklen;
- unsigned char block[MAXBLOCKSIZE],
- prev[MAXBLOCKSIZE],
- Lu[2][MAXBLOCKSIZE];
- symmetric_key key;
-} omac_state;
-
-int omac_init(omac_state *omac, int cipher, const unsigned char *key, unsigned long keylen);
-int omac_process(omac_state *omac, const unsigned char *in, unsigned long inlen);
-int omac_done(omac_state *omac, unsigned char *out, unsigned long *outlen);
-int omac_memory(int cipher,
- const unsigned char *key, unsigned long keylen,
- const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-int omac_memory_multi(int cipher,
- const unsigned char *key, unsigned long keylen,
- unsigned char *out, unsigned long *outlen,
- const unsigned char *in, unsigned long inlen, ...);
-int omac_file(int cipher,
- const unsigned char *key, unsigned long keylen,
- const char *filename,
- unsigned char *out, unsigned long *outlen);
-int omac_test(void);
-#endif /* LTC_OMAC */
-
-#ifdef LTC_PMAC
-
-typedef struct {
- unsigned char Ls[32][MAXBLOCKSIZE], /* L shifted by i bits to the left */
- Li[MAXBLOCKSIZE], /* value of Li [current value, we calc from previous recall] */
- Lr[MAXBLOCKSIZE], /* L * x^-1 */
- block[MAXBLOCKSIZE], /* currently accumulated block */
- checksum[MAXBLOCKSIZE]; /* current checksum */
-
- symmetric_key key; /* scheduled key for cipher */
- unsigned long block_index; /* index # for current block */
- int cipher_idx, /* cipher idx */
- block_len, /* length of block */
- buflen; /* number of bytes in the buffer */
-} pmac_state;
-
-int pmac_init(pmac_state *pmac, int cipher, const unsigned char *key, unsigned long keylen);
-int pmac_process(pmac_state *pmac, const unsigned char *in, unsigned long inlen);
-int pmac_done(pmac_state *pmac, unsigned char *out, unsigned long *outlen);
-
-int pmac_memory(int cipher,
- const unsigned char *key, unsigned long keylen,
- const unsigned char *msg, unsigned long msglen,
- unsigned char *out, unsigned long *outlen);
-
-int pmac_memory_multi(int cipher,
- const unsigned char *key, unsigned long keylen,
- unsigned char *out, unsigned long *outlen,
- const unsigned char *in, unsigned long inlen, ...);
-
-int pmac_file(int cipher,
- const unsigned char *key, unsigned long keylen,
- const char *filename,
- unsigned char *out, unsigned long *outlen);
-
-int pmac_test(void);
-
-/* internal functions */
-int pmac_ntz(unsigned long x);
-void pmac_shift_xor(pmac_state *pmac);
-
-#endif /* PMAC */
-
-#ifdef LTC_EAX_MODE
-
-#if !(defined(LTC_OMAC) && defined(LTC_CTR_MODE))
- #error LTC_EAX_MODE requires LTC_OMAC and CTR
-#endif
-
-typedef struct {
- unsigned char N[MAXBLOCKSIZE];
- symmetric_CTR ctr;
- omac_state headeromac, ctomac;
-} eax_state;
-
-int eax_init(eax_state *eax, int cipher, const unsigned char *key, unsigned long keylen,
- const unsigned char *nonce, unsigned long noncelen,
- const unsigned char *header, unsigned long headerlen);
-
-int eax_encrypt(eax_state *eax, const unsigned char *pt, unsigned char *ct, unsigned long length);
-int eax_decrypt(eax_state *eax, const unsigned char *ct, unsigned char *pt, unsigned long length);
-int eax_addheader(eax_state *eax, const unsigned char *header, unsigned long length);
-int eax_done(eax_state *eax, unsigned char *tag, unsigned long *taglen);
-
-int eax_encrypt_authenticate_memory(int cipher,
- const unsigned char *key, unsigned long keylen,
- const unsigned char *nonce, unsigned long noncelen,
- const unsigned char *header, unsigned long headerlen,
- const unsigned char *pt, unsigned long ptlen,
- unsigned char *ct,
- unsigned char *tag, unsigned long *taglen);
-
-int eax_decrypt_verify_memory(int cipher,
- const unsigned char *key, unsigned long keylen,
- const unsigned char *nonce, unsigned long noncelen,
- const unsigned char *header, unsigned long headerlen,
- const unsigned char *ct, unsigned long ctlen,
- unsigned char *pt,
- unsigned char *tag, unsigned long taglen,
- int *stat);
-
- int eax_test(void);
-#endif /* EAX MODE */
-
-#ifdef LTC_OCB_MODE
-typedef struct {
- unsigned char L[MAXBLOCKSIZE], /* L value */
- Ls[32][MAXBLOCKSIZE], /* L shifted by i bits to the left */
- Li[MAXBLOCKSIZE], /* value of Li [current value, we calc from previous recall] */
- Lr[MAXBLOCKSIZE], /* L * x^-1 */
- R[MAXBLOCKSIZE], /* R value */
- checksum[MAXBLOCKSIZE]; /* current checksum */
-
- symmetric_key key; /* scheduled key for cipher */
- unsigned long block_index; /* index # for current block */
- int cipher, /* cipher idx */
- block_len; /* length of block */
-} ocb_state;
-
-int ocb_init(ocb_state *ocb, int cipher,
- const unsigned char *key, unsigned long keylen, const unsigned char *nonce);
-
-int ocb_encrypt(ocb_state *ocb, const unsigned char *pt, unsigned char *ct);
-int ocb_decrypt(ocb_state *ocb, const unsigned char *ct, unsigned char *pt);
-
-int ocb_done_encrypt(ocb_state *ocb,
- const unsigned char *pt, unsigned long ptlen,
- unsigned char *ct,
- unsigned char *tag, unsigned long *taglen);
-
-int ocb_done_decrypt(ocb_state *ocb,
- const unsigned char *ct, unsigned long ctlen,
- unsigned char *pt,
- const unsigned char *tag, unsigned long taglen, int *stat);
-
-int ocb_encrypt_authenticate_memory(int cipher,
- const unsigned char *key, unsigned long keylen,
- const unsigned char *nonce,
- const unsigned char *pt, unsigned long ptlen,
- unsigned char *ct,
- unsigned char *tag, unsigned long *taglen);
-
-int ocb_decrypt_verify_memory(int cipher,
- const unsigned char *key, unsigned long keylen,
- const unsigned char *nonce,
- const unsigned char *ct, unsigned long ctlen,
- unsigned char *pt,
- const unsigned char *tag, unsigned long taglen,
- int *stat);
-
-int ocb_test(void);
-
-/* internal functions */
-void ocb_shift_xor(ocb_state *ocb, unsigned char *Z);
-int ocb_ntz(unsigned long x);
-int s_ocb_done(ocb_state *ocb, const unsigned char *pt, unsigned long ptlen,
- unsigned char *ct, unsigned char *tag, unsigned long *taglen, int mode);
-
-#endif /* LTC_OCB_MODE */
-
-#ifdef LTC_CCM_MODE
-
-#define CCM_ENCRYPT 0
-#define CCM_DECRYPT 1
-
-int ccm_memory(int cipher,
- const unsigned char *key, unsigned long keylen,
- symmetric_key *uskey,
- const unsigned char *nonce, unsigned long noncelen,
- const unsigned char *header, unsigned long headerlen,
- unsigned char *pt, unsigned long ptlen,
- unsigned char *ct,
- unsigned char *tag, unsigned long *taglen,
- int direction);
-
-int ccm_test(void);
-
-#endif /* LTC_CCM_MODE */
-
-#if defined(LRW_MODE) || defined(LTC_GCM_MODE)
-void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c);
-#endif
-
-
-/* table shared between GCM and LRW */
-#if defined(LTC_GCM_TABLES) || defined(LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST))
-extern const unsigned char gcm_shift_table[];
-#endif
-
-#ifdef LTC_GCM_MODE
-
-#define GCM_ENCRYPT 0
-#define GCM_DECRYPT 1
-
-#define LTC_GCM_MODE_IV 0
-#define LTC_GCM_MODE_AAD 1
-#define LTC_GCM_MODE_TEXT 2
-
-typedef struct {
- symmetric_key K;
- unsigned char H[16], /* multiplier */
- X[16], /* accumulator */
- Y[16], /* counter */
- Y_0[16], /* initial counter */
- buf[16]; /* buffer for stuff */
-
- int cipher, /* which cipher */
- ivmode, /* Which mode is the IV in? */
- mode, /* mode the GCM code is in */
- buflen; /* length of data in buf */
-
- ulong64 totlen, /* 64-bit counter used for IV and AAD */
- pttotlen; /* 64-bit counter for the PT */
-
-#ifdef LTC_GCM_TABLES
- unsigned char PC[16][256][16] /* 16 tables of 8x128 */
-#ifdef LTC_GCM_TABLES_SSE2
-__attribute__ ((aligned (16)))
-#endif
-;
-#endif
-} gcm_state;
-
-void gcm_mult_h(gcm_state *gcm, unsigned char *I);
-
-int gcm_init(gcm_state *gcm, int cipher,
- const unsigned char *key, int keylen);
-
-int gcm_reset(gcm_state *gcm);
-
-int gcm_add_iv(gcm_state *gcm,
- const unsigned char *IV, unsigned long IVlen);
-
-int gcm_add_aad(gcm_state *gcm,
- const unsigned char *adata, unsigned long adatalen);
-
-int gcm_process(gcm_state *gcm,
- unsigned char *pt, unsigned long ptlen,
- unsigned char *ct,
- int direction);
-
-int gcm_done(gcm_state *gcm,
- unsigned char *tag, unsigned long *taglen);
-
-int gcm_memory( int cipher,
- const unsigned char *key, unsigned long keylen,
- const unsigned char *IV, unsigned long IVlen,
- const unsigned char *adata, unsigned long adatalen,
- unsigned char *pt, unsigned long ptlen,
- unsigned char *ct,
- unsigned char *tag, unsigned long *taglen,
- int direction);
-int gcm_test(void);
-
-#endif /* LTC_GCM_MODE */
-
-#ifdef LTC_PELICAN
-
-typedef struct pelican_state
-{
- symmetric_key K;
- unsigned char state[16];
- int buflen;
-} pelican_state;
-
-int pelican_init(pelican_state *pelmac, const unsigned char *key, unsigned long keylen);
-int pelican_process(pelican_state *pelmac, const unsigned char *in, unsigned long inlen);
-int pelican_done(pelican_state *pelmac, unsigned char *out);
-int pelican_test(void);
-
-int pelican_memory(const unsigned char *key, unsigned long keylen,
- const unsigned char *in, unsigned long inlen,
- unsigned char *out);
-
-#endif
-
-#ifdef LTC_XCBC
-
-/* add this to "keylen" to xcbc_init to use a pure three-key XCBC MAC */
-#define LTC_XCBC_PURE 0x8000UL
-
-typedef struct {
- unsigned char K[3][MAXBLOCKSIZE],
- IV[MAXBLOCKSIZE];
-
- symmetric_key key;
-
- int cipher,
- buflen,
- blocksize;
-} xcbc_state;
-
-int xcbc_init(xcbc_state *xcbc, int cipher, const unsigned char *key, unsigned long keylen);
-int xcbc_process(xcbc_state *xcbc, const unsigned char *in, unsigned long inlen);
-int xcbc_done(xcbc_state *xcbc, unsigned char *out, unsigned long *outlen);
-int xcbc_memory(int cipher,
- const unsigned char *key, unsigned long keylen,
- const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-int xcbc_memory_multi(int cipher,
- const unsigned char *key, unsigned long keylen,
- unsigned char *out, unsigned long *outlen,
- const unsigned char *in, unsigned long inlen, ...);
-int xcbc_file(int cipher,
- const unsigned char *key, unsigned long keylen,
- const char *filename,
- unsigned char *out, unsigned long *outlen);
-int xcbc_test(void);
-
-#endif
-
-#ifdef LTC_F9_MODE
-
-typedef struct {
- unsigned char akey[MAXBLOCKSIZE],
- ACC[MAXBLOCKSIZE],
- IV[MAXBLOCKSIZE];
-
- symmetric_key key;
-
- int cipher,
- buflen,
- keylen,
- blocksize;
-} f9_state;
-
-int f9_init(f9_state *f9, int cipher, const unsigned char *key, unsigned long keylen);
-int f9_process(f9_state *f9, const unsigned char *in, unsigned long inlen);
-int f9_done(f9_state *f9, unsigned char *out, unsigned long *outlen);
-int f9_memory(int cipher,
- const unsigned char *key, unsigned long keylen,
- const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-int f9_memory_multi(int cipher,
- const unsigned char *key, unsigned long keylen,
- unsigned char *out, unsigned long *outlen,
- const unsigned char *in, unsigned long inlen, ...);
-int f9_file(int cipher,
- const unsigned char *key, unsigned long keylen,
- const char *filename,
- unsigned char *out, unsigned long *outlen);
-int f9_test(void);
-
-#endif
-
-
-/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_mac.h,v $ */
-/* $Revision: 1.23 $ */
-/* $Date: 2007/05/12 14:37:41 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_macros.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_macros.h
deleted file mode 100644
index 53bda9bb4ba..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_macros.h
+++ /dev/null
@@ -1,424 +0,0 @@
-/* fix for MSVC ...evil! */
-#ifdef _MSC_VER
- #define CONST64(n) n ## ui64
- typedef unsigned __int64 ulong64;
-#else
- #define CONST64(n) n ## ULL
- typedef unsigned long long ulong64;
-#endif
-
-/* this is the "32-bit at least" data type
- * Re-define it to suit your platform but it must be at least 32-bits
- */
-#if defined(__x86_64__) || (defined(__sparc__) && defined(__arch64__))
- typedef unsigned ulong32;
-#else
- typedef unsigned long ulong32;
-#endif
-
-/* ---- HELPER MACROS ---- */
-#ifdef ENDIAN_NEUTRAL
-
-#define STORE32L(x, y) \
- { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \
- (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); }
-
-#define LOAD32L(x, y) \
- { x = ((unsigned long)((y)[3] & 255)<<24) | \
- ((unsigned long)((y)[2] & 255)<<16) | \
- ((unsigned long)((y)[1] & 255)<<8) | \
- ((unsigned long)((y)[0] & 255)); }
-
-#define STORE64L(x, y) \
- { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255); \
- (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255); \
- (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \
- (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); }
-
-#define LOAD64L(x, y) \
- { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48)| \
- (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32)| \
- (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16)| \
- (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); }
-
-#define STORE32H(x, y) \
- { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \
- (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); }
-
-#define LOAD32H(x, y) \
- { x = ((unsigned long)((y)[0] & 255)<<24) | \
- ((unsigned long)((y)[1] & 255)<<16) | \
- ((unsigned long)((y)[2] & 255)<<8) | \
- ((unsigned long)((y)[3] & 255)); }
-
-#define STORE64H(x, y) \
- { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \
- (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \
- (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \
- (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
-
-#define LOAD64H(x, y) \
- { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48) | \
- (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32) | \
- (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16) | \
- (((ulong64)((y)[6] & 255))<<8)|(((ulong64)((y)[7] & 255))); }
-
-#endif /* ENDIAN_NEUTRAL */
-
-#ifdef ENDIAN_LITTLE
-
-#if !defined(LTC_NO_BSWAP) && (defined(INTEL_CC) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__) || defined(__x86_64__))))
-
-#define STORE32H(x, y) \
-asm __volatile__ ( \
- "bswapl %0 \n\t" \
- "movl %0,(%1)\n\t" \
- "bswapl %0 \n\t" \
- ::"r"(x), "r"(y));
-
-#define LOAD32H(x, y) \
-asm __volatile__ ( \
- "movl (%1),%0\n\t" \
- "bswapl %0\n\t" \
- :"=r"(x): "r"(y));
-
-#else
-
-#define STORE32H(x, y) \
- { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \
- (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); }
-
-#define LOAD32H(x, y) \
- { x = ((unsigned long)((y)[0] & 255)<<24) | \
- ((unsigned long)((y)[1] & 255)<<16) | \
- ((unsigned long)((y)[2] & 255)<<8) | \
- ((unsigned long)((y)[3] & 255)); }
-
-#endif
-
-
-/* x86_64 processor */
-#if !defined(LTC_NO_BSWAP) && (defined(__GNUC__) && defined(__x86_64__))
-
-#define STORE64H(x, y) \
-asm __volatile__ ( \
- "bswapq %0 \n\t" \
- "movq %0,(%1)\n\t" \
- "bswapq %0 \n\t" \
- ::"r"(x), "r"(y));
-
-#define LOAD64H(x, y) \
-asm __volatile__ ( \
- "movq (%1),%0\n\t" \
- "bswapq %0\n\t" \
- :"=r"(x): "r"(y));
-
-#else
-
-#define STORE64H(x, y) \
- { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \
- (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \
- (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \
- (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
-
-#define LOAD64H(x, y) \
- { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48) | \
- (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32) | \
- (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16) | \
- (((ulong64)((y)[6] & 255))<<8)|(((ulong64)((y)[7] & 255))); }
-
-#endif
-
-#ifdef ENDIAN_32BITWORD
-
-#define STORE32L(x, y) \
- { ulong32 __t = (x); XMEMCPY(y, &__t, 4); }
-
-#define LOAD32L(x, y) \
- XMEMCPY(&(x), y, 4);
-
-#define STORE64L(x, y) \
- { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255); \
- (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255); \
- (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \
- (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); }
-
-#define LOAD64L(x, y) \
- { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48)| \
- (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32)| \
- (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16)| \
- (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); }
-
-#else /* 64-bit words then */
-
-#define STORE32L(x, y) \
- { ulong32 __t = (x); XMEMCPY(y, &__t, 4); }
-
-#define LOAD32L(x, y) \
- { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; }
-
-#define STORE64L(x, y) \
- { ulong64 __t = (x); XMEMCPY(y, &__t, 8); }
-
-#define LOAD64L(x, y) \
- { XMEMCPY(&(x), y, 8); }
-
-#endif /* ENDIAN_64BITWORD */
-
-#endif /* ENDIAN_LITTLE */
-
-#ifdef ENDIAN_BIG
-#define STORE32L(x, y) \
- { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \
- (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); }
-
-#define LOAD32L(x, y) \
- { x = ((unsigned long)((y)[3] & 255)<<24) | \
- ((unsigned long)((y)[2] & 255)<<16) | \
- ((unsigned long)((y)[1] & 255)<<8) | \
- ((unsigned long)((y)[0] & 255)); }
-
-#define STORE64L(x, y) \
- { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255); \
- (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255); \
- (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \
- (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); }
-
-#define LOAD64L(x, y) \
- { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48) | \
- (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32) | \
- (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16) | \
- (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); }
-
-#ifdef ENDIAN_32BITWORD
-
-#define STORE32H(x, y) \
- { ulong32 __t = (x); XMEMCPY(y, &__t, 4); }
-
-#define LOAD32H(x, y) \
- XMEMCPY(&(x), y, 4);
-
-#define STORE64H(x, y) \
- { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \
- (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \
- (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \
- (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
-
-#define LOAD64H(x, y) \
- { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48)| \
- (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32)| \
- (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16)| \
- (((ulong64)((y)[6] & 255))<<8)| (((ulong64)((y)[7] & 255))); }
-
-#else /* 64-bit words then */
-
-#define STORE32H(x, y) \
- { ulong32 __t = (x); XMEMCPY(y, &__t, 4); }
-
-#define LOAD32H(x, y) \
- { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; }
-
-#define STORE64H(x, y) \
- { ulong64 __t = (x); XMEMCPY(y, &__t, 8); }
-
-#define LOAD64H(x, y) \
- { XMEMCPY(&(x), y, 8); }
-
-#endif /* ENDIAN_64BITWORD */
-#endif /* ENDIAN_BIG */
-
-#define BSWAP(x) ( ((x>>24)&0x000000FFUL) | ((x<<24)&0xFF000000UL) | \
- ((x>>8)&0x0000FF00UL) | ((x<<8)&0x00FF0000UL) )
-
-
-/* 32-bit Rotates */
-#if defined(_MSC_VER)
-
-/* instrinsic rotate */
-#include <stdlib.h>
-#pragma intrinsic(_lrotr,_lrotl)
-#define ROR(x,n) _lrotr(x,n)
-#define ROL(x,n) _lrotl(x,n)
-#define RORc(x,n) _lrotr(x,n)
-#define ROLc(x,n) _lrotl(x,n)
-
-#elif !defined(__STRICT_ANSI__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) && !defined(INTEL_CC) && !defined(LTC_NO_ASM)
-
-static inline unsigned ROL(unsigned word, int i)
-{
- asm ("roll %%cl,%0"
- :"=r" (word)
- :"0" (word),"c" (i));
- return word;
-}
-
-static inline unsigned ROR(unsigned word, int i)
-{
- asm ("rorl %%cl,%0"
- :"=r" (word)
- :"0" (word),"c" (i));
- return word;
-}
-
-#ifndef LTC_NO_ROLC
-
-static inline unsigned ROLc(unsigned word, const int i)
-{
- asm ("roll %2,%0"
- :"=r" (word)
- :"0" (word),"I" (i));
- return word;
-}
-
-static inline unsigned RORc(unsigned word, const int i)
-{
- asm ("rorl %2,%0"
- :"=r" (word)
- :"0" (word),"I" (i));
- return word;
-}
-
-#else
-
-#define ROLc ROL
-#define RORc ROR
-
-#endif
-
-#elif !defined(__STRICT_ANSI__) && defined(LTC_PPC32)
-
-static inline unsigned ROL(unsigned word, int i)
-{
- asm ("rotlw %0,%0,%2"
- :"=r" (word)
- :"0" (word),"r" (i));
- return word;
-}
-
-static inline unsigned ROR(unsigned word, int i)
-{
- asm ("rotlw %0,%0,%2"
- :"=r" (word)
- :"0" (word),"r" (32-i));
- return word;
-}
-
-#ifndef LTC_NO_ROLC
-
-static inline unsigned ROLc(unsigned word, const int i)
-{
- asm ("rotlwi %0,%0,%2"
- :"=r" (word)
- :"0" (word),"I" (i));
- return word;
-}
-
-static inline unsigned RORc(unsigned word, const int i)
-{
- asm ("rotrwi %0,%0,%2"
- :"=r" (word)
- :"0" (word),"I" (i));
- return word;
-}
-
-#else
-
-#define ROLc ROL
-#define RORc ROR
-
-#endif
-
-
-#else
-
-/* rotates the hard way */
-#define ROL(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
-#define ROR(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
-#define ROLc(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
-#define RORc(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
-
-#endif
-
-
-/* 64-bit Rotates */
-#if !defined(__STRICT_ANSI__) && defined(__GNUC__) && defined(__x86_64__) && !defined(LTC_NO_ASM)
-
-static inline unsigned long ROL64(unsigned long word, int i)
-{
- asm("rolq %%cl,%0"
- :"=r" (word)
- :"0" (word),"c" (i));
- return word;
-}
-
-static inline unsigned long ROR64(unsigned long word, int i)
-{
- asm("rorq %%cl,%0"
- :"=r" (word)
- :"0" (word),"c" (i));
- return word;
-}
-
-#ifndef LTC_NO_ROLC
-
-static inline unsigned long ROL64c(unsigned long word, const int i)
-{
- asm("rolq %2,%0"
- :"=r" (word)
- :"0" (word),"J" (i));
- return word;
-}
-
-static inline unsigned long ROR64c(unsigned long word, const int i)
-{
- asm("rorq %2,%0"
- :"=r" (word)
- :"0" (word),"J" (i));
- return word;
-}
-
-#else /* LTC_NO_ROLC */
-
-#define ROL64c ROL64
-#define ROR64c ROR64
-
-#endif
-
-#else /* Not x86_64 */
-
-#define ROL64(x, y) \
- ( (((x)<<((ulong64)(y)&63)) | \
- (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)64-((y)&63)))) & CONST64(0xFFFFFFFFFFFFFFFF))
-
-#define ROR64(x, y) \
- ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)(y)&CONST64(63))) | \
- ((x)<<((ulong64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF))
-
-#define ROL64c(x, y) \
- ( (((x)<<((ulong64)(y)&63)) | \
- (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)64-((y)&63)))) & CONST64(0xFFFFFFFFFFFFFFFF))
-
-#define ROR64c(x, y) \
- ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)(y)&CONST64(63))) | \
- ((x)<<((ulong64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF))
-
-#endif
-
-#ifndef MAX
- #define MAX(x, y) ( ((x)>(y))?(x):(y) )
-#endif
-
-#ifndef MIN
- #define MIN(x, y) ( ((x)<(y))?(x):(y) )
-#endif
-
-/* extract a byte portably */
-#ifdef _MSC_VER
- #define byte(x, n) ((unsigned char)((x) >> (8 * (n))))
-#else
- #define byte(x, n) (((x) >> (8 * (n))) & 255)
-#endif
-
-/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_macros.h,v $ */
-/* $Revision: 1.15 $ */
-/* $Date: 2006/11/29 23:43:57 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_math.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_math.h
deleted file mode 100644
index a05d7fff942..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_math.h
+++ /dev/null
@@ -1,500 +0,0 @@
-/** math functions **/
-
-#define LTC_MP_LT -1
-#define LTC_MP_EQ 0
-#define LTC_MP_GT 1
-
-#define LTC_MP_NO 0
-#define LTC_MP_YES 1
-
-#ifndef LTC_MECC
- typedef void ecc_point;
-#endif
-
-#ifndef LTC_MRSA
- typedef void rsa_key;
-#endif
-
-/** math descriptor */
-typedef struct {
- /** Name of the math provider */
- char *name;
-
- /** Bits per digit, amount of bits must fit in an unsigned long */
- int bits_per_digit;
-
-/* ---- init/deinit functions ---- */
-
- /** initialize a bignum
- @param a The number to initialize
- @return CRYPT_OK on success
- */
- int (*init)(void **a);
-
- /** init copy
- @param dst The number to initialize and write to
- @param src The number to copy from
- @return CRYPT_OK on success
- */
- int (*init_copy)(void **dst, void *src);
-
- /** deinit
- @param a The number to free
- @return CRYPT_OK on success
- */
- void (*deinit)(void *a);
-
-/* ---- data movement ---- */
-
- /** negate
- @param src The number to negate
- @param dst The destination
- @return CRYPT_OK on success
- */
- int (*neg)(void *src, void *dst);
-
- /** copy
- @param src The number to copy from
- @param dst The number to write to
- @return CRYPT_OK on success
- */
- int (*copy)(void *src, void *dst);
-
-/* ---- trivial low level functions ---- */
-
- /** set small constant
- @param a Number to write to
- @param n Source upto bits_per_digit (actually meant for very small constants)
- @return CRYPT_OK on succcess
- */
- int (*set_int)(void *a, unsigned long n);
-
- /** get small constant
- @param a Number to read, only fetches upto bits_per_digit from the number
- @return The lower bits_per_digit of the integer (unsigned)
- */
- unsigned long (*get_int)(void *a);
-
- /** get digit n
- @param a The number to read from
- @param n The number of the digit to fetch
- @return The bits_per_digit sized n'th digit of a
- */
- unsigned long (*get_digit)(void *a, int n);
-
- /** Get the number of digits that represent the number
- @param a The number to count
- @return The number of digits used to represent the number
- */
- int (*get_digit_count)(void *a);
-
- /** compare two integers
- @param a The left side integer
- @param b The right side integer
- @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise. (signed comparison)
- */
- int (*compare)(void *a, void *b);
-
- /** compare against int
- @param a The left side integer
- @param b The right side integer (upto bits_per_digit)
- @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise. (signed comparison)
- */
- int (*compare_d)(void *a, unsigned long n);
-
- /** Count the number of bits used to represent the integer
- @param a The integer to count
- @return The number of bits required to represent the integer
- */
- int (*count_bits)(void * a);
-
- /** Count the number of LSB bits which are zero
- @param a The integer to count
- @return The number of contiguous zero LSB bits
- */
- int (*count_lsb_bits)(void *a);
-
- /** Compute a power of two
- @param a The integer to store the power in
- @param n The power of two you want to store (a = 2^n)
- @return CRYPT_OK on success
- */
- int (*twoexpt)(void *a , int n);
-
-/* ---- radix conversions ---- */
-
- /** read ascii string
- @param a The integer to store into
- @param str The string to read
- @param radix The radix the integer has been represented in (2-64)
- @return CRYPT_OK on success
- */
- int (*read_radix)(void *a, const char *str, int radix);
-
- /** write number to string
- @param a The integer to store
- @param str The destination for the string
- @param radix The radix the integer is to be represented in (2-64)
- @return CRYPT_OK on success
- */
- int (*write_radix)(void *a, char *str, int radix);
-
- /** get size as unsigned char string
- @param a The integer to get the size (when stored in array of octets)
- @return The length of the integer
- */
- unsigned long (*unsigned_size)(void *a);
-
- /** store an integer as an array of octets
- @param src The integer to store
- @param dst The buffer to store the integer in
- @return CRYPT_OK on success
- */
- int (*unsigned_write)(void *src, unsigned char *dst);
-
- /** read an array of octets and store as integer
- @param dst The integer to load
- @param src The array of octets
- @param len The number of octets
- @return CRYPT_OK on success
- */
- int (*unsigned_read)(void *dst, unsigned char *src, unsigned long len);
-
-/* ---- basic math ---- */
-
- /** add two integers
- @param a The first source integer
- @param b The second source integer
- @param c The destination of "a + b"
- @return CRYPT_OK on success
- */
- int (*add)(void *a, void *b, void *c);
-
-
- /** add two integers
- @param a The first source integer
- @param b The second source integer (single digit of upto bits_per_digit in length)
- @param c The destination of "a + b"
- @return CRYPT_OK on success
- */
- int (*addi)(void *a, unsigned long b, void *c);
-
- /** subtract two integers
- @param a The first source integer
- @param b The second source integer
- @param c The destination of "a - b"
- @return CRYPT_OK on success
- */
- int (*sub)(void *a, void *b, void *c);
-
- /** subtract two integers
- @param a The first source integer
- @param b The second source integer (single digit of upto bits_per_digit in length)
- @param c The destination of "a - b"
- @return CRYPT_OK on success
- */
- int (*subi)(void *a, unsigned long b, void *c);
-
- /** multiply two integers
- @param a The first source integer
- @param b The second source integer (single digit of upto bits_per_digit in length)
- @param c The destination of "a * b"
- @return CRYPT_OK on success
- */
- int (*mul)(void *a, void *b, void *c);
-
- /** multiply two integers
- @param a The first source integer
- @param b The second source integer (single digit of upto bits_per_digit in length)
- @param c The destination of "a * b"
- @return CRYPT_OK on success
- */
- int (*muli)(void *a, unsigned long b, void *c);
-
- /** Square an integer
- @param a The integer to square
- @param b The destination
- @return CRYPT_OK on success
- */
- int (*sqr)(void *a, void *b);
-
- /** Divide an integer
- @param a The dividend
- @param b The divisor
- @param c The quotient (can be NULL to signify don't care)
- @param d The remainder (can be NULL to signify don't care)
- @return CRYPT_OK on success
- */
- int (*mpdiv)(void *a, void *b, void *c, void *d);
-
- /** divide by two
- @param a The integer to divide (shift right)
- @param b The destination
- @return CRYPT_OK on success
- */
- int (*div_2)(void *a, void *b);
-
- /** Get remainder (small value)
- @param a The integer to reduce
- @param b The modulus (upto bits_per_digit in length)
- @param c The destination for the residue
- @return CRYPT_OK on success
- */
- int (*modi)(void *a, unsigned long b, unsigned long *c);
-
- /** gcd
- @param a The first integer
- @param b The second integer
- @param c The destination for (a, b)
- @return CRYPT_OK on success
- */
- int (*gcd)(void *a, void *b, void *c);
-
- /** lcm
- @param a The first integer
- @param b The second integer
- @param c The destination for [a, b]
- @return CRYPT_OK on success
- */
- int (*lcm)(void *a, void *b, void *c);
-
- /** Modular multiplication
- @param a The first source
- @param b The second source
- @param c The modulus
- @param d The destination (a*b mod c)
- @return CRYPT_OK on success
- */
- int (*mulmod)(void *a, void *b, void *c, void *d);
-
- /** Modular squaring
- @param a The first source
- @param b The modulus
- @param c The destination (a*a mod b)
- @return CRYPT_OK on success
- */
- int (*sqrmod)(void *a, void *b, void *c);
-
- /** Modular inversion
- @param a The value to invert
- @param b The modulus
- @param c The destination (1/a mod b)
- @return CRYPT_OK on success
- */
- int (*invmod)(void *, void *, void *);
-
-/* ---- reduction ---- */
-
- /** setup montgomery
- @param a The modulus
- @param b The destination for the reduction digit
- @return CRYPT_OK on success
- */
- int (*montgomery_setup)(void *a, void **b);
-
- /** get normalization value
- @param a The destination for the normalization value
- @param b The modulus
- @return CRYPT_OK on success
- */
- int (*montgomery_normalization)(void *a, void *b);
-
- /** reduce a number
- @param a The number [and dest] to reduce
- @param b The modulus
- @param c The value "b" from montgomery_setup()
- @return CRYPT_OK on success
- */
- int (*montgomery_reduce)(void *a, void *b, void *c);
-
- /** clean up (frees memory)
- @param a The value "b" from montgomery_setup()
- @return CRYPT_OK on success
- */
- void (*montgomery_deinit)(void *a);
-
-/* ---- exponentiation ---- */
-
- /** Modular exponentiation
- @param a The base integer
- @param b The power (can be negative) integer
- @param c The modulus integer
- @param d The destination
- @return CRYPT_OK on success
- */
- int (*exptmod)(void *a, void *b, void *c, void *d);
-
- /** Primality testing
- @param a The integer to test
- @param b The destination of the result (FP_YES if prime)
- @return CRYPT_OK on success
- */
- int (*isprime)(void *a, int *b);
-
-/* ---- (optional) ecc point math ---- */
-
- /** ECC GF(p) point multiplication (from the NIST curves)
- @param k The integer to multiply the point by
- @param G The point to multiply
- @param R The destination for kG
- @param modulus The modulus for the field
- @param map Boolean indicated whether to map back to affine or not (can be ignored if you work in affine only)
- @return CRYPT_OK on success
- */
- int (*ecc_ptmul)(void *k, ecc_point *G, ecc_point *R, void *modulus, int map);
-
- /** ECC GF(p) point addition
- @param P The first point
- @param Q The second point
- @param R The destination of P + Q
- @param modulus The modulus
- @param mp The "b" value from montgomery_setup()
- @return CRYPT_OK on success
- */
- int (*ecc_ptadd)(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp);
-
- /** ECC GF(p) point double
- @param P The first point
- @param R The destination of 2P
- @param modulus The modulus
- @param mp The "b" value from montgomery_setup()
- @return CRYPT_OK on success
- */
- int (*ecc_ptdbl)(ecc_point *P, ecc_point *R, void *modulus, void *mp);
-
- /** ECC mapping from projective to affine, currently uses (x,y,z) => (x/z^2, y/z^3, 1)
- @param P The point to map
- @param modulus The modulus
- @param mp The "b" value from montgomery_setup()
- @return CRYPT_OK on success
- @remark The mapping can be different but keep in mind a ecc_point only has three
- integers (x,y,z) so if you use a different mapping you have to make it fit.
- */
- int (*ecc_map)(ecc_point *P, void *modulus, void *mp);
-
- /** Computes kA*A + kB*B = C using Shamir's Trick
- @param A First point to multiply
- @param kA What to multiple A by
- @param B Second point to multiply
- @param kB What to multiple B by
- @param C [out] Destination point (can overlap with A or B
- @param modulus Modulus for curve
- @return CRYPT_OK on success
- */
- int (*ecc_mul2add)(ecc_point *A, void *kA,
- ecc_point *B, void *kB,
- ecc_point *C,
- void *modulus);
-
-/* ---- (optional) rsa optimized math (for internal CRT) ---- */
-
- /** RSA Key Generation
- @param prng An active PRNG state
- @param wprng The index of the PRNG desired
- @param size The size of the modulus (key size) desired (octets)
- @param e The "e" value (public key). e==65537 is a good choice
- @param key [out] Destination of a newly created private key pair
- @return CRYPT_OK if successful, upon error all allocated ram is freed
- */
- int (*rsa_keygen)(prng_state *prng, int wprng, int size, long e, rsa_key *key);
-
-
- /** RSA exponentiation
- @param in The octet array representing the base
- @param inlen The length of the input
- @param out The destination (to be stored in an octet array format)
- @param outlen The length of the output buffer and the resulting size (zero padded to the size of the modulus)
- @param which PK_PUBLIC for public RSA and PK_PRIVATE for private RSA
- @param key The RSA key to use
- @return CRYPT_OK on success
- */
- int (*rsa_me)(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen, int which,
- rsa_key *key);
-} ltc_math_descriptor;
-
-extern ltc_math_descriptor ltc_mp;
-
-int ltc_init_multi(void **a, ...);
-void ltc_deinit_multi(void *a, ...);
-
-#ifdef LTM_DESC
-extern const ltc_math_descriptor ltm_desc;
-#endif
-
-#ifdef TFM_DESC
-extern const ltc_math_descriptor tfm_desc;
-#endif
-
-#ifdef GMP_DESC
-extern const ltc_math_descriptor gmp_desc;
-#endif
-
-#if !defined(DESC_DEF_ONLY) && defined(LTC_SOURCE)
-
-#define MP_DIGIT_BIT ltc_mp.bits_per_digit
-
-/* some handy macros */
-#define mp_init(a) ltc_mp.init(a)
-#define mp_init_multi ltc_init_multi
-#define mp_clear(a) ltc_mp.deinit(a)
-#define mp_clear_multi ltc_deinit_multi
-#define mp_init_copy(a, b) ltc_mp.init_copy(a, b)
-
-#define mp_neg(a, b) ltc_mp.neg(a, b)
-#define mp_copy(a, b) ltc_mp.copy(a, b)
-
-#define mp_set(a, b) ltc_mp.set_int(a, b)
-#define mp_set_int(a, b) ltc_mp.set_int(a, b)
-#define mp_get_int(a) ltc_mp.get_int(a)
-#define mp_get_digit(a, n) ltc_mp.get_digit(a, n)
-#define mp_get_digit_count(a) ltc_mp.get_digit_count(a)
-#define mp_cmp(a, b) ltc_mp.compare(a, b)
-#define mp_cmp_d(a, b) ltc_mp.compare_d(a, b)
-#define mp_count_bits(a) ltc_mp.count_bits(a)
-#define mp_cnt_lsb(a) ltc_mp.count_lsb_bits(a)
-#define mp_2expt(a, b) ltc_mp.twoexpt(a, b)
-
-#define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c)
-#define mp_toradix(a, b, c) ltc_mp.write_radix(a, b, c)
-#define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a)
-#define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b)
-#define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c)
-
-#define mp_add(a, b, c) ltc_mp.add(a, b, c)
-#define mp_add_d(a, b, c) ltc_mp.addi(a, b, c)
-#define mp_sub(a, b, c) ltc_mp.sub(a, b, c)
-#define mp_sub_d(a, b, c) ltc_mp.subi(a, b, c)
-#define mp_mul(a, b, c) ltc_mp.mul(a, b, c)
-#define mp_mul_d(a, b, c) ltc_mp.muli(a, b, c)
-#define mp_sqr(a, b) ltc_mp.sqr(a, b)
-#define mp_div(a, b, c, d) ltc_mp.mpdiv(a, b, c, d)
-#define mp_div_2(a, b) ltc_mp.div_2(a, b)
-#define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c)
-#define mp_mod_d(a, b, c) ltc_mp.modi(a, b, c)
-#define mp_gcd(a, b, c) ltc_mp.gcd(a, b, c)
-#define mp_lcm(a, b, c) ltc_mp.lcm(a, b, c)
-
-#define mp_mulmod(a, b, c, d) ltc_mp.mulmod(a, b, c, d)
-#define mp_sqrmod(a, b, c) ltc_mp.sqrmod(a, b, c)
-#define mp_invmod(a, b, c) ltc_mp.invmod(a, b, c)
-
-#define mp_montgomery_setup(a, b) ltc_mp.montgomery_setup(a, b)
-#define mp_montgomery_normalization(a, b) ltc_mp.montgomery_normalization(a, b)
-#define mp_montgomery_reduce(a, b, c) ltc_mp.montgomery_reduce(a, b, c)
-#define mp_montgomery_free(a) ltc_mp.montgomery_deinit(a)
-
-#define mp_exptmod(a,b,c,d) ltc_mp.exptmod(a,b,c,d)
-#define mp_prime_is_prime(a, b, c) ltc_mp.isprime(a, c)
-
-#define mp_iszero(a) (mp_cmp_d(a, 0) == LTC_MP_EQ ? LTC_MP_YES : LTC_MP_NO)
-#define mp_isodd(a) (mp_get_digit_count(a) > 0 ? (mp_get_digit(a, 0) & 1 ? LTC_MP_YES : LTC_MP_NO) : LTC_MP_NO)
-#define mp_exch(a, b) do { void *ABC__tmp = a; a = b; b = ABC__tmp; } while(0);
-
-#define mp_tohex(a, b) mp_toradix(a, b, 16)
-
-#endif
-
-/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_math.h,v $ */
-/* $Revision: 1.44 $ */
-/* $Date: 2007/05/12 14:32:35 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_misc.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_misc.h
deleted file mode 100644
index f5384cacc51..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_misc.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* ---- LTC_BASE64 Routines ---- */
-#ifdef LTC_BASE64
-int base64_encode(const unsigned char *in, unsigned long len,
- unsigned char *out, unsigned long *outlen);
-
-int base64_decode(const unsigned char *in, unsigned long len,
- unsigned char *out, unsigned long *outlen);
-#endif
-
-/* ---- MEM routines ---- */
-void zeromem(void *dst, size_t len);
-void burn_stack(unsigned long len);
-
-const char *error_to_string(int err);
-
-extern const char *crypt_build_settings;
-
-/* ---- HMM ---- */
-int crypt_fsa(void *mp, ...);
-
-/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_misc.h,v $ */
-/* $Revision: 1.5 $ */
-/* $Date: 2007/05/12 14:32:35 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pk.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pk.h
deleted file mode 100644
index b5f277a8848..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pk.h
+++ /dev/null
@@ -1,558 +0,0 @@
-/* ---- NUMBER THEORY ---- */
-
-enum {
- PK_PUBLIC=0,
- PK_PRIVATE=1
-};
-
-int rand_prime(void *N, long len, prng_state *prng, int wprng);
-
-/* ---- RSA ---- */
-#ifdef LTC_MRSA
-
-/* Min and Max RSA key sizes (in bits) */
-#define MIN_RSA_SIZE 1024
-#define MAX_RSA_SIZE 4096
-
-/** RSA LTC_PKCS style key */
-typedef struct Rsa_key {
- /** Type of key, PK_PRIVATE or PK_PUBLIC */
- int type;
- /** The public exponent */
- void *e;
- /** The private exponent */
- void *d;
- /** The modulus */
- void *N;
- /** The p factor of N */
- void *p;
- /** The q factor of N */
- void *q;
- /** The 1/q mod p CRT param */
- void *qP;
- /** The d mod (p - 1) CRT param */
- void *dP;
- /** The d mod (q - 1) CRT param */
- void *dQ;
-} rsa_key;
-
-int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key);
-
-int rsa_exptmod(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen, int which,
- rsa_key *key);
-
-void rsa_free(rsa_key *key);
-
-/* These use LTC_PKCS #1 v2.0 padding */
-#define rsa_encrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, _key) \
- rsa_encrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, LTC_LTC_PKCS_1_OAEP, _key)
-
-#define rsa_decrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, _stat, _key) \
- rsa_decrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, LTC_LTC_PKCS_1_OAEP, _stat, _key)
-
-#define rsa_sign_hash(_in, _inlen, _out, _outlen, _prng, _prng_idx, _hash_idx, _saltlen, _key) \
- rsa_sign_hash_ex(_in, _inlen, _out, _outlen, LTC_LTC_PKCS_1_PSS, _prng, _prng_idx, _hash_idx, _saltlen, _key)
-
-#define rsa_verify_hash(_sig, _siglen, _hash, _hashlen, _hash_idx, _saltlen, _stat, _key) \
- rsa_verify_hash_ex(_sig, _siglen, _hash, _hashlen, LTC_LTC_PKCS_1_PSS, _hash_idx, _saltlen, _stat, _key)
-
-/* These can be switched between LTC_PKCS #1 v2.x and LTC_PKCS #1 v1.5 paddings */
-int rsa_encrypt_key_ex(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- const unsigned char *lparam, unsigned long lparamlen,
- prng_state *prng, int prng_idx, int hash_idx, int padding, rsa_key *key);
-
-int rsa_decrypt_key_ex(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- const unsigned char *lparam, unsigned long lparamlen,
- int hash_idx, int padding,
- int *stat, rsa_key *key);
-
-int rsa_sign_hash_ex(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- int padding,
- prng_state *prng, int prng_idx,
- int hash_idx, unsigned long saltlen,
- rsa_key *key);
-
-int rsa_verify_hash_ex(const unsigned char *sig, unsigned long siglen,
- const unsigned char *hash, unsigned long hashlen,
- int padding,
- int hash_idx, unsigned long saltlen,
- int *stat, rsa_key *key);
-
-/* LTC_PKCS #1 import/export */
-int rsa_export(unsigned char *out, unsigned long *outlen, int type, rsa_key *key);
-int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key);
-
-/* Ladik: Added for verifying Blizzard strong signature verification */
-int rsa_verify_simple(const unsigned char *sig, unsigned long siglen,
- const unsigned char *hash, unsigned long hashlen,
- int *stat,
- rsa_key *key);
-
-#endif
-
-/* ---- Katja ---- */
-#ifdef MKAT
-
-/* Min and Max KAT key sizes (in bits) */
-#define MIN_KAT_SIZE 1024
-#define MAX_KAT_SIZE 4096
-
-/** Katja LTC_PKCS style key */
-typedef struct KAT_key {
- /** Type of key, PK_PRIVATE or PK_PUBLIC */
- int type;
- /** The private exponent */
- void *d;
- /** The modulus */
- void *N;
- /** The p factor of N */
- void *p;
- /** The q factor of N */
- void *q;
- /** The 1/q mod p CRT param */
- void *qP;
- /** The d mod (p - 1) CRT param */
- void *dP;
- /** The d mod (q - 1) CRT param */
- void *dQ;
- /** The pq param */
- void *pq;
-} katja_key;
-
-int katja_make_key(prng_state *prng, int wprng, int size, katja_key *key);
-
-int katja_exptmod(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen, int which,
- katja_key *key);
-
-void katja_free(katja_key *key);
-
-/* These use LTC_PKCS #1 v2.0 padding */
-int katja_encrypt_key(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- const unsigned char *lparam, unsigned long lparamlen,
- prng_state *prng, int prng_idx, int hash_idx, katja_key *key);
-
-int katja_decrypt_key(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- const unsigned char *lparam, unsigned long lparamlen,
- int hash_idx, int *stat,
- katja_key *key);
-
-/* LTC_PKCS #1 import/export */
-int katja_export(unsigned char *out, unsigned long *outlen, int type, katja_key *key);
-int katja_import(const unsigned char *in, unsigned long inlen, katja_key *key);
-
-#endif
-
-/* ---- ECC Routines ---- */
-#ifdef LTC_MECC
-
-/* size of our temp buffers for exported keys */
-#define ECC_BUF_SIZE 256
-
-/* max private key size */
-#define ECC_MAXSIZE 66
-
-/** Structure defines a NIST GF(p) curve */
-typedef struct {
- /** The size of the curve in octets */
- int size;
-
- /** name of curve */
- char *name;
-
- /** The prime that defines the field the curve is in (encoded in hex) */
- char *prime;
-
- /** The fields B param (hex) */
- char *B;
-
- /** The order of the curve (hex) */
- char *order;
-
- /** The x co-ordinate of the base point on the curve (hex) */
- char *Gx;
-
- /** The y co-ordinate of the base point on the curve (hex) */
- char *Gy;
-} ltc_ecc_set_type;
-
-/** A point on a ECC curve, stored in Jacbobian format such that (x,y,z) => (x/z^2, y/z^3, 1) when interpretted as affine */
-typedef struct {
- /** The x co-ordinate */
- void *x;
-
- /** The y co-ordinate */
- void *y;
-
- /** The z co-ordinate */
- void *z;
-} ecc_point;
-
-/** An ECC key */
-typedef struct {
- /** Type of key, PK_PRIVATE or PK_PUBLIC */
- int type;
-
- /** Index into the ltc_ecc_sets[] for the parameters of this curve; if -1, then this key is using user supplied curve in dp */
- int idx;
-
- /** pointer to domain parameters; either points to NIST curves (identified by idx >= 0) or user supplied curve */
- const ltc_ecc_set_type *dp;
-
- /** The public key */
- ecc_point pubkey;
-
- /** The private key */
- void *k;
-} ecc_key;
-
-/** the ECC params provided */
-extern const ltc_ecc_set_type ltc_ecc_sets[];
-
-int ecc_test(void);
-void ecc_sizes(int *low, int *high);
-int ecc_get_size(ecc_key *key);
-
-int ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key);
-int ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_set_type *dp);
-void ecc_free(ecc_key *key);
-
-int ecc_export(unsigned char *out, unsigned long *outlen, int type, ecc_key *key);
-int ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key);
-int ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_set_type *dp);
-
-int ecc_ansi_x963_export(ecc_key *key, unsigned char *out, unsigned long *outlen);
-int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key);
-int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, ltc_ecc_set_type *dp);
-
-int ecc_shared_secret(ecc_key *private_key, ecc_key *public_key,
- unsigned char *out, unsigned long *outlen);
-
-int ecc_encrypt_key(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- prng_state *prng, int wprng, int hash,
- ecc_key *key);
-
-int ecc_decrypt_key(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- ecc_key *key);
-
-int ecc_sign_hash(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- prng_state *prng, int wprng, ecc_key *key);
-
-int ecc_verify_hash(const unsigned char *sig, unsigned long siglen,
- const unsigned char *hash, unsigned long hashlen,
- int *stat, ecc_key *key);
-
-/* low level functions */
-ecc_point *ltc_ecc_new_point(void);
-void ltc_ecc_del_point(ecc_point *p);
-int ltc_ecc_is_valid_idx(int n);
-
-/* point ops (mp == montgomery digit) */
-#if !defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC) || defined(GMP_LTC_DESC)
-/* R = 2P */
-int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp);
-
-/* R = P + Q */
-int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp);
-#endif
-
-#if defined(LTC_MECC_FP)
-/* optimized point multiplication using fixed point cache (HAC algorithm 14.117) */
-int ltc_ecc_fp_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map);
-
-/* functions for saving/loading/freeing/adding to fixed point cache */
-int ltc_ecc_fp_save_state(unsigned char **out, unsigned long *outlen);
-int ltc_ecc_fp_restore_state(unsigned char *in, unsigned long inlen);
-void ltc_ecc_fp_free(void);
-int ltc_ecc_fp_add_point(ecc_point *g, void *modulus, int lock);
-
-/* lock/unlock all points currently in fixed point cache */
-void ltc_ecc_fp_tablelock(int lock);
-#endif
-
-/* R = kG */
-int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map);
-
-#ifdef LTC_ECC_SHAMIR
-/* kA*A + kB*B = C */
-int ltc_ecc_mul2add(ecc_point *A, void *kA,
- ecc_point *B, void *kB,
- ecc_point *C,
- void *modulus);
-
-#ifdef LTC_MECC_FP
-/* Shamir's trick with optimized point multiplication using fixed point cache */
-int ltc_ecc_fp_mul2add(ecc_point *A, void *kA,
- ecc_point *B, void *kB,
- ecc_point *C, void *modulus);
-#endif
-
-#endif
-
-
-/* map P to affine from projective */
-int ltc_ecc_map(ecc_point *P, void *modulus, void *mp);
-
-#endif
-
-#ifdef LTC_MDSA
-
-/* Max diff between group and modulus size in bytes */
-#define LTC_MDSA_DELTA 512
-
-/* Max DSA group size in bytes (default allows 4k-bit groups) */
-#define LTC_MDSA_MAX_GROUP 512
-
-/** DSA key structure */
-typedef struct {
- /** The key type, PK_PRIVATE or PK_PUBLIC */
- int type;
-
- /** The order of the sub-group used in octets */
- int qord;
-
- /** The generator */
- void *g;
-
- /** The prime used to generate the sub-group */
- void *q;
-
- /** The large prime that generats the field the contains the sub-group */
- void *p;
-
- /** The private key */
- void *x;
-
- /** The public key */
- void *y;
-} dsa_key;
-
-int dsa_make_key(prng_state *prng, int wprng, int group_size, int modulus_size, dsa_key *key);
-void dsa_free(dsa_key *key);
-
-int dsa_sign_hash_raw(const unsigned char *in, unsigned long inlen,
- void *r, void *s,
- prng_state *prng, int wprng, dsa_key *key);
-
-int dsa_sign_hash(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- prng_state *prng, int wprng, dsa_key *key);
-
-int dsa_verify_hash_raw( void *r, void *s,
- const unsigned char *hash, unsigned long hashlen,
- int *stat, dsa_key *key);
-
-int dsa_verify_hash(const unsigned char *sig, unsigned long siglen,
- const unsigned char *hash, unsigned long hashlen,
- int *stat, dsa_key *key);
-
-int dsa_encrypt_key(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- prng_state *prng, int wprng, int hash,
- dsa_key *key);
-
-int dsa_decrypt_key(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- dsa_key *key);
-
-int dsa_import(const unsigned char *in, unsigned long inlen, dsa_key *key);
-int dsa_export(unsigned char *out, unsigned long *outlen, int type, dsa_key *key);
-int dsa_verify_key(dsa_key *key, int *stat);
-
-int dsa_shared_secret(void *private_key, void *base,
- dsa_key *public_key,
- unsigned char *out, unsigned long *outlen);
-#endif
-
-#ifdef LTC_DER
-/* DER handling */
-
-enum {
- LTC_ASN1_EOL,
- LTC_ASN1_BOOLEAN,
- LTC_ASN1_INTEGER,
- LTC_ASN1_SHORT_INTEGER,
- LTC_ASN1_BIT_STRING,
- LTC_ASN1_OCTET_STRING,
- LTC_ASN1_NULL,
- LTC_ASN1_OBJECT_IDENTIFIER,
- LTC_ASN1_IA5_STRING,
- LTC_ASN1_PRINTABLE_STRING,
- LTC_ASN1_UTF8_STRING,
- LTC_ASN1_UTCTIME,
- LTC_ASN1_CHOICE,
- LTC_ASN1_SEQUENCE,
- LTC_ASN1_SET,
- LTC_ASN1_SETOF
-};
-
-/** A LTC ASN.1 list type */
-typedef struct ltc_asn1_list_ {
- /** The LTC ASN.1 enumerated type identifier */
- int type;
- /** The data to encode or place for decoding */
- void *data;
- /** The size of the input or resulting output */
- unsigned long size;
- /** The used flag, this is used by the CHOICE ASN.1 type to indicate which choice was made */
- int used;
- /** prev/next entry in the list */
- struct ltc_asn1_list_ *prev, *next, *child, *parent;
-} ltc_asn1_list;
-
-#define LTC_SET_ASN1(list, index, Type, Data, Size) \
- do { \
- int LTC_MACRO_temp = (index); \
- ltc_asn1_list *LTC_MACRO_list = (list); \
- LTC_MACRO_list[LTC_MACRO_temp].type = (Type); \
- LTC_MACRO_list[LTC_MACRO_temp].data = (void*)(Data); \
- LTC_MACRO_list[LTC_MACRO_temp].size = (Size); \
- LTC_MACRO_list[LTC_MACRO_temp].used = 0; \
- } while (0);
-
-/* SEQUENCE */
-int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen,
- unsigned char *out, unsigned long *outlen, int type_of);
-
-#define der_encode_sequence(list, inlen, out, outlen) der_encode_sequence_ex(list, inlen, out, outlen, LTC_ASN1_SEQUENCE)
-
-int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen,
- ltc_asn1_list *list, unsigned long outlen, int ordered);
-
-#define der_decode_sequence(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 1)
-
-int der_length_sequence(ltc_asn1_list *list, unsigned long inlen,
- unsigned long *outlen);
-
-/* SET */
-#define der_decode_set(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 0)
-#define der_length_set der_length_sequence
-int der_encode_set(ltc_asn1_list *list, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-
-int der_encode_setof(ltc_asn1_list *list, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-
-/* VA list handy helpers with triplets of <type, size, data> */
-int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...);
-int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...);
-
-/* FLEXI DECODER handle unknown list decoder */
-int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out);
-void der_free_sequence_flexi(ltc_asn1_list *list);
-void der_sequence_free(ltc_asn1_list *in);
-
-/* BOOLEAN */
-int der_length_boolean(unsigned long *outlen);
-int der_encode_boolean(int in,
- unsigned char *out, unsigned long *outlen);
-int der_decode_boolean(const unsigned char *in, unsigned long inlen,
- int *out);
-/* INTEGER */
-int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen);
-int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num);
-int der_length_integer(void *num, unsigned long *len);
-
-/* INTEGER -- handy for 0..2^32-1 values */
-int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num);
-int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen);
-int der_length_short_integer(unsigned long num, unsigned long *outlen);
-
-/* BIT STRING */
-int der_encode_bit_string(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-int der_decode_bit_string(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-int der_length_bit_string(unsigned long nbits, unsigned long *outlen);
-
-/* OCTET STRING */
-int der_encode_octet_string(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-int der_decode_octet_string(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-int der_length_octet_string(unsigned long noctets, unsigned long *outlen);
-
-/* OBJECT IDENTIFIER */
-int der_encode_object_identifier(unsigned long *words, unsigned long nwords,
- unsigned char *out, unsigned long *outlen);
-int der_decode_object_identifier(const unsigned char *in, unsigned long inlen,
- unsigned long *words, unsigned long *outlen);
-int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen);
-unsigned long der_object_identifier_bits(unsigned long x);
-
-/* IA5 STRING */
-int der_encode_ia5_string(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-int der_decode_ia5_string(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen);
-
-int der_ia5_char_encode(int c);
-int der_ia5_value_decode(int v);
-
-/* Printable STRING */
-int der_encode_printable_string(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-int der_decode_printable_string(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen);
-
-int der_printable_char_encode(int c);
-int der_printable_value_decode(int v);
-
-/* UTF-8 */
-#if (defined(SIZE_MAX) || __STDC_VERSION__ >= 199901L || defined(WCHAR_MAX) || defined(_WCHAR_T) || defined(_WCHAR_T_DEFINED) || defined (__WCHAR_TYPE__)) && !defined(LTC_NO_WCHAR)
-#include <wchar.h>
-#else
-typedef ulong32 wchar_t;
-#endif
-
-int der_encode_utf8_string(const wchar_t *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen);
-
-int der_decode_utf8_string(const unsigned char *in, unsigned long inlen,
- wchar_t *out, unsigned long *outlen);
-unsigned long der_utf8_charsize(const wchar_t c);
-int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen);
-
-
-/* CHOICE */
-int der_decode_choice(const unsigned char *in, unsigned long *inlen,
- ltc_asn1_list *list, unsigned long outlen);
-
-/* UTCTime */
-typedef struct {
- unsigned YY, /* year */
- MM, /* month */
- DD, /* day */
- hh, /* hour */
- mm, /* minute */
- ss, /* second */
- off_dir, /* timezone offset direction 0 == +, 1 == - */
- off_hh, /* timezone offset hours */
- off_mm; /* timezone offset minutes */
-} ltc_utctime;
-
-int der_encode_utctime(ltc_utctime *utctime,
- unsigned char *out, unsigned long *outlen);
-
-int der_decode_utctime(const unsigned char *in, unsigned long *inlen,
- ltc_utctime *out);
-
-int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen);
-
-
-#endif
-
-/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pk.h,v $ */
-/* $Revision: 1.81 $ */
-/* $Date: 2007/05/12 14:32:35 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pkcs.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pkcs.h
deleted file mode 100644
index 84fb82a6229..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_pkcs.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/* LTC_PKCS Header Info */
-
-/* ===> LTC_PKCS #1 -- RSA Cryptography <=== */
-#ifdef LTC_PKCS_1
-
-enum ltc_pkcs_1_v1_5_blocks
-{
- LTC_LTC_PKCS_1_EMSA = 1, /* Block type 1 (LTC_PKCS #1 v1.5 signature padding) */
- LTC_LTC_PKCS_1_EME = 2 /* Block type 2 (LTC_PKCS #1 v1.5 encryption padding) */
-};
-
-enum ltc_pkcs_1_paddings
-{
- LTC_LTC_PKCS_1_V1_5 = 1, /* LTC_PKCS #1 v1.5 padding (\sa ltc_pkcs_1_v1_5_blocks) */
- LTC_LTC_PKCS_1_OAEP = 2, /* LTC_PKCS #1 v2.0 encryption padding */
- LTC_LTC_PKCS_1_PSS = 3 /* LTC_PKCS #1 v2.1 signature padding */
-};
-
-int pkcs_1_mgf1( int hash_idx,
- const unsigned char *seed, unsigned long seedlen,
- unsigned char *mask, unsigned long masklen);
-
-int pkcs_1_i2osp(void *n, unsigned long modulus_len, unsigned char *out);
-int pkcs_1_os2ip(void *n, unsigned char *in, unsigned long inlen);
-
-/* *** v1.5 padding */
-int pkcs_1_v1_5_encode(const unsigned char *msg,
- unsigned long msglen,
- int block_type,
- unsigned long modulus_bitlen,
- prng_state *prng,
- int prng_idx,
- unsigned char *out,
- unsigned long *outlen);
-
-int pkcs_1_v1_5_decode(const unsigned char *msg,
- unsigned long msglen,
- int block_type,
- unsigned long modulus_bitlen,
- unsigned char *out,
- unsigned long *outlen,
- int *is_valid);
-
-/* *** v2.1 padding */
-int pkcs_1_oaep_encode(const unsigned char *msg, unsigned long msglen,
- const unsigned char *lparam, unsigned long lparamlen,
- unsigned long modulus_bitlen, prng_state *prng,
- int prng_idx, int hash_idx,
- unsigned char *out, unsigned long *outlen);
-
-int pkcs_1_oaep_decode(const unsigned char *msg, unsigned long msglen,
- const unsigned char *lparam, unsigned long lparamlen,
- unsigned long modulus_bitlen, int hash_idx,
- unsigned char *out, unsigned long *outlen,
- int *res);
-
-int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen,
- unsigned long saltlen, prng_state *prng,
- int prng_idx, int hash_idx,
- unsigned long modulus_bitlen,
- unsigned char *out, unsigned long *outlen);
-
-int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen,
- const unsigned char *sig, unsigned long siglen,
- unsigned long saltlen, int hash_idx,
- unsigned long modulus_bitlen, int *res);
-
-#endif /* LTC_PKCS_1 */
-
-/* ===> LTC_PKCS #5 -- Password Based Cryptography <=== */
-#ifdef LTC_PKCS_5
-
-/* Algorithm #1 (old) */
-int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,
- const unsigned char *salt,
- int iteration_count, int hash_idx,
- unsigned char *out, unsigned long *outlen);
-
-/* Algorithm #2 (new) */
-int pkcs_5_alg2(const unsigned char *password, unsigned long password_len,
- const unsigned char *salt, unsigned long salt_len,
- int iteration_count, int hash_idx,
- unsigned char *out, unsigned long *outlen);
-
-#endif /* LTC_PKCS_5 */
-
-/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pkcs.h,v $ */
-/* $Revision: 1.8 $ */
-/* $Date: 2007/05/12 14:32:35 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_prng.h b/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_prng.h
deleted file mode 100644
index f3e3e550e88..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/headers/tomcrypt_prng.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/* ---- PRNG Stuff ---- */
-#ifdef LTC_YARROW
-struct yarrow_prng {
- int cipher, hash;
- unsigned char pool[MAXBLOCKSIZE];
- symmetric_CTR ctr;
- LTC_MUTEX_TYPE(prng_lock)
-};
-#endif
-
-#ifdef LTC_RC4
-struct rc4_prng {
- int x, y;
- unsigned char buf[256];
-};
-#endif
-
-#ifdef LTC_FORTUNA
-struct fortuna_prng {
- hash_state pool[LTC_FORTUNA_POOLS]; /* the pools */
-
- symmetric_key skey;
-
- unsigned char K[32], /* the current key */
- IV[16]; /* IV for CTR mode */
-
- unsigned long pool_idx, /* current pool we will add to */
- pool0_len, /* length of 0'th pool */
- wd;
-
- ulong64 reset_cnt; /* number of times we have reset */
- LTC_MUTEX_TYPE(prng_lock)
-};
-#endif
-
-#ifdef LTC_SOBER128
-struct sober128_prng {
- ulong32 R[17], /* Working storage for the shift register */
- initR[17], /* saved register contents */
- konst, /* key dependent constant */
- sbuf; /* partial word encryption buffer */
-
- int nbuf, /* number of part-word stream bits buffered */
- flag, /* first add_entropy call or not? */
- set; /* did we call add_entropy to set key? */
-
-};
-#endif
-
-typedef union Prng_state {
- char dummy[1];
-#ifdef LTC_YARROW
- struct yarrow_prng yarrow;
-#endif
-#ifdef LTC_RC4
- struct rc4_prng rc4;
-#endif
-#ifdef LTC_FORTUNA
- struct fortuna_prng fortuna;
-#endif
-#ifdef LTC_SOBER128
- struct sober128_prng sober128;
-#endif
-} prng_state;
-
-/** PRNG descriptor */
-extern struct ltc_prng_descriptor {
- /** Name of the PRNG */
- char *name;
- /** size in bytes of exported state */
- int export_size;
- /** Start a PRNG state
- @param prng [out] The state to initialize
- @return CRYPT_OK if successful
- */
- int (*start)(prng_state *prng);
- /** Add entropy to the PRNG
- @param in The entropy
- @param inlen Length of the entropy (octets)\
- @param prng The PRNG state
- @return CRYPT_OK if successful
- */
- int (*add_entropy)(const unsigned char *in, unsigned long inlen, prng_state *prng);
- /** Ready a PRNG state to read from
- @param prng The PRNG state to ready
- @return CRYPT_OK if successful
- */
- int (*ready)(prng_state *prng);
- /** Read from the PRNG
- @param out [out] Where to store the data
- @param outlen Length of data desired (octets)
- @param prng The PRNG state to read from
- @return Number of octets read
- */
- unsigned long (*read)(unsigned char *out, unsigned long outlen, prng_state *prng);
- /** Terminate a PRNG state
- @param prng The PRNG state to terminate
- @return CRYPT_OK if successful
- */
- int (*done)(prng_state *prng);
- /** Export a PRNG state
- @param out [out] The destination for the state
- @param outlen [in/out] The max size and resulting size of the PRNG state
- @param prng The PRNG to export
- @return CRYPT_OK if successful
- */
- int (*pexport)(unsigned char *out, unsigned long *outlen, prng_state *prng);
- /** Import a PRNG state
- @param in The data to import
- @param inlen The length of the data to import (octets)
- @param prng The PRNG to initialize/import
- @return CRYPT_OK if successful
- */
- int (*pimport)(const unsigned char *in, unsigned long inlen, prng_state *prng);
- /** Self-test the PRNG
- @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
- */
- int (*test)(void);
-} prng_descriptor[];
-
-#ifdef LTC_YARROW
-int yarrow_start(prng_state *prng);
-int yarrow_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int yarrow_ready(prng_state *prng);
-unsigned long yarrow_read(unsigned char *out, unsigned long outlen, prng_state *prng);
-int yarrow_done(prng_state *prng);
-int yarrow_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
-int yarrow_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int yarrow_test(void);
-extern const struct ltc_prng_descriptor yarrow_desc;
-#endif
-
-#ifdef LTC_FORTUNA
-int fortuna_start(prng_state *prng);
-int fortuna_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int fortuna_ready(prng_state *prng);
-unsigned long fortuna_read(unsigned char *out, unsigned long outlen, prng_state *prng);
-int fortuna_done(prng_state *prng);
-int fortuna_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
-int fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int fortuna_test(void);
-extern const struct ltc_prng_descriptor fortuna_desc;
-#endif
-
-#ifdef LTC_RC4
-int rc4_start(prng_state *prng);
-int rc4_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int rc4_ready(prng_state *prng);
-unsigned long rc4_read(unsigned char *out, unsigned long outlen, prng_state *prng);
-int rc4_done(prng_state *prng);
-int rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
-int rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int rc4_test(void);
-extern const struct ltc_prng_descriptor rc4_desc;
-#endif
-
-#ifdef LTC_SPRNG
-int sprng_start(prng_state *prng);
-int sprng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int sprng_ready(prng_state *prng);
-unsigned long sprng_read(unsigned char *out, unsigned long outlen, prng_state *prng);
-int sprng_done(prng_state *prng);
-int sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
-int sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int sprng_test(void);
-extern const struct ltc_prng_descriptor sprng_desc;
-#endif
-
-#ifdef LTC_SOBER128
-int sober128_start(prng_state *prng);
-int sober128_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int sober128_ready(prng_state *prng);
-unsigned long sober128_read(unsigned char *out, unsigned long outlen, prng_state *prng);
-int sober128_done(prng_state *prng);
-int sober128_export(unsigned char *out, unsigned long *outlen, prng_state *prng);
-int sober128_import(const unsigned char *in, unsigned long inlen, prng_state *prng);
-int sober128_test(void);
-extern const struct ltc_prng_descriptor sober128_desc;
-#endif
-
-int find_prng(const char *name);
-int register_prng(const struct ltc_prng_descriptor *prng);
-int unregister_prng(const struct ltc_prng_descriptor *prng);
-int prng_is_valid(int idx);
-LTC_MUTEX_PROTO(ltc_prng_mutex)
-
-/* Slow RNG you **might** be able to use to seed a PRNG with. Be careful as this
- * might not work on all platforms as planned
- */
-unsigned long rng_get_bytes(unsigned char *out,
- unsigned long outlen,
- void (*callback)(void));
-
-int rng_make_prng(int bits, int wprng, prng_state *prng, void (*callback)(void));
-
-
-/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_prng.h,v $ */
-/* $Revision: 1.9 $ */
-/* $Date: 2007/05/12 14:32:35 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/misc/crypt_argchk.c b/dep/CascLib/src/libtomcrypt/src/misc/crypt_argchk.c
deleted file mode 100644
index 39c686a5d46..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/misc/crypt_argchk.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- *
- * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
- */
-#include "../headers/tomcrypt.h"
-#include <signal.h>
-
-/**
- @file crypt_argchk.c
- Perform argument checking, Tom St Denis
-*/
-
-#if (ARGTYPE == 0)
-void crypt_argchk(char *v, char *s, int d)
-{
- fprintf(stderr, "LTC_ARGCHK '%s' failure on line %d of file %s\n",
- v, d, s);
- (void)raise(SIGABRT);
-}
-#endif
-
-/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_argchk.c,v $ */
-/* $Revision: 1.5 $ */
-/* $Date: 2006/12/28 01:27:24 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_descriptor.c b/dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_descriptor.c
deleted file mode 100644
index 9953551a8b7..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_descriptor.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- *
- * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
- */
-#include "../headers/tomcrypt.h"
-
-/**
- @file crypt_hash_descriptor.c
- Stores the hash descriptor table, Tom St Denis
-*/
-
-struct ltc_hash_descriptor hash_descriptor[TAB_SIZE] = {
-{ NULL, 0, 0, 0, { 0 }, 0, NULL, NULL, NULL, NULL, NULL }
-};
-
-LTC_MUTEX_GLOBAL(ltc_hash_mutex)
-
-
-/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_descriptor.c,v $ */
-/* $Revision: 1.10 $ */
-/* $Date: 2006/12/28 01:27:24 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_is_valid.c b/dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_is_valid.c
deleted file mode 100644
index 20cc30cb0e1..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/misc/crypt_hash_is_valid.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- *
- * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
- */
-#include "../headers/tomcrypt.h"
-
-/**
- @file crypt_hash_is_valid.c
- Determine if hash is valid, Tom St Denis
-*/
-
-/*
- Test if a hash index is valid
- @param idx The index of the hash to search for
- @return CRYPT_OK if valid
-*/
-int hash_is_valid(int idx)
-{
- LTC_MUTEX_LOCK(&ltc_hash_mutex);
- if (idx < 0 || idx >= TAB_SIZE || hash_descriptor[idx].name == NULL) {
- LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
- return CRYPT_INVALID_HASH;
- }
- LTC_MUTEX_UNLOCK(&ltc_hash_mutex);
- return CRYPT_OK;
-}
-
-/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_is_valid.c,v $ */
-/* $Revision: 1.6 $ */
-/* $Date: 2006/12/28 01:27:24 $ */
diff --git a/dep/CascLib/src/libtomcrypt/src/misc/crypt_libc.c b/dep/CascLib/src/libtomcrypt/src/misc/crypt_libc.c
deleted file mode 100644
index bcc89f4f94d..00000000000
--- a/dep/CascLib/src/libtomcrypt/src/misc/crypt_libc.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/*****************************************************************************/
-/* crypt_libc.c Copyright (c) Ladislav Zezula 2010 */
-/*---------------------------------------------------------------------------*/
-/* Description: */
-/*---------------------------------------------------------------------------*/
-/* Date Ver Who Comment */
-/* -------- ---- --- ------- */
-/* 05.05.10 1.00 Lad The first version of crypt_libc.c */
-/*****************************************************************************/
-
-// LibTomCrypt header
-#include <stdlib.h>
-#include "../headers/tomcrypt.h"
-
-void * LibTomMalloc(size_t n)
-{
- return malloc(n);
-}
-
-void * LibTomCalloc(size_t n, size_t s)
-{
- return calloc(n, s);
-}
-
-void * LibTomRealloc(void *p, size_t n)
-{
- return realloc(p, n);
-}
-
-void LibTomFree(void * p)
-{
- free(p);
-}
-
-clock_t LibTomClock(void)
-{
- return clock();
-}
-
-void LibTomQsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *))
-{
- qsort(base, nmemb, size, compar);
-}
diff --git a/dep/CascLib/src/md5/md5.cpp b/dep/CascLib/src/md5/md5.cpp
new file mode 100644
index 00000000000..b6cc49db42b
--- /dev/null
+++ b/dep/CascLib/src/md5/md5.cpp
@@ -0,0 +1,291 @@
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001. No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * (This is a heavily cut-down "BSD license".)
+ *
+ * This differs from Colin Plumb's older public domain implementation in that
+ * no exactly 32-bit integer data type is required (any 32-bit or wider
+ * unsigned integer data type will do), there's no compile-time endianness
+ * configuration, and the function prototypes match OpenSSL's. No code from
+ * Colin Plumb's implementation has been reused; this comment merely compares
+ * the properties of the two independent implementations.
+ *
+ * The primary goals of this implementation are portability and ease of use.
+ * It is meant to be fast, but not as fast as possible. Some known
+ * optimizations are not included to reduce source code size and avoid
+ * compile-time configuration.
+ */
+
+#ifndef HAVE_OPENSSL
+
+#include <string.h>
+
+#include "md5.h"
+
+/*
+ * The basic MD5 functions.
+ *
+ * F and G are optimized compared to their RFC 1321 definitions for
+ * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
+ * implementation.
+ */
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z) (((x) ^ (y)) ^ (z))
+#define H2(x, y, z) ((x) ^ ((y) ^ (z)))
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+
+/*
+ * The MD5 transformation for all four rounds.
+ */
+#define STEP(f, a, b, c, d, x, t, s) \
+ (a) += f((b), (c), (d)) + (x) + (t); \
+ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+ (a) += (b);
+
+/*
+ * SET reads 4 input bytes in little-endian byte order and stores them in a
+ * properly aligned word in host byte order.
+ *
+ * The check for little-endian architectures that tolerate unaligned memory
+ * accesses is just an optimization. Nothing will break if it fails to detect
+ * a suitable architecture.
+ *
+ * Unfortunately, this optimization may be a C strict aliasing rules violation
+ * if the caller's data buffer has effective type that cannot be aliased by
+ * MD5_u32plus. In practice, this problem may occur if these MD5 routines are
+ * inlined into a calling function, or with future and dangerously advanced
+ * link-time optimizations. For the time being, keeping these MD5 routines in
+ * their own translation unit avoids the problem.
+ */
+#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
+#define SET(n) \
+ (*(MD5_u32plus *)&ptr[(n) * 4])
+#define GET(n) \
+ SET(n)
+#else
+#define SET(n) \
+ (ctx->block[(n)] = \
+ (MD5_u32plus)ptr[(n) * 4] | \
+ ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
+#define GET(n) \
+ (ctx->block[(n)])
+#endif
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update the bit
+ * counters. There are no alignment requirements.
+ */
+static const void *body(MD5_CTX *ctx, const void *data, unsigned long size)
+{
+ const unsigned char *ptr;
+ MD5_u32plus a, b, c, d;
+ MD5_u32plus saved_a, saved_b, saved_c, saved_d;
+
+ ptr = (const unsigned char *)data;
+
+ a = ctx->a;
+ b = ctx->b;
+ c = ctx->c;
+ d = ctx->d;
+
+ do {
+ saved_a = a;
+ saved_b = b;
+ saved_c = c;
+ saved_d = d;
+
+/* Round 1 */
+ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
+ STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+ STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+ STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+ STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+ STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+ STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+ STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+ STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+ STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+ STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+ STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+ STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+ STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+ STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+ STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+
+/* Round 2 */
+ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+ STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+ STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+ STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+ STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+ STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+ STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+ STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+ STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+ STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+ STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+ STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+ STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+ STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+ STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+ STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+/* Round 3 */
+ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+ STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11)
+ STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+ STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23)
+ STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+ STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+ STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+ STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23)
+ STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+ STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11)
+ STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+ STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23)
+ STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+ STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11)
+ STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+ STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+/* Round 4 */
+ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+ STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+ STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+ STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+ STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+ STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+ STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+ STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+ STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+ STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+ STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+ STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+ STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+ STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+ STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+ STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+ a += saved_a;
+ b += saved_b;
+ c += saved_c;
+ d += saved_d;
+
+ ptr += 64;
+ } while (size -= 64);
+
+ ctx->a = a;
+ ctx->b = b;
+ ctx->c = c;
+ ctx->d = d;
+
+ return ptr;
+}
+
+void MD5_Init(MD5_CTX *ctx)
+{
+ ctx->a = 0x67452301;
+ ctx->b = 0xefcdab89;
+ ctx->c = 0x98badcfe;
+ ctx->d = 0x10325476;
+
+ ctx->lo = 0;
+ ctx->hi = 0;
+}
+
+void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size)
+{
+ MD5_u32plus saved_lo;
+ unsigned long used, available;
+
+ saved_lo = ctx->lo;
+ if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
+ ctx->hi++;
+ ctx->hi += size >> 29;
+
+ used = saved_lo & 0x3f;
+
+ if (used) {
+ available = 64 - used;
+
+ if (size < available) {
+ memcpy(&ctx->buffer[used], data, size);
+ return;
+ }
+
+ memcpy(&ctx->buffer[used], data, available);
+ data = (const unsigned char *)data + available;
+ size -= available;
+ body(ctx, ctx->buffer, 64);
+ }
+
+ if (size >= 64) {
+ data = body(ctx, data, size & ~(unsigned long)0x3f);
+ size &= 0x3f;
+ }
+
+ memcpy(ctx->buffer, data, size);
+}
+
+#define OUT(dst, src) \
+ (dst)[0] = (unsigned char)(src); \
+ (dst)[1] = (unsigned char)((src) >> 8); \
+ (dst)[2] = (unsigned char)((src) >> 16); \
+ (dst)[3] = (unsigned char)((src) >> 24);
+
+void MD5_Final(unsigned char *result, MD5_CTX *ctx)
+{
+ unsigned long used, available;
+
+ used = ctx->lo & 0x3f;
+
+ ctx->buffer[used++] = 0x80;
+
+ available = 64 - used;
+
+ if (available < 8) {
+ memset(&ctx->buffer[used], 0, available);
+ body(ctx, ctx->buffer, 64);
+ used = 0;
+ available = 64;
+ }
+
+ memset(&ctx->buffer[used], 0, available - 8);
+
+ ctx->lo <<= 3;
+ OUT(&ctx->buffer[56], ctx->lo)
+ OUT(&ctx->buffer[60], ctx->hi)
+
+ body(ctx, ctx->buffer, 64);
+
+ OUT(&result[0], ctx->a)
+ OUT(&result[4], ctx->b)
+ OUT(&result[8], ctx->c)
+ OUT(&result[12], ctx->d)
+
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+#endif
diff --git a/dep/CascLib/src/md5/md5.h b/dep/CascLib/src/md5/md5.h
new file mode 100644
index 00000000000..4216c0204c8
--- /dev/null
+++ b/dep/CascLib/src/md5/md5.h
@@ -0,0 +1,45 @@
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001. No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See md5.c for more information.
+ */
+
+#ifdef HAVE_OPENSSL
+#include <openssl/md5.h>
+#elif !defined(_MD5_H)
+#define _MD5_H
+
+/* Any 32-bit or wider unsigned integer data type will do */
+typedef unsigned int MD5_u32plus;
+
+typedef struct {
+ MD5_u32plus lo, hi;
+ MD5_u32plus a, b, c, d;
+ unsigned char buffer[64];
+ MD5_u32plus block[16];
+} MD5_CTX;
+
+void MD5_Init(MD5_CTX *ctx);
+void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size);
+void MD5_Final(unsigned char *result, MD5_CTX *ctx);
+
+#endif
diff --git a/dep/PackageList.txt b/dep/PackageList.txt
index e928f137448..e539385e789 100644
--- a/dep/PackageList.txt
+++ b/dep/PackageList.txt
@@ -62,7 +62,7 @@ recastnavigation (Recast is state of the art navigation mesh construction toolse
CascLib (An open-source implementation of library for reading CASC storage from Blizzard games since 2014)
https://github.com/ladislav-zezula/CascLib
- Version: c63818ecf8d998fae811a2d6db23045f4f6e36a1
+ Version: a1197edf0b3bd4d52c3f39be7fa7b44bb0b98012
rapidjson (A fast JSON parser/generator for C++ with both SAX/DOM style API http://rapidjson.org/)
https://github.com/miloyip/rapidjson
diff --git a/src/tools/extractor_common/CascHandles.cpp b/src/tools/extractor_common/CascHandles.cpp
index 44805fdccd1..9487b3de093 100644
--- a/src/tools/extractor_common/CascHandles.cpp
+++ b/src/tools/extractor_common/CascHandles.cpp
@@ -72,32 +72,30 @@ CASC::StorageHandle CASC::OpenStorage(boost::filesystem::path const& path, DWORD
namespace CASC
{
- static DWORD GetStorageInfo(StorageHandle const& storage, CASC_STORAGE_INFO_CLASS storageInfoClass)
+ template<typename T>
+ static bool GetStorageInfo(StorageHandle const& storage, CASC_STORAGE_INFO_CLASS storageInfoClass, T* value)
{
- DWORD value = 0;
size_t infoDataSizeNeeded = 0;
- if (!::CascGetStorageInfo(storage.get(), storageInfoClass, &value, sizeof(value), &infoDataSizeNeeded))
- {
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
- {
- std::unique_ptr<char> buf(new char[infoDataSizeNeeded]);
- ::CascGetStorageInfo(storage.get(), storageInfoClass, buf.get(), infoDataSizeNeeded, &infoDataSizeNeeded);
- return *reinterpret_cast<DWORD*>(buf.get());
- }
- }
-
- return value;
+ return ::CascGetStorageInfo(storage.get(), storageInfoClass, value, sizeof(T), &infoDataSizeNeeded);
}
}
DWORD CASC::GetBuildNumber(StorageHandle const& storage)
{
- return GetStorageInfo(storage, CascStorageGameBuild);
+ CASC_STORAGE_PRODUCT product;
+ if (GetStorageInfo(storage, CascStorageProduct, &product))
+ return product.dwBuildNumber;
+
+ return 0;
}
DWORD CASC::GetInstalledLocalesMask(StorageHandle const& storage)
{
- return GetStorageInfo(storage, CascStorageInstalledLocales);
+ DWORD locales;
+ if (GetStorageInfo(storage, CascStorageInstalledLocales, &locales))
+ return locales;
+
+ return 0;
}
CASC::FileHandle CASC::OpenFile(StorageHandle const& storage, char const* fileName, DWORD localeMask, bool printErrors /*= false*/)