aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src/CascRootFile_WoW.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dep/CascLib/src/CascRootFile_WoW.cpp')
-rw-r--r--dep/CascLib/src/CascRootFile_WoW.cpp152
1 files changed, 119 insertions, 33 deletions
diff --git a/dep/CascLib/src/CascRootFile_WoW.cpp b/dep/CascLib/src/CascRootFile_WoW.cpp
index b3e75fa746e..6f3521cfceb 100644
--- a/dep/CascLib/src/CascRootFile_WoW.cpp
+++ b/dep/CascLib/src/CascRootFile_WoW.cpp
@@ -35,6 +35,19 @@ typedef enum _ROOT_FORMAT
RootFormatWoW_v2, // Since build 30080 (WoW 8.2.0)
} ROOT_FORMAT, *PROOT_FORMAT;
+// The last byte of the structure causes wrong alignment with default compiler options
+#pragma pack(push, 1)
+typedef struct _FILE_ROOT_GROUP_HEADER_58221 // Since build 58221 (11.1.0.58221)
+{
+ DWORD NumberOfFiles; // Number of entries
+ DWORD LocaleFlags; // File locale mask (CASC_LOCALE_XXX)
+ DWORD ContentFlags1;
+ DWORD ContentFlags2;
+ BYTE ContentFlags3;
+
+} FILE_ROOT_GROUPHEADER_58221, *PFILE_ROOT_GROUPHEADER_58221;
+#pragma pack(pop)
+
// ROOT file header since build 50893 (10.1.7)
typedef struct _FILE_ROOT_HEADER_50893
{
@@ -43,7 +56,7 @@ typedef struct _FILE_ROOT_HEADER_50893
DWORD Version; // Must be 1
DWORD TotalFiles;
DWORD FilesWithNameHash;
-} FILE_ROOT_HEADER_50893, * PFILE_ROOT_HEADER_50893;
+} FILE_ROOT_HEADER_50893, *PFILE_ROOT_HEADER_50893;
// ROOT file header since build 30080 (8.2.0)
typedef struct _FILE_ROOT_HEADER_30080
@@ -97,14 +110,15 @@ struct TRootHandler_WoW : public TFileTreeRoot
{
public:
- typedef LPBYTE (*CAPTURE_ROOT_HEADER)(LPBYTE pbRootPtr, LPBYTE pbRootEnd, PROOT_FORMAT RootFormat, PDWORD FileCounterHashless);
+ typedef LPBYTE (*CAPTURE_ROOT_HEADER)(LPBYTE pbRootPtr, LPBYTE pbRootEnd, PROOT_FORMAT RootFormat, PDWORD FileCounterHashless, PDWORD Version);
- TRootHandler_WoW(ROOT_FORMAT RFormat, DWORD HashlessFileCount) : TFileTreeRoot(FTREE_FLAGS_WOW)
+ TRootHandler_WoW(ROOT_FORMAT aRootFormat, DWORD aFileCounterHashless, LPCTSTR szDumpFile = NULL) : TFileTreeRoot(FTREE_FLAGS_WOW)
{
// Turn off the "we know file names" bit
- FileCounterHashless = HashlessFileCount;
+ FileCounterHashless = aFileCounterHashless;
FileCounter = 0;
- RootFormat = RFormat;
+ RootFormat = aRootFormat;
+ fp = NULL;
// Update the flags based on format
switch(RootFormat)
@@ -117,10 +131,44 @@ struct TRootHandler_WoW : public TFileTreeRoot
dwFeatures |= CASC_FEATURE_ROOT_CKEY | CASC_FEATURE_LOCALE_FLAGS | CASC_FEATURE_CONTENT_FLAGS | CASC_FEATURE_FNAME_HASHES;
break;
}
+
+ // Create the file for dumping listfile
+ if(szDumpFile && szDumpFile[0])
+ {
+ fp = _tfopen(szDumpFile, _T("wt"));
+ }
+ }
+
+ ~TRootHandler_WoW()
+ {
+ if(fp != NULL)
+ fclose(fp);
+ fp = NULL;
}
+#ifdef CASCLIB_WRITE_VERIFIED_FILENAMES
+ void VerifyAndLogFileName(LPCSTR szFileName, ULONG FileDataId)
+ {
+ PCASC_FILE_NODE pFileNode;
+ ULONGLONG FileNameHash = CalcFileNameHash(szFileName);
+
+ if((pFileNode = FileTree.Find(FileNameHash)) != NULL)
+ {
+ if(pFileNode->FileNameHash == FileNameHash)
+ {
+ if(FileDataId != 0)
+ fprintf(fp, "%u;%s\n", FileDataId, szFileName);
+ else
+ fprintf(fp, "%s\n", szFileName);
+ }
+ }
+ }
+#else
+ #define VerifyAndLogFileName(szFileName, FileDataId) /* */
+#endif
+
// 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)
+ static LPBYTE CaptureRootHeader_50893(LPBYTE pbRootPtr, LPBYTE pbRootEnd, PROOT_FORMAT RootFormat, PDWORD FileCounterHashless, PDWORD Version)
{
FILE_ROOT_HEADER_50893 RootHeader;
@@ -132,7 +180,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
// Verify the root file header
if(RootHeader.Signature != CASC_WOW_ROOT_SIGNATURE)
return NULL;
- if(RootHeader.Version != 1)
+ if(RootHeader.Version != 1 && RootHeader.Version != 2)
return NULL;
if(RootHeader.FilesWithNameHash > RootHeader.TotalFiles)
return NULL;
@@ -142,11 +190,12 @@ struct TRootHandler_WoW : public TFileTreeRoot
*RootFormat = RootFormatWoW_v2;
*FileCounterHashless = RootHeader.TotalFiles - RootHeader.FilesWithNameHash;
+ *Version = RootHeader.Version;
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)
+ static LPBYTE CaptureRootHeader_30080(LPBYTE pbRootPtr, LPBYTE pbRootEnd, PROOT_FORMAT RootFormat, PDWORD FileCounterHashless, PDWORD Version)
{
FILE_ROOT_HEADER_30080 RootHeader;
@@ -163,11 +212,12 @@ struct TRootHandler_WoW : public TFileTreeRoot
*RootFormat = RootFormatWoW_v2;
*FileCounterHashless = RootHeader.TotalFiles - RootHeader.FilesWithNameHash;
+ *Version = 0;
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)
+ static LPBYTE CaptureRootHeader_18125(LPBYTE pbRootPtr, LPBYTE pbRootEnd, PROOT_FORMAT RootFormat, PDWORD FileCounterHashless, PDWORD Version)
{
size_t DataLength;
@@ -183,10 +233,11 @@ struct TRootHandler_WoW : public TFileTreeRoot
*RootFormat = RootFormatWoW_v1;
*FileCounterHashless = 0;
+ *Version = 0;
return pbRootPtr;
}
- static LPBYTE CaptureRootHeader(LPBYTE pbRootPtr, LPBYTE pbRootEnd, PROOT_FORMAT RootFormat, PDWORD FileCounterHashless)
+ static LPBYTE CaptureRootHeader(LPBYTE pbRootPtr, LPBYTE pbRootEnd, PROOT_FORMAT RootFormat, PDWORD FileCounterHashless, PDWORD Version)
{
CAPTURE_ROOT_HEADER PfnCaptureRootHeader[] =
{
@@ -199,7 +250,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
{
LPBYTE pbCapturedPtr;
- if((pbCapturedPtr = PfnCaptureRootHeader[i](pbRootPtr, pbRootEnd, RootFormat, FileCounterHashless)) != NULL)
+ if((pbCapturedPtr = PfnCaptureRootHeader[i](pbRootPtr, pbRootEnd, RootFormat, FileCounterHashless, Version)) != NULL)
{
return pbCapturedPtr;
}
@@ -207,16 +258,34 @@ struct TRootHandler_WoW : public TFileTreeRoot
return NULL;
}
- LPBYTE CaptureRootGroup(FILE_ROOT_GROUP & RootGroup, LPBYTE pbRootPtr, LPBYTE pbRootEnd)
+ LPBYTE CaptureRootGroup(FILE_ROOT_GROUP & RootGroup, LPBYTE pbRootPtr, LPBYTE pbRootEnd, DWORD dwRootVersion)
{
// 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);
+ if(dwRootVersion == 0 || dwRootVersion == 1)
+ {
+ // 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);
+ }
+ else if(dwRootVersion == 2)
+ {
+ PFILE_ROOT_GROUPHEADER_58221 pRootGroupHeader;
+
+ // Get pointer to the root group header
+ if((pbRootPtr + sizeof(FILE_ROOT_GROUPHEADER_58221)) >= pbRootEnd)
+ return NULL;
+ pRootGroupHeader = (PFILE_ROOT_GROUPHEADER_58221)pbRootPtr;
+ pbRootPtr = pbRootPtr + sizeof(FILE_ROOT_GROUPHEADER_58221);
+
+ // Convert to old ContentFlags for now...
+ RootGroup.Header.NumberOfFiles = pRootGroupHeader->NumberOfFiles;
+ RootGroup.Header.ContentFlags = pRootGroupHeader->ContentFlags1 | pRootGroupHeader->ContentFlags2 | (DWORD)(pRootGroupHeader->ContentFlags3 << 17);
+ RootGroup.Header.LocaleFlags = pRootGroupHeader->LocaleFlags;
+ }
// Validate the array of file data IDs
if((pbRootPtr + (sizeof(DWORD) * RootGroup.Header.NumberOfFiles)) >= pbRootEnd)
@@ -345,7 +414,8 @@ struct TRootHandler_WoW : public TFileTreeRoot
LPBYTE pbRootEnd,
DWORD dwLocaleMask,
BYTE bOverrideLowViolence,
- BYTE bAudioLocale)
+ BYTE bAudioLocale,
+ DWORD dwRootVersion)
{
FILE_ROOT_GROUP RootBlock;
@@ -360,7 +430,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
//OutputDebugStringA(szMessage);
// Validate the file locale block
- pbRootPtr = CaptureRootGroup(RootBlock, pbRootPtr, pbRootEnd);
+ pbRootPtr = CaptureRootGroup(RootBlock, pbRootPtr, pbRootEnd, dwRootVersion);
if(pbRootPtr == NULL)
return ERROR_BAD_FORMAT;
@@ -446,33 +516,34 @@ struct TRootHandler_WoW : public TFileTreeRoot
LPBYTE pbRootPtr,
LPBYTE pbRootEnd,
DWORD dwLocaleMask,
- BYTE bAudioLocale)
+ BYTE bAudioLocale,
+ DWORD dwRootVersion)
{
DWORD dwErrCode;
// Load the locale as-is
- dwErrCode = ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, dwLocaleMask, false, bAudioLocale);
+ dwErrCode = ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, dwLocaleMask, false, bAudioLocale, dwRootVersion);
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);
+ ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, CASC_LOCALE_ENUS, false, bAudioLocale, dwRootVersion);
if(dwLocaleMask == CASC_LOCALE_PTPT)
- ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, CASC_LOCALE_PTBR, false, bAudioLocale);
+ ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, CASC_LOCALE_PTBR, false, bAudioLocale, dwRootVersion);
return ERROR_SUCCESS;
}
// WoW.exe: 004146C7 (BuildManifest::Load)
- DWORD Load(TCascStorage * hs, LPBYTE pbRootPtr, LPBYTE pbRootEnd, DWORD dwLocaleMask)
+ DWORD Load(TCascStorage * hs, LPBYTE pbRootPtr, LPBYTE pbRootEnd, DWORD dwLocaleMask, DWORD dwRootVersion)
{
DWORD dwErrCode;
- dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 0);
+ dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 0, dwRootVersion);
if(dwErrCode == ERROR_SUCCESS)
- dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 1);
+ dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 1, dwRootVersion);
#ifdef CASCLIB_DEBUG
// Dump the array of the file data IDs
@@ -507,6 +578,9 @@ struct TRootHandler_WoW : public TFileTreeRoot
break;
}
+ // Try to verify the file name by hash
+ VerifyAndLogFileName(szFileName, FileDataId);
+
//
// Several files were renamed around WoW build 50893 (10.1.7). Example:
//
@@ -543,10 +617,11 @@ struct TRootHandler_WoW : public TFileTreeRoot
break;
}
- // Calculate the hash of the file name
- FileNameHash = CalcFileNameHash(szFileName);
+ // Try to verify the file name by hash
+ VerifyAndLogFileName(szFileName, 0);
- // Try to find the file node by file name hash
+ // Calculate the hash of the file name and lookup in tree
+ FileNameHash = CalcFileNameHash(szFileName);
pFileNode = FileTree.Find(FileNameHash);
if(pFileNode != NULL && pFileNode->NameLength == 0)
{
@@ -562,6 +637,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
}
ROOT_FORMAT RootFormat; // Root file format
+ FILE * fp; // Handle to the dump file
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
};
@@ -573,24 +649,34 @@ DWORD RootHandler_CreateWoW(TCascStorage * hs, CASC_BLOB & RootFile, DWORD dwLoc
{
TRootHandler_WoW * pRootHandler = NULL;
ROOT_FORMAT RootFormat = RootFormatWoW_v1;
+ LPCTSTR szDumpFile = NULL;
LPBYTE pbRootFile = RootFile.pbData;
LPBYTE pbRootEnd = RootFile.End();
LPBYTE pbRootPtr;
DWORD FileCounterHashless = 0;
+ DWORD RootVersion = 0;
DWORD dwErrCode = ERROR_BAD_FORMAT;
// Verify the root header
- if((pbRootPtr = TRootHandler_WoW::CaptureRootHeader(pbRootFile, pbRootEnd, &RootFormat, &FileCounterHashless)) == NULL)
+ if((pbRootPtr = TRootHandler_WoW::CaptureRootHeader(pbRootFile, pbRootEnd, &RootFormat, &FileCounterHashless, &RootVersion)) == NULL)
return ERROR_BAD_FORMAT;
- // Create the WOW handler
- pRootHandler = new TRootHandler_WoW(RootFormat, FileCounterHashless);
+#ifdef CASCLIB_WRITE_VERIFIED_FILENAMES
+ LPCTSTR szExtension = (RootFormat == RootFormatWoW_v1) ? _T("txt") : _T("csv");
+ TCHAR szBuffer[MAX_PATH];
+
+ CascStrPrintf(szBuffer, _countof(szBuffer), _T("\\listfile_wow_%u_%s.%s"), hs->dwBuildNumber, hs->szCodeName, szExtension);
+ szDumpFile = szBuffer;
+#endif
+
+ // Create the root handler
+ pRootHandler = new TRootHandler_WoW(RootFormat, FileCounterHashless, szDumpFile);
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);
+ dwErrCode = pRootHandler->Load(hs, pbRootPtr, pbRootEnd, dwLocaleMask, RootVersion);
if(dwErrCode != ERROR_SUCCESS)
{
delete pRootHandler;