aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src
diff options
context:
space:
mode:
authorNayd <dnpd.dd@gmail.com>2015-01-11 22:12:49 +0000
committerNayd <dnpd.dd@gmail.com>2015-01-11 23:02:19 +0000
commit31e3dc2e75459f681480ae09641f1087096dfd69 (patch)
tree70685fc84efd01d450f8bc06a4cb266e6478b694 /dep/CascLib/src
parent8bbd9133d57fc34b77544cbdd59526ed9ccaa607 (diff)
Dep/CascLib: Update to https://github.com/ladislav-zezula/CascLib/commit/5d3789af3435534c288c2145e158d422651c7fe1
Closes #13866
Diffstat (limited to 'dep/CascLib/src')
-rw-r--r--dep/CascLib/src/CascBuildCfg.cpp6
-rw-r--r--dep/CascLib/src/CascCommon.h141
-rw-r--r--dep/CascLib/src/CascDumpData.cpp361
-rw-r--r--dep/CascLib/src/CascFindFile.cpp66
-rw-r--r--dep/CascLib/src/CascLib.h24
-rw-r--r--dep/CascLib/src/CascMndxRoot.cpp53
-rw-r--r--dep/CascLib/src/CascMndxRoot.h2
-rw-r--r--dep/CascLib/src/CascOpenFile.cpp191
-rw-r--r--dep/CascLib/src/CascOpenStorage.cpp605
-rw-r--r--dep/CascLib/src/CascReadFile.cpp224
-rw-r--r--dep/CascLib/src/common/Common.cpp23
-rw-r--r--dep/CascLib/src/common/Common.h9
-rw-r--r--dep/CascLib/src/common/FileStream.cpp8
-rw-r--r--dep/CascLib/src/common/ListFile.cpp163
-rw-r--r--dep/CascLib/src/common/ListFile.h32
-rw-r--r--dep/CascLib/src/common/Map.h2
16 files changed, 1128 insertions, 782 deletions
diff --git a/dep/CascLib/src/CascBuildCfg.cpp b/dep/CascLib/src/CascBuildCfg.cpp
index effb74552c5..b1d6584771c 100644
--- a/dep/CascLib/src/CascBuildCfg.cpp
+++ b/dep/CascLib/src/CascBuildCfg.cpp
@@ -99,7 +99,7 @@ static TCHAR * CheckForIndexDirectory(TCascStorage * hs, const TCHAR * szSubDir)
return hs->szIndexPath;
}
- delete [] szIndexPath;
+ CASC_FREE(szIndexPath);
return NULL;
}
@@ -270,7 +270,7 @@ static int LoadInfoVariable(PQUERY_KEY pVarBlob, const char * szLineBegin, const
// Initialize the blob
pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) + 1);
- pVarBlob->cbData = (size_t)(szLinePtr - szLineBegin);
+ pVarBlob->cbData = (DWORD)(szLinePtr - szLineBegin);
// Check for success
if(pVarBlob->pbData == NULL)
@@ -826,7 +826,7 @@ static int LoadCdnBuildFile(TCascStorage * hs, PQUERY_KEY pFileBlob)
//-----------------------------------------------------------------------------
// Public functions
-int LoadBuildConfiguration(TCascStorage * hs)
+int LoadBuildInfo(TCascStorage * hs)
{
QUERY_KEY InfoFile = {NULL, 0};
QUERY_KEY FileData = {NULL, 0};
diff --git a/dep/CascLib/src/CascCommon.h b/dep/CascLib/src/CascCommon.h
index b2f6a156e39..51b6689ec16 100644
--- a/dep/CascLib/src/CascCommon.h
+++ b/dep/CascLib/src/CascCommon.h
@@ -23,9 +23,9 @@
#include "CascPort.h"
#include "common/Common.h"
+#include "common/Map.h"
#include "common/FileStream.h"
#include "common/ListFile.h"
-#include "common/Map.h"
// Headers from LibTomCrypt
#include "libtomcrypt/src/headers/tomcrypt.h"
@@ -48,6 +48,20 @@
#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.xxx:
+// 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)
@@ -57,16 +71,46 @@
#define CASC_PACKAGE_BUFFER 0x1000
//-----------------------------------------------------------------------------
-// Structures
+// On-disk structures
+
+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;
+
+typedef struct _FILE_ROOT_ENTRY
+{
+ DWORD EncodingKey[4]; // MD5 of the file
+ ULONGLONG FileNameHash; // Jenkins hash of the file name
+
+} FILE_ROOT_ENTRY, *PFILE_ROOT_ENTRY;
+
+typedef struct _ROOT_BLOCK_INFO
+{
+ PFILE_LOCALE_BLOCK pLocaleBlockHdr; // Pointer to the locale block
+ PDWORD pInt32Array; // Pointer to the array of 32-bit integers
+ PFILE_ROOT_ENTRY pRootEntries;
+
+} ROOT_BLOCK_INFO, *PROOT_BLOCK_INFO;
+
+//-----------------------------------------------------------------------------
+// In-memory structures
+class TMndxFindResult;
struct TFileStream;
struct _MAR_FILE;
typedef struct _CASC_INDEX_ENTRY
{
BYTE IndexKey[CASC_FILE_KEY_SIZE]; // The first 9 bytes of the encoding key
- BYTE FileOffset[5]; //
- BYTE FileSize[4];
+ 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
@@ -113,7 +157,7 @@ typedef struct _CASC_ENCODING_HEADER
typedef struct _CASC_ENCODING_ENTRY
{
USHORT KeyCount; // Number of subitems
- BYTE FileSizeBytes[4]; // File size as bytes (big-endian)
+ BYTE FileSizeBE[4]; // Compressed file size (header area + frame headers + compressed frames), in bytes
BYTE EncodingKey[MD5_HASH_SIZE]; // File encoding key
// Followed by the index keys
@@ -132,29 +176,35 @@ typedef struct _CASC_ROOT_LOCALE_BLOCK
} CASC_ROOT_LOCALE_BLOCK, *PCASC_ROOT_LOCALE_BLOCK;
+// Root file entry for CASC storages with MNDX root file (Heroes of the Storm)
+// Corresponds to the in-file structure
+typedef struct _CASC_ROOT_ENTRY_MNDX
+{
+ DWORD Flags; // High 8 bits: Flags, low 24 bits: package index
+ BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key for the file
+ DWORD FileSize; // Uncompressed file size, in bytes
+
+} CASC_ROOT_ENTRY_MNDX, *PCASC_ROOT_ENTRY_MNDX;
+
+// 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_ROOT_ENTRY
{
- BYTE EncodingKey[MD5_HASH_SIZE]; // File encoding key (MD5)
ULONGLONG FileNameHash; // Jenkins hash of the file name
- DWORD Locales; // File locales (see CASC_LOCALE_XXX)
- DWORD Flags; // File flags
+ DWORD SumValue; // Sum value
+ DWORD Locales; // Locale flags of the file
+ DWORD EncodingKey[4]; // File encoding key (MD5)
} CASC_ROOT_ENTRY, *PCASC_ROOT_ENTRY;
-typedef struct _CASC_ROOT_KEY_INFO
+// Definition of the hash table for CASC root items
+typedef struct _CASC_ROOT_HASH_TABLE
{
- BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key for the file, obtained from root info
- ULONGLONG FileSize; // Size of the file, in bytes
- BYTE Flags; // File flags
-} CASC_ROOT_KEY_INFO, *PCASC_ROOT_KEY_INFO;
+ PCASC_ROOT_ENTRY TablePtr; // Pointer to the CASC root table
+ DWORD TableSize; // Total size of the root table
+ DWORD ItemCount; // Number of items currently in the table
-typedef struct _CASC_MNDX_ENTRY
-{
- DWORD Flags; // High 8 bits: Flags, low 24 bits: package index
- BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key for the file
- DWORD FileSize; // Size of the file, in bytes
-
-} CASC_MNDX_ENTRY, *PCASC_MNDX_ENTRY;
+} CASC_ROOT_HASH_TABLE, *PCASC_ROOT_HASH_TABLE;
typedef struct _CASC_MNDX_INFO
{
@@ -174,8 +224,8 @@ typedef struct _CASC_MNDX_INFO
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_MNDX_ENTRY pMndxEntries;
- PCASC_MNDX_ENTRY * ppValidEntries;
+ PCASC_ROOT_ENTRY_MNDX pMndxEntries;
+ PCASC_ROOT_ENTRY_MNDX * ppValidEntries;
} CASC_MNDX_INFO, *PCASC_MNDX_INFO;
@@ -208,6 +258,7 @@ typedef struct _TCascStorage
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
QUERY_KEY CdnConfigKey;
QUERY_KEY CdnBuildKey;
@@ -239,9 +290,7 @@ typedef struct _TCascStorage
PCASC_ENCODING_ENTRY * ppEncodingEntries; // Map of encoding entries
size_t nEncodingEntries;
- PCASC_ROOT_ENTRY * ppRootEntries; // Sorted array of root entries
- PCASC_ROOT_ENTRY pRootEntries; // Linear array of root entries
- size_t nRootEntries; // Number of root entries
+ CASC_ROOT_HASH_TABLE RootTable; // Hash table for the root entries
PCASC_MNDX_INFO pMndxInfo; // Used for storages which have MNDX/MAR file
PCASC_PACKAGES pPackages; // Linear list of present packages
@@ -258,9 +307,11 @@ typedef struct _TCascFile
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_FILE_FRAME pFrames; // Array of file frames
DWORD FrameCount; // Number of the file frames
@@ -270,9 +321,15 @@ typedef struct _TCascFile
DWORD CacheStart; // Starting offset in the cache
DWORD CacheEnd; // Ending offset in the cache
-} TCascFile;
+#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
-class TMndxFindResult;
+} TCascFile;
typedef struct _TCascSearch
{
@@ -284,8 +341,7 @@ typedef struct _TCascSearch
char * szMask;
char szFileName[MAX_PATH]; // Buffer for the file name
char szNormName[MAX_PATH]; // Buffer for normalized file name
- ULONGLONG FileNameHash; // Name hash being searched right now
- size_t RootIndex; // Root index of the previously found item
+ DWORD RootIndex; // Root index of the previously found item
DWORD dwState; // Pointer to the state (0 = listfile, 1 = nameless, 2 = done)
BYTE BitArray[1]; // Bit array of already-reported files
@@ -305,17 +361,10 @@ typedef struct _TCascSearch
//
#if defined(_MSC_VER) && defined(_DEBUG)
-/*
-void * DbgRealloc(void * ptr, size_t nSize);
-#define CASC_REALLOC(type, ptr, count) (type *)DbgRealloc(ptr, ((count) * sizeof(type)))
+#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)
-*/
-
-#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)
#else
@@ -336,7 +385,7 @@ ULONGLONG ConvertBytesToInteger_5(LPBYTE ValueAsBytes);
//-----------------------------------------------------------------------------
// Build configuration reading
-int LoadBuildConfiguration(TCascStorage * hs);
+int LoadBuildInfo(TCascStorage * hs);
//-----------------------------------------------------------------------------
// Internal file functions
@@ -344,31 +393,23 @@ int LoadBuildConfiguration(TCascStorage * hs);
TCascStorage * IsValidStorageHandle(HANDLE hStorage);
TCascFile * IsValidFileHandle(HANDLE hFile);
-PCASC_ROOT_ENTRY FindFirstRootEntry(TCascStorage * hs, const char * szFileName, size_t * PtrIndex);
+PCASC_ROOT_ENTRY FindRootEntry(TCascStorage * hs, const char * szFileName, DWORD * PtrTableIndex);
PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKey, size_t * PtrIndex);
PCASC_INDEX_ENTRY FindIndexEntry(TCascStorage * hs, PQUERY_KEY pIndexKey);
int CascDecompress(void * pvOutBuffer, PDWORD pcbOutBuffer, void * pvInBuffer, DWORD cbInBuffer);
//-----------------------------------------------------------------------------
-// Dump data
+// Dumping CASC data structures
#ifdef _DEBUG
void CascDumpSparseArray(const char * szFileName, void * pvSparseArray);
void CascDumpNameFragTable(const char * szFileName, void * pvMarFile);
void CascDumpFileNames(const char * szFileName, void * pvMarFile);
-void CascDumpMndxRoot(const char * szFileName, PCASC_MNDX_INFO pMndxInfo);
void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs);
-void CascDumpStorage(const char * szFileName, TCascStorage * hs, const TCHAR * szListFile);
+void CascDumpMndxRoot(const char * szFileName, PCASC_MNDX_INFO pMndxInfo);
+void CascDumpRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, const char * szFormat, const TCHAR * szListFile, int nDumpLevel);
void CascDumpFile(const char * szFileName, HANDLE hFile);
-#else // _DEBUG
-#define CascDumpSparseArray(n,a) /* */
-#define CascDumpNameFragTable(n, m) /* */
-#define CascDumpFileNames(n, m) /* */
-#define CascDumpMndxRoot(n,i) /* */
-#define CascDumpIndexEntries(n,h) /* */
-#define CascDumpStorage(n,h) /* */
-#define CascDumpFile(n,h) /* */
#endif // _DEBUG
#endif // __CASCCOMMON_H__
diff --git a/dep/CascLib/src/CascDumpData.cpp b/dep/CascLib/src/CascDumpData.cpp
index d3871709fce..5dc9110b6cd 100644
--- a/dep/CascLib/src/CascDumpData.cpp
+++ b/dep/CascLib/src/CascDumpData.cpp
@@ -16,24 +16,19 @@
#ifdef _DEBUG // The entire file is only valid for debug purposes
//-----------------------------------------------------------------------------
-// Local functions
+// Forward definitions
-static char * StringFromIndexKey(LPBYTE md5, char * szBuffer)
-{
- return StringFromBinary(md5, 9, szBuffer);
-}
+LPBYTE VerifyLocaleBlock(PROOT_BLOCK_INFO pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd);
-static char * StringFromMD5(LPBYTE md5, char * szBuffer)
-{
- return StringFromBinary(md5, MD5_HASH_SIZE, szBuffer);
-}
+//-----------------------------------------------------------------------------
+// Sort compare functions
static int CompareIndexEntries_FilePos(const void *, const void * pvIndexEntry1, const void * pvIndexEntry2)
{
PCASC_INDEX_ENTRY pIndexEntry1 = (PCASC_INDEX_ENTRY)pvIndexEntry1;
PCASC_INDEX_ENTRY pIndexEntry2 = (PCASC_INDEX_ENTRY)pvIndexEntry2;
- ULONGLONG FileOffset1 = ConvertBytesToInteger_5(pIndexEntry1->FileOffset);
- ULONGLONG FileOffset2 = ConvertBytesToInteger_5(pIndexEntry2->FileOffset);
+ ULONGLONG FileOffset1 = ConvertBytesToInteger_5(pIndexEntry1->FileOffsetBE);
+ ULONGLONG FileOffset2 = ConvertBytesToInteger_5(pIndexEntry2->FileOffsetBE);
DWORD ArchIndex1 = (DWORD)(FileOffset1 >> 0x1E);
DWORD ArchIndex2 = (DWORD)(FileOffset2 >> 0x1E);
@@ -54,98 +49,121 @@ static int CompareIndexEntries_FilePos(const void *, const void * pvIndexEntry1,
return 0;
}
+//-----------------------------------------------------------------------------
+// Local functions
-static char ** CreateFileNameArray(TCascStorage * hs, const TCHAR * szListFile)
+static char * StringFromMD5(LPBYTE md5, char * szBuffer)
{
- PCASC_ROOT_ENTRY pRootEntry;
- char ** FileNameArray = NULL;
- void * pvListFile;
- size_t nRootIndex;
- char szFileName1[MAX_PATH+1];
- char szFileName2[MAX_PATH+1];
-
- // Open the listfile stream and initialize the listfile cache
- pvListFile = ListFile_OpenExternal(szListFile);
- if(pvListFile != NULL)
+ return StringFromBinary(md5, MD5_HASH_SIZE, szBuffer);
+}
+
+static char * FormatFileName(const char * szFormat, TCascStorage * hs)
+{
+ char * szFileName;
+ char * szSrc;
+ char * szTrg;
+
+ // Create copy of the file name
+ szFileName = szSrc = szTrg = NewStr(szFormat, 0);
+ if(szFileName != NULL)
{
- // Allocate the array of file names
- FileNameArray = CASC_ALLOC(char*, hs->nRootEntries);
- if(FileNameArray != NULL)
+ // Format the file name
+ while(szSrc[0] != 0)
{
- // Zero the name array
- memset(FileNameArray, 0, hs->nRootEntries * sizeof(char *));
-
- // Perform search
- while(ListFile_GetNext(pvListFile, "*", szFileName1, MAX_PATH))
+ if(szSrc[0] == '%')
{
- // Create normalized name
- strcpy(szFileName2, szFileName1);
- NormalizeFileName_UpperBkSlash(szFileName2);
-
- // Try to find the root entry
- pRootEntry = FindFirstRootEntry(hs, szFileName2, &nRootIndex);
- if(pRootEntry != NULL)
+ // Replace "%build%" with a build number
+ if(!strncmp(szSrc, "%build%", 7))
{
- assert(nRootIndex < hs->nRootEntries);
- if(FileNameArray[nRootIndex] == NULL)
- FileNameArray[nRootIndex] = NewStr(szFileName1, 0);
+ szTrg += sprintf(szTrg, "%u", hs->dwBuildNumber);
+ szSrc += 7;
+ continue;
}
}
+
+ // Just copy the character
+ *szTrg++ = *szSrc++;
}
- // Close the listfile cache
- ListFile_Free(pvListFile);
+ // Terminate the target file name
+ szTrg[0] = 0;
}
- return FileNameArray;
+ return szFileName;
}
-static void FreeFileNameArray(TCascStorage * hs, char ** FileNameArray)
+FILE * CreateDumpFile(const char * szFormat, TCascStorage * hs)
{
- if(FileNameArray != NULL)
+ FILE * fp = NULL;
+ char * szFileName;
+
+ // Validate the storage handle
+ if(hs != NULL)
{
- // Free all sub-entries
- for(size_t i = 0; i < hs->nRootEntries; i++)
+ // Format the real file name
+ szFileName = FormatFileName(szFormat, hs);
+ if(szFileName != NULL)
{
- if(FileNameArray[i] != NULL)
- CASC_FREE(FileNameArray[i]);
+ // Create the dump file
+ fp = fopen(szFileName, "wt");
+ CASC_FREE(szFileName);
}
-
- // Free the array itself
- CASC_FREE(FileNameArray);
}
+
+ return fp;
}
static void DumpIndexKey(
FILE * fp,
TCascStorage * hs,
- LPBYTE pbEncodingKey,
- PCASC_INDEX_ENTRY * ppIndexEntries)
+ LPBYTE pbIndexKey,
+ int nDumpLevel)
{
PCASC_INDEX_ENTRY pIndexEntry;
+ TCascFile * hf;
QUERY_KEY QueryKey;
- size_t EntryIndex = 0;
- char szMd5[MD5_STRING_SIZE];
+ HANDLE hFile;
+ BYTE HeaderArea[MAX_HEADER_AREA_SIZE];
+ char szBuffer[0x20];
- QueryKey.pbData = pbEncodingKey;
+ QueryKey.pbData = pbIndexKey;
QueryKey.cbData = MD5_HASH_SIZE;
pIndexEntry = FindIndexEntry(hs, &QueryKey);
if(pIndexEntry != NULL)
{
- ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffset);
+ ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffsetBE);
DWORD ArchIndex = (DWORD)(FileOffset >> 0x1E);
- DWORD FileSize = *(PDWORD)pIndexEntry->FileSize;
-
- // Mark the index entry as dumped
- ppIndexEntries[EntryIndex] = NULL;
+ DWORD FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE);
// Mask the file offset
FileOffset &= 0x3FFFFFFF;
- fprintf(fp, " Index: %s, ArchIdx: %02x FileOffset %08x FileSize: %lx\n",
- StringFromIndexKey(pIndexEntry->IndexKey, szMd5),
+ fprintf(fp, " 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
+ fprintf(fp, " FileSize: %X Rest: %s\n",
+ ConvertBytesToInteger_4_LE(&HeaderArea[0x10]),
+ StringFromBinary(&HeaderArea[0x14], 10, szBuffer));
+ }
+ }
}
else
{
@@ -157,122 +175,31 @@ static void DumpEncodingEntry(
FILE * fp,
TCascStorage * hs,
PCASC_ENCODING_ENTRY pEncodingEntry,
- PCASC_INDEX_ENTRY * ppIndexEntries)
+ int nDumpLevel)
{
- LPBYTE pbEncodingKey;
+ LPBYTE pbIndexKey;
char szMd5[MD5_STRING_SIZE];
- fprintf(fp, " Encoding Key: %s Key Count: %u Size: %lx\n",
- StringFromMD5(pEncodingEntry->EncodingKey, szMd5),
- pEncodingEntry->KeyCount,
- ConvertBytesToInteger_4(pEncodingEntry->FileSizeBytes));
-
- // If there is a file key
- if(pEncodingEntry->KeyCount != 0)
- {
- // Get the first encoding key
- pbEncodingKey = pEncodingEntry->EncodingKey + MD5_HASH_SIZE;
+ // If the encoding key exists
+ if(pEncodingEntry != NULL)
+ {
+ fprintf(fp, " Size %lx Key Count: %u\n",
+ ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE),
+ pEncodingEntry->KeyCount);
- // Dump all encoding keys
+ // Dump all index keys
+ pbIndexKey = pEncodingEntry->EncodingKey + MD5_HASH_SIZE;
for(DWORD j = 0; j < pEncodingEntry->KeyCount; j++)
{
- fprintf(fp, " Index Key: %s\n", StringFromMD5(pbEncodingKey, szMd5));
- DumpIndexKey(fp, hs, pbEncodingKey, ppIndexEntries);
- pbEncodingKey += MD5_HASH_SIZE;
+ fprintf(fp, " %s\n", StringFromMD5(pbIndexKey, szMd5));
+ DumpIndexKey(fp, hs, pbIndexKey, nDumpLevel);
+ pbIndexKey += MD5_HASH_SIZE;
}
- }
- else
- {
- fprintf(fp, " ZERO FILE KEYS\n");
- return;
- }
-}
-static void DumpEncodingEntry(
- FILE * fp,
- TCascStorage * hs,
- PCASC_ROOT_ENTRY pRootEntry,
- PCASC_ENCODING_ENTRY * ppEncodingKeys,
- PCASC_INDEX_ENTRY * ppIndexEntries)
-{
- PCASC_ENCODING_ENTRY pEncodingKey;
- QUERY_KEY QueryKey;
- size_t EntryIndex = 0;
-
- // Find the encoding key
- QueryKey.pbData = pRootEntry->EncodingKey;
- QueryKey.cbData = MD5_HASH_SIZE;
- pEncodingKey = FindEncodingEntry(hs, &QueryKey, &EntryIndex);
- if(pEncodingKey == NULL)
- {
- fprintf(fp, " NO ENCODING KEY\n");
- return;
}
-
- // Get the file key, clear the encoding key
- ppEncodingKeys[EntryIndex] = NULL;
- DumpEncodingEntry(fp, hs, pEncodingKey, ppIndexEntries);
-}
-
-static void DumpRootEntries(FILE * fp, TCascStorage * hs, char ** FileNameArray)
-{
- PCASC_ENCODING_ENTRY * ppEncodingEntries; // Array of encoding entries
- PCASC_INDEX_ENTRY * ppIndexEntries; // Complete list of key entries for all files
- const char * szFileName = NULL;
- ULONGLONG PrevNameHash = (ULONGLONG)-1;
- char szMd5[MD5_STRING_SIZE];
-
- // Create copy of the encoding keys and file keys
- ppEncodingEntries = CASC_ALLOC(PCASC_ENCODING_ENTRY, hs->nEncodingEntries);
- ppIndexEntries = CASC_ALLOC(PCASC_INDEX_ENTRY, hs->pIndexEntryMap->ItemCount);
- if(ppEncodingEntries && ppIndexEntries)
+ else
{
- // Copy all pointers
- memcpy(ppEncodingEntries, hs->ppEncodingEntries, hs->nEncodingEntries * sizeof(PCASC_ENCODING_ENTRY));
- Map_EnumObjects(hs->pIndexEntryMap, (void **)ppIndexEntries);
-
- // Parse all entries
- for(size_t i = 0; i < hs->nRootEntries; i++)
- {
- PCASC_ROOT_ENTRY pRootEntry = hs->ppRootEntries[i];
- const char * szDuplicate = "";
-
- // Check duplicates
- if(pRootEntry->FileNameHash != PrevNameHash)
- szFileName = FileNameArray[i];
- else
- szDuplicate = "(DUPLICATE) ";
-
- // Dump the root entry
- fprintf(fp, "NameHash: %016llx Locales: %08lx MD5: %s %sFileName: %s\n",
- pRootEntry->FileNameHash,
- pRootEntry->Locales,
- StringFromMD5(pRootEntry->EncodingKey, szMd5),
- szDuplicate,
- szFileName);
-
- DumpEncodingEntry(fp, hs, pRootEntry, ppEncodingEntries, ppIndexEntries);
- PrevNameHash = pRootEntry->FileNameHash;
- fprintf(fp, "\n");
- }
-
- // Dump all orphaned encoding keys
- for(size_t i = 0; i < hs->nEncodingEntries; i++)
- {
- if(ppEncodingEntries[i] != NULL)
- {
- fprintf(fp, "[NO ROOT KEY]\n");
-
- DumpEncodingEntry(fp, hs, ppEncodingEntries[i], ppIndexEntries);
- ppEncodingEntries[i] = NULL;
-
- fprintf(fp, "\n");
- }
- }
-
-
- CASC_FREE(ppIndexEntries);
- CASC_FREE(ppEncodingEntries);
+ fprintf(fp, " NO ENCODING KEYS\n");
}
}
@@ -398,7 +325,7 @@ void CascDumpFileNames(const char * szFileName, void * pvMarFile)
void CascDumpMndxRoot(const char * szFileName, PCASC_MNDX_INFO pMndxInfo)
{
- PCASC_MNDX_ENTRY pMndxEntry;
+ PCASC_ROOT_ENTRY_MNDX pRootEntry;
FILE * fp;
char szMd5[MD5_STRING_SIZE];
@@ -409,12 +336,12 @@ void CascDumpMndxRoot(const char * szFileName, PCASC_MNDX_INFO pMndxInfo)
fprintf(fp, "Indx Fl+Asset EncodingKey FileSize\n==== ======== ================================ ========\n");
for(DWORD i = 0; i < pMndxInfo->MndxEntriesValid; i++)
{
- pMndxEntry = pMndxInfo->ppValidEntries[i];
+ pRootEntry = pMndxInfo->ppValidEntries[i];
fprintf(fp, "%04X %08X %s %08X\n", i,
- pMndxEntry->Flags,
- StringFromMD5(pMndxEntry->EncodingKey, szMd5),
- pMndxEntry->FileSize);
+ pRootEntry->Flags,
+ StringFromMD5(pRootEntry->EncodingKey, szMd5),
+ pRootEntry->FileSize);
}
fclose(fp);
}
@@ -446,11 +373,11 @@ void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs)
for(size_t i = 0; i < nIndexEntries; i++)
{
PCASC_INDEX_ENTRY pIndexEntry = ppIndexEntries[i];
- ULONGLONG ArchOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffset);
+ ULONGLONG ArchOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffsetBE);
DWORD ArchIndex = (DWORD)(ArchOffset >> 0x1E);
DWORD FileSize;
- FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSize);
+ FileSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE);
ArchOffset &= 0x3FFFFFFF;
fprintf(fp, " %02X %08X %08X %s\n", ArchIndex, ArchOffset, FileSize, StringFromBinary(pIndexEntry->IndexKey, CASC_FILE_KEY_SIZE, szIndexKey));
@@ -463,33 +390,81 @@ void CascDumpIndexEntries(const char * szFileName, TCascStorage * hs)
}
}
-void CascDumpStorage(const char * szFileName, TCascStorage * hs, const TCHAR * szListFile)
+void CascDumpRootFile(
+ TCascStorage * hs,
+ LPBYTE pbRootFile,
+ DWORD cbRootFile,
+ const char * szFormat,
+ const TCHAR * szListFile,
+ int nDumpLevel)
{
- char ** FileNameArray = NULL;
+ PCASC_ENCODING_ENTRY pEncodingEntry;
+ ROOT_BLOCK_INFO BlockInfo;
+ PLISTFILE_MAP pListMap;
+ QUERY_KEY EncodingKey;
+ LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
+ LPBYTE pbFilePointer;
FILE * fp;
+ char szOneLine[0x100];
+ DWORD i;
- // Validate the storage handle
- if(hs != NULL)
+ // This function only dumps WoW-style root file
+ assert(*(PDWORD)pbRootFile != CASC_MNDX_SIGNATURE);
+
+ // Create the dump file
+ fp = CreateDumpFile(szFormat, hs);
+ if(fp != NULL)
{
- // Create the dump file
- fp = fopen(szFileName, "wt");
- if(fp != NULL)
+ // Create the listfile map
+// DWORD dwTickCount = GetTickCount();
+ pListMap = ListFile_CreateMap(szListFile);
+// dwTickCount = GetTickCount() - dwTickCount;
+
+ // Dump the root entries as-is
+ for(pbFilePointer = pbRootFile; pbFilePointer <= pbRootFileEnd; )
{
- // If we also have listfile, open it
- if(szListFile != NULL)
- FileNameArray = CreateFileNameArray(hs, szListFile);
+ // Validate the root block
+ pbFilePointer = VerifyLocaleBlock(&BlockInfo, pbFilePointer, pbRootFileEnd);
+ if(pbFilePointer == NULL)
+ break;
+
+ // Dump the locale block
+ fprintf(fp, "Flags: %08X Locales: %08X NumberOfFiles: %u\n"
+ "=========================================================\n",
+ BlockInfo.pLocaleBlockHdr->Flags,
+ BlockInfo.pLocaleBlockHdr->Locales,
+ BlockInfo.pLocaleBlockHdr->NumberOfFiles);
- // Dump all root keys
- fprintf(fp, "Root Entries\n=========\n\n");
- DumpRootEntries(fp, hs, FileNameArray);
+ // Dump the hashes and encoding keys
+ for(i = 0; i < BlockInfo.pLocaleBlockHdr->NumberOfFiles; i++)
+ {
+ // Dump the entry
+ fprintf(fp, "%08X %08X-%08X %s %s\n",
+ (DWORD)(BlockInfo.pInt32Array[i]),
+ (DWORD)(BlockInfo.pRootEntries[i].FileNameHash >> 0x20),
+ (DWORD)(BlockInfo.pRootEntries[i].FileNameHash),
+ StringFromMD5((LPBYTE)BlockInfo.pRootEntries[i].EncodingKey, szOneLine),
+ ListFile_FindName(pListMap, BlockInfo.pRootEntries[i].FileNameHash));
+
+ // Find the encoding entry in the encoding table
+ if(nDumpLevel > 1)
+ {
+ EncodingKey.pbData = (LPBYTE)BlockInfo.pRootEntries[i].EncodingKey;
+ EncodingKey.cbData = MD5_HASH_SIZE;
+ pEncodingEntry = FindEncodingEntry(hs, &EncodingKey, NULL);
+ DumpEncodingEntry(fp, hs, pEncodingEntry, nDumpLevel);
+ }
+ }
- FreeFileNameArray(hs, FileNameArray);
- fclose(fp);
+ // Put extra newline
+ fprintf(fp, "\n");
}
+
+ ListFile_FreeMap(pListMap);
+ fclose(fp);
}
}
-
void CascDumpFile(const char * szFileName, HANDLE hFile)
{
FILE * fp;
diff --git a/dep/CascLib/src/CascFindFile.cpp b/dep/CascLib/src/CascFindFile.cpp
index f1b14560bed..753e00a90a2 100644
--- a/dep/CascLib/src/CascFindFile.cpp
+++ b/dep/CascLib/src/CascFindFile.cpp
@@ -72,7 +72,7 @@ static bool VerifyRootEntry(TCascSearch * pSearch, PCASC_ROOT_ENTRY pRootEntry,
// dwRootEntries++;
// Now try to find that encoding key in the array of encoding keys
- QueryKey.pbData = pRootEntry->EncodingKey;
+ QueryKey.pbData = (LPBYTE)pRootEntry->EncodingKey;
QueryKey.cbData = MD5_HASH_SIZE;
pEncodingEntry = FindEncodingEntry(hs, &QueryKey, NULL);
if(pEncodingEntry == NULL)
@@ -94,7 +94,9 @@ static bool VerifyRootEntry(TCascSearch * pSearch, PCASC_ROOT_ENTRY pRootEntry,
pFindData->FileNameHash = pRootEntry->FileNameHash;
pFindData->dwPackageIndex = 0;
pFindData->dwLocaleFlags = pRootEntry->Locales;
- pFindData->dwFileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBytes);
+
+ // Fill-in the file size
+ pFindData->dwFileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBE);
return true;
}
@@ -104,7 +106,7 @@ static TCascSearch * AllocateSearchHandle(TCascStorage * hs, const TCHAR * szLis
size_t cbToAllocate;
// When using the MNDX info, do not allocate the extra bit array
- cbToAllocate = sizeof(TCascSearch) + ((hs->pMndxInfo == NULL) ? (hs->nRootEntries / 8) : 0);
+ cbToAllocate = sizeof(TCascSearch) + ((hs->pMndxInfo == NULL) ? (hs->RootTable.TableSize / 8) : 0);
pSearch = (TCascSearch *)CASC_ALLOC(BYTE, cbToAllocate);
if(pSearch != NULL)
{
@@ -147,53 +149,28 @@ static bool DoStorageSearch_ListFile(TCascSearch * pSearch, PCASC_FIND_DATA pFin
{
PCASC_ROOT_ENTRY pRootEntry;
TCascStorage * hs = pSearch->hs;
- size_t RootIndex;
+ DWORD TableIndex = 0;
- for(;;)
+ // Get next file from the listfile
+ while(ListFile_GetNext(pSearch->pCache, pSearch->szMask, pSearch->szFileName, MAX_PATH))
{
- // Shall we get a new file name from the listfile?
- if(pSearch->FileNameHash == 0)
+#ifdef _DEBUG
+ //if(!_stricmp(pSearch->szFileName, "Character\\NightElf\\Female\\NightElf_FemaleFacialLowerHair01_02_HD.blp"))
+ // DebugBreak();
+#endif
+
+ // Find the root entry
+ pRootEntry = FindRootEntry(hs, pSearch->szFileName, &TableIndex);
+ if(pRootEntry != NULL)
{
- // Try to get next file from the listfile
- if(!ListFile_GetNext(pSearch->pCache, pSearch->szMask, pSearch->szFileName, MAX_PATH))
- break;
-
-// if(!_stricmp(pSearch->szFileName, "Interface\\Glues\\MODELS\\UI_MainMenu_Warlords\\Caustic_MedFreq.blp"))
-// DebugBreak();
-
- // Normalize the file name
- strcpy(pSearch->szNormName, pSearch->szFileName);
- NormalizeFileName_UpperBkSlash(pSearch->szNormName);
-
- // Find the first root entry belonging to this file name
- pRootEntry = FindFirstRootEntry(pSearch->hs, pSearch->szNormName, &RootIndex);
- if(pRootEntry == NULL)
- continue;
-
- // We have the name now, search all locales
- pSearch->FileNameHash = pRootEntry->FileNameHash;
- pSearch->RootIndex = RootIndex;
- }
-
- // Is the root index in range?
- while(pSearch->RootIndex < hs->nRootEntries)
- {
- // Get the next root entry. If name mismatches, stop searching
- pRootEntry = hs->ppRootEntries[pSearch->RootIndex];
- if(pRootEntry->FileNameHash != pSearch->FileNameHash)
- break;
-
// Verify whether the file exists in the storage
- if(VerifyRootEntry(pSearch, pRootEntry, pFindData, pSearch->RootIndex++))
+ if(VerifyRootEntry(pSearch, pRootEntry, pFindData, TableIndex))
{
strcpy(pFindData->szFileName, pSearch->szFileName);
pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName);
return true;
}
}
-
- // Reset the name hash and root index and retry search
- pSearch->FileNameHash = 0;
}
// Listfile search ended
@@ -206,13 +183,13 @@ static bool DoStorageSearch_Hash(TCascSearch * pSearch, PCASC_FIND_DATA pFindDat
TCascStorage * hs = pSearch->hs;
// Check if there is more files with the same name hash
- while(pSearch->RootIndex < hs->nRootEntries)
+ while(pSearch->RootIndex < hs->RootTable.TableSize)
{
// Get the pointer to the root entry
- pRootEntry = hs->ppRootEntries[pSearch->RootIndex];
+ pRootEntry = hs->RootTable.TablePtr + pSearch->RootIndex;
// Verify if that root entry exists in the CASC storage
- // Note that the file name will be there from the previous search
+ // and was not found before
if(VerifyRootEntry(pSearch, pRootEntry, pFindData, pSearch->RootIndex))
{
pFindData->szFileName[0] = 0;
@@ -242,7 +219,6 @@ static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
pSearch->pCache = ListFile_OpenExternal(pSearch->szListFile);
// Move the search phase to the listfile searching
- pSearch->FileNameHash = 0;
pSearch->RootIndex = 0;
pSearch->dwState++;
@@ -259,7 +235,7 @@ static bool DoStorageSearch(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
return true;
// Move to the nameless search state
- pSearch->RootIndex = 0;
+ assert(pSearch->RootIndex == 0);
pSearch->dwState++;
}
diff --git a/dep/CascLib/src/CascLib.h b/dep/CascLib/src/CascLib.h
index a3960a558c2..330a4b2bb49 100644
--- a/dep/CascLib/src/CascLib.h
+++ b/dep/CascLib/src/CascLib.h
@@ -66,7 +66,7 @@ extern "C" {
#define CASC_LOCALE_UNKNOWN1 0x00000001
#define CASC_LOCALE_ENUS 0x00000002
#define CASC_LOCALE_KOKR 0x00000004
-#define CASC_LOCALE_UNKNOWN8 0x00000008
+#define CASC_LOCALE_RESERVED 0x00000008
#define CASC_LOCALE_FRFR 0x00000010
#define CASC_LOCALE_DEDE 0x00000020
#define CASC_LOCALE_ZHCN 0x00000040
@@ -81,6 +81,24 @@ 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_DUAL_LANG 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)
#ifndef MD5_HASH_SIZE
@@ -110,6 +128,8 @@ typedef enum _CASC_STORAGE_INFO_CLASS
{
CascStorageFileCount,
CascStorageFeatures,
+ CascStorageGameInfo,
+ CascStorageGameBuild,
CascStorageInfoClassMax
} CASC_STORAGE_INFO_CLASS, *PCASC_STORAGE_INFO_CLASS;
@@ -148,7 +168,7 @@ void qsort_pointer_array(void ** base, size_t num, int (*compare)(const void *,
//-----------------------------------------------------------------------------
// Functions for storage manipulation
-bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwFlags, HANDLE * phStorage);
+bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwLocaleMask, HANDLE * phStorage);
bool WINAPI CascGetStorageInfo(HANDLE hStorage, CASC_STORAGE_INFO_CLASS InfoClass, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded);
bool WINAPI CascCloseStorage(HANDLE hStorage);
diff --git a/dep/CascLib/src/CascMndxRoot.cpp b/dep/CascLib/src/CascMndxRoot.cpp
index a788b652573..db29fba1662 100644
--- a/dep/CascLib/src/CascMndxRoot.cpp
+++ b/dep/CascLib/src/CascMndxRoot.cpp
@@ -202,6 +202,11 @@ DWORD GetNumberOfSetBits(DWORD Value32)
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;
@@ -2887,7 +2892,7 @@ PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName)
static bool FillFindData(TCascSearch * pSearch, PCASC_FIND_DATA pFindData, TMndxFindResult * pStruct1C)
{
- CASC_ROOT_KEY_INFO RootKeyInfo;
+ PCASC_ROOT_ENTRY_MNDX pRootEntry = NULL;
TCascStorage * hs = pSearch->hs;
PCASC_PACKAGE pPackage;
char * szStrippedName;
@@ -2899,6 +2904,7 @@ static bool FillFindData(TCascSearch * pSearch, PCASC_FIND_DATA pFindData, TMndx
// Fill the file name
memcpy(pFindData->szFileName, pStruct1C->szFoundPath, pStruct1C->cchFoundPath);
pFindData->szFileName[pStruct1C->cchFoundPath] = 0;
+ pFindData->szPlainName = (char *)GetPlainFileName(pFindData->szFileName);
pFindData->dwFileSize = CASC_INVALID_SIZE;
// Fill the file size
@@ -2910,10 +2916,10 @@ static bool FillFindData(TCascSearch * pSearch, PCASC_FIND_DATA pFindData, TMndx
while(szStrippedName[0] == '/')
szStrippedName++;
- nError = SearchMndxInfo(hs->pMndxInfo, szStrippedName, (DWORD)(pPackage - hs->pPackages->Packages), &RootKeyInfo);
+ nError = SearchMndxInfo(hs->pMndxInfo, szStrippedName, (DWORD)(pPackage - hs->pPackages->Packages), &pRootEntry);
if(nError == ERROR_SUCCESS)
{
- pFindData->dwFileSize = (DWORD)RootKeyInfo.FileSize;
+ pFindData->dwFileSize = pRootEntry->FileSize;
}
}
return true;
@@ -3014,7 +3020,7 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
{
if(pMndxInfo->pMarFile1 == NULL || pMndxInfo->pMarFile2 == NULL || pMndxInfo->pMarFile3 == NULL)
nError = ERROR_BAD_FORMAT;
- if(pMndxInfo->MndxEntrySize != sizeof(CASC_MNDX_ENTRY))
+ if(pMndxInfo->MndxEntrySize != sizeof(CASC_ROOT_ENTRY_MNDX))
nError = ERROR_BAD_FORMAT;
}
@@ -3028,7 +3034,7 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
if(nError == ERROR_SUCCESS && FileNameCount == pMndxInfo->MndxEntriesValid)
{
cbToAllocate = pMndxInfo->MndxEntriesTotal * pMndxInfo->MndxEntrySize;
- pMndxInfo->pMndxEntries = (PCASC_MNDX_ENTRY)CASC_ALLOC(BYTE, cbToAllocate);
+ pMndxInfo->pMndxEntries = (PCASC_ROOT_ENTRY_MNDX)CASC_ALLOC(BYTE, cbToAllocate);
if(pMndxInfo->pMndxEntries != NULL)
{
if(!RootFileRead(pbRootFile + pMndxInfo->MndxEntriesOffset, pbRootFileEnd, pMndxInfo->pMndxEntries, cbToAllocate))
@@ -3045,10 +3051,10 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
if(nError == ERROR_SUCCESS)
{
assert(pMndxInfo->MndxEntriesValid <= pMndxInfo->MndxEntriesTotal);
- pMndxInfo->ppValidEntries = CASC_ALLOC(PCASC_MNDX_ENTRY, pMndxInfo->MndxEntriesValid + 1);
+ pMndxInfo->ppValidEntries = CASC_ALLOC(PCASC_ROOT_ENTRY_MNDX, pMndxInfo->MndxEntriesValid + 1);
if(pMndxInfo->ppValidEntries != NULL)
{
- PCASC_MNDX_ENTRY pMndxEntry = pMndxInfo->pMndxEntries;
+ PCASC_ROOT_ENTRY_MNDX pRootEntry = pMndxInfo->pMndxEntries;
DWORD ValidEntryCount = 1; // edx
DWORD nIndex1 = 0;
@@ -3056,14 +3062,14 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
pMndxInfo->ppValidEntries[nIndex1++] = pMndxInfo->pMndxEntries;
// Put the remaining entries
- for(i = 0; i < pMndxInfo->MndxEntriesTotal; i++, pMndxEntry++)
+ for(i = 0; i < pMndxInfo->MndxEntriesTotal; i++, pRootEntry++)
{
if(ValidEntryCount > pMndxInfo->MndxEntriesValid)
break;
- if(pMndxEntry->Flags & 0x80000000)
+ if(pRootEntry->Flags & 0x80000000)
{
- pMndxInfo->ppValidEntries[nIndex1++] = pMndxEntry + 1;
+ pMndxInfo->ppValidEntries[nIndex1++] = pRootEntry + 1;
ValidEntryCount++;
}
}
@@ -3106,9 +3112,9 @@ int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
return nError;
}
-int SearchMndxInfo(PCASC_MNDX_INFO pMndxInfo, const char * szFileName, DWORD dwPackage, PCASC_ROOT_KEY_INFO pFoundInfo)
+int SearchMndxInfo(PCASC_MNDX_INFO pMndxInfo, const char * szFileName, DWORD dwPackage, PCASC_ROOT_ENTRY_MNDX * ppRootEntry)
{
- PCASC_MNDX_ENTRY pMndxEntry;
+ PCASC_ROOT_ENTRY_MNDX pRootEntry;
TMndxFindResult Struct1C;
// Search the database for the file name
@@ -3124,20 +3130,19 @@ int SearchMndxInfo(PCASC_MNDX_INFO pMndxInfo, const char * szFileName, DWORD dwP
if(Struct1C.FileNameIndex < pMndxInfo->MndxEntriesValid)
{
// HOTS: E945F4
- pMndxEntry = pMndxInfo->ppValidEntries[Struct1C.FileNameIndex];
- while((pMndxEntry->Flags & 0x00FFFFFF) != dwPackage)
+ pRootEntry = pMndxInfo->ppValidEntries[Struct1C.FileNameIndex];
+ while((pRootEntry->Flags & 0x00FFFFFF) != dwPackage)
{
// The highest bit serves as a terminator if set
- if(pMndxEntry->Flags & 0x80000000)
+ if(pRootEntry->Flags & 0x80000000)
return ERROR_FILE_NOT_FOUND;
- pMndxEntry++;
+ pRootEntry++;
}
- // Fill the root info
- memcpy(pFoundInfo->EncodingKey, pMndxEntry->EncodingKey, MD5_HASH_SIZE);
- pFoundInfo->FileSize = pMndxEntry->FileSize;
- pFoundInfo->Flags = (BYTE)((pMndxEntry->Flags >> 0x18) & 0x3F);
+ // Give the root entry pointer to the caller
+ if(ppRootEntry != NULL)
+ ppRootEntry[0] = pRootEntry;
return ERROR_SUCCESS;
}
}
@@ -3213,14 +3218,18 @@ extern "C" {
extern "C" void * allocate_zeroed_memory_x86(size_t bytes)
{
- return calloc(bytes, 1);
+ void * ptr = CASC_ALLOC(BYTE, bytes);
+
+ if(ptr != NULL)
+ memset(ptr, 0, bytes);
+ return ptr;
}
extern "C" void free_memory_x86(void * ptr)
{
if(ptr != NULL)
{
- free(ptr);
+ CASC_FREE(ptr);
}
}
diff --git a/dep/CascLib/src/CascMndxRoot.h b/dep/CascLib/src/CascMndxRoot.h
index 23017642ff9..bd93f230845 100644
--- a/dep/CascLib/src/CascMndxRoot.h
+++ b/dep/CascLib/src/CascMndxRoot.h
@@ -358,7 +358,7 @@ inline bool IS_SINGLE_CHAR_MATCH(TGenericArray & Table, DWORD ItemIndex)
int LoadMndxRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
PCASC_PACKAGE FindMndxPackage(TCascStorage * hs, const char * szFileName);
-int SearchMndxInfo(PCASC_MNDX_INFO pMndxInfo, const char * szFileName, DWORD dwPackage, PCASC_ROOT_KEY_INFO pFoundInfo);
+int SearchMndxInfo(PCASC_MNDX_INFO pMndxInfo, const char * szFileName, DWORD dwPackage, PCASC_ROOT_ENTRY_MNDX * ppFoundInfo);
bool DoStorageSearch_MNDX(TCascSearch * pSearch, PCASC_FIND_DATA pFindData);
void FreeMndxInfo(PCASC_MNDX_INFO pMndxInfo);
diff --git a/dep/CascLib/src/CascOpenFile.cpp b/dep/CascLib/src/CascOpenFile.cpp
index e7cdb9aa107..e5a9855f97f 100644
--- a/dep/CascLib/src/CascOpenFile.cpp
+++ b/dep/CascLib/src/CascOpenFile.cpp
@@ -69,115 +69,47 @@ PCASC_ENCODING_ENTRY FindEncodingEntry(TCascStorage * hs, PQUERY_KEY pEncodingKe
}
// Also used in CascSearchFile
-PCASC_ROOT_ENTRY FindFirstRootEntry(TCascStorage * hs, const char * szFileName, size_t * PtrIndex)
+PCASC_ROOT_ENTRY FindRootEntry(TCascStorage * hs, const char * szFileName, DWORD * PtrTableIndex)
{
- PCASC_ROOT_ENTRY pFoundEntry = NULL;
PCASC_ROOT_ENTRY pRootEntry;
ULONGLONG FileNameHash;
+ DWORD TableIndex;
uint32_t dwHashHigh = 0;
uint32_t dwHashLow = 0;
- size_t StartEntry = 0;
- size_t MidlEntry = 0;
- size_t EndEntry = hs->nRootEntries;
// Calculate the HASH value of the normalized file name
hashlittle2(szFileName, strlen(szFileName), &dwHashHigh, &dwHashLow);
FileNameHash = ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow;
- // Perform binary search
- while(StartEntry < EndEntry)
- {
- // Calculate the middle of the interval
- MidlEntry = StartEntry + ((EndEntry - StartEntry) / 2);
- pRootEntry = hs->ppRootEntries[MidlEntry];
+ // Get the first table index
+ TableIndex = (DWORD)(FileNameHash & (hs->RootTable.TableSize - 1));
+ assert(hs->RootTable.ItemCount < hs->RootTable.TableSize);
- // Did we find it?
+ // Search the proper entry
+ for(;;)
+ {
+ // Does the has match?
+ pRootEntry = hs->RootTable.TablePtr + TableIndex;
if(pRootEntry->FileNameHash == FileNameHash)
{
- pFoundEntry = pRootEntry;
- break;
+ if(PtrTableIndex != NULL)
+ PtrTableIndex[0] = TableIndex;
+ return pRootEntry;
}
- // Move the interval to the left or right
- (FileNameHash < pRootEntry->FileNameHash) ? EndEntry = MidlEntry : StartEntry = MidlEntry + 1;
- }
+ // If the entry is free, the file is not there
+ if(pRootEntry->FileNameHash == 0 && pRootEntry->SumValue == 0)
+ return NULL;
- // Move the pointer back to the first entry with that hash
- if(pFoundEntry != NULL)
- {
- while(MidlEntry > 0 && hs->ppRootEntries[MidlEntry - 1]->FileNameHash == FileNameHash)
- {
- pFoundEntry = hs->ppRootEntries[MidlEntry - 1];
- MidlEntry--;
- }
+ // Move to the next entry
+ TableIndex = (DWORD)((TableIndex + 1) & (hs->RootTable.TableSize - 1));
}
-
- // Return what we found
- if(PtrIndex != NULL)
- *PtrIndex = MidlEntry;
- return pFoundEntry;
-}
-
-// Check the root directory for that hash
-PCASC_ROOT_ENTRY FindRootEntryLocale(TCascStorage * hs, char * szFileName, DWORD Locale)
-{
- PCASC_ROOT_ENTRY pThatEntry = NULL;
- PCASC_ROOT_ENTRY pENUSEntry = NULL;
- PCASC_ROOT_ENTRY pENGBEntry = NULL;
- PCASC_ROOT_ENTRY pAnyEntry = NULL;
- PCASC_ROOT_ENTRY pEndEntry = NULL;
- PCASC_ROOT_ENTRY pRootEntry = NULL;
- ULONGLONG FileNameHash;
- size_t EntryIndex = 0;
- size_t EndEntry = hs->nRootEntries;
-
- // Find a root entry with the given name hash
- pRootEntry = FindFirstRootEntry(hs, szFileName, &EntryIndex);
- if(pRootEntry != NULL)
- {
- // Rememeber the file name hash
- pEndEntry = hs->pRootEntries + hs->nRootEntries;
- FileNameHash = pRootEntry->FileNameHash;
-
- // Find all suitable root entries
- while(EntryIndex < EndEntry)
- {
- // Get the root entry
- pRootEntry = hs->ppRootEntries[EntryIndex++];
- if(pRootEntry->FileNameHash != FileNameHash)
- break;
-
- // If a locale has been given, check it
- if(pThatEntry == NULL && Locale != 0 && (Locale & pRootEntry->Locales))
- pThatEntry = pRootEntry;
- if(pENUSEntry == NULL && (pRootEntry->Locales & CASC_LOCALE_ENUS))
- pENUSEntry = pRootEntry;
- if(pENGBEntry == NULL && (pRootEntry->Locales & CASC_LOCALE_ENGB))
- pENGBEntry = pRootEntry;
- if(pAnyEntry == NULL)
- pAnyEntry = pRootEntry;
-
- // Move to the next one
- pRootEntry++;
- }
-
- // Return the key by priority
- if(pThatEntry != NULL)
- return pThatEntry;
- if(pENGBEntry != NULL)
- return pENGBEntry;
- if(pENUSEntry != NULL)
- return pENUSEntry;
- }
-
- // Return whatever we got
- return pAnyEntry;
}
static TCascFile * CreateFileHandle(TCascStorage * hs, PCASC_INDEX_ENTRY pIndexEntry)
{
ULONGLONG FileOffsMask = ((ULONGLONG)1 << hs->KeyMapping[0].SegmentBits) - 1;
- ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffset);
+ ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffsetBE);
TCascFile * hf;
// Allocate the CASC file structure
@@ -190,12 +122,13 @@ static TCascFile * CreateFileHandle(TCascStorage * hs, PCASC_INDEX_ENTRY pIndexE
hf->HeaderOffset = (DWORD)(FileOffset & FileOffsMask);
hf->szClassName = "TCascFile";
- // Copy the compressed file size
- hf->CompressedSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSize) - 0x1E;
+ // 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 itself
+ // 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
@@ -206,10 +139,9 @@ static TCascFile * CreateFileHandle(TCascStorage * hs, PCASC_INDEX_ENTRY pIndexE
return hf;
}
-static bool OpenFileByIndexKey(TCascStorage * hs, PQUERY_KEY pIndexKey, DWORD dwFlags, HANDLE * phFile)
+static bool OpenFileByIndexKey(TCascStorage * hs, PQUERY_KEY pIndexKey, DWORD dwFlags, TCascFile ** ppCascFile)
{
PCASC_INDEX_ENTRY pIndexEntry;
- TCascFile * hf = NULL;
int nError = ERROR_SUCCESS;
CASCLIB_UNUSED(dwFlags);
@@ -222,43 +154,53 @@ static bool OpenFileByIndexKey(TCascStorage * hs, PQUERY_KEY pIndexKey, DWORD dw
// Create the file handle structure
if(nError == ERROR_SUCCESS)
{
- hf = CreateFileHandle(hs, pIndexEntry);
- *phFile = (HANDLE)hf;
- if(hf == NULL)
+ ppCascFile[0] = CreateFileHandle(hs, pIndexEntry);
+ if(ppCascFile[0] == NULL)
nError = ERROR_FILE_NOT_FOUND;
}
+#ifdef CASCLIB_TEST
+ if(nError == ERROR_SUCCESS && ppCascFile[0] != NULL)
+ {
+ ppCascFile[0]->FileSize_IdxEntry = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE);
+ }
+#endif
+
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return (nError == ERROR_SUCCESS);
}
-static bool OpenFileByEncodingKey(TCascStorage * hs, PQUERY_KEY pEncodingKey, DWORD dwFlags, HANDLE * phFile)
+static bool OpenFileByEncodingKey(TCascStorage * hs, PQUERY_KEY pEncodingKey, DWORD dwFlags, TCascFile ** ppCascFile)
{
PCASC_ENCODING_ENTRY pEncodingEntry;
QUERY_KEY IndexKey;
- TCascFile * hf = NULL;
- int nError = ERROR_SUCCESS;
// Find the encoding entry
pEncodingEntry = FindEncodingEntry(hs, pEncodingKey, NULL);
if(pEncodingEntry == NULL)
- nError = ERROR_FILE_NOT_FOUND;
+ {
+ 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 = pEncodingEntry->EncodingKey + (MD5_HASH_SIZE * pEncodingEntry->KeyCount);
-// assert(pEncodingEntry->KeyCount == 1);
IndexKey.pbData = pEncodingEntry->EncodingKey + MD5_HASH_SIZE;
IndexKey.cbData = MD5_HASH_SIZE;
- if(OpenFileByIndexKey(hs, &IndexKey, dwFlags, phFile))
+ if(OpenFileByIndexKey(hs, &IndexKey, dwFlags, ppCascFile))
{
- // Fix the file size from the encoding key
- hf = IsValidFileHandle(*phFile);
- if(hf != NULL)
+ // Check if the file handle was created
+ if(ppCascFile[0] != NULL)
{
- hf->FileSize = ConvertBytesToInteger_4(pEncodingEntry->FileSizeBytes);
+ // 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;
}
}
@@ -289,7 +231,7 @@ bool WINAPI CascOpenFileByIndexKey(HANDLE hStorage, PQUERY_KEY pIndexKey, DWORD
}
// Use the internal function to open the file
- return OpenFileByIndexKey(hs, pIndexKey, dwFlags, phFile);
+ return OpenFileByIndexKey(hs, pIndexKey, dwFlags, (TCascFile **)phFile);
}
bool WINAPI CascOpenFileByEncodingKey(HANDLE hStorage, PQUERY_KEY pEncodingKey, DWORD dwFlags, HANDLE * phFile)
@@ -312,12 +254,12 @@ bool WINAPI CascOpenFileByEncodingKey(HANDLE hStorage, PQUERY_KEY pEncodingKey,
}
// Use the internal function fo open the file
- return OpenFileByEncodingKey(hs, pEncodingKey, dwFlags, phFile);
+ return OpenFileByEncodingKey(hs, pEncodingKey, dwFlags, (TCascFile **)phFile);
}
bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocale, DWORD dwFlags, HANDLE * phFile)
{
- CASC_ROOT_KEY_INFO EncodingKeyInfo;
+ PCASC_ROOT_ENTRY_MNDX pRootEntryMndx = NULL;
PCASC_ROOT_ENTRY pRootEntry;
PCASC_PACKAGE pPackage;
TCascStorage * hs;
@@ -326,6 +268,8 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocal
char * szFileName2;
int nError = ERROR_SUCCESS;
+ CASCLIB_UNUSED(dwLocale);
+
// Validate the storage handle
hs = IsValidStorageHandle(hStorage);
if(hs == NULL)
@@ -360,11 +304,11 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocal
while(szStrippedName[0] == '/')
szStrippedName++;
- nError = SearchMndxInfo(hs->pMndxInfo, szStrippedName, (DWORD)(pPackage - hs->pPackages->Packages), &EncodingKeyInfo);
+ nError = SearchMndxInfo(hs->pMndxInfo, szStrippedName, (DWORD)(pPackage - hs->pPackages->Packages), &pRootEntryMndx);
if(nError == ERROR_SUCCESS)
{
// Prepare the encoding key
- EncodingKey.pbData = EncodingKeyInfo.EncodingKey;
+ EncodingKey.pbData = pRootEntryMndx->EncodingKey;
EncodingKey.cbData = MD5_HASH_SIZE;
}
}
@@ -376,23 +320,27 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocal
else
{
// Convert the file name to lowercase + slashes
- NormalizeFileName_UpperBkSlash(szFileName2);
+ NormalizeFileName_UpperBkSlash(szFileName2, szFileName2);
// Check the root directory for that hash
- pRootEntry = FindRootEntryLocale(hs, szFileName2, dwLocale);
- nError = (pRootEntry != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND;
+ pRootEntry = FindRootEntry(hs, szFileName2, NULL);
if(pRootEntry != NULL)
{
// Prepare the root key
- EncodingKey.pbData = pRootEntry->EncodingKey;
+ EncodingKey.pbData = (LPBYTE)pRootEntry->EncodingKey;
EncodingKey.cbData = MD5_HASH_SIZE;
+ nError = ERROR_SUCCESS;
+ }
+ else
+ {
+ nError = ERROR_FILE_NOT_FOUND;
}
}
// Use the root key to find the file in the encoding table entry
if(nError == ERROR_SUCCESS)
{
- if(!OpenFileByEncodingKey(hs, &EncodingKey, dwFlags, phFile))
+ if(!OpenFileByEncodingKey(hs, &EncodingKey, dwFlags, (TCascFile **)phFile))
{
assert(GetLastError() != ERROR_SUCCESS);
nError = GetLastError();
@@ -400,11 +348,18 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocal
}
// Delete the file name copy
- delete [] szFileName2;
+ CASC_FREE(szFileName2);
}
else
nError = ERROR_NOT_ENOUGH_MEMORY;
+#ifdef CASCLIB_TEST
+ if(phFile[0] != NULL && pRootEntryMndx != NULL)
+ {
+ ((TCascFile *)(phFile[0]))->FileSize_RootEntry = pRootEntryMndx->FileSize;
+ }
+#endif
+
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return (nError == ERROR_SUCCESS);
diff --git a/dep/CascLib/src/CascOpenStorage.cpp b/dep/CascLib/src/CascOpenStorage.cpp
index f9e6ba6f4d4..490d889742f 100644
--- a/dep/CascLib/src/CascOpenStorage.cpp
+++ b/dep/CascLib/src/CascOpenStorage.cpp
@@ -2,6 +2,8 @@
/* 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 */
/* -------- ---- --- ------- */
@@ -14,9 +16,17 @@
#include "CascMndxRoot.h"
//-----------------------------------------------------------------------------
+// Dumping options
+
+#ifdef _DEBUG
+#define CASC_DUMP_ROOT_FILE 2 // The root file will be dumped (level 2)
+#endif
+
+//-----------------------------------------------------------------------------
// Local structures
-#define CASC_ENCODING_SEGMENT_SIZE 0x1000
+#define CASC_INITIAL_ROOT_TABLE_SIZE 0x00100000
+#define CASC_ENCODING_SEGMENT_SIZE 0x1000
typedef struct _BLOCK_SIZE_AND_HASH
{
@@ -79,32 +89,6 @@ typedef struct _FILE_ENCODING_SEGMENT
} FILE_ENCODING_SEGMENT, *PFILE_ENCODING_SEGMENT;
-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;
-
-typedef struct _FILE_ROOT_ENTRY
-{
- BYTE EncodingKey[MD5_HASH_SIZE]; // MD5 of the file
- ULONGLONG FileNameHash; // Jenkins hash of the file name
-
-} FILE_ROOT_ENTRY, *PFILE_ROOT_ENTRY;
-
-typedef struct _ROOT_BLOCK_INFO
-{
- PFILE_LOCALE_BLOCK pLocaleBlockHdr; // Pointer to the locale block
- PDWORD pInt32Array; // Pointer to the array of 32-bit integers
- PFILE_ROOT_ENTRY pRootEntries;
-
-} ROOT_BLOCK_INFO, *PROOT_BLOCK_INFO;
-
//-----------------------------------------------------------------------------
// Local variables
@@ -181,19 +165,6 @@ static void QUERY_KEY_FreeArray(PQUERY_KEY pBlobArray)
CASC_FREE(pBlobArray);
}
-static int CompareRootEntries(const void *, const void * pvKeyEntry1, const void * pvKeyEntry2)
-{
- PCASC_ROOT_ENTRY pRootEntry1 = (PCASC_ROOT_ENTRY)pvKeyEntry1;
- PCASC_ROOT_ENTRY pRootEntry2 = (PCASC_ROOT_ENTRY)pvKeyEntry2;
-
- // Compare name hash first
- if(pRootEntry1->FileNameHash < pRootEntry2->FileNameHash)
- return -1;
- if(pRootEntry1->FileNameHash > pRootEntry2->FileNameHash)
- return +1;
- return 0;
-}
-
static bool IsCascIndexHeader_V1(LPBYTE pbFileData, DWORD cbFileData)
{
PFILE_INDEX_HEADER_V1 pIndexHeader = (PFILE_INDEX_HEADER_V1)pbFileData;
@@ -235,18 +206,18 @@ static bool IsCascIndexHeader_V2(LPBYTE pbFileData, DWORD cbFileData)
return (HashHigh == pSizeAndHash->dwBlockHash);
}
-static LPBYTE VerifyLocaleBlock(PROOT_BLOCK_INFO pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd)
+LPBYTE VerifyLocaleBlock(PROOT_BLOCK_INFO pBlockInfo, LPBYTE pbFilePointer, LPBYTE pbFileEnd)
{
- // Validate the locale header
+ // Validate the file locale block
pBlockInfo->pLocaleBlockHdr = (PFILE_LOCALE_BLOCK)pbFilePointer;
- pbFilePointer += sizeof(FILE_LOCALE_BLOCK);
- if(pbFilePointer >= pbFileEnd)
+ pbFilePointer = (LPBYTE)(pBlockInfo->pLocaleBlockHdr + 1);
+ if(pbFilePointer > pbFileEnd)
return NULL;
// Validate the array of 32-bit integers
pBlockInfo->pInt32Array = (PDWORD)pbFilePointer;
pbFilePointer = (LPBYTE)(pBlockInfo->pInt32Array + pBlockInfo->pLocaleBlockHdr->NumberOfFiles);
- if(pbFilePointer >= pbFileEnd)
+ if(pbFilePointer > pbFileEnd)
return NULL;
// Validate the array of root entries
@@ -488,7 +459,7 @@ static int VerifyAndParseKeyMapping_V2(PCASC_MAPPING_TABLE pKeyMapping, DWORD Ke
if(PtrLastPart[0] == 0)
return ERROR_SUCCESS;
- HashLow = hashlittle(PtrLastPart + 1, 0x13, 0) | 0x80000000;
+ HashLow = hashlittle(PtrLastPart + i, 0x13, 0) | 0x80000000;
if(HashLow != PtrLastPart[0])
return ERROR_BAD_FORMAT;
}
@@ -533,7 +504,7 @@ static int LoadKeyMapping(PCASC_MAPPING_TABLE pKeyMapping, DWORD KeyIndex)
{
// Retrieve the file size
FileStream_GetSize(pStream, &FileSize);
- if((0 < FileSize && FileSize <= 0x90000) || 1)
+ if(0 < FileSize && FileSize <= 0x100000)
{
// WoW6 actually reads THE ENTIRE file to memory
// Verified on Mac build (x64)
@@ -664,112 +635,146 @@ static int CreateMapOfEncodingKeys(TCascStorage * hs, PFILE_ENCODING_SEGMENT pEn
return nError;
}
-static DWORD GetSizeOfEncodingFile(HANDLE hFile)
+static int LoadIndexFiles(TCascStorage * hs)
+{
+ DWORD IndexArray[CASC_INDEX_COUNT];
+ DWORD OldIndexArray[CASC_INDEX_COUNT];
+ int nError;
+ int i;
+
+ // 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)
+ {
+ // 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;
+ }
+ }
+ }
+
+ // Now we need to build the map of the index entries
+ if(nError == ERROR_SUCCESS)
+ {
+ nError = CreateArrayOfIndexEntries(hs);
+ }
+
+ return nError;
+}
+
+static LPBYTE LoadEncodingFileToMemory(HANDLE hFile, DWORD * pcbEncodingFile)
{
CASC_ENCODING_HEADER EncodingHeader;
+ LPBYTE pbEncodingFile = NULL;
DWORD cbEncodingFile = 0;
- DWORD dwSegmentPos;
- DWORD dwNumSegments;
+ DWORD dwSegmentPos = 0;
+ DWORD dwNumSegments = 0;
DWORD dwBytesRead;
+ int nError = ERROR_BAD_FORMAT;
- // Read the endoding header
+ // Read the encoding header
CascReadFile(hFile, &EncodingHeader, sizeof(CASC_ENCODING_HEADER), &dwBytesRead);
if(dwBytesRead == sizeof(CASC_ENCODING_HEADER))
{
dwNumSegments = ConvertBytesToInteger_4(EncodingHeader.NumSegments);
dwSegmentPos = ConvertBytesToInteger_4(EncodingHeader.SegmentsPos);
+ 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);
- }
-
- // Reset the position back
- CascSetFilePointer(hFile, 0, NULL, FILE_BEGIN);
- return cbEncodingFile;
-}
-
-static LPBYTE LoadCascFile(HANDLE hFile, DWORD cbMaxSize, PDWORD pcbFileData)
-{
- LPBYTE pbFileData = NULL;
- DWORD cbFileData;
- DWORD dwBytesRead = 0;
- int nError = ERROR_SUCCESS;
-
- // Retrieve the size of the file
- cbFileData = CascGetFileSize(hFile, NULL);
- if(cbFileData != 0 && cbFileData != CASC_INVALID_SIZE)
- {
- // Trim the size to the maximum
- cbFileData = CASCLIB_MIN(cbMaxSize, cbFileData);
-
- // Allocate the buffer that will hold the entire 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
+ pbEncodingFile = CASC_ALLOC(BYTE, cbEncodingFile);
+ if(pbEncodingFile == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
- else
- nError = ERROR_FILE_CORRUPT;
- // If something failed, clean-up the buffers
- if(nError != ERROR_SUCCESS)
+ // If all went OK, we load the entire file to memory
+ if(nError == ERROR_SUCCESS)
{
- // Clear the file data
- if(pbFileData != NULL)
- CASC_FREE(pbFileData);
- pbFileData = NULL;
- cbFileData = 0;
+ // Copy the header itself
+ memcpy(pbEncodingFile, &EncodingHeader, sizeof(CASC_ENCODING_HEADER));
- // Set the last error value
- SetLastError(nError);
+ // 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;
}
- // Return what we got
- if(pcbFileData != NULL)
- *pcbFileData = cbFileData;
- return pbFileData;
+ // Give the loaded file length
+ if(pcbEncodingFile != NULL)
+ *pcbEncodingFile = cbEncodingFile;
+ return pbEncodingFile;
}
-static int LoadIndexFiles(TCascStorage * hs)
+static LPBYTE LoadRootFileToMemory(HANDLE hFile, DWORD * pcbRootFile)
{
- DWORD IndexArray[CASC_INDEX_COUNT];
- DWORD OldIndexArray[CASC_INDEX_COUNT];
- int nError;
- int i;
+ TCascFile * hf;
+ LPBYTE pbRootFile = NULL;
+ DWORD cbRootFile = 0;
+ DWORD dwBytesRead;
+ BYTE StartOfFile[0x10];
+ 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);
+ // Dummy read the first 16 bytes
+ CascReadFile(hFile, &StartOfFile, sizeof(StartOfFile), &dwBytesRead);
+ if(dwBytesRead != sizeof(StartOfFile))
+ nError = ERROR_BAD_FORMAT;
+
+ // Calculate and allocate space for the entire file
if(nError == ERROR_SUCCESS)
{
- // Load each index file
- for(i = 0; i < CASC_INDEX_COUNT; i++)
+ // Convert the file handle to pointer to TCascFile
+ hf = IsValidFileHandle(hFile);
+ if(hf != NULL)
{
- hs->KeyMapping[i].szFileName = CreateIndexFileName(hs, i, IndexArray[i]);
- if(hs->KeyMapping[i].szFileName != NULL)
+ // Parse the frames to get the file size
+ for(DWORD i = 0; i < hf->FrameCount; i++)
{
- nError = LoadKeyMapping(&hs->KeyMapping[i], i);
- if(nError != ERROR_SUCCESS)
- break;
+ cbRootFile += hf->pFrames[i].FrameSize;
}
}
+
+ // Evaluate the error
+ nError = (cbRootFile != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
}
- // Now we need to build the map of the index entries
+ // Allocate space for the entire file
if(nError == ERROR_SUCCESS)
{
- nError = CreateArrayOfIndexEntries(hs);
+ pbRootFile = CASC_ALLOC(BYTE, cbRootFile);
+ if(pbRootFile == NULL)
+ nError = ERROR_NOT_ENOUGH_MEMORY;
}
- return nError;
+ // If all went OK, we load the entire file to memory
+ if(nError == ERROR_SUCCESS)
+ {
+ // Copy the header itself
+ memcpy(pbRootFile, StartOfFile, sizeof(StartOfFile));
+
+ // Read the rest of the data
+ CascReadFile(hFile, pbRootFile + sizeof(StartOfFile), cbRootFile - sizeof(StartOfFile), &dwBytesRead);
+ if(dwBytesRead != (cbRootFile - sizeof(StartOfFile)))
+ nError = ERROR_FILE_CORRUPT;
+ }
+
+ // Give the loaded file length
+ if(pcbRootFile != NULL)
+ *pcbRootFile = cbRootFile;
+ return pbRootFile;
}
static int LoadEncodingFile(TCascStorage * hs)
@@ -788,18 +793,15 @@ static int LoadEncodingFile(TCascStorage * hs)
if(!CascOpenFileByIndexKey((HANDLE)hs, &hs->EncodingEKey, 0, &hFile))
nError = GetLastError();
- // Load the encoding file to memory
+ // Load the entire ENCODING file to memory
if(nError == ERROR_SUCCESS)
{
- // Retrieve the CASC header. We do not usually need to load
- // the entire file, but we need to know how big part of it we need
- cbEncodingFile = GetSizeOfEncodingFile(hFile);
-
- // Load the entire file to memory
- pbEncodingFile = LoadCascFile(hFile, cbEncodingFile, &cbEncodingFile);
+ // 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;
+ // Close the encoding file
CascCloseFile(hFile);
}
@@ -859,91 +861,237 @@ static int LoadEncodingFile(TCascStorage * hs)
return nError;
}
-static int LoadRootFile(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
+typedef struct _CHECK_ROOT_ENTRY_INPUT
+{
+ ULONGLONG FileNameHash;
+ DWORD SumValue;
+ DWORD EncodingKey[4];
+
+} CHECK_ROOT_ENTRY_INPUT, *PCHECK_ROOT_ENTRY_INPUT;
+
+typedef struct _CHECK_ROOT_ENTRY_OUTPUT
+{
+ DWORD field_0;
+ DWORD field_4;
+ DWORD field_8;
+ bool field_C;
+
+} CHECK_ROOT_ENTRY_OUTPUT, *PCHECK_ROOT_ENTRY_OUTPUT;
+
+
+// WoW6: 00413F61
+static bool EnlargeHashTableIfMoreThan75PercentUsed(PCASC_ROOT_HASH_TABLE pRootTable, DWORD NewItemCount)
+{
+ // Don't relocate anything, just check
+ assert((double)NewItemCount / (double)pRootTable->TableSize < .75);
+ return true;
+}
+
+// WOW6: 00414402
+// Finds an existing root table entry or a free one
+PCASC_ROOT_ENTRY CascRootTable_FindFreeEntryWithEnlarge(
+ PCASC_ROOT_HASH_TABLE pRootTable,
+ PCASC_ROOT_ENTRY pNewEntry)
{
- PFILE_ROOT_ENTRY pSrcEntry;
- PCASC_ROOT_ENTRY pTrgEntry;
+ PCASC_ROOT_ENTRY pEntry;
+ DWORD TableIndex;
+
+ // The table size must be a power of two
+ assert((pRootTable->TableSize & (pRootTable->TableSize - 1)) == 0);
+
+ // Make sure that number of occupied items is never bigger
+ // than 75% of the table size
+ if(!EnlargeHashTableIfMoreThan75PercentUsed(pRootTable, pRootTable->ItemCount + 1))
+ return NULL;
+
+ // Get the start index of the table
+ TableIndex = (DWORD)(pNewEntry->FileNameHash) & (pRootTable->TableSize - 1);
+
+ // If that entry is already occupied, move to a next entry
+ for(;;)
+ {
+ // Check that entry if it's free or not
+ pEntry = pRootTable->TablePtr + TableIndex;
+ if(pEntry->SumValue == 0)
+ break;
+
+ // Is the found entry equal to the existing one?
+ if(pEntry->FileNameHash == pNewEntry->FileNameHash)
+ break;
+
+ // Move to the next entry
+ TableIndex = (TableIndex + 1) & (pRootTable->TableSize - 1);
+ }
+
+ // Either return a free entry or an existing one
+ return pEntry;
+}
+
+// WOW6: 004145D1
+static void CascRootTable_InsertTableEntry(
+ PCASC_ROOT_HASH_TABLE pRootTable,
+ PCASC_ROOT_ENTRY pNewEntry)
+{
+ PCASC_ROOT_ENTRY pEntry;
+
+ // Find an existing entry or an empty one
+ pEntry = CascRootTable_FindFreeEntryWithEnlarge(pRootTable, pNewEntry);
+ assert(pEntry != NULL);
+
+ // If that entry is not used yet, fill it in
+ if(pEntry->FileNameHash == 0)
+ {
+ *pEntry = *pNewEntry;
+ pRootTable->ItemCount++;
+ }
+}
+
+static int LoadWowRootFileLocales(
+ TCascStorage * hs,
+ LPBYTE pbRootFile,
+ DWORD cbRootFile,
+ DWORD dwLocaleMask,
+ bool bLoadBlocksWithFlags80,
+ BYTE HighestBitValue)
+{
+ CASC_ROOT_ENTRY NewRootEntry;
ROOT_BLOCK_INFO BlockInfo;
LPBYTE pbRootFileEnd = pbRootFile + cbRootFile;
LPBYTE pbFilePointer;
- size_t nRootEntries = 0;
- size_t nRootIndex = 0;
- int nError = ERROR_NOT_ENOUGH_MEMORY;
- // Calculate the root entries
+ // Now parse the root file
for(pbFilePointer = pbRootFile; pbFilePointer <= pbRootFileEnd; )
{
- // Validate the root block
+ // Validate the file locale block
pbFilePointer = VerifyLocaleBlock(&BlockInfo, pbFilePointer, pbRootFileEnd);
if(pbFilePointer == NULL)
break;
- // Add the number of entries
- nRootEntries = nRootEntries + BlockInfo.pLocaleBlockHdr->NumberOfFiles;
- }
-
- // Create a linear array of the root entries and sort it
- hs->pRootEntries = pTrgEntry = CASC_ALLOC(CASC_ROOT_ENTRY, nRootEntries);
- hs->ppRootEntries = CASC_ALLOC(PCASC_ROOT_ENTRY, nRootEntries);
- if(hs->ppRootEntries && hs->pRootEntries)
- {
- // Convert each entry from FILE_ROOT_ENTRY to CASC_ROOT_ENTRY
- for(pbFilePointer = pbRootFile; pbFilePointer <= pbRootFileEnd; )
- {
- // Validate the root block
- pbFilePointer = VerifyLocaleBlock(&BlockInfo, pbFilePointer, pbRootFileEnd);
- if(pbFilePointer == NULL)
- break;
+ // WoW.exe (build 19116): Entries with flag 0x100 set are skipped
+ if(BlockInfo.pLocaleBlockHdr->Flags & 0x100)
+ continue;
- // Get the pointer to the first root entry
- pSrcEntry = (PFILE_ROOT_ENTRY)BlockInfo.pRootEntries;
+ // WoW.exe (build 19116): Entries with flag 0x80 set are skipped if arg_4 is set to FALSE (which is by default)
+ if(bLoadBlocksWithFlags80 == 0 && (BlockInfo.pLocaleBlockHdr->Flags & 0x80))
+ continue;
- // Convert all entries
- for(DWORD i = 0; i < BlockInfo.pLocaleBlockHdr->NumberOfFiles; i++)
- {
- // Copy the root entry
- CopyFileKey(pTrgEntry->EncodingKey, pSrcEntry->EncodingKey);
- pTrgEntry->FileNameHash = pSrcEntry->FileNameHash;
- pTrgEntry->Locales = BlockInfo.pLocaleBlockHdr->Locales;
- pTrgEntry->Flags = BlockInfo.pLocaleBlockHdr->Flags;
+ // WoW.exe (build 19116): Entries with (flags >> 0x1F) not equal to arg_8 are skipped
+ if((BYTE)(BlockInfo.pLocaleBlockHdr->Flags >> 0x1F) != HighestBitValue)
+ continue;
-// if(pTrgEntry->FileNameHash == 0x5ddb88608673f698ULL)
-// DebugBreak();
+ // WoW.exe (build 19116): Locales other than defined mask are skipped too
+ if((BlockInfo.pLocaleBlockHdr->Locales & dwLocaleMask) == 0)
+ continue;
- // Insert the CASC root entry to the linear array of pointers
- hs->ppRootEntries[nRootIndex++] = pTrgEntry;
+ // Reset the sum value
+ NewRootEntry.SumValue = 0;
- // Move to the next root entry
- pSrcEntry++;
- pTrgEntry++;
- }
+ // WoW.exe (build 19116): Blocks with zero files are skipped
+ for(DWORD i = 0; i < BlockInfo.pLocaleBlockHdr->NumberOfFiles; i++)
+ {
+ // (004147A3) Prepare the CASC_ROOT_ENTRY structure
+ NewRootEntry.FileNameHash = BlockInfo.pRootEntries[i].FileNameHash;
+ NewRootEntry.SumValue = NewRootEntry.SumValue + BlockInfo.pInt32Array[i];
+ NewRootEntry.Locales = BlockInfo.pLocaleBlockHdr->Locales;
+ NewRootEntry.EncodingKey[0] = BlockInfo.pRootEntries[i].EncodingKey[0];
+ NewRootEntry.EncodingKey[1] = BlockInfo.pRootEntries[i].EncodingKey[1];
+ NewRootEntry.EncodingKey[2] = BlockInfo.pRootEntries[i].EncodingKey[2];
+ NewRootEntry.EncodingKey[3] = BlockInfo.pRootEntries[i].EncodingKey[3];
+
+ // Insert the root table item to the hash table
+ CascRootTable_InsertTableEntry(&hs->RootTable, &NewRootEntry);
+ NewRootEntry.SumValue++;
}
+ }
- // Save the number of entries
- assert(nRootIndex == nRootEntries);
- hs->nRootEntries = nRootIndex;
+ return 1;
+}
- // Now sort the array
- qsort_pointer_array((void **)hs->ppRootEntries, hs->nRootEntries, CompareRootEntries, NULL);
- nError = ERROR_SUCCESS;
- }
+// WoW.exe: 004146C7 (BuildManifest::Load)
+static int LoadWowRootFileWithParams(
+ TCascStorage * hs,
+ LPBYTE pbRootFile,
+ DWORD cbRootFile,
+ DWORD dwLocaleBits,
+ BYTE HighestBitValue)
+{
+ // Load the locale as-is
+ LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, dwLocaleBits, false, HighestBitValue);
- return nError;
+ // If we wanted enGB, we also load enUS for the missing files
+ if(dwLocaleBits == CASC_LOCALE_ENGB)
+ LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENUS, false, HighestBitValue);
+
+ if(dwLocaleBits == CASC_LOCALE_PTPT)
+ LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTBR, false, HighestBitValue);
+
+ return ERROR_SUCCESS;
+}
/*
- FILE * fp = fopen("E:\\root_entries.txt", "wt");
- if(fp != NULL)
+ // Code from WoW.exe
+ if(dwLocaleBits == CASC_LOCALE_DUAL_LANG)
{
- for(size_t i = 0; i < nRootEntries; i++)
+ // Is this english version of WoW?
+ if(arg_4 == CASC_LOCALE_BIT_ENUS)
+ {
+ LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENGB, false, HighestBitValue);
+ LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_ENUS, false, HighestBitValue);
+ return ERROR_SUCCESS;
+ }
+
+ // Is this portuguese version of WoW?
+ if(arg_4 == CASC_LOCALE_BIT_PTBR)
{
- fprintf(fp, "%08X: %016I64lX\n", i, hs->ppRootEntries[i]->FileNameHash);
+ LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTPT, false, HighestBitValue);
+ LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, CASC_LOCALE_PTBR, false, HighestBitValue);
}
- fclose(fp);
}
+
+ LoadWowRootFileLocales(hs, pbRootFile, cbRootFile, (1 << arg_4), false, HighestBitValue);
*/
+
+static int LoadWowRootFile(
+ TCascStorage * hs,
+ LPBYTE pbRootFile,
+ DWORD cbRootFile,
+ DWORD dwLocaleMask)
+{
+ int nError;
+
+ // Dump the root file, if needed
+#ifdef CASC_DUMP_ROOT_FILE
+ //CascDumpRootFile(hs,
+ // pbRootFile,
+ // cbRootFile,
+ // "\\casc_root_%build%.txt",
+ // _T("\\Ladik\\Appdir\\CascLib\\listfile\\listfile-wow6.txt"),
+ // CASC_DUMP_ROOT_FILE);
+#endif
+
+ // Allocate root table entries. Note that the initial size
+ // of the root table is set to 0x00200000 by World of Warcraft 6.x
+ hs->RootTable.TablePtr = CASC_ALLOC(CASC_ROOT_ENTRY, CASC_INITIAL_ROOT_TABLE_SIZE);
+ hs->RootTable.TableSize = CASC_INITIAL_ROOT_TABLE_SIZE;
+ if(hs->RootTable.TablePtr == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Clear the entire table
+ memset(hs->RootTable.TablePtr, 0, CASC_INITIAL_ROOT_TABLE_SIZE * sizeof(CASC_ROOT_ENTRY));
+
+ // Load the root file
+ nError = LoadWowRootFileWithParams(hs, pbRootFile, cbRootFile, dwLocaleMask, 0);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ nError = LoadWowRootFileWithParams(hs, pbRootFile, cbRootFile, dwLocaleMask, 1);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+
+ return ERROR_SUCCESS;
}
-static int LoadRootFile(TCascStorage * hs)
+static int LoadRootFile(TCascStorage * hs, DWORD dwLocaleMask)
{
PDWORD FileSignature;
HANDLE hFile = NULL;
@@ -952,9 +1100,14 @@ static int LoadRootFile(TCascStorage * hs)
int nError = ERROR_SUCCESS;
// Sanity checks
+ assert(hs->RootTable.TablePtr == NULL);
+ assert(hs->RootTable.ItemCount == 0);
assert(hs->ppEncodingEntries != NULL);
- assert(hs->pRootEntries == NULL);
- assert(hs->nRootEntries == 0);
+
+ // Locale: The default parameter is 0 - in that case,
+ // we load enUS+enGB
+ if(dwLocaleMask == 0)
+ dwLocaleMask = CASC_LOCALE_ENUS | CASC_LOCALE_ENGB;
// The root file is either MNDX file (Heroes of the Storm)
// or a file containing an array of root entries (World of Warcraft 6.0+)
@@ -963,15 +1116,15 @@ static int LoadRootFile(TCascStorage * hs)
if(!CascOpenFileByEncodingKey((HANDLE)hs, &hs->RootKey, 0, &hFile))
nError = GetLastError();
- // Load ther entire root file to memory
+ // Load the entire ROOT file to memory
if(nError == ERROR_SUCCESS)
{
- // Load the entire root file to memory
- pbRootFile = LoadCascFile(hFile, 0xFFFFFFFF, &cbRootFile);
- if(pbRootFile == NULL || cbRootFile == 0)
+ // Load the necessary part of the ENCODING file to memory
+ pbRootFile = LoadRootFileToMemory(hFile, &cbRootFile);
+ if(pbRootFile == NULL || cbRootFile <= sizeof(PFILE_LOCALE_BLOCK))
nError = ERROR_FILE_CORRUPT;
- // Close the root file
+ // Close the encoding file
CascCloseFile(hFile);
}
@@ -985,7 +1138,8 @@ static int LoadRootFile(TCascStorage * hs)
}
else
{
- nError = LoadRootFile(hs, pbRootFile, cbRootFile);
+ // WOW6: 00415000
+ nError = LoadWowRootFile(hs, pbRootFile, cbRootFile, dwLocaleMask);
}
}
@@ -1007,10 +1161,8 @@ static TCascStorage * FreeCascStorage(TCascStorage * hs)
FreeMndxInfo(hs->pMndxInfo);
// Free the pointers to file entries
- if(hs->ppRootEntries != NULL)
- CASC_FREE(hs->ppRootEntries);
- if(hs->pRootEntries != NULL)
- CASC_FREE(hs->pRootEntries);
+ if(hs->RootTable.TablePtr != NULL)
+ CASC_FREE(hs->RootTable.TablePtr);
if(hs->ppEncodingEntries != NULL)
CASC_FREE(hs->ppEncodingEntries);
if(hs->pEncodingHeader != NULL)
@@ -1074,13 +1226,11 @@ static TCascStorage * FreeCascStorage(TCascStorage * hs)
//-----------------------------------------------------------------------------
// Public functions
-bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwFlags, HANDLE * phStorage)
+bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwLocaleMask, HANDLE * phStorage)
{
TCascStorage * hs;
int nError = ERROR_SUCCESS;
- CASCLIB_UNUSED(dwFlags);
-
// Allocate the storage structure
hs = (TCascStorage *)CASC_ALLOC(TCascStorage, 1);
if(hs == NULL)
@@ -1092,6 +1242,7 @@ bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwFlags, HANDLE * ph
// Prepare the base storage parameters
memset(hs, 0, sizeof(TCascStorage));
hs->szClassName = "TCascStorage";
+ hs->dwFileBeginDelta = 0xFFFFFFFF;
hs->dwRefCount = 1;
nError = InitializeCascDirectories(hs, szDataPath);
}
@@ -1099,7 +1250,7 @@ bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwFlags, HANDLE * ph
// Now we need to load the root file so we know the config files
if(nError == ERROR_SUCCESS)
{
- nError = LoadBuildConfiguration(hs);
+ nError = LoadBuildInfo(hs);
}
// Load the index files
@@ -1117,17 +1268,9 @@ bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwFlags, HANDLE * ph
// Load the index files
if(nError == ERROR_SUCCESS)
{
- nError = LoadRootFile(hs);
+ nError = LoadRootFile(hs, dwLocaleMask);
}
-#ifdef _DEBUG
-// if(nError == ERROR_SUCCESS)
-// {
-// CascDumpStorage("E:\\casc_dump.txt", hs, _T("e:\\Ladik\\Appdir\\CascLib\\listfile\\listfile-wow6.txt"));
-// CascDumpIndexEntries("E:\\casc_index.txt", hs);
-// }
-#endif
-
// If something failed, free the storage and return
if(nError != ERROR_SUCCESS)
{
@@ -1147,7 +1290,7 @@ bool WINAPI CascGetStorageInfo(
size_t * pcbLengthNeeded)
{
TCascStorage * hs;
- DWORD dwCascFeatures = 0;
+ DWORD dwInfoValue = 0;
// Verify the storage handle
hs = IsValidStorageHandle(hStorage);
@@ -1161,41 +1304,41 @@ bool WINAPI CascGetStorageInfo(
switch(InfoClass)
{
case CascStorageFileCount:
-
- // Check the buffer size
- if(cbStorageInfo < sizeof(DWORD))
- {
- *pcbLengthNeeded = sizeof(DWORD);
- SetLastError(ERROR_INSUFFICIENT_BUFFER);
- return false;
- }
-
- // Give the number of files
- *(PDWORD)pvStorageInfo = (DWORD)hs->pIndexEntryMap->ItemCount;
- return true;
+ dwInfoValue = (DWORD)hs->pIndexEntryMap->ItemCount;
+ break;
case CascStorageFeatures:
-
- // Check the buffer size
- if(cbStorageInfo < sizeof(DWORD))
- {
- *pcbLengthNeeded = sizeof(DWORD);
- SetLastError(ERROR_INSUFFICIENT_BUFFER);
- return false;
- }
-
- // Construct the features
if(hs->pMndxInfo != NULL)
- dwCascFeatures |= CASC_FEATURE_LISTFILE;
+ dwInfoValue |= CASC_FEATURE_LISTFILE;
+ break;
+
+ case CascStorageGameInfo:
+ dwInfoValue = hs->dwGameInfo;
+ break;
- // Give the number of files
- *(PDWORD)pvStorageInfo = dwCascFeatures;
- return true;
+ case CascStorageGameBuild:
+ dwInfoValue = hs->dwBuildNumber;
+ break;
default:
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
+
+ //
+ // Return the required DWORD value
+ //
+
+ if(cbStorageInfo < sizeof(DWORD))
+ {
+ *pcbLengthNeeded = sizeof(DWORD);
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return false;
+ }
+
+ // Give the number of files
+ *(PDWORD)pvStorageInfo = dwInfoValue;
+ return true;
}
diff --git a/dep/CascLib/src/CascReadFile.cpp b/dep/CascLib/src/CascReadFile.cpp
index 64dd66ef88a..d35da531ec4 100644
--- a/dep/CascLib/src/CascReadFile.cpp
+++ b/dep/CascLib/src/CascReadFile.cpp
@@ -15,28 +15,11 @@
//-----------------------------------------------------------------------------
// Local structures
-#define BLTE_HEADER_SIGNATURE 0x45544C42
-
-// Data file begin:
-// BYTE HeaderHash[MD5_HASH_SIZE]; // MD5 of the frame array
-// DWORD dwFileSize; // Size of the file
-// BYTE SomeSize[4]; // Some size (big endian)
-// BYTE Padding[6]; // Padding (?)
-
-typedef struct _BLTE_HEADER
-{
- DWORD dwSignature; // Must be "BLTE"
- BYTE HeaderSizeAsBytes[4]; // Header size in bytes (big endian)
- BYTE MustBe0F; // Must be 0x0F
- BYTE FrameCount[3]; // Number of frames (big endian)
-
-} BLTE_HEADER, *PBLTE_HEADER;
-
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 frame
+ BYTE md5[MD5_HASH_SIZE]; // Hash of the compressed frame
} BLTE_FRAME, *PBLTE_FRAME;
@@ -65,11 +48,6 @@ static int EnsureDataStreamIsOpen(TCascFile * hf)
// Open the stream
pStream = FileStream_OpenFile(szDataFile, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
hs->DataFileArray[hf->ArchiveIndex] = pStream;
-
- // TODO: There is 0x1E bytes at the beginning of the file stream
- // Ignore them for now, but we will want to know what they mean
- // Offs0000: MD5 of something
- // Offs0010: 2 bytes
CASC_FREE(szDataFile);
}
}
@@ -79,7 +57,7 @@ static int EnsureDataStreamIsOpen(TCascFile * hf)
return (hf->pStream != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND;
}
-static int LoadFileFrames(TCascFile * hf, DWORD FrameCount)
+static int LoadFileFrames(TCascFile * hf)
{
PBLTE_FRAME pFileFrames;
PBLTE_FRAME pFileFrame;
@@ -93,40 +71,41 @@ static int LoadFileFrames(TCascFile * hf, DWORD FrameCount)
assert(hf->pFrames != NULL);
// Allocate frame array
- pFileFrames = pFileFrame = CASC_ALLOC(BLTE_FRAME, FrameCount);
+ pFileFrames = pFileFrame = CASC_ALLOC(BLTE_FRAME, hf->FrameCount);
if(pFileFrames != NULL)
{
// Load the frame array
ArchiveFileOffset = hf->FramesOffset;
- if(FileStream_Read(hf->pStream, &ArchiveFileOffset, pFileFrames, FrameCount * sizeof(BLTE_FRAME)))
+ if(FileStream_Read(hf->pStream, &ArchiveFileOffset, pFileFrames, hf->FrameCount * sizeof(BLTE_FRAME)))
{
// Move the raw archive offset
ArchiveFileOffset += (hf->FrameCount * sizeof(BLTE_FRAME));
// Copy the frames to the file structure
- for(DWORD i = 0; i < FrameCount; i++, pFileFrame++)
+ for(DWORD i = 0; i < hf->FrameCount; i++, pFileFrame++)
{
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);
+ 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;
}
-
- // Fill-in the frame count
- hf->FrameCount = FrameCount;
}
else
nError = GetLastError();
- // Verify the file size
-// assert(FileSize == hf->FileSize);
+ // Note: Do not take the FileSize from the sum of frames.
+ // This value is invalid when loading the ENCODING file.
// hf->FileSize = FileSize;
+#ifdef CASCLIB_TEST
+ hf->FileSize_FrameSum = FileSize;
+#endif
+
// Free the array
CASC_FREE(pFileFrames);
}
@@ -136,66 +115,115 @@ static int LoadFileFrames(TCascFile * hf, DWORD FrameCount)
return nError;
}
-static int EnsureFrameHeadersLoaded(TCascFile * hf)
+static int EnsureHeaderAreaIsLoaded(TCascFile * hf)
{
- PBLTE_HEADER pBlteHeader;
+ TCascStorage * hs = hf->hs;
ULONGLONG FileOffset = hf->HeaderOffset;
- DWORD dwHeaderOffsetFixup = 0;
- DWORD dwFrameHeaderSize;
- DWORD dwFrameCount;
- BYTE HeaderBuffer[sizeof(BLTE_HEADER) + 0x20];
- int nError = ERROR_SUCCESS;
-
- // Sanity check
- assert(hf->pStream != NULL);
+ 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;
- // If the frame headers are not loaded yet, do it
- if(hf->pFrames == NULL)
+ hs->dwFileBeginDelta = (FileSignature == BLTE_HEADER_SIGNATURE) ? BLTE_HEADER_DELTA : 0;
+ }
+
+ // If the file size is not loaded yet, do it
+ if(hf->FrameCount == 0)
{
- // Note that older builds of Heroes of the Storm have entries pointing
- // to the begin of the BLTE header, which is MD5 + some junk.
- // Newer versions of HOTS have encoding entries pointing directly to
- // the BLTE header
- FileStream_Read(hf->pStream, &FileOffset, HeaderBuffer, sizeof(HeaderBuffer));
- pBlteHeader = (PBLTE_HEADER)HeaderBuffer;
-
- // If we don't have the BLTE header right there,
- // just get the block that is 0x1E bytes later
- if(pBlteHeader->dwSignature != BLTE_HEADER_SIGNATURE)
+ // 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);
+
+ // Load the size of the frame headers
+ hf->HeaderSize = ConvertBytesToInteger_4(pbHeaderArea);
+ if(hf->HeaderSize & 0x80000000)
+ return ERROR_BAD_FORMAT;
+ pbHeaderArea += sizeof(DWORD);
+
+ // Read the header size
+ assert(hs->dwFileBeginDelta <= BLTE_HEADER_DELTA);
+ hf->HeaderOffset += (BLTE_HEADER_DELTA - hs->dwFileBeginDelta);
+ hf->FrameCount = 1;
+
+ // Retrieve the frame count, if different from 1
+ if(hf->HeaderSize != 0)
{
- memcpy(&HeaderBuffer[0x00], &HeaderBuffer[0x1E], sizeof(BLTE_HEADER));
- dwHeaderOffsetFixup = 0x1E;
+ // The next byte must be 0x0F
+ if(pbHeaderArea[0] != 0x0F)
+ return ERROR_BAD_FORMAT;
+ pbHeaderArea++;
+
+ // Next three bytes form number of frames
+ hf->FrameCount = ConvertBytesToInteger_3(pbHeaderArea);
}
- // Check for the BLTE header signature
- if(pBlteHeader->dwSignature != BLTE_HEADER_SIGNATURE)
- return ERROR_BAD_FORMAT;
- hf->HeaderOffset += dwHeaderOffsetFixup;
+#ifdef CASCLIB_TEST
+ hf->FileSize_HdrArea = FileSize;
+#endif
+ }
+
+ return ERROR_SUCCESS;
+}
+
+static int EnsureFrameHeadersLoaded(TCascFile * hf)
+{
+ int nError;
- // Check for a single unit file
- dwFrameHeaderSize = ConvertBytesToInteger_4(pBlteHeader->HeaderSizeAsBytes);
- dwFrameCount = (dwFrameHeaderSize != 0) ? ConvertBytesToInteger_3(pBlteHeader->FrameCount) : 1;
+ // Make sure we have header area loaded
+ nError = EnsureHeaderAreaIsLoaded(hf);
+ if(nError != ERROR_SUCCESS)
+ return nError;
+ // If the frame headers are not loaded yet, do it
+ if(hf->pFrames == NULL)
+ {
// Allocate the frame array
- hf->pFrames = CASC_ALLOC(CASC_FILE_FRAME, dwFrameCount);
+ hf->pFrames = CASC_ALLOC(CASC_FILE_FRAME, hf->FrameCount);
if(hf->pFrames != NULL)
{
- // Save the number of frames
- hf->FrameCount = dwFrameCount;
-
// Either load the frames from the file or supply them on our own
- if(dwFrameHeaderSize != 0)
+ if(hf->HeaderSize != 0)
{
- if(pBlteHeader->MustBe0F != 0x0F)
- return ERROR_FILE_CORRUPT;
-
- hf->FramesOffset = hf->HeaderOffset + sizeof(BLTE_HEADER);
- nError = LoadFileFrames(hf, dwFrameCount);
+ 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(pBlteHeader->dwSignature) + sizeof(pBlteHeader->HeaderSizeAsBytes);
+ hf->FramesOffset = hf->HeaderOffset + sizeof(DWORD) + sizeof(DWORD);
hf->pFrames[0].FrameArchiveOffset = hf->FramesOffset;
hf->pFrames[0].FrameFileOffset = 0;
@@ -239,9 +267,27 @@ static PCASC_FILE_FRAME FindFileFrame(TCascFile * hf, DWORD FilePointer)
//-----------------------------------------------------------------------------
// Public functions
+//
+// 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
+//
+
DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh)
{
TCascFile * hf;
+ int nError;
CASCLIB_UNUSED(pdwFileSizeHigh);
@@ -252,6 +298,14 @@ DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh)
return CASC_INVALID_SIZE;
}
+ // Make sure that the file header area is loaded
+ nError = EnsureHeaderAreaIsLoaded(hf);
+ if(nError != ERROR_SUCCESS)
+ {
+ SetLastError(nError);
+ return CASC_INVALID_SIZE;
+ }
+
// Give the file size to the caller
if(pdwFileSizeHigh != NULL)
*pdwFileSizeHigh = 0;
@@ -348,23 +402,17 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW
return false;
}
- // If the file position is at or beyond end of file, do nothing
- if(hf->FilePointer >= hf->FileSize)
- {
- *pdwBytesRead = 0;
- return ERROR_SUCCESS;
- }
-
- // Make sure we have that data file open
+ // If the file frames are not loaded yet, do it now
if(nError == ERROR_SUCCESS)
{
- nError = EnsureDataStreamIsOpen(hf);
+ nError = EnsureFrameHeadersLoaded(hf);
}
- // If the file frames are not loaded yet, do it now
- if(nError == ERROR_SUCCESS)
+ // If the file position is at or beyond end of file, do nothing
+ if(nError == ERROR_SUCCESS && hf->FilePointer >= hf->FileSize)
{
- nError = EnsureFrameHeadersLoaded(hf);
+ *pdwBytesRead = 0;
+ return ERROR_SUCCESS;
}
// Find the file frame where to read from
@@ -423,7 +471,7 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW
}
// Verify the block MD5
- if(IsValidMD5(pFrame->md5) && !VerifyDataBlockHash(pbRawData, pFrame->CompressedSize, pFrame->md5))
+ if(!VerifyDataBlockHash(pbRawData, pFrame->CompressedSize, pFrame->md5))
{
CASC_FREE(pbRawData);
nError = ERROR_FILE_CORRUPT;
diff --git a/dep/CascLib/src/common/Common.cpp b/dep/CascLib/src/common/Common.cpp
index d81c1b6fd89..74f86d44853 100644
--- a/dep/CascLib/src/common/Common.cpp
+++ b/dep/CascLib/src/common/Common.cpp
@@ -62,20 +62,6 @@ unsigned char AsciiToUpperTable[256] =
unsigned char IntToHexChar[] = "0123456789abcdef";
//-----------------------------------------------------------------------------
-// Support for memory reallocation
-
-#if defined(_MSC_VER) && defined(_DEBUG)
-void * DbgRealloc(void * ptr, size_t nSize)
-{
- // HeapReAlloc does not support NULL as previous block
- if(ptr == NULL)
- return HeapAlloc(GetProcessHeap, 0, nSize);
-
- return HeapReAlloc(GetProcessHeap(), 0, ptr, nSize);
-}
-#endif
-
-//-----------------------------------------------------------------------------
// GetLastError/SetLastError support for non-Windows platform
#ifndef PLATFORM_WINDOWS
@@ -232,11 +218,14 @@ TCHAR * CombinePath(const TCHAR * szDirectory, const TCHAR * szSubDir)
return szFullPath;
}
-void NormalizeFileName_UpperBkSlash(char * szFileName)
+void NormalizeFileName_UpperBkSlash(const char * szSrcFileName, char * szTrgFileName)
{
+ size_t i;
+
// Normalize the file name: ToLower + BackSlashToSlash
- for(size_t i = 0; szFileName[i] != 0; i++)
- szFileName[i] = AsciiToUpperTable[szFileName[i]];
+ for(i = 0; szSrcFileName[i] != 0; i++)
+ szTrgFileName[i] = AsciiToUpperTable[szSrcFileName[i]];
+ szTrgFileName[i] = 0;
}
void NormalizeFileName_LowerSlash(char * szFileName)
diff --git a/dep/CascLib/src/common/Common.h b/dep/CascLib/src/common/Common.h
index eb192fd50ca..40712b99eed 100644
--- a/dep/CascLib/src/common/Common.h
+++ b/dep/CascLib/src/common/Common.h
@@ -29,13 +29,6 @@ extern unsigned char AsciiToUpperTable[256];
extern unsigned char IntToHexChar[];
//-----------------------------------------------------------------------------
-// Memory management helper
-
-#if defined(_MSC_VER) && defined(_DEBUG)
-void * DbgRealloc(void * ptr, size_t nSize);
-#endif
-
-//-----------------------------------------------------------------------------
// GetLastError/SetLastError support for non-Windows platform
#ifndef PLATFORM_WINDOWS
@@ -57,7 +50,7 @@ TCHAR * NewStrFromAnsi(LPBYTE pbStringBegin, LPBYTE pbStringEnd);
TCHAR * CombinePath(const TCHAR * szPath, const TCHAR * szSubDir);
-void NormalizeFileName_UpperBkSlash(char * szFileName);
+void NormalizeFileName_UpperBkSlash(const char * szSrcFileName, char * szTrgFileName);
void NormalizeFileName_LowerSlash(char * szFileName);
int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue);
diff --git a/dep/CascLib/src/common/FileStream.cpp b/dep/CascLib/src/common/FileStream.cpp
index 09ac47bd095..8e541b44fca 100644
--- a/dep/CascLib/src/common/FileStream.cpp
+++ b/dep/CascLib/src/common/FileStream.cpp
@@ -2551,6 +2551,14 @@ bool FileStream_SetCallback(TFileStream * pStream, STREAM_DOWNLOAD_CALLBACK pfnC
*/
bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead)
{
+ //FILE * fp = fopen("E:\\Loading.txt", "at");
+ //if(fp != NULL)
+ //{
+ // ULONGLONG ByteOffset = (pByteOffset != NULL) ? pByteOffset[0] : 0;
+ // fprintf(fp, "%-32ws\t%08X\t%08X\n", GetPlainFileName(pStream->szFileName), (ULONG)ByteOffset, dwBytesToRead);
+ // fclose(fp);
+ //}
+
assert(pStream->StreamRead != NULL);
return pStream->StreamRead(pStream, pByteOffset, pvBuffer, dwBytesToRead);
}
diff --git a/dep/CascLib/src/common/ListFile.cpp b/dep/CascLib/src/common/ListFile.cpp
index 4b742b1d07c..8e0b1374241 100644
--- a/dep/CascLib/src/common/ListFile.cpp
+++ b/dep/CascLib/src/common/ListFile.cpp
@@ -125,12 +125,12 @@ static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, size_t nM
}
// If we have found a newline, stop loading
- if(*pCache->pPos == 0x0D || *pCache->pPos == 0x0A)
+ if(pCache->pPos[0] == 0x0D || pCache->pPos[0] == 0x0A)
break;
// Blizzard listfiles can also contain information about patch:
// Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326)
- if(*pCache->pPos == '~')
+ if(pCache->pPos[0] == '~')
szExtraString = szLine;
// Copy the character
@@ -190,7 +190,7 @@ static TListFileCache * CreateListFileCache(RELOAD_CACHE pfnReloadCache, CLOSE_S
}
//-----------------------------------------------------------------------------
-// Listfile functions
+// Functions for parsing an external listfile
void * ListFile_OpenExternal(const TCHAR * szListFile)
{
@@ -264,3 +264,160 @@ void ListFile_Free(void * pvListFile)
CASC_FREE(pCache);
}
}
+
+//-----------------------------------------------------------------------------
+// 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;
+ char szFileName2[MAX_PATH+1];
+ size_t cbToAllocate;
+ size_t cbEntrySize;
+ uint32_t dwHashHigh = 0;
+ uint32_t dwHashLow = 0;
+
+ // 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);
+
+ // Get the name hash
+ NormalizeFileName_UpperBkSlash(szFileName, szFileName2);
+ hashlittle2(szFileName2, nLength, &dwHashHigh, &dwHashLow);
+
+ // Calculate the HASH value of the normalized file name
+ pListEntry->FileNameHash = ((ULONGLONG)dwHashHigh << 0x20) | dwHashLow;
+ pListEntry->cbEntrySize = (DWORD)cbEntrySize;
+ 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);
+ }
+
+ return pListMap;
+}
+
+PLISTFILE_MAP ListFile_CreateMap(const TCHAR * szListFile)
+{
+ PLISTFILE_MAP pListMap = NULL;
+ void * pvListFile;
+ char szFileName[MAX_PATH+1];
+ size_t nLength;
+
+ // Only if the listfile name has been given
+ if(szListFile != 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;
+ }
+
+ // Finish the listfile map
+ pListMap = ListMap_Finish(pListMap);
+
+ // Free the listfile
+ ListFile_Free(pvListFile);
+ }
+ }
+ }
+
+ // 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);
+ return (pListEntry != NULL) ? pListEntry->szFileName : "";
+}
+
+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 1c603af3766..9815160e1ea 100644
--- a/dep/CascLib/src/common/ListFile.h
+++ b/dep/CascLib/src/common/ListFile.h
@@ -11,8 +11,40 @@
#ifndef __LISTFILE_H__
#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);
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);
+
#endif // __LISTFILE_H__
diff --git a/dep/CascLib/src/common/Map.h b/dep/CascLib/src/common/Map.h
index b4b9c918df3..40ea4238b81 100644
--- a/dep/CascLib/src/common/Map.h
+++ b/dep/CascLib/src/common/Map.h
@@ -14,7 +14,7 @@
//-----------------------------------------------------------------------------
// Structures
-#define KEY_LENGTH_STRING 0xFFFFFFFF // Pass this to Map_Create as dwKeyLength when you want map of string->object
+#define KEY_LENGTH_STRING 0xFFFFFFFF // Pass this to Map_Create as dwKeyLength when you want map of string->object
typedef struct _CASC_MAP
{