aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src/CascOpenStorage.cpp
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/CascOpenStorage.cpp
parent8bbd9133d57fc34b77544cbdd59526ed9ccaa607 (diff)
Dep/CascLib: Update to https://github.com/ladislav-zezula/CascLib/commit/5d3789af3435534c288c2145e158d422651c7fe1
Closes #13866
Diffstat (limited to 'dep/CascLib/src/CascOpenStorage.cpp')
-rw-r--r--dep/CascLib/src/CascOpenStorage.cpp605
1 files changed, 374 insertions, 231 deletions
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;
}