mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 23:50:44 +01:00
608 lines
24 KiB
C++
608 lines
24 KiB
C++
/*****************************************************************************/
|
|
/* CascRootFile_WoW.cpp Copyright (c) Ladislav Zezula 2014 */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Storage functions for CASC */
|
|
/* Note: WoW offsets refer to WoW.exe 6.0.3.19116 (32-bit) */
|
|
/* SHA1: c10e9ffb7d040a37a356b96042657e1a0c95c0dd */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Date Ver Who Comment */
|
|
/* -------- ---- --- ------- */
|
|
/* 29.04.14 1.00 Lad The first version of CascRootFile_WoW.cpp */
|
|
/*****************************************************************************/
|
|
|
|
#define __CASCLIB_SELF__
|
|
#include "CascLib.h"
|
|
#include "CascCommon.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Local structures
|
|
|
|
#define ROOT_SEARCH_PHASE_INITIALIZING 0
|
|
#define ROOT_SEARCH_PHASE_LISTFILE 1
|
|
#define ROOT_SEARCH_PHASE_NAMELESS 2
|
|
#define ROOT_SEARCH_PHASE_FINISHED 3
|
|
|
|
// Known dwRegion values returned from sub_661316 (7.0.3.22210 x86 win), also referred by lua GetCurrentRegion
|
|
#define WOW_REGION_US 0x01
|
|
#define WOW_REGION_KR 0x02
|
|
#define WOW_REGION_EU 0x03
|
|
#define WOW_REGION_TW 0x04
|
|
#define WOW_REGION_CN 0x05
|
|
|
|
typedef enum _ROOT_FORMAT
|
|
{
|
|
RootFormatWoW_v1, // Since build 18125 (WoW 6.0.1)
|
|
RootFormatWoW_v2, // Since build 30080 (WoW 8.2.0)
|
|
} ROOT_FORMAT, *PROOT_FORMAT;
|
|
|
|
// ROOT file header since build 50893 (10.1.7)
|
|
typedef struct _FILE_ROOT_HEADER_50893
|
|
{
|
|
DWORD Signature; // Must be CASC_WOW_ROOT_SIGNATURE
|
|
DWORD SizeOfHeader;
|
|
DWORD Version; // Must be 1
|
|
DWORD TotalFiles;
|
|
DWORD FilesWithNameHash;
|
|
} FILE_ROOT_HEADER_50893, * PFILE_ROOT_HEADER_50893;
|
|
|
|
// ROOT file header since build 30080 (8.2.0)
|
|
typedef struct _FILE_ROOT_HEADER_30080
|
|
{
|
|
DWORD Signature; // Must be CASC_WOW_ROOT_SIGNATURE
|
|
DWORD TotalFiles;
|
|
DWORD FilesWithNameHash;
|
|
} FILE_ROOT_HEADER_30080, *PFILE_ROOT_HEADER_30080;
|
|
|
|
// On-disk version of root group. A root group contains a group of file
|
|
// with the same locale and file flags
|
|
typedef struct _FILE_ROOT_GROUP_HEADER
|
|
{
|
|
DWORD NumberOfFiles; // Number of entries
|
|
DWORD ContentFlags;
|
|
DWORD LocaleFlags; // File locale mask (CASC_LOCALE_XXX)
|
|
|
|
// Followed by a block of file data IDs (count: NumberOfFiles)
|
|
// Followed by the MD5 and file name hash (count: NumberOfFiles)
|
|
|
|
} FILE_ROOT_GROUP_HEADER, *PFILE_ROOT_GROUP_HEADER;
|
|
|
|
// On-disk version of root entry. Only present in versions 6.x - 8.1.xx
|
|
// Each root entry represents one file in the CASC storage
|
|
// In WoW build 30080 (8.2.0)+, CKey and FileNameHash are split into separate arrays
|
|
// and FileNameHash is optional
|
|
typedef struct _FILE_ROOT_ENTRY
|
|
{
|
|
CONTENT_KEY CKey; // MD5 of the file
|
|
ULONGLONG FileNameHash; // Jenkins hash of the file name
|
|
|
|
} FILE_ROOT_ENTRY, *PFILE_ROOT_ENTRY;
|
|
|
|
typedef struct _FILE_ROOT_GROUP
|
|
{
|
|
FILE_ROOT_GROUP_HEADER Header;
|
|
PDWORD FileDataIds; // Pointer to the array of File Data IDs
|
|
|
|
PFILE_ROOT_ENTRY pRootEntries; // Valid for WoW since 18125
|
|
PCONTENT_KEY pCKeyEntries; // Valid for WoW since 30080
|
|
PULONGLONG pHashes; // Valid for WoW since 30080 (optional)
|
|
|
|
} FILE_ROOT_GROUP, *PFILE_ROOT_GROUP;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// TRootHandler_WoW interface / implementation
|
|
|
|
#define FTREE_FLAGS_WOW (FTREE_FLAG_USE_DATA_ID | FTREE_FLAG_USE_LOCALE_FLAGS | FTREE_FLAG_USE_CONTENT_FLAGS)
|
|
|
|
struct TRootHandler_WoW : public TFileTreeRoot
|
|
{
|
|
public:
|
|
|
|
typedef LPBYTE (*CAPTURE_ROOT_HEADER)(LPBYTE pbRootPtr, LPBYTE pbRootEnd, PROOT_FORMAT RootFormat, PDWORD FileCounterHashless);
|
|
|
|
TRootHandler_WoW(ROOT_FORMAT RFormat, DWORD HashlessFileCount) : TFileTreeRoot(FTREE_FLAGS_WOW)
|
|
{
|
|
// Turn off the "we know file names" bit
|
|
FileCounterHashless = HashlessFileCount;
|
|
FileCounter = 0;
|
|
RootFormat = RFormat;
|
|
|
|
// Update the flags based on format
|
|
switch(RootFormat)
|
|
{
|
|
case RootFormatWoW_v2:
|
|
dwFeatures |= CASC_FEATURE_ROOT_CKEY | CASC_FEATURE_LOCALE_FLAGS | CASC_FEATURE_CONTENT_FLAGS | CASC_FEATURE_FILE_DATA_IDS | CASC_FEATURE_FNAME_HASHES_OPTIONAL;
|
|
break;
|
|
|
|
case RootFormatWoW_v1:
|
|
dwFeatures |= CASC_FEATURE_ROOT_CKEY | CASC_FEATURE_LOCALE_FLAGS | CASC_FEATURE_CONTENT_FLAGS | CASC_FEATURE_FNAME_HASHES;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check for the new format (World of Warcraft 10.1.7, build 50893)
|
|
static LPBYTE CaptureRootHeader_50893(LPBYTE pbRootPtr, LPBYTE pbRootEnd, PROOT_FORMAT RootFormat, PDWORD FileCounterHashless)
|
|
{
|
|
FILE_ROOT_HEADER_50893 RootHeader;
|
|
|
|
// Validate the root file header
|
|
if((pbRootPtr + sizeof(FILE_ROOT_HEADER_50893)) >= pbRootEnd)
|
|
return NULL;
|
|
memcpy(&RootHeader, pbRootPtr, sizeof(FILE_ROOT_HEADER_50893));
|
|
|
|
// Verify the root file header
|
|
if(RootHeader.Signature != CASC_WOW_ROOT_SIGNATURE)
|
|
return NULL;
|
|
if(RootHeader.Version != 1)
|
|
return NULL;
|
|
if(RootHeader.FilesWithNameHash > RootHeader.TotalFiles)
|
|
return NULL;
|
|
// wow client doesn't seem to think this is a fatal error, we will do the same for now
|
|
if(RootHeader.SizeOfHeader < 4)
|
|
RootHeader.SizeOfHeader = 4;
|
|
|
|
*RootFormat = RootFormatWoW_v2;
|
|
*FileCounterHashless = RootHeader.TotalFiles - RootHeader.FilesWithNameHash;
|
|
return pbRootPtr + RootHeader.SizeOfHeader;
|
|
}
|
|
|
|
// Check for the root format for build 30080+ (WoW 8.2.0)
|
|
static LPBYTE CaptureRootHeader_30080(LPBYTE pbRootPtr, LPBYTE pbRootEnd, PROOT_FORMAT RootFormat, PDWORD FileCounterHashless)
|
|
{
|
|
FILE_ROOT_HEADER_30080 RootHeader;
|
|
|
|
// Validate the root file header
|
|
if((pbRootPtr + sizeof(FILE_ROOT_HEADER_30080)) >= pbRootEnd)
|
|
return NULL;
|
|
memcpy(&RootHeader, pbRootPtr, sizeof(FILE_ROOT_HEADER_30080));
|
|
|
|
// Verify the root file header
|
|
if(RootHeader.Signature != CASC_WOW_ROOT_SIGNATURE)
|
|
return NULL;
|
|
if(RootHeader.FilesWithNameHash > RootHeader.TotalFiles)
|
|
return NULL;
|
|
|
|
*RootFormat = RootFormatWoW_v2;
|
|
*FileCounterHashless = RootHeader.TotalFiles - RootHeader.FilesWithNameHash;
|
|
return pbRootPtr + sizeof(FILE_ROOT_HEADER_30080);
|
|
}
|
|
|
|
// Check for the root format for build 18125+ (WoW 6.0.1)
|
|
static LPBYTE CaptureRootHeader_18125(LPBYTE pbRootPtr, LPBYTE pbRootEnd, PROOT_FORMAT RootFormat, PDWORD FileCounterHashless)
|
|
{
|
|
size_t DataLength;
|
|
|
|
// There is no header. Right at the begin, there's FILE_ROOT_GROUP_HEADER structure,
|
|
// followed by the array of DWORDs and FILE_ROOT_ENTRYs
|
|
if((pbRootPtr + sizeof(FILE_ROOT_GROUP_HEADER)) >= pbRootEnd)
|
|
return NULL;
|
|
DataLength = ((PFILE_ROOT_GROUP_HEADER)(pbRootPtr))->NumberOfFiles * (sizeof(DWORD) + sizeof(FILE_ROOT_ENTRY));
|
|
|
|
// Validate the array of data
|
|
if((pbRootPtr + sizeof(FILE_ROOT_GROUP_HEADER) + DataLength) >= pbRootEnd)
|
|
return NULL;
|
|
|
|
*RootFormat = RootFormatWoW_v1;
|
|
*FileCounterHashless = 0;
|
|
return pbRootPtr;
|
|
}
|
|
|
|
static LPBYTE CaptureRootHeader(LPBYTE pbRootPtr, LPBYTE pbRootEnd, PROOT_FORMAT RootFormat, PDWORD FileCounterHashless)
|
|
{
|
|
CAPTURE_ROOT_HEADER PfnCaptureRootHeader[] =
|
|
{
|
|
&CaptureRootHeader_50893,
|
|
&CaptureRootHeader_30080,
|
|
&CaptureRootHeader_18125,
|
|
};
|
|
|
|
for(size_t i = 0; i < _countof(PfnCaptureRootHeader); i++)
|
|
{
|
|
LPBYTE pbCapturedPtr;
|
|
|
|
if((pbCapturedPtr = PfnCaptureRootHeader[i](pbRootPtr, pbRootEnd, RootFormat, FileCounterHashless)) != NULL)
|
|
{
|
|
return pbCapturedPtr;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
LPBYTE CaptureRootGroup(FILE_ROOT_GROUP & RootGroup, LPBYTE pbRootPtr, LPBYTE pbRootEnd)
|
|
{
|
|
// Reset the entire root group structure
|
|
memset(&RootGroup, 0, sizeof(FILE_ROOT_GROUP));
|
|
|
|
// Validate the locale block header
|
|
if((pbRootPtr + sizeof(FILE_ROOT_GROUP_HEADER)) >= pbRootEnd)
|
|
return NULL;
|
|
memcpy(&RootGroup.Header, pbRootPtr, sizeof(FILE_ROOT_GROUP_HEADER));
|
|
pbRootPtr = pbRootPtr + sizeof(FILE_ROOT_GROUP_HEADER);
|
|
|
|
// Validate the array of file data IDs
|
|
if((pbRootPtr + (sizeof(DWORD) * RootGroup.Header.NumberOfFiles)) >= pbRootEnd)
|
|
return NULL;
|
|
RootGroup.FileDataIds = (PDWORD)pbRootPtr;
|
|
pbRootPtr = pbRootPtr + (sizeof(DWORD) * RootGroup.Header.NumberOfFiles);
|
|
|
|
// Add the number of files in this block to the number of files loaded
|
|
FileCounter += RootGroup.Header.NumberOfFiles;
|
|
|
|
// Validate the array of root entries
|
|
switch(RootFormat)
|
|
{
|
|
case RootFormatWoW_v2:
|
|
|
|
// Verify the position of array of CONTENT_KEY
|
|
if((pbRootPtr + (sizeof(CONTENT_KEY) * RootGroup.Header.NumberOfFiles)) > pbRootEnd)
|
|
return NULL;
|
|
RootGroup.pCKeyEntries = (PCONTENT_KEY)pbRootPtr;
|
|
pbRootPtr = pbRootPtr + (sizeof(CONTENT_KEY) * RootGroup.Header.NumberOfFiles);
|
|
|
|
// Also include array of file hashes
|
|
if(!(RootGroup.Header.ContentFlags & CASC_CFLAG_NO_NAME_HASH))
|
|
{
|
|
if((pbRootPtr + (sizeof(ULONGLONG) * RootGroup.Header.NumberOfFiles)) > pbRootEnd)
|
|
return NULL;
|
|
RootGroup.pHashes = (PULONGLONG)pbRootPtr;
|
|
pbRootPtr = pbRootPtr + (sizeof(ULONGLONG) * RootGroup.Header.NumberOfFiles);
|
|
}
|
|
|
|
return pbRootPtr;
|
|
|
|
case RootFormatWoW_v1:
|
|
if((pbRootPtr + (sizeof(FILE_ROOT_ENTRY) * RootGroup.Header.NumberOfFiles)) > pbRootEnd)
|
|
return NULL;
|
|
RootGroup.pRootEntries = (PFILE_ROOT_ENTRY)pbRootPtr;
|
|
|
|
// Return the position of the next block
|
|
return pbRootPtr + (sizeof(FILE_ROOT_ENTRY) * RootGroup.Header.NumberOfFiles);
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// Since WoW build 30080 (8.2.0)
|
|
DWORD ParseWowRootFile_AddFiles_v2(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup)
|
|
{
|
|
PCASC_CKEY_ENTRY pCKeyEntry;
|
|
PCONTENT_KEY pCKey = RootGroup.pCKeyEntries;
|
|
DWORD FileDataId = 0;
|
|
|
|
// Sanity check
|
|
assert(RootGroup.pCKeyEntries != NULL);
|
|
|
|
// WoW.exe (build 19116): Blocks with zero files are skipped
|
|
for(DWORD i = 0; i < RootGroup.Header.NumberOfFiles; i++, pCKey++)
|
|
{
|
|
// Set the file data ID
|
|
FileDataId = FileDataId + RootGroup.FileDataIds[i];
|
|
|
|
// Find the item in the central storage. Insert it to the tree
|
|
if((pCKeyEntry = FindCKeyEntry_CKey(hs, pCKey->Value)) != NULL)
|
|
{
|
|
// If we know the file name hash, we're gonna insert it by hash AND file data id.
|
|
// If we don't know the hash, we're gonna insert it just by file data id.
|
|
if(RootGroup.pHashes != NULL && RootGroup.pHashes[i] != 0)
|
|
{
|
|
FileTree.InsertByHash(pCKeyEntry, RootGroup.pHashes[i], FileDataId, RootGroup.Header.LocaleFlags, RootGroup.Header.ContentFlags);
|
|
}
|
|
else
|
|
{
|
|
FileTree.InsertById(pCKeyEntry, FileDataId, RootGroup.Header.LocaleFlags, RootGroup.Header.ContentFlags);
|
|
}
|
|
}
|
|
|
|
// Update the file data ID
|
|
assert((FileDataId + 1) > FileDataId);
|
|
FileDataId++;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// Since WoW build 18125 (6.0.1)
|
|
DWORD ParseWowRootFile_AddFiles_v1(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup)
|
|
{
|
|
PFILE_ROOT_ENTRY pRootEntry = RootGroup.pRootEntries;
|
|
PCASC_CKEY_ENTRY pCKeyEntry;
|
|
DWORD FileDataId = 0;
|
|
|
|
// Sanity check
|
|
assert(RootGroup.pRootEntries != NULL);
|
|
|
|
// WoW.exe (build 19116): Blocks with zero files are skipped
|
|
for(DWORD i = 0; i < RootGroup.Header.NumberOfFiles; i++, pRootEntry++)
|
|
{
|
|
// Set the file data ID
|
|
FileDataId = FileDataId + RootGroup.FileDataIds[i];
|
|
// BREAKIF(FileDataId == 2823765);
|
|
|
|
// Find the item in the central storage. Insert it to the tree
|
|
if((pCKeyEntry = FindCKeyEntry_CKey(hs, pRootEntry->CKey.Value)) != NULL)
|
|
{
|
|
if(pRootEntry->FileNameHash != 0)
|
|
{
|
|
FileTree.InsertByHash(pCKeyEntry, pRootEntry->FileNameHash, FileDataId, RootGroup.Header.LocaleFlags, RootGroup.Header.ContentFlags);
|
|
}
|
|
else
|
|
{
|
|
FileTree.InsertById(pCKeyEntry, FileDataId, RootGroup.Header.LocaleFlags, RootGroup.Header.ContentFlags);
|
|
}
|
|
}
|
|
|
|
// Update the file data ID
|
|
assert((FileDataId + 1) > FileDataId);
|
|
FileDataId++;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD ParseWowRootFile_Level2(
|
|
TCascStorage * hs,
|
|
LPBYTE pbRootPtr,
|
|
LPBYTE pbRootEnd,
|
|
DWORD dwLocaleMask,
|
|
BYTE bOverrideLowViolence,
|
|
BYTE bAudioLocale)
|
|
{
|
|
FILE_ROOT_GROUP RootBlock;
|
|
|
|
// Reset the total file counter
|
|
FileCounter = 0;
|
|
|
|
// Now parse the root file
|
|
while(pbRootPtr < pbRootEnd)
|
|
{
|
|
//char szMessage[0x100];
|
|
//StringCchPrintfA(szMessage, _countof(szMessage), "%p\n", (pbRootEnd - pbRootPtr));
|
|
//OutputDebugStringA(szMessage);
|
|
|
|
// Validate the file locale block
|
|
pbRootPtr = CaptureRootGroup(RootBlock, pbRootPtr, pbRootEnd);
|
|
if(pbRootPtr == NULL)
|
|
return ERROR_BAD_FORMAT;
|
|
|
|
// WoW.exe (build 19116): Entries with flag 0x100 set are skipped
|
|
if(RootBlock.Header.ContentFlags & CASC_CFLAG_DONT_LOAD)
|
|
continue;
|
|
|
|
// WoW.exe (build 19116): Entries with flag 0x80 set are skipped if overrideArchive CVAR is set to FALSE (which is by default in non-chinese clients)
|
|
if((RootBlock.Header.ContentFlags & CASC_CFLAG_LOW_VIOLENCE) && bOverrideLowViolence == 0)
|
|
continue;
|
|
|
|
// WoW.exe (build 19116): Entries with (flags >> 0x1F) not equal to bAudioLocale are skipped
|
|
if((RootBlock.Header.ContentFlags >> 0x1F) != bAudioLocale)
|
|
continue;
|
|
|
|
// WoW.exe (build 19116): Locales other than defined mask are skipped too
|
|
if(RootBlock.Header.LocaleFlags != 0 && (RootBlock.Header.LocaleFlags & dwLocaleMask) == 0)
|
|
continue;
|
|
|
|
// Now call the custom function
|
|
switch(RootFormat)
|
|
{
|
|
case RootFormatWoW_v2:
|
|
ParseWowRootFile_AddFiles_v2(hs, RootBlock);
|
|
break;
|
|
|
|
case RootFormatWoW_v1:
|
|
ParseWowRootFile_AddFiles_v1(hs, RootBlock);
|
|
break;
|
|
|
|
default:
|
|
return ERROR_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
#define CASC_LOCALE_BIT_ENUS 0x01
|
|
#define CASC_LOCALE_BIT_KOKR 0x02
|
|
#define CASC_LOCALE_BIT_RESERVED 0x03
|
|
#define CASC_LOCALE_BIT_FRFR 0x04
|
|
#define CASC_LOCALE_BIT_DEDE 0x05
|
|
#define CASC_LOCALE_BIT_ZHCN 0x06
|
|
#define CASC_LOCALE_BIT_ESES 0x07
|
|
#define CASC_LOCALE_BIT_ZHTW 0x08
|
|
#define CASC_LOCALE_BIT_ENGB 0x09
|
|
#define CASC_LOCALE_BIT_ENCN 0x0A
|
|
#define CASC_LOCALE_BIT_ENTW 0x0B
|
|
#define CASC_LOCALE_BIT_ESMX 0x0C
|
|
#define CASC_LOCALE_BIT_RURU 0x0D
|
|
#define CASC_LOCALE_BIT_PTBR 0x0E
|
|
#define CASC_LOCALE_BIT_ITIT 0x0F
|
|
#define CASC_LOCALE_BIT_PTPT 0x10
|
|
|
|
// dwLocale is obtained from a WOW_LOCALE_* to CASC_LOCALE_BIT_* mapping (sub_6615D0 in 7.0.3.22210 x86 win)
|
|
// because (ENUS, ENGB) and (PTBR, PTPT) pairs share the same value on WOW_LOCALE_* enum
|
|
// dwRegion is used to distinguish them
|
|
if(dwRegion == WOW_REGION_EU)
|
|
{
|
|
// Is this english version of WoW?
|
|
if(dwLocale == CASC_LOCALE_BIT_ENUS)
|
|
{
|
|
LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, CASC_LOCALE_ENGB, bOverrideArchive, bAudioLocale);
|
|
LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, CASC_LOCALE_ENUS, bOverrideArchive, bAudioLocale);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// Is this portuguese version of WoW?
|
|
if(dwLocale == CASC_LOCALE_BIT_PTBR)
|
|
{
|
|
LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, CASC_LOCALE_PTPT, bOverrideArchive, bAudioLocale);
|
|
LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, CASC_LOCALE_PTBR, bOverrideArchive, bAudioLocale);
|
|
}
|
|
}
|
|
else
|
|
LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, (1 << dwLocale), bOverrideArchive, bAudioLocale);
|
|
*/
|
|
|
|
DWORD ParseWowRootFile_Level1(
|
|
TCascStorage * hs,
|
|
LPBYTE pbRootPtr,
|
|
LPBYTE pbRootEnd,
|
|
DWORD dwLocaleMask,
|
|
BYTE bAudioLocale)
|
|
{
|
|
DWORD dwErrCode;
|
|
|
|
// Load the locale as-is
|
|
dwErrCode = ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, dwLocaleMask, false, bAudioLocale);
|
|
if(dwErrCode != ERROR_SUCCESS)
|
|
return dwErrCode;
|
|
|
|
// If we wanted enGB, we also load enUS for the missing files
|
|
if(dwLocaleMask == CASC_LOCALE_ENGB)
|
|
ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, CASC_LOCALE_ENUS, false, bAudioLocale);
|
|
|
|
if(dwLocaleMask == CASC_LOCALE_PTPT)
|
|
ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, CASC_LOCALE_PTBR, false, bAudioLocale);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// WoW.exe: 004146C7 (BuildManifest::Load)
|
|
DWORD Load(TCascStorage * hs, LPBYTE pbRootPtr, LPBYTE pbRootEnd, DWORD dwLocaleMask)
|
|
{
|
|
DWORD dwErrCode;
|
|
|
|
dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 0);
|
|
if(dwErrCode == ERROR_SUCCESS)
|
|
dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 1);
|
|
|
|
#ifdef CASCLIB_DEBUG
|
|
// Dump the array of the file data IDs
|
|
//FileTree.DumpFileDataIds("e:\\file-data-ids.bin");
|
|
#endif
|
|
return dwErrCode;
|
|
}
|
|
|
|
// Search for files
|
|
PCASC_CKEY_ENTRY Search(TCascSearch * pSearch, PCASC_FIND_DATA pFindData)
|
|
{
|
|
// If we have a listfile, we'll feed the listfile entries to the file tree
|
|
if(pSearch->pCache != NULL && pSearch->bListFileUsed == false)
|
|
{
|
|
PCASC_FILE_NODE pFileNode;
|
|
ULONGLONG FileNameHash;
|
|
size_t nLength;
|
|
DWORD FileDataId = CASC_INVALID_ID;
|
|
char szFileName[MAX_PATH];
|
|
|
|
if(RootFormat == RootFormatWoW_v2)
|
|
{
|
|
// Keep going through the listfile
|
|
for(;;)
|
|
{
|
|
// Retrieve the next line from the list file. Ignore lines that are too long to fit in the buffer
|
|
nLength = ListFile_GetNext(pSearch->pCache, szFileName, _countof(szFileName), &FileDataId);
|
|
if(nLength == 0)
|
|
{
|
|
if(GetCascError() == ERROR_INSUFFICIENT_BUFFER)
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Several files were renamed around WoW build 50893 (10.1.7). Example:
|
|
//
|
|
// * 2965132; interface/icons/inv_helm_armor_explorer_d_01.blp file name hash = 0x770b8d2dc4d940aa
|
|
// * 2965132; interface/icons/inv_armor_explorer_d_01_helm.blp file name hash = 0xf47ec17f4a1e49a2
|
|
//
|
|
// For that reason, we also need to check whether the file name hash matches
|
|
//
|
|
|
|
// BREAKIF(FileDataId == 2965132);
|
|
|
|
if((pFileNode = FileTree.FindById(FileDataId)) != NULL)
|
|
{
|
|
if(pFileNode->NameLength == 0)
|
|
{
|
|
if(pFileNode->FileNameHash && pFileNode->FileNameHash != CalcFileNameHash(szFileName))
|
|
continue;
|
|
FileTree.SetNodeFileName(pFileNode, szFileName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Keep going through the listfile
|
|
for(;;)
|
|
{
|
|
// Retrieve the next line from the list file. Ignore lines that are too long to fit in the buffer
|
|
nLength = ListFile_GetNextLine(pSearch->pCache, szFileName, _countof(szFileName));
|
|
if(nLength == 0)
|
|
{
|
|
if(GetCascError() == ERROR_INSUFFICIENT_BUFFER)
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
// Calculate the hash of the file name
|
|
FileNameHash = CalcFileNameHash(szFileName);
|
|
|
|
// Try to find the file node by file name hash
|
|
pFileNode = FileTree.Find(FileNameHash);
|
|
if(pFileNode != NULL && pFileNode->NameLength == 0)
|
|
{
|
|
FileTree.SetNodeFileName(pFileNode, szFileName);
|
|
}
|
|
}
|
|
}
|
|
pSearch->bListFileUsed = true;
|
|
}
|
|
|
|
// Let the file tree root give us the file names
|
|
return TFileTreeRoot::Search(pSearch, pFindData);
|
|
}
|
|
|
|
ROOT_FORMAT RootFormat; // Root file format
|
|
DWORD FileCounterHashless; // Number of files for which we don't have hash. Meaningless for WoW before 8.2.0
|
|
DWORD FileCounter; // Counter of loaded files. Only used during loading of ROOT file
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Public functions
|
|
|
|
DWORD RootHandler_CreateWoW(TCascStorage * hs, CASC_BLOB & RootFile, DWORD dwLocaleMask)
|
|
{
|
|
TRootHandler_WoW * pRootHandler = NULL;
|
|
ROOT_FORMAT RootFormat = RootFormatWoW_v1;
|
|
LPBYTE pbRootFile = RootFile.pbData;
|
|
LPBYTE pbRootEnd = RootFile.End();
|
|
LPBYTE pbRootPtr;
|
|
DWORD FileCounterHashless = 0;
|
|
DWORD dwErrCode = ERROR_BAD_FORMAT;
|
|
|
|
// Verify the root header
|
|
if((pbRootPtr = TRootHandler_WoW::CaptureRootHeader(pbRootFile, pbRootEnd, &RootFormat, &FileCounterHashless)) == NULL)
|
|
return ERROR_BAD_FORMAT;
|
|
|
|
// Create the WOW handler
|
|
pRootHandler = new TRootHandler_WoW(RootFormat, FileCounterHashless);
|
|
if(pRootHandler != NULL)
|
|
{
|
|
//fp = fopen("E:\\file-data-ids2.txt", "wt");
|
|
|
|
// Load the root directory. If load failed, we free the object
|
|
dwErrCode = pRootHandler->Load(hs, pbRootPtr, pbRootEnd, dwLocaleMask);
|
|
if(dwErrCode != ERROR_SUCCESS)
|
|
{
|
|
delete pRootHandler;
|
|
pRootHandler = NULL;
|
|
}
|
|
|
|
//fclose(fp);
|
|
}
|
|
|
|
// Assign the root directory (or NULL) and return error
|
|
hs->pRootHandler = pRootHandler;
|
|
return dwErrCode;
|
|
}
|
|
|