mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Dep/CascLib: Update to ladislav-zezula/CascLib@b91f87c770
This commit is contained in:
@@ -9,8 +9,10 @@ set(HEADER_FILES
|
||||
src/common/Directory.h
|
||||
src/common/FileStream.h
|
||||
src/common/FileTree.h
|
||||
src/common/IndexMap.h
|
||||
src/common/ListFile.h
|
||||
src/common/Map.h
|
||||
src/common/Path.h
|
||||
src/common/RootHandler.h
|
||||
src/jenkins/lookup.h
|
||||
)
|
||||
@@ -24,7 +26,6 @@ set(SRC_FILES
|
||||
src/common/ListFile.cpp
|
||||
src/common/RootHandler.cpp
|
||||
src/jenkins/lookup3.c
|
||||
src/CascCommon.cpp
|
||||
src/CascDecompress.cpp
|
||||
src/CascDecrypt.cpp
|
||||
src/CascDumpData.cpp
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
/*****************************************************************************/
|
||||
/* CascCommon.cpp Copyright (c) Ladislav Zezula 2014 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Common functions for CascLib */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 29.04.14 1.00 Lad The first version of CascCommon.cpp */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __CASCLIB_SELF__
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions
|
||||
|
||||
LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD * pcbFileData)
|
||||
{
|
||||
LPBYTE pbFileData = NULL;
|
||||
HANDLE hFile = NULL;
|
||||
DWORD cbFileData = pcbFileData[0];
|
||||
DWORD dwBytesRead = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Open the file either by CKey or by EKey
|
||||
if(OpenFileByCKeyEntry(hs, pCKeyEntry, CASC_STRICT_DATA_CHECK, &hFile))
|
||||
{
|
||||
// Retrieve the size of the file. Note that the caller might specify
|
||||
// the real size of the file, in case the file size is not retrievable
|
||||
// or if the size is wrong. Example: ENCODING file has size specified in BUILD
|
||||
if(cbFileData == 0 || cbFileData == CASC_INVALID_SIZE)
|
||||
{
|
||||
cbFileData = CascGetFileSize(hFile, NULL);
|
||||
if(cbFileData == 0 || cbFileData == CASC_INVALID_SIZE)
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Retrieve the size of the ENCODING file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Allocate space for the ENCODING file
|
||||
pbFileData = CASC_ALLOC(BYTE, cbFileData);
|
||||
if(pbFileData != NULL)
|
||||
{
|
||||
// Read the entire file to memory
|
||||
CascReadFile(hFile, pbFileData, cbFileData, &dwBytesRead);
|
||||
if(dwBytesRead != cbFileData)
|
||||
{
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
// Close the file
|
||||
CascCloseFile(hFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
||||
// Handle errors
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
// Free the file data
|
||||
CASC_FREE(pbFileData);
|
||||
cbFileData = 0;
|
||||
|
||||
// Set the last error
|
||||
SetLastError(nError);
|
||||
}
|
||||
|
||||
// Give the loaded file length
|
||||
if(pcbFileData != NULL)
|
||||
*pcbFileData = cbFileData;
|
||||
return pbFileData;
|
||||
}
|
||||
|
||||
LPBYTE LoadFileToMemory(const TCHAR * szFileName, DWORD * pcbFileData)
|
||||
{
|
||||
TFileStream * pStream;
|
||||
ULONGLONG FileSize = 0;
|
||||
LPBYTE pbFileData = NULL;
|
||||
DWORD cbFileData = 0;
|
||||
|
||||
// Open the stream for read-only access and read the file
|
||||
// Note that this fails when the game is running (sharing violation).
|
||||
pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
|
||||
if(pStream != NULL)
|
||||
{
|
||||
// Retrieve the file size
|
||||
FileStream_GetSize(pStream, &FileSize);
|
||||
cbFileData = (DWORD)FileSize;
|
||||
|
||||
// Do not load zero files or too larget files
|
||||
if(0 < FileSize && FileSize <= 0x2000000)
|
||||
{
|
||||
// Allocate file data buffer. Make it 1 byte longer
|
||||
// so string functions can put '\0' there
|
||||
pbFileData = CASC_ALLOC(BYTE, cbFileData + 1);
|
||||
if(pbFileData != NULL)
|
||||
{
|
||||
if(!FileStream_Read(pStream, NULL, pbFileData, cbFileData))
|
||||
{
|
||||
CASC_FREE(pbFileData);
|
||||
cbFileData = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||
cbFileData = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLastError(ERROR_BAD_FORMAT);
|
||||
cbFileData = 0;
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Close the file stream
|
||||
FileStream_Close(pStream);
|
||||
}
|
||||
|
||||
// Give out values
|
||||
if(pcbFileData != NULL)
|
||||
pcbFileData[0] = cbFileData;
|
||||
return pbFileData;
|
||||
}
|
||||
|
||||
void FreeCascBlob(PQUERY_KEY pBlob)
|
||||
{
|
||||
if(pBlob != NULL)
|
||||
{
|
||||
CASC_FREE(pBlob->pbData);
|
||||
pBlob->cbData = 0;
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "common/Directory.h"
|
||||
#include "common/ListFile.h"
|
||||
#include "common/Csv.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/RootHandler.h"
|
||||
|
||||
// Headers from Alexander Peslyak's MD5 implementation
|
||||
@@ -52,6 +53,14 @@
|
||||
#define BREAKIF(condition) { /* NOTHING */ }
|
||||
#endif
|
||||
|
||||
#define CASC_MAGIC_STORAGE 0x524F545343534143 // 'CASCSTOR'
|
||||
#define CASC_MAGIC_FILE 0x454C494643534143 // 'CASCFILE'
|
||||
#define CASC_MAGIC_FIND 0x444E494643534143 // 'CASCFIND'
|
||||
|
||||
// For CASC_CDN_DOWNLOAD::Flags
|
||||
#define CASC_CDN_FLAG_PORT1119 0x0001 // Use port 1119
|
||||
#define CASC_CDN_FORCE_DOWNLOAD 0x0002 // Force downloading the file even if in the cache
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// In-memory structures
|
||||
|
||||
@@ -63,6 +72,13 @@ typedef enum _CBLD_TYPE
|
||||
CascVersionsDb // versions (downloaded online)
|
||||
} CBLD_TYPE, *PCBLD_TYPE;
|
||||
|
||||
typedef enum _CSTRTG
|
||||
{
|
||||
CascCacheInvalid, // Do not cache anything. Used as invalid value
|
||||
CascCacheNothing, // Do not cache anything. Used on internal files, where the content is loaded directly to the user buffer
|
||||
CascCacheLastFrame, // Only cache one file frame
|
||||
} CSTRTG, *PCSTRTG;
|
||||
|
||||
// Tag file entry, loaded from the DOWNLOAD file
|
||||
typedef struct _CASC_TAG_ENTRY
|
||||
{
|
||||
@@ -97,7 +113,7 @@ typedef struct _CASC_ARCINDEX_FOOTER
|
||||
BYTE Version; // Version of the index footer
|
||||
BYTE OffsetBytes; // Length, in bytes, of the file offset field
|
||||
BYTE SizeBytes; // Length, in bytes, of the file size field
|
||||
BYTE EKeyBytes; // Length, in bytes, of the EKey field
|
||||
BYTE EKeyLength; // Length, in bytes, of the EKey field
|
||||
BYTE FooterHashBytes; // Length, in bytes, of the hash and checksum
|
||||
BYTE Alignment[3];
|
||||
size_t ElementCount; // total number of elements in the index file
|
||||
@@ -107,15 +123,6 @@ typedef struct _CASC_ARCINDEX_FOOTER
|
||||
|
||||
} CASC_ARCINDEX_FOOTER, *PCASC_ARCINDEX_FOOTER;
|
||||
|
||||
// Normalized structure for archive index entry
|
||||
typedef struct _CASC_ARCINDEX_ENTRY
|
||||
{
|
||||
BYTE IndexHash[MD5_HASH_SIZE];
|
||||
BYTE EKey[MD5_HASH_SIZE];
|
||||
DWORD ArchiveOffset;
|
||||
DWORD EncodedSize;
|
||||
} CASC_ARCINDEX_ENTRY, *PCASC_ARCINDEX_ENTRY;
|
||||
|
||||
// Normalized header of the ENCODING file
|
||||
typedef struct _CASC_ENCODING_HEADER
|
||||
{
|
||||
@@ -188,12 +195,51 @@ typedef struct _CASC_INSTALL_HEADER
|
||||
typedef struct _CASC_FILE_FRAME
|
||||
{
|
||||
CONTENT_KEY FrameHash; // MD5 hash of the file frame
|
||||
ULONGLONG StartOffset; // Starting offset of the file span
|
||||
ULONGLONG EndOffset; // Ending offset of the file span
|
||||
DWORD DataFileOffset; // Offset in the data file (data.###)
|
||||
DWORD FileOffset; // File offset of this frame
|
||||
DWORD EncodedSize; // Encoded size of the frame
|
||||
DWORD ContentSize; // Content size of the frame
|
||||
} CASC_FILE_FRAME, *PCASC_FILE_FRAME;
|
||||
|
||||
typedef struct _CASC_FILE_SPAN
|
||||
{
|
||||
ULONGLONG StartOffset; // Starting offset of the file span
|
||||
ULONGLONG EndOffset; // Ending offset of the file span
|
||||
PCASC_FILE_FRAME pFrames;
|
||||
TFileStream * pStream; // [Opened] stream for the file span
|
||||
DWORD ArchiveIndex; // Index of the archive
|
||||
DWORD ArchiveOffs; // Offset in the archive
|
||||
DWORD HeaderSize; // Size of encoded frame headers
|
||||
DWORD FrameCount; // Number of frames in this span
|
||||
|
||||
} CASC_FILE_SPAN, *PCASC_FILE_SPAN;
|
||||
|
||||
// Structure for downloading a file from the CDN (https://wowdev.wiki/TACT#File_types)
|
||||
// Remote path is combined as the following:
|
||||
// [szCdnsHost] /[szCdnsPath]/[szPathType]/EKey[0-1]/EKey[2-3]/[EKey].[Extension]
|
||||
// level3.blizzard.com/tpr/bnt001 /data /fe /3d /fe3d7cf9d04e07066de32bd95a5c2627.index
|
||||
typedef struct _CASC_CDN_DOWNLOAD
|
||||
{
|
||||
ULONGLONG ArchiveOffs; // Archive offset (if pbArchiveKey != NULL)
|
||||
LPCTSTR szCdnsHost; // Address of the remote CDN server. ("level3.blizzard.com")
|
||||
// If NULL, the downloader will try all CDN servers from the storage
|
||||
LPCTSTR szCdnsPath; // Remote CDN path ("tpr/bnt001")
|
||||
LPCTSTR szPathType; // Path type ("config", "data", "patch")
|
||||
LPCTSTR szLoPaType; // Local path type ("config", "data", "patch"). If NULL, it's gonna be the same like szPathType, If "", then it's not used
|
||||
LPCTSTR szFileName; // Plain file name, without path and extension
|
||||
LPBYTE pbArchiveKey; // If non-NULL, then the file is present in the archive.
|
||||
LPBYTE pbEKey; // 16-byte EKey of the file of of the archive
|
||||
LPCTSTR szExtension; // Extension for the file. Can be NULL.
|
||||
|
||||
LPTSTR szLocalPath; // Pointer to the variable that, upon success, reveives the local path where the file was downloaded
|
||||
size_t ccLocalPath; // Maximum length of szLocalPath, in TCHARs
|
||||
DWORD ArchiveIndex; // Index of the archive (if pbArchiveKey != NULL)
|
||||
DWORD EncodedSize; // Encoded length (if pbArchiveKey != NULL)
|
||||
DWORD Flags; // See CASC_CDN_FLAG_XXX
|
||||
|
||||
} CASC_CDN_DOWNLOAD, *PCASC_CDN_DOWNLOAD;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structures for CASC storage and CASC file
|
||||
|
||||
@@ -201,16 +247,26 @@ struct TCascStorage
|
||||
{
|
||||
TCascStorage();
|
||||
~TCascStorage();
|
||||
|
||||
TCascStorage * AddRef();
|
||||
TCascStorage * Release();
|
||||
|
||||
static TCascStorage * IsValid(HANDLE hStorage);
|
||||
static TCascStorage * IsValid(HANDLE hStorage)
|
||||
{
|
||||
TCascStorage * hs = (TCascStorage *)hStorage;
|
||||
|
||||
return (hs != INVALID_HANDLE_VALUE &&
|
||||
hs != NULL &&
|
||||
hs->ClassName == CASC_MAGIC_STORAGE) ? hs : NULL;
|
||||
}
|
||||
|
||||
// Class recognizer. Has constant value of 'CASCSTOR' (CASC_MAGIC_STORAGE)
|
||||
ULONGLONG ClassName;
|
||||
|
||||
// Class members
|
||||
PCASC_OPEN_STORAGE_ARGS pArgs; // Open storage arguments. Only valid during opening the storage
|
||||
|
||||
LPCTSTR szIndexFormat; // Format of the index file name
|
||||
LPCSTR szClassName; // "TCascStorage"
|
||||
LPCSTR szProductName; // String representation of the product name
|
||||
LPTSTR szCodeName; // On local storage, this select a product in a multi-product storage. For online storage, this selects a product
|
||||
LPTSTR szRootPath; // Path where the build file is
|
||||
LPTSTR szDataPath; // This is the directory where data files are
|
||||
@@ -219,12 +275,10 @@ struct TCascStorage
|
||||
LPTSTR szCdnServers; // Multi-SZ list of CDN servers
|
||||
LPTSTR szCdnPath; // Remote CDN sub path for the product
|
||||
LPSTR szRegion; // Product region. Only when "versions" is used as storage root file
|
||||
CASC_PRODUCT Product; // Product enum value (see CASC_PRODUCT)
|
||||
DWORD dwDefaultLocale; // Default locale, read from ".build.info"
|
||||
DWORD dwBuildNumber; // Product build number
|
||||
DWORD dwRefCount; // Number of references
|
||||
DWORD dwDefaultLocale; // Default locale, read from ".build.info"
|
||||
DWORD dwFeatures; // List of CASC features. See CASC_FEATURE_XXX
|
||||
bool bAllowOrphans; // If TRUE, then orphaned items are allowed
|
||||
|
||||
CBLD_TYPE BuildFileType; // Type of the build file
|
||||
|
||||
@@ -249,20 +303,17 @@ struct TCascStorage
|
||||
CASC_ARRAY VfsRootList; // List of CASC_EKEY_ENTRY for each TVFS sub-root
|
||||
|
||||
TRootHandler * pRootHandler; // Common handler for various ROOT file formats
|
||||
CASC_ARRAY ArcIndexArray; // Array of CASC_ARCINDEX_ENTRY, loaded from archive indexes
|
||||
CASC_ARRAY IndexArray; // Array of CASC_CKEY_ENTRY loaded from local index files
|
||||
CASC_ARRAY CKeyArray; // Array of CASC_CKEY_ENTRY, one entry for each physical file
|
||||
CASC_ARRAY IndexArray; // Array of CASC_EKEY_ENTRY, loaded from online indexes
|
||||
CASC_ARRAY CKeyArray; // Array of CASC_CKEY_ENTRY, loaded from ENCODING file
|
||||
CASC_ARRAY TagsArray; // Array of CASC_DOWNLOAD_TAG2
|
||||
CASC_MAP ArcIndexMap; // Map of EKey -> CASC_ARCINDEX_ENTRY
|
||||
CASC_MAP CKeyMap; // Map of CKey -> CASC_CKEY_ENTRY
|
||||
CASC_MAP EKeyMap; // Map of EKey -> CASC_EKEY_ENTRY
|
||||
CASC_MAP IndexMap; // Map of EKey -> IndexArray (for online archives)
|
||||
CASC_MAP CKeyMap; // Map of CKey -> CKeyArray
|
||||
CASC_MAP EKeyMap; // Map of EKey -> CKeyArray
|
||||
size_t LocalFiles; // Number of files that are present locally
|
||||
size_t TotalFiles; // Total number of files in the storage, some may not be present locally
|
||||
size_t EKeyEntries; // Number of CKeyEntry-ies loaded from text build file
|
||||
size_t OrphanItems; // Number of EKey entries in indexes that do not have their counterpart in ENCODING
|
||||
size_t SkippedItems; // Number of EKey entries in indexes that were ignored due to insufficient capacity of CKeyArray
|
||||
size_t EKeyLength; // EKey length from the index files
|
||||
DWORD FileOffsetBits; // Nimber of bits in the storage offset which mean data segent offset
|
||||
DWORD FileOffsetBits; // Number of bits in the storage offset which mean data segent offset
|
||||
|
||||
CASC_ARRAY ExtraKeysList; // List additional encryption keys
|
||||
CASC_MAP EncryptionKeys; // Map of encryption keys
|
||||
@@ -271,49 +322,12 @@ struct TCascStorage
|
||||
|
||||
struct TCascFile
|
||||
{
|
||||
TCascFile(TCascStorage * ahs, PCASC_CKEY_ENTRY apCKeyEntry)
|
||||
{
|
||||
ULONGLONG StorageOffset;
|
||||
ULONGLONG FileOffsMask;
|
||||
TCascFile(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry);
|
||||
~TCascFile();
|
||||
|
||||
// Reference the storage handle
|
||||
hs = ahs->AddRef();
|
||||
szClassName = "TCascFile";
|
||||
|
||||
// Init class variables
|
||||
pCKeyEntry = apCKeyEntry;
|
||||
pFrames = NULL;
|
||||
pbFileCache = NULL;
|
||||
cbFileCache = 0;
|
||||
|
||||
// Supply the sizes
|
||||
StorageOffset = pCKeyEntry->StorageOffset;
|
||||
FileOffsMask = ((ULONGLONG)1 << hs->FileOffsetBits) - 1;
|
||||
ArchiveIndex = (DWORD)(StorageOffset >> hs->FileOffsetBits);
|
||||
ArchiveOffset = (DWORD)(StorageOffset & FileOffsMask);
|
||||
EncodedSize = pCKeyEntry->EncodedSize;
|
||||
ContentSize = pCKeyEntry->ContentSize;
|
||||
FilePointer = 0;
|
||||
FrameCount = 0;
|
||||
bVerifyIntegrity = false;
|
||||
bDownloadFileIf = false;
|
||||
bLocalFileStream = false;
|
||||
}
|
||||
|
||||
~TCascFile()
|
||||
{
|
||||
// Close (dereference) the archive handle
|
||||
hs = hs->Release();
|
||||
szClassName = NULL;
|
||||
|
||||
// Free the file cache and frame array
|
||||
CASC_FREE(pbFileCache);
|
||||
CASC_FREE(pFrames);
|
||||
|
||||
// If we are supposed to close the file stream, do it
|
||||
if (pStream && bLocalFileStream)
|
||||
FileStream_Close(pStream);
|
||||
}
|
||||
DWORD OpenFileSpans(LPCTSTR szSpanList);
|
||||
void InitFileSpans(PCASC_FILE_SPAN pSpans, DWORD dwSpanCount);
|
||||
void InitCacheStrategy();
|
||||
|
||||
static TCascFile * IsValid(HANDLE hFile)
|
||||
{
|
||||
@@ -321,31 +335,32 @@ struct TCascFile
|
||||
|
||||
return (hf != INVALID_HANDLE_VALUE &&
|
||||
hf != NULL &&
|
||||
hf->hs != NULL &&
|
||||
hf->szClassName != NULL &&
|
||||
hf->ClassName == CASC_MAGIC_FILE &&
|
||||
hf->pCKeyEntry != NULL) ? hf : NULL;
|
||||
}
|
||||
|
||||
TCascStorage * hs; // Pointer to storage structure
|
||||
TFileStream * pStream; // An open data stream
|
||||
const char * szClassName; // "TCascFile"
|
||||
// Class recognizer. Has constant value of 'CASCFILE' (CASC_MAGIC_FILE)
|
||||
ULONGLONG ClassName;
|
||||
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
PCASC_FILE_FRAME pFrames; // Array of file frames
|
||||
DWORD ArchiveIndex; // Index of the archive (data.###)
|
||||
DWORD ArchiveOffset; // Offset in the archive (data.###)
|
||||
DWORD FilePointer; // Current file pointer
|
||||
DWORD EncodedSize; // Encoded size. This is the size of encoded header, all file frame headers and all file frames
|
||||
DWORD ContentSize; // Content size. This is the size of the file content, aka the file size
|
||||
DWORD FrameCount; // Number of the file frames
|
||||
// Class members
|
||||
TCascStorage * hs; // Pointer to storage structure
|
||||
|
||||
PCASC_CKEY_ENTRY pCKeyEntry; // Pointer to the first CKey entry. Each entry describes one file span
|
||||
PCASC_FILE_SPAN pFileSpan; // Pointer to the first file span entry
|
||||
ULONGLONG ContentSize; // Content size. This is the summed content size of all file spans
|
||||
ULONGLONG EncodedSize; // Encoded size. This is the summed encoded size of all file spans
|
||||
ULONGLONG FilePointer; // Current file pointer
|
||||
DWORD SpanCount; // Number of file spans. There is one CKey entry for each file span
|
||||
DWORD bVerifyIntegrity:1; // If true, then the data are validated more strictly when read
|
||||
DWORD bDownloadFileIf:1; // If true, then the data will be downloaded from the online storage if missing
|
||||
DWORD bLocalFileStream:1; // If true, then the file stream is a local file
|
||||
DWORD bCloseFileStream:1; // If true, file stream needs to be closed during CascCloseFile
|
||||
DWORD bOvercomeEncrypted:1; // If true, then CascReadFile will fill the part that is encrypted (and key was not found) with zeros
|
||||
DWORD bFreeCKeyEntries:1; // If true, dectructor will free the array of CKey entries
|
||||
|
||||
LPBYTE pbFileCache; // Pointer to file cache
|
||||
DWORD cbFileCache; // Size of the file cache
|
||||
|
||||
ULONGLONG FileCacheStart; // Starting offset of the file cached area
|
||||
ULONGLONG FileCacheEnd; // Ending offset of the file cached area
|
||||
LPBYTE pbFileCache; // Pointer to file cached area
|
||||
CSTRTG CacheStrategy; // Caching strategy. See CSTRTG enum for more info
|
||||
};
|
||||
|
||||
struct TCascSearch
|
||||
@@ -353,7 +368,7 @@ struct TCascSearch
|
||||
TCascSearch(TCascStorage * ahs, LPCTSTR aszListFile, const char * aszMask)
|
||||
{
|
||||
// Init the class
|
||||
szClassName = "TCascSearch";
|
||||
ClassName = CASC_MAGIC_FIND;
|
||||
hs = ahs->AddRef();
|
||||
|
||||
// Init provider-specific data
|
||||
@@ -371,7 +386,7 @@ struct TCascSearch
|
||||
{
|
||||
// Dereference the CASC storage
|
||||
hs = hs->Release();
|
||||
szClassName = NULL;
|
||||
ClassName = 0;
|
||||
|
||||
// Free the rest of the members
|
||||
CASC_FREE(szMask);
|
||||
@@ -385,14 +400,14 @@ struct TCascSearch
|
||||
|
||||
return (hFind != INVALID_HANDLE_VALUE &&
|
||||
hFind != NULL &&
|
||||
pSearch->szClassName != NULL &&
|
||||
!strcmp(pSearch->szClassName, "TCascSearch") &&
|
||||
pSearch->ClassName == CASC_MAGIC_FIND &&
|
||||
pSearch->szMask != NULL) ? pSearch : NULL;
|
||||
}
|
||||
|
||||
ULONGLONG ClassName; // Contains 'CASCFIND'
|
||||
|
||||
TCascStorage * hs; // Pointer to the storage handle
|
||||
const char * szClassName; // Contains "TCascSearch"
|
||||
TCHAR * szListFile; // Name of the listfile
|
||||
LPTSTR szListFile; // Name of the listfile
|
||||
void * pCache; // Listfile cache
|
||||
char * szMask; // Search mask
|
||||
|
||||
@@ -405,22 +420,32 @@ struct TCascSearch
|
||||
//-----------------------------------------------------------------------------
|
||||
// Common functions (CascCommon.cpp)
|
||||
|
||||
bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD dwOpenFlags, HANDLE * PtrFileHandle);
|
||||
LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD * pcbFileData);
|
||||
LPBYTE LoadFileToMemory(const TCHAR * szFileName, DWORD * pcbFileData);
|
||||
void FreeCascBlob(PQUERY_KEY pQueryKey);
|
||||
inline void FreeCascBlob(PQUERY_KEY pBlob)
|
||||
{
|
||||
if(pBlob != NULL)
|
||||
{
|
||||
CASC_FREE(pBlob->pbData);
|
||||
pBlob->cbData = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Text file parsing (CascFiles.cpp)
|
||||
|
||||
bool InvokeProgressCallback(TCascStorage * hs, LPCSTR szMessage, LPCSTR szObject, DWORD CurrentValue, DWORD TotalValue);
|
||||
DWORD DownloadFileFromCDN(TCascStorage * hs, const TCHAR * szSubDir, LPBYTE pbEKey, const TCHAR * szExtension, TCHAR * szOutLocalPath, size_t cchOutLocalPath);
|
||||
DWORD CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory);
|
||||
DWORD LoadCdnsInfo(TCascStorage * hs);
|
||||
DWORD GetFileSpanInfo(PCASC_CKEY_ENTRY pCKeyEntry, PULONGLONG PtrContentSize, PULONGLONG PtrEncodedSize = NULL);
|
||||
DWORD DownloadFileFromCDN(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInfo);
|
||||
DWORD CheckGameDirectory(TCascStorage * hs, LPTSTR szDirectory);
|
||||
DWORD LoadCdnsFile(TCascStorage * hs);
|
||||
DWORD LoadBuildInfo(TCascStorage * hs);
|
||||
DWORD LoadCdnConfigFile(TCascStorage * hs);
|
||||
DWORD LoadCdnBuildFile(TCascStorage * hs);
|
||||
|
||||
LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD * pcbFileData);
|
||||
LPBYTE LoadFileToMemory(LPCTSTR szFileName, DWORD * pcbFileData);
|
||||
bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD dwOpenFlags, HANDLE * PtrFileHandle);
|
||||
bool SetCacheStrategy(HANDLE hFile, CSTRTG CacheStrategy);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Internal file functions
|
||||
|
||||
@@ -431,27 +456,27 @@ PCASC_CKEY_ENTRY FindCKeyEntry_EKey(TCascStorage * hs, LPBYTE pbEKey, PDWORD Ptr
|
||||
|
||||
size_t GetTagBitmapLength(LPBYTE pbFilePtr, LPBYTE pbFileEnd, DWORD EntryCount);
|
||||
|
||||
int CascDecompress(LPBYTE pvOutBuffer, PDWORD pcbOutBuffer, LPBYTE pvInBuffer, DWORD cbInBuffer);
|
||||
int CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer);
|
||||
DWORD CascDecompress(LPBYTE pvOutBuffer, PDWORD pcbOutBuffer, LPBYTE pvInBuffer, DWORD cbInBuffer);
|
||||
DWORD CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer);
|
||||
|
||||
int CascLoadEncryptionKeys(TCascStorage * hs);
|
||||
int CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex);
|
||||
DWORD CascLoadEncryptionKeys(TCascStorage * hs);
|
||||
DWORD CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Support for index files
|
||||
|
||||
int LoadIndexFiles(TCascStorage * hs);
|
||||
DWORD LoadIndexFiles(TCascStorage * hs);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Support for ROOT file
|
||||
|
||||
int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
int RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
int RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask);
|
||||
int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
int RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
int RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
DWORD RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
DWORD RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
DWORD RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
DWORD RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask);
|
||||
DWORD RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
DWORD RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
DWORD RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Dumpers (CascDumpData.cpp)
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int CascDecompress(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer)
|
||||
DWORD CascDecompress(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer)
|
||||
{
|
||||
z_stream z; // Stream information for zlib
|
||||
DWORD dwErrCode = ERROR_FILE_CORRUPT;
|
||||
uInt cbOutBuffer = *pcbOutBuffer;
|
||||
int nResult;
|
||||
int nError = ERROR_FILE_CORRUPT;
|
||||
|
||||
// Fill the stream structure for zlib
|
||||
z.next_in = pbInBuffer;
|
||||
@@ -44,7 +44,7 @@ int CascDecompress(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, D
|
||||
{
|
||||
// Give the size of the uncompressed data
|
||||
cbOutBuffer = z.total_out;
|
||||
nError = ERROR_SUCCESS;
|
||||
dwErrCode = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
inflateEnd(&z);
|
||||
@@ -52,5 +52,5 @@ int CascDecompress(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, D
|
||||
|
||||
// Give the caller the number of bytes needed
|
||||
pcbOutBuffer[0] = cbOutBuffer;
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
@@ -16,16 +16,17 @@
|
||||
// Local structures
|
||||
|
||||
#define CASC_EXTRA_KEYS 0x80
|
||||
#define CASC_KEY_LENGTH 0x10
|
||||
|
||||
typedef struct _CASC_ENCRYPTION_KEY
|
||||
{
|
||||
ULONGLONG KeyName; // "Name" of the key
|
||||
BYTE Key[0x10]; // The key itself
|
||||
BYTE Key[CASC_KEY_LENGTH]; // The key itself
|
||||
} CASC_ENCRYPTION_KEY, *PCASC_ENCRYPTION_KEY;
|
||||
|
||||
typedef struct _CASC_SALSA20
|
||||
{
|
||||
DWORD Key[0x10];
|
||||
DWORD Key[CASC_KEY_LENGTH];
|
||||
DWORD dwRounds;
|
||||
|
||||
} CASC_SALSA20, *PCASC_SALSA20;
|
||||
@@ -57,15 +58,15 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
|
||||
{ 0xDEE3A0521EFF6F03ULL, { 0xAD, 0x74, 0x0C, 0xE3, 0xFF, 0xFF, 0x92, 0x31, 0x46, 0x81, 0x26, 0x98, 0x57, 0x08, 0xE1, 0xB9 } }, // 0.8.0.24919_retailx64 (streamed from server)
|
||||
{ 0x8C9106108AA84F07ULL, { 0x53, 0xD8, 0x59, 0xDD, 0xA2, 0x63, 0x5A, 0x38, 0xDC, 0x32, 0xE7, 0x2B, 0x11, 0xB3, 0x2F, 0x29 } }, // 0.8.0.24919_retailx64 (streamed from server)
|
||||
{ 0x49166D358A34D815ULL, { 0x66, 0x78, 0x68, 0xCD, 0x94, 0xEA, 0x01, 0x35, 0xB9, 0xB1, 0x6C, 0x93, 0xB1, 0x12, 0x4A, 0xBA } }, // 0.8.0.24919_retailx64 (streamed from server)
|
||||
{ 0x1463A87356778D14ULL, { 0x69, 0xBD, 0x2A, 0x78, 0xD0, 0x5C, 0x50, 0x3E, 0x93, 0x99, 0x49, 0x59, 0xB3, 0x0E, 0x5A, 0xEC } }, // ? 1.0.3.0 (streamed from server)
|
||||
{ 0x5E152DE44DFBEE01ULL, { 0xE4, 0x5A, 0x17, 0x93, 0xB3, 0x7E, 0xE3, 0x1A, 0x8E, 0xB8, 0x5C, 0xEE, 0x0E, 0xEE, 0x1B, 0x68 } }, // ? 1.0.3.0 (streamed from server)
|
||||
{ 0x1463A87356778D14ULL, { 0x69, 0xBD, 0x2A, 0x78, 0xD0, 0x5C, 0x50, 0x3E, 0x93, 0x99, 0x49, 0x59, 0xB3, 0x0E, 0x5A, 0xEC } }, // ? 1.0.3.0 (streamed from server)
|
||||
{ 0x5E152DE44DFBEE01ULL, { 0xE4, 0x5A, 0x17, 0x93, 0xB3, 0x7E, 0xE3, 0x1A, 0x8E, 0xB8, 0x5C, 0xEE, 0x0E, 0xEE, 0x1B, 0x68 } }, // ? 1.0.3.0 (streamed from server)
|
||||
{ 0x9B1F39EE592CA415ULL, { 0x54, 0xA9, 0x9F, 0x08, 0x1C, 0xAD, 0x0D, 0x08, 0xF7, 0xE3, 0x36, 0xF4, 0x36, 0x8E, 0x89, 0x4C } }, // ? 1.0.3.0 (streamed from server)
|
||||
{ 0x24C8B75890AD5917ULL, { 0x31, 0x10, 0x0C, 0x00, 0xFD, 0xE0, 0xCE, 0x18, 0xBB, 0xB3, 0x3F, 0x3A, 0xC1, 0x5B, 0x30, 0x9F } }, // ? 1.0.3.0 (included in game)
|
||||
{ 0xEA658B75FDD4890FULL, { 0xDE, 0xC7, 0xA4, 0xE7, 0x21, 0xF4, 0x25, 0xD1, 0x33, 0x03, 0x98, 0x95, 0xC3, 0x60, 0x36, 0xF8 } }, // ? 1.0.3.0 (included in game)
|
||||
{ 0x026FDCDF8C5C7105ULL, { 0x8F, 0x41, 0x80, 0x9D, 0xA5, 0x53, 0x66, 0xAD, 0x41, 0x6D, 0x3C, 0x33, 0x74, 0x59, 0xEE, 0xE3 } }, // (included in game)
|
||||
{ 0xCAE3FAC925F20402ULL, { 0x98, 0xB7, 0x8E, 0x87, 0x74, 0xBF, 0x27, 0x50, 0x93, 0xCB, 0x1B, 0x5F, 0xC7, 0x14, 0x51, 0x1B } }, // (included in game)
|
||||
{ 0x061581CA8496C80CULL, { 0xDA, 0x2E, 0xF5, 0x05, 0x2D, 0xB9, 0x17, 0x38, 0x0B, 0x8A, 0xA6, 0xEF, 0x7A, 0x5F, 0x8E, 0x6A } }, //
|
||||
{ 0xBE2CB0FAD3698123ULL, { 0x90, 0x2A, 0x12, 0x85, 0x83, 0x6C, 0xE6, 0xDA, 0x58, 0x95, 0x02, 0x0D, 0xD6, 0x03, 0xB0, 0x65 } }, //
|
||||
{ 0x061581CA8496C80CULL, { 0xDA, 0x2E, 0xF5, 0x05, 0x2D, 0xB9, 0x17, 0x38, 0x0B, 0x8A, 0xA6, 0xEF, 0x7A, 0x5F, 0x8E, 0x6A } }, //
|
||||
{ 0xBE2CB0FAD3698123ULL, { 0x90, 0x2A, 0x12, 0x85, 0x83, 0x6C, 0xE6, 0xDA, 0x58, 0x95, 0x02, 0x0D, 0xD6, 0x03, 0xB0, 0x65 } }, //
|
||||
{ 0x57A5A33B226B8E0AULL, { 0xFD, 0xFC, 0x35, 0xC9, 0x9B, 0x9D, 0xB1, 0x1A, 0x32, 0x62, 0x60, 0xCA, 0x24, 0x6A, 0xCB, 0x41 } }, // 1.1.0.0.30200 Ana
|
||||
{ 0x42B9AB1AF5015920ULL, { 0xC6, 0x87, 0x78, 0x82, 0x3C, 0x96, 0x4C, 0x6F, 0x24, 0x7A, 0xCC, 0x0F, 0x4A, 0x25, 0x84, 0xF8 } }, // 1.2.0.1.30684 Summer Games
|
||||
{ 0x4F0FE18E9FA1AC1AULL, { 0x89, 0x38, 0x1C, 0x74, 0x8F, 0x65, 0x31, 0xBB, 0xFC, 0xD9, 0x77, 0x53, 0xD0, 0x6C, 0xC3, 0xCD } }, // 1.2.0.1.30684
|
||||
@@ -78,7 +79,7 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
|
||||
{ 0x90CA73B2CDE3164BULL, { 0x5C, 0xBF, 0xF1, 0x1F, 0x22, 0x72, 0x0B, 0xAC, 0xC2, 0xAE, 0x6A, 0xAD, 0x8F, 0xE5, 0x33, 0x17 } }, // 1.6.1.0.33236 Oasis map
|
||||
{ 0x6DD3212FB942714AULL, { 0xE0, 0x2C, 0x16, 0x43, 0x60, 0x2E, 0xC1, 0x6C, 0x3A, 0xE2, 0xA4, 0xD2, 0x54, 0xA0, 0x8F, 0xD9 } }, // 1.6.1.0.33236
|
||||
{ 0x11DDB470ABCBA130ULL, { 0x66, 0x19, 0x87, 0x66, 0xB1, 0xC4, 0xAF, 0x75, 0x89, 0xEF, 0xD1, 0x3A, 0xD4, 0xDD, 0x66, 0x7A } }, // 1.6.1.0.33236 Winter Wonderland
|
||||
{ 0x5BEF27EEE95E0B4BULL, { 0x36, 0xBC, 0xD2, 0xB5, 0x51, 0xFF, 0x1C, 0x84, 0xAA, 0x3A, 0x39, 0x94, 0xCC, 0xEB, 0x03, 0x3E } }, //
|
||||
{ 0x5BEF27EEE95E0B4BULL, { 0x36, 0xBC, 0xD2, 0xB5, 0x51, 0xFF, 0x1C, 0x84, 0xAA, 0x3A, 0x39, 0x94, 0xCC, 0xEB, 0x03, 0x3E } }, //
|
||||
{ 0x9359B46E49D2DA42ULL, { 0x17, 0x3D, 0x65, 0xE7, 0xFC, 0xAE, 0x29, 0x8A, 0x93, 0x63, 0xBD, 0x6A, 0xA1, 0x89, 0xF2, 0x00 } }, // Diablo's 20th anniversary
|
||||
{ 0x1A46302EF8896F34ULL, { 0x80, 0x29, 0xAD, 0x54, 0x51, 0xD4, 0xBC, 0x18, 0xE9, 0xD0, 0xF5, 0xAC, 0x44, 0x9D, 0xC0, 0x55 } }, // 1.7.0.2.34156 Year of the Rooster
|
||||
{ 0x693529F7D40A064CULL, { 0xCE, 0x54, 0x87, 0x3C, 0x62, 0xDA, 0xA4, 0x8E, 0xFF, 0x27, 0xFC, 0xC0, 0x32, 0xBD, 0x07, 0xE3 } }, // 1.8.0.0.34470 CTF Maps
|
||||
@@ -86,8 +87,8 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
|
||||
{ 0xE218F69AAC6C104DULL, { 0xF4, 0x3D, 0x12, 0xC9, 0x4A, 0x9A, 0x52, 0x84, 0x97, 0x97, 0x1F, 0x1C, 0xBE, 0x41, 0xAD, 0x4D } }, // 1.9.0.0.34986 Orisa
|
||||
{ 0xF432F0425363F250ULL, { 0xBA, 0x69, 0xF2, 0xB3, 0x3C, 0x27, 0x68, 0xF5, 0xF2, 0x9B, 0xFE, 0x78, 0xA5, 0xA1, 0xFA, 0xD5 } }, // 1.10.0.0.35455 Uprising
|
||||
{ 0x061D52F86830B35DULL, { 0xD7, 0x79, 0xF9, 0xC6, 0xCC, 0x9A, 0x4B, 0xE1, 0x03, 0xA4, 0xE9, 0x0A, 0x73, 0x38, 0xF7, 0x93 } }, // 1.10.0.0.35455 D.Va Officer Skin (HotS Nexus Challenge 2)
|
||||
{ 0x1275C84CF113EF65ULL, { 0xCF, 0x58, 0xB6, 0x93, 0x3E, 0xAF, 0x98, 0xAF, 0x53, 0xE7, 0x6F, 0x84, 0x26, 0xCC, 0x7E, 0x6C } }, //
|
||||
{ 0xD9C7C7AC0F14C868ULL, { 0x3A, 0xFD, 0xF6, 0x8E, 0x3A, 0x5D, 0x63, 0xBA, 0xBA, 0x1E, 0x68, 0x21, 0x88, 0x3F, 0x06, 0x7D } }, //
|
||||
{ 0x1275C84CF113EF65ULL, { 0xCF, 0x58, 0xB6, 0x93, 0x3E, 0xAF, 0x98, 0xAF, 0x53, 0xE7, 0x6F, 0x84, 0x26, 0xCC, 0x7E, 0x6C } }, //
|
||||
{ 0xD9C7C7AC0F14C868ULL, { 0x3A, 0xFD, 0xF6, 0x8E, 0x3A, 0x5D, 0x63, 0xBA, 0xBA, 0x1E, 0x68, 0x21, 0x88, 0x3F, 0x06, 0x7D } }, //
|
||||
{ 0xBD4E42661A432951ULL, { 0x6D, 0xE8, 0xE2, 0x8C, 0x85, 0x11, 0x64, 0x4D, 0x55, 0x95, 0xFC, 0x45, 0xE5, 0x35, 0x14, 0x72 } }, // 1.11.0.0.36376 Anniversary event
|
||||
{ 0xC43CB14355249451ULL, { 0x0E, 0xA2, 0xB4, 0x4F, 0x96, 0xA2, 0x69, 0xA3, 0x86, 0x85, 0x6D, 0x04, 0x9A, 0x3D, 0xEC, 0x86 } }, // 1.12.0.0.37104 Horizon Lunar Colony
|
||||
{ 0xE6D914F8E4744953ULL, { 0xC8, 0x47, 0x7C, 0x28, 0x9D, 0xCE, 0x66, 0xD9, 0x13, 0x65, 0x07, 0xA3, 0x3A, 0xA3, 0x33, 0x01 } }, // 1.13.0.0.37646 Doomfist
|
||||
@@ -132,12 +133,12 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
|
||||
{ 0xFEBBF66DAEF6C9BEULL, { 0x4A, 0x22, 0x0A, 0xE3, 0xA6, 0x80, 0x8E, 0xD2, 0x69, 0x74, 0x10, 0xC9, 0x4C, 0x1C, 0xE9, 0x70 } }, // 1.30.0.1.????? Ashe
|
||||
{ 0x2AD4834DAB3986ABULL, { 0xEA, 0x69, 0x71, 0xF7, 0x8A, 0xBE, 0xBD, 0x2A, 0xF8, 0x83, 0xD4, 0x6A, 0x54, 0x86, 0xF3, 0x9F } }, // 1.31.?.?.????? Winter 2018
|
||||
{ 0xD89B24D62F00A04EULL, { 0xC3, 0xA4, 0xD0, 0x10, 0xAA, 0xA7, 0x28, 0x7A, 0x3E, 0xAC, 0xCD, 0x45, 0xED, 0x47, 0x13, 0x45 } }, // 1.31.?.?.????? Bastet Challenge
|
||||
{ 0x32DDC40236DAEA7BULL, { 0x2D, 0xBD, 0xE4, 0xFB, 0x9F, 0xDA, 0x77, 0x6A, 0xA2, 0x94, 0x87, 0x08, 0x54, 0xFE, 0x9B, 0x02 } }, // 1.31.?.?.????? Lunar New Year 2019 (Pig)
|
||||
{ 0xF481EFC2302EE415ULL, { 0x37, 0xA7, 0xF2, 0xB8, 0x7D, 0x0B, 0x8B, 0x70, 0x04, 0x56, 0xC6, 0xA9, 0x2C, 0x71, 0xD6, 0xDD } }, // 1.33.?.?.????? Paris Map
|
||||
{ 0xD1AC8C1903524D9AULL, { 0xD7, 0x81, 0xD0, 0xAA, 0x35, 0xE5, 0xC1, 0x06, 0xBC, 0xA7, 0xCF, 0x01, 0xDE, 0xBD, 0x14, 0x94 } }, // 1.34.0.1.55918 Baptiste
|
||||
{ 0x32DDC40236DAEA7BULL, { 0x2D, 0xBD, 0xE4, 0xFB, 0x9F, 0xDA, 0x77, 0x6A, 0xA2, 0x94, 0x87, 0x08, 0x54, 0xFE, 0x9B, 0x02 } }, // 1.31.?.?.????? Lunar New Year 2019 (Pig)
|
||||
{ 0xF481EFC2302EE415ULL, { 0x37, 0xA7, 0xF2, 0xB8, 0x7D, 0x0B, 0x8B, 0x70, 0x04, 0x56, 0xC6, 0xA9, 0x2C, 0x71, 0xD6, 0xDD } }, // 1.33.?.?.????? Paris Map
|
||||
{ 0xD1AC8C1903524D9AULL, { 0xD7, 0x81, 0xD0, 0xAA, 0x35, 0xE5, 0xC1, 0x06, 0xBC, 0xA7, 0xCF, 0x01, 0xDE, 0xBD, 0x14, 0x94 } }, // 1.34.0.1.55918 Baptiste
|
||||
{ 0x71EEBE93590AA903ULL, { 0x0C, 0xCD, 0x10, 0xD4, 0x55, 0x3E, 0xEC, 0x7E, 0x97, 0xFD, 0x36, 0xA9, 0xE8, 0xAD, 0xF0, 0xFF } }, // 1.34.0.1.55918 Baptiste Cosmetics
|
||||
|
||||
// Streamed WoW keys
|
||||
// Streamed WoW keys
|
||||
{ 0xFA505078126ACB3EULL, { 0xBD, 0xC5, 0x18, 0x62, 0xAB, 0xED, 0x79, 0xB2, 0xDE, 0x48, 0xC8, 0xE7, 0xE6, 0x6C, 0x62, 0x00 } }, // 15 WOW-20740patch7.0.1_Beta <not used between 7.0 and 7.3>
|
||||
{ 0xFF813F7D062AC0BCULL, { 0xAA, 0x0B, 0x5C, 0x77, 0xF0, 0x88, 0xCC, 0xC2, 0xD3, 0x90, 0x49, 0xBD, 0x26, 0x7F, 0x06, 0x6D } }, // 25 WOW-20740patch7.0.1_Beta <not used between 7.0 and 7.3>
|
||||
{ 0xD1E9B5EDF9283668ULL, { 0x8E, 0x4A, 0x25, 0x79, 0x89, 0x4E, 0x38, 0xB4, 0xAB, 0x90, 0x58, 0xBA, 0x5C, 0x73, 0x28, 0xEE } }, // 39 WOW-20740patch7.0.1_Beta Enchanted Torch pet
|
||||
@@ -217,7 +218,7 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
|
||||
{ 0xE2F6BD41298A2AB9ULL, { 0xC5, 0xDC, 0x1B, 0xB4, 0x3B, 0x8C, 0xF3, 0xF0, 0x85, 0xD6, 0x98, 0x68, 0x26, 0xB9, 0x28, 0xEC } }, // 241 WOW-28151patch8.1.0_PTR Horde fireworks
|
||||
{ 0x14C4257E557B49A1ULL, { 0x06, 0x4A, 0x97, 0x09, 0xF4, 0x2D, 0x50, 0xCB, 0x5F, 0x8B, 0x94, 0xBC, 0x1A, 0xCF, 0xDD, 0x5D } }, // 242 WOW-28440patch8.1.0_PTR dor cinematic
|
||||
{ 0x1254E65319C6EEFFULL, { 0x79, 0xD2, 0xB3, 0xD1, 0xCC, 0xB0, 0x15, 0x47, 0x4E, 0x71, 0x58, 0x81, 0x38, 0x64, 0xB8, 0xE6 } }, // 243 WOW-28440patch8.1.0_PTR akt cinematic
|
||||
// { 0xC8753773ADF1174CULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 244 WOW-28938patch8.1.5_PTR starts at fdid 2615771, total of 15 fdids
|
||||
{ 0xC8753773ADF1174CULL, { 0x1E, 0x0E, 0x37, 0xD4, 0x2E, 0xE5, 0xCE, 0x5E, 0x80, 0x67, 0xF0, 0x39, 0x4B, 0x09, 0x05, 0xF2 } }, // 244 WOW-28938patch8.1.5_PTR starts at fdid 2615771, total of 15 fdids
|
||||
// { 0x2170BCAA9FA96E22ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 245 WOW-28938patch8.1.5_PTR alpaca mount
|
||||
// { 0x75485627AA225F4DULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 246 WOW-28938patch8.1.5_PTR fdid 2741546, 2741548, 2741549
|
||||
{ 0x08717B15BF3C7955ULL, { 0x4B, 0x06, 0xBF, 0x9D, 0x17, 0x66, 0x3C, 0xEB, 0x33, 0x12, 0xEA, 0x3C, 0x69, 0xFB, 0xC5, 0xDD } }, // 248 WOW-29220patch8.1.5_PTR inv_encrypted20.blp (fdid 2823166)
|
||||
@@ -225,13 +226,23 @@ static CASC_ENCRYPTION_KEY CascKeys[] =
|
||||
{ 0x9FD609902B4B2E07ULL, { 0xAB, 0xE0, 0xC5, 0xF9, 0xC1, 0x23, 0xE6, 0xE2, 0x4E, 0x7B, 0xEA, 0x43, 0xC2, 0xBF, 0x00, 0xAC } }, // 250 WOW-29418patch8.1.5_PTR Derek Proudmoore cinematic (dpr, 5 files)
|
||||
// { 0xCB26B441FAE4C8CDULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 251 WOW-30080patch8.2.0_PTR fdid 2888623, 2892270, 2892271, 2892272, 2892274, 2892275
|
||||
// { 0xA98C7594F55C02F0ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 252 WOW-30080patch8.2.0_PTR starts at fdid 2921871, total of 24 fdids
|
||||
// { 0x259EE68CD9E76DBAULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 253 WOW-30080patch8.2.0_PTR starts at fdid 2957406, total of 30 fdids
|
||||
{ 0x259EE68CD9E76DBAULL, { 0x46, 0x5D, 0x78, 0x4F, 0x10, 0x19, 0x66, 0x1C, 0xCF, 0x41, 0x7F, 0xE4, 0x66, 0x80, 0x12, 0x83 } }, // 253 WOW-30080patch8.2.0_PTR starts at fdid 2957406, total of 30 fdids
|
||||
// { 0x6A026290FBDB3754ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 255 WOW-30080patch8.2.0_PTR starts at fdid 2976294, total of 201 fdids
|
||||
{ 0xCF72FD04608D36EDULL, { 0xA0, 0xA8, 0x89, 0x97, 0x6D, 0x02, 0xFA, 0x8D, 0x00, 0xF7, 0xAF, 0x00, 0x17, 0xAD, 0x72, 0x1F } }, // 257 WOW-30262patch8.2.0_PTR Azshara Warbringer cinematic (5 files)
|
||||
// { 0x17F07C2E3A45DB3DULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 258 WOW-30262patch8.2.0_PTR starts at fdid 982457, total of 17 fdids
|
||||
{ 0x17F07C2E3A45DB3DULL, { 0x6D, 0x38, 0x86, 0xBD, 0xB9, 0x1E, 0x71, 0x5A, 0xE7, 0x18, 0x2D, 0x9F, 0x3A, 0x08, 0xF2, 0xC9 } }, // 258 WOW-30262patch8.2.0_PTR Solesa Naksu Nazjatar phase (17 files)
|
||||
// { 0xDFAB5841B87802B5ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 259 WOW-31337patch8.2.5_PTR starts at fdid 3016206, total of 8 fdids
|
||||
{ 0xC050FA06BB0538F6ULL, { 0xC5, 0x52, 0xF5, 0xD0, 0xB7, 0x22, 0x31, 0x50, 0x2D, 0x25, 0x47, 0x31, 0x4E, 0x60, 0x15, 0xF7 } }, // 260 WOW-30495patch8.2.0_PTR Crossroads cinematic (5 files)
|
||||
// { 0xAB5CDD3FC321831FULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 261 WOW-30495patch8.2.0_PTR Unknown cinematic fdid 3022943-3022947 (5 files)
|
||||
{ 0xAB5CDD3FC321831FULL, { 0xE1, 0x38, 0x4F, 0x5B, 0x06, 0xEB, 0xBC, 0xD3, 0x33, 0x69, 0x5A, 0xA6, 0xFF, 0xC6, 0x83, 0x18 } }, // 261 WOW-30495patch8.2.0_PTR Azshara kill cinematic (5 files)
|
||||
{ 0xA7B7D1F12395040EULL, { 0x36, 0xAD, 0x3B, 0x31, 0x27, 0x3F, 0x1E, 0xBC, 0xEE, 0x85, 0x20, 0xAA, 0xA7, 0x4B, 0x12, 0xF2 } }, // 262 WOW-30495patch8.2.0_PTR Nazjatar intro cinematics (9 files)
|
||||
// { 0x83A2AB72DD8AE992ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 263 WOW-31337patch8.2.5_PTR starts at fdid 801707, total of 372 fdids
|
||||
// { 0xBEAF567CC45362F0ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 264 WOW-31337patch8.2.5_PTR starts at fdid 1002186, total of 71 fdids
|
||||
// { 0x7BB3A77FD8D14783ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 265 WOW-31337patch8.2.5_PTR fdid 1251882, 1251883
|
||||
// { 0x8F4098E2470FE0C8ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 266 WOW-31337patch8.2.5_PTR starts at fdid 841604, total of 58 fdids
|
||||
// { 0x6AC5C837A2027A6BULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 267 WOW-31337patch8.2.5_PTR starts at fdid 3037834, total of 263 fdids
|
||||
// { 0x302AAD8B1F441D95ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 271 WOW-31337patch8.2.5_PTR starts at fdid 1376212, total of 300 fdids
|
||||
// { 0x5C909F00088734B9ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 272 WOW-31337patch8.2.5_PTR
|
||||
// { 0xF785977C76DE9C77ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 273 WOW-31337patch8.2.5_PTR starts at fdid 3071600, total of 295 fdids
|
||||
// { 0x1CDAF3931871BEC3ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 275 WOW-31337patch8.2.5_PTR fdid 988200, 1140079, 1263818, 1347275
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -374,27 +385,27 @@ static LPBYTE CascFindKey(TCascStorage * hs, ULONGLONG KeyName)
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int CascLoadEncryptionKeys(TCascStorage * hs)
|
||||
DWORD CascLoadEncryptionKeys(TCascStorage * hs)
|
||||
{
|
||||
size_t nKeyCount = (sizeof(CascKeys) / sizeof(CASC_ENCRYPTION_KEY));
|
||||
size_t nMaxItems = nKeyCount + CASC_EXTRA_KEYS;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Create fast map of KeyName -> Key
|
||||
nError = hs->EncryptionKeys.Create(nMaxItems, sizeof(ULONGLONG), FIELD_OFFSET(CASC_ENCRYPTION_KEY, KeyName));
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = hs->EncryptionKeys.Create(nMaxItems, sizeof(ULONGLONG), FIELD_OFFSET(CASC_ENCRYPTION_KEY, KeyName));
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Insert all static keys
|
||||
for (size_t i = 0; i < nKeyCount; i++)
|
||||
hs->EncryptionKeys.InsertObject(&CascKeys[i], &CascKeys[i].KeyName);
|
||||
|
||||
// Create array for extra keys
|
||||
nError = hs->ExtraKeysList.Create<CASC_ENCRYPTION_KEY>(CASC_EXTRA_KEYS);
|
||||
return nError;
|
||||
dwErrCode = hs->ExtraKeysList.Create<CASC_ENCRYPTION_KEY>(CASC_EXTRA_KEYS);
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
int CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex)
|
||||
DWORD CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex)
|
||||
{
|
||||
ULONGLONG KeyName = 0;
|
||||
LPBYTE pbBufferEnd = pbInBuffer + cbInBuffer;
|
||||
@@ -404,7 +415,7 @@ int CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBY
|
||||
DWORD IVSize;
|
||||
BYTE Vector[0x08];
|
||||
BYTE EncryptionType;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Verify and retrieve the key name size
|
||||
if(pbInBuffer >= pbBufferEnd)
|
||||
@@ -462,15 +473,15 @@ int CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBY
|
||||
switch(EncryptionType)
|
||||
{
|
||||
case 'S': // Salsa20
|
||||
nError = Decrypt_Salsa20(pbOutBuffer, pbInBuffer, (pbBufferEnd - pbInBuffer), pbKey, 0x10, Vector);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = Decrypt_Salsa20(pbOutBuffer, pbInBuffer, (pbBufferEnd - pbInBuffer), pbKey, 0x10, Vector);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Supply the size of the output buffer
|
||||
pcbOutBuffer[0] = (DWORD)(pbBufferEnd - pbInBuffer);
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
// case 'A':
|
||||
// case 'A':
|
||||
// return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
@@ -478,7 +489,7 @@ int CascDecrypt(TCascStorage * hs, LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBY
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
int CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer)
|
||||
DWORD CascDirectCopy(LPBYTE pbOutBuffer, PDWORD pcbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer)
|
||||
{
|
||||
// Check the buffer size
|
||||
if((cbInBuffer - 1) > pcbOutBuffer[0])
|
||||
@@ -526,7 +537,34 @@ bool WINAPI CascAddEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPBYTE Key)
|
||||
pEncKey->KeyName = KeyName;
|
||||
|
||||
// Also insert the key to the map
|
||||
return hs->EncryptionKeys.InsertObject(pEncKey, &pEncKey->KeyName);
|
||||
if (!hs->EncryptionKeys.InsertObject(pEncKey, &pEncKey->KeyName))
|
||||
{
|
||||
SetLastError(ERROR_ALREADY_EXISTS);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WINAPI CascAddStringEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPCSTR szKey)
|
||||
{
|
||||
BYTE Key[CASC_KEY_LENGTH];
|
||||
|
||||
// Check the length of the string key
|
||||
if(strlen(szKey) != CASC_KEY_LENGTH * 2)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the string key to the binary array
|
||||
if(ConvertStringToBinary(szKey, CASC_KEY_LENGTH * 2, Key) != ERROR_SUCCESS)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
return CascAddEncryptionKey(hStorage, KeyName, Key);
|
||||
}
|
||||
|
||||
LPBYTE WINAPI CascFindEncryptionKey(HANDLE hStorage, ULONGLONG KeyName)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,49 +24,52 @@ static void ResetFindData(PCASC_FIND_DATA pFindData)
|
||||
pFindData->szFileName[0] = 0;
|
||||
pFindData->szPlainName = pFindData->szFileName;
|
||||
pFindData->TagBitMask = 0;
|
||||
pFindData->FileSize = CASC_INVALID_SIZE64;
|
||||
pFindData->dwFileDataId = CASC_INVALID_ID;
|
||||
pFindData->dwFileSize = CASC_INVALID_SIZE;
|
||||
pFindData->dwLocaleFlags = CASC_INVALID_ID;
|
||||
pFindData->dwContentFlags = CASC_INVALID_ID;
|
||||
pFindData->dwSpanCount = 1;
|
||||
pFindData->NameType = CascNameFull;
|
||||
pFindData->bFileAvailable = false;
|
||||
pFindData->bCanOpenByName = false;
|
||||
pFindData->bCanOpenByDataId = false;
|
||||
pFindData->bCanOpenByCKey = false;
|
||||
pFindData->bCanOpenByEKey = false;
|
||||
}
|
||||
|
||||
static void SupplyFakeFileName(PCASC_FIND_DATA pFindData)
|
||||
static void SupplyFakeFileName(PCASC_FIND_DATA pFindData, PCASC_CKEY_ENTRY pCKeyEntry)
|
||||
{
|
||||
// If the file can be open by file data id, create fake file name
|
||||
if(pFindData->bCanOpenByDataId)
|
||||
// If there is a file data ID, create fake file name
|
||||
if(pFindData->dwFileDataId != CASC_INVALID_ID)
|
||||
{
|
||||
CascStrPrintf(pFindData->szFileName, _countof(pFindData->szFileName), "FILE%08X.dat", pFindData->dwFileDataId);
|
||||
pFindData->NameType = CascNameDataId;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the file can be open by CKey, convert the CKey to file name
|
||||
if(pFindData->bCanOpenByCKey)
|
||||
// If there is a CKey, convert the CKey to file name
|
||||
if(pCKeyEntry->Flags & CASC_CE_HAS_CKEY)
|
||||
{
|
||||
StringFromBinary(pFindData->CKey, MD5_HASH_SIZE, pFindData->szFileName);
|
||||
pFindData->NameType = CascNameCKey;
|
||||
return;
|
||||
}
|
||||
|
||||
// CKey should be always present
|
||||
StringFromBinary(pFindData->EKey, MD5_HASH_SIZE, pFindData->szFileName);
|
||||
pFindData->NameType = CascNameEKey;
|
||||
assert(pFindData->bCanOpenByEKey != false);
|
||||
// EKey should be always present
|
||||
if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY)
|
||||
{
|
||||
StringFromBinary(pFindData->EKey, MD5_HASH_SIZE, pFindData->szFileName);
|
||||
pFindData->NameType = CascNameEKey;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
}
|
||||
|
||||
static bool CopyCKeyEntryToFindData(PCASC_FIND_DATA pFindData, PCASC_CKEY_ENTRY pCKeyEntry)
|
||||
{
|
||||
ULONGLONG ContentSize = 0;
|
||||
ULONGLONG EncodedSize = 0;
|
||||
|
||||
// Supply both keys
|
||||
CopyMemory16(pFindData->CKey, pCKeyEntry->CKey);
|
||||
CopyMemory16(pFindData->EKey, pCKeyEntry->EKey);
|
||||
pFindData->bCanOpenByCKey = (pCKeyEntry->Flags & CASC_CE_HAS_CKEY) ? true : false;
|
||||
pFindData->bCanOpenByEKey = (pCKeyEntry->Flags & CASC_CE_HAS_EKEY) ? true : false;
|
||||
|
||||
// Supply the tag mask
|
||||
pFindData->TagBitMask = pCKeyEntry->TagBitMask;
|
||||
@@ -77,15 +80,21 @@ static bool CopyCKeyEntryToFindData(PCASC_FIND_DATA pFindData, PCASC_CKEY_ENTRY
|
||||
|
||||
// If we retrieved the file size directly from the root provider, use it
|
||||
// Otherwise, supply EncodedSize or ContentSize, whichever is available (but ContentSize > EncodedSize)
|
||||
if(pFindData->dwFileSize == CASC_INVALID_SIZE)
|
||||
pFindData->dwFileSize = pCKeyEntry->ContentSize;
|
||||
pFindData->dwSpanCount = GetFileSpanInfo(pCKeyEntry, &ContentSize, &EncodedSize);
|
||||
if(pFindData->FileSize == CASC_INVALID_SIZE64)
|
||||
{
|
||||
if(ContentSize != CASC_INVALID_SIZE64)
|
||||
pFindData->FileSize = ContentSize;
|
||||
else
|
||||
pFindData->FileSize = EncodedSize;
|
||||
}
|
||||
|
||||
// Set flag indicating that the file is locally available
|
||||
pFindData->bFileAvailable = (pCKeyEntry->Flags & CASC_CE_FILE_IS_LOCAL);
|
||||
|
||||
// Supply a fake file name, if there is none supplied by the root handler
|
||||
if(pFindData->szFileName[0] == 0)
|
||||
SupplyFakeFileName(pFindData);
|
||||
SupplyFakeFileName(pFindData, pCKeyEntry);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -127,9 +136,12 @@ static bool DoStorageSearch_CKey(TCascSearch * pSearch, PCASC_FIND_DATA pFindDat
|
||||
// Check for CKeys that haven't been found yet
|
||||
while(pSearch->nFileIndex < nTotalItems)
|
||||
{
|
||||
// Locate the n-th CKey entry. If this entry is not referenced by the root handler, we include it in the search result
|
||||
// Locate the n-th CKey entry.
|
||||
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.ItemAt(pSearch->nFileIndex++);
|
||||
if((pCKeyEntry->Flags & CASC_CE_FOLDER_ENTRY) == 0 && pCKeyEntry->RefCount == 0)
|
||||
// BREAK_ON_XKEY3(pCKeyEntry->CKey, 0x2B, 0xfc, 0xe4);
|
||||
|
||||
// Only report files that are unreferenced by the ROOT handler
|
||||
if(pCKeyEntry->IsFile() && pCKeyEntry->RefCount == 0)
|
||||
{
|
||||
return CopyCKeyEntryToFindData(pFindData, pCKeyEntry);
|
||||
}
|
||||
@@ -189,31 +201,31 @@ HANDLE WINAPI CascFindFirstFile(
|
||||
{
|
||||
TCascStorage * hs;
|
||||
TCascSearch * pSearch = NULL;
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// Check parameters
|
||||
if((hs = TCascStorage::IsValid(hStorage)) == NULL)
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
dwErrCode = ERROR_INVALID_HANDLE;
|
||||
if(szMask == NULL || pFindData == NULL)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
dwErrCode = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Init the search structure and search handle
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Allocate the search handle
|
||||
pSearch = new TCascSearch(hs, szListFile, szMask);
|
||||
if(pSearch == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Perform search
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
if(!DoStorageSearch(pSearch, pFindData))
|
||||
nError = ERROR_NO_MORE_FILES;
|
||||
dwErrCode = ERROR_NO_MORE_FILES;
|
||||
}
|
||||
|
||||
if(nError != ERROR_SUCCESS)
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
{
|
||||
delete pSearch;
|
||||
pSearch = (TCascSearch *)INVALID_HANDLE_VALUE;
|
||||
|
||||
@@ -129,6 +129,17 @@ static TCHAR * CreateIndexFileName(TCascStorage * hs, DWORD IndexValue, DWORD In
|
||||
return CombinePath(hs->szIndexPath, szPlainName);
|
||||
}
|
||||
|
||||
static void SaveFileOffsetBitsAndEKeyLength(TCascStorage * hs, BYTE FileOffsetBits, BYTE EKeyLength)
|
||||
{
|
||||
if(hs->FileOffsetBits == 0)
|
||||
hs->FileOffsetBits = FileOffsetBits;
|
||||
assert(hs->FileOffsetBits == FileOffsetBits);
|
||||
|
||||
if(hs->EKeyLength == 0)
|
||||
hs->EKeyLength = EKeyLength;
|
||||
assert(hs->EKeyLength == EKeyLength);
|
||||
}
|
||||
|
||||
// Verifies a guarded block - data availability and checksum match
|
||||
static LPBYTE CaptureGuardedBlock1(LPBYTE pbFileData, LPBYTE pbFileEnd)
|
||||
{
|
||||
@@ -206,74 +217,84 @@ static LPBYTE CaptureGuardedBlock3(LPBYTE pbFileData, LPBYTE pbFileEnd, size_t E
|
||||
return (LPBYTE)(PtrEntryHash + 1);
|
||||
}
|
||||
|
||||
static bool CaptureIndexEntry(CASC_INDEX_HEADER & InHeader, PCASC_CKEY_ENTRY pCKeyEntry, LPBYTE pbEKeyEntry)
|
||||
static bool CaptureIndexEntry(CASC_INDEX_HEADER & InHeader, PCASC_EKEY_ENTRY pEKeyEntry, LPBYTE pbEKeyEntry)
|
||||
{
|
||||
// Zero both CKey and EKey
|
||||
ZeroMemory16(pCKeyEntry->CKey);
|
||||
ZeroMemory16(pCKeyEntry->EKey);
|
||||
// Copy the EKey of the variable length
|
||||
pbEKeyEntry = CaptureEncodedKey(pEKeyEntry->EKey, pbEKeyEntry, InHeader.EKeyLength);
|
||||
|
||||
// Copy the EKey. We assume 9 bytes
|
||||
pCKeyEntry->EKey[0x00] = pbEKeyEntry[0];
|
||||
pCKeyEntry->EKey[0x01] = pbEKeyEntry[1];
|
||||
pCKeyEntry->EKey[0x02] = pbEKeyEntry[2];
|
||||
pCKeyEntry->EKey[0x03] = pbEKeyEntry[3];
|
||||
pCKeyEntry->EKey[0x04] = pbEKeyEntry[4];
|
||||
pCKeyEntry->EKey[0x05] = pbEKeyEntry[5];
|
||||
pCKeyEntry->EKey[0x06] = pbEKeyEntry[6];
|
||||
pCKeyEntry->EKey[0x07] = pbEKeyEntry[7];
|
||||
pCKeyEntry->EKey[0x08] = pbEKeyEntry[8];
|
||||
pCKeyEntry->EKey[0x09] = pbEKeyEntry[9];
|
||||
pbEKeyEntry += InHeader.EKeyLength;
|
||||
|
||||
// Copy the storage offset
|
||||
pCKeyEntry->StorageOffset = ConvertBytesToInteger_5(pbEKeyEntry);
|
||||
pbEKeyEntry += InHeader.StorageOffsetLength;
|
||||
|
||||
// Clear the tag bit mask
|
||||
pCKeyEntry->TagBitMask = 0;
|
||||
|
||||
// Copy the encoded length
|
||||
pCKeyEntry->EncodedSize = ConvertBytesToInteger_4_LE(pbEKeyEntry);
|
||||
pCKeyEntry->ContentSize = CASC_INVALID_SIZE;
|
||||
pCKeyEntry->RefCount = 0;
|
||||
pCKeyEntry->Priority = 0;
|
||||
pCKeyEntry->Flags = CASC_CE_FILE_IS_LOCAL | CASC_CE_HAS_EKEY | CASC_CE_HAS_EKEY_PARTIAL;
|
||||
// Copy the storage offset and encoded size
|
||||
pEKeyEntry->StorageOffset = ConvertBytesToInteger_5(pbEKeyEntry);
|
||||
pEKeyEntry->EncodedSize = ConvertBytesToInteger_4_LE(pbEKeyEntry + InHeader.StorageOffsetLength);
|
||||
pEKeyEntry->Alignment = 0;
|
||||
|
||||
// We ignore items that have EncodedSize of 0x1E
|
||||
return (pCKeyEntry->EncodedSize > FIELD_OFFSET(BLTE_ENCODED_HEADER, Signature));
|
||||
return (pEKeyEntry->EncodedSize > FIELD_OFFSET(BLTE_ENCODED_HEADER, Signature));
|
||||
}
|
||||
|
||||
static void CheckForEncodingManifestCKey(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry)
|
||||
static void InsertCKeyEntry(TCascStorage * hs, CASC_EKEY_ENTRY & EKeyEntry, DWORD Flags)
|
||||
{
|
||||
// If the encoding file was not found yet
|
||||
if(hs->EncodingCKey.StorageOffset == CASC_INVALID_OFFS64)
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
|
||||
// Multiple items with the same EKey in the index files may exist.
|
||||
// Example: "2018 - New CASC\00001", EKey 37 89 16 5b 2d cc 71 c1 25 00 00 00 00 00 00 00
|
||||
// Positions: 0x1D, 0x1E, 0x1F
|
||||
// In that case, we only take the first one into account
|
||||
// BREAK_ON_XKEY3(EKeyEntry.EKey, 0x09, 0xF3, 0xCD);
|
||||
|
||||
// If the item is not there yet, insert a new one
|
||||
if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKeyEntry.EKey)) == NULL)
|
||||
{
|
||||
if(!memcmp(pCKeyEntry->EKey, hs->EncodingCKey.EKey, hs->EKeyLength))
|
||||
// Insert a new entry to the array. DO NOT ALLOW enlarge array here
|
||||
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false);
|
||||
if(pCKeyEntry == NULL)
|
||||
return;
|
||||
|
||||
// Fill-in the information
|
||||
ZeroMemory16(pCKeyEntry->CKey);
|
||||
CopyMemory16(pCKeyEntry->EKey, EKeyEntry.EKey);
|
||||
pCKeyEntry->StorageOffset = EKeyEntry.StorageOffset;
|
||||
pCKeyEntry->TagBitMask = 0;
|
||||
pCKeyEntry->ContentSize = CASC_INVALID_SIZE;
|
||||
pCKeyEntry->EncodedSize = EKeyEntry.EncodedSize;
|
||||
pCKeyEntry->Flags = CASC_CE_HAS_EKEY | CASC_CE_HAS_EKEY_PARTIAL;
|
||||
pCKeyEntry->RefCount = 0;
|
||||
pCKeyEntry->SpanCount = 1;
|
||||
pCKeyEntry->Priority = 0;
|
||||
|
||||
// Insert the item to the EKey table
|
||||
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The entry already exists. True e.g. for ENCODING.
|
||||
// Only copy the storage offset and sizes if not available yet
|
||||
if(pCKeyEntry->StorageOffset == CASC_INVALID_OFFS64)
|
||||
{
|
||||
hs->EncodingCKey.StorageOffset = pCKeyEntry->StorageOffset;
|
||||
hs->EncodingCKey.EncodedSize = pCKeyEntry->EncodedSize;
|
||||
hs->EncodingCKey.Flags |= CASC_CE_FILE_IS_LOCAL;
|
||||
pCKeyEntry->StorageOffset = EKeyEntry.StorageOffset;
|
||||
pCKeyEntry->EncodedSize = EKeyEntry.EncodedSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the extra flag
|
||||
pCKeyEntry->Flags |= Flags;
|
||||
}
|
||||
|
||||
static int LoadIndexItems(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbEKeyEntry, LPBYTE pbEKeyEnd)
|
||||
static DWORD LoadIndexItems(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbEKeyEntry, LPBYTE pbEKeyEnd)
|
||||
{
|
||||
size_t EntryLength = InHeader.EntryLength;
|
||||
|
||||
while((pbEKeyEntry + EntryLength) <= pbEKeyEnd)
|
||||
{
|
||||
CASC_CKEY_ENTRY CKeyEntry;
|
||||
CASC_EKEY_ENTRY EKeyEntry;
|
||||
|
||||
// Capture the index entry and verify it.
|
||||
if(CaptureIndexEntry(InHeader, &CKeyEntry, pbEKeyEntry))
|
||||
if(CaptureIndexEntry(InHeader, &EKeyEntry, pbEKeyEntry))
|
||||
{
|
||||
// Insert new entry to the array of CKey entries
|
||||
if(hs->IndexArray.Insert(&CKeyEntry, 1) == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
// DOWNLOAD in HOTS
|
||||
//BREAK_ON_XKEY3(EKeyEntry.EKey, 0x09, 0xF3, 0xCD);
|
||||
|
||||
// Verify whether the key is not a CKEy entry for ENCODING file
|
||||
CheckForEncodingManifestCKey(hs, &CKeyEntry);
|
||||
// Insert the index entry to the central table
|
||||
InsertCKeyEntry(hs, EKeyEntry, CASC_CE_FILE_IS_LOCAL);
|
||||
}
|
||||
|
||||
pbEKeyEntry += EntryLength;
|
||||
@@ -282,7 +303,7 @@ static int LoadIndexItems(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYT
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int CaptureIndexHeader_V1(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
|
||||
static DWORD CaptureIndexHeader_V1(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
|
||||
{
|
||||
PFILE_INDEX_HEADER_V1 pIndexHeader = (PFILE_INDEX_HEADER_V1)pbFileData;
|
||||
LPBYTE pbKeyEntries;
|
||||
@@ -342,7 +363,7 @@ static int CaptureIndexHeader_V1(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int CaptureIndexHeader_V2(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
|
||||
static DWORD CaptureIndexHeader_V2(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
|
||||
{
|
||||
PFILE_INDEX_HEADER_V2 pIndexHeader;
|
||||
LPBYTE pbFileEnd = pbFileData + cbFileData;
|
||||
@@ -376,30 +397,28 @@ static int CaptureIndexHeader_V2(CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int LoadIndexFile_V1(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData)
|
||||
static DWORD LoadIndexFile_V1(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData)
|
||||
{
|
||||
LPBYTE pbEKeyEntries = pbFileData + InHeader.HeaderLength + InHeader.HeaderPadding;
|
||||
|
||||
// Remember the values from the index header
|
||||
hs->FileOffsetBits = InHeader.FileOffsetBits;
|
||||
hs->EKeyLength = InHeader.EKeyLength;
|
||||
SaveFileOffsetBitsAndEKeyLength(hs, InHeader.FileOffsetBits, InHeader.EKeyLength);
|
||||
|
||||
// Load the entries from a continuous array
|
||||
return LoadIndexItems(hs, InHeader, pbEKeyEntries, pbFileData + cbFileData);
|
||||
}
|
||||
|
||||
static int LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData)
|
||||
static DWORD LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPBYTE pbFileData, DWORD cbFileData)
|
||||
{
|
||||
LPBYTE pbEKeyEntry;
|
||||
LPBYTE pbFileEnd = pbFileData + cbFileData;
|
||||
LPBYTE pbFilePtr = pbFileData + InHeader.HeaderLength + InHeader.HeaderPadding;
|
||||
size_t EKeyEntriesLength;
|
||||
DWORD BlockSize = 0;
|
||||
int nError = ERROR_NOT_SUPPORTED;
|
||||
DWORD dwErrCode = ERROR_NOT_SUPPORTED;
|
||||
|
||||
// Remember the values from the index header
|
||||
hs->FileOffsetBits = InHeader.FileOffsetBits;
|
||||
hs->EKeyLength = InHeader.EKeyLength;
|
||||
SaveFileOffsetBitsAndEKeyLength(hs, InHeader.FileOffsetBits, InHeader.EKeyLength);
|
||||
|
||||
// Get the pointer to the first block of EKey entries
|
||||
if((pbEKeyEntry = CaptureGuardedBlock2(pbFilePtr, pbFileEnd, InHeader.EntryLength, &BlockSize)) != NULL)
|
||||
@@ -427,7 +446,7 @@ static int LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPB
|
||||
|
||||
while(pbEKeyEntry < pbEndPage)
|
||||
{
|
||||
CASC_CKEY_ENTRY CKeyEntry;
|
||||
CASC_EKEY_ENTRY EKeyEntry;
|
||||
|
||||
// Check the EKey entry protected by 32-bit hash
|
||||
if((pbEKeyEntry = CaptureGuardedBlock3(pbEKeyEntry, pbEndPage, InHeader.EntryLength)) == NULL)
|
||||
@@ -437,14 +456,9 @@ static int LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPB
|
||||
//BREAK_ON_XKEY3(pbEKeyEntry, 0xbc, 0xe8, 0x23);
|
||||
|
||||
// Capture the index entry and verify it.
|
||||
if(CaptureIndexEntry(InHeader, &CKeyEntry, pbEKeyEntry))
|
||||
if(CaptureIndexEntry(InHeader, &EKeyEntry, pbEKeyEntry))
|
||||
{
|
||||
// Insert the EKey entry to the array
|
||||
if(hs->IndexArray.Insert(&CKeyEntry, 1) == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Check whether the CKey entry is an encoding entry
|
||||
CheckForEncodingManifestCKey(hs, &CKeyEntry);
|
||||
InsertCKeyEntry(hs, EKeyEntry, CASC_CE_FILE_IS_LOCAL);
|
||||
}
|
||||
|
||||
// Move to the next entry
|
||||
@@ -454,13 +468,13 @@ static int LoadIndexFile_V2(TCascStorage * hs, CASC_INDEX_HEADER & InHeader, LPB
|
||||
// Move to the next chunk
|
||||
pbStartPage += FILE_INDEX_PAGE_SIZE;
|
||||
}
|
||||
nError = ERROR_SUCCESS;
|
||||
dwErrCode = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
static int LoadIndexFile(TCascStorage * hs, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
|
||||
static DWORD LoadIndexFile(TCascStorage * hs, LPBYTE pbFileData, DWORD cbFileData, DWORD BucketIndex)
|
||||
{
|
||||
CASC_INDEX_HEADER InHeader;
|
||||
|
||||
@@ -477,76 +491,73 @@ static int LoadIndexFile(TCascStorage * hs, LPBYTE pbFileData, DWORD cbFileData,
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
static int LoadIndexFile(TCascStorage * hs, const TCHAR * szFileName, DWORD BucketIndex)
|
||||
static DWORD LoadIndexFile(TCascStorage * hs, const TCHAR * szFileName, DWORD BucketIndex)
|
||||
{
|
||||
LPBYTE pbFileData;
|
||||
DWORD cbFileData;
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// WoW6 actually reads THE ENTIRE file to memory. Verified on Mac build (x64).
|
||||
pbFileData = LoadFileToMemory(szFileName, &cbFileData);
|
||||
if(pbFileData && cbFileData)
|
||||
{
|
||||
// Parse and load the index file
|
||||
nError = LoadIndexFile(hs, pbFileData, cbFileData, BucketIndex);
|
||||
dwErrCode = LoadIndexFile(hs, pbFileData, cbFileData, BucketIndex);
|
||||
CASC_FREE(pbFileData);
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = GetLastError();
|
||||
dwErrCode = GetLastError();
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
static int LoadLocalIndexFiles(TCascStorage * hs)
|
||||
static DWORD LoadLocalIndexFiles(TCascStorage * hs)
|
||||
{
|
||||
TCHAR * szFileName;
|
||||
DWORD OldIndexArray[CASC_INDEX_COUNT];
|
||||
DWORD IndexArray[CASC_INDEX_COUNT];
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Scan all index files and load contained EKEY entries
|
||||
memset(OldIndexArray, 0, sizeof(OldIndexArray));
|
||||
memset(IndexArray, 0, sizeof(IndexArray));
|
||||
nError = ScanIndexDirectory(hs->szIndexPath, IndexDirectory_OnFileFound, IndexArray, OldIndexArray, hs);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
dwErrCode = ScanIndexDirectory(hs->szIndexPath, IndexDirectory_OnFileFound, IndexArray, OldIndexArray, hs);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Initialize the array of index files
|
||||
if((nError = hs->IndexArray.Create(sizeof(CASC_CKEY_ENTRY), 0x200000)) == ERROR_SUCCESS)
|
||||
// Load each index file
|
||||
for(DWORD i = 0; i < CASC_INDEX_COUNT; i++)
|
||||
{
|
||||
// Load each index file
|
||||
for(DWORD i = 0; i < CASC_INDEX_COUNT; i++)
|
||||
// Create the name of the index file
|
||||
if((szFileName = CreateIndexFileName(hs, i, IndexArray[i])) != NULL)
|
||||
{
|
||||
// Create the name of the index file
|
||||
if((szFileName = CreateIndexFileName(hs, i, IndexArray[i])) != NULL)
|
||||
// Inform the user about what we are doing
|
||||
if(InvokeProgressCallback(hs, "Loading index files", NULL, i, CASC_INDEX_COUNT))
|
||||
{
|
||||
// Inform the user about what we are doing
|
||||
if(InvokeProgressCallback(hs, "Loading index files", NULL, i, CASC_INDEX_COUNT))
|
||||
{
|
||||
nError = ERROR_CANCELLED;
|
||||
break;
|
||||
}
|
||||
|
||||
// Load the index file
|
||||
if((nError = LoadIndexFile(hs, szFileName, i)) != ERROR_SUCCESS)
|
||||
break;
|
||||
CASC_FREE(szFileName);
|
||||
dwErrCode = ERROR_CANCELLED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remember the number of files that are present locally
|
||||
hs->LocalFiles = hs->IndexArray.ItemCount();
|
||||
// Load the index file
|
||||
if((dwErrCode = LoadIndexFile(hs, szFileName, i)) != ERROR_SUCCESS)
|
||||
break;
|
||||
CASC_FREE(szFileName);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember the number of files that are present locally
|
||||
hs->LocalFiles = hs->CKeyArray.ItemCount();
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Online index files
|
||||
// https://wowdev.wiki/TACT#CDN_File_Organization
|
||||
|
||||
static int CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, DWORD cbIndexFile)
|
||||
static DWORD CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, DWORD cbIndexFile)
|
||||
{
|
||||
FILE_INDEX_FOOTER<0x08> * pFooter08;
|
||||
BYTE checksum_data[0x40] = { 0 };
|
||||
@@ -566,10 +577,10 @@ static int CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndex
|
||||
InFooter.Version = pFooter08->Version;
|
||||
InFooter.OffsetBytes = pFooter08->OffsetBytes;
|
||||
InFooter.SizeBytes = pFooter08->SizeBytes;
|
||||
InFooter.EKeyBytes = pFooter08->EKeySizeBytes;
|
||||
InFooter.EKeyLength = pFooter08->EKeyLength;
|
||||
InFooter.FooterHashBytes = pFooter08->FooterHashBytes;
|
||||
InFooter.PageLength = pFooter08->PageSizeKB << 10;
|
||||
InFooter.ItemLength = pFooter08->EKeySizeBytes + pFooter08->OffsetBytes + pFooter08->SizeBytes;
|
||||
InFooter.ItemLength = pFooter08->EKeyLength + pFooter08->OffsetBytes + pFooter08->SizeBytes;
|
||||
InFooter.FooterLength = sizeof(FILE_INDEX_FOOTER<0x08>);
|
||||
InFooter.ElementCount = ConvertBytesToInteger_4_LE(pFooter08->ElementCount);
|
||||
|
||||
@@ -585,30 +596,33 @@ static int CaptureArcIndexFooter(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndex
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
static int CaptureArcIndexEntry(CASC_ARCINDEX_FOOTER & InFooter, CASC_ARCINDEX_ENTRY & InEntry, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd)
|
||||
static DWORD CaptureIndexEntry(CASC_ARCINDEX_FOOTER & InFooter, CASC_EKEY_ENTRY & EKeyEntry, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd, size_t nArchive)
|
||||
{
|
||||
ULONGLONG StorageOffset = nArchive;
|
||||
ULONGLONG ArchiveOffset;
|
||||
|
||||
// If there enough bytes for one entry/
|
||||
if ((pbIndexPage + InFooter.ItemLength) > pbIndexPageEnd)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Copy the item
|
||||
memcpy(InEntry.EKey, pbIndexPage, InFooter.EKeyBytes);
|
||||
pbIndexPage += InFooter.EKeyBytes;
|
||||
// Capture the EKey (variable length)
|
||||
pbIndexPage = CaptureEncodedKey(EKeyEntry.EKey, pbIndexPage, InFooter.EKeyLength);
|
||||
|
||||
// Copy the archive offset
|
||||
InEntry.EncodedSize = ConvertBytesToInteger_X(pbIndexPage, InFooter.OffsetBytes);
|
||||
pbIndexPage += InFooter.OffsetBytes;
|
||||
|
||||
// Copy thefile encoded size
|
||||
InEntry.ArchiveOffset = ConvertBytesToInteger_X(pbIndexPage, InFooter.SizeBytes);
|
||||
if (InEntry.ArchiveOffset >= 0x10000000)
|
||||
ArchiveOffset = ConvertBytesToInteger_X(pbIndexPage + InFooter.SizeBytes, InFooter.OffsetBytes);
|
||||
if (ArchiveOffset >= 0x10000000)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Capture the storage offset and encoded size
|
||||
EKeyEntry.StorageOffset = (StorageOffset << (InFooter.OffsetBytes * 8)) | ArchiveOffset;
|
||||
EKeyEntry.EncodedSize = ConvertBytesToInteger_X(pbIndexPage, InFooter.SizeBytes);
|
||||
EKeyEntry.Alignment = 0;
|
||||
|
||||
// Is there a valid hash?
|
||||
return CascIsValidMD5(InEntry.EKey) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
|
||||
return CascIsValidMD5(EKeyEntry.EKey) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
static int VerifyIndexSize(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, size_t cbIndexFile, LPBYTE * PtrIndexEnd)
|
||||
static DWORD VerifyIndexSize(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile, size_t cbIndexFile, LPBYTE * PtrIndexEnd)
|
||||
{
|
||||
size_t nPageCount;
|
||||
|
||||
@@ -626,21 +640,20 @@ static int VerifyIndexSize(CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexFile,
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int LoadArchiveIndexPage(TCascStorage * hs, CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexHash, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd)
|
||||
static DWORD LoadArchiveIndexPage(TCascStorage * hs, CASC_ARCINDEX_FOOTER & InFooter, LPBYTE pbIndexPage, LPBYTE pbIndexPageEnd, size_t nArchive)
|
||||
{
|
||||
CASC_ARCINDEX_ENTRY InEntry;
|
||||
int nError;
|
||||
CASC_EKEY_ENTRY EKeyEntry;
|
||||
DWORD dwErrCode;
|
||||
|
||||
while (pbIndexPage < pbIndexPageEnd)
|
||||
while (pbIndexPage <= pbIndexPageEnd)
|
||||
{
|
||||
// Capture the index entry
|
||||
nError = CaptureArcIndexEntry(InFooter, InEntry, pbIndexPage, pbIndexPageEnd);
|
||||
if (nError != ERROR_SUCCESS)
|
||||
dwErrCode = CaptureIndexEntry(InFooter, EKeyEntry, pbIndexPage, pbIndexPageEnd, nArchive);
|
||||
if (dwErrCode != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Insert the index entry to the array
|
||||
memcpy(InEntry.IndexHash, pbIndexHash, MD5_HASH_SIZE);
|
||||
if (hs->ArcIndexArray.Insert(&InEntry, 1) == NULL)
|
||||
// Insert a new entry to the index array
|
||||
if((hs->IndexArray.Insert(&EKeyEntry, 1)) == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Move to the next entry
|
||||
@@ -650,28 +663,31 @@ static int LoadArchiveIndexPage(TCascStorage * hs, CASC_ARCINDEX_FOOTER & InFoot
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int LoadArchiveIndexFile(TCascStorage * hs, LPBYTE pbIndexHash, LPBYTE pbIndexFile, DWORD cbIndexFile)
|
||||
static DWORD LoadArchiveIndexFile(TCascStorage * hs, LPBYTE pbIndexFile, DWORD cbIndexFile, size_t nArchive)
|
||||
{
|
||||
CASC_ARCINDEX_FOOTER InFooter;
|
||||
LPBYTE pbIndexEnd = NULL;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Validate and capture the footer
|
||||
nError = CaptureArcIndexFooter(InFooter, pbIndexFile, cbIndexFile);
|
||||
if (nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = CaptureArcIndexFooter(InFooter, pbIndexFile, cbIndexFile);
|
||||
if (dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Remember the file offset and EKey length
|
||||
SaveFileOffsetBitsAndEKeyLength(hs, InFooter.OffsetBytes * 8, InFooter.EKeyLength);
|
||||
|
||||
// Verify the size of the index file
|
||||
nError = VerifyIndexSize(InFooter, pbIndexFile, cbIndexFile, &pbIndexEnd);
|
||||
if (nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = VerifyIndexSize(InFooter, pbIndexFile, cbIndexFile, &pbIndexEnd);
|
||||
if (dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Parse all pages
|
||||
while (pbIndexFile < pbIndexEnd)
|
||||
{
|
||||
// Load the entire page
|
||||
nError = LoadArchiveIndexPage(hs, InFooter, pbIndexHash, pbIndexFile, pbIndexFile + InFooter.PageLength);
|
||||
if (nError != ERROR_SUCCESS)
|
||||
dwErrCode = LoadArchiveIndexPage(hs, InFooter, pbIndexFile, pbIndexFile + InFooter.PageLength, nArchive);
|
||||
if (dwErrCode != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Move to the next page
|
||||
@@ -681,87 +697,95 @@ static int LoadArchiveIndexFile(TCascStorage * hs, LPBYTE pbIndexHash, LPBYTE pb
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int BuildMapOfArcIndices(TCascStorage * hs)
|
||||
static DWORD BuildMapOfArchiveIndices(TCascStorage * hs)
|
||||
{
|
||||
PCASC_ARCINDEX_ENTRY pEntry;
|
||||
size_t nItemCount = hs->ArcIndexArray.ItemCount();
|
||||
int nError;
|
||||
PCASC_EKEY_ENTRY pEKeyEntry;
|
||||
size_t nItemCount = hs->IndexArray.ItemCount();
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Create the map
|
||||
nError = hs->ArcIndexMap.Create(nItemCount, MD5_HASH_SIZE, FIELD_OFFSET(CASC_ARCINDEX_ENTRY, EKey));
|
||||
if (nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = hs->IndexMap.Create(nItemCount, MD5_HASH_SIZE, FIELD_OFFSET(CASC_EKEY_ENTRY, EKey));
|
||||
if (dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Insert all items
|
||||
for(size_t i = 0; i < nItemCount; i++)
|
||||
{
|
||||
pEntry = (PCASC_ARCINDEX_ENTRY)hs->ArcIndexArray.ItemAt(i);
|
||||
if (pEntry != NULL)
|
||||
pEKeyEntry = (PCASC_EKEY_ENTRY)hs->IndexArray.ItemAt(i);
|
||||
if (pEKeyEntry != NULL)
|
||||
{
|
||||
if (!hs->ArcIndexMap.InsertObject(pEntry, pEntry->EKey))
|
||||
if (!hs->IndexMap.InsertObject(pEKeyEntry, pEKeyEntry->EKey))
|
||||
{
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
static int LoadArchiveIndexFiles(TCascStorage * hs)
|
||||
static DWORD LoadArchiveIndexFiles(TCascStorage * hs)
|
||||
{
|
||||
LPBYTE pbFileData;
|
||||
TCHAR szLocalPath[MAX_PATH];
|
||||
DWORD cbFileData = 0;
|
||||
size_t nArchiveCount = (hs->ArchivesKey.cbData / MD5_HASH_SIZE);
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// Create the array object for the indices
|
||||
nError = hs->ArcIndexArray.Create(sizeof(CASC_ARCINDEX_ENTRY), 10000);
|
||||
if (nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = hs->IndexArray.Create(sizeof(CASC_EKEY_ENTRY), 0x10000);
|
||||
if (dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Load all the indices
|
||||
for (size_t i = 0; i < nArchiveCount; i++)
|
||||
{
|
||||
CASC_CDN_DOWNLOAD CdnsInfo = {0};
|
||||
LPBYTE pbIndexHash = hs->ArchivesKey.pbData + (i * MD5_HASH_SIZE);
|
||||
|
||||
// Inform the user about what we are doing
|
||||
if(InvokeProgressCallback(hs, "Downloading archive indexes", NULL, (DWORD)(i), (DWORD)(nArchiveCount)))
|
||||
{
|
||||
nError = ERROR_CANCELLED;
|
||||
dwErrCode = ERROR_CANCELLED;
|
||||
break;
|
||||
}
|
||||
|
||||
// Make sure that we have local copy of the file
|
||||
nError = DownloadFileFromCDN(hs, _T("data"), pbIndexHash, _T(".index"), szLocalPath, _countof(szLocalPath));
|
||||
if (nError == ERROR_SUCCESS)
|
||||
// Prepare the download structure for "%CDNS_HOST%/%CDNS_PATH%/##/##/EKey" file
|
||||
CdnsInfo.szCdnsPath = hs->szCdnPath;
|
||||
CdnsInfo.szPathType = _T("data");
|
||||
CdnsInfo.pbEKey = pbIndexHash;
|
||||
CdnsInfo.szExtension = _T(".index");
|
||||
CdnsInfo.szLocalPath = szLocalPath;
|
||||
CdnsInfo.ccLocalPath = _countof(szLocalPath);
|
||||
dwErrCode = DownloadFileFromCDN(hs, CdnsInfo);
|
||||
|
||||
// Load and parse the archive index
|
||||
if (dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Load the index file to memory
|
||||
pbFileData = LoadFileToMemory(szLocalPath, &cbFileData);
|
||||
if (pbFileData && cbFileData)
|
||||
{
|
||||
nError = LoadArchiveIndexFile(hs, pbIndexHash, pbFileData, cbFileData);
|
||||
dwErrCode = LoadArchiveIndexFile(hs, pbFileData, cbFileData, i);
|
||||
CASC_FREE(pbFileData);
|
||||
}
|
||||
}
|
||||
|
||||
// Break if an error
|
||||
if (nError != ERROR_SUCCESS)
|
||||
if (dwErrCode != ERROR_SUCCESS)
|
||||
break;
|
||||
}
|
||||
|
||||
// Build map of EKey -> CASC_ARCINDEX_ENTRY
|
||||
if (nError == ERROR_SUCCESS)
|
||||
nError = BuildMapOfArcIndices(hs);
|
||||
|
||||
return nError;
|
||||
// Build map of EKey -> CASC_EKEY_ENTRY
|
||||
if (dwErrCode == ERROR_SUCCESS)
|
||||
dwErrCode = BuildMapOfArchiveIndices(hs);
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int LoadIndexFiles(TCascStorage * hs)
|
||||
DWORD LoadIndexFiles(TCascStorage * hs)
|
||||
{
|
||||
if (hs->dwFeatures & CASC_FEATURE_ONLINE)
|
||||
{
|
||||
|
||||
@@ -13,13 +13,17 @@ EXPORTS
|
||||
CascOpenOnlineStorage
|
||||
CascGetStorageInfo
|
||||
CascAddEncryptionKey
|
||||
CascAddStringEncryptionKey
|
||||
CascFindEncryptionKey
|
||||
CascCloseStorage
|
||||
|
||||
CascOpenFile
|
||||
CascOpenLocalFile
|
||||
CascGetFileInfo
|
||||
CascGetFileSize
|
||||
CascGetFileSize64
|
||||
CascSetFilePointer
|
||||
CascSetFilePointer64
|
||||
CascReadFile
|
||||
CascCloseFile
|
||||
|
||||
@@ -29,4 +33,3 @@ EXPORTS
|
||||
|
||||
GetLastError=Kernel32.GetLastError
|
||||
SetLastError=Kernel32.SetLastError
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#define __CASCLIB_H__
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
|
||||
#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
|
||||
#pragma warning(disable:4820) // 'XXX' : '2' bytes padding added after data member 'XXX::yyy'
|
||||
#endif
|
||||
|
||||
@@ -41,29 +41,29 @@ extern "C" {
|
||||
#ifdef _DEBUG // DEBUG VERSIONS
|
||||
#ifndef _UNICODE
|
||||
#ifdef _DLL
|
||||
#pragma comment(lib, "CascLibDAD.lib") // Debug Ansi CRT-DLL version
|
||||
#pragma comment(lib, "CascLibDAD.lib") // Debug Ansi CRT-DLL version
|
||||
#else
|
||||
#pragma comment(lib, "CascLibDAS.lib") // Debug Ansi CRT-LIB version
|
||||
#pragma comment(lib, "CascLibDAS.lib") // Debug Ansi CRT-LIB version
|
||||
#endif
|
||||
#else
|
||||
#ifdef _DLL
|
||||
#pragma comment(lib, "CascLibDUD.lib") // Debug Unicode CRT-DLL version
|
||||
#pragma comment(lib, "CascLibDUD.lib") // Debug Unicode CRT-DLL version
|
||||
#else
|
||||
#pragma comment(lib, "CascLibDUS.lib") // Debug Unicode CRT-LIB version
|
||||
#pragma comment(lib, "CascLibDUS.lib") // Debug Unicode CRT-LIB version
|
||||
#endif
|
||||
#endif
|
||||
#else // RELEASE VERSIONS
|
||||
#ifndef _UNICODE
|
||||
#ifdef _DLL
|
||||
#pragma comment(lib, "CascLibRAD.lib") // Release Ansi CRT-DLL version
|
||||
#pragma comment(lib, "CascLibRAD.lib") // Release Ansi CRT-DLL version
|
||||
#else
|
||||
#pragma comment(lib, "CascLibRAS.lib") // Release Ansi CRT-LIB version
|
||||
#pragma comment(lib, "CascLibRAS.lib") // Release Ansi CRT-LIB version
|
||||
#endif
|
||||
#else
|
||||
#ifdef _DLL
|
||||
#pragma comment(lib, "CascLibRUD.lib") // Release Unicode CRT-DLL version
|
||||
#pragma comment(lib, "CascLibRUD.lib") // Release Unicode CRT-DLL version
|
||||
#else
|
||||
#pragma comment(lib, "CascLibRUS.lib") // Release Unicode CRT-LIB version
|
||||
#pragma comment(lib, "CascLibRUS.lib") // Release Unicode CRT-LIB version
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
@@ -72,14 +72,14 @@ extern "C" {
|
||||
//-----------------------------------------------------------------------------
|
||||
// Defines
|
||||
|
||||
#define CASCLIB_VERSION 0x0132 // Current version of CascLib (1.50)
|
||||
#define CASCLIB_VERSION_STRING "1.50" // String version of CascLib version
|
||||
#define CASCLIB_VERSION 0x0200 // CascLib version - integral (1.50)
|
||||
#define CASCLIB_VERSION_STRING "2.0" // CascLib version - string
|
||||
|
||||
// Values for CascOpenFile
|
||||
#define CASC_OPEN_BY_NAME 0x00000000 // Open the file by name. This is the default value
|
||||
#define CASC_OPEN_BY_CKEY 0x00000001 // The name is just the content key; skip ROOT file processing
|
||||
#define CASC_OPEN_BY_EKEY 0x00000002 // The name is just the encoded key; skip ROOT file processing
|
||||
#define CASC_OPEN_BY_FILEID 0x00000003 // The name is CASC_IDTONAME(FileDataId)
|
||||
#define CASC_OPEN_BY_FILEID 0x00000003 // The name is CASC_FILE_DATA_ID(FileDataId)
|
||||
#define CASC_OPEN_TYPE_MASK 0x0000000F // The mask which gets open type from the dwFlags
|
||||
#define CASC_OPEN_FLAGS_MASK 0xFFFFFFF0 // The mask which gets open type from the dwFlags
|
||||
#define CASC_STRICT_DATA_CHECK 0x00000010 // Verify all data read from a file
|
||||
@@ -119,16 +119,13 @@ extern "C" {
|
||||
#define MD5_STRING_SIZE 0x20
|
||||
#endif
|
||||
|
||||
#ifndef SHA1_DIGEST_SIZE
|
||||
#define SHA1_DIGEST_SIZE 0x14 // 160 bits
|
||||
#endif
|
||||
|
||||
// Return value for CascGetFileSize and CascSetFilePointer
|
||||
#define CASC_INVALID_INDEX 0xFFFFFFFF
|
||||
#define CASC_INVALID_SIZE 0xFFFFFFFF
|
||||
#define CASC_INVALID_POS 0xFFFFFFFF
|
||||
#define CASC_INVALID_ID 0xFFFFFFFF
|
||||
#define CASC_INVALID_OFFS64 0xFFFFFFFFFFFFFFFF
|
||||
#define CASC_INVALID_SIZE64 0xFFFFFFFFFFFFFFFF
|
||||
|
||||
// Flags for CASC_STORAGE_FEATURES::dwFeatures
|
||||
#define CASC_FEATURE_FILE_NAMES 0x00000001 // File names are supported by the storage
|
||||
@@ -160,7 +157,7 @@ typedef enum _CASC_STORAGE_INFO_CLASS
|
||||
|
||||
|
||||
CascStorageFeatures, // Returns the features flag
|
||||
CascStorageInstalledLocales,
|
||||
CascStorageInstalledLocales, // Not supported
|
||||
CascStorageProduct, // Gives CASC_STORAGE_PRODUCT
|
||||
CascStorageTags, // Gives CASC_STORAGE_TAGS structure
|
||||
CascStoragePathProduct, // Gives Path:Product into a LPTSTR buffer
|
||||
@@ -173,27 +170,10 @@ typedef enum _CASC_FILE_INFO_CLASS
|
||||
CascFileContentKey,
|
||||
CascFileEncodedKey,
|
||||
CascFileFullInfo, // Gives CASC_FILE_FULL_INFO structure
|
||||
CascFileSpanInfo, // Gives CASC_FILE_SPAN_INFO structure for each file span
|
||||
CascFileInfoClassMax
|
||||
} CASC_FILE_INFO_CLASS, *PCASC_FILE_INFO_CLASS;
|
||||
|
||||
// See https://wowdev.wiki/TACT#Products
|
||||
typedef enum _CASC_PRODUCT
|
||||
{
|
||||
UnknownProduct,
|
||||
HeroesOfTheStorm,
|
||||
Diablo3,
|
||||
Overwatch,
|
||||
StarCraft1,
|
||||
StarCraft2,
|
||||
WorldOfWarcraft,
|
||||
WarCraft3,
|
||||
Destiny2,
|
||||
CallOfDutyBlackOps4,
|
||||
Odin,
|
||||
MaxProductValue
|
||||
|
||||
} CASC_PRODUCT, *PCASC_PRODUCT;
|
||||
|
||||
// CascLib may provide a fake name, constructed from file data id, CKey or EKey.
|
||||
// This enum helps to see what name was actually returned
|
||||
// Note that any of these names can be passed to CascOpenFile with no extra flags
|
||||
@@ -221,27 +201,29 @@ typedef struct _CASC_FIND_DATA
|
||||
// Tag mask. Only valid if the storage supports tags, otherwise 0
|
||||
ULONGLONG TagBitMask;
|
||||
|
||||
// Size of the file, as retrieved from CKey entry
|
||||
ULONGLONG FileSize;
|
||||
|
||||
// Plain name of the found file. Pointing inside the 'szFileName' array
|
||||
char * szPlainName;
|
||||
|
||||
// File data ID. Only valid if the storage supports file data IDs, otherwise CASC_INVALID_ID
|
||||
DWORD dwFileDataId;
|
||||
|
||||
// Size of the file, as retrieved from CKey entry or EKey entry
|
||||
DWORD dwFileSize;
|
||||
|
||||
// Locale flags. Only valid if the storage supports locale flags, otherwise CASC_INVALID_ID
|
||||
DWORD dwLocaleFlags;
|
||||
|
||||
// Content flags. Only valid if the storage supports content flags, otherwise CASC_INVALID_ID
|
||||
DWORD dwContentFlags;
|
||||
|
||||
// Hints as for which open method is suitable
|
||||
DWORD bFileAvailable:1; // If true the file is available locally
|
||||
DWORD bCanOpenByName:1;
|
||||
DWORD bCanOpenByDataId:1;
|
||||
DWORD bCanOpenByCKey:1;
|
||||
DWORD bCanOpenByEKey:1;
|
||||
// Span count
|
||||
DWORD dwSpanCount;
|
||||
|
||||
// If true the file is available locally
|
||||
DWORD bFileAvailable:1;
|
||||
|
||||
// Name type in 'szFileName'. In case the file name is not known,
|
||||
// CascLib can put FileDataId-like name or a string representation of CKey/EKey
|
||||
CASC_NAME_TYPE NameType;
|
||||
|
||||
} CASC_FIND_DATA, *PCASC_FIND_DATA;
|
||||
@@ -264,9 +246,8 @@ typedef struct _CASC_STORAGE_TAGS
|
||||
|
||||
typedef struct _CASC_STORAGE_PRODUCT
|
||||
{
|
||||
LPCSTR szProductName;
|
||||
DWORD dwBuildNumber;
|
||||
CASC_PRODUCT Product;
|
||||
char szCodeName[0x1C]; // Code name of the product ("wowt" = "World of Warcraft PTR")
|
||||
DWORD BuildNumber; // Build number. If zero, then CascLib didn't recognize build number
|
||||
|
||||
} CASC_STORAGE_PRODUCT, *PCASC_STORAGE_PRODUCT;
|
||||
|
||||
@@ -279,15 +260,29 @@ typedef struct _CASC_FILE_FULL_INFO
|
||||
ULONGLONG SegmentOffset; // Offset of the file in the segment file ("data.###")
|
||||
ULONGLONG TagBitMask; // Bitmask of tags. Zero if not supported
|
||||
ULONGLONG FileNameHash; // Hash of the file name. Zero if not supported
|
||||
ULONGLONG ContentSize; // Content size of all spans
|
||||
ULONGLONG EncodedSize; // Encoded size of all spans
|
||||
DWORD SegmentIndex; // Index of the segment file (aka 0 = "data.000")
|
||||
DWORD SpanCount; // Number of spans forming the file
|
||||
DWORD FileDataId; // File data ID. CASC_INVALID_ID if not supported.
|
||||
DWORD ContentSize; // Content size of the file
|
||||
DWORD EncodedSize; // Encoded size of the file
|
||||
DWORD LocaleFlags; // Locale flags. CASC_INVALID_ID if not supported.
|
||||
DWORD ContentFlags; // Locale flags. CASC_INVALID_ID if not supported
|
||||
|
||||
} CASC_FILE_FULL_INFO, *PCASC_FILE_FULL_INFO;
|
||||
|
||||
typedef struct _CASC_FILE_SPAN_INFO
|
||||
{
|
||||
BYTE CKey[MD5_HASH_SIZE]; // Content key of the file span
|
||||
BYTE EKey[MD5_HASH_SIZE]; // Encoded key of the file span
|
||||
ULONGLONG StartOffset; // Starting offset of the file span
|
||||
ULONGLONG EndOffset; // Ending offset of the file span
|
||||
DWORD ArchiveIndex; // Index of the archive
|
||||
DWORD ArchiveOffs; // Offset in the archive
|
||||
DWORD HeaderSize; // Size of encoded frame headers
|
||||
DWORD FrameCount; // Number of frames in this span
|
||||
|
||||
} CASC_FILE_SPAN_INFO, *PCASC_FILE_SPAN_INFO;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Extended version of CascOpenStorage
|
||||
|
||||
@@ -348,16 +343,21 @@ bool WINAPI CascOpenStorage(LPCTSTR szParams, DWORD dwLocaleMask, HANDLE * phSt
|
||||
bool WINAPI CascOpenOnlineStorage(LPCTSTR szParams, DWORD dwLocaleMask, HANDLE * phStorage);
|
||||
bool WINAPI CascGetStorageInfo(HANDLE hStorage, CASC_STORAGE_INFO_CLASS InfoClass, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded);
|
||||
bool WINAPI CascAddEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPBYTE Key);
|
||||
bool WINAPI CascAddStringEncryptionKey(HANDLE hStorage, ULONGLONG KeyName, LPCSTR szKey);
|
||||
LPBYTE WINAPI CascFindEncryptionKey(HANDLE hStorage, ULONGLONG KeyName);
|
||||
bool WINAPI CascCloseStorage(HANDLE hStorage);
|
||||
|
||||
bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocaleFlags, DWORD dwOpenFlags, HANDLE * phFile);
|
||||
bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocaleFlags, DWORD dwOpenFlags, HANDLE * PtrFileHandle);
|
||||
bool WINAPI CascOpenLocalFile(LPCTSTR szFileName, DWORD dwOpenFlags, HANDLE * PtrFileHandle);
|
||||
bool WINAPI CascGetFileInfo(HANDLE hFile, CASC_FILE_INFO_CLASS InfoClass, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded);
|
||||
DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh);
|
||||
DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod);
|
||||
bool WINAPI CascGetFileSize64(HANDLE hFile, PULONGLONG PtrFileSize);
|
||||
bool WINAPI CascSetFilePointer64(HANDLE hFile, LONGLONG DistanceToMove, PULONGLONG PtrNewPos, DWORD dwMoveMethod);
|
||||
bool WINAPI CascReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, PDWORD pdwRead);
|
||||
bool WINAPI CascCloseFile(HANDLE hFile);
|
||||
|
||||
DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh);
|
||||
DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * PtrFilePosHigh, DWORD dwMoveMethod);
|
||||
|
||||
HANDLE WINAPI CascFindFirstFile(HANDLE hStorage, LPCSTR szMask, PCASC_FIND_DATA pFindData, LPCTSTR szListFile);
|
||||
bool WINAPI CascFindNextFile(HANDLE hFind, PCASC_FIND_DATA pFindData);
|
||||
bool WINAPI CascFindClose(HANDLE hFind);
|
||||
|
||||
@@ -12,9 +12,172 @@
|
||||
#include "CascLib.h"
|
||||
#include "CascCommon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// TCascFile class functions
|
||||
|
||||
TCascFile::TCascFile(TCascStorage * ahs, PCASC_CKEY_ENTRY apCKeyEntry)
|
||||
{
|
||||
// Reference the storage handle
|
||||
if((hs = ahs) != NULL)
|
||||
hs->AddRef();
|
||||
ClassName = CASC_MAGIC_FILE;
|
||||
|
||||
FilePointer = 0;
|
||||
pCKeyEntry = apCKeyEntry;
|
||||
SpanCount = (pCKeyEntry->SpanCount != 0) ? pCKeyEntry->SpanCount : 1;
|
||||
bVerifyIntegrity = false;
|
||||
bDownloadFileIf = false;
|
||||
bCloseFileStream = false;
|
||||
bFreeCKeyEntries = false;
|
||||
|
||||
// Allocate the array of file spans
|
||||
if((pFileSpan = CASC_ALLOC_ZERO<CASC_FILE_SPAN>(SpanCount)) != NULL)
|
||||
{
|
||||
InitFileSpans(pFileSpan, SpanCount);
|
||||
InitCacheStrategy();
|
||||
}
|
||||
}
|
||||
|
||||
TCascFile::~TCascFile()
|
||||
{
|
||||
// Free all stuff related to file spans
|
||||
if (pFileSpan != NULL)
|
||||
{
|
||||
PCASC_FILE_SPAN pSpanPtr = pFileSpan;
|
||||
|
||||
for(DWORD i = 0; i < SpanCount; i++, pSpanPtr++)
|
||||
{
|
||||
// Close the span file stream if this is a local file
|
||||
if(bCloseFileStream)
|
||||
FileStream_Close(pSpanPtr->pStream);
|
||||
pSpanPtr->pStream = NULL;
|
||||
|
||||
// Free the span frames
|
||||
CASC_FREE(pSpanPtr->pFrames);
|
||||
}
|
||||
|
||||
CASC_FREE(pFileSpan);
|
||||
}
|
||||
|
||||
// Free the CKey entries, if needed
|
||||
if(pCKeyEntry && bFreeCKeyEntries)
|
||||
delete [] pCKeyEntry;
|
||||
pCKeyEntry = NULL;
|
||||
|
||||
// Free the file cache
|
||||
CASC_FREE(pbFileCache);
|
||||
|
||||
// Close (dereference) the archive handle
|
||||
if(hs != NULL)
|
||||
hs = hs->Release();
|
||||
ClassName = 0;
|
||||
}
|
||||
|
||||
DWORD TCascFile::OpenFileSpans(LPCTSTR szSpanList)
|
||||
{
|
||||
TFileStream * pStream;
|
||||
ULONGLONG FileSize = 0;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
for(DWORD i = 0; i < SpanCount; i++)
|
||||
{
|
||||
// Open the file span
|
||||
pFileSpan[i].pStream = pStream = FileStream_OpenFile(szSpanList, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT);
|
||||
if(pFileSpan[i].pStream == NULL)
|
||||
{
|
||||
dwErrCode = GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
// If succeeded, we assign the span to the
|
||||
FileStream_GetSize(pStream, &FileSize);
|
||||
if((FileSize >> 0x1E) != 0)
|
||||
{
|
||||
dwErrCode = ERROR_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
pCKeyEntry[i].EncodedSize = (DWORD)FileSize;
|
||||
}
|
||||
|
||||
// Free the so-far-opened files
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
{
|
||||
for(DWORD i = 0; i < SpanCount; i++)
|
||||
{
|
||||
if(pFileSpan[i].pStream != NULL)
|
||||
FileStream_Close(pFileSpan[i].pStream);
|
||||
pFileSpan[i].pStream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
void TCascFile::InitFileSpans(PCASC_FILE_SPAN pSpans, DWORD dwSpanCount)
|
||||
{
|
||||
ULONGLONG FileOffsetBits = 30;
|
||||
ULONGLONG FileOffsetMask = 0;
|
||||
ULONGLONG FileOffset = 0;
|
||||
|
||||
// Initialize the file sizes. Note that if any of the spans has invalid size,
|
||||
// the entire file size will be set to CASC_INVALID_SIZE64.
|
||||
GetFileSpanInfo(pCKeyEntry, &ContentSize, &EncodedSize);
|
||||
|
||||
// Resolve the file offset bits and file offset mask
|
||||
if(hs != NULL)
|
||||
FileOffsetBits = hs->FileOffsetBits;
|
||||
FileOffsetMask = ((ULONGLONG)1 << FileOffsetBits) - 1;
|
||||
|
||||
// Add all span sizes
|
||||
for(DWORD i = 0; i < dwSpanCount; i++, pSpans++)
|
||||
{
|
||||
// Put the archive index and archive offset
|
||||
pSpans->ArchiveIndex = (DWORD)(pCKeyEntry[i].StorageOffset >> FileOffsetBits);
|
||||
pSpans->ArchiveOffs = (DWORD)(pCKeyEntry[i].StorageOffset & FileOffsetMask);
|
||||
|
||||
// Add to the total encoded size
|
||||
if(ContentSize != CASC_INVALID_SIZE64)
|
||||
{
|
||||
pSpans->StartOffset = FileOffset;
|
||||
FileOffset = FileOffset + pCKeyEntry[i].ContentSize;
|
||||
pSpans->EndOffset = FileOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TCascFile::InitCacheStrategy()
|
||||
{
|
||||
CacheStrategy = CascCacheLastFrame;
|
||||
FileCacheStart = FileCacheEnd = 0;
|
||||
pbFileCache = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static size_t GetSpanFileCount(LPTSTR szSpanList)
|
||||
{
|
||||
LPTSTR szSpanPtr = szSpanList;
|
||||
size_t nSpanCount = 1;
|
||||
|
||||
while(szSpanPtr[0] != 0)
|
||||
{
|
||||
// End of a file?
|
||||
if(szSpanPtr[0] == ';' && szSpanPtr[1] != 0)
|
||||
{
|
||||
szSpanPtr[0] = 0;
|
||||
nSpanCount++;
|
||||
}
|
||||
|
||||
szSpanPtr++;
|
||||
}
|
||||
|
||||
// Place an additional zero to make the list terminated by double EOS
|
||||
szSpanPtr[1] = 0;
|
||||
return nSpanCount;
|
||||
}
|
||||
|
||||
PCASC_CKEY_ENTRY FindCKeyEntry_CKey(TCascStorage * hs, LPBYTE pbCKey, PDWORD PtrIndex)
|
||||
{
|
||||
return (PCASC_CKEY_ENTRY)hs->CKeyMap.FindObject(pbCKey, PtrIndex);
|
||||
@@ -28,7 +191,7 @@ PCASC_CKEY_ENTRY FindCKeyEntry_EKey(TCascStorage * hs, LPBYTE pbEKey, PDWORD Ptr
|
||||
bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD dwOpenFlags, HANDLE * PtrFileHandle)
|
||||
{
|
||||
TCascFile * hf = NULL;
|
||||
int nError = ERROR_FILE_NOT_FOUND;
|
||||
DWORD dwErrCode = ERROR_FILE_NOT_FOUND;
|
||||
|
||||
// If the CKey entry is NULL, we consider the file non-existant
|
||||
if(pCKeyEntry != NULL)
|
||||
@@ -39,11 +202,11 @@ bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD d
|
||||
hf->bVerifyIntegrity = (dwOpenFlags & CASC_STRICT_DATA_CHECK) ? true : false;
|
||||
hf->bDownloadFileIf = (hs->dwFeatures & CASC_FEATURE_ONLINE) ? true : false;
|
||||
hf->bOvercomeEncrypted = (dwOpenFlags & CASC_OVERCOME_ENCRYPTED) ? true : false;
|
||||
nError = ERROR_SUCCESS;
|
||||
dwErrCode = ERROR_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,9 +214,89 @@ bool OpenFileByCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD d
|
||||
PtrFileHandle[0] = (HANDLE)hf;
|
||||
|
||||
// Handle last error
|
||||
if(nError != ERROR_SUCCESS)
|
||||
SetLastError(nError);
|
||||
return (nError == ERROR_SUCCESS);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
SetLastError(dwErrCode);
|
||||
return (dwErrCode == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
bool OpenLocalFile(LPCTSTR szFileName, DWORD dwOpenFlags, HANDLE * PtrFileHandle)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
TCascFile * hf = NULL;
|
||||
LPTSTR szSpanList;
|
||||
size_t nSpanCount;
|
||||
DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Create a copy of the file name. It is actually a file name list,
|
||||
// separated by comma (for supporting multi-span files)
|
||||
if((szSpanList = CascNewStr(szFileName, 1)) != NULL)
|
||||
{
|
||||
// Calculate the span count
|
||||
if((nSpanCount = GetSpanFileCount(szSpanList)) != 0 || nSpanCount > 0xFF)
|
||||
{
|
||||
// Allocate CKey array for the file. Each entry describes one file span
|
||||
if((pCKeyEntry = new CASC_CKEY_ENTRY[nSpanCount]) != NULL)
|
||||
{
|
||||
// Prepare the span count to the first item
|
||||
pCKeyEntry->SpanCount = (BYTE)nSpanCount;
|
||||
|
||||
// Prepare the archive offset in each CKey entry
|
||||
for(size_t i = 0; i < nSpanCount; i++)
|
||||
pCKeyEntry[i].StorageOffset = 0;
|
||||
|
||||
// Create an instance of the TCascFile
|
||||
if((hf = new TCascFile(NULL, pCKeyEntry)) != NULL)
|
||||
{
|
||||
// Prepare the structure
|
||||
hf->bVerifyIntegrity = (dwOpenFlags & CASC_STRICT_DATA_CHECK) ? true : false;
|
||||
hf->bOvercomeEncrypted = (dwOpenFlags & CASC_OVERCOME_ENCRYPTED) ? true : false;
|
||||
hf->bCloseFileStream = true;
|
||||
|
||||
// Open all local file spans
|
||||
dwErrCode = hf->OpenFileSpans(szSpanList);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
{
|
||||
delete hf;
|
||||
hf = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dwErrCode = ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
delete [] szSpanList;
|
||||
}
|
||||
|
||||
// Give the output parameter, no matter what
|
||||
PtrFileHandle[0] = (HANDLE)hf;
|
||||
|
||||
// Handle last error
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
SetLastError(dwErrCode);
|
||||
return (dwErrCode == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
bool SetCacheStrategy(HANDLE hFile, CSTRTG CacheStrategy)
|
||||
{
|
||||
TCascFile * hf;
|
||||
|
||||
// Validate the file handle
|
||||
if((hf = TCascFile::IsValid(hFile)) != NULL)
|
||||
{
|
||||
// The cache must not be initialized yet
|
||||
if(hf->pbFileCache == NULL)
|
||||
{
|
||||
hf->CacheStrategy = CacheStrategy;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Failed. This should never happen
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -66,7 +309,7 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocal
|
||||
const char * szFileName;
|
||||
DWORD FileDataId = CASC_INVALID_ID;
|
||||
BYTE CKeyEKeyBuffer[MD5_HASH_SIZE];
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// This parameter is not used
|
||||
CASCLIB_UNUSED(dwLocaleFlags);
|
||||
@@ -162,7 +405,7 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocal
|
||||
default:
|
||||
|
||||
// Unknown open mode
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
dwErrCode = ERROR_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -170,6 +413,18 @@ bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocal
|
||||
return OpenFileByCKeyEntry(hs, pCKeyEntry, dwOpenFlags, PtrFileHandle);
|
||||
}
|
||||
|
||||
bool WINAPI CascOpenLocalFile(LPCTSTR szFileName, DWORD dwOpenFlags, HANDLE * PtrFileHandle)
|
||||
{
|
||||
// Verify parameters
|
||||
if(szFileName == NULL || szFileName[0] == 0 || PtrFileHandle == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
|
||||
return OpenLocalFile(szFileName, dwOpenFlags, PtrFileHandle);
|
||||
}
|
||||
|
||||
bool WINAPI CascCloseFile(HANDLE hFile)
|
||||
{
|
||||
TCascFile * hf;
|
||||
|
||||
@@ -17,33 +17,29 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local defines
|
||||
|
||||
// Limit for "orphaned" items - those that are in index files, but are not in ENCODING manifest
|
||||
#define CASC_MAX_ORPHANED_ITEMS 0x100
|
||||
// Limit for "additional" items in CKey table
|
||||
#define CASC_MAX_EXTRA_ITEMS 0x40
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// TCascStorage service functions
|
||||
// TCascStorage class functions
|
||||
|
||||
TCascStorage::TCascStorage()
|
||||
{
|
||||
// Prepare the base storage parameters
|
||||
szClassName = "TCascStorage";
|
||||
ClassName = CASC_MAGIC_STORAGE;
|
||||
pRootHandler = NULL;
|
||||
dwDefaultLocale = CASC_LOCALE_ENUS | CASC_LOCALE_ENGB;
|
||||
dwRefCount = 1;
|
||||
|
||||
szRootPath = szDataPath = szIndexPath = szBuildFile = szCdnServers = szCdnPath = szCodeName = NULL;
|
||||
szProductName = NULL;
|
||||
szIndexFormat = NULL;
|
||||
szRegion = NULL;
|
||||
|
||||
memset(DataFiles, 0, sizeof(DataFiles));
|
||||
Product = UnknownProduct;
|
||||
dwBuildNumber = 0;
|
||||
dwFeatures = 0;
|
||||
bAllowOrphans = false;
|
||||
BuildFileType = CascBuildNone;
|
||||
|
||||
LocalFiles = TotalFiles = EKeyEntries = OrphanItems = SkippedItems = EKeyLength = FileOffsetBits = 0;
|
||||
LocalFiles = TotalFiles = EKeyEntries = EKeyLength = FileOffsetBits = 0;
|
||||
}
|
||||
|
||||
TCascStorage::~TCascStorage()
|
||||
@@ -79,32 +75,24 @@ TCascStorage::~TCascStorage()
|
||||
FreeCascBlob(&PatchArchivesKey);
|
||||
FreeCascBlob(&PatchArchivesGroup);
|
||||
FreeCascBlob(&BuildFiles);
|
||||
szClassName = NULL;
|
||||
ClassName = 0;
|
||||
}
|
||||
|
||||
TCascStorage * TCascStorage::AddRef()
|
||||
{
|
||||
dwRefCount++;
|
||||
CascInterlockedIncrement(&dwRefCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
TCascStorage * TCascStorage::Release()
|
||||
{
|
||||
if (dwRefCount == 1)
|
||||
if(CascInterlockedDecrement(&dwRefCount) == 0)
|
||||
{
|
||||
delete this;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dwRefCount--;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TCascStorage * TCascStorage::IsValid(HANDLE hStorage)
|
||||
{
|
||||
TCascStorage * hs = (TCascStorage *)hStorage;
|
||||
|
||||
return (hs != NULL && hs->szClassName != NULL && !strcmp(hs->szClassName, "TCascStorage")) ? hs : NULL;
|
||||
return this;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -139,38 +127,205 @@ static TCHAR * CheckForIndexDirectory(TCascStorage * hs, const TCHAR * szSubDir)
|
||||
return szIndexPath;
|
||||
}
|
||||
|
||||
static int CreateCKeyMaps(TCascStorage * hs, CASC_ENCODING_HEADER & EnHeader)
|
||||
// Inserts an entry from the text build file
|
||||
static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, CASC_CKEY_ENTRY & CKeyEntry)
|
||||
{
|
||||
size_t nEstimatedEntries = (EnHeader.CKeyPageCount * EnHeader.CKeyPageSize) / sizeof(FILE_CKEY_ENTRY);
|
||||
size_t nIxEntries = hs->IndexArray.ItemCount();
|
||||
int nError;
|
||||
PCASC_CKEY_ENTRY pCKeyEntry = NULL;
|
||||
|
||||
// Orphaned items: These are present in INDEX files (by EKey), but missing in the ENCODING manifest.
|
||||
// Probably a bug in generator of "2018 - New CASC\00001", but we want to open the storage nontheless.
|
||||
if(nEstimatedEntries < 0x100)
|
||||
// Skip entries without any key
|
||||
if(CKeyEntry.Flags & (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY))
|
||||
{
|
||||
nEstimatedEntries = nEstimatedEntries + nIxEntries;
|
||||
hs->bAllowOrphans = true;
|
||||
// Check if there is an existing entry
|
||||
if((pCKeyEntry = FindCKeyEntry_CKey(hs, CKeyEntry.CKey)) == NULL)
|
||||
{
|
||||
// Insert a new entry to the array. DO NOT ALLOW enlarge array here
|
||||
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false);
|
||||
if(pCKeyEntry == NULL)
|
||||
return NULL;
|
||||
|
||||
// Fill in the item
|
||||
memcpy(pCKeyEntry, &CKeyEntry, sizeof(CASC_CKEY_ENTRY));
|
||||
|
||||
// If we have CKey present, insert it to the CKey map
|
||||
if(CKeyEntry.Flags & CASC_CE_HAS_CKEY)
|
||||
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
|
||||
|
||||
// If we have EKey present, insert it to the EKey map
|
||||
if(CKeyEntry.Flags & CASC_CE_HAS_EKEY)
|
||||
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
|
||||
pCKeyEntry->ContentSize = CKeyEntry.ContentSize;
|
||||
if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE)
|
||||
pCKeyEntry->EncodedSize = CKeyEntry.EncodedSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow some room for extra entries
|
||||
nEstimatedEntries += CASC_MAX_ORPHANED_ITEMS;
|
||||
return pCKeyEntry;
|
||||
}
|
||||
|
||||
// Inserts an entry from ENCODING
|
||||
static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, PFILE_CKEY_ENTRY pFileEntry)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
|
||||
// Check whether the entry is already there
|
||||
if((pCKeyEntry = FindCKeyEntry_EKey(hs, pFileEntry->EKey)) == NULL)
|
||||
{
|
||||
// Insert a new entry to the array. DO NOT ALLOW enlarge array here
|
||||
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false);
|
||||
if(pCKeyEntry == NULL)
|
||||
return NULL;
|
||||
|
||||
CopyMemory16(pCKeyEntry->CKey, pFileEntry->CKey);
|
||||
CopyMemory16(pCKeyEntry->EKey, pFileEntry->EKey);
|
||||
pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64;
|
||||
pCKeyEntry->TagBitMask = 0;
|
||||
pCKeyEntry->ContentSize = ConvertBytesToInteger_4(pFileEntry->ContentSize);
|
||||
pCKeyEntry->EncodedSize = CASC_INVALID_SIZE;
|
||||
pCKeyEntry->Flags = CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_IN_ENCODING;
|
||||
pCKeyEntry->RefCount = 0;
|
||||
pCKeyEntry->SpanCount = 1;
|
||||
pCKeyEntry->Priority = 0;
|
||||
|
||||
// Insert the item into both maps
|
||||
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
|
||||
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Supply both CKey and EKey. Rewrite EKey regardless, because ENCODING manifest contains a full one
|
||||
CopyMemory16(pCKeyEntry->CKey, pFileEntry->CKey);
|
||||
CopyMemory16(pCKeyEntry->EKey, pFileEntry->EKey);
|
||||
|
||||
// Supply the content size
|
||||
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
|
||||
pCKeyEntry->ContentSize = ConvertBytesToInteger_4(pFileEntry->ContentSize);
|
||||
pCKeyEntry->Flags |= CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_IN_ENCODING;
|
||||
pCKeyEntry->Flags &= ~CASC_CE_HAS_EKEY_PARTIAL;
|
||||
|
||||
// Insert the item into CKey map
|
||||
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
|
||||
}
|
||||
|
||||
return pCKeyEntry;
|
||||
}
|
||||
|
||||
// Inserts an entry from DOWNLOAD
|
||||
static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, CASC_DOWNLOAD_ENTRY & DlEntry)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
|
||||
// Check whether the entry is already there
|
||||
if((pCKeyEntry = FindCKeyEntry_EKey(hs, DlEntry.EKey)) == NULL)
|
||||
{
|
||||
// Insert dummy CKey entry to the array. DO NOT allow to enlarge the array
|
||||
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1, false);
|
||||
if(pCKeyEntry == NULL)
|
||||
return NULL;
|
||||
|
||||
// Copy the entry
|
||||
ZeroMemory16(pCKeyEntry->CKey);
|
||||
CopyMemory16(pCKeyEntry->EKey, DlEntry.EKey);
|
||||
pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64;
|
||||
pCKeyEntry->TagBitMask = 0;
|
||||
pCKeyEntry->ContentSize = CASC_INVALID_SIZE;
|
||||
pCKeyEntry->EncodedSize = (DWORD)DlEntry.EncodedSize;
|
||||
pCKeyEntry->Flags = CASC_CE_HAS_EKEY | CASC_CE_IN_DOWNLOAD;
|
||||
pCKeyEntry->RefCount = 0;
|
||||
pCKeyEntry->SpanCount = 1;
|
||||
|
||||
// Insert the entry to the map. Only insert it to the EKey map, as there is no CKey present
|
||||
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy the EKey if we only have partial one
|
||||
if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY_PARTIAL)
|
||||
CopyMemory16(pCKeyEntry->EKey, DlEntry.EKey);
|
||||
|
||||
// Supply the encoded size, if unknown yet
|
||||
if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE)
|
||||
pCKeyEntry->EncodedSize = (DWORD)DlEntry.EncodedSize;
|
||||
pCKeyEntry->Flags = (pCKeyEntry->Flags & ~CASC_CE_HAS_EKEY_PARTIAL) | CASC_CE_IN_DOWNLOAD;
|
||||
}
|
||||
|
||||
// Supply the rest
|
||||
pCKeyEntry->Priority = DlEntry.Priority;
|
||||
return pCKeyEntry;
|
||||
}
|
||||
|
||||
static DWORD CopyBuildFileItemsToCKeyArray(TCascStorage * hs)
|
||||
{
|
||||
// Insert the well-known files
|
||||
// InsertCKeyEntry(hs, hs->EncodingCKey);
|
||||
InsertCKeyEntry(hs, hs->DownloadCKey);
|
||||
InsertCKeyEntry(hs, hs->InstallCKey);
|
||||
InsertCKeyEntry(hs, hs->PatchFile);
|
||||
InsertCKeyEntry(hs, hs->RootFile);
|
||||
InsertCKeyEntry(hs, hs->SizeFile);
|
||||
InsertCKeyEntry(hs, hs->VfsRoot);
|
||||
|
||||
// Insert all VFS roots
|
||||
for(size_t i = 0; i < hs->VfsRootList.ItemCount(); i++)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i);
|
||||
InsertCKeyEntry(hs, *pCKeyEntry);
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// Estimate the total number of files, so we won't have to re-allocate the array
|
||||
static size_t GetEstimatedNumberOfFiles(TCascStorage * hs)
|
||||
{
|
||||
// If we know the size of DOWNLOAD at this point, we estimate number of files from it.
|
||||
// Size of one entry in DOWNLOAD is at least 26 bytes. This is the most reliable method.
|
||||
// However, for some online storages ("agent"), this is a very small value
|
||||
if(hs->DownloadCKey.ContentSize != CASC_INVALID_SIZE)
|
||||
return (hs->DownloadCKey.ContentSize / 26) + CASC_MAX_EXTRA_ITEMS;
|
||||
|
||||
// If we know the size of ENCODING at this point, we estimate number of files from it.
|
||||
// Size of one entry in ENCODING is at least 38 bytes. This method fails on storages
|
||||
// with TVFS file system, as ENCODING only contains a small subset of file.
|
||||
// Fortunately, all known TVFS-based storages have "download-size" present
|
||||
if(hs->EncodingCKey.ContentSize != CASC_INVALID_SIZE)
|
||||
return (hs->EncodingCKey.ContentSize / 26) + CASC_MAX_EXTRA_ITEMS;
|
||||
|
||||
// By default, it's gonna be half a million, which is the maximum observed number of files
|
||||
// for all older storages (HOTS before 39445, WoW before 19116)
|
||||
return 500000;
|
||||
}
|
||||
|
||||
static DWORD InitCKeyArray(TCascStorage * hs)
|
||||
{
|
||||
size_t nNumberOfFiles = GetEstimatedNumberOfFiles(hs);
|
||||
DWORD dwErrCode;
|
||||
|
||||
//
|
||||
// Allocate array and map of CKey entries
|
||||
//
|
||||
|
||||
// Create the array of CKey items
|
||||
nError = hs->CKeyArray.Create(sizeof(CASC_CKEY_ENTRY), nEstimatedEntries);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = hs->CKeyArray.Create(sizeof(CASC_CKEY_ENTRY), nNumberOfFiles);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Create the map CKey -> CASC_CKEY_ENTRY
|
||||
nError = hs->CKeyMap.Create(nEstimatedEntries, EnHeader.CKeyLength, FIELD_OFFSET(CASC_CKEY_ENTRY, CKey));
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = hs->CKeyMap.Create(nNumberOfFiles, MD5_HASH_SIZE, FIELD_OFFSET(CASC_CKEY_ENTRY, CKey));
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Create the map EKey -> CASC_CKEY_ENTRY
|
||||
nError = hs->EKeyMap.Create(nEstimatedEntries, hs->EKeyLength, FIELD_OFFSET(CASC_CKEY_ENTRY, EKey));
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
// Create the map CKey -> CASC_CKEY_ENTRY. Note that TVFS root references files
|
||||
// using 9-byte EKey, so cut the search EKey length to 9 bytes
|
||||
dwErrCode = hs->EKeyMap.Create(nNumberOfFiles, CASC_EKEY_SIZE, FIELD_OFFSET(CASC_CKEY_ENTRY, EKey));
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Insert the entry of ENCODING file. This is vital for its opening and loading
|
||||
InsertCKeyEntry(hs, hs->EncodingCKey);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -200,7 +355,6 @@ int CaptureEncodingHeader(CASC_ENCODING_HEADER & EnHeader, LPBYTE pbFileData, si
|
||||
|
||||
static int LoadEncodingCKeyPage(TCascStorage * hs, CASC_ENCODING_HEADER & EnHeader, LPBYTE pbPageBegin, LPBYTE pbEndOfPage)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
PFILE_CKEY_ENTRY pFileEntry;
|
||||
LPBYTE pbFileEntry = pbPageBegin;
|
||||
|
||||
@@ -219,26 +373,10 @@ static int LoadEncodingCKeyPage(TCascStorage * hs, CASC_ENCODING_HEADER & EnHead
|
||||
// Example of a file entry with multiple EKeys:
|
||||
// Overwatch build 24919, CKey: 0e 90 94 fa d2 cb 85 ac d0 7c ea 09 f9 c5 ba 00
|
||||
// BREAKIF(pFileEntry->EKeyCount > 1);
|
||||
// BREAK_ON_XKEY3(pFileEntry->EKey, 0x09, 0xF3, 0xCD);
|
||||
|
||||
// Insert the CKey entry into the array
|
||||
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1);
|
||||
if(pCKeyEntry != NULL)
|
||||
{
|
||||
// Supply both CKey and EKey. Rewrite EKey regardless, because ENCODING manifest contains a full one
|
||||
CopyMemory16(pCKeyEntry->CKey, pFileEntry->CKey);
|
||||
CopyMemory16(pCKeyEntry->EKey, pFileEntry->EKey);
|
||||
pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64;
|
||||
pCKeyEntry->TagBitMask = 0;
|
||||
pCKeyEntry->EncodedSize = CASC_INVALID_SIZE;
|
||||
pCKeyEntry->ContentSize = ConvertBytesToInteger_4(pFileEntry->ContentSize);
|
||||
pCKeyEntry->RefCount = 0;
|
||||
pCKeyEntry->Priority = 0;
|
||||
pCKeyEntry->Flags = (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_IN_ENCODING);
|
||||
|
||||
// Insert the item into both maps
|
||||
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
|
||||
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
|
||||
}
|
||||
// Insert the entry to the central CKey table
|
||||
InsertCKeyEntry(hs, pFileEntry);
|
||||
|
||||
// Move to the next encoding entry
|
||||
pbFileEntry = pbFileEntry + 2 + 4 + EnHeader.CKeyLength + (pFileEntry->EKeyCount * EnHeader.EKeyLength);
|
||||
@@ -246,245 +384,88 @@ static int LoadEncodingCKeyPage(TCascStorage * hs, CASC_ENCODING_HEADER & EnHead
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, PCASC_CKEY_ENTRY pSourceEntry, bool bAllowOrphans, bool * pbAllocatedNewEntry)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry = NULL;
|
||||
bool bAllocatedNewEntry = false;
|
||||
|
||||
if(pSourceEntry->Flags & CASC_CE_HAS_EKEY)
|
||||
{
|
||||
// If there is that item already, reuse it
|
||||
pCKeyEntry = FindCKeyEntry_EKey(hs, pSourceEntry->EKey);
|
||||
if(pCKeyEntry == NULL)
|
||||
{
|
||||
// Increment number of orphaned index entries
|
||||
hs->OrphanItems++;
|
||||
|
||||
// Insert the orphan item only of they are allowed and if we won't overflow the array
|
||||
if(bAllowOrphans && (hs->CKeyArray.ItemCount() + 1) < hs->CKeyArray.ItemCountMax())
|
||||
{
|
||||
// Insert a new entry to the array
|
||||
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.Insert(1);
|
||||
if(pCKeyEntry != NULL)
|
||||
{
|
||||
// Copy CKey, EKey and some flags
|
||||
if(pSourceEntry->Flags & CASC_CE_HAS_CKEY)
|
||||
CopyMemory16(pCKeyEntry->CKey, pSourceEntry->CKey);
|
||||
|
||||
if(pSourceEntry->Flags & CASC_CE_HAS_EKEY)
|
||||
CopyMemory16(pCKeyEntry->EKey, pSourceEntry->EKey);
|
||||
|
||||
pCKeyEntry->StorageOffset = CASC_INVALID_OFFS64;
|
||||
pCKeyEntry->TagBitMask = 0;
|
||||
pCKeyEntry->RefCount = 0;
|
||||
pCKeyEntry->Priority = 0;
|
||||
|
||||
pCKeyEntry->EncodedSize = CASC_INVALID_SIZE;
|
||||
pCKeyEntry->ContentSize = CASC_INVALID_SIZE;
|
||||
pCKeyEntry->Flags = (pSourceEntry->Flags & (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_HAS_EKEY_PARTIAL));
|
||||
bAllocatedNewEntry = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hs->SkippedItems++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pbAllocatedNewEntry != NULL)
|
||||
pbAllocatedNewEntry[0] = bAllocatedNewEntry;
|
||||
return pCKeyEntry;
|
||||
}
|
||||
|
||||
static PCASC_CKEY_ENTRY CopyBuildFileItemToCKeyArray(TCascStorage * hs, PCASC_CKEY_ENTRY pSourceEntry)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry = NULL;
|
||||
bool bAllocatedNewEntry = false;
|
||||
|
||||
pCKeyEntry = InsertCKeyEntry(hs, pSourceEntry, true, &bAllocatedNewEntry);
|
||||
if(pCKeyEntry != NULL)
|
||||
{
|
||||
// Fill the values that might be known
|
||||
if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE)
|
||||
pCKeyEntry->EncodedSize = pSourceEntry->EncodedSize;
|
||||
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
|
||||
pCKeyEntry->ContentSize = pSourceEntry->ContentSize;
|
||||
|
||||
// If this is a new entry, we need to insert it to the maps
|
||||
if(bAllocatedNewEntry)
|
||||
{
|
||||
if(pCKeyEntry->Flags & CASC_CE_HAS_CKEY)
|
||||
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
|
||||
if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY)
|
||||
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
|
||||
}
|
||||
}
|
||||
|
||||
return pCKeyEntry;
|
||||
}
|
||||
|
||||
static int CopyBuildFileItemsToCKeyArray(TCascStorage * hs)
|
||||
{
|
||||
// Insert the well-known files
|
||||
CopyBuildFileItemToCKeyArray(hs, &hs->EncodingCKey);
|
||||
CopyBuildFileItemToCKeyArray(hs, &hs->DownloadCKey);
|
||||
CopyBuildFileItemToCKeyArray(hs, &hs->InstallCKey);
|
||||
CopyBuildFileItemToCKeyArray(hs, &hs->PatchFile);
|
||||
CopyBuildFileItemToCKeyArray(hs, &hs->RootFile);
|
||||
CopyBuildFileItemToCKeyArray(hs, &hs->SizeFile);
|
||||
CopyBuildFileItemToCKeyArray(hs, &hs->VfsRoot);
|
||||
|
||||
// Insert all VFS roots
|
||||
for(size_t i = 0; i < hs->VfsRootList.ItemCount(); i++)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i);
|
||||
CopyBuildFileItemToCKeyArray(hs, pCKeyEntry);
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int CopyIndexItemsToCKeyArray(TCascStorage * hs)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pIndexEntry;
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
size_t nItemCount = hs->IndexArray.ItemCount();
|
||||
bool bAllocatedNewEntry = false;
|
||||
|
||||
// Iterate over all index items
|
||||
for(size_t i = 0; i < nItemCount; i++)
|
||||
{
|
||||
// Get the n-th index entry
|
||||
pIndexEntry = (PCASC_CKEY_ENTRY)hs->IndexArray.ItemAt(i);
|
||||
|
||||
// Sometimes, there are multiple items with the same EKey in the index files
|
||||
// Example: "2018 - New CASC\00001", EKey 37 89 16 5b 2d cc 71 c1 25 00 00 00 00 00 00 00
|
||||
// Positions: 0x2D, 0x2E, 0x2F
|
||||
//BREAK_ON_XKEY3(pIndexEntry->EKey, 0x37, 0x89, 0x16);
|
||||
|
||||
// Copy the index entry to the central storage
|
||||
if((pCKeyEntry = InsertCKeyEntry(hs, pIndexEntry, hs->bAllowOrphans, &bAllocatedNewEntry)) != NULL)
|
||||
{
|
||||
// Make sure that the CKey is zeroed when not present
|
||||
if((pCKeyEntry->Flags & CASC_CE_HAS_CKEY) == 0)
|
||||
ZeroMemory16(pCKeyEntry->CKey);
|
||||
|
||||
// Only copy the storage offset and sizes if not available yet
|
||||
if(pCKeyEntry->StorageOffset == CASC_INVALID_OFFS64)
|
||||
{
|
||||
pCKeyEntry->StorageOffset = pIndexEntry->StorageOffset;
|
||||
pCKeyEntry->EncodedSize = pIndexEntry->EncodedSize;
|
||||
}
|
||||
|
||||
if(bAllocatedNewEntry)
|
||||
{
|
||||
if(pCKeyEntry->Flags & CASC_CE_HAS_CKEY)
|
||||
hs->CKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->CKey);
|
||||
if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY)
|
||||
hs->EKeyMap.InsertObject(pCKeyEntry, pCKeyEntry->EKey);
|
||||
}
|
||||
|
||||
// Mark the file as available locally
|
||||
pCKeyEntry->Flags |= CASC_CE_FILE_IS_LOCAL;
|
||||
}
|
||||
}
|
||||
|
||||
// We free the index array at this point
|
||||
hs->IndexArray.Free();
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int LoadEncodingManifest(TCascStorage * hs)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->EncodingCKey.CKey);
|
||||
LPBYTE pbEncodingFile;
|
||||
DWORD cbEncodingFile = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// Inform the user about what we are doing
|
||||
if(InvokeProgressCallback(hs, "Loading ENCODING manifest", NULL, 0, 0))
|
||||
return ERROR_CANCELLED;
|
||||
|
||||
// Load the entire encoding file to memory
|
||||
pbEncodingFile = LoadInternalFileToMemory(hs, &hs->EncodingCKey, &cbEncodingFile);
|
||||
pbEncodingFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbEncodingFile);
|
||||
if(pbEncodingFile != NULL && cbEncodingFile != 0)
|
||||
{
|
||||
CASC_ENCODING_HEADER EnHeader;
|
||||
|
||||
// Capture the header of the ENCODING file
|
||||
nError = CaptureEncodingHeader(EnHeader, pbEncodingFile, cbEncodingFile);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
dwErrCode = CaptureEncodingHeader(EnHeader, pbEncodingFile, cbEncodingFile);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Get the CKey page header and the first page
|
||||
PFILE_CKEY_PAGE pPageHeader = (PFILE_CKEY_PAGE)(pbEncodingFile + sizeof(FILE_ENCODING_HEADER) + EnHeader.ESpecBlockSize);
|
||||
LPBYTE pbCKeyPage = (LPBYTE)(pPageHeader + EnHeader.CKeyPageCount);
|
||||
|
||||
// Since ENCODING contains the full list of all files (even those not downloaded),
|
||||
// we can now make a fair estimate about how large maps shall we create.
|
||||
// So, we can build the maps CKey and EKey map.
|
||||
if((nError = CreateCKeyMaps(hs, EnHeader)) == ERROR_SUCCESS)
|
||||
// Go through all CKey pages and verify them
|
||||
for(DWORD i = 0; i < EnHeader.CKeyPageCount; i++)
|
||||
{
|
||||
// Go through all CKey pages and verify them
|
||||
for(DWORD i = 0; i < EnHeader.CKeyPageCount; i++)
|
||||
// Check if there is enough space in the buffer
|
||||
if((pbCKeyPage + EnHeader.CKeyPageSize) > (pbEncodingFile + cbEncodingFile))
|
||||
{
|
||||
PFILE_CKEY_ENTRY pCKeyEntry = (PFILE_CKEY_ENTRY)pbCKeyPage;
|
||||
|
||||
// Check if there is enough space in the buffer
|
||||
if((pbCKeyPage + EnHeader.CKeyPageSize) > (pbEncodingFile + cbEncodingFile))
|
||||
{
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check the hash of the entire segment
|
||||
// Note that verifying takes considerable time of the storage loading
|
||||
// if(!VerifyDataBlockHash(pbCKeyPage, EnHeader.CKeyPageSize, pEncodingSegment->SegmentHash))
|
||||
// {
|
||||
// nError = ERROR_FILE_CORRUPT;
|
||||
// break;
|
||||
// }
|
||||
|
||||
// Check if the CKey matches with the expected first value
|
||||
if(memcmp(pCKeyEntry->CKey, pPageHeader[i].FirstKey, CASC_CKEY_SIZE))
|
||||
{
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Load the entire page of CKey entries.
|
||||
// This operation will never fail, because all memory is already pre-allocated
|
||||
nError = LoadEncodingCKeyPage(hs, EnHeader, pbCKeyPage, pbCKeyPage + EnHeader.CKeyPageSize);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Move to the next CKey page
|
||||
pbCKeyPage += EnHeader.CKeyPageSize;
|
||||
dwErrCode = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check the hash of the entire segment
|
||||
// Note that verifying takes considerable time of the storage loading
|
||||
// if(!VerifyDataBlockHash(pbCKeyPage, EnHeader.CKeyPageSize, pEncodingSegment->SegmentHash))
|
||||
// {
|
||||
// dwErrCode = ERROR_FILE_CORRUPT;
|
||||
// break;
|
||||
// }
|
||||
|
||||
// Check if the CKey matches with the expected first value
|
||||
if(memcmp(((PFILE_CKEY_ENTRY)pbCKeyPage)->CKey, pPageHeader[i].FirstKey, MD5_HASH_SIZE))
|
||||
{
|
||||
dwErrCode = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Load the entire page of CKey entries.
|
||||
// This operation will never fail, because all memory is already pre-allocated
|
||||
dwErrCode = LoadEncodingCKeyPage(hs, EnHeader, pbCKeyPage, pbCKeyPage + EnHeader.CKeyPageSize);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Move to the next CKey page
|
||||
pbCKeyPage += EnHeader.CKeyPageSize;
|
||||
}
|
||||
}
|
||||
|
||||
// All CKey->EKey entries from the text build files need to be copied to the CKey array
|
||||
// This also includes the ENCODING file itself, which is vital for later loading
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
nError = CopyBuildFileItemsToCKeyArray(hs);
|
||||
dwErrCode = CopyBuildFileItemsToCKeyArray(hs);
|
||||
}
|
||||
|
||||
// Now supply all the entries from the index files
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = CopyIndexItemsToCKeyArray(hs);
|
||||
}
|
||||
//if(dwErrCode == ERROR_SUCCESS)
|
||||
//{
|
||||
// dwErrCode = CopyIndexItemsToCKeyArray(hs);
|
||||
//}
|
||||
|
||||
// Free the loaded ENCODING file
|
||||
CASC_FREE(pbEncodingFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = GetLastError();
|
||||
dwErrCode = GetLastError();
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
size_t GetTagBitmapLength(LPBYTE pbFilePtr, LPBYTE pbFileEnd, DWORD EntryCount)
|
||||
@@ -615,7 +596,7 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
|
||||
LPBYTE pbTag = pbTags;
|
||||
size_t nMaxNameLength = 0;
|
||||
size_t nTagEntryLengh = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// Does the storage support tags?
|
||||
if(DlHeader.TagCount != 0)
|
||||
@@ -624,7 +605,7 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
|
||||
hs->dwFeatures |= CASC_FEATURE_TAGS;
|
||||
|
||||
// Allocate space for the tag array
|
||||
TagArray = CASC_ALLOC(CASC_TAG_ENTRY1, DlHeader.TagCount);
|
||||
TagArray = CASC_ALLOC<CASC_TAG_ENTRY1>(DlHeader.TagCount);
|
||||
if(TagArray != NULL)
|
||||
{
|
||||
// Get the longest tag name
|
||||
@@ -640,8 +621,8 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
|
||||
nTagEntryLengh = ALIGN_TO_SIZE(nTagEntryLengh, 8);
|
||||
|
||||
// Load the tags into array in the storage structure
|
||||
nError = hs->TagsArray.Create(nTagEntryLengh, DlHeader.TagCount);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
dwErrCode = hs->TagsArray.Create(nTagEntryLengh, DlHeader.TagCount);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Convert the array of CASC_DOWNLOAD_TAG1 to array of CASC_DOWNLOAD_TAG2
|
||||
for(DWORD i = 0; i < DlHeader.TagCount; i++)
|
||||
@@ -653,7 +634,7 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
|
||||
pTargetTag = (PCASC_TAG_ENTRY2)hs->TagsArray.Insert(1);
|
||||
if(pTargetTag == NULL)
|
||||
{
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -667,7 +648,7 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -676,20 +657,21 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
|
||||
{
|
||||
CASC_DOWNLOAD_ENTRY DlEntry;
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
ULONGLONG TagBit = 1;
|
||||
size_t BitMaskOffset = (i / 8);
|
||||
size_t TagItemCount = hs->TagsArray.ItemCount();
|
||||
BYTE BitMaskBit = 0x80 >> (i % 8);
|
||||
|
||||
// Capture the download entry
|
||||
if(CaptureDownloadEntry(DlHeader, DlEntry, pbEntry, pbFileEnd) != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Make sure we have the entry in CKey table
|
||||
pCKeyEntry = FindCKeyEntry_EKey(hs, DlEntry.EKey);
|
||||
if(pCKeyEntry != NULL)
|
||||
{
|
||||
ULONGLONG TagBit = 1;
|
||||
size_t TagItemCount = hs->TagsArray.ItemCount();
|
||||
// COD4: zone/base.xpak
|
||||
//BREAK_ON_XKEY3(DlEntry.EKey, 0xa5, 0x00, 0x16);
|
||||
|
||||
// Insert the entry to the central CKey table
|
||||
if((pCKeyEntry = InsertCKeyEntry(hs, DlEntry)) != NULL)
|
||||
{
|
||||
// Supply the tag bits
|
||||
for(size_t j = 0; j < TagItemCount; j++)
|
||||
{
|
||||
@@ -700,17 +682,6 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
|
||||
// Move to the next bit
|
||||
TagBit <<= 1;
|
||||
}
|
||||
|
||||
// If the EKey has partial EKey only, fix that
|
||||
if(pCKeyEntry->Flags & CASC_CE_HAS_EKEY_PARTIAL)
|
||||
{
|
||||
CopyMemory16(pCKeyEntry->EKey, DlEntry.EKey);
|
||||
pCKeyEntry->Flags &= ~CASC_CE_HAS_EKEY_PARTIAL;
|
||||
}
|
||||
|
||||
// Supply the priority
|
||||
pCKeyEntry->Priority = DlEntry.Priority;
|
||||
pCKeyEntry->Flags |= CASC_CE_IN_DOWNLOAD;
|
||||
}
|
||||
|
||||
// Move to the next entry
|
||||
@@ -722,7 +693,7 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
|
||||
|
||||
// Remember the total file count
|
||||
hs->TotalFiles = hs->CKeyArray.ItemCount();
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
static int LoadDownloadManifest(TCascStorage * hs)
|
||||
@@ -730,7 +701,7 @@ static int LoadDownloadManifest(TCascStorage * hs)
|
||||
PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->DownloadCKey.CKey);
|
||||
LPBYTE pbDownloadFile = NULL;
|
||||
DWORD cbDownloadFile = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// Inform the user about what we are doing
|
||||
if(InvokeProgressCallback(hs, "Loading DOWNLOAD manifest", NULL, 0, 0))
|
||||
@@ -743,11 +714,11 @@ static int LoadDownloadManifest(TCascStorage * hs)
|
||||
CASC_DOWNLOAD_HEADER DlHeader;
|
||||
|
||||
// Capture the header of the DOWNLOAD file
|
||||
nError = CaptureDownloadHeader(DlHeader, pbDownloadFile, cbDownloadFile);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
dwErrCode = CaptureDownloadHeader(DlHeader, pbDownloadFile, cbDownloadFile);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Parse the entire download manifest
|
||||
nError = LoadDownloadManifest(hs, DlHeader, pbDownloadFile, pbDownloadFile + cbDownloadFile);
|
||||
dwErrCode = LoadDownloadManifest(hs, DlHeader, pbDownloadFile, pbDownloadFile + cbDownloadFile);
|
||||
}
|
||||
|
||||
// Free the loaded manifest
|
||||
@@ -755,7 +726,7 @@ static int LoadDownloadManifest(TCascStorage * hs)
|
||||
}
|
||||
|
||||
// If the DOWNLOAD manifest is not present, we won't abort the downloading process.
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -767,7 +738,7 @@ static int LoadInstallManifest(TCascStorage * hs)
|
||||
PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->InstallCKey.CKey);
|
||||
LPBYTE pbInstallFile = NULL;
|
||||
DWORD cbInstallFile = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// Inform the user about what we are doing
|
||||
if(InvokeProgressCallback(hs, "Loading INSTALL manifest", NULL, 0, 0))
|
||||
@@ -777,18 +748,18 @@ static int LoadInstallManifest(TCascStorage * hs)
|
||||
pbInstallFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbInstallFile);
|
||||
if (pbInstallFile != NULL && cbInstallFile != 0)
|
||||
{
|
||||
nError = RootHandler_CreateInstall(hs, pbInstallFile, cbInstallFile);
|
||||
dwErrCode = RootHandler_CreateInstall(hs, pbInstallFile, cbInstallFile);
|
||||
CASC_FREE(pbInstallFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = GetLastError();
|
||||
dwErrCode = GetLastError();
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
static bool InsertWellKnownFile(TCascStorage * hs, const char * szFileName, CASC_CKEY_ENTRY & FakeCKeyEntry)
|
||||
static bool InsertWellKnownFile(TCascStorage * hs, const char * szFileName, CASC_CKEY_ENTRY & FakeCKeyEntry, DWORD dwFlags = 0)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry = NULL;
|
||||
|
||||
@@ -799,9 +770,24 @@ static bool InsertWellKnownFile(TCascStorage * hs, const char * szFileName, CASC
|
||||
pCKeyEntry = FindCKeyEntry_CKey(hs, FakeCKeyEntry.CKey);
|
||||
if(pCKeyEntry != NULL)
|
||||
{
|
||||
// Insert the key to the root handler, unless it's already referenced by a name
|
||||
if(pCKeyEntry->RefCount == 0)
|
||||
hs->pRootHandler->Insert(szFileName, pCKeyEntry);
|
||||
// Insert the key to the root handler. Note that the file can already be referenced
|
||||
// ("index" vs "vfs-root" in Warcraft III storages)
|
||||
hs->pRootHandler->Insert(szFileName, pCKeyEntry);
|
||||
pCKeyEntry->Flags |= (CASC_CE_IN_BUILD | dwFlags);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Special case: the PATCH file is usually not in any indices.
|
||||
// It's also never locally available
|
||||
if((dwFlags & CASC_CE_FILE_PATCH) && (hs->dwFeatures & CASC_FEATURE_ONLINE))
|
||||
{
|
||||
// Get or insert the PATCH entry
|
||||
pCKeyEntry = InsertCKeyEntry(hs, FakeCKeyEntry);
|
||||
if(pCKeyEntry != NULL)
|
||||
{
|
||||
hs->pRootHandler->Insert(szFileName, pCKeyEntry);
|
||||
pCKeyEntry->Flags |= (CASC_CE_IN_BUILD | dwFlags);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -815,16 +801,14 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
|
||||
PDWORD FileSignature;
|
||||
LPBYTE pbRootFile = NULL;
|
||||
DWORD cbRootFile = 0;
|
||||
int nError = ERROR_BAD_FORMAT;
|
||||
DWORD dwErrCode = ERROR_BAD_FORMAT;
|
||||
|
||||
// Sanity checks
|
||||
assert(hs->CKeyMap.IsInitialized() == true);
|
||||
assert(hs->pRootHandler == NULL);
|
||||
|
||||
// Locale: The default parameter is 0 - in that case,
|
||||
// we assign the default locale, loaded from the .build.info file
|
||||
if(dwLocaleMask == 0)
|
||||
dwLocaleMask = hs->dwDefaultLocale;
|
||||
// Locale: The default parameter is 0 - in that case, we load all locales
|
||||
dwLocaleMask = (dwLocaleMask != 0) ? dwLocaleMask : 0xFFFFFFFF;
|
||||
|
||||
// Prioritize the VFS root over legacy ROOT file
|
||||
pCKeyEntry = (hs->VfsRoot.ContentSize != CASC_INVALID_SIZE) ? &hs->VfsRoot : &hs->RootFile;
|
||||
@@ -846,19 +830,19 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
|
||||
switch(FileSignature[0])
|
||||
{
|
||||
case CASC_MNDX_ROOT_SIGNATURE:
|
||||
nError = RootHandler_CreateMNDX(hs, pbRootFile, cbRootFile);
|
||||
dwErrCode = RootHandler_CreateMNDX(hs, pbRootFile, cbRootFile);
|
||||
break;
|
||||
|
||||
case CASC_DIABLO3_ROOT_SIGNATURE:
|
||||
nError = RootHandler_CreateDiablo3(hs, pbRootFile, cbRootFile);
|
||||
dwErrCode = RootHandler_CreateDiablo3(hs, pbRootFile, cbRootFile);
|
||||
break;
|
||||
|
||||
case CASC_TVFS_ROOT_SIGNATURE:
|
||||
nError = RootHandler_CreateTVFS(hs, pbRootFile, cbRootFile);
|
||||
dwErrCode = RootHandler_CreateTVFS(hs, pbRootFile, cbRootFile);
|
||||
break;
|
||||
|
||||
case CASC_WOW82_ROOT_SIGNATURE:
|
||||
nError = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask);
|
||||
dwErrCode = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -868,13 +852,13 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
|
||||
// If the format was not recognized, they need to return ERROR_BAD_FORMAT
|
||||
//
|
||||
|
||||
nError = RootHandler_CreateOverwatch(hs, pbRootFile, cbRootFile);
|
||||
if(nError == ERROR_BAD_FORMAT)
|
||||
dwErrCode = RootHandler_CreateOverwatch(hs, pbRootFile, cbRootFile);
|
||||
if(dwErrCode == ERROR_BAD_FORMAT)
|
||||
{
|
||||
nError = RootHandler_CreateStarcraft1(hs, pbRootFile, cbRootFile);
|
||||
if(nError == ERROR_BAD_FORMAT)
|
||||
dwErrCode = RootHandler_CreateStarcraft1(hs, pbRootFile, cbRootFile);
|
||||
if(dwErrCode == ERROR_BAD_FORMAT)
|
||||
{
|
||||
nError = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask);
|
||||
dwErrCode = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -886,10 +870,10 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = GetLastError();
|
||||
dwErrCode = GetLastError();
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
static DWORD GetStorageTotalFileCount(TCascStorage * hs)
|
||||
@@ -902,7 +886,7 @@ static DWORD GetStorageTotalFileCount(TCascStorage * hs)
|
||||
{
|
||||
if((pCKeyEntry = (PCASC_CKEY_ENTRY)hs->CKeyArray.ItemAt(i)) != NULL)
|
||||
{
|
||||
if((pCKeyEntry->Flags & CASC_CE_FOLDER_ENTRY) == 0)
|
||||
if(pCKeyEntry->IsFile())
|
||||
{
|
||||
// If there is zero or one file name reference, we count the item as one file.
|
||||
// If there is more than 1 name reference, we count the file as many times as number of references
|
||||
@@ -925,9 +909,13 @@ static bool GetStorageProduct(TCascStorage * hs, void * pvStorageInfo, size_t cb
|
||||
pProductInfo = (PCASC_STORAGE_PRODUCT)ProbeOutputBuffer(pvStorageInfo, cbStorageInfo, sizeof(CASC_STORAGE_PRODUCT), pcbLengthNeeded);
|
||||
if(pProductInfo != NULL)
|
||||
{
|
||||
pProductInfo->szProductName = hs->szProductName;
|
||||
pProductInfo->dwBuildNumber = hs->dwBuildNumber;
|
||||
pProductInfo->Product = hs->Product;
|
||||
// Clear the entire structure
|
||||
memset(pProductInfo, 0, sizeof(CASC_STORAGE_PRODUCT));
|
||||
|
||||
// Copy the product code name and build number
|
||||
if(hs->szCodeName != NULL)
|
||||
CascStrCopy(pProductInfo->szCodeName, _countof(pProductInfo->szCodeName), hs->szCodeName);
|
||||
pProductInfo->BuildNumber = hs->dwBuildNumber;
|
||||
}
|
||||
|
||||
return (pProductInfo != NULL);
|
||||
@@ -1083,21 +1071,13 @@ static DWORD InitializeLocalDirectories(TCascStorage * hs, PCASC_OPEN_STORAGE_AR
|
||||
|
||||
static DWORD InitializeOnlineDirectories(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
|
||||
{
|
||||
LPCTSTR szLocalCache = pArgs->szLocalPath;
|
||||
LPCTSTR szCodeName = pArgs->szCodeName;
|
||||
|
||||
// Create the root path
|
||||
hs->szRootPath = CombinePath(szLocalCache, szCodeName);
|
||||
hs->szRootPath = CascNewStr(pArgs->szLocalPath);
|
||||
if (hs->szRootPath != NULL)
|
||||
{
|
||||
// Create the name of the build file
|
||||
hs->szBuildFile = CombinePath(hs->szRootPath, _T("versions"));
|
||||
if(hs->szBuildFile != NULL)
|
||||
{
|
||||
hs->BuildFileType = CascVersionsDb;
|
||||
hs->dwFeatures |= CASC_FEATURE_ONLINE;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
hs->BuildFileType = CascVersionsDb;
|
||||
hs->dwFeatures |= CASC_FEATURE_ONLINE;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
@@ -1131,7 +1111,7 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
|
||||
// For online storages, we need to load CDN servers
|
||||
if ((dwErrCode == ERROR_SUCCESS) && (hs->dwFeatures & CASC_FEATURE_ONLINE))
|
||||
{
|
||||
dwErrCode = LoadCdnsInfo(hs);
|
||||
dwErrCode = LoadCdnsFile(hs);
|
||||
}
|
||||
|
||||
// Now, load the main storage file ".build.info" (or ".build.db" in old storages)
|
||||
@@ -1145,6 +1125,8 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
|
||||
if (dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
dwErrCode = LoadCdnConfigFile(hs);
|
||||
if(dwErrCode != ERROR_SUCCESS && (hs->dwFeatures & CASC_FEATURE_ONLINE) == 0)
|
||||
dwErrCode = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// Proceed with loading the CDN build file
|
||||
@@ -1153,6 +1135,12 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
|
||||
dwErrCode = LoadCdnBuildFile(hs);
|
||||
}
|
||||
|
||||
// Create the central file array
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
dwErrCode = InitCKeyArray(hs);
|
||||
}
|
||||
|
||||
// Load the index files. Store information from the index files to the CKeyArray.
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
@@ -1190,7 +1178,7 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
|
||||
InsertWellKnownFile(hs, "ENCODING", hs->EncodingCKey);
|
||||
InsertWellKnownFile(hs, "DOWNLOAD", hs->DownloadCKey);
|
||||
InsertWellKnownFile(hs, "INSTALL", hs->InstallCKey);
|
||||
InsertWellKnownFile(hs, "PATCH", hs->PatchFile);
|
||||
InsertWellKnownFile(hs, "PATCH", hs->PatchFile, CASC_CE_FILE_PATCH);
|
||||
InsertWellKnownFile(hs, "ROOT", hs->RootFile);
|
||||
InsertWellKnownFile(hs, "SIZE", hs->SizeFile);
|
||||
|
||||
@@ -1267,7 +1255,6 @@ static LPTSTR ParseOpenParams(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs)
|
||||
bool WINAPI CascOpenStorageEx(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs, bool bOnlineStorage, HANDLE * phStorage)
|
||||
{
|
||||
CASC_OPEN_STORAGE_ARGS LocalArgs = {sizeof(CASC_OPEN_STORAGE_ARGS)};
|
||||
DWORD (*PfnInitDirs)(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs);
|
||||
TCascStorage * hs;
|
||||
LPTSTR szParamsCopy = NULL;
|
||||
DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||
@@ -1297,8 +1284,7 @@ bool WINAPI CascOpenStorageEx(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs, b
|
||||
if((hs = new TCascStorage()) != NULL)
|
||||
{
|
||||
// Setup the directories
|
||||
PfnInitDirs = (bOnlineStorage) ? InitializeOnlineDirectories : InitializeLocalDirectories;
|
||||
dwErrCode = PfnInitDirs(hs, pArgs);
|
||||
dwErrCode = (bOnlineStorage) ? InitializeOnlineDirectories(hs, pArgs) : InitializeLocalDirectories(hs, pArgs);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Perform the entire storage loading
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <cassert>
|
||||
|
||||
// Support for PowerPC on Max OS X
|
||||
@@ -161,6 +162,8 @@
|
||||
typedef int LONG;
|
||||
typedef unsigned int DWORD;
|
||||
typedef long long LONGLONG;
|
||||
typedef signed long long LONGLONG;
|
||||
typedef signed long long *PLONGLONG;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef unsigned long long *PULONGLONG;
|
||||
typedef void * HANDLE;
|
||||
@@ -303,6 +306,27 @@
|
||||
#define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b))
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Interlocked operations
|
||||
|
||||
inline DWORD CascInterlockedIncrement(PDWORD PtrValue)
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
return (DWORD)InterlockedIncrement((LONG *)(PtrValue));
|
||||
#else
|
||||
return ++PtrValue[0];
|
||||
#endif
|
||||
}
|
||||
|
||||
inline DWORD CascInterlockedDecrement(PDWORD PtrValue)
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
return (DWORD)InterlockedIncrement((LONG *)(PtrValue));
|
||||
#else
|
||||
return --PtrValue[0];
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forbidden functions, do not use
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -195,33 +195,6 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
FreeLoadingStuff();
|
||||
}
|
||||
|
||||
void AppendBackslashToTotalPath(PATH_BUFFER & PathBuffer)
|
||||
{
|
||||
if(PathBuffer.szPtr < PathBuffer.szEnd)
|
||||
{
|
||||
PathBuffer.szPtr[0] = '\\';
|
||||
PathBuffer.szPtr++;
|
||||
}
|
||||
}
|
||||
|
||||
void AppendPathToTotalPath(PATH_BUFFER & PathBuffer, const char * szFileName, const char * szFileEnd)
|
||||
{
|
||||
char * szPathPtr = PathBuffer.szPtr;
|
||||
size_t nLength = (szFileEnd - szFileName);
|
||||
|
||||
// Append the name
|
||||
if((szPathPtr + nLength) < PathBuffer.szEnd)
|
||||
{
|
||||
memcpy(szPathPtr, szFileName, nLength);
|
||||
szPathPtr += nLength;
|
||||
}
|
||||
|
||||
// Append zero terminator
|
||||
if(szPathPtr < PathBuffer.szEnd)
|
||||
szPathPtr[0] = 0;
|
||||
PathBuffer.szPtr = szPathPtr;
|
||||
}
|
||||
|
||||
PDIABLO3_ASSET_INFO GetAssetInfo(DWORD dwAssetIndex)
|
||||
{
|
||||
if(dwAssetIndex < DIABLO3_ASSET_COUNT && Assets[dwAssetIndex].szDirectoryName != NULL)
|
||||
@@ -404,52 +377,28 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
}
|
||||
|
||||
bool CreateAssetFileName(
|
||||
PATH_BUFFER & PathBuffer,
|
||||
CASC_PATH<char> & PathBuffer,
|
||||
DWORD FileIndex,
|
||||
DWORD SubIndex)
|
||||
{
|
||||
PDIABLO3_CORE_TOC_ENTRY pTocEntry;
|
||||
PDIABLO3_ASSET_INFO pAssetInfo;
|
||||
const char * szPackageName = NULL;
|
||||
const char * szPlainName;
|
||||
const char * szFormat;
|
||||
char * szPathEnd = PathBuffer.szEnd;
|
||||
char * szPathPtr = PathBuffer.szPtr;
|
||||
size_t nLength = 0;
|
||||
LPCSTR szPackageName = NULL;
|
||||
LPCSTR szPlainName;
|
||||
LPCSTR szFormat;
|
||||
char szBuffer[MAX_PATH];
|
||||
|
||||
// Find and check the entry
|
||||
pTocEntry = pFileIndices + FileIndex;
|
||||
if(pTocEntry->FileIndex == FileIndex)
|
||||
{
|
||||
// Retrieve the asset information
|
||||
szPlainName = (LPCSTR)(pbCoreTocData + pTocEntry->NameOffset);
|
||||
pAssetInfo = GetAssetInfo(pTocEntry->AssetIndex);
|
||||
|
||||
// Either use the asset info for getting the folder name or supply "Asset##"
|
||||
if(pAssetInfo != NULL)
|
||||
{
|
||||
CascStrCopy(szPathPtr, (szPathEnd - szPathPtr), pAssetInfo->szDirectoryName);
|
||||
szPathPtr += strlen(szPathPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
szPathPtr[0] = 'A';
|
||||
szPathPtr[1] = 's';
|
||||
szPathPtr[2] = 's';
|
||||
szPathPtr[3] = 'e';
|
||||
szPathPtr[4] = 't';
|
||||
szPathPtr[5] = (char)('0' + (pTocEntry->AssetIndex / 10));
|
||||
szPathPtr[6] = (char)('0' + (pTocEntry->AssetIndex % 10));
|
||||
szPathPtr += 7;
|
||||
}
|
||||
|
||||
// Put the backslash
|
||||
if(szPathPtr < PathBuffer.szEnd)
|
||||
*szPathPtr++ = '\\';
|
||||
|
||||
// Construct the file name, up to the extension. Don't include the '.'
|
||||
szPlainName = (const char *)(pbCoreTocData + pTocEntry->NameOffset);
|
||||
szFormat = (SubIndex != CASC_INVALID_INDEX) ? "%s\\%04u" : "%s";
|
||||
nLength = CascStrPrintf(szPathPtr, (szPathEnd - szPathPtr), szFormat, szPlainName, SubIndex);
|
||||
CascStrPrintf(szBuffer, _countof(szBuffer), szFormat, szPlainName, SubIndex);
|
||||
|
||||
// Try to fixup the file extension from the package name.
|
||||
// File extensions are not predictable because for subitems,
|
||||
@@ -466,27 +415,41 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
// We use the Base\Data_D3\PC\Misc\Packages.dat for real file extensions, where possible
|
||||
//
|
||||
|
||||
if(PackagesMap.IsInitialized() && pAssetInfo != NULL)
|
||||
if(pAssetInfo != NULL)
|
||||
{
|
||||
// Retrieve the asset name
|
||||
szPackageName = FindPackageName(pAssetInfo->szDirectoryName, szPathPtr);
|
||||
szPackageName = FindPackageName(pAssetInfo->szDirectoryName, szBuffer);
|
||||
if(szPackageName != NULL)
|
||||
{
|
||||
CascStrCopy(PathBuffer.szPtr, (PathBuffer.szEnd - PathBuffer.szPtr), szPackageName);
|
||||
PathBuffer.AppendString(szPackageName, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Append the directory name
|
||||
PathBuffer.AppendString(pAssetInfo->szDirectoryName, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Append generic name "Asset##" and continue
|
||||
PathBuffer.AppendString("Asset", false);
|
||||
PathBuffer.AppendChar((char)('0' + (pTocEntry->AssetIndex / 10)));
|
||||
PathBuffer.AppendChar((char)('0' + (pTocEntry->AssetIndex % 10)));
|
||||
}
|
||||
|
||||
// Use the extension from the AssetInfo, if we have any
|
||||
// Append the content of the buffer
|
||||
PathBuffer.AppendString(szBuffer, true);
|
||||
|
||||
// If we have an extension, use it. Otherwise, supply "a##"
|
||||
if(pAssetInfo != NULL && pAssetInfo->szExtension != NULL)
|
||||
{
|
||||
szPathPtr[nLength++] = '.';
|
||||
CascStrCopy(szPathPtr + nLength, (szPathEnd - (szPathPtr + nLength)), pAssetInfo->szExtension);
|
||||
return true;
|
||||
PathBuffer.AppendChar('.');
|
||||
PathBuffer.AppendString(pAssetInfo->szExtension, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
CascStrPrintf(szBuffer, _countof(szBuffer), ".a%02u", pTocEntry->AssetIndex);
|
||||
PathBuffer.AppendString(szBuffer, false);
|
||||
}
|
||||
|
||||
// Otherwise, supply "a##"
|
||||
CascStrPrintf(szPathPtr + nLength, (szPathEnd - (szPathPtr + nLength)), ".a%02u", pTocEntry->AssetIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -494,13 +457,14 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
}
|
||||
|
||||
// Parse the asset entries
|
||||
int ParseAssetEntries(
|
||||
DWORD ParseAssetEntries(
|
||||
TCascStorage * hs,
|
||||
DIABLO3_DIRECTORY & Directory,
|
||||
PATH_BUFFER & PathBuffer)
|
||||
CASC_PATH<char> & PathBuffer)
|
||||
{
|
||||
PDIABLO3_ASSET_ENTRY pEntry = (PDIABLO3_ASSET_ENTRY)Directory.pbAssetEntries;
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
size_t nSavePos = PathBuffer.Save();
|
||||
DWORD dwEntries = Directory.dwAssetEntries;
|
||||
|
||||
// Do nothing if there is no entries
|
||||
@@ -516,8 +480,11 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
if(CreateAssetFileName(PathBuffer, pEntry->FileIndex, CASC_INVALID_INDEX))
|
||||
{
|
||||
// Insert the entry to the file tree
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer);
|
||||
}
|
||||
|
||||
// Restore the path buffer position
|
||||
PathBuffer.Restore(nSavePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -525,13 +492,14 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int ParseAssetAndIdxEntries(
|
||||
DWORD ParseAssetAndIdxEntries(
|
||||
TCascStorage * hs,
|
||||
DIABLO3_DIRECTORY & Directory,
|
||||
PATH_BUFFER & PathBuffer)
|
||||
CASC_PATH<char> & PathBuffer)
|
||||
{
|
||||
PDIABLO3_ASSETIDX_ENTRY pEntry = (PDIABLO3_ASSETIDX_ENTRY)Directory.pbAssetIdxEntries;
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
size_t nSavePos = PathBuffer.Save();
|
||||
DWORD dwEntries = Directory.dwAssetIdxEntries;
|
||||
|
||||
// Do nothing if there is no entries
|
||||
@@ -548,8 +516,11 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
{
|
||||
// Insert the entry to the file tree
|
||||
// fprintf(fp, "%08u %04u %s\n", pEntry->FileIndex, pEntry->SubIndex, PathBuffer.szBegin);
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer);
|
||||
}
|
||||
|
||||
// Restore the path buffer position
|
||||
PathBuffer.Restore(nSavePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -558,16 +529,16 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
}
|
||||
|
||||
// Parse the named entries of all folders
|
||||
int ParseDirectory_Phase1(
|
||||
DWORD ParseDirectory_Phase1(
|
||||
TCascStorage * hs,
|
||||
DIABLO3_DIRECTORY & Directory,
|
||||
PATH_BUFFER & PathBuffer,
|
||||
CASC_PATH<char> & PathBuffer,
|
||||
bool bIsRootDirectory)
|
||||
{
|
||||
DIABLO3_NAMED_ENTRY NamedEntry;
|
||||
char * szSavePtr = PathBuffer.szPtr;
|
||||
size_t nFolderIndex = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
size_t nSavePos = PathBuffer.Save();
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// Do nothing if there is no named headers
|
||||
if(Directory.pbNamedEntries && Directory.dwNamedEntries)
|
||||
@@ -587,33 +558,32 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Append the path fragment to the total path
|
||||
AppendPathToTotalPath(PathBuffer, NamedEntry.szFileName, NamedEntry.szFileEnd);
|
||||
PathBuffer.AppendStringN(NamedEntry.szFileName, (NamedEntry.szFileEnd - NamedEntry.szFileName), true);
|
||||
|
||||
// Check whether the file exists in the storage
|
||||
pCKeyEntry = FindCKeyEntry_CKey(hs, NamedEntry.pCKey->Value);
|
||||
if(pCKeyEntry != NULL)
|
||||
{
|
||||
// Create file node belonging to this folder
|
||||
pFileNode = FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
|
||||
pFileNode = FileTree.InsertByName(pCKeyEntry, PathBuffer);
|
||||
dwNodeIndex = (DWORD)FileTree.IndexOf(pFileNode);
|
||||
|
||||
// If we are parsing root folder, we also need to load the data of the sub-folder file
|
||||
if(bIsRootDirectory)
|
||||
{
|
||||
// Mark the node as directory
|
||||
AppendBackslashToTotalPath(PathBuffer);
|
||||
pCKeyEntry->Flags |= CASC_CE_FOLDER_ENTRY;
|
||||
pFileNode->Flags |= CFN_FLAG_FOLDER;
|
||||
|
||||
// Load the sub-directory file
|
||||
nError = LoadDirectoryFile(hs, RootFolders[nFolderIndex], pCKeyEntry);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = LoadDirectoryFile(hs, RootFolders[nFolderIndex], pCKeyEntry);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Parse the sub-directory file
|
||||
nError = ParseDirectory_Phase1(hs, RootFolders[nFolderIndex], PathBuffer, false);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = ParseDirectory_Phase1(hs, RootFolders[nFolderIndex], PathBuffer, false);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Also save the item pointer and increment the folder index
|
||||
RootFolders[nFolderIndex].dwNodeIndex = dwNodeIndex;
|
||||
@@ -621,20 +591,19 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
}
|
||||
|
||||
// Restore the path pointer
|
||||
PathBuffer.szPtr = szSavePtr;
|
||||
szSavePtr[0] = 0;
|
||||
PathBuffer.Restore(nSavePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
// Parse the nameless entries of all folders
|
||||
int ParseDirectory_Phase2(TCascStorage * hs)
|
||||
{
|
||||
PATH_BUFFER PathBuffer;
|
||||
char szPathBuffer[MAX_PATH];
|
||||
CASC_PATH<char> PathBuffer;
|
||||
char szBuffer[MAX_PATH];
|
||||
|
||||
// Parse each root subdirectory
|
||||
for(size_t i = 0; i < DIABLO3_MAX_ROOT_FOLDERS; i++)
|
||||
@@ -642,16 +611,11 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
// Is this root folder loaded?
|
||||
if(RootFolders[i].pbDirectoryData != NULL)
|
||||
{
|
||||
PathBuffer.szBegin = szPathBuffer;
|
||||
PathBuffer.szPtr = szPathBuffer;
|
||||
PathBuffer.szEnd = szPathBuffer + MAX_PATH - 1;
|
||||
szPathBuffer[0] = 0;
|
||||
|
||||
// Retrieve the parent name
|
||||
if(RootFolders[i].dwNodeIndex != 0)
|
||||
{
|
||||
FileTree.PathAt(szPathBuffer, MAX_PATH, RootFolders[i].dwNodeIndex);
|
||||
PathBuffer.szPtr = PathBuffer.szBegin + strlen(szPathBuffer);
|
||||
FileTree.PathAt(szBuffer, _countof(szBuffer), RootFolders[i].dwNodeIndex);
|
||||
PathBuffer.SetPathRoot(szBuffer);
|
||||
}
|
||||
|
||||
// Array of DIABLO3_ASSET_ENTRY entries.
|
||||
@@ -671,12 +635,12 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
|
||||
// Creates an array of DIABLO3_CORE_TOC_ENTRY entries indexed by FileIndex
|
||||
// Used as lookup table when we have FileIndex and need Asset+PlainName
|
||||
int CreateMapOfFileIndices(TCascStorage * hs, const char * szFileName)
|
||||
DWORD CreateMapOfFileIndices(TCascStorage * hs, const char * szFileName)
|
||||
{
|
||||
PDIABLO3_CORE_TOC_HEADER pTocHeader = NULL;
|
||||
LPBYTE pbCoreTocPtr = pbCoreTocFile;
|
||||
DWORD dwMaxFileIndex = 0;
|
||||
int nError = ERROR_CAN_NOT_COMPLETE;
|
||||
DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE;
|
||||
|
||||
// Load the entire file to memory
|
||||
pbCoreTocFile = pbCoreTocPtr = LoadFileToMemory(hs, szFileName, &cbCoreTocFile);
|
||||
@@ -693,7 +657,7 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
// Allocate and populate the array of DIABLO3_CORE_TOC_ENTRYs
|
||||
pFileIndices = CASC_ALLOC(DIABLO3_CORE_TOC_ENTRY, dwMaxFileIndex + 1);
|
||||
pFileIndices = CASC_ALLOC<DIABLO3_CORE_TOC_ENTRY>(dwMaxFileIndex + 1);
|
||||
if(pFileIndices != NULL)
|
||||
{
|
||||
// Initialize all entries to invalid
|
||||
@@ -720,10 +684,10 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
// Save the file to the root handler
|
||||
pbCoreTocData = pbCoreTocPtr;
|
||||
nFileIndices = dwMaxFileIndex;
|
||||
nError = ERROR_SUCCESS;
|
||||
dwErrCode = ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
// Packages.dat contains a list of full file names (without locale prefix).
|
||||
@@ -740,19 +704,7 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
{
|
||||
LPBYTE pbPackagesPtr = pbPackagesDat;
|
||||
LPBYTE pbPackagesEnd = pbPackagesDat + cbPackagesDat;
|
||||
/*
|
||||
LPBYTE pbPackagesPtr = pbPackagesDat + 8;
|
||||
FILE * fp = fopen("E:\\Packages.dat", "wt");
|
||||
if(fp != NULL)
|
||||
{
|
||||
while(pbPackagesPtr < pbPackagesEnd)
|
||||
{
|
||||
fprintf(fp, "%s\n", pbPackagesPtr);
|
||||
pbPackagesPtr = pbPackagesPtr + strlen((char *)pbPackagesPtr) + 1;
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
*/
|
||||
|
||||
// Get the header. There is just Signature + NumberOfNames
|
||||
if((pbPackagesPtr = CaptureInteger32(pbPackagesPtr, pbPackagesEnd, &Signature)) == NULL)
|
||||
return ERROR_BAD_FORMAT;
|
||||
@@ -783,27 +735,20 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int Load(TCascStorage * hs, DIABLO3_DIRECTORY & RootDirectory)
|
||||
DWORD Load(TCascStorage * hs, DIABLO3_DIRECTORY & RootDirectory)
|
||||
{
|
||||
PATH_BUFFER PathBuffer;
|
||||
char szPathBuffer[MAX_PATH];
|
||||
int nError;
|
||||
|
||||
// Initialize path buffer and go parse the directory
|
||||
PathBuffer.szBegin = szPathBuffer;
|
||||
PathBuffer.szPtr = szPathBuffer;
|
||||
PathBuffer.szEnd = szPathBuffer + MAX_PATH;
|
||||
szPathBuffer[0] = 0;
|
||||
CASC_PATH<char> PathBuffer;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Always parse the named entries first. They always point to a file.
|
||||
// These are entries with arbitrary names, and they do not belong to an asset
|
||||
nError = ParseDirectory_Phase1(hs, RootDirectory, PathBuffer, true);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
dwErrCode = ParseDirectory_Phase1(hs, RootDirectory, PathBuffer, true);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// The asset entries in the ROOT file don't contain file names, but indices.
|
||||
// To convert a file index to a file name, we need to load and parse the "Base\\CoreTOC.dat" file.
|
||||
nError = CreateMapOfFileIndices(hs, "Base\\CoreTOC.dat");
|
||||
if(nError == ERROR_SUCCESS)
|
||||
dwErrCode = CreateMapOfFileIndices(hs, "Base\\CoreTOC.dat");
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// The file "Base\Data_D3\PC\Misc\Packages.dat" contains the file names
|
||||
// (without level-0 and level-1 directory).
|
||||
@@ -818,7 +763,7 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
FreeLoadingStuff();
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
void FreeLoadingStuff()
|
||||
@@ -860,11 +805,11 @@ struct TDiabloRoot : public TFileTreeRoot
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
DWORD RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
{
|
||||
TDiabloRoot * pRootHandler = NULL;
|
||||
DIABLO3_DIRECTORY RootDirectory;
|
||||
int nError = ERROR_BAD_FORMAT;
|
||||
DWORD dwErrCode = ERROR_BAD_FORMAT;
|
||||
|
||||
// Verify the header of the ROOT file
|
||||
if(TDiabloRoot::CaptureDirectoryData(RootDirectory, pbRootFile, cbRootFile) != NULL)
|
||||
@@ -874,8 +819,8 @@ int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRoot
|
||||
if(pRootHandler != NULL)
|
||||
{
|
||||
// Load the root directory. If load failed, we free the object
|
||||
nError = pRootHandler->Load(hs, RootDirectory);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
dwErrCode = pRootHandler->Load(hs, RootDirectory);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
{
|
||||
delete pRootHandler;
|
||||
pRootHandler = NULL;
|
||||
@@ -885,5 +830,5 @@ int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRoot
|
||||
|
||||
// Assign the root directory (or NULL) and return error
|
||||
hs->pRootHandler = pRootHandler;
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
@@ -25,30 +25,7 @@ struct TRootHandler_Install : public TFileTreeRoot
|
||||
dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY);
|
||||
}
|
||||
|
||||
static int CaptureInstallHeader(CASC_INSTALL_HEADER & InHeader, LPBYTE pbFileData, size_t cbFileData)
|
||||
{
|
||||
PFILE_INSTALL_HEADER pFileHeader = (PFILE_INSTALL_HEADER)pbFileData;
|
||||
|
||||
// Check the signature ('DL') and version
|
||||
if (cbFileData < sizeof(FILE_INSTALL_HEADER) || pFileHeader->Magic != FILE_MAGIC_INSTALL || pFileHeader->Version != 1)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Note that we don't support CKey sizes greater than 0x10 in the INSTALL file
|
||||
if (pFileHeader->EKeyLength > MD5_HASH_SIZE)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Capture the header version 1
|
||||
memset(&InHeader, 0, sizeof(CASC_INSTALL_HEADER));
|
||||
InHeader.Magic = pFileHeader->Magic;
|
||||
InHeader.Version = pFileHeader->Version;
|
||||
InHeader.EKeyLength = pFileHeader->EKeyLength;
|
||||
InHeader.TagCount = ConvertBytesToInteger_2(pFileHeader->TagCount);
|
||||
InHeader.EntryCount = ConvertBytesToInteger_4(pFileHeader->EntryCount);
|
||||
InHeader.HeaderLength = sizeof(FILE_INSTALL_HEADER);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int Load(TCascStorage * hs, CASC_INSTALL_HEADER InHeader, LPBYTE pbInstallFile, LPBYTE pbInstallEnd)
|
||||
DWORD Load(TCascStorage * hs, CASC_INSTALL_HEADER InHeader, LPBYTE pbInstallFile, LPBYTE pbInstallEnd)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
const char * szString;
|
||||
@@ -90,32 +67,48 @@ struct TRootHandler_Install : public TFileTreeRoot
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
//
|
||||
// Starcraft ROOT file is a text file with the following format:
|
||||
// HD2/portraits/NBluCrit/NLCFID01.webm|c2795b120592355d45eba9cdc37f691e
|
||||
// locales/enUS/Assets/campaign/EXPZerg/Zerg08/staredit/wav/zovtra01.ogg|316b0274bf2dabaa8db60c3ff1270c85
|
||||
// locales/zhCN/Assets/sound/terran/ghost/tghdth01.wav|6637ed776bd22089e083b8b0b2c0374c
|
||||
//
|
||||
DWORD CaptureInstallHeader(CASC_INSTALL_HEADER & InHeader, LPBYTE pbFileData, size_t cbFileData)
|
||||
{
|
||||
PFILE_INSTALL_HEADER pFileHeader = (PFILE_INSTALL_HEADER)pbFileData;
|
||||
|
||||
int RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbInstallFile, DWORD cbInstallFile)
|
||||
// Check the signature ('DL') and version
|
||||
if (cbFileData < sizeof(FILE_INSTALL_HEADER) || pFileHeader->Magic != FILE_MAGIC_INSTALL || pFileHeader->Version != 1)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Note that we don't support CKey sizes greater than 0x10 in the INSTALL file
|
||||
if (pFileHeader->EKeyLength > MD5_HASH_SIZE)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Capture the header version 1
|
||||
memset(&InHeader, 0, sizeof(CASC_INSTALL_HEADER));
|
||||
InHeader.Magic = pFileHeader->Magic;
|
||||
InHeader.Version = pFileHeader->Version;
|
||||
InHeader.EKeyLength = pFileHeader->EKeyLength;
|
||||
InHeader.TagCount = ConvertBytesToInteger_2(pFileHeader->TagCount);
|
||||
InHeader.EntryCount = ConvertBytesToInteger_4(pFileHeader->EntryCount);
|
||||
InHeader.HeaderLength = sizeof(FILE_INSTALL_HEADER);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbInstallFile, DWORD cbInstallFile)
|
||||
{
|
||||
CASC_INSTALL_HEADER InHeader;
|
||||
TRootHandler_Install * pRootHandler = NULL;
|
||||
int nError = ERROR_BAD_FORMAT;
|
||||
DWORD dwErrCode = ERROR_BAD_FORMAT;
|
||||
|
||||
// Capture the header of the DOWNLOAD file
|
||||
nError = TRootHandler_Install::CaptureInstallHeader(InHeader, pbInstallFile, cbInstallFile);
|
||||
if (nError == ERROR_SUCCESS)
|
||||
dwErrCode = CaptureInstallHeader(InHeader, pbInstallFile, cbInstallFile);
|
||||
if (dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Allocate the root handler object
|
||||
pRootHandler = new TRootHandler_Install();
|
||||
if (pRootHandler != NULL)
|
||||
{
|
||||
// Parse the entire install manifest
|
||||
nError = pRootHandler->Load(hs, InHeader, pbInstallFile, pbInstallFile + cbInstallFile);
|
||||
dwErrCode = pRootHandler->Load(hs, InHeader, pbInstallFile, pbInstallFile + cbInstallFile);
|
||||
hs->pRootHandler = pRootHandler;
|
||||
}
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
@@ -368,14 +368,14 @@ class TByteStream
|
||||
|
||||
// HOTS: 1957160 <DWORD>
|
||||
template <typename T>
|
||||
int GetValue(T & Value)
|
||||
DWORD GetValue(T & Value)
|
||||
{
|
||||
T * Pointer;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
nError = GetBytes(sizeof(T), (LPBYTE *)(&Pointer));
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = GetBytes(sizeof(T), (LPBYTE *)(&Pointer));
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
Value = Pointer[0];
|
||||
return ERROR_SUCCESS;
|
||||
@@ -383,15 +383,15 @@ class TByteStream
|
||||
|
||||
// Retrieves the item count in the array
|
||||
template <typename T>
|
||||
int GetArrayItemCount(DWORD & ArraySize, DWORD & ItemCount)
|
||||
DWORD GetArrayItemCount(DWORD & ArraySize, DWORD & ItemCount)
|
||||
{
|
||||
ULONGLONG ByteCount;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// The first 8 bytes is the byte size of the array
|
||||
nError = GetValue<ULONGLONG>(ByteCount);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = GetValue<ULONGLONG>(ByteCount);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Extract the number of bytes
|
||||
if(ByteCount > 0xFFFFFFFF || (ByteCount % sizeof(T)) != 0)
|
||||
@@ -408,9 +408,9 @@ class TByteStream
|
||||
// HOTS: 1957230: <BYTE>
|
||||
// HOTS: 1957280: <HASH_ENTRY>
|
||||
template <typename T>
|
||||
int GetArray(T ** Pointer, size_t ItemCount)
|
||||
DWORD GetArray(T ** Pointer, size_t ItemCount)
|
||||
{
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// Verify parameters
|
||||
if(Pointer == NULL && ItemCount != 0)
|
||||
@@ -421,15 +421,15 @@ class TByteStream
|
||||
// Allocate bytes for the array
|
||||
if (Pointer != NULL)
|
||||
{
|
||||
Pointer[0] = CASC_ALLOC(T, ItemCount);
|
||||
Pointer[0] = CASC_ALLOC<T>(ItemCount);
|
||||
if (Pointer[0] == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Get the pointer to the array
|
||||
nError = CopyBytes(Pointer[0], sizeof(T) * ItemCount);
|
||||
dwErrCode = CopyBytes(Pointer[0], sizeof(T) * ItemCount);
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
LPBYTE pbByteData;
|
||||
@@ -488,7 +488,7 @@ class TGenericArray
|
||||
T * NewArray;
|
||||
|
||||
// Allocate new data buffer
|
||||
NewArray = CASC_ALLOC(T, NewMaxItemCount);
|
||||
NewArray = CASC_ALLOC<T>(NewMaxItemCount);
|
||||
if(NewArray != NULL)
|
||||
{
|
||||
// Copy the old items to the buffer
|
||||
@@ -555,24 +555,24 @@ class TGenericArray
|
||||
// HOTS: 1957700 <HASH_ENTRY>
|
||||
// HOTS: 195A220 <char>
|
||||
// HOTS: 1958580 <TBitStream, DWORD>
|
||||
int LoadFromStream(TByteStream & InStream)
|
||||
DWORD LoadFromStream(TByteStream & InStream)
|
||||
{
|
||||
DWORD NumberOfBytes;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Get and verify the number of items
|
||||
nError = InStream.GetArrayItemCount<T>(NumberOfBytes, ItemCount);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = InStream.GetArrayItemCount<T>(NumberOfBytes, ItemCount);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Get the pointer to the array
|
||||
nError = InStream.GetArray<T>(&ItemArray, ItemCount);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = InStream.GetArray<T>(&ItemArray, ItemCount);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
nError = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = InStream.SkipBytes((0 - (DWORD)NumberOfBytes) & 0x07);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
return SetArrayValid();
|
||||
}
|
||||
@@ -619,28 +619,28 @@ class TBitEntryArray : public TGenericArray<DWORD>
|
||||
return dwResult & EntryBitMask;
|
||||
}
|
||||
|
||||
int LoadBitsFromStream(TByteStream & InStream)
|
||||
DWORD LoadBitsFromStream(TByteStream & InStream)
|
||||
{
|
||||
ULONGLONG Value64 = 0;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
nError = LoadFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = LoadFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
nError = InStream.GetValue<DWORD>(BitsPerEntry);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = InStream.GetValue<DWORD>(BitsPerEntry);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
if(BitsPerEntry > 0x20)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
nError = InStream.GetValue<DWORD>(EntryBitMask);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = InStream.GetValue<DWORD>(EntryBitMask);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
nError = InStream.GetValue<ULONGLONG>(Value64);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = InStream.GetValue<ULONGLONG>(Value64);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
if(Value64 > 0xFFFFFFFF)
|
||||
return ERROR_BAD_FORMAT;
|
||||
TotalEntries = (DWORD)Value64;
|
||||
@@ -687,39 +687,39 @@ class TSparseArray
|
||||
}
|
||||
|
||||
// HOTS: 1958630
|
||||
int LoadFromStream(TByteStream & InStream)
|
||||
DWORD LoadFromStream(TByteStream & InStream)
|
||||
{
|
||||
DWORD total_count = 0;
|
||||
DWORD valid_count = 0;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
nError = ItemBits.LoadFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = ItemBits.LoadFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
nError = InStream.GetValue<DWORD>(total_count);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
nError = InStream.GetValue<DWORD>(valid_count);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = InStream.GetValue<DWORD>(total_count);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
dwErrCode = InStream.GetValue<DWORD>(valid_count);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
if(valid_count > total_count)
|
||||
return ERROR_FILE_CORRUPT;
|
||||
|
||||
TotalItemCount = total_count;
|
||||
ValidItemCount = valid_count;
|
||||
|
||||
nError = BaseVals.LoadFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = BaseVals.LoadFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
nError = IndexToItem0.LoadFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = IndexToItem0.LoadFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
nError = IndexToItem1.LoadFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = IndexToItem1.LoadFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
@@ -1521,13 +1521,13 @@ class TPathFragmentTable
|
||||
}
|
||||
|
||||
// HOTS: 0195A820
|
||||
int LoadFromStream(TByteStream & InStream)
|
||||
DWORD LoadFromStream(TByteStream & InStream)
|
||||
{
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
nError = PathFragments.LoadFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = PathFragments.LoadFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
return PathMarks.LoadFromStream(InStream);
|
||||
}
|
||||
@@ -1606,10 +1606,10 @@ class TStruct10
|
||||
}
|
||||
|
||||
// HOTS: 19572E0
|
||||
int sub_19572E0(DWORD dwBitMask)
|
||||
DWORD sub_19572E0(DWORD dwBitMask)
|
||||
{
|
||||
DWORD dwSubMask;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
if(dwBitMask & 0xFFF00000)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@@ -1618,9 +1618,9 @@ class TStruct10
|
||||
if(dwSubMask)
|
||||
field_0 = dwSubMask;
|
||||
|
||||
nError = sub_1956FD0(dwBitMask);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = sub_1956FD0(dwBitMask);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
dwSubMask = dwBitMask & 0xF000;
|
||||
if(dwSubMask == 0 || dwSubMask == 0x1000)
|
||||
@@ -1718,32 +1718,32 @@ class TFileNameDatabase
|
||||
}
|
||||
|
||||
// HOTS: 1956DA0
|
||||
int Load(LPBYTE pbMarData, size_t cbMarData)
|
||||
DWORD Load(LPBYTE pbMarData, size_t cbMarData)
|
||||
{
|
||||
TByteStream ByteStream;
|
||||
DWORD dwSignature;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
if(pbMarData == NULL && cbMarData != 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
nError = ByteStream.SetByteBuffer(pbMarData, cbMarData);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
dwErrCode = ByteStream.SetByteBuffer(pbMarData, cbMarData);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Get pointer to MAR signature
|
||||
nError = ByteStream.GetValue<DWORD>(dwSignature);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = ByteStream.GetValue<DWORD>(dwSignature);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Verify the signature
|
||||
if(dwSignature != MNDX_MAR_SIGNATURE)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// HOTS: 1956E11
|
||||
nError = LoadFromStream(ByteStream);
|
||||
dwErrCode = LoadFromStream(ByteStream);
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
// HOTS: 19584B0
|
||||
@@ -2398,36 +2398,36 @@ class TFileNameDatabase
|
||||
}
|
||||
|
||||
// HOTS: 1959790
|
||||
int LoadFromStream(TByteStream & InStream)
|
||||
DWORD LoadFromStream(TByteStream & InStream)
|
||||
{
|
||||
DWORD dwBitMask;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
nError = CollisionTable.LoadFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = CollisionTable.LoadFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
nError = FileNameIndexes.LoadFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = FileNameIndexes.LoadFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
nError = CollisionHiBitsIndexes.LoadFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = CollisionHiBitsIndexes.LoadFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// HOTS: 019597CD
|
||||
nError = LoBitsTable.LoadFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = LoBitsTable.LoadFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
nError = HiBitsTable.LoadBitsFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = HiBitsTable.LoadBitsFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// HOTS: 019597F5
|
||||
nError = PathFragmentTable.LoadFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = PathFragmentTable.LoadFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// HOTS: 0195980A
|
||||
if(CollisionHiBitsIndexes.ValidItemCount != 0 && PathFragmentTable.PathFragments.ItemCount == 0)
|
||||
@@ -2438,29 +2438,29 @@ class TFileNameDatabase
|
||||
if (pNewDB == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
nError = SetChildDatabase(pNewDB);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = SetChildDatabase(pNewDB);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
nError = pChildDB->LoadFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = pChildDB->LoadFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
// HOTS: 0195986B
|
||||
nError = HashTable.LoadFromStream(InStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = HashTable.LoadFromStream(InStream);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
HashTableMask = HashTable.ItemCount - 1;
|
||||
|
||||
nError = InStream.GetValue<DWORD>(field_214);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = InStream.GetValue<DWORD>(field_214);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
nError = InStream.GetValue<DWORD>(dwBitMask);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = InStream.GetValue<DWORD>(dwBitMask);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
return Struct10.sub_1957800(dwBitMask);
|
||||
}
|
||||
@@ -2508,7 +2508,7 @@ class TMndxMarFile
|
||||
int LoadRootData(FILE_MAR_INFO & MarInfo, LPBYTE pbRootFile, LPBYTE pbRootEnd)
|
||||
{
|
||||
// Allocate the MAR data
|
||||
pbMarData = CASC_ALLOC(BYTE, MarInfo.MarDataSize);
|
||||
pbMarData = CASC_ALLOC<BYTE>(MarInfo.MarDataSize);
|
||||
cbMarData = MarInfo.MarDataSize;
|
||||
if(pbMarData == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
@@ -2526,29 +2526,29 @@ class TMndxMarFile
|
||||
}
|
||||
|
||||
// HOTS: 1956C60
|
||||
int SearchFile(TMndxSearch * pSearch)
|
||||
DWORD SearchFile(TMndxSearch * pSearch)
|
||||
{
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
if(pDatabase == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if(!pDatabase->FindFileInDatabase(pSearch))
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
dwErrCode = ERROR_FILE_NOT_FOUND;
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
// HOTS: 1956CE0
|
||||
int DoSearch(TMndxSearch * pSearch, bool * pbFindResult)
|
||||
DWORD DoSearch(TMndxSearch * pSearch, bool * pbFindResult)
|
||||
{
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
if(pDatabase == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
*pbFindResult = pDatabase->DoSearch(pSearch);
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
// HOTS: 1956D20
|
||||
@@ -2638,23 +2638,23 @@ struct TMndxHandler
|
||||
return pbRootPtr + sizeof(FILE_MNDX_HEADER);
|
||||
}
|
||||
|
||||
int LoadPackageNames()
|
||||
DWORD LoadPackageNames()
|
||||
{
|
||||
TMndxMarFile * pMarFile = MndxInfo.MarFiles[MAR_PACKAGE_NAMES];
|
||||
TMndxSearch Search;
|
||||
PMNDX_PACKAGE pPackage;
|
||||
size_t nPackageCount = 0x40;
|
||||
bool bFindResult = false;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Prepare the file name search in the top level directory
|
||||
Search.SetSearchMask("", 0);
|
||||
|
||||
// Allocate initial name list structure
|
||||
pMarFile->GetFileNameCount(&nPackageCount);
|
||||
nError = Packages.Create<MNDX_PACKAGE>(nPackageCount);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = Packages.Create<MNDX_PACKAGE>(nPackageCount);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Reset the package array
|
||||
Packages.Reset();
|
||||
@@ -2671,7 +2671,7 @@ struct TMndxHandler
|
||||
assert(pPackage->szFileName == NULL);
|
||||
|
||||
// Allocate space for the file name
|
||||
pPackage->szFileName = CASC_ALLOC(char, Search.cchFoundPath + 1);
|
||||
pPackage->szFileName = CASC_ALLOC<char>(Search.cchFoundPath + 1);
|
||||
if (pPackage->szFileName == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
@@ -2687,13 +2687,13 @@ struct TMndxHandler
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int Load(const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd)
|
||||
DWORD Load(const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd)
|
||||
{
|
||||
TMndxMarFile * pMarFile;
|
||||
FILE_MAR_INFO MarInfo;
|
||||
size_t nFilePointer = 0;
|
||||
DWORD i;
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// Copy the header into the MNDX info
|
||||
MndxInfo.HeaderVersion = MndxHeader.HeaderVersion;
|
||||
@@ -2728,13 +2728,13 @@ struct TMndxHandler
|
||||
pMarFile = new TMndxMarFile();
|
||||
if(pMarFile == NULL)
|
||||
{
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
// Create the database from the MAR data
|
||||
nError = pMarFile->LoadRootData(MarInfo, pbRootFile, pbRootEnd);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
dwErrCode = pMarFile->LoadRootData(MarInfo, pbRootFile, pbRootEnd);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Assign the MAR file to the MNDX info structure
|
||||
@@ -2743,23 +2743,23 @@ struct TMndxHandler
|
||||
|
||||
// All three MAR files must be loaded
|
||||
// HOTS: 00E9503B
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
if(MndxInfo.MarFiles[MAR_PACKAGE_NAMES] == NULL || MndxInfo.MarFiles[MAR_STRIPPED_NAMES] == NULL || MndxInfo.MarFiles[MAR_FULL_NAMES] == NULL)
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
dwErrCode = ERROR_BAD_FORMAT;
|
||||
if(MndxInfo.CKeyEntrySize != sizeof(MNDX_CKEY_ENTRY))
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
dwErrCode = ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
// Load the array of Ckey entries. All present files are in the array,
|
||||
// the same names (differentiated by package ID) are groupped together
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
size_t CKeyEntriesSize;
|
||||
size_t FileNameCount = 0;
|
||||
|
||||
pMarFile = MndxInfo.MarFiles[MAR_STRIPPED_NAMES];
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
dwErrCode = ERROR_FILE_CORRUPT;
|
||||
|
||||
// Capture the array of CKey entries
|
||||
if(pMarFile->GetFileNameCount(&FileNameCount) == ERROR_SUCCESS && FileNameCount == MndxInfo.FileNameCount)
|
||||
@@ -2768,16 +2768,16 @@ struct TMndxHandler
|
||||
if ((pbRootFile + MndxInfo.CKeyEntriesOffset + CKeyEntriesSize) <= pbRootEnd)
|
||||
{
|
||||
pCKeyEntries = (PMNDX_CKEY_ENTRY)(pbRootFile + MndxInfo.CKeyEntriesOffset);
|
||||
nError = ERROR_SUCCESS;
|
||||
dwErrCode = ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pick the CKey entries that are the first with a given name
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
assert(MndxInfo.FileNameCount <= MndxInfo.CKeyEntriesCount);
|
||||
FileNameIndexToCKeyIndex = CASC_ALLOC(PMNDX_CKEY_ENTRY, MndxInfo.FileNameCount + 1);
|
||||
FileNameIndexToCKeyIndex = CASC_ALLOC<PMNDX_CKEY_ENTRY>(MndxInfo.FileNameCount + 1);
|
||||
if(FileNameIndexToCKeyIndex != NULL)
|
||||
{
|
||||
PMNDX_CKEY_ENTRY pRootEntry = pCKeyEntries;
|
||||
@@ -2800,22 +2800,22 @@ struct TMndxHandler
|
||||
|
||||
// Verify the final number of file names
|
||||
if ((nFileNameIndex - 1) != MndxInfo.FileNameCount)
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
dwErrCode = ERROR_BAD_FORMAT;
|
||||
}
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Load the package names from the 0-th MAR file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
nError = LoadPackageNames();
|
||||
dwErrCode = LoadPackageNames();
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
int LoadFileNames(TCascStorage * hs, CASC_FILE_TREE & FileTree)
|
||||
DWORD LoadFileNames(TCascStorage * hs, CASC_FILE_TREE & FileTree)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
PMNDX_CKEY_ENTRY pRootEntry;
|
||||
@@ -2825,13 +2825,13 @@ struct TMndxHandler
|
||||
TMndxSearch Search;
|
||||
char szFileName[MAX_PATH];
|
||||
bool bFindResult = false;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Setup the search mask
|
||||
Search.SetSearchMask("", 0);
|
||||
|
||||
// Keep searching ad long as we found something
|
||||
while ((nError = pMarFile->DoSearch(&Search, &bFindResult)) == ERROR_SUCCESS && bFindResult)
|
||||
while ((dwErrCode = pMarFile->DoSearch(&Search, &bFindResult)) == ERROR_SUCCESS && bFindResult)
|
||||
{
|
||||
// Sanity check
|
||||
assert(Search.cchFoundPath < MAX_PATH);
|
||||
@@ -2874,7 +2874,7 @@ struct TMndxHandler
|
||||
}
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -2931,32 +2931,32 @@ struct TRootHandler_MNDX : public TFileTreeRoot
|
||||
dwFeatures |= CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY;
|
||||
}
|
||||
|
||||
int Load(TCascStorage * hs, const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd)
|
||||
DWORD Load(TCascStorage * hs, const FILE_MNDX_HEADER & MndxHeader, LPBYTE pbRootFile, LPBYTE pbRootEnd)
|
||||
{
|
||||
TMndxHandler Handler;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Load and parse the entire MNDX structure
|
||||
nError = Handler.Load(MndxHeader, pbRootFile, pbRootEnd);
|
||||
if (nError == ERROR_SUCCESS)
|
||||
dwErrCode = Handler.Load(MndxHeader, pbRootFile, pbRootEnd);
|
||||
if (dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Search all file names and insert them into the file tree
|
||||
nError = Handler.LoadFileNames(hs, FileTree);
|
||||
dwErrCode = Handler.LoadFileNames(hs, FileTree);
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions - MNDX info
|
||||
|
||||
int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
DWORD RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
{
|
||||
TRootHandler_MNDX * pRootHandler = NULL;
|
||||
FILE_MNDX_HEADER MndxHeader;
|
||||
LPBYTE pbRootEnd = pbRootFile + cbRootFile;
|
||||
int nError = ERROR_BAD_FORMAT;
|
||||
DWORD dwErrCode = ERROR_BAD_FORMAT;
|
||||
|
||||
// Verify the header of the ROOT file
|
||||
if(TMndxHandler::CaptureRootHeader(MndxHeader, pbRootFile, pbRootEnd) != NULL)
|
||||
@@ -2966,8 +2966,8 @@ int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFil
|
||||
if(pRootHandler != NULL)
|
||||
{
|
||||
// Load the root directory. If load failed, we free the object
|
||||
nError = pRootHandler->Load(hs, MndxHeader, pbRootFile, pbRootEnd);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
dwErrCode = pRootHandler->Load(hs, MndxHeader, pbRootFile, pbRootEnd);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
{
|
||||
delete pRootHandler;
|
||||
pRootHandler = NULL;
|
||||
@@ -2977,5 +2977,5 @@ int RootHandler_CreateMNDX(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFil
|
||||
|
||||
// Assign the root directory (or NULL) and return error
|
||||
hs->pRootHandler = pRootHandler;
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ struct TApmFile
|
||||
LPBYTE CaptureArrayOfEntries(LPBYTE pbArrayOfEntries, LPBYTE pbApmEnd)
|
||||
{
|
||||
// Allocate array of entries
|
||||
pApmEntries = CASC_ALLOC(APM_ENTRY, EntryCount);
|
||||
pApmEntries = CASC_ALLOC<APM_ENTRY>(EntryCount);
|
||||
if(pApmEntries != NULL)
|
||||
{
|
||||
// The newest format
|
||||
@@ -310,12 +310,9 @@ struct TApmFile
|
||||
LPBYTE CapturePackageEntries(LPBYTE pbArrayOfEntries, LPBYTE pbApmEnd)
|
||||
{
|
||||
// Allocate array of entries
|
||||
pApmPackages = CASC_ALLOC(APM_PACKAGE_ENTRY, PackageCount);
|
||||
pApmPackages = CASC_ALLOC_ZERO<APM_PACKAGE_ENTRY>(PackageCount);
|
||||
if(pApmPackages != NULL)
|
||||
{
|
||||
// Zero the entire array
|
||||
memset(pApmPackages, 0, PackageCount * sizeof(APM_PACKAGE_ENTRY));
|
||||
|
||||
// The newest format
|
||||
if(BuildNumber > 45104 && BuildNumber != 45214)
|
||||
{
|
||||
@@ -426,12 +423,12 @@ struct TRootHandler_OW : public TFileTreeRoot
|
||||
return false;
|
||||
}
|
||||
|
||||
int LoadApmFile(TCascStorage * hs, CONTENT_KEY & CKey, const char * szFileName)
|
||||
DWORD LoadApmFile(TCascStorage * hs, CONTENT_KEY & CKey, const char * szFileName)
|
||||
{
|
||||
TApmFile ApmFile;
|
||||
LPBYTE pbApmData;
|
||||
DWORD cbApmData = 0;
|
||||
int nError = ERROR_BAD_FORMAT;
|
||||
DWORD dwErrCode = ERROR_BAD_FORMAT;
|
||||
|
||||
pbApmData = LoadInternalFileToMemory(hs, CKey.Value, CASC_OPEN_BY_CKEY, &cbApmData);
|
||||
if(pbApmData != NULL)
|
||||
@@ -456,16 +453,15 @@ struct TRootHandler_OW : public TFileTreeRoot
|
||||
CASC_FREE(pbApmData);
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
static int LoadCmfFile(TCascStorage * hs, CONTENT_KEY & CKey, const char * szFileName)
|
||||
static DWORD LoadCmfFile(TCascStorage * hs, CONTENT_KEY & CKey, const char * szFileName)
|
||||
{
|
||||
TCmfFile CmfFile;
|
||||
LPBYTE pbCmfData;
|
||||
DWORD cbCmfData = 0;
|
||||
|
||||
int nError = ERROR_BAD_FORMAT;
|
||||
DWORD dwErrCode = ERROR_BAD_FORMAT;
|
||||
|
||||
pbCmfData = LoadInternalFileToMemory(hs, CKey.Value, CASC_OPEN_BY_CKEY, &cbCmfData);
|
||||
if(pbCmfData != NULL)
|
||||
@@ -484,7 +480,7 @@ struct TRootHandler_OW : public TFileTreeRoot
|
||||
CASC_FREE(pbCmfData);
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
*/
|
||||
int Load(TCascStorage * hs, CASC_CSV & Csv, size_t nFileNameIndex, size_t nCKeyIndex)
|
||||
@@ -525,7 +521,7 @@ struct TRootHandler_OW : public TFileTreeRoot
|
||||
}
|
||||
/*
|
||||
// Load all CMF+APM files
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
for(size_t i = 0; i < nApmFiles; i++)
|
||||
{
|
||||
@@ -548,8 +544,8 @@ struct TRootHandler_OW : public TFileTreeRoot
|
||||
break;
|
||||
|
||||
// Create the map of CMF entries
|
||||
nError = LoadCmfFile(hs, pFileNode2->CKey, szCmfFile);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
dwErrCode = LoadCmfFile(hs, pFileNode2->CKey, szCmfFile);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
}
|
||||
@@ -563,16 +559,16 @@ struct TRootHandler_OW : public TFileTreeRoot
|
||||
// Public functions
|
||||
|
||||
// TODO: There is way more files in the Overwatch CASC storage than present in the ROOT file.
|
||||
int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
DWORD RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
{
|
||||
TRootHandler_OW * pRootHandler = NULL;
|
||||
CASC_CSV Csv(0, true);
|
||||
size_t Indices[2];
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Load the ROOT file
|
||||
nError = Csv.Load(pbRootFile, cbRootFile);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
dwErrCode = Csv.Load(pbRootFile, cbRootFile);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Retrieve the indices of the file name and MD5 columns
|
||||
Indices[0] = Csv.GetColumnIndex("FILENAME");
|
||||
@@ -585,8 +581,8 @@ int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRo
|
||||
if (pRootHandler != NULL)
|
||||
{
|
||||
// Load the root directory. If load failed, we free the object
|
||||
nError = pRootHandler->Load(hs, Csv, Indices[0], Indices[1]);
|
||||
if (nError != ERROR_SUCCESS)
|
||||
dwErrCode = pRootHandler->Load(hs, Csv, Indices[0], Indices[1]);
|
||||
if (dwErrCode != ERROR_SUCCESS)
|
||||
{
|
||||
delete pRootHandler;
|
||||
pRootHandler = NULL;
|
||||
@@ -595,11 +591,11 @@ int RootHandler_CreateOverwatch(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRo
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
dwErrCode = ERROR_BAD_FORMAT;
|
||||
}
|
||||
}
|
||||
|
||||
// Assign the root directory (or NULL) and return error
|
||||
hs->pRootHandler = pRootHandler;
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
@@ -98,19 +98,6 @@ typedef struct _TVFS_PATH_TABLE_ENTRY
|
||||
DWORD NodeValue; // Node value
|
||||
} TVFS_PATH_TABLE_ENTRY, *PTVFS_PATH_TABLE_ENTRY;
|
||||
|
||||
// In-memory layout of VFS span entry
|
||||
typedef struct _TVFS_SPAN_ENTRY
|
||||
{
|
||||
LPBYTE pbCftFileTable;
|
||||
LPBYTE pbCftFileEntry;
|
||||
LPBYTE pbCftFileEnd;
|
||||
|
||||
DWORD dwFileOffset; // Offset into the referenced file
|
||||
DWORD dwSpanSize; // Size of the span
|
||||
DWORD dwCftOffset; // Offset relative to the beginning of the container file table
|
||||
|
||||
} TVFS_SPAN_ENTRY, *PTVFS_SPAN_ENTRY;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Handler definition for TVFS root file
|
||||
|
||||
@@ -123,10 +110,9 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
{
|
||||
// TVFS supports file names, but DOESN'T support CKeys.
|
||||
dwFeatures |= CASC_FEATURE_FILE_NAMES;
|
||||
dwNestLevel = 0;
|
||||
}
|
||||
|
||||
// Returns size of "container file table offset" fiels in the VFS.
|
||||
// Returns size of "container file table offset" field in the VFS.
|
||||
// - If the container file table is larger than 0xffffff bytes, it's 4 bytes
|
||||
// - If the container file table is larger than 0xffff bytes, it's 3 bytes
|
||||
// - If the container file table is larger than 0xff bytes, it's 2 bytes
|
||||
@@ -142,50 +128,24 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool PathBuffer_AddChar(PATH_BUFFER & PathBuffer, char chOneChar)
|
||||
{
|
||||
if(PathBuffer.szPtr >= PathBuffer.szEnd)
|
||||
return false;
|
||||
|
||||
*PathBuffer.szPtr++ = chOneChar;
|
||||
*PathBuffer.szPtr = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PathBuffer_AddString(PATH_BUFFER & PathBuffer, LPBYTE pbNamePtr, LPBYTE pbNameEnd)
|
||||
{
|
||||
size_t nLength = (pbNameEnd - pbNamePtr);
|
||||
|
||||
// Check whether we have enough space
|
||||
if ((PathBuffer.szPtr + nLength) > PathBuffer.szEnd)
|
||||
return false;
|
||||
|
||||
// Copy the node name
|
||||
memcpy(PathBuffer.szPtr, pbNamePtr, nLength);
|
||||
PathBuffer.szPtr += nLength;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PathBuffer_AppendNode(PATH_BUFFER & PathBuffer, TVFS_PATH_TABLE_ENTRY & PathEntry)
|
||||
bool PathBuffer_AppendNode(CASC_PATH<char> & PathBuffer, TVFS_PATH_TABLE_ENTRY & PathEntry)
|
||||
{
|
||||
// Append the prefix separator, if needed
|
||||
if (PathEntry.NodeFlags & TVFS_PTE_PATH_SEPARATOR_PRE)
|
||||
PathBuffer_AddChar(PathBuffer, '/');
|
||||
PathBuffer.AppendChar('/');
|
||||
|
||||
// Append the name fragment, if any
|
||||
if (PathEntry.pbNameEnd > PathEntry.pbNamePtr)
|
||||
PathBuffer_AddString(PathBuffer, PathEntry.pbNamePtr, PathEntry.pbNameEnd);
|
||||
PathBuffer.AppendStringN((const char *)PathEntry.pbNamePtr, (PathEntry.pbNameEnd - PathEntry.pbNamePtr), false);
|
||||
|
||||
// Append the postfix separator, if needed
|
||||
if (PathEntry.NodeFlags & TVFS_PTE_PATH_SEPARATOR_POST)
|
||||
PathBuffer_AddChar(PathBuffer, '/');
|
||||
PathBuffer.AppendChar('/');
|
||||
|
||||
// Always end the buffer with zero
|
||||
PathBuffer.szPtr[0] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int CaptureDirectoryHeader(TVFS_DIRECTORY_HEADER & DirHeader, LPBYTE pbDataPtr, LPBYTE pbDataEnd)
|
||||
static DWORD CaptureDirectoryHeader(TVFS_DIRECTORY_HEADER & DirHeader, LPBYTE pbDataPtr, LPBYTE pbDataEnd)
|
||||
{
|
||||
// Fill the header structure with zeros
|
||||
memset(&DirHeader, 0, sizeof(TVFS_DIRECTORY_HEADER));
|
||||
@@ -262,32 +222,50 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
return (1 <= SpanCount && SpanCount <= 224) ? pbVfsFileEntry : NULL;
|
||||
}
|
||||
|
||||
LPBYTE CaptureVfsSpanEntry(TVFS_DIRECTORY_HEADER & DirHeader, LPBYTE pbVfsSpanEntry, TVFS_SPAN_ENTRY & SpanEntry)
|
||||
LPBYTE CaptureVfsSpanEntries(TVFS_DIRECTORY_HEADER & DirHeader, LPBYTE pbVfsSpanEntry, PCASC_CKEY_ENTRY PtrSpanEntry, size_t SpanCount)
|
||||
{
|
||||
LPBYTE pbCftFileTable;
|
||||
LPBYTE pbCftFileEntry;
|
||||
LPBYTE pbCftFileEnd;
|
||||
LPBYTE pbVfsFileTable = DirHeader.pbDirectoryData + DirHeader.VfsTableOffset;
|
||||
LPBYTE pbVfsFileEnd = pbVfsFileTable + DirHeader.VfsTableSize;
|
||||
size_t ItemSize = sizeof(DWORD) + sizeof(DWORD) + DirHeader.CftOffsSize;
|
||||
|
||||
// Check the range being captured
|
||||
if(pbVfsSpanEntry < pbVfsFileTable || (pbVfsSpanEntry + ItemSize) > pbVfsFileEnd)
|
||||
// Check whether all spans are included in the valid range
|
||||
if(pbVfsSpanEntry < pbVfsFileTable || (pbVfsSpanEntry + (ItemSize * SpanCount)) > pbVfsFileEnd)
|
||||
return NULL;
|
||||
|
||||
//
|
||||
// Structure of the span entry:
|
||||
// (4bytes): Offset into the referenced file (big endian)
|
||||
// (4bytes): Size of the span (big endian)
|
||||
// (?bytes): Offset into Container File Table. Length depends on container file table size
|
||||
//
|
||||
// Convert all spans
|
||||
for(size_t i = 0; i < SpanCount; i++)
|
||||
{
|
||||
DWORD dwCftOffset = ConvertBytesToInteger_X(pbVfsSpanEntry + sizeof(DWORD) + sizeof(DWORD), DirHeader.CftOffsSize);
|
||||
|
||||
SpanEntry.dwFileOffset = ConvertBytesToInteger_4(pbVfsSpanEntry);
|
||||
SpanEntry.dwSpanSize = ConvertBytesToInteger_4(pbVfsSpanEntry + sizeof(DWORD));
|
||||
SpanEntry.dwCftOffset = ConvertBytesToInteger_X(pbVfsSpanEntry + sizeof(DWORD) + sizeof(DWORD), DirHeader.CftOffsSize);
|
||||
//
|
||||
// Structure of the span entry:
|
||||
// (4bytes): Offset into the referenced file (big endian)
|
||||
// (4bytes): Size of the span (big endian)
|
||||
// (?bytes): Offset into Container File Table. Length depends on container file table size
|
||||
//
|
||||
|
||||
// Resolve the Container File Table entries
|
||||
SpanEntry.pbCftFileTable = DirHeader.pbDirectoryData + DirHeader.CftTableOffset;
|
||||
SpanEntry.pbCftFileEntry = SpanEntry.pbCftFileTable + SpanEntry.dwCftOffset;
|
||||
SpanEntry.pbCftFileEnd = SpanEntry.pbCftFileTable + DirHeader.CftTableSize;
|
||||
return pbVfsSpanEntry + ItemSize;
|
||||
// Resolve the Container File Table entry
|
||||
pbCftFileTable = DirHeader.pbDirectoryData + DirHeader.CftTableOffset;
|
||||
pbCftFileEntry = pbCftFileTable + dwCftOffset;
|
||||
pbCftFileEnd = pbCftFileTable + DirHeader.CftTableSize;
|
||||
|
||||
// Capture the EKey and the file size
|
||||
if((pbCftFileEntry + DirHeader.EKeySize + sizeof(DWORD)) > pbCftFileEnd)
|
||||
return NULL;
|
||||
|
||||
// Copy the EKey and content size
|
||||
CaptureEncodedKey(PtrSpanEntry->EKey, pbCftFileEntry, DirHeader.EKeySize);
|
||||
PtrSpanEntry->ContentSize = ConvertBytesToInteger_4(pbVfsSpanEntry + sizeof(DWORD));
|
||||
|
||||
// Move to the next entry
|
||||
pbVfsSpanEntry += ItemSize;
|
||||
PtrSpanEntry++;
|
||||
}
|
||||
|
||||
return pbVfsSpanEntry;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -365,7 +343,7 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
return pbPathTablePtr;
|
||||
}
|
||||
|
||||
bool IsVfsFileEKey(TCascStorage * hs, ENCODED_KEY & EKey, size_t EKeyLength)
|
||||
bool IsVfsFileEKey(TCascStorage * hs, LPBYTE EKey, size_t EKeyLength)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
size_t ItemCount = hs->VfsRootList.ItemCount();
|
||||
@@ -376,7 +354,7 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
pCKeyEntry = (PCASC_CKEY_ENTRY)hs->VfsRootList.ItemAt(i);
|
||||
if (pCKeyEntry != NULL)
|
||||
{
|
||||
if (!memcmp(pCKeyEntry->EKey, EKey.Value, EKeyLength))
|
||||
if (!memcmp(pCKeyEntry->EKey, EKey, EKeyLength))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -387,27 +365,27 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
|
||||
// This function verifies whether a file is actually a sub-directory.
|
||||
// If yes, it contains just another "TVFS" virtual file system, just like the ROOT file.
|
||||
int IsVfsSubDirectory(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, TVFS_DIRECTORY_HEADER & SubHeader, ENCODED_KEY & EKey, DWORD dwFileSize)
|
||||
DWORD IsVfsSubDirectory(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, TVFS_DIRECTORY_HEADER & SubHeader, LPBYTE EKey, DWORD dwFileSize)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
LPBYTE pbVfsData = NULL;
|
||||
DWORD cbVfsData = dwFileSize;
|
||||
int nError = ERROR_BAD_FORMAT;
|
||||
DWORD dwErrCode = ERROR_BAD_FORMAT;
|
||||
|
||||
// Verify whether the EKey is in the list of VFS root files
|
||||
if(IsVfsFileEKey(hs, EKey, DirHeader.EKeySize))
|
||||
{
|
||||
// Locate the CKey entry
|
||||
if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey.Value)) != NULL)
|
||||
if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey)) != NULL)
|
||||
{
|
||||
// Load the entire file into memory
|
||||
pbVfsData = LoadInternalFileToMemory(hs, pCKeyEntry, &cbVfsData);
|
||||
if (pbVfsData && cbVfsData)
|
||||
{
|
||||
// Capture the file folder. This also serves as test
|
||||
nError = CaptureDirectoryHeader(SubHeader, pbVfsData, pbVfsData + cbVfsData);
|
||||
if (nError == ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = CaptureDirectoryHeader(SubHeader, pbVfsData, pbVfsData + cbVfsData);
|
||||
if (dwErrCode == ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Clear the captured header
|
||||
memset(&SubHeader, 0, sizeof(TVFS_DIRECTORY_HEADER));
|
||||
@@ -416,7 +394,7 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
}
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
void InsertRootVfsEntry(TCascStorage * hs, LPBYTE pbCKey, const char * szFormat, size_t nIndex)
|
||||
@@ -432,15 +410,18 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
}
|
||||
}
|
||||
|
||||
DWORD ParsePathFileTable(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, PATH_BUFFER & PathBuffer, LPBYTE pbPathTablePtr, LPBYTE pbPathTableEnd)
|
||||
DWORD ParsePathFileTable(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, CASC_PATH<char> & PathBuffer, LPBYTE pbPathTablePtr, LPBYTE pbPathTableEnd)
|
||||
{
|
||||
TVFS_DIRECTORY_HEADER SubHeader;
|
||||
TVFS_PATH_TABLE_ENTRY PathEntry;
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
LPBYTE pbVfsSpanEntry;
|
||||
char * szSavePathPtr = PathBuffer.szPtr;
|
||||
DWORD dwSpanCount = 0;
|
||||
int nError;
|
||||
size_t nSavePos = PathBuffer.Save();
|
||||
DWORD dwSpanCount;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Sanity check
|
||||
assert(SpanArray.IsInitialized());
|
||||
|
||||
// Parse the file table
|
||||
while(pbPathTablePtr < pbPathTableEnd)
|
||||
@@ -467,9 +448,9 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
assert((PathEntry.NodeValue & TVFS_FOLDER_SIZE_MASK) >= sizeof(DWORD));
|
||||
|
||||
// Recursively call the folder parser on the same file
|
||||
nError = ParsePathFileTable(hs, DirHeader, PathBuffer, pbPathTablePtr, pbDirectoryEnd);
|
||||
if (nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = ParsePathFileTable(hs, DirHeader, PathBuffer, pbPathTablePtr, pbDirectoryEnd);
|
||||
if (dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Skip the directory data
|
||||
pbPathTablePtr = pbDirectoryEnd;
|
||||
@@ -481,63 +462,117 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
if(pbVfsSpanEntry == NULL)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// TODO: Need to support multi-span files larger than 4 GB
|
||||
// Example: CoD: Black Ops 4, file "zone/base.xpak" 0x16 spans, over 15 GB size
|
||||
assert(dwSpanCount == 1);
|
||||
dwSpanCount = 1;
|
||||
|
||||
// Parse all span entries
|
||||
for(DWORD i = 0; i < dwSpanCount; i++)
|
||||
// If it's one span, it's either a subdirectory or an entire file
|
||||
if(dwSpanCount == 1)
|
||||
{
|
||||
TVFS_SPAN_ENTRY SpanEntry;
|
||||
ENCODED_KEY EKey = {0};
|
||||
CASC_CKEY_ENTRY SpanEntry;
|
||||
|
||||
// Capture the n-th span entry
|
||||
pbVfsSpanEntry = CaptureVfsSpanEntry(DirHeader, pbVfsSpanEntry, SpanEntry);
|
||||
// Capture the single span entry
|
||||
pbVfsSpanEntry = CaptureVfsSpanEntries(DirHeader, pbVfsSpanEntry, &SpanEntry, 1);
|
||||
if(pbVfsSpanEntry == NULL)
|
||||
return ERROR_FILE_CORRUPT;
|
||||
|
||||
// Capture the encoded key
|
||||
if((SpanEntry.pbCftFileEntry + DirHeader.EKeySize) > SpanEntry.pbCftFileEnd)
|
||||
return ERROR_BAD_FORMAT;
|
||||
|
||||
// Copy the EKey
|
||||
memcpy(EKey.Value, SpanEntry.pbCftFileEntry, DirHeader.EKeySize);
|
||||
|
||||
// We need to check whether this is another TVFS directory file
|
||||
if (IsVfsSubDirectory(hs, DirHeader, SubHeader, EKey, SpanEntry.dwSpanSize) == ERROR_SUCCESS)
|
||||
// Find the CKey entry
|
||||
pCKeyEntry = FindCKeyEntry_EKey(hs, SpanEntry.EKey);
|
||||
if(pCKeyEntry != NULL)
|
||||
{
|
||||
// Add colon (':')
|
||||
PathBuffer_AddChar(PathBuffer, ':');
|
||||
|
||||
// Insert the file to the file tree
|
||||
if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey.Value)) != NULL)
|
||||
// We need to check whether this is another TVFS directory file
|
||||
if (IsVfsSubDirectory(hs, DirHeader, SubHeader, SpanEntry.EKey, SpanEntry.ContentSize) == ERROR_SUCCESS)
|
||||
{
|
||||
// Add colon (':')
|
||||
PathBuffer.AppendChar(':');
|
||||
|
||||
// The file content size should already be there
|
||||
assert(pCKeyEntry->ContentSize == SpanEntry.dwSpanSize);
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
|
||||
assert(pCKeyEntry->ContentSize == SpanEntry.ContentSize);
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer);
|
||||
|
||||
// Parse the subdir
|
||||
ParseDirectoryData(hs, SubHeader, PathBuffer);
|
||||
CASC_FREE(SubHeader.pbDirectoryData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the content content size is not there, supply it now
|
||||
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
|
||||
pCKeyEntry->ContentSize = SpanEntry.ContentSize;
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PCASC_CKEY_ENTRY pSpanEntries;
|
||||
PCASC_FILE_NODE pFileNode;
|
||||
USHORT RefCount;
|
||||
bool bFilePresent = true;
|
||||
|
||||
//
|
||||
// Need to support multi-span files, possibly lager than 4 GB
|
||||
// Example: CoD: Black Ops 4, file "zone/base.xpak" 0x16 spans, over 15 GB size
|
||||
//
|
||||
|
||||
// Allocate buffer for all span entries
|
||||
pSpanEntries = (PCASC_CKEY_ENTRY)SpanArray.Insert(dwSpanCount);
|
||||
if(pSpanEntries == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Capture all span entries
|
||||
pbVfsSpanEntry = CaptureVfsSpanEntries(DirHeader, pbVfsSpanEntry, pSpanEntries, dwSpanCount);
|
||||
if(pbVfsSpanEntry == NULL)
|
||||
return ERROR_FILE_CORRUPT;
|
||||
|
||||
// Parse all span entries
|
||||
for(DWORD dwSpanIndex = 0; dwSpanIndex < dwSpanCount; dwSpanIndex++)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pSpanEntry = pSpanEntries + dwSpanIndex;
|
||||
|
||||
// Find the CKey entry
|
||||
pCKeyEntry = FindCKeyEntry_EKey(hs, pSpanEntries[dwSpanIndex].EKey);
|
||||
if(pCKeyEntry == NULL)
|
||||
{
|
||||
bFilePresent = false;
|
||||
break;
|
||||
}
|
||||
|
||||
ParseDirectoryData(hs, SubHeader, PathBuffer);
|
||||
CASC_FREE(SubHeader.pbDirectoryData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Insert the file to the file tree
|
||||
if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey.Value)) != NULL)
|
||||
// Supply the content size
|
||||
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
|
||||
pCKeyEntry->ContentSize = pSpanEntry->ContentSize;
|
||||
assert(pCKeyEntry->ContentSize == pSpanEntry->ContentSize);
|
||||
|
||||
// Fill-in the span entry
|
||||
if(dwSpanIndex == 0)
|
||||
{
|
||||
// If the file content is not there, supply it now
|
||||
if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
|
||||
pCKeyEntry->ContentSize = SpanEntry.dwSpanSize;
|
||||
FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
|
||||
pCKeyEntry->SpanCount = (BYTE)(dwSpanCount);
|
||||
pCKeyEntry->RefCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mark the CKey entry as a file span. Note that a CKey entry
|
||||
// can actually be both a file span and a standalone file:
|
||||
// * zone/zm_red.xpak - { zone/zm_red.xpak_1, zone/zm_red.xpak_2, ..., zone/zm_red.xpak_6 }
|
||||
pCKeyEntry->Flags |= CASC_CE_FILE_SPAN;
|
||||
}
|
||||
|
||||
// Copy all from the existing CKey entry
|
||||
memcpy(pSpanEntry, pCKeyEntry, sizeof(CASC_CKEY_ENTRY));
|
||||
}
|
||||
|
||||
// Do nothing if the file is not present locally
|
||||
if(bFilePresent)
|
||||
{
|
||||
// Insert a new file node that will contain pointer to the span entries
|
||||
RefCount = pSpanEntries->RefCount;
|
||||
pFileNode = FileTree.InsertByName(pSpanEntries, PathBuffer);
|
||||
pSpanEntries->RefCount = RefCount;
|
||||
|
||||
if(pFileNode == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the position of the path buffer
|
||||
PathBuffer.szPtr = szSavePathPtr;
|
||||
PathBuffer.szPtr[0] = 0;
|
||||
PathBuffer.Restore(nSavePos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,7 +580,7 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int ParseDirectoryData(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, PATH_BUFFER & PathBuffer)
|
||||
DWORD ParseDirectoryData(TCascStorage * hs, TVFS_DIRECTORY_HEADER & DirHeader, CASC_PATH<char> & PathBuffer)
|
||||
{
|
||||
LPBYTE pbRootDirectory = DirHeader.pbDirectoryData + DirHeader.PathTableOffset;
|
||||
LPBYTE pbRootDirPtr = pbRootDirectory;
|
||||
@@ -583,21 +618,19 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
return ParsePathFileTable(hs, DirHeader, PathBuffer, pbRootDirPtr, pbRootDirEnd);
|
||||
}
|
||||
|
||||
int Load(TCascStorage * hs, TVFS_DIRECTORY_HEADER & RootHeader)
|
||||
DWORD Load(TCascStorage * hs, TVFS_DIRECTORY_HEADER & RootHeader)
|
||||
{
|
||||
// PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
PATH_BUFFER PathBuffer;
|
||||
char szPathBuffer[MAX_PATH];
|
||||
|
||||
// Initialize the path buffer
|
||||
memset(szPathBuffer, 0, sizeof(szPathBuffer));
|
||||
PathBuffer.szBegin =
|
||||
PathBuffer.szPtr = szPathBuffer;
|
||||
PathBuffer.szEnd = szPathBuffer + MAX_PATH;
|
||||
CASC_PATH<char> PathBuffer;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Save the length of the key
|
||||
FileTree.SetKeyLength(RootHeader.EKeySize);
|
||||
|
||||
// Initialize the array of span entries
|
||||
dwErrCode = SpanArray.Create(sizeof(CASC_CKEY_ENTRY), 0x100);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
|
||||
// Insert the main VFS root file as named entry
|
||||
InsertRootVfsEntry(hs, hs->VfsRoot.CKey, "vfs-root", 0);
|
||||
|
||||
@@ -612,29 +645,29 @@ struct TRootHandler_TVFS : public TFileTreeRoot
|
||||
return ParseDirectoryData(hs, RootHeader, PathBuffer);
|
||||
}
|
||||
|
||||
DWORD dwNestLevel;
|
||||
CASC_ARRAY SpanArray; // Array of CASC_SPAN_ENTRY for all multi-span files
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions - TVFS root
|
||||
|
||||
int RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
DWORD RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
{
|
||||
TRootHandler_TVFS * pRootHandler = NULL;
|
||||
TVFS_DIRECTORY_HEADER RootHeader;
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Capture the entire root directory
|
||||
nError = TRootHandler_TVFS::CaptureDirectoryHeader(RootHeader, pbRootFile, pbRootFile + cbRootFile);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
dwErrCode = TRootHandler_TVFS::CaptureDirectoryHeader(RootHeader, pbRootFile, pbRootFile + cbRootFile);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Allocate the root handler object
|
||||
pRootHandler = new TRootHandler_TVFS();
|
||||
if(pRootHandler != NULL)
|
||||
{
|
||||
// Load the root directory. If load failed, we free the object
|
||||
nError = pRootHandler->Load(hs, RootHeader);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
dwErrCode = pRootHandler->Load(hs, RootHeader);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
{
|
||||
delete pRootHandler;
|
||||
pRootHandler = NULL;
|
||||
@@ -644,5 +677,5 @@ int RootHandler_CreateTVFS(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFil
|
||||
|
||||
// Assign the root directory (or NULL) and return error
|
||||
hs->pRootHandler = pRootHandler;
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
@@ -49,16 +49,16 @@ struct TRootHandler_SC1 : public TFileTreeRoot
|
||||
return bResult;
|
||||
}
|
||||
|
||||
int Load(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
DWORD Load(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
CASC_CSV Csv(0, false);
|
||||
BYTE CKey[MD5_HASH_SIZE];
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Parse the ROOT file first in order to see whether we have the correct format
|
||||
nError = Csv.Load(pbRootFile, cbRootFile);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
dwErrCode = Csv.Load(pbRootFile, cbRootFile);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Parse all lines
|
||||
while(Csv.LoadNextLine())
|
||||
@@ -79,7 +79,7 @@ struct TRootHandler_SC1 : public TFileTreeRoot
|
||||
}
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -93,10 +93,10 @@ struct TRootHandler_SC1 : public TFileTreeRoot
|
||||
// locales/zhCN/Assets/sound/terran/ghost/tghdth01.wav|6637ed776bd22089e083b8b0b2c0374c
|
||||
//
|
||||
|
||||
int RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
DWORD RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile)
|
||||
{
|
||||
TRootHandler_SC1 * pRootHandler = NULL;
|
||||
int nError = ERROR_BAD_FORMAT;
|
||||
DWORD dwErrCode = ERROR_BAD_FORMAT;
|
||||
|
||||
// Verify whether this looks like a Starcraft I root file
|
||||
if(TRootHandler_SC1::IsRootFile(pbRootFile, cbRootFile))
|
||||
@@ -106,8 +106,8 @@ int RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbR
|
||||
if(pRootHandler != NULL)
|
||||
{
|
||||
// Load the root directory. If load failed, we free the object
|
||||
nError = pRootHandler->Load(hs, pbRootFile, cbRootFile);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
dwErrCode = pRootHandler->Load(hs, pbRootFile, cbRootFile);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
{
|
||||
delete pRootHandler;
|
||||
pRootHandler = NULL;
|
||||
@@ -117,5 +117,5 @@ int RootHandler_CreateStarcraft1(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbR
|
||||
|
||||
// Assign the root directory (or NULL) and return error
|
||||
hs->pRootHandler = pRootHandler;
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
|
||||
}
|
||||
}
|
||||
|
||||
int ParseWowRootFile_AddFiles_6x(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup)
|
||||
DWORD ParseWowRootFile_AddFiles_6x(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup)
|
||||
{
|
||||
PFILE_ROOT_ENTRY pRootEntry = RootGroup.pRootEntries;
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
@@ -215,7 +215,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int ParseWowRootFile_AddFiles_82(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup)
|
||||
DWORD ParseWowRootFile_AddFiles_82(TCascStorage * hs, FILE_ROOT_GROUP & RootGroup)
|
||||
{
|
||||
PCASC_CKEY_ENTRY pCKeyEntry;
|
||||
PCONTENT_KEY pCKey = RootGroup.pCKeyEntries;
|
||||
@@ -229,6 +229,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
|
||||
{
|
||||
// Set the file data ID
|
||||
FileDataId = FileDataId + RootGroup.FileDataIds[i];
|
||||
//printf("File Data ID: %u\n", FileDataId);
|
||||
|
||||
// Find the item in the central storage. Insert it to the tree
|
||||
if((pCKeyEntry = FindCKeyEntry_CKey(hs, pCKey->Value)) != NULL)
|
||||
@@ -253,7 +254,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int ParseWowRootFile_Level2(
|
||||
DWORD ParseWowRootFile_Level2(
|
||||
TCascStorage * hs,
|
||||
LPBYTE pbRootPtr,
|
||||
LPBYTE pbRootEnd,
|
||||
@@ -269,6 +270,10 @@ struct TRootHandler_WoW : public TFileTreeRoot
|
||||
// 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)
|
||||
@@ -287,7 +292,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
|
||||
continue;
|
||||
|
||||
// WoW.exe (build 19116): Locales other than defined mask are skipped too
|
||||
if((RootBlock.Header.LocaleFlags & dwLocaleMask) == 0)
|
||||
if(RootBlock.Header.LocaleFlags != 0 && (RootBlock.Header.LocaleFlags & dwLocaleMask) == 0)
|
||||
continue;
|
||||
|
||||
// Now call the custom function
|
||||
@@ -351,19 +356,19 @@ struct TRootHandler_WoW : public TFileTreeRoot
|
||||
LoadWowRootFileLocales(hs, pbRootPtr, cbRootFile, (1 << dwLocale), bOverrideArchive, bAudioLocale);
|
||||
*/
|
||||
|
||||
int ParseWowRootFile_Level1(
|
||||
DWORD ParseWowRootFile_Level1(
|
||||
TCascStorage * hs,
|
||||
LPBYTE pbRootPtr,
|
||||
LPBYTE pbRootEnd,
|
||||
DWORD dwLocaleMask,
|
||||
BYTE bAudioLocale)
|
||||
{
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Load the locale as-is
|
||||
nError = ParseWowRootFile_Level2(hs, pbRootPtr, pbRootEnd, dwLocaleMask, false, bAudioLocale);
|
||||
if (nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
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)
|
||||
@@ -376,15 +381,15 @@ struct TRootHandler_WoW : public TFileTreeRoot
|
||||
}
|
||||
|
||||
// WoW.exe: 004146C7 (BuildManifest::Load)
|
||||
int Load(TCascStorage * hs, LPBYTE pbRootPtr, LPBYTE pbRootEnd, DWORD dwLocaleMask)
|
||||
DWORD Load(TCascStorage * hs, LPBYTE pbRootPtr, LPBYTE pbRootEnd, DWORD dwLocaleMask)
|
||||
{
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
nError = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 0);
|
||||
if (nError == ERROR_SUCCESS)
|
||||
nError = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 1);
|
||||
dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 0);
|
||||
if (dwErrCode == ERROR_SUCCESS)
|
||||
dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 1);
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
// Search for files
|
||||
@@ -461,7 +466,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask)
|
||||
DWORD RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask)
|
||||
{
|
||||
TRootHandler_WoW * pRootHandler = NULL;
|
||||
FILE_ROOT_HEADER_82 RootHeader;
|
||||
@@ -469,7 +474,7 @@ int RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile
|
||||
LPBYTE pbRootEnd = pbRootFile + cbRootFile;
|
||||
LPBYTE pbRootPtr;
|
||||
DWORD FileCounterHashless = 0;
|
||||
int nError = ERROR_BAD_FORMAT;
|
||||
DWORD dwErrCode = ERROR_BAD_FORMAT;
|
||||
|
||||
// Check for the new format (World of Warcraft 8.2, build 30170)
|
||||
pbRootPtr = TRootHandler_WoW::CaptureRootHeader(RootHeader, pbRootFile, pbRootEnd);
|
||||
@@ -485,8 +490,8 @@ int RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile
|
||||
if(pRootHandler != NULL)
|
||||
{
|
||||
// Load the root directory. If load failed, we free the object
|
||||
nError = pRootHandler->Load(hs, pbRootFile, pbRootEnd, dwLocaleMask);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
dwErrCode = pRootHandler->Load(hs, pbRootFile, pbRootEnd, dwLocaleMask);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
{
|
||||
delete pRootHandler;
|
||||
pRootHandler = NULL;
|
||||
@@ -495,6 +500,6 @@ int RootHandler_CreateWoW(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile
|
||||
|
||||
// Assign the root directory (or NULL) and return error
|
||||
hs->pRootHandler = pRootHandler;
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ struct FILE_INDEX_FOOTER
|
||||
BYTE PageSizeKB; // Length, in kilobytes, of the index page
|
||||
BYTE OffsetBytes; // Normally 4 for archive indices, 6 for group indices, and 0 for loose file indices
|
||||
BYTE SizeBytes; // Normally 4
|
||||
BYTE EKeySizeBytes; // Normally 16
|
||||
BYTE EKeyLength; // Normally 16
|
||||
BYTE FooterHashBytes; // Normally 8, <= 0x10
|
||||
BYTE ElementCount[4]; // BigEndian in _old_ versions (e.g. 18179)
|
||||
BYTE FooterHash[CHKSUM_LENGTH];
|
||||
|
||||
@@ -25,8 +25,8 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,50,0,156
|
||||
PRODUCTVERSION 1,50,0,156
|
||||
FILEVERSION 1,50,0,158
|
||||
PRODUCTVERSION 1,50,0,158
|
||||
FILEFLAGSMASK 0x17L
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -42,12 +42,12 @@ BEGIN
|
||||
BLOCK "040504b0"
|
||||
BEGIN
|
||||
VALUE "FileDescription", "CascLib library for reading Blizzard CASC storages"
|
||||
VALUE "FileVersion", "1, 50, 0, 156\0"
|
||||
VALUE "FileVersion", "1, 50, 0, 158\0"
|
||||
VALUE "InternalName", "CascLib"
|
||||
VALUE "LegalCopyright", "Copyright (c) 2014 - 2019 Ladislav Zezula"
|
||||
VALUE "OriginalFilename", "CascLib.dll"
|
||||
VALUE "ProductName", "CascLib"
|
||||
VALUE "ProductVersion", "1, 50, 0, 156\0"
|
||||
VALUE "ProductVersion", "1, 50, 0, 158\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -43,7 +43,7 @@ class CASC_ARRAY
|
||||
int Create(size_t ItemSize, size_t ItemCountMax)
|
||||
{
|
||||
// Create the array
|
||||
if ((m_pItemArray = CASC_ALLOC(BYTE, ItemSize * ItemCountMax)) == NULL)
|
||||
if ((m_pItemArray = CASC_ALLOC<BYTE>(ItemSize * ItemCountMax)) == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
m_ItemCountMax = ItemCountMax;
|
||||
@@ -53,12 +53,12 @@ class CASC_ARRAY
|
||||
}
|
||||
|
||||
// Inserts one or more items; returns pointer to the first inserted item
|
||||
void * Insert(size_t NewItemCount)
|
||||
void * Insert(size_t NewItemCount, bool bEnlargeAllowed = true)
|
||||
{
|
||||
void * pNewItems;
|
||||
|
||||
// Try to enlarge the buffer, if needed
|
||||
if (!EnlargeArray(m_ItemCount + NewItemCount))
|
||||
if (!EnlargeArray(m_ItemCount + NewItemCount, bEnlargeAllowed))
|
||||
return NULL;
|
||||
pNewItems = m_pItemArray + (m_ItemCount * m_ItemSize);
|
||||
|
||||
@@ -70,9 +70,9 @@ class CASC_ARRAY
|
||||
}
|
||||
|
||||
// Inserts one or more items; returns pointer to the first inserted item
|
||||
void * Insert(const void * NewItems, size_t NewItemCount)
|
||||
void * Insert(const void * NewItems, size_t NewItemCount, bool bEnlargeAllowed = true)
|
||||
{
|
||||
void * pNewItem = Insert(NewItemCount);
|
||||
void * pNewItem = Insert(NewItemCount, bEnlargeAllowed);
|
||||
|
||||
// Copy the item(s) to the array, if any
|
||||
if (pNewItem && NewItems)
|
||||
@@ -99,7 +99,7 @@ class CASC_ARRAY
|
||||
LPBYTE pbNewItem;
|
||||
|
||||
// Make sure we have array large enough
|
||||
if(!EnlargeArray(ItemIndex + 1))
|
||||
if(!EnlargeArray(ItemIndex + 1, true))
|
||||
return NULL;
|
||||
|
||||
// Get the items range
|
||||
@@ -169,7 +169,7 @@ class CASC_ARRAY
|
||||
|
||||
protected:
|
||||
|
||||
bool EnlargeArray(size_t NewItemCount)
|
||||
bool EnlargeArray(size_t NewItemCount, bool bEnlargeAllowed)
|
||||
{
|
||||
LPBYTE NewItemArray;
|
||||
size_t ItemCountMax;
|
||||
@@ -181,6 +181,10 @@ class CASC_ARRAY
|
||||
// Shall we enlarge the table?
|
||||
if (NewItemCount > m_ItemCountMax)
|
||||
{
|
||||
// Deny enlarge if not allowed
|
||||
if(bEnlargeAllowed == false)
|
||||
return false;
|
||||
|
||||
// Calculate new table size
|
||||
ItemCountMax = m_ItemCountMax;
|
||||
while (ItemCountMax < NewItemCount)
|
||||
|
||||
@@ -81,6 +81,19 @@ void SetLastError(DWORD dwErrCode)
|
||||
//-----------------------------------------------------------------------------
|
||||
// Linear data stream manipulation
|
||||
|
||||
LPBYTE CaptureInteger16_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue)
|
||||
{
|
||||
// Is there enough data?
|
||||
if((pbDataPtr + sizeof(USHORT)) > pbDataEnd)
|
||||
return NULL;
|
||||
|
||||
// Convert data from Little endian to
|
||||
PtrValue[0] = ConvertBytesToInteger_2(pbDataPtr);
|
||||
|
||||
// Return the pointer to data following after the integer
|
||||
return pbDataPtr + sizeof(USHORT);
|
||||
}
|
||||
|
||||
LPBYTE CaptureInteger32(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue)
|
||||
{
|
||||
// Is there enough data?
|
||||
@@ -133,6 +146,49 @@ LPBYTE CaptureContentKey(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PCONTENT_KEY * PtrC
|
||||
return pbDataPtr + sizeof(CONTENT_KEY);
|
||||
}
|
||||
|
||||
LPBYTE CaptureEncodedKey(LPBYTE pbEKey, LPBYTE pbData, BYTE EKeyLength)
|
||||
{
|
||||
// Two usual lengths of EKey
|
||||
assert(EKeyLength == 0x09 || EKeyLength == 0x10);
|
||||
|
||||
// Copy the first 0x09 bytes
|
||||
if(EKeyLength >= 0x09)
|
||||
{
|
||||
pbEKey[0x00] = pbData[0x00];
|
||||
pbEKey[0x01] = pbData[0x01];
|
||||
pbEKey[0x02] = pbData[0x02];
|
||||
pbEKey[0x03] = pbData[0x03];
|
||||
pbEKey[0x04] = pbData[0x04];
|
||||
pbEKey[0x05] = pbData[0x05];
|
||||
pbEKey[0x06] = pbData[0x06];
|
||||
pbEKey[0x07] = pbData[0x07];
|
||||
pbEKey[0x08] = pbData[0x08];
|
||||
|
||||
if(EKeyLength == 0x10)
|
||||
{
|
||||
pbEKey[0x09] = pbData[0x09];
|
||||
pbEKey[0x0A] = pbData[0x0A];
|
||||
pbEKey[0x0B] = pbData[0x0B];
|
||||
pbEKey[0x0C] = pbData[0x0C];
|
||||
pbEKey[0x0D] = pbData[0x0D];
|
||||
pbEKey[0x0E] = pbData[0x0E];
|
||||
pbEKey[0x0F] = pbData[0x0F];
|
||||
}
|
||||
else
|
||||
{
|
||||
pbEKey[0x09] = 0;
|
||||
pbEKey[0x0A] = 0;
|
||||
pbEKey[0x0B] = 0;
|
||||
pbEKey[0x0C] = 0;
|
||||
pbEKey[0x0D] = 0;
|
||||
pbEKey[0x0E] = 0;
|
||||
pbEKey[0x0F] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return pbData + EKeyLength;
|
||||
}
|
||||
|
||||
LPBYTE CaptureArray_(LPBYTE pbDataPtr, LPBYTE pbDataEnd, LPBYTE * PtrArray, size_t ItemSize, size_t ItemCount)
|
||||
{
|
||||
size_t ArraySize = ItemSize * ItemCount;
|
||||
@@ -229,6 +285,7 @@ size_t CascStrPrintf(char * buffer, size_t nCount, const char * format, ...)
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
StringCchVPrintfExA(buffer, nCount, &buffend, NULL, 0, format, argList);
|
||||
// buffend = buffer + vsnprintf(buffer, nCount, format, argList);
|
||||
#else
|
||||
buffend = buffer + vsnprintf(buffer, nCount, format, argList);
|
||||
#endif
|
||||
@@ -248,6 +305,7 @@ size_t CascStrPrintf(wchar_t * buffer, size_t nCount, const wchar_t * format, ..
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
StringCchVPrintfExW(buffer, nCount, &buffend, NULL, 0, format, argList);
|
||||
// buffend = buffer + vswprintf(buffer, nCount, format, argList);
|
||||
#else
|
||||
buffend = buffer + vswprintf(buffer, nCount, format, argList);
|
||||
#endif
|
||||
@@ -268,7 +326,7 @@ char * CascNewStr(const char * szString, size_t nCharsToReserve)
|
||||
if(szString != NULL)
|
||||
{
|
||||
nLength = strlen(szString);
|
||||
szNewString = CASC_ALLOC(char, nLength + nCharsToReserve + 1);
|
||||
szNewString = CASC_ALLOC<char>(nLength + nCharsToReserve + 1);
|
||||
if(szNewString != NULL)
|
||||
{
|
||||
memcpy(szNewString, szString, nLength);
|
||||
@@ -287,7 +345,7 @@ wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve)
|
||||
if(szString != NULL)
|
||||
{
|
||||
nLength = wcslen(szString);
|
||||
szNewString = CASC_ALLOC(wchar_t, nLength + nCharsToReserve + 1);
|
||||
szNewString = CASC_ALLOC<wchar_t>(nLength + nCharsToReserve + 1);
|
||||
if(szNewString != NULL)
|
||||
{
|
||||
memcpy(szNewString, szString, nLength * sizeof(wchar_t));
|
||||
@@ -298,43 +356,8 @@ wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve)
|
||||
return szNewString;
|
||||
}
|
||||
|
||||
template <typename XCHAR>
|
||||
TCHAR * AppendPathFragment(TCHAR * szBuffer, TCHAR * szBufferEnd, const XCHAR * szPath, char chSeparator, bool bFirstFragment = false)
|
||||
{
|
||||
// The "Path" must not be empty
|
||||
if(szPath && szPath[0])
|
||||
{
|
||||
// Append the path separator after the first fragment
|
||||
if(szBuffer < szBufferEnd && bFirstFragment == false)
|
||||
{
|
||||
if(szBuffer[-1] != chSeparator)
|
||||
{
|
||||
*szBuffer++ = chSeparator;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the sub path
|
||||
while(szBuffer < szBufferEnd && szPath[0] != 0)
|
||||
{
|
||||
// If there is a path separator, we skip it (all of them) and put single separator there
|
||||
if(szPath[0] == '\\' || szPath[0] == '/')
|
||||
{
|
||||
while(szPath[0] == '\\' || szPath[0] == '/')
|
||||
szPath++;
|
||||
*szBuffer++ = chSeparator;
|
||||
}
|
||||
else
|
||||
{
|
||||
*szBuffer++ = *szPath++;
|
||||
}
|
||||
}
|
||||
|
||||
// Append end of string
|
||||
szBuffer[0] = 0;
|
||||
}
|
||||
|
||||
return szBuffer;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// String merging
|
||||
|
||||
LPTSTR GetLastPathPart(LPTSTR szWorkPath)
|
||||
{
|
||||
@@ -377,19 +400,18 @@ bool CutLastPathPart(TCHAR * szWorkPath)
|
||||
|
||||
size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, char chSeparator, va_list argList)
|
||||
{
|
||||
LPTSTR szSaveBuffer = szBuffer;
|
||||
LPTSTR szBufferEnd = szBuffer + nMaxChars - 1;
|
||||
LPTSTR szFragment;
|
||||
bool bFirstFragment = true;
|
||||
CASC_PATH<TCHAR> Path(chSeparator);
|
||||
LPCTSTR szFragment;
|
||||
bool bWithSeparator = false;
|
||||
|
||||
// Combine all parts of the path here
|
||||
while((szFragment = va_arg(argList, LPTSTR)) != NULL)
|
||||
{
|
||||
szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szFragment, chSeparator, bFirstFragment);
|
||||
bFirstFragment = false;
|
||||
Path.AppendString(szFragment, bWithSeparator);
|
||||
bWithSeparator = true;
|
||||
}
|
||||
|
||||
return (szBuffer - szSaveBuffer);
|
||||
return Path.Copy(szBuffer, nMaxChars);
|
||||
}
|
||||
|
||||
size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, char chSeparator, ...)
|
||||
@@ -406,40 +428,12 @@ size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, char chSeparator, ...)
|
||||
|
||||
LPTSTR CombinePath(LPCTSTR szDirectory, LPCTSTR szSubDir)
|
||||
{
|
||||
LPTSTR szFullPath;
|
||||
size_t nLength = 0;
|
||||
CASC_PATH<TCHAR> Path(PATH_SEP_CHAR);
|
||||
|
||||
// Calculate length
|
||||
if(szDirectory != NULL)
|
||||
nLength += (_tcslen(szDirectory) + 1);
|
||||
if(szSubDir != NULL)
|
||||
nLength += (_tcslen(szSubDir) + 1);
|
||||
|
||||
// Allocate buffer
|
||||
if((szFullPath = CASC_ALLOC(TCHAR, nLength)) != NULL)
|
||||
{
|
||||
CombinePath(szFullPath, nLength, PATH_SEP_CHAR, szDirectory, szSubDir, NULL);
|
||||
}
|
||||
|
||||
return szFullPath;
|
||||
}
|
||||
|
||||
size_t CreateCascSubdirectoryName(LPTSTR szBuffer, size_t nMaxChars, LPCTSTR szSubDir, LPCTSTR szExtension, LPBYTE pbEKey)
|
||||
{
|
||||
TCHAR * szSaveBuffer = szBuffer;
|
||||
TCHAR * szBufferEnd = szBuffer + nMaxChars - 1;
|
||||
char szHashSubPath[0x80];
|
||||
char szHashText[MD5_STRING_SIZE+1];
|
||||
|
||||
// Prepare the subpath
|
||||
StringFromBinary(pbEKey, MD5_HASH_SIZE, szHashText);
|
||||
CascStrPrintf(szHashSubPath, _countof(szHashSubPath), "%02x/%02x/%s", pbEKey[0], pbEKey[1], szHashText);
|
||||
|
||||
// Combine the path together
|
||||
szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szSubDir, URL_SEP_CHAR, true);
|
||||
szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szHashSubPath, URL_SEP_CHAR);
|
||||
szBuffer = AppendPathFragment(szBuffer, szBufferEnd, szExtension, URL_SEP_CHAR, true);
|
||||
return (szBuffer - szSaveBuffer);
|
||||
// Merge the path
|
||||
Path.AppendString(szDirectory, false);
|
||||
Path.AppendString(szSubDir, true);
|
||||
return Path.New();
|
||||
}
|
||||
|
||||
size_t NormalizeFileName(const unsigned char * NormTable, char * szNormName, const char * szFileName, size_t cchMaxChars)
|
||||
@@ -585,32 +579,6 @@ int ConvertStringToBinary(
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer)
|
||||
{
|
||||
char * szSaveBuffer = szBuffer;
|
||||
|
||||
// Verify the binary pointer
|
||||
if(pbBinary && cbBinary)
|
||||
{
|
||||
// Convert the string to the array of MD5
|
||||
// Copy the blob data as text
|
||||
for(size_t i = 0; i < cbBinary; i++)
|
||||
{
|
||||
*szBuffer++ = IntToHexChar[pbBinary[i] >> 0x04];
|
||||
*szBuffer++ = IntToHexChar[pbBinary[i] & 0x0F];
|
||||
}
|
||||
}
|
||||
|
||||
// Terminate the string
|
||||
*szBuffer = 0;
|
||||
return szSaveBuffer;
|
||||
}
|
||||
|
||||
char * StringFromMD5(LPBYTE md5, char * szBuffer)
|
||||
{
|
||||
return StringFromBinary(md5, MD5_HASH_SIZE, szBuffer);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File name utilities
|
||||
|
||||
@@ -693,10 +661,8 @@ bool CascCheckWildCard(const char * szString, const char * szWildCard)
|
||||
{
|
||||
if(szWildCardPtr[0] == '*')
|
||||
{
|
||||
szWildCardPtr++;
|
||||
|
||||
if(szWildCardPtr[0] == '*')
|
||||
continue;
|
||||
while(szWildCardPtr[0] == '*')
|
||||
szWildCardPtr++;
|
||||
|
||||
if(szWildCardPtr[0] == 0)
|
||||
return true;
|
||||
|
||||
@@ -39,13 +39,19 @@ typedef struct _CONTENT_KEY
|
||||
|
||||
} CONTENT_KEY, *PCONTENT_KEY, ENCODED_KEY, *PENCODED_KEY;
|
||||
|
||||
// Helper structure for merging file paths
|
||||
typedef struct _PATH_BUFFER
|
||||
//-----------------------------------------------------------------------------
|
||||
// EKey entry, captured from index files of all types. This structure
|
||||
// is somewhat less memory consuming than CASC_CKEY_ENTRY
|
||||
|
||||
typedef struct _CASC_EKEY_ENTRY
|
||||
{
|
||||
char * szBegin;
|
||||
char * szPtr;
|
||||
char * szEnd;
|
||||
} PATH_BUFFER, *PPATH_BUFFER;
|
||||
BYTE EKey[MD5_HASH_SIZE]; // Encoded key. Length depends on TCascStorage::EKeyLength
|
||||
ULONGLONG StorageOffset; // Offset of the encoded file in archive.
|
||||
// Lower (TCascStorage::FileOffsetBits) bits are archive offset.
|
||||
// Upper bits are archive index
|
||||
DWORD EncodedSize; // Encoded size
|
||||
DWORD Alignment; // Alignment to 8-byte boundary. Reserved for future use
|
||||
} CASC_EKEY_ENTRY, *PCASC_EKEY_ENTRY;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Basic structure used by all CascLib objects to describe a single entry
|
||||
@@ -59,7 +65,12 @@ typedef struct _PATH_BUFFER
|
||||
#define CASC_CE_HAS_EKEY_PARTIAL 0x00000008 // The EKey is only partial, padded by zeros. Always used with CASC_CE_HAS_EKEY
|
||||
#define CASC_CE_IN_ENCODING 0x00000010 // Present in the ENCODING manifest
|
||||
#define CASC_CE_IN_DOWNLOAD 0x00000020 // Present in the DOWNLOAD manifest
|
||||
#define CASC_CE_FOLDER_ENTRY 0x00000040 // This CKey entry is a folder
|
||||
#define CASC_CE_IN_BUILD 0x00000040 // Present in the BUILD (text) manifest
|
||||
#define CASC_CE_IN_ARCHIVE 0x00000080 // File is stored in an archive (for online storages)
|
||||
#define CASC_CE_FOLDER_ENTRY 0x00000100 // This CKey entry is a folder
|
||||
#define CASC_CE_FILE_SPAN 0x00000200 // This CKey entry is a follow-up file span
|
||||
#define CASC_CE_FILE_PATCH 0x00000400 // The file is in PATCH subfolder in remote storage
|
||||
#define CASC_CE_PLAIN_DATA 0x00000800 // The file data is not BLTE encoded, but in plain format
|
||||
|
||||
// In-memory representation of a single entry.
|
||||
struct CASC_CKEY_ENTRY
|
||||
@@ -75,19 +86,36 @@ struct CASC_CKEY_ENTRY
|
||||
StorageOffset = CASC_INVALID_OFFS64;
|
||||
EncodedSize = CASC_INVALID_SIZE;
|
||||
ContentSize = CASC_INVALID_SIZE;
|
||||
SpanCount = 1;
|
||||
}
|
||||
|
||||
bool IsFile()
|
||||
{
|
||||
// Must not be a folder entry
|
||||
if((Flags & CASC_CE_FOLDER_ENTRY) == 0)
|
||||
{
|
||||
// There can be entries that are both file span or the standalone file
|
||||
// * zone/zm_red.xpak - { zone/zm_red.xpak_1, zone/zm_red.xpak_2, ..., zone/zm_red.xpak_6 }
|
||||
if(RefCount != 0)
|
||||
return true;
|
||||
|
||||
// To include the file, it must either be present in ENCODING, DOWNLOAD or in BUILD file
|
||||
if(((Flags & CASC_CE_FILE_SPAN) == 0) && (Flags & (CASC_CE_IN_ENCODING | CASC_CE_IN_DOWNLOAD | CASC_CE_IN_BUILD)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
BYTE CKey[MD5_HASH_SIZE]; // Content key of the full length
|
||||
BYTE EKey[MD5_HASH_SIZE]; // Encoded key of the full length
|
||||
ULONGLONG StorageOffset; // Linear offset over the entire storage. 0 if not present
|
||||
ULONGLONG TagBitMask; // Bitmap for the tags. 0 ig tags are not supported
|
||||
DWORD EncodedSize; // Encoded size of the file. 0 if not supported
|
||||
DWORD ContentSize; // Content size of the file. 0 if not supported
|
||||
DWORD ContentSize; // Content size of the file
|
||||
DWORD EncodedSize; // Encoded size of the file
|
||||
DWORD Flags; // See CASC_CE_XXX
|
||||
USHORT RefCount; // This is the number of file names referencing this entry
|
||||
BYTE SpanCount; // Number of spans for the file
|
||||
BYTE Priority; // Download priority
|
||||
BYTE Alignment;
|
||||
|
||||
};
|
||||
typedef CASC_CKEY_ENTRY *PCASC_CKEY_ENTRY;
|
||||
|
||||
@@ -112,7 +140,22 @@ extern unsigned char IntToHexChar[];
|
||||
//
|
||||
|
||||
#define CASC_REALLOC(type, ptr, count) (type *)realloc(ptr, (count) * sizeof(type))
|
||||
#define CASC_ALLOC(type, count) (type *)malloc((count) * sizeof(type))
|
||||
|
||||
template <typename T>
|
||||
T * CASC_ALLOC(size_t nCount)
|
||||
{
|
||||
return (T *)malloc(nCount * sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T * CASC_ALLOC_ZERO(size_t nCount)
|
||||
{
|
||||
T * ptr = CASC_ALLOC<T>(nCount);
|
||||
|
||||
if(ptr != NULL)
|
||||
memset(ptr, 0, sizeof(T) * nCount);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void CASC_FREE(T *& ptr)
|
||||
@@ -241,12 +284,14 @@ inline void CopyMemory16(void * Target, void * Source)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Linear data stream manipulation
|
||||
// Capturing various integral values
|
||||
|
||||
LPBYTE CaptureInteger16_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue);
|
||||
LPBYTE CaptureInteger32(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue);
|
||||
LPBYTE CaptureInteger32_BE(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PDWORD PtrValue);
|
||||
LPBYTE CaptureByteArray(LPBYTE pbDataPtr, LPBYTE pbDataEnd, size_t nLength, LPBYTE pbOutput);
|
||||
LPBYTE CaptureContentKey(LPBYTE pbDataPtr, LPBYTE pbDataEnd, PCONTENT_KEY * PtrCKey);
|
||||
LPBYTE CaptureEncodedKey(LPBYTE pbEKey, LPBYTE pbData, BYTE EKeyLength);
|
||||
LPBYTE CaptureArray_(LPBYTE pbDataPtr, LPBYTE pbDataEnd, LPBYTE * PtrArray, size_t ItemSize, size_t ItemCount);
|
||||
|
||||
#define CaptureArray(pbDataPtr, pbDataEnd, PtrArray, type, count) CaptureArray_(pbDataPtr, pbDataEnd, PtrArray, sizeof(type), count)
|
||||
@@ -277,7 +322,6 @@ LPTSTR CombinePath(LPCTSTR szPath, LPCTSTR szSubDir);
|
||||
LPTSTR GetLastPathPart(LPTSTR szWorkPath);
|
||||
bool CutLastPathPart(TCHAR * szWorkPath);
|
||||
|
||||
size_t CreateCascSubdirectoryName(TCHAR * szBuffer, size_t nMaxChars, const TCHAR * szSubDir, const TCHAR * szExtension, LPBYTE pbEKey);
|
||||
size_t NormalizeFileName_UpperBkSlash(char * szNormName, const char * szFileName, size_t cchMaxChars);
|
||||
size_t NormalizeFileName_LowerSlash(char * szNormName, const char * szFileName, size_t cchMaxChars);
|
||||
|
||||
@@ -288,8 +332,31 @@ int ConvertDigitToInt32(const TCHAR * szString, PDWORD PtrValue);
|
||||
int ConvertStringToInt08(const char * szString, PDWORD PtrValue);
|
||||
int ConvertStringToInt32(const TCHAR * szString, size_t nMaxDigits, PDWORD PtrValue);
|
||||
int ConvertStringToBinary(const char * szString, size_t nMaxDigits, LPBYTE pbBinary);
|
||||
char * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, char * szBuffer);
|
||||
char * StringFromMD5(LPBYTE md5, char * szBuffer);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Conversion from binary array to string. The caller must ensure that
|
||||
// the buffer has at least ((cbBinary * 2) + 1) characters
|
||||
|
||||
template <typename xchar>
|
||||
xchar * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, xchar * szBuffer)
|
||||
{
|
||||
xchar * szSaveBuffer = szBuffer;
|
||||
|
||||
// Verify the binary pointer
|
||||
if(pbBinary && cbBinary)
|
||||
{
|
||||
// Convert the bytes to string array
|
||||
for(size_t i = 0; i < cbBinary; i++)
|
||||
{
|
||||
*szBuffer++ = IntToHexChar[pbBinary[i] >> 0x04];
|
||||
*szBuffer++ = IntToHexChar[pbBinary[i] & 0x0F];
|
||||
}
|
||||
}
|
||||
|
||||
// Terminate the string
|
||||
*szBuffer = 0;
|
||||
return szSaveBuffer;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structure query key
|
||||
|
||||
@@ -185,10 +185,10 @@ CASC_CSV::~CASC_CSV()
|
||||
m_szCsvFile = NULL;
|
||||
}
|
||||
|
||||
int CASC_CSV::Load(const TCHAR * szFileName)
|
||||
DWORD CASC_CSV::Load(const TCHAR * szFileName)
|
||||
{
|
||||
DWORD cbFileData = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
m_szCsvFile = (char *)LoadFileToMemory(szFileName, &cbFileData);
|
||||
if (m_szCsvFile != NULL)
|
||||
@@ -199,19 +199,19 @@ int CASC_CSV::Load(const TCHAR * szFileName)
|
||||
m_nCsvFile = cbFileData;
|
||||
|
||||
// Parse the data
|
||||
nError = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
|
||||
dwErrCode = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = GetLastError();
|
||||
dwErrCode = GetLastError();
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
int CASC_CSV::Load(LPBYTE pbData, size_t cbData)
|
||||
DWORD CASC_CSV::Load(LPBYTE pbData, size_t cbData)
|
||||
{
|
||||
int nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
m_szCsvFile = new char[cbData + 1];
|
||||
if (m_szCsvFile != NULL)
|
||||
@@ -223,10 +223,10 @@ int CASC_CSV::Load(LPBYTE pbData, size_t cbData)
|
||||
m_nCsvFile = cbData;
|
||||
|
||||
// Parse the data
|
||||
nError = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
|
||||
dwErrCode = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
return nError;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
bool CASC_CSV::LoadNextLine()
|
||||
|
||||
@@ -69,8 +69,8 @@ class CASC_CSV
|
||||
CASC_CSV(size_t nLinesMax, bool bHasHeader);
|
||||
~CASC_CSV();
|
||||
|
||||
int Load(LPBYTE pbData, size_t cbData);
|
||||
int Load(const TCHAR * szFileName);
|
||||
DWORD Load(LPBYTE pbData, size_t cbData);
|
||||
DWORD Load(const TCHAR * szFileName);
|
||||
bool LoadNextLine();
|
||||
|
||||
const CASC_CSV_COLUMN & operator[](const char * szColumnName) const;
|
||||
|
||||
@@ -615,7 +615,7 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
|
||||
HINTERNET hRequest;
|
||||
DWORD dwTemp = 0;
|
||||
bool bFileAvailable = false;
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// Check alternate ports
|
||||
if(dwStreamFlags & STREAM_FLAG_USE_PORT_1119)
|
||||
@@ -625,10 +625,10 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
|
||||
|
||||
// Don't download if we are not connected to the internet
|
||||
if(!InternetGetConnectedState(&dwTemp, 0))
|
||||
nError = GetLastError();
|
||||
dwErrCode = GetLastError();
|
||||
|
||||
// Initiate the connection to the internet
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
pStream->Base.Http.hInternet = InternetOpen(_T("agent/2.17.2.6700"),
|
||||
INTERNET_OPEN_TYPE_PRECONFIG,
|
||||
@@ -636,11 +636,11 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
|
||||
NULL,
|
||||
0);
|
||||
if(pStream->Base.Http.hInternet == NULL)
|
||||
nError = GetLastError();
|
||||
dwErrCode = GetLastError();
|
||||
}
|
||||
|
||||
// Connect to the server
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
TCHAR szServerName[MAX_PATH];
|
||||
DWORD dwFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_CACHE_WRITE;
|
||||
@@ -656,11 +656,11 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
|
||||
dwFlags,
|
||||
0);
|
||||
if(pStream->Base.Http.hConnect == NULL)
|
||||
nError = GetLastError();
|
||||
dwErrCode = GetLastError();
|
||||
}
|
||||
|
||||
// Now try to query the file size
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Open HTTP request to the file
|
||||
hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0);
|
||||
@@ -702,7 +702,7 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
dwErrCode = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
InternetCloseHandle(hRequest);
|
||||
@@ -714,7 +714,7 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD
|
||||
if(bFileAvailable == false)
|
||||
{
|
||||
pStream->BaseClose(pStream);
|
||||
SetLastError(nError);
|
||||
SetLastError(dwErrCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -889,7 +889,7 @@ static bool BlockStream_Read(
|
||||
BlockBufferOffset = (DWORD)(ByteOffset & (BlockSize - 1));
|
||||
|
||||
// Allocate buffer for reading blocks
|
||||
TransferBuffer = BlockBuffer = CASC_ALLOC(BYTE, (BlockCount * BlockSize));
|
||||
TransferBuffer = BlockBuffer = CASC_ALLOC<BYTE>(BlockCount * BlockSize);
|
||||
if(TransferBuffer == NULL)
|
||||
{
|
||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||
@@ -1054,7 +1054,7 @@ static TFileStream * AllocateFileStream(
|
||||
}
|
||||
|
||||
// Allocate the stream structure for the given stream type
|
||||
pStream = (TFileStream *)CASC_ALLOC(BYTE, StreamSize + FileNameSize + sizeof(TCHAR));
|
||||
pStream = (TFileStream *)CASC_ALLOC<BYTE>(StreamSize + FileNameSize + sizeof(TCHAR));
|
||||
if(pStream != NULL)
|
||||
{
|
||||
// Zero the entire structure
|
||||
@@ -1137,7 +1137,7 @@ static bool FlatStream_LoadBitmap(TBlockStream * pStream)
|
||||
if(ByteOffset + BitmapSize + sizeof(FILE_BITMAP_FOOTER) == pStream->Base.File.FileSize)
|
||||
{
|
||||
// Allocate space for the bitmap
|
||||
FileBitmap = CASC_ALLOC(BYTE, BitmapSize);
|
||||
FileBitmap = CASC_ALLOC<BYTE>(BitmapSize);
|
||||
if(FileBitmap != NULL)
|
||||
{
|
||||
// Load the bitmap bits
|
||||
@@ -1349,12 +1349,11 @@ static bool FlatStream_CreateMirror(TBlockStream * pStream)
|
||||
}
|
||||
|
||||
// Allocate the bitmap array
|
||||
FileBitmap = CASC_ALLOC(BYTE, dwBitmapSize);
|
||||
FileBitmap = CASC_ALLOC_ZERO<BYTE>(dwBitmapSize);
|
||||
if(FileBitmap == NULL)
|
||||
return false;
|
||||
|
||||
// Initialize the bitmap
|
||||
memset(FileBitmap, 0, dwBitmapSize);
|
||||
pStream->FileBitmap = FileBitmap;
|
||||
pStream->BitmapSize = dwBitmapSize;
|
||||
pStream->BlockSize = DEFAULT_BLOCK_SIZE;
|
||||
@@ -1521,7 +1520,7 @@ static bool PartStream_LoadBitmap(TBlockStream * pStream)
|
||||
if((ByteOffset + BitmapSize) < pStream->Base.File.FileSize)
|
||||
{
|
||||
// Allocate space for the array of PART_FILE_MAP_ENTRY
|
||||
FileBitmap = CASC_ALLOC(PART_FILE_MAP_ENTRY, BlockCount);
|
||||
FileBitmap = CASC_ALLOC<PART_FILE_MAP_ENTRY>(BlockCount);
|
||||
if(FileBitmap != NULL)
|
||||
{
|
||||
// Load the block map
|
||||
@@ -1771,12 +1770,11 @@ static bool PartStream_CreateMirror(TBlockStream * pStream)
|
||||
}
|
||||
|
||||
// Allocate the bitmap array
|
||||
FileBitmap = CASC_ALLOC(BYTE, dwBitmapSize);
|
||||
FileBitmap = CASC_ALLOC_ZERO<BYTE>(dwBitmapSize);
|
||||
if(FileBitmap == NULL)
|
||||
return false;
|
||||
|
||||
// Initialize the bitmap
|
||||
memset(FileBitmap, 0, dwBitmapSize);
|
||||
pStream->FileBitmap = FileBitmap;
|
||||
pStream->BitmapSize = dwBitmapSize;
|
||||
pStream->BlockSize = DEFAULT_BLOCK_SIZE;
|
||||
@@ -2268,7 +2266,7 @@ static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamF
|
||||
pStream->BlockRead = (BLOCK_READ)Block4Stream_BlockRead;
|
||||
|
||||
// Allocate work space for numeric names
|
||||
szNameBuff = CASC_ALLOC(TCHAR, nNameLength + 4);
|
||||
szNameBuff = CASC_ALLOC<TCHAR>(nNameLength + 4);
|
||||
if(szNameBuff != NULL)
|
||||
{
|
||||
// Set the base flags
|
||||
@@ -2283,7 +2281,7 @@ static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamF
|
||||
break;
|
||||
|
||||
// If the open succeeded, we re-allocate the base provider array
|
||||
NewBaseArray = CASC_ALLOC(TBaseProviderData, dwBaseFiles + 1);
|
||||
NewBaseArray = CASC_ALLOC<TBaseProviderData>(dwBaseFiles + 1);
|
||||
if(NewBaseArray == NULL)
|
||||
{
|
||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||
@@ -2369,7 +2367,7 @@ static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamF
|
||||
*/
|
||||
|
||||
TFileStream * FileStream_CreateFile(
|
||||
const TCHAR * szFileName,
|
||||
LPCTSTR szFileName,
|
||||
DWORD dwStreamFlags)
|
||||
{
|
||||
TFileStream * pStream;
|
||||
@@ -2424,7 +2422,7 @@ TFileStream * FileStream_CreateFile(
|
||||
*/
|
||||
|
||||
TFileStream * FileStream_OpenFile(
|
||||
const TCHAR * szFileName,
|
||||
LPCTSTR szFileName,
|
||||
DWORD dwStreamFlags)
|
||||
{
|
||||
DWORD dwProvider = dwStreamFlags & STREAM_PROVIDERS_MASK;
|
||||
@@ -2473,7 +2471,7 @@ const TCHAR * FileStream_GetFileName(TFileStream * pStream)
|
||||
* \a pdwStreamProvider Pointer to a DWORD variable that receives stream provider (STREAM_PROVIDER_XXX)
|
||||
*/
|
||||
|
||||
size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider)
|
||||
size_t FileStream_Prefix(LPCTSTR szFileName, DWORD * pdwProvider)
|
||||
{
|
||||
size_t nPrefixLength1 = 0;
|
||||
size_t nPrefixLength2 = 0;
|
||||
@@ -2592,14 +2590,6 @@ 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);
|
||||
}
|
||||
|
||||
@@ -247,10 +247,10 @@ struct TEncryptedStream : public TBlockStream
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions for file stream
|
||||
|
||||
TFileStream * FileStream_CreateFile(const TCHAR * szFileName, DWORD dwStreamFlags);
|
||||
TFileStream * FileStream_OpenFile(const TCHAR * szFileName, DWORD dwStreamFlags);
|
||||
TFileStream * FileStream_CreateFile(LPCTSTR szFileName, DWORD dwStreamFlags);
|
||||
TFileStream * FileStream_OpenFile(LPCTSTR szFileName, DWORD dwStreamFlags);
|
||||
const TCHAR * FileStream_GetFileName(TFileStream * pStream);
|
||||
size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider);
|
||||
size_t FileStream_Prefix(LPCTSTR szFileName, DWORD * pdwProvider);
|
||||
|
||||
bool FileStream_SetCallback(TFileStream * pStream, STREAM_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData);
|
||||
|
||||
|
||||
@@ -30,28 +30,7 @@ inline void SET_NODE_INT32(void * node, size_t offset, DWORD value)
|
||||
|
||||
PtrValue[0] = value;
|
||||
}
|
||||
/*
|
||||
static bool CompareFileNode(void * pvObject, void * pvUserData)
|
||||
{
|
||||
PCASC_COMPARE_CONTEXT pCtx = (PCASC_COMPARE_CONTEXT)pvUserData;
|
||||
PCASC_FILE_TREE pFileTree = (PCASC_FILE_TREE)pCtx->pThis;
|
||||
PCASC_FILE_NODE pFileNode = (PCASC_FILE_NODE)pvObject;
|
||||
char szFullPath[MAX_PATH];
|
||||
|
||||
// First of all, the name hash must match
|
||||
if(pFileNode->FileNameHash == pCtx->FileNameHash)
|
||||
{
|
||||
// Then also compare the full path name
|
||||
pFileTree->PathAt(szFullPath, _countof(szFullPath), pFileNode);
|
||||
if(!_stricmp(szFullPath, pCtx->szFileName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
//-----------------------------------------------------------------------------
|
||||
// Protected functions
|
||||
|
||||
@@ -224,11 +203,11 @@ bool CASC_FILE_TREE::RebuildNameMaps()
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
int CASC_FILE_TREE::Create(DWORD Flags)
|
||||
DWORD CASC_FILE_TREE::Create(DWORD Flags)
|
||||
{
|
||||
PCASC_FILE_NODE pRootNode;
|
||||
size_t FileNodeSize = FIELD_OFFSET(CASC_FILE_NODE, ExtraValues);
|
||||
int nError;
|
||||
DWORD dwErrCode;
|
||||
|
||||
// Initialize the file tree
|
||||
memset(this, 0, sizeof(CASC_FILE_TREE));
|
||||
@@ -242,9 +221,9 @@ int CASC_FILE_TREE::Create(DWORD Flags)
|
||||
FileNodeSize += sizeof(DWORD);
|
||||
|
||||
// Create the array for FileDataId -> CASC_FILE_NODE
|
||||
nError = FileDataIds.Create<PCASC_FILE_NODE>(START_ITEM_COUNT);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
dwErrCode = FileDataIds.Create<PCASC_FILE_NODE>(START_ITEM_COUNT);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
// Shall we use the locale ID in the tree node?
|
||||
@@ -264,12 +243,12 @@ int CASC_FILE_TREE::Create(DWORD Flags)
|
||||
FileNodeSize = ALIGN_TO_SIZE(FileNodeSize, 8);
|
||||
|
||||
// Initialize the dynamic array
|
||||
nError = NodeTable.Create(FileNodeSize, START_ITEM_COUNT);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
dwErrCode = NodeTable.Create(FileNodeSize, START_ITEM_COUNT);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Create the dynamic array that will hold the node names
|
||||
nError = NameTable.Create<char>(START_ITEM_COUNT);
|
||||
if(nError == ERROR_SUCCESS)
|
||||
dwErrCode = NameTable.Create<char>(START_ITEM_COUNT);
|
||||
if(dwErrCode == ERROR_SUCCESS)
|
||||
{
|
||||
// Insert the first "root" node, without name
|
||||
pRootNode = (PCASC_FILE_NODE)NodeTable.Insert(1);
|
||||
@@ -287,8 +266,8 @@ int CASC_FILE_TREE::Create(DWORD Flags)
|
||||
|
||||
// Create both maps
|
||||
if(!RebuildNameMaps())
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
return nError;
|
||||
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||
return dwErrCode;
|
||||
}
|
||||
|
||||
void CASC_FILE_TREE::Free()
|
||||
@@ -307,21 +286,26 @@ void CASC_FILE_TREE::Free()
|
||||
|
||||
PCASC_FILE_NODE CASC_FILE_TREE::InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const char * szFileName, DWORD FileDataId, DWORD LocaleFlags, DWORD ContentFlags)
|
||||
{
|
||||
CASC_COMPARE_CONTEXT CmpCtx;
|
||||
PCASC_FILE_NODE pFileNode;
|
||||
ULONGLONG FileNameHash;
|
||||
|
||||
// Sanity checks
|
||||
assert(szFileName != NULL && szFileName[0] != 0);
|
||||
assert(pCKeyEntry != NULL);
|
||||
|
||||
//char szCKey[MD5_STRING_SIZE+1];
|
||||
//char szEKey[MD5_STRING_SIZE+1];
|
||||
//StringFromBinary(pCKeyEntry->CKey, MD5_HASH_SIZE, szCKey);
|
||||
//StringFromBinary(pCKeyEntry->EKey, MD5_HASH_SIZE, szEKey);
|
||||
//printf("%s\t%s\t%s\n", szCKey, szEKey, szFileName);
|
||||
|
||||
//BREAK_ON_XKEY3(pCKeyEntry->EKey, 0x03, 0xDC, 0x7D);
|
||||
|
||||
// Calculate the file name hash
|
||||
CmpCtx.FileNameHash = CalcFileNameHash(szFileName);
|
||||
// CmpCtx.szFileName = szFileName;
|
||||
// CmpCtx.pThis = this;
|
||||
FileNameHash = CalcFileNameHash(szFileName);
|
||||
|
||||
// Do nothing if the file name is there already.
|
||||
// pFileNode = (PCASC_FILE_NODE)NameMap.FindObjectEx(CompareFileNode, &CmpCtx);
|
||||
pFileNode = (PCASC_FILE_NODE)NameMap.FindObject(&CmpCtx.FileNameHash);
|
||||
pFileNode = (PCASC_FILE_NODE)NameMap.FindObject(&FileNameHash);
|
||||
if(pFileNode == NULL)
|
||||
{
|
||||
// Insert new item
|
||||
@@ -329,7 +313,7 @@ PCASC_FILE_NODE CASC_FILE_TREE::InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const
|
||||
if(pFileNode != NULL)
|
||||
{
|
||||
// Supply the name hash
|
||||
pFileNode->FileNameHash = CmpCtx.FileNameHash;
|
||||
pFileNode->FileNameHash = FileNameHash;
|
||||
|
||||
// Set the file data id and the extra values
|
||||
SetExtras(pFileNode, FileDataId, LocaleFlags, ContentFlags);
|
||||
@@ -340,12 +324,13 @@ PCASC_FILE_NODE CASC_FILE_TREE::InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const
|
||||
// Also make sure that it's in the file data id table, if the table is initialized
|
||||
InsertToIdTable(pFileNode);
|
||||
|
||||
// Set the file name of the new file node. This also increments the number of references
|
||||
// Set the file name of the new file node
|
||||
SetNodeFileName(pFileNode, szFileName);
|
||||
|
||||
// If we created a new node, we need to increment the reference count
|
||||
assert(pCKeyEntry->RefCount != 0xFFFF);
|
||||
pCKeyEntry->RefCount++;
|
||||
FileNodes++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,8 +477,6 @@ PCASC_FILE_NODE CASC_FILE_TREE::Find(const char * szFullPath, DWORD FileDataId,
|
||||
if(pFileNode != NULL && pFindData != NULL)
|
||||
{
|
||||
GetExtras(pFileNode, &pFindData->dwFileDataId, &pFindData->dwLocaleFlags, &pFindData->dwContentFlags);
|
||||
pFindData->bCanOpenByName = (pFileNode->FileNameHash != 0);
|
||||
pFindData->bCanOpenByDataId = (FileDataIdOffset != 0);
|
||||
}
|
||||
|
||||
return pFileNode;
|
||||
@@ -543,7 +526,85 @@ bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szF
|
||||
{
|
||||
ULONGLONG FileNameHash = 0;
|
||||
PCASC_FILE_NODE pFolderNode = NULL;
|
||||
const char * szNodeBegin = szFileName;
|
||||
CASC_PATH<char> PathBuffer;
|
||||
LPCSTR szNodeBegin = szFileName;
|
||||
size_t nFileNode = NodeTable.IndexOf(pFileNode);
|
||||
size_t i;
|
||||
DWORD Parent = 0;
|
||||
|
||||
// Sanity checks
|
||||
assert(szFileName != NULL && szFileName[0] != 0);
|
||||
|
||||
// Traverse the entire path. For each subfolder, we insert an appropriate fake entry
|
||||
for(i = 0; szFileName[i] != 0; i++)
|
||||
{
|
||||
char chOneChar = szFileName[i];
|
||||
|
||||
// Is there a path separator?
|
||||
// Note: Warcraft III paths may contain "mount points".
|
||||
// Example: "frFR-War3Local.mpq:Maps/FrozenThrone/Campaign/NightElfX06Interlude.w3x:war3map.j"
|
||||
if(chOneChar == '\\' || chOneChar == '/' || chOneChar == ':')
|
||||
{
|
||||
// Calculate hash of the file name up to the end of the node name
|
||||
FileNameHash = CalcNormNameHash(PathBuffer, i);
|
||||
|
||||
// If the entry is not there yet, create new one
|
||||
if((pFolderNode = Find(FileNameHash)) == NULL)
|
||||
{
|
||||
// Insert new entry to the tree
|
||||
pFolderNode = InsertNew();
|
||||
if(pFolderNode == NULL)
|
||||
return false;
|
||||
|
||||
// Populate the file entry
|
||||
pFolderNode->FileNameHash = FileNameHash;
|
||||
pFolderNode->Parent = Parent;
|
||||
pFolderNode->Flags |= (chOneChar == ':') ? (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT) : CFN_FLAG_FOLDER;
|
||||
FolderNodes++;
|
||||
|
||||
// Set the node sub name to the node
|
||||
SetNodePlainName(pFolderNode, szNodeBegin, szFileName + i);
|
||||
|
||||
// Insert the entry to the name map
|
||||
InsertToHashTable(pFolderNode);
|
||||
}
|
||||
|
||||
// Move the parent to the current node
|
||||
Parent = (DWORD)NodeTable.IndexOf(pFolderNode);
|
||||
|
||||
// Move the begin of the node after the separator
|
||||
szNodeBegin = szFileName + i + 1;
|
||||
}
|
||||
|
||||
// Copy the next character, even if it was slash/backslash before
|
||||
PathBuffer.AppendChar(AsciiToUpperTable_BkSlash[chOneChar]);
|
||||
}
|
||||
|
||||
// If anything left, this is gonna be our node name
|
||||
if(szNodeBegin < szFileName + i)
|
||||
{
|
||||
// We need to reset the file node pointer, as the file node table might have changed
|
||||
pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nFileNode);
|
||||
|
||||
// Write the plain file name to the node
|
||||
SetNodePlainName(pFileNode, szNodeBegin, szFileName + i);
|
||||
pFileNode->Parent = Parent;
|
||||
|
||||
// Also insert the node to the hash table so CascOpenFile can find it
|
||||
if(pFileNode->FileNameHash == 0)
|
||||
{
|
||||
pFileNode->FileNameHash = CalcNormNameHash(PathBuffer, i);
|
||||
InsertToHashTable(pFileNode);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szFileName)
|
||||
{
|
||||
ULONGLONG FileNameHash = 0;
|
||||
PCASC_FILE_NODE pFolderNode = NULL;
|
||||
LPCSTR szNodeBegin = szFileName;
|
||||
char szPathBuffer[MAX_PATH+1];
|
||||
size_t nFileNode = NodeTable.IndexOf(pFileNode);
|
||||
size_t i;
|
||||
@@ -551,7 +612,6 @@ bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szF
|
||||
|
||||
// Sanity checks
|
||||
assert(szFileName != NULL && szFileName[0] != 0);
|
||||
assert(pFileNode->pCKeyEntry != NULL);
|
||||
|
||||
// Traverse the entire path. For each subfolder, we insert an appropriate fake entry
|
||||
for(i = 0; szFileName[i] != 0; i++)
|
||||
@@ -577,8 +637,8 @@ bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szF
|
||||
// Populate the file entry
|
||||
pFolderNode->FileNameHash = FileNameHash;
|
||||
pFolderNode->Parent = Parent;
|
||||
pFolderNode->Flags |= (chOneChar == ':') ? CFN_FLAG_MOUNT_POINT : 0;
|
||||
pFolderNode->Flags |= CFN_FLAG_FOLDER;
|
||||
pFolderNode->Flags |= (chOneChar == ':') ? (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT) : CFN_FLAG_FOLDER;
|
||||
FolderNodes++;
|
||||
|
||||
// Set the node sub name to the node
|
||||
SetNodePlainName(pFolderNode, szNodeBegin, szFileName + i);
|
||||
@@ -604,12 +664,20 @@ bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szF
|
||||
// We need to reset the file node pointer, as the file node table might have changed
|
||||
pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nFileNode);
|
||||
|
||||
// Write the plain file name to the node
|
||||
SetNodePlainName(pFileNode, szNodeBegin, szFileName + i);
|
||||
pFileNode->Parent = Parent;
|
||||
|
||||
// Also insert the node to the hash table so CascOpenFile can find it
|
||||
if(pFileNode->FileNameHash == 0)
|
||||
{
|
||||
pFileNode->FileNameHash = CalcNormNameHash(szPathBuffer, i);
|
||||
InsertToHashTable(pFileNode);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
*/
|
||||
size_t CASC_FILE_TREE::GetMaxFileIndex()
|
||||
{
|
||||
if(FileDataIds.IsInitialized())
|
||||
|
||||
@@ -25,32 +25,25 @@
|
||||
typedef struct _CASC_FILE_NODE
|
||||
{
|
||||
ULONGLONG FileNameHash; // Jenkins hash of the normalized file name (uppercase, backslashes)
|
||||
PCASC_CKEY_ENTRY pCKeyEntry; // Pointer to the CKey entry.
|
||||
PCASC_CKEY_ENTRY pCKeyEntry; // Pointer to the CKey entry
|
||||
|
||||
DWORD Parent; // The index of a parent directory. If CASC_INVALID_INDEX, then this is the root item
|
||||
DWORD NameIndex; // Index of the node name. If CASC_INVALID_INDEX, then this node has no name
|
||||
USHORT NameLength; // Length of the node name (without the zero terminator)
|
||||
USHORT Flags; // See CFE_FLAG_XXX
|
||||
USHORT Flags; // See CFN_FLAG_XXX
|
||||
|
||||
DWORD ExtraValues[4]; // FileDataId: Only if FTREE_FLAG_USE_DATA_ID specified at create
|
||||
// LocaleFlags: Only if FTREE_FLAG_USE_LOCALE_FLAGS specified at create
|
||||
// ContentFlags: Only if FTREE_FLAG_USE_CONTENT_FLAGS specified at create
|
||||
} CASC_FILE_NODE, *PCASC_FILE_NODE;
|
||||
|
||||
// Common structure for comparing a file node
|
||||
typedef struct _CASC_COMPARE_CONTEXT
|
||||
{
|
||||
ULONGLONG FileNameHash;
|
||||
const char * szFileName;
|
||||
void * pThis;
|
||||
} CASC_COMPARE_CONTEXT, *PCASC_COMPARE_CONTEXT;
|
||||
|
||||
// Main structure for the file tree
|
||||
class CASC_FILE_TREE
|
||||
{
|
||||
public:
|
||||
|
||||
// Initializes/destroys the entire tree
|
||||
int Create(DWORD Flags = 0);
|
||||
DWORD Create(DWORD Flags = 0);
|
||||
void Free();
|
||||
|
||||
// Inserts a new node to the tree; either with name or nameless
|
||||
@@ -108,6 +101,8 @@ class CASC_FILE_TREE
|
||||
size_t FileDataIdOffset; // If nonzero, this is the offset of the "FileDataId" field in the CASC_FILE_NODE
|
||||
size_t LocaleFlagsOffset; // If nonzero, this is the offset of the "LocaleFlags" field in the CASC_FILE_NODE
|
||||
size_t ContentFlagsOffset; // If nonzero, this is the offset of the "ContentFlags" field in the CASC_FILE_NODE
|
||||
size_t FolderNodes; // Number of folder nodes
|
||||
size_t FileNodes; // Number of file nodes
|
||||
DWORD KeyLength; // Actual length of the key supported by the root handler
|
||||
};
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ static PLISTFILE_CACHE ListFile_CreateCache(DWORD dwFileSize)
|
||||
PLISTFILE_CACHE pCache;
|
||||
|
||||
// Allocate cache for one file block
|
||||
pCache = (PLISTFILE_CACHE)CASC_ALLOC(BYTE, sizeof(LISTFILE_CACHE) + dwFileSize);
|
||||
pCache = (PLISTFILE_CACHE)CASC_ALLOC<BYTE>(sizeof(LISTFILE_CACHE) + dwFileSize);
|
||||
if(pCache != NULL)
|
||||
{
|
||||
// Set the initial pointers
|
||||
@@ -254,7 +254,7 @@ size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PD
|
||||
PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile;
|
||||
const char * szTemp;
|
||||
size_t nLength = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD dwErrCode = ERROR_SUCCESS;
|
||||
|
||||
// Check for parameters
|
||||
for(;;)
|
||||
@@ -266,12 +266,12 @@ size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PD
|
||||
if(pCache->Flags & LISTFILE_FLAG_USES_FILEDATAID)
|
||||
{
|
||||
// Retrieve the data ID from the current position
|
||||
nError = ListFile_GetFileDataId(pCache, &FileDataId);
|
||||
if(nError == ERROR_NO_MORE_FILES)
|
||||
dwErrCode = ListFile_GetFileDataId(pCache, &FileDataId);
|
||||
if(dwErrCode == ERROR_NO_MORE_FILES)
|
||||
break;
|
||||
|
||||
// If there was an error, skip the current line
|
||||
if(nError != ERROR_SUCCESS || FileDataId == CASC_INVALID_ID)
|
||||
if(dwErrCode != ERROR_SUCCESS || FileDataId == CASC_INVALID_ID)
|
||||
{
|
||||
ListFile_GetNextLine(pvListFile, &szTemp, &szTemp);
|
||||
continue;
|
||||
@@ -282,7 +282,7 @@ size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PD
|
||||
nLength = ListFile_GetNextLine(pvListFile, szBuffer, nMaxChars);
|
||||
if(nLength == 0)
|
||||
{
|
||||
nError = GetLastError();
|
||||
dwErrCode = GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -291,8 +291,8 @@ size_t ListFile_GetNext(void * pvListFile, char * szBuffer, size_t nMaxChars, PD
|
||||
return nLength;
|
||||
}
|
||||
|
||||
if(nError != ERROR_SUCCESS)
|
||||
SetLastError(nError);
|
||||
if(dwErrCode != ERROR_SUCCESS)
|
||||
SetLastError(dwErrCode);
|
||||
return nLength;
|
||||
}
|
||||
|
||||
|
||||
@@ -129,13 +129,8 @@ class CASC_MAP
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Allocate new map for the objects
|
||||
m_HashTable = (void **)CASC_ALLOC(void *, m_HashTableSize);
|
||||
if(m_HashTable == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Initialize the map object
|
||||
memset(m_HashTable, 0, m_HashTableSize * sizeof(void *));
|
||||
return ERROR_SUCCESS;
|
||||
m_HashTable = (void **)CASC_ALLOC_ZERO<void *>(m_HashTableSize);
|
||||
return (m_HashTable != NULL) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
void * FindObject(void * pvKey, PDWORD PtrIndex = NULL)
|
||||
|
||||
182
dep/CascLib/src/common/Path.h
Normal file
182
dep/CascLib/src/common/Path.h
Normal file
@@ -0,0 +1,182 @@
|
||||
/*****************************************************************************/
|
||||
/* Path.h Copyright (c) Ladislav Zezula 2019 */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Global path merger class */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Date Ver Who Comment */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 24.07.17 1.00 Lad Created */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __CASC_PATH_H__
|
||||
#define __CASC_PATH_H__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structures
|
||||
|
||||
template <typename xchar>
|
||||
struct CASC_PATH
|
||||
{
|
||||
CASC_PATH(xchar chSeparator = PATH_SEP_CHAR)
|
||||
{
|
||||
m_szBufferBegin = m_szBufferPtr = m_Buffer;
|
||||
m_szBufferEnd = m_szBufferBegin + _countof(m_Buffer);
|
||||
m_chSeparator = chSeparator;
|
||||
m_Buffer[0] = 0;
|
||||
}
|
||||
|
||||
~CASC_PATH()
|
||||
{
|
||||
if(m_szBufferBegin != m_Buffer)
|
||||
{
|
||||
CASC_FREE(m_szBufferBegin);
|
||||
}
|
||||
}
|
||||
|
||||
// LPCTSTR szPath = Path;
|
||||
operator const xchar *()
|
||||
{
|
||||
return m_szBufferBegin;
|
||||
}
|
||||
|
||||
// LPTSTR szPath = Path.New();
|
||||
xchar * New()
|
||||
{
|
||||
xchar * szNewStr;
|
||||
|
||||
if((szNewStr = CASC_ALLOC<xchar>(Length() + 1)) != NULL)
|
||||
{
|
||||
memcpy(szNewStr, m_szBufferBegin, Length() * sizeof(xchar));
|
||||
szNewStr[Length()] = 0;
|
||||
}
|
||||
|
||||
return szNewStr;
|
||||
}
|
||||
|
||||
size_t Save()
|
||||
{
|
||||
return (m_szBufferPtr - m_szBufferBegin);
|
||||
}
|
||||
|
||||
bool Restore(size_t nSavePos)
|
||||
{
|
||||
if((m_szBufferBegin + nSavePos) < m_szBufferEnd)
|
||||
{
|
||||
m_szBufferPtr = m_szBufferBegin + nSavePos;
|
||||
m_szBufferPtr[0] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Path.Copy(szBuffer, _countof(szBuffer));
|
||||
bool Copy(xchar * szBuffer, size_t cchBuffer)
|
||||
{
|
||||
if((Length() + 1) > cchBuffer)
|
||||
return false;
|
||||
|
||||
memcpy(szBuffer, m_szBufferBegin, Length() * sizeof(xchar));
|
||||
szBuffer[Length()] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Length()
|
||||
{
|
||||
return m_szBufferPtr - m_szBufferBegin;
|
||||
}
|
||||
|
||||
bool SetPathRoot(const xchar * szRoot)
|
||||
{
|
||||
// Make sure that there is no characters
|
||||
m_szBufferPtr = m_szBufferBegin;
|
||||
m_szBufferPtr[0] = 0;
|
||||
|
||||
// Append the root path
|
||||
return AppendString(szRoot, false);
|
||||
}
|
||||
|
||||
bool AppendStringN(const xchar * szString, size_t nMaxchars, bool bWithSeparator)
|
||||
{
|
||||
const xchar * szStringEnd = szString + nMaxchars;
|
||||
xchar chOneChar;
|
||||
|
||||
if(szString && szString[0] && nMaxchars)
|
||||
{
|
||||
// Append separator, if required and not in begin of the string
|
||||
if(m_szBufferPtr > m_szBufferBegin && bWithSeparator)
|
||||
AppendChar(m_chSeparator);
|
||||
|
||||
// Append the characters from the string
|
||||
while(szString[0] && szString < szStringEnd)
|
||||
{
|
||||
// Retrieve the single character
|
||||
chOneChar = *szString++;
|
||||
|
||||
// Normalize the character
|
||||
if(chOneChar == '/' || chOneChar == '\\')
|
||||
chOneChar = m_chSeparator;
|
||||
|
||||
if(!AppendChar(chOneChar))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AppendString(const xchar * szString, bool bWithSeparator)
|
||||
{
|
||||
return AppendStringN(szString, (0x10000 / sizeof(xchar)), bWithSeparator);
|
||||
}
|
||||
|
||||
bool AppendEKey(LPBYTE pbEKey)
|
||||
{
|
||||
xchar szEKey[MD5_STRING_SIZE + 1];
|
||||
|
||||
StringFromBinary(pbEKey, MD5_HASH_SIZE, szEKey);
|
||||
AppendStringN(szEKey, 2, true);
|
||||
AppendStringN(szEKey+2, 2, true);
|
||||
return AppendString(szEKey, true);
|
||||
}
|
||||
|
||||
bool AppendChar(xchar chOneChar)
|
||||
{
|
||||
// Buffer our of space?
|
||||
if((m_szBufferPtr + 2) >= m_szBufferEnd)
|
||||
{
|
||||
xchar * szOldBuffer = m_szBufferBegin;
|
||||
xchar * szNewBuffer;
|
||||
size_t nToAllocate = (m_szBufferEnd - m_szBufferBegin) * 2;
|
||||
size_t nLength = (m_szBufferPtr - m_szBufferBegin);
|
||||
|
||||
if((szNewBuffer = CASC_ALLOC<xchar>(nToAllocate)) == NULL)
|
||||
return false;
|
||||
|
||||
// Copy the chars
|
||||
memcpy(szNewBuffer, m_szBufferBegin, (m_szBufferPtr - m_szBufferBegin) * sizeof(xchar));
|
||||
m_szBufferBegin = szNewBuffer;
|
||||
m_szBufferPtr = m_szBufferBegin + nLength;
|
||||
m_szBufferEnd = m_szBufferBegin + nToAllocate;
|
||||
|
||||
// Free the old buffer
|
||||
if(szOldBuffer != m_Buffer)
|
||||
CASC_FREE(szOldBuffer);
|
||||
}
|
||||
|
||||
// Append the character
|
||||
*m_szBufferPtr++ = chOneChar;
|
||||
m_szBufferPtr[0] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
xchar * m_szBufferBegin;
|
||||
xchar * m_szBufferPtr;
|
||||
xchar * m_szBufferEnd;
|
||||
xchar m_Buffer[MAX_PATH];
|
||||
xchar m_chSeparator;
|
||||
};
|
||||
|
||||
#endif // __CASC_PATH_H__
|
||||
@@ -81,10 +81,6 @@ PCASC_CKEY_ENTRY TFileTreeRoot::Search(TCascSearch * pSearch, PCASC_FIND_DATA pF
|
||||
// Retrieve the extra values (FileDataId, file size and locale flags)
|
||||
FileTree.GetExtras(pFileNode, &pFindData->dwFileDataId, &pFindData->dwLocaleFlags, &pFindData->dwContentFlags);
|
||||
|
||||
// Supply the bCanOpenByDataId variable
|
||||
pFindData->bCanOpenByName = (pFileNode->FileNameHash != 0);
|
||||
pFindData->bCanOpenByDataId = (pFindData->dwFileDataId != CASC_INVALID_ID);
|
||||
|
||||
// Return the found CKey entry
|
||||
return pFileNode->pCKeyEntry;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ recastnavigation (Recast is state of the art navigation mesh construction toolse
|
||||
|
||||
CascLib (An open-source implementation of library for reading CASC storage from Blizzard games since 2014)
|
||||
https://github.com/ladislav-zezula/CascLib
|
||||
Version: b802391cfb9418d5550dc1671f18da1ca1c445d4
|
||||
Version: b91f87c770c78340dcd96df970e55b5c0469e884
|
||||
|
||||
rapidjson (A fast JSON parser/generator for C++ with both SAX/DOM style API http://rapidjson.org/)
|
||||
https://github.com/miloyip/rapidjson
|
||||
|
||||
Reference in New Issue
Block a user