aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2019-08-10 19:01:24 +0200
committerShauren <shauren.trinity@gmail.com>2019-08-10 19:01:24 +0200
commitcd720efbfa60f434f420ab66e220eca742c48e45 (patch)
tree3c960ac5249d0711b71fbfdc62f6c0b665ed85ee /dep/CascLib
parent0d6320dfd3932865edb69c8528327b767bd476ef (diff)
Dep/CascLib: Update to ladislav-zezula/CascLib@b91f87c770c78340dcd96df970e55b5c0469e884
Diffstat (limited to 'dep/CascLib')
-rw-r--r--dep/CascLib/CMakeLists.txt3
-rw-r--r--dep/CascLib/src/CascCommon.cpp144
-rw-r--r--dep/CascLib/src/CascCommon.h245
-rw-r--r--dep/CascLib/src/CascDecompress.cpp8
-rw-r--r--dep/CascLib/src/CascDecrypt.cpp102
-rw-r--r--dep/CascLib/src/CascFiles.cpp790
-rw-r--r--dep/CascLib/src/CascFindFile.cpp70
-rw-r--r--dep/CascLib/src/CascIndexFiles.cpp342
-rw-r--r--dep/CascLib/src/CascLib.def5
-rw-r--r--dep/CascLib/src/CascLib.h104
-rw-r--r--dep/CascLib/src/CascOpenFile.cpp271
-rw-r--r--dep/CascLib/src/CascOpenStorage.cpp652
-rw-r--r--dep/CascLib/src/CascPort.h24
-rw-r--r--dep/CascLib/src/CascReadFile.cpp1045
-rw-r--r--dep/CascLib/src/CascRootFile_Diablo3.cpp219
-rw-r--r--dep/CascLib/src/CascRootFile_Install.cpp65
-rw-r--r--dep/CascLib/src/CascRootFile_MNDX.cpp328
-rw-r--r--dep/CascLib/src/CascRootFile_OW.cpp42
-rw-r--r--dep/CascLib/src/CascRootFile_TVFS.cpp309
-rw-r--r--dep/CascLib/src/CascRootFile_Text.cpp20
-rw-r--r--dep/CascLib/src/CascRootFile_WoW.cpp45
-rw-r--r--dep/CascLib/src/CascStructs.h2
-rw-r--r--dep/CascLib/src/DllMain.rc8
-rw-r--r--dep/CascLib/src/common/Array.h18
-rw-r--r--dep/CascLib/src/common/Common.cpp184
-rw-r--r--dep/CascLib/src/common/Common.h99
-rw-r--r--dep/CascLib/src/common/Csv.cpp18
-rw-r--r--dep/CascLib/src/common/Csv.h4
-rw-r--r--dep/CascLib/src/common/FileStream.cpp50
-rw-r--r--dep/CascLib/src/common/FileStream.h6
-rw-r--r--dep/CascLib/src/common/FileTree.cpp162
-rw-r--r--dep/CascLib/src/common/FileTree.h17
-rw-r--r--dep/CascLib/src/common/ListFile.cpp16
-rw-r--r--dep/CascLib/src/common/Map.h9
-rw-r--r--dep/CascLib/src/common/Path.h182
-rw-r--r--dep/CascLib/src/common/RootHandler.cpp4
36 files changed, 3317 insertions, 2295 deletions
diff --git a/dep/CascLib/CMakeLists.txt b/dep/CascLib/CMakeLists.txt
index 5c3fa7c55e6..cce693a6146 100644
--- a/dep/CascLib/CMakeLists.txt
+++ b/dep/CascLib/CMakeLists.txt
@@ -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
diff --git a/dep/CascLib/src/CascCommon.cpp b/dep/CascLib/src/CascCommon.cpp
deleted file mode 100644
index f5e346ec14c..00000000000
--- a/dep/CascLib/src/CascCommon.cpp
+++ /dev/null
@@ -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;
- }
-}
diff --git a/dep/CascLib/src/CascCommon.h b/dep/CascLib/src/CascCommon.h
index b1248544b9f..a0caa5d85eb 100644
--- a/dep/CascLib/src/CascCommon.h
+++ b/dep/CascLib/src/CascCommon.h
@@ -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;
-
- // 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;
+ TCascFile(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry);
+ ~TCascFile();
- // 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;
}
+ // Class recognizer. Has constant value of 'CASCFILE' (CASC_MAGIC_FILE)
+ ULONGLONG ClassName;
+
+ // Class members
TCascStorage * hs; // Pointer to storage structure
- TFileStream * pStream; // An open data stream
- const char * szClassName; // "TCascFile"
-
- 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
+
+ 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)
diff --git a/dep/CascLib/src/CascDecompress.cpp b/dep/CascLib/src/CascDecompress.cpp
index 3262c59ddf7..42c4758eb79 100644
--- a/dep/CascLib/src/CascDecompress.cpp
+++ b/dep/CascLib/src/CascDecompress.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;
}
diff --git a/dep/CascLib/src/CascDecrypt.cpp b/dep/CascLib/src/CascDecrypt.cpp
index c5d491da2de..1e07342d9c5 100644
--- a/dep/CascLib/src/CascDecrypt.cpp
+++ b/dep/CascLib/src/CascDecrypt.cpp
@@ -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)
diff --git a/dep/CascLib/src/CascFiles.cpp b/dep/CascLib/src/CascFiles.cpp
index 5f4080bc4f2..8d90df23000 100644
--- a/dep/CascLib/src/CascFiles.cpp
+++ b/dep/CascLib/src/CascFiles.cpp
@@ -17,8 +17,8 @@
// Local defines
typedef DWORD (*PARSECSVFILE)(TCascStorage * hs, CASC_CSV & Csv);
-typedef int (*PARSETEXTFILE)(TCascStorage * hs, void * pvListFile);
-typedef int (*PARSE_VARIABLE)(TCascStorage * hs, const char * szVariableName, const char * szDataBegin, const char * szDataEnd, void * pvParam);
+typedef DWORD (*PARSETEXTFILE)(TCascStorage * hs, void * pvListFile);
+typedef DWORD (*PARSE_VARIABLE)(TCascStorage * hs, const char * szVariableName, const char * szDataBegin, const char * szDataEnd, void * pvParam);
#define MAX_VAR_NAME 80
@@ -56,26 +56,6 @@ static const TCHAR * DataDirs[] =
NULL,
};
-static const TGameLocaleString LocaleStrings[] =
-{
- {"enUS", CASC_LOCALE_ENUS},
- {"koKR", CASC_LOCALE_KOKR},
- {"frFR", CASC_LOCALE_FRFR},
- {"deDE", CASC_LOCALE_DEDE},
- {"zhCN", CASC_LOCALE_ZHCN},
- {"esES", CASC_LOCALE_ESES},
- {"zhTW", CASC_LOCALE_ZHTW},
- {"enGB", CASC_LOCALE_ENGB},
- {"enCN", CASC_LOCALE_ENCN},
- {"enTW", CASC_LOCALE_ENTW},
- {"esMX", CASC_LOCALE_ESMX},
- {"ruRU", CASC_LOCALE_RURU},
- {"ptBR", CASC_LOCALE_PTBR},
- {"itIT", CASC_LOCALE_ITIT},
- {"ptPT", CASC_LOCALE_PTPT},
- {NULL, 0}
-};
-
//-----------------------------------------------------------------------------
// Local functions
@@ -94,6 +74,11 @@ static bool IsCharDigit(BYTE OneByte)
return ('0' <= OneByte && OneByte <= '9');
}
+static bool StringEndsWith(LPCSTR szString, size_t nLength, LPCSTR szSuffix, size_t nSuffixLength)
+{
+ return ((nLength > nSuffixLength) && !strcmp(szString + nLength - nSuffixLength, szSuffix));
+}
+
static const char * CaptureDecimalInteger(const char * szDataPtr, const char * szDataEnd, PDWORD PtrValue)
{
const char * szSaveDataPtr = szDataPtr;
@@ -197,14 +182,34 @@ static const char * CaptureHashCount(const char * szDataPtr, const char * szData
return szDataPtr;
}
-static DWORD GetLocaleMask(const char * szTag)
+static DWORD GetLocaleValue(LPCSTR szTag)
{
- for(size_t i = 0; LocaleStrings[i].szLocale != NULL; i++)
+ DWORD Language = 0;
+
+ // Convert the string language to integer
+ Language = (Language << 0x08) | szTag[0];
+ Language = (Language << 0x08) | szTag[1];
+ Language = (Language << 0x08) | szTag[2];
+ Language = (Language << 0x08) | szTag[3];
+
+ // Language-specific action
+ switch(Language)
{
- if(!strncmp(LocaleStrings[i].szLocale, szTag, 4))
- {
- return LocaleStrings[i].dwLocale;
- }
+ case 0x656e5553: return CASC_LOCALE_ENUS;
+ case 0x656e4742: return CASC_LOCALE_ENGB;
+ case 0x656e434e: return CASC_LOCALE_ENCN;
+ case 0x656e5457: return CASC_LOCALE_ENTW;
+ case 0x65734553: return CASC_LOCALE_ESES;
+ case 0x65734d58: return CASC_LOCALE_ESMX;
+ case 0x70744252: return CASC_LOCALE_PTBR;
+ case 0x70745054: return CASC_LOCALE_PTPT;
+ case 0x7a68434e: return CASC_LOCALE_ZHCN;
+ case 0x7a685457: return CASC_LOCALE_ZHTW;
+ case 0x6b6f4b52: return CASC_LOCALE_KOKR;
+ case 0x66724652: return CASC_LOCALE_FRFR;
+ case 0x64654445: return CASC_LOCALE_DEDE;
+ case 0x72755255: return CASC_LOCALE_RURU;
+ case 0x69744954: return CASC_LOCALE_ITIT;
}
return 0;
@@ -240,17 +245,17 @@ static bool CheckConfigFileVariable(
return (PfnParseProc(hs, szVariableName, szLinePtr, szLineEnd, pvParseParam) == ERROR_SUCCESS);
}
-static int LoadHashArray(
+static DWORD LoadHashArray(
PQUERY_KEY pBlob,
const char * szLinePtr,
const char * szLineEnd,
size_t HashCount)
{
- int nError = ERROR_NOT_ENOUGH_MEMORY;
+ DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
// Allocate the blob buffer
pBlob->cbData = (DWORD)(HashCount * MD5_HASH_SIZE);
- pBlob->pbData = CASC_ALLOC(BYTE, pBlob->cbData);
+ pBlob->pbData = CASC_ALLOC<BYTE>(pBlob->cbData);
if (pBlob->pbData != NULL)
{
LPBYTE pbBuffer = pBlob->pbData;
@@ -266,16 +271,16 @@ static int LoadHashArray(
pbBuffer += MD5_HASH_SIZE;
}
- nError = ERROR_SUCCESS;
+ dwErrCode = ERROR_SUCCESS;
}
- return nError;
+ return dwErrCode;
}
-static int LoadMultipleHashes(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd)
+static DWORD LoadMultipleHashes(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd)
{
size_t HashCount = 0;
- int nError = ERROR_SUCCESS;
+ DWORD dwErrCode = ERROR_SUCCESS;
// Retrieve the hash count
if (CaptureHashCount(szLineBegin, szLineEnd, &HashCount) == NULL)
@@ -284,41 +289,47 @@ static int LoadMultipleHashes(PQUERY_KEY pBlob, const char * szLineBegin, const
// Do nothing if there is no data
if(HashCount != 0)
{
- nError = LoadHashArray(pBlob, szLineBegin, szLineEnd, HashCount);
+ dwErrCode = LoadHashArray(pBlob, szLineBegin, szLineEnd, HashCount);
}
- return nError;
+ return dwErrCode;
}
// Loads a query key from the text form
// A QueryKey is an array of "ContentKey EncodedKey1 ... EncodedKeyN"
-static int LoadQueryKey(TCascStorage * /* hs */, const char * /* szVariableName */, const char * szDataBegin, const char * szDataEnd, void * pvParam)
+static DWORD LoadQueryKey(TCascStorage * /* hs */, const char * /* szVariableName */, const char * szDataBegin, const char * szDataEnd, void * pvParam)
{
return LoadMultipleHashes((PQUERY_KEY)pvParam, szDataBegin, szDataEnd);
}
-static int LoadCKeyEntry(TCascStorage * hs, const char * szVariableName, const char * szDataPtr, const char * szDataEnd, void * pvParam)
+static DWORD LoadCKeyEntry(TCascStorage * hs, const char * szVariableName, const char * szDataPtr, const char * szDataEnd, void * pvParam)
{
PCASC_CKEY_ENTRY pCKeyEntry = (PCASC_CKEY_ENTRY)pvParam;
size_t nLength = strlen(szVariableName);
size_t HashCount = 0;
+ // Ignore "xxx-config" items
+ if(StringEndsWith(szVariableName, nLength, "-config", 7))
+ return ERROR_SUCCESS;
+
// If the variable ends at "-size", it means we need to capture the size
- if((nLength > 5) && !strcmp(szVariableName + nLength - 5, "-size"))
+ if(StringEndsWith(szVariableName, nLength, "-size", 5))
{
DWORD ContentSize = CASC_INVALID_SIZE;
DWORD EncodedSize = CASC_INVALID_SIZE;
// Load the content size
szDataPtr = CaptureDecimalInteger(szDataPtr, szDataEnd, &ContentSize);
- if(szDataPtr != NULL)
- {
- CaptureDecimalInteger(szDataPtr, szDataEnd, &EncodedSize);
- pCKeyEntry->ContentSize = ContentSize;
- pCKeyEntry->EncodedSize = EncodedSize;
- return ERROR_SUCCESS;
- }
+ if(szDataPtr == NULL)
+ return ERROR_BAD_FORMAT;
+
+ CaptureDecimalInteger(szDataPtr, szDataEnd, &EncodedSize);
+ pCKeyEntry->ContentSize = ContentSize;
+ pCKeyEntry->EncodedSize = EncodedSize;
+ return ERROR_SUCCESS;
}
+
+ // If the string doesn't end with "-config", assume it's the CKey/EKey
else
{
// Get the number of hashes. It is expected to be 1 or 2
@@ -355,7 +366,30 @@ static int LoadCKeyEntry(TCascStorage * hs, const char * szVariableName, const c
return ERROR_BAD_FORMAT;
}
-static int LoadVfsRootEntry(TCascStorage * hs, const char * szVariableName, const char * szDataPtr, const char * szDataEnd, void * pvParam)
+// For files like PATCH, which only contain EKey in the build file
+static DWORD LoadEKeyEntry(TCascStorage * hs, const char * szVariableName, const char * szDataPtr, const char * szDataEnd, void * pvParam)
+{
+ PCASC_CKEY_ENTRY pCKeyEntry = (PCASC_CKEY_ENTRY)pvParam;
+ DWORD dwErrCode;
+
+ // Load the entry as if it was a CKey. Then move CKey into EKey and adjust flags
+ if((dwErrCode = LoadCKeyEntry(hs, szVariableName, szDataPtr, szDataEnd, pvParam)) == ERROR_SUCCESS)
+ {
+ if((pCKeyEntry->Flags & (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY | CASC_CE_HAS_EKEY_PARTIAL)) == CASC_CE_HAS_CKEY)
+ {
+ // Move the CKey into EKey
+ CopyMemory16(pCKeyEntry->EKey, pCKeyEntry->CKey);
+ ZeroMemory16(pCKeyEntry->CKey);
+
+ // Adjust flags
+ pCKeyEntry->Flags = (pCKeyEntry->Flags & ~CASC_CE_HAS_CKEY) | CASC_CE_HAS_EKEY;
+ }
+ }
+
+ return dwErrCode;
+}
+
+static DWORD LoadVfsRootEntry(TCascStorage * hs, const char * szVariableName, const char * szDataPtr, const char * szDataEnd, void * pvParam)
{
PCASC_CKEY_ENTRY pCKeyEntry;
CASC_ARRAY * pArray = (CASC_ARRAY *)pvParam;
@@ -397,93 +431,16 @@ static int LoadVfsRootEntry(TCascStorage * hs, const char * szVariableName, cons
return ERROR_SUCCESS;
}
-static int LoadBuildProductId(TCascStorage * hs, const char * /* szVariableName */, const char * szDataBegin, const char * szDataEnd, void * /* pvParam */)
+static DWORD LoadBuildProductId(TCascStorage * hs, const char * /* szVariableName */, const char * szDataBegin, const char * szDataEnd, void * /* pvParam */)
{
- DWORD dwBuildUid = 0;
-
- // Convert up to 4 chars to DWORD
- for(size_t i = 0; i < 4 && szDataBegin < szDataEnd; i++)
- {
- dwBuildUid = (dwBuildUid << 0x08) | (BYTE)szDataBegin[0];
- szDataBegin++;
- }
+ size_t nLength = (szDataEnd - szDataBegin);
- // Product-specific. See https://wowdev.wiki/TACT#Products
- switch(dwBuildUid)
+ if(hs->szCodeName == NULL)
{
- case 0x00006433: // 'd3'
- case 0x00643364: // 'd3b': Diablo 3 Beta (2013)
- case 0x6433636e: // 'd3cn': Diablo 3 China
- case 0x00643374: // 'd3t': Diablo 3 Test
- hs->szProductName = "Diablo 3";
- hs->Product = Diablo3;
- break;
-
- case 0x64737432: // 'dst2':
- hs->szProductName = "Destiny 2";
- hs->Product = Destiny2;
- break;
-
- case 0x00626e74: // 'bnt': Heroes of the Storm Alpha
- case 0x6865726f: // 'hero': Heroes of the Storm Retail
- case 0x73746f72: // 'stor': Heroes of the Storm (deprecated)
- hs->szProductName = "Heroes Of The Storm";
- hs->Product = HeroesOfTheStorm;
- break;
-
- case 0x0070726f: // 'pro':
- case 0x70726f63: // 'proc':
- case 0x70726f64: // 'prod': "prodev": Overwatch Dev
- case 0x70726f65: // 'proe': Not on public CDNs
- case 0x70726f74: // 'prot': Overwatch Test
- case 0x70726f76: // 'prov': Overwatch Vendor
- case 0x70726f6d: // 'prom': "proms": Overwatch World Cup Viewer
- hs->szProductName = "Overwatch";
- hs->Product = Overwatch;
- break;
-
- case 0x00007331: // 's1': StarCraft 1
- case 0x00733161: // 's1a': Starcraft 1 Alpha
- case 0x00733174: // 's1t': StarCraft 1 Test
- hs->szProductName = "Starcraft 1";
- hs->Product = StarCraft1;
- break;
-
- case 0x00007332: // 's2': StarCraft 2
- case 0x00733262: // 's2b': Starcraft 2 Beta
- case 0x00733274: // 's2t': StarCraft 2 Test
- case 0x00736332: // 'sc2': StarCraft 2 (deprecated)
- hs->szProductName = "Starcraft 2";
- hs->Product = StarCraft2;
- break;
-
- case 0x76697065: // "viper", "viperdev", "viperv1": Call of Duty Black Ops 4
- hs->szProductName = "Call Of Duty Black Ops 4";
- hs->Product = CallOfDutyBlackOps4;
- break;
-
- case 0x00007733: // 'w3': Warcraft III
- case 0x00773374: // 'w3t': Warcraft III Public Test
- case 0x77617233: // 'war3': Warcraft III (old)
- hs->szProductName = "WarCraft 3";
- hs->Product = WarCraft3;
- break;
-
- case 0x00776f77: // 'wow': World of Warcraft
- case 0x776f775f: // "wow_beta", "wow_classic", "wow_classic_beta"
- case 0x776f7764: // "wowdev", "wowdemo"
- case 0x776f7765: // "wowe1", "wowe3", "wowe3"
- case 0x776f7774: // 'wowt': World of Warcraft Test
- case 0x776f7776: // 'wowv': World of Warcraft Vendor
- case 0x776f777a: // 'wowz': World of Warcraft Submission (previously Vendor)
- hs->szProductName = "World Of Warcraft";
- hs->Product = WorldOfWarcraft;
- break;
-
- default:
- hs->szProductName = "Unknown Product";
- hs->Product = UnknownProduct;
- break;
+ if((hs->szCodeName = CASC_ALLOC<TCHAR>(nLength + 1)) != NULL)
+ {
+ CascStrCopy(hs->szCodeName, nLength + 1, szDataBegin, nLength);
+ }
}
return ERROR_SUCCESS;
@@ -493,7 +450,7 @@ static int LoadBuildProductId(TCascStorage * hs, const char * /* szVariableName
// "WOW-18125patch6.0.1"
// "30013_Win32_2_2_0_Ptr_ptr"
// "prometheus-0_8_0_0-24919"
-static int LoadBuildNumber(TCascStorage * hs, const char * /* szVariableName */, const char * szDataBegin, const char * szDataEnd, void * /* pvParam */)
+static DWORD LoadBuildNumber(TCascStorage * hs, const char * /* szVariableName */, const char * szDataBegin, const char * szDataEnd, void * /* pvParam */)
{
DWORD dwBuildNumber = 0;
DWORD dwMaxValue = 0;
@@ -535,19 +492,30 @@ static int LoadQueryKey(const CASC_CSV_COLUMN & Column, QUERY_KEY & Key)
return LoadHashArray(&Key, Column.szValue, Column.szValue + Column.nLength, 1);
}
+static void SetProductCodeName(TCascStorage * hs, LPCSTR szCodeName)
+{
+ TCHAR szCodeNameT[0x40];
+
+ if(hs->szCodeName == NULL && szCodeName != NULL)
+ {
+ CascStrCopy(szCodeNameT, _countof(szCodeNameT), szCodeName);
+ hs->szCodeName = CascNewStr(szCodeNameT);
+ }
+}
+
static int GetDefaultLocaleMask(TCascStorage * hs, const CASC_CSV_COLUMN & Column)
{
- const char * szTagEnd = Column.szValue + Column.nLength;
- const char * szTagPtr = Column.szValue;
+ LPCSTR szTagEnd = Column.szValue + Column.nLength - 4;
+ LPCSTR szTagPtr = Column.szValue;
DWORD dwLocaleMask = 0;
while(szTagPtr < szTagEnd)
{
- // Could this be a locale string?
- if((szTagPtr + 4) <= szTagEnd && (szTagPtr[4] == ',' || szTagPtr[4] == ' '))
+ DWORD dwLocaleValue = GetLocaleValue(szTagPtr);
+
+ if(dwLocaleValue != 0)
{
- // Check whether the current tag is a language identifier
- dwLocaleMask = dwLocaleMask | GetLocaleMask(szTagPtr);
+ dwLocaleMask = dwLocaleMask | GetLocaleValue(szTagPtr);
szTagPtr += 4;
}
else
@@ -560,17 +528,6 @@ static int GetDefaultLocaleMask(TCascStorage * hs, const CASC_CSV_COLUMN & Colum
return ERROR_SUCCESS;
}
-static void SetProductCodeName(TCascStorage * hs, LPCSTR szCodeName)
-{
- TCHAR szCodeNameT[0x40];
-
- if(hs->szCodeName == NULL && szCodeName != NULL)
- {
- CascStrCopy(szCodeNameT, _countof(szCodeNameT), szCodeName);
- hs->szCodeName = CascNewStr(szCodeNameT);
- }
-}
-
static DWORD ParseFile_CDNS(TCascStorage * hs, CASC_CSV & Csv)
{
const char * szWantedRegion = hs->szRegion;
@@ -617,7 +574,6 @@ static DWORD ParseFile_BuildInfo(TCascStorage * hs, CASC_CSV & Csv)
size_t nLineCount = Csv.GetLineCount();
size_t nSelected = CASC_INVALID_INDEX;
DWORD dwErrCode;
- char szWantedCodeName[0x40] = "";
//
// Find the configuration that we're gonna load.
@@ -664,12 +620,6 @@ static DWORD ParseFile_BuildInfo(TCascStorage * hs, CASC_CSV & Csv)
}
}
- // If the product is specified by hs->szCodeName and the ".build.info" contains "Product!STRING:0", we watch for that product.
- if(hs->szCodeName != NULL && nProductColumnIndex != CASC_INVALID_INDEX)
- {
- CascStrCopy(szWantedCodeName, _countof(szWantedCodeName), hs->szCodeName);
- }
-
// Parse all lines in the CSV file. Either take the first that is active
// or take the one that has been selected by the callback
for(size_t i = 0; i < nLineCount; i++)
@@ -677,29 +627,28 @@ static DWORD ParseFile_BuildInfo(TCascStorage * hs, CASC_CSV & Csv)
// Ignore anything that is not marked "active"
if(!strcmp(Csv[i]["Active!DEC:1"].szValue, "1"))
{
- // If we have no product code name specified, we pick the very first active build
- if(hs->szCodeName == NULL)
+ // If the product code is specified and exists in the build file, we need to check it
+ if(hs->szCodeName != NULL && (szCodeName = Csv[i]["Product!STRING:0"].szValue) != NULL)
{
+ TCHAR szBuffer[0x20];
+
+ // Skip if they are not equal
+ CascStrCopy(szBuffer, _countof(szBuffer), szCodeName);
+ if(_tcsicmp(hs->szCodeName, szBuffer))
+ continue;
+
// Save the code name of the selected product
SetProductCodeName(hs, Csv[i]["Product!STRING:0"].szValue);
nSelected = i;
- goto __ChooseThisProduct;
+ break;
}
- // If we have a product code name specified, pick the first matching
- else if((szCodeName = Csv[i]["Product!STRING:0"].szValue) != NULL)
- {
- if(!strcmp(szCodeName, szWantedCodeName))
- {
- nSelected = i;
- goto __ChooseThisProduct;
- }
- }
+ // If the caller didn't specify the product code name, pick the first active
+ nSelected = i;
+ break;
}
}
- __ChooseThisProduct:
-
// Load the selected product
if(nSelected < nLineCount)
{
@@ -779,18 +728,15 @@ static DWORD ParseFile_BuildDb(TCascStorage * hs, CASC_CSV & Csv)
if (dwErrCode != ERROR_SUCCESS)
return dwErrCode;
- // Load the the tags
- GetDefaultLocaleMask(hs, Csv[CSV_ZERO][2]);
-
// Verify all variables
return (hs->CdnBuildKey.pbData != NULL && hs->CdnConfigKey.pbData != NULL) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
}
-static int ParseFile_CdnConfig(TCascStorage * hs, void * pvListFile)
+static DWORD ParseFile_CdnConfig(TCascStorage * hs, void * pvListFile)
{
const char * szLineBegin;
const char * szLineEnd;
- int nError = ERROR_SUCCESS;
+ DWORD dwErrCode = ERROR_SUCCESS;
// Keep parsing the listfile while there is something in there
for(;;)
@@ -827,19 +773,19 @@ static int ParseFile_CdnConfig(TCascStorage * hs, void * pvListFile)
if(hs->ArchivesKey.pbData == NULL || hs->ArchivesKey.cbData == 0)
return ERROR_BAD_FORMAT;
- return nError;
+ return dwErrCode;
}
-static int ParseFile_CdnBuild(TCascStorage * hs, void * pvListFile)
+static DWORD ParseFile_CdnBuild(TCascStorage * hs, void * pvListFile)
{
const char * szLineBegin;
const char * szLineEnd = NULL;
- int nError;
+ DWORD dwErrCode;
// Initialize the empty VFS array
- nError = hs->VfsRootList.Create<CASC_CKEY_ENTRY>(0x10);
- if (nError != ERROR_SUCCESS)
- return nError;
+ dwErrCode = hs->VfsRootList.Create<CASC_CKEY_ENTRY>(0x10);
+ if (dwErrCode != ERROR_SUCCESS)
+ return dwErrCode;
// Parse all variables
while(ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd) != 0)
@@ -870,7 +816,7 @@ static int ParseFile_CdnBuild(TCascStorage * hs, void * pvListFile)
continue;
// PATCH file
- if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "patch*", LoadCKeyEntry, &hs->PatchFile))
+ if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "patch*", LoadEKeyEntry, &hs->PatchFile))
continue;
// SIZE file
@@ -887,30 +833,21 @@ static int ParseFile_CdnBuild(TCascStorage * hs, void * pvListFile)
}
// Special case: Some builds of WoW (22267) don't have the variable in the build file
- if(hs->szProductName == NULL || hs->Product == UnknownProduct)
+ if(hs->szCodeName == NULL && hs->szCdnPath != NULL)
{
- if(hs->szCdnPath != NULL)
- {
- TCHAR * szPlainName = (TCHAR *)GetPlainFileName(hs->szCdnPath);
-
- if(szPlainName[0] == 'w' && szPlainName[1] == 'o' && szPlainName[2] == 'w')
- {
- hs->szProductName = "World Of Warcraft";
- hs->Product = WorldOfWarcraft;
- }
- }
+ hs->szCodeName = CascNewStr(GetPlainFileName(hs->szCdnPath));
}
// Both CKey and EKey of ENCODING file is required
if((hs->EncodingCKey.Flags & (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY)) != (CASC_CE_HAS_CKEY | CASC_CE_HAS_EKEY))
- return ERROR_BAD_FORMAT;
- return nError;
+ dwErrCode = ERROR_BAD_FORMAT;
+ return dwErrCode;
}
-static int CheckDataDirectory(TCascStorage * hs, TCHAR * szDirectory)
+static DWORD CheckDataDirectory(TCascStorage * hs, TCHAR * szDirectory)
{
TCHAR * szDataPath;
- int nError = ERROR_FILE_NOT_FOUND;
+ DWORD dwErrCode = ERROR_FILE_NOT_FOUND;
// Try all known subdirectories
for(size_t i = 0; DataDirs[i] != NULL; i++)
@@ -932,7 +869,7 @@ static int CheckDataDirectory(TCascStorage * hs, TCHAR * szDirectory)
}
}
- return nError;
+ return dwErrCode;
}
static DWORD LoadCsvFile(TCascStorage * hs, const TCHAR * szFileName, PARSECSVFILE PfnParseProc, bool bHasHeader)
@@ -973,12 +910,68 @@ static const TCHAR * ExtractCdnServerName(TCHAR * szServerName, size_t cchServer
return NULL;
}
-static int ForcePathExist(const TCHAR * szFileName, bool bIsFileName)
+static void CreateRemoteAndLocalPath(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInfo, CASC_PATH<TCHAR> & RemotePath, CASC_PATH<TCHAR> & LocalPath)
+{
+ PCASC_EKEY_ENTRY pEKeyEntry;
+ ULONGLONG ByteMask = 1;
+ DWORD ArchiveIndex;
+
+ // Append the CDN host / root folder
+ RemotePath.SetPathRoot(CdnsInfo.szCdnsHost);
+ RemotePath.AppendString(CdnsInfo.szCdnsPath, true);
+ LocalPath.SetPathRoot(hs->szRootPath);
+
+ // If there is an EKey, take EKey
+ if(CdnsInfo.pbEKey != NULL)
+ {
+ // The file is given by EKey. It's either a loose file, or it's stored in an archive.
+ // We check that using the EKey map
+ if ((pEKeyEntry = (PCASC_EKEY_ENTRY)hs->IndexMap.FindObject(CdnsInfo.pbEKey)) != NULL)
+ {
+ // Change the path type to "data"
+ RemotePath.AppendString(_T("data"), true);
+ LocalPath.AppendString(_T("data"), true);
+
+ // Append the EKey of the archive instead of the file itself
+ ArchiveIndex = (DWORD)(pEKeyEntry->StorageOffset >> hs->FileOffsetBits);
+ CdnsInfo.pbArchiveKey = hs->ArchivesKey.pbData + (MD5_HASH_SIZE * ArchiveIndex);
+ RemotePath.AppendEKey(CdnsInfo.pbArchiveKey);
+ LocalPath.AppendEKey(CdnsInfo.pbArchiveKey);
+
+ // Get the archive index and archive offset
+ CdnsInfo.ArchiveIndex = ArchiveIndex;
+ CdnsInfo.ArchiveOffs = pEKeyEntry->StorageOffset & ((ByteMask << hs->FileOffsetBits) - 1);
+ CdnsInfo.EncodedSize = pEKeyEntry->EncodedSize;
+ }
+ else
+ {
+ // Append the path type
+ RemotePath.AppendString(CdnsInfo.szPathType, true);
+ LocalPath.AppendString((CdnsInfo.szLoPaType != NULL) ? CdnsInfo.szLoPaType : CdnsInfo.szPathType, true);
+
+ // Append the EKey
+ RemotePath.AppendEKey(CdnsInfo.pbEKey);
+ LocalPath.AppendEKey(CdnsInfo.pbEKey);
+ }
+ }
+ else
+ {
+ assert(CdnsInfo.szFileName != NULL);
+ RemotePath.AppendString(CdnsInfo.szFileName, true);
+ LocalPath.AppendString(CdnsInfo.szFileName, true);
+ }
+
+ // Append extension
+ RemotePath.AppendString(CdnsInfo.szExtension, false);
+ LocalPath.AppendString(CdnsInfo.szExtension, false);
+}
+
+static DWORD ForcePathExist(const TCHAR * szFileName, bool bIsFileName)
{
TCHAR * szLocalPath;
size_t nIndex;
bool bFirstSeparator = false;
- int nError = ERROR_NOT_ENOUGH_MEMORY;
+ DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
// Sanity checks
if(szFileName && szFileName[0])
@@ -1007,7 +1000,7 @@ static int ForcePathExist(const TCHAR * szFileName, bool bIsFileName)
// Is it there?
if(DirectoryExists(szLocalPath) == false && MakeDirectory(szLocalPath) == false)
{
- nError = ERROR_PATH_NOT_FOUND;
+ dwErrCode = ERROR_PATH_NOT_FOUND;
break;
}
}
@@ -1015,31 +1008,31 @@ static int ForcePathExist(const TCHAR * szFileName, bool bIsFileName)
// Restore the character
szLocalPath[nIndex] = PATH_SEP_CHAR;
bFirstSeparator = true;
- nError = ERROR_SUCCESS;
+ dwErrCode = ERROR_SUCCESS;
}
}
// Now check the final path
if(DirectoryExists(szLocalPath) || MakeDirectory(szLocalPath))
{
- nError = ERROR_SUCCESS;
+ dwErrCode = ERROR_SUCCESS;
}
}
else
{
- nError = ERROR_SUCCESS;
+ dwErrCode = ERROR_SUCCESS;
}
CASC_FREE(szLocalPath);
}
}
- return nError;
+ return dwErrCode;
}
-static int DownloadFile(
- const TCHAR * szRemoteName,
- const TCHAR * szLocalName,
+static DWORD DownloadFile(
+ LPCTSTR szRemoteName,
+ LPCTSTR szLocalName,
PULONGLONG PtrByteOffset,
DWORD cbReadSize,
DWORD dwPortFlags)
@@ -1047,7 +1040,7 @@ static int DownloadFile(
TFileStream * pRemStream;
TFileStream * pLocStream;
LPBYTE pbFileData;
- int nError = ERROR_NOT_ENOUGH_MEMORY;
+ DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
// Open the remote stream
pRemStream = FileStream_OpenFile(szRemoteName, BASE_PROVIDER_HTTP | STREAM_PROVIDER_FLAT | dwPortFlags);
@@ -1058,8 +1051,8 @@ static int DownloadFile(
{
ULONGLONG FileSize = 0;
- // Retrieve the file size
- if (FileStream_GetSize(pRemStream, &FileSize) && 0 < FileSize && FileSize < 0x10000000)
+ // Retrieve the file size, but not longer than 1 GB
+ if (FileStream_GetSize(pRemStream, &FileSize) && 0 < FileSize && FileSize < 0x40000000)
{
// Cut the file size down to 32 bits
cbReadSize = (DWORD)FileSize;
@@ -1067,7 +1060,7 @@ static int DownloadFile(
}
// Shall we read something?
- if ((cbReadSize != 0) && (pbFileData = CASC_ALLOC(BYTE, cbReadSize)) != NULL)
+ if ((cbReadSize != 0) && (pbFileData = CASC_ALLOC<BYTE>(cbReadSize)) != NULL)
{
// Read all required data from the remote file
if (FileStream_Read(pRemStream, PtrByteOffset, pbFileData, cbReadSize))
@@ -1076,7 +1069,7 @@ static int DownloadFile(
if (pLocStream != NULL)
{
if (FileStream_Write(pLocStream, NULL, pbFileData, cbReadSize))
- nError = ERROR_SUCCESS;
+ dwErrCode = ERROR_SUCCESS;
FileStream_Close(pLocStream);
}
@@ -1091,90 +1084,102 @@ static int DownloadFile(
}
else
{
- nError = GetLastError();
+ dwErrCode = GetLastError();
}
- return nError;
+ return dwErrCode;
}
-static int DownloadFile(
- TCascStorage * hs,
- const TCHAR * szServerName,
- const TCHAR * szServerPath1,
- const TCHAR * szServerPath2,
- const TCHAR * szLocalPath2,
- PULONGLONG PtrByteOffset,
- DWORD cbReadSize,
- TCHAR * szOutLocalPath,
- size_t cchOutLocalPath,
- DWORD dwPortFlags,
- bool bAlwaysDownload,
- bool bWowClassicRedirect)
+static DWORD DownloadFileFromCDN2(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInfo)
{
- TCHAR szRemotePath[MAX_PATH];
- TCHAR szLocalPath[MAX_PATH];
- size_t nLength;
- int nError = ERROR_NOT_ENOUGH_MEMORY;
-
- // Format the target URL
- if(bWowClassicRedirect && !_tcsicmp(szServerPath1, _T("wow_classic_beta")))
- szServerPath1 = _T("wow");
- if (szLocalPath2 == NULL)
- szLocalPath2 = szServerPath2;
-
- // Create remote path
- CombinePath(szRemotePath, _countof(szRemotePath), URL_SEP_CHAR, szServerName, szServerPath1, szServerPath2, NULL);
- CombinePath(szLocalPath, _countof(szLocalPath), PATH_SEP_CHAR, hs->szRootPath, szLocalPath2, NULL);
+ CASC_PATH<TCHAR> RemotePath(URL_SEP_CHAR);
+ CASC_PATH<TCHAR> LocalPath(PATH_SEP_CHAR);
+ DWORD dwPortFlags = (CdnsInfo.Flags & CASC_CDN_FLAG_PORT1119) ? STREAM_FLAG_USE_PORT_1119 : 0;
+ DWORD dwErrCode;
- // Make sure that the path exists
- ForcePathExist(szLocalPath, true);
+ // Assemble both the remote and local path
+ assert(CdnsInfo.szCdnsHost != NULL && CdnsInfo.szCdnsHost[0] != 0);
+ CreateRemoteAndLocalPath(hs, CdnsInfo, RemotePath, LocalPath);
- // If we are not forced to download a new one, try if local file exists.
- if ((bAlwaysDownload == false) && (_taccess(szLocalPath, 0) == 0))
- {
- nError = ERROR_SUCCESS;
- }
- else
+ // Check whether the local file exists
+ if((CdnsInfo.Flags & CASC_CDN_FORCE_DOWNLOAD) || (_taccess(LocalPath, 0) == -1))
{
- nError = DownloadFile(szRemotePath, szLocalPath, PtrByteOffset, cbReadSize, dwPortFlags);
+ // Make sure that the path exists
+ dwErrCode = ForcePathExist(LocalPath, true);
+ if(dwErrCode != ERROR_SUCCESS)
+ return dwErrCode;
+
+ // Attempt to download the file
+ dwErrCode = DownloadFile(RemotePath, LocalPath, NULL, 0, dwPortFlags);
+ if(dwErrCode != ERROR_SUCCESS)
+ return dwErrCode;
}
- // If succeeded, give the local path
- if(nError == ERROR_SUCCESS)
+ // Give the path to the caller, if any
+ LocalPath.Copy(CdnsInfo.szLocalPath, CdnsInfo.ccLocalPath);
+ return ERROR_SUCCESS;
+}
+
+DWORD DownloadFileFromCDN(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInfo)
+{
+ LPCTSTR szCdnServers = hs->szCdnServers;
+ TCHAR szCdnHost[MAX_PATH] = _T("");
+ DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE;
+
+ // If we have a given CDN server, use it. If not, try all CDNs
+ // from the storage's configuration
+ if(CdnsInfo.szCdnsHost == NULL)
{
- // If the caller wanted local path, give it to him
- if (szOutLocalPath && cchOutLocalPath)
+ // Try all download servers
+ while((szCdnServers = ExtractCdnServerName(szCdnHost, _countof(szCdnHost), szCdnServers)) != NULL)
{
- nLength = _tcslen(szLocalPath);
- if ((nLength + 1) <= cchOutLocalPath)
- {
- CascStrCopy(szOutLocalPath, cchOutLocalPath, szLocalPath);
- }
+ CdnsInfo.szCdnsHost = szCdnHost;
+ if((dwErrCode = DownloadFileFromCDN2(hs, CdnsInfo)) == ERROR_SUCCESS)
+ return ERROR_SUCCESS;
}
}
+ else
+ {
+ dwErrCode = DownloadFileFromCDN2(hs, CdnsInfo);
+ }
- return nError;
+ return dwErrCode;
}
-static int FetchAndLoadConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey, PARSETEXTFILE PfnParseProc)
+static DWORD FetchAndLoadConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey, PARSETEXTFILE PfnParseProc)
{
- const TCHAR * szPathType = _T("config");
+ LPCTSTR szPathType = _T("config");
TCHAR szLocalPath[MAX_PATH];
TCHAR szSubDir[0x80] = _T("");
void * pvListFile = NULL;
- int nError = ERROR_CAN_NOT_COMPLETE;
+ DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE;
// If online storage, we download the file. Otherwise, create a local path
if(hs->dwFeatures & CASC_FEATURE_ONLINE)
{
- nError = DownloadFileFromCDN(hs, szPathType, pFileKey->pbData, NULL, szLocalPath, _countof(szLocalPath));
- if(nError != ERROR_SUCCESS)
- return nError;
+ CASC_CDN_DOWNLOAD CdnsInfo = {0};
+
+ // Prepare the download structure for "%CDNS_HOST%/%CDNS_PATH%/##/##/EKey" file
+ CdnsInfo.szCdnsPath = hs->szCdnPath;
+ CdnsInfo.szPathType = szPathType;
+ CdnsInfo.pbEKey = pFileKey->pbData;
+ CdnsInfo.szLocalPath = szLocalPath;
+ CdnsInfo.ccLocalPath = _countof(szLocalPath);
+
+ // Download the config file
+ dwErrCode = DownloadFileFromCDN(hs, CdnsInfo);
+ if(dwErrCode != ERROR_SUCCESS)
+ return dwErrCode;
}
else
{
- CreateCascSubdirectoryName(szSubDir, _countof(szSubDir), szPathType, NULL, pFileKey->pbData);
- CombinePath(szLocalPath, _countof(szLocalPath), PATH_SEP_CHAR, hs->szDataPath, szSubDir, NULL);
+ CASC_PATH<TCHAR> Path;
+
+ Path.AppendString(hs->szDataPath, false);
+ Path.AppendString(szSubDir, true);
+ Path.AppendString(szPathType, true);
+ Path.AppendEKey(pFileKey->pbData);
+ Path.Copy(szLocalPath, _countof(szLocalPath));
}
// Load and verify the external listfile
@@ -1183,20 +1188,20 @@ static int FetchAndLoadConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey, PARSET
{
if (ListFile_VerifyMD5(pvListFile, pFileKey->pbData))
{
- nError = PfnParseProc(hs, pvListFile);
+ dwErrCode = PfnParseProc(hs, pvListFile);
}
else
{
- nError = ERROR_FILE_CORRUPT;
+ dwErrCode = ERROR_FILE_CORRUPT;
}
CASC_FREE(pvListFile);
}
else
{
- nError = ERROR_FILE_NOT_FOUND;
+ dwErrCode = ERROR_FILE_NOT_FOUND;
}
- return nError;
+ return dwErrCode;
}
//-----------------------------------------------------------------------------
@@ -1212,57 +1217,33 @@ bool InvokeProgressCallback(TCascStorage * hs, LPCSTR szMessage, LPCSTR szObject
return bResult;
}
-DWORD DownloadFileFromCDN(TCascStorage * hs, const TCHAR * szSubDir, LPBYTE pbEKey, const TCHAR * szExtension, TCHAR * szOutLocalPath, size_t cchOutLocalPath)
+DWORD GetFileSpanInfo(PCASC_CKEY_ENTRY pCKeyEntry, PULONGLONG PtrContentSize, PULONGLONG PtrEncodedSize)
{
- PCASC_ARCINDEX_ENTRY pIndexEntry;
- const TCHAR * szCdnServers = hs->szCdnServers;
- TCHAR szRemoteFolder[MAX_PATH];
- TCHAR szLocalFolder[MAX_PATH];
- TCHAR szServerName[MAX_PATH];
- DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE;
+ ULONGLONG ContentSize = 0;
+ ULONGLONG EncodedSize = 0;
+ DWORD dwSpanCount = pCKeyEntry->SpanCount;
+ bool bContentSizeError = false;
+ bool bEncodedSizeError = false;
- // Try all download servers
- while((szCdnServers = ExtractCdnServerName(szServerName, _countof(szServerName), szCdnServers)) != NULL)
- {
- // Create the local subdirectory
- CreateCascSubdirectoryName(szLocalFolder, _countof(szLocalFolder), szSubDir, szExtension, pbEKey);
+ // Sanity check
+ assert(pCKeyEntry->SpanCount != 0);
- // If the EKey is in an archive, we need to change the source
- if ((pIndexEntry = (PCASC_ARCINDEX_ENTRY)hs->ArcIndexMap.FindObject(pbEKey)) != NULL)
- {
- ULONGLONG ByteOffset;
-
- // Change the subpath to an archive
- CreateCascSubdirectoryName(szRemoteFolder, _countof(szRemoteFolder), szSubDir, szExtension, pIndexEntry->IndexHash);
- ByteOffset = pIndexEntry->ArchiveOffset;
- dwErrCode = DownloadFile(hs,
- szServerName,
- hs->szCdnPath,
- szRemoteFolder,
- szLocalFolder,
- &ByteOffset,
- pIndexEntry->EncodedSize,
- szOutLocalPath,
- cchOutLocalPath, 0, false, false);
- }
- else
- {
- dwErrCode = DownloadFile(hs,
- szServerName,
- hs->szCdnPath,
- szLocalFolder,
- szLocalFolder,
- NULL,
- 0,
- szOutLocalPath,
- cchOutLocalPath, 0, false, false);
- }
+ // Sum all span size
+ for(DWORD i = 0; i < dwSpanCount; i++, pCKeyEntry++)
+ {
+ if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
+ bContentSizeError = true;
+ if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE)
+ bEncodedSizeError = true;
- if (dwErrCode == ERROR_SUCCESS)
- break;
+ ContentSize = ContentSize + pCKeyEntry->ContentSize;
+ EncodedSize = EncodedSize + pCKeyEntry->EncodedSize;
}
- return dwErrCode;
+ // Reset the sizes if there was an error
+ PtrContentSize[0] = (bContentSizeError == false) ? ContentSize : CASC_INVALID_SIZE64;
+ PtrEncodedSize[0] = (bEncodedSizeError == false) ? EncodedSize : CASC_INVALID_SIZE64;
+ return dwSpanCount;
}
// Checks whether there is a ".build.info" or ".build.db".
@@ -1314,19 +1295,33 @@ DWORD LoadBuildInfo(TCascStorage * hs)
// If the storage is online storage, we need to download "versions"
if(hs->dwFeatures & CASC_FEATURE_ONLINE)
{
+ CASC_CDN_DOWNLOAD CdnsInfo = {0};
+ TCHAR szLocalFile[MAX_PATH];
+
// Inform the user about loading the build.info/build.db/versions
if(InvokeProgressCallback(hs, "Downloading the \"versions\" file", NULL, 0, 0))
return ERROR_CANCELLED;
+ // Prepare the download structure for "us.patch.battle.net/%CODENAME%/versions" file
+ CdnsInfo.szCdnsHost = _T("us.patch.battle.net");
+ CdnsInfo.szCdnsPath = hs->szCodeName;
+ CdnsInfo.szPathType = _T("");
+ CdnsInfo.szFileName = _T("versions");
+ CdnsInfo.szLocalPath = szLocalFile;
+ CdnsInfo.ccLocalPath = _countof(szLocalFile);
+ CdnsInfo.Flags = CASC_CDN_FLAG_PORT1119 | CASC_CDN_FORCE_DOWNLOAD;
+
// Attempt to download the "versions" file
- dwErrCode = DownloadFile(hs, _T("us.patch.battle.net"), hs->szCodeName, _T("versions"), NULL, NULL, 0, NULL, 0, STREAM_FLAG_USE_PORT_1119, true, false);
+ dwErrCode = DownloadFileFromCDN(hs, CdnsInfo);
if(dwErrCode != ERROR_SUCCESS)
- {
return dwErrCode;
- }
+
+ // Retrieve the name of the "versions" file
+ if((hs->szBuildFile = CascNewStr(szLocalFile)) == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
}
- // We support either ".build.info" or ".build.db"
+ // We support ".build.info", ".build.db" or "versions"
switch (hs->BuildFileType)
{
case CascBuildInfo:
@@ -1346,11 +1341,13 @@ DWORD LoadBuildInfo(TCascStorage * hs)
return ERROR_NOT_SUPPORTED;
}
+ assert(hs->szBuildFile != NULL);
return LoadCsvFile(hs, hs->szBuildFile, PfnParseProc, bHasHeader);
}
-DWORD LoadCdnsInfo(TCascStorage * hs)
+DWORD LoadCdnsFile(TCascStorage * hs)
{
+ CASC_CDN_DOWNLOAD CdnsInfo = {0};
TCHAR szLocalPath[MAX_PATH];
DWORD dwErrCode = ERROR_SUCCESS;
@@ -1361,9 +1358,17 @@ DWORD LoadCdnsInfo(TCascStorage * hs)
if(InvokeProgressCallback(hs, "Downloading the \"cdns\" file", NULL, 0, 0))
return ERROR_CANCELLED;
+ // Prepare the download structure
+ CdnsInfo.szCdnsHost = _T("us.patch.battle.net");
+ CdnsInfo.szCdnsPath = hs->szCodeName;
+ CdnsInfo.szPathType = _T("");
+ CdnsInfo.szFileName = _T("cdns");
+ CdnsInfo.szLocalPath = szLocalPath;
+ CdnsInfo.ccLocalPath = _countof(szLocalPath);
+ CdnsInfo.Flags = CASC_CDN_FLAG_PORT1119 | CASC_CDN_FORCE_DOWNLOAD;
+
// Download file and parse it
- dwErrCode = DownloadFile(hs, _T("us.patch.battle.net"), hs->szCodeName, _T("cdns"), NULL, NULL, 0, szLocalPath, _countof(szLocalPath), STREAM_FLAG_USE_PORT_1119, false, true);
- if(dwErrCode == ERROR_SUCCESS)
+ if((dwErrCode = DownloadFileFromCDN(hs, CdnsInfo)) == ERROR_SUCCESS)
{
dwErrCode = LoadCsvFile(hs, szLocalPath, ParseFile_CDNS, true);
}
@@ -1377,7 +1382,7 @@ DWORD LoadCdnConfigFile(TCascStorage * hs)
assert(hs->CdnConfigKey.pbData != NULL && hs->CdnConfigKey.cbData == MD5_HASH_SIZE);
// Inform the user about what we are doing
- if(InvokeProgressCallback(hs, "Downloading CDN config file", NULL, 0, 0))
+ if(InvokeProgressCallback(hs, "Loading CDN config file", NULL, 0, 0))
return ERROR_CANCELLED;
// Load the CDN config file
@@ -1390,10 +1395,139 @@ DWORD LoadCdnBuildFile(TCascStorage * hs)
assert(hs->CdnBuildKey.pbData != NULL && hs->CdnBuildKey.cbData == MD5_HASH_SIZE);
// Inform the user about what we are doing
- if(InvokeProgressCallback(hs, "Downloading CDN build file", NULL, 0, 0))
+ if(InvokeProgressCallback(hs, "Loading CDN build file", NULL, 0, 0))
return ERROR_CANCELLED;
// Load the CDN config file. Note that we don't
// need it for anything, really, so we don't care if it fails
return FetchAndLoadConfigFile(hs, &hs->CdnBuildKey, ParseFile_CdnBuild);
}
+
+LPBYTE LoadInternalFileToMemory(TCascStorage * hs, PCASC_CKEY_ENTRY pCKeyEntry, DWORD * pcbFileData)
+{
+ LPBYTE pbFileData = NULL;
+ HANDLE hFile = NULL;
+ DWORD dwFileSizeHi = 0;
+ DWORD cbFileData = 0;
+ DWORD dwBytesRead = 0;
+ DWORD dwErrCode = ERROR_SUCCESS;
+
+ // Open the file either by CKey or by EKey
+ if(OpenFileByCKeyEntry(hs, pCKeyEntry, CASC_STRICT_DATA_CHECK, &hFile))
+ {
+ // Make the file not cached. We always load the entire file to memory,
+ // so no need to cache (and needlessly copy from one buffer to another)
+ SetCacheStrategy(hFile, CascCacheNothing);
+
+ // 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(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
+ {
+ cbFileData = CascGetFileSize(hFile, &dwFileSizeHi);
+ if(cbFileData == CASC_INVALID_SIZE || dwFileSizeHi != 0)
+ dwErrCode = ERROR_FILE_CORRUPT;
+ }
+ else
+ {
+ cbFileData = pCKeyEntry->ContentSize;
+ }
+
+ // Retrieve the size of the ENCODING file
+ if(dwErrCode == 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)
+ {
+ dwErrCode = ERROR_FILE_CORRUPT;
+ }
+ }
+ else
+ {
+ dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ // Close the file
+ CascCloseFile(hFile);
+ }
+ else
+ {
+ dwErrCode = GetLastError();
+ }
+
+ // Handle errors
+ if(dwErrCode != ERROR_SUCCESS)
+ {
+ // Free the file data
+ CASC_FREE(pbFileData);
+ cbFileData = 0;
+
+ // Set the last error
+ SetLastError(dwErrCode);
+ }
+
+ // Give the loaded file length
+ if(pcbFileData != NULL)
+ *pcbFileData = cbFileData;
+ return pbFileData;
+}
+
+LPBYTE LoadFileToMemory(LPCTSTR 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;
+}
+
diff --git a/dep/CascLib/src/CascFindFile.cpp b/dep/CascLib/src/CascFindFile.cpp
index 129403188c5..f33541f07d0 100644
--- a/dep/CascLib/src/CascFindFile.cpp
+++ b/dep/CascLib/src/CascFindFile.cpp
@@ -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;
diff --git a/dep/CascLib/src/CascIndexFiles.cpp b/dep/CascLib/src/CascIndexFiles.cpp
index ed680f4c4d9..9cab9c6a901 100644
--- a/dep/CascLib/src/CascIndexFiles.cpp
+++ b/dep/CascLib/src/CascIndexFiles.cpp
@@ -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. 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 EKey of the variable length
+ pbEKeyEntry = CaptureEncodedKey(pEKeyEntry->EKey, pbEKeyEntry, InHeader.EKeyLength);
+
+ // 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)
+ {
+ // 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
{
- if(!memcmp(pCKeyEntry->EKey, hs->EncodingCKey.EKey, hs->EKeyLength))
+ // 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)
{
diff --git a/dep/CascLib/src/CascLib.def b/dep/CascLib/src/CascLib.def
index fbed70bf128..0359dfa3a2e 100644
--- a/dep/CascLib/src/CascLib.def
+++ b/dep/CascLib/src/CascLib.def
@@ -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
- \ No newline at end of file
diff --git a/dep/CascLib/src/CascLib.h b/dep/CascLib/src/CascLib.h
index 1ed387aee01..90be65a00b2 100644
--- a/dep/CascLib/src/CascLib.h
+++ b/dep/CascLib/src/CascLib.h
@@ -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);
diff --git a/dep/CascLib/src/CascOpenFile.cpp b/dep/CascLib/src/CascOpenFile.cpp
index 45d10bc18f1..8ff9739d6e3 100644
--- a/dep/CascLib/src/CascOpenFile.cpp
+++ b/dep/CascLib/src/CascOpenFile.cpp
@@ -13,8 +13,171 @@
#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;
diff --git a/dep/CascLib/src/CascOpenStorage.cpp b/dep/CascLib/src/CascOpenStorage.cpp
index e10db730d41..abc17e1e9ce 100644
--- a/dep/CascLib/src/CascOpenStorage.cpp
+++ b/dep/CascLib/src/CascOpenStorage.cpp
@@ -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;
- }
+ 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))
-// {
-// 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))
+// {
+// dwErrCode = 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;
- }
+ // 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
- nError = LoadEncodingCKeyPage(hs, EnHeader, pbCKeyPage, pbCKeyPage + EnHeader.CKeyPageSize);
- if(nError != ERROR_SUCCESS)
- 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;
- }
+ // 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
diff --git a/dep/CascLib/src/CascPort.h b/dep/CascLib/src/CascPort.h
index 8172a5dde35..21cd991a09c 100644
--- a/dep/CascLib/src/CascPort.h
+++ b/dep/CascLib/src/CascPort.h
@@ -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;
@@ -304,6 +307,27 @@
#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
#ifdef __CASCLIB_SELF__
diff --git a/dep/CascLib/src/CascReadFile.cpp b/dep/CascLib/src/CascReadFile.cpp
index 2f444d0d80f..aa2a718198c 100644
--- a/dep/CascLib/src/CascReadFile.cpp
+++ b/dep/CascLib/src/CascReadFile.cpp
@@ -15,62 +15,97 @@
//-----------------------------------------------------------------------------
// Local functions
-static int EnsureDataStreamIsOpen(TCascFile * hf)
+static DWORD GetStreamEncodedSize(TFileStream * pStream)
+{
+ ULONGLONG FileSize = 0;
+
+ FileStream_GetSize(pStream, &FileSize);
+ assert((FileSize >> 32) == 0);
+
+ return (DWORD)(FileSize);
+}
+
+static DWORD OpenDataStream(TCascFile * hf, PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry, bool bDownloadFileIf)
{
TCascStorage * hs = hf->hs;
TFileStream * pStream = NULL;
- ULONGLONG EncodedSize = 0;
- TCHAR * szDataFile;
TCHAR szCachePath[MAX_PATH];
+ TCHAR szDataFile[MAX_PATH];
TCHAR szPlainName[0x80];
- int nError;
+ DWORD dwErrCode;
// If the file is available locally, we rely on data files.
// If not, we download the file and open the stream
- if(hf->pCKeyEntry->Flags & CASC_CE_FILE_IS_LOCAL)
+ if(pCKeyEntry->Flags & CASC_CE_FILE_IS_LOCAL)
{
+ DWORD dwArchiveIndex = pFileSpan->ArchiveIndex;
+
// If the file is not open yet, do it
- if(hs->DataFiles[hf->ArchiveIndex] == NULL)
+ if(hs->DataFiles[dwArchiveIndex] == NULL)
{
// Prepare the name of the data file
- CascStrPrintf(szPlainName, _countof(szPlainName), _T("data.%03u"), hf->ArchiveIndex);
- szDataFile = CombinePath(hs->szIndexPath, szPlainName);
+ CascStrPrintf(szPlainName, _countof(szPlainName), _T("data.%03u"), dwArchiveIndex);
+ CombinePath(szDataFile, _countof(szDataFile), PATH_SEP_CHAR, hs->szIndexPath, szPlainName, NULL);
- // Open the data file
- if(szDataFile != NULL)
- {
- // Open the data stream with read+write sharing to prevent Battle.net agent
- // detecting a corruption and redownloading the entire package
- pStream = FileStream_OpenFile(szDataFile, STREAM_FLAG_READ_ONLY | STREAM_FLAG_WRITE_SHARE | STREAM_PROVIDER_FLAT | STREAM_FLAG_FILL_MISSING | BASE_PROVIDER_FILE);
- hs->DataFiles[hf->ArchiveIndex] = pStream;
- CASC_FREE(szDataFile);
- }
+ // Open the data stream with read+write sharing to prevent Battle.net agent
+ // detecting a corruption and redownloading the entire package
+ pStream = FileStream_OpenFile(szDataFile, STREAM_FLAG_READ_ONLY | STREAM_FLAG_WRITE_SHARE | STREAM_PROVIDER_FLAT | STREAM_FLAG_FILL_MISSING | BASE_PROVIDER_FILE);
+ hs->DataFiles[dwArchiveIndex] = pStream;
}
// Return error or success
- hf->pStream = hs->DataFiles[hf->ArchiveIndex];
- return (hf->pStream != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND;
+ pFileSpan->pStream = hs->DataFiles[dwArchiveIndex];
+ return (pFileSpan->pStream != NULL) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND;
}
else
{
- if(hf->bDownloadFileIf)
+ if(bDownloadFileIf)
{
- // Create the local folder path and download the file from CDN
- nError = DownloadFileFromCDN(hf->hs, _T("data"), hf->pCKeyEntry->EKey, NULL, szCachePath, _countof(szCachePath));
- if(nError == ERROR_SUCCESS)
+ CASC_CDN_DOWNLOAD CdnsInfo = {0};
+ LPCTSTR szPathType = (pCKeyEntry->Flags & CASC_CE_FILE_PATCH) ? _T("patch") : _T("data");
+
+ // Prepare the download structure for "%CDNS_HOST%/%CDNS_PATH%/##/##/EKey" file
+ CdnsInfo.szCdnsPath = hs->szCdnPath;
+ CdnsInfo.szPathType = szPathType;
+ CdnsInfo.pbEKey = pCKeyEntry->EKey;
+ CdnsInfo.szLocalPath = szCachePath;
+ CdnsInfo.ccLocalPath = _countof(szCachePath);
+
+ // Download the file from CDN
+ dwErrCode = DownloadFileFromCDN(hs, CdnsInfo);
+ if(dwErrCode == ERROR_SUCCESS)
{
- hf->pStream = FileStream_OpenFile(szCachePath, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT);
- if(hf->pStream != NULL)
+ pStream = FileStream_OpenFile(szCachePath, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT);
+ if(pStream != NULL)
{
- // Supply the file size, if unknown yet
- if(hf->EncodedSize == CASC_INVALID_SIZE)
+ // Initialize information about the position and size of the file in archive
+ // On loose files, their position is zero and encoded size is length of the file
+ if(CdnsInfo.pbArchiveKey != NULL)
{
- FileStream_GetSize(hf->pStream, &EncodedSize);
- hf->pCKeyEntry->EncodedSize = (DWORD)EncodedSize;
- hf->EncodedSize = (DWORD)EncodedSize;
+ // Archive position
+ pFileSpan->ArchiveIndex = CdnsInfo.ArchiveIndex;
+ pFileSpan->ArchiveOffs = (DWORD)CdnsInfo.ArchiveOffs;
+
+ // Encoded size
+ if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE)
+ pCKeyEntry->EncodedSize = CdnsInfo.EncodedSize;
+ assert(pCKeyEntry->EncodedSize == CdnsInfo.EncodedSize);
+ }
+ else
+ {
+ // Archive position
+ pFileSpan->ArchiveIndex = 0;
+ pFileSpan->ArchiveOffs = 0;
+
+ // Encoded size
+ if(pCKeyEntry->EncodedSize == CASC_INVALID_SIZE)
+ pCKeyEntry->EncodedSize = GetStreamEncodedSize(pStream);
+ assert(pCKeyEntry->EncodedSize == GetStreamEncodedSize(pStream));
}
- hf->bLocalFileStream = true;
+ // We need to close the file stream after we're done
+ pFileSpan->pStream = pStream;
+ hf->bCloseFileStream = true;
return ERROR_SUCCESS;
}
}
@@ -125,7 +160,7 @@ static void VerifyHeaderSpan(PBLTE_ENCODED_HEADER pBlteHeader, ULONGLONG HeaderO
}
#endif
-static int ParseBlteHeader(TCascFile * hf, ULONGLONG HeaderOffset, LPBYTE pbEncodedBuffer, size_t cbEncodedBuffer, size_t * pcbHeaderSize)
+static DWORD ParseBlteHeader(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry, ULONGLONG HeaderOffset, LPBYTE pbEncodedBuffer, size_t cbEncodedBuffer, size_t * pcbHeaderSize)
{
PBLTE_ENCODED_HEADER pEncodedHeader = (PBLTE_ENCODED_HEADER)pbEncodedBuffer;
PBLTE_HEADER pBlteHeader = (PBLTE_HEADER)pbEncodedBuffer;
@@ -143,12 +178,12 @@ static int ParseBlteHeader(TCascFile * hf, ULONGLONG HeaderOffset, LPBYTE pbEnco
// There must be at least some bytes
if (cbEncodedBuffer < FIELD_OFFSET(BLTE_ENCODED_HEADER, MustBe0F))
return ERROR_BAD_FORMAT;
- if (pEncodedHeader->EncodedSize != hf->EncodedSize)
+ if (pEncodedHeader->EncodedSize != pCKeyEntry->EncodedSize)
return ERROR_BAD_FORMAT;
#ifdef _DEBUG
// Not really needed, it's here just for explanation of what the values mean
- //assert(memcmp(hf->pCKeyEntry->EKey, pEncodedHeader->EKey.Value, MD5_HASH_SIZE) == 0);
+ //assert(memcmp(pCKeyEntry->EKey, pEncodedHeader->EKey.Value, MD5_HASH_SIZE) == 0);
VerifyHeaderSpan(pEncodedHeader, HeaderOffset);
#endif
// Capture the EKey
@@ -183,11 +218,11 @@ static int ParseBlteHeader(TCascFile * hf, ULONGLONG HeaderOffset, LPBYTE pbEnco
}
// Give the frame count
- hf->FrameCount = FrameCount;
+ pFileSpan->FrameCount = FrameCount;
return ERROR_SUCCESS;
}
-static LPBYTE ReadMissingHeaderData(TCascFile * hf, ULONGLONG DataFileOffset, LPBYTE pbEncodedBuffer, size_t cbEncodedBuffer, size_t cbTotalHeaderSize)
+static LPBYTE ReadMissingHeaderData(PCASC_FILE_SPAN pFileSpan, ULONGLONG DataFileOffset, LPBYTE pbEncodedBuffer, size_t cbEncodedBuffer, size_t cbTotalHeaderSize)
{
LPBYTE pbNewBuffer;
@@ -197,7 +232,7 @@ static LPBYTE ReadMissingHeaderData(TCascFile * hf, ULONGLONG DataFileOffset, LP
{
// Load the missing data
DataFileOffset += cbEncodedBuffer;
- if (FileStream_Read(hf->pStream, &DataFileOffset, pbNewBuffer + cbEncodedBuffer, (DWORD)(cbTotalHeaderSize - cbEncodedBuffer)))
+ if (FileStream_Read(pFileSpan->pStream, &DataFileOffset, pbNewBuffer + cbEncodedBuffer, (DWORD)(cbTotalHeaderSize - cbEncodedBuffer)))
{
return pbNewBuffer;
}
@@ -208,138 +243,200 @@ static LPBYTE ReadMissingHeaderData(TCascFile * hf, ULONGLONG DataFileOffset, LP
return NULL;
}
-static int LoadFileFrames(TCascFile * hf, ULONGLONG DataFileOffset, LPBYTE pbFramePtr, LPBYTE pbFrameEnd, size_t cbHeaderSize)
+static LPBYTE CaptureBlteFileFrame(CASC_FILE_FRAME & Frame, LPBYTE pbFramePtr, LPBYTE pbFrameEnd)
+{
+ PBLTE_FRAME pFileFrame = (PBLTE_FRAME)pbFramePtr;
+
+ // Check whether we have enough data ready
+ if((pbFramePtr + sizeof(BLTE_FRAME)) > pbFrameEnd)
+ return NULL;
+
+ Frame.FrameHash = pFileFrame->FrameHash;
+ Frame.ContentSize = ConvertBytesToInteger_4(pFileFrame->ContentSize);
+ Frame.EncodedSize = ConvertBytesToInteger_4(pFileFrame->EncodedSize);
+ return pbFramePtr + sizeof(BLTE_FRAME);
+}
+
+static DWORD LoadSpanFrames(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry, DWORD DataFileOffset, LPBYTE pbFramePtr, LPBYTE pbFrameEnd, size_t cbHeaderSize)
{
- PBLTE_FRAME pFileFrame;
+ PCASC_FILE_FRAME pFrames = NULL;
DWORD ContentSize = 0;
- DWORD FileOffset = 0;
- int nError = ERROR_SUCCESS;
+ DWORD dwErrCode = ERROR_SUCCESS;
- assert(hf != NULL);
- assert(hf->pStream != NULL);
- assert(hf->pFrames == NULL);
+ assert(pFileSpan != NULL);
+ assert(pFileSpan->pStream != NULL);
+ assert(pFileSpan->pFrames == NULL);
- if (hf->FrameCount != 0)
+ if (pFileSpan->FrameCount != 0)
{
// Move the raw archive offset
- DataFileOffset += (hf->FrameCount * sizeof(BLTE_FRAME));
+ DataFileOffset += (pFileSpan->FrameCount * sizeof(BLTE_FRAME));
// Allocate array of file frames
- hf->pFrames = CASC_ALLOC(CASC_FILE_FRAME, hf->FrameCount);
- if (hf->pFrames != NULL)
+ pFrames = CASC_ALLOC<CASC_FILE_FRAME>(pFileSpan->FrameCount);
+ if (pFrames != NULL)
{
// Copy the frames to the file structure
- for (DWORD i = 0; i < hf->FrameCount; i++, pbFramePtr += sizeof(BLTE_FRAME))
+ for (DWORD i = 0; i < pFileSpan->FrameCount; i++)
{
- // Capture the file frame
- if ((pbFramePtr + sizeof(BLTE_FRAME)) > pbFrameEnd)
- return ERROR_BAD_FORMAT;
- pFileFrame = (PBLTE_FRAME)pbFramePtr;
-
- // Convert the file frame to the native format
- hf->pFrames[i].DataFileOffset = (DWORD)DataFileOffset;
- hf->pFrames[i].FileOffset = CASC_INVALID_POS;
- hf->pFrames[i].EncodedSize = ConvertBytesToInteger_4(pFileFrame->EncodedSize);
- hf->pFrames[i].ContentSize = ConvertBytesToInteger_4(pFileFrame->ContentSize);
- hf->pFrames[i].FrameHash = pFileFrame->FrameHash;
-
- DataFileOffset += hf->pFrames[i].EncodedSize;
- ContentSize += hf->pFrames[i].ContentSize;
- FileOffset += hf->pFrames[i].ContentSize;
+ CASC_FILE_FRAME & Frame = pFrames[i];
+
+ // Capture the single BLTE frame
+ pbFramePtr = CaptureBlteFileFrame(Frame, pbFramePtr, pbFrameEnd);
+ if(pbFramePtr == NULL)
+ {
+ dwErrCode = ERROR_BAD_FORMAT;
+ break;
+ }
+
+ // Fill-in the file range of the frame
+ Frame.StartOffset = pFileSpan->StartOffset + ContentSize;
+ Frame.EndOffset = Frame.StartOffset + Frame.ContentSize;
+ ContentSize += Frame.ContentSize;
+
+ // Fill-in the archive range of the frame
+ assert((DataFileOffset + Frame.EncodedSize) > DataFileOffset);
+ Frame.DataFileOffset = DataFileOffset;
+ DataFileOffset += Frame.EncodedSize;
}
// Save the content size of the file
- if(hf->pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
+ if(pCKeyEntry->ContentSize == CASC_INVALID_SIZE)
{
- hf->pCKeyEntry->ContentSize = ContentSize;
- hf->ContentSize = ContentSize;
+ pCKeyEntry->ContentSize = ContentSize;
}
}
+ else
+ {
+ dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
+ }
}
else
{
- // The content size in the file structure must be valid at this point,
- // otherwise we don't know the frame content size
- if (hf->ContentSize == CASC_INVALID_SIZE)
+ // Allocate single "dummy" frame
+ pFrames = CASC_ALLOC<CASC_FILE_FRAME>(1);
+ if (pFrames != NULL)
{
- assert(false);
- return ERROR_CAN_NOT_COMPLETE;
+ // Fill the single frame
+ memset(&pFrames->FrameHash, 0, sizeof(CONTENT_KEY));
+ pFrames->StartOffset = pFileSpan->StartOffset;
+ pFrames->EndOffset = pFileSpan->EndOffset;
+ pFrames->DataFileOffset = DataFileOffset;
+ pFrames->EncodedSize = (DWORD)(pCKeyEntry->EncodedSize - cbHeaderSize);
+ pFrames->ContentSize = pCKeyEntry->ContentSize;
+
+ // Save the number of file frames
+ pFileSpan->FrameCount = 1;
}
-
- // Save the number of file frames
- hf->FrameCount = 1;
-
- // Allocate single "dummy" frame
- hf->pFrames = CASC_ALLOC(CASC_FILE_FRAME, 1);
- if (hf->pFrames != NULL)
+ else
{
- memset(&hf->pFrames->FrameHash, 0, sizeof(CONTENT_KEY));
- hf->pFrames->DataFileOffset = (DWORD)DataFileOffset;
- hf->pFrames->FileOffset = CASC_INVALID_POS;
- hf->pFrames->EncodedSize = (DWORD)(hf->EncodedSize - cbHeaderSize);
- hf->pFrames->ContentSize = hf->ContentSize;
+ dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
}
- if (hf->pFrames == NULL)
- nError = ERROR_NOT_ENOUGH_MEMORY;
- return nError;
+ // Free the frame array on error
+ if(dwErrCode != ERROR_SUCCESS)
+ {
+ pFileSpan->FrameCount = 0;
+ CASC_FREE(pFrames);
+ }
+
+ pFileSpan->pFrames = pFrames;
+ return dwErrCode;
}
-static int LoadEncodedHeaderAndFileFrames(TCascFile * hf)
+static DWORD LoadSpanFramesForPlainFile(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry)
+{
+ PCASC_FILE_FRAME pFrames;
+
+ // Allocate single "dummy" frame
+ pFrames = CASC_ALLOC<CASC_FILE_FRAME>(1);
+ if (pFrames != NULL)
+ {
+ // Setup the size
+ pFileSpan->EndOffset = pFileSpan->StartOffset + pCKeyEntry->ContentSize;
+ pCKeyEntry->Flags |= CASC_CE_PLAIN_DATA;
+
+ // Fill the single frame
+ memset(&pFrames->FrameHash, 0, sizeof(CONTENT_KEY));
+ pFrames->StartOffset = pFileSpan->StartOffset;
+ pFrames->EndOffset = pFrames->StartOffset + pCKeyEntry->ContentSize;
+ pFrames->DataFileOffset = 0;
+ pFrames->EncodedSize = pCKeyEntry->EncodedSize;
+ pFrames->ContentSize = pCKeyEntry->ContentSize;
+
+ // Save the number of file frames
+ pFileSpan->FrameCount = 1;
+ pFileSpan->pFrames = pFrames;
+ return ERROR_SUCCESS;
+ }
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+}
+
+static DWORD LoadEncodedHeaderAndSpanFrames(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry)
{
LPBYTE pbEncodedBuffer;
size_t cbEncodedBuffer = MAX_ENCODED_HEADER;
- int nError = ERROR_SUCCESS;
+ DWORD dwErrCode = ERROR_SUCCESS;
// Should only be called when the file frames are NOT loaded
- assert(hf->pFrames == NULL);
- assert(hf->FrameCount == 0);
+ assert(pFileSpan->pFrames == NULL);
+ assert(pFileSpan->FrameCount == 0);
// Allocate the initial buffer for the encoded headers
- pbEncodedBuffer = CASC_ALLOC(BYTE, MAX_ENCODED_HEADER);
+ pbEncodedBuffer = CASC_ALLOC<BYTE>(MAX_ENCODED_HEADER);
if (pbEncodedBuffer != NULL)
{
- ULONGLONG ReadOffset = hf->ArchiveOffset;
+ ULONGLONG ReadOffset = pFileSpan->ArchiveOffs;
size_t cbTotalHeaderSize;
size_t cbHeaderSize = 0;
// At this point, we expect encoded size to be known
- assert(hf->EncodedSize != CASC_INVALID_SIZE);
+ assert(pCKeyEntry->EncodedSize != CASC_INVALID_SIZE);
// Do not read more than encoded size
- cbEncodedBuffer = CASCLIB_MIN(cbEncodedBuffer, hf->EncodedSize);
+ cbEncodedBuffer = CASCLIB_MIN(cbEncodedBuffer, pCKeyEntry->EncodedSize);
// Load the entire (eventual) header area. This is faster than doing
// two read operations in a row. Read as much as possible. If the file is cut,
// the FileStream will pad it with zeros
- if (FileStream_Read(hf->pStream, &ReadOffset, pbEncodedBuffer, (DWORD)cbEncodedBuffer))
+ if (FileStream_Read(pFileSpan->pStream, &ReadOffset, pbEncodedBuffer, (DWORD)cbEncodedBuffer))
{
// Parse the BLTE header
- nError = ParseBlteHeader(hf, ReadOffset, pbEncodedBuffer, cbEncodedBuffer, &cbHeaderSize);
- if (nError == ERROR_SUCCESS)
+ dwErrCode = ParseBlteHeader(pFileSpan, pCKeyEntry, ReadOffset, pbEncodedBuffer, cbEncodedBuffer, &cbHeaderSize);
+ if (dwErrCode == ERROR_SUCCESS)
{
- // If the headers are larger than the initial read size,
- // We read the missing data
- cbTotalHeaderSize = cbHeaderSize + (hf->FrameCount * sizeof(BLTE_FRAME));
+ // If the headers are larger than the initial read size, we read the missing data
+ pFileSpan->HeaderSize = (DWORD)(cbTotalHeaderSize = cbHeaderSize + (pFileSpan->FrameCount * sizeof(BLTE_FRAME)));
if (cbTotalHeaderSize > cbEncodedBuffer)
{
- pbEncodedBuffer = ReadMissingHeaderData(hf, ReadOffset, pbEncodedBuffer, cbEncodedBuffer, cbTotalHeaderSize);
+ pbEncodedBuffer = ReadMissingHeaderData(pFileSpan, ReadOffset, pbEncodedBuffer, cbEncodedBuffer, cbTotalHeaderSize);
if (pbEncodedBuffer == NULL)
- nError = GetLastError();
+ dwErrCode = GetLastError();
cbEncodedBuffer = cbTotalHeaderSize;
}
// Load the array of frame headers
- if (nError == ERROR_SUCCESS)
+ if (dwErrCode == ERROR_SUCCESS)
+ {
+ assert((DWORD)(ReadOffset + cbHeaderSize) > (DWORD)ReadOffset);
+ dwErrCode = LoadSpanFrames(pFileSpan, pCKeyEntry, (DWORD)(ReadOffset + cbHeaderSize), pbEncodedBuffer + cbHeaderSize, pbEncodedBuffer + cbEncodedBuffer, cbHeaderSize);
+ }
+ }
+ else
+ {
+ // Special treatment for plain files ("PATCH"): If the content size and encoded size
+ // are equal, we will create a single fake frame
+ if(pCKeyEntry->EncodedSize == pCKeyEntry->ContentSize)
{
- nError = LoadFileFrames(hf, ReadOffset + cbHeaderSize, pbEncodedBuffer + cbHeaderSize, pbEncodedBuffer + cbEncodedBuffer, cbHeaderSize);
+ dwErrCode = LoadSpanFramesForPlainFile(pFileSpan, pCKeyEntry);
}
}
}
else
{
- nError = ERROR_FILE_CORRUPT;
+ dwErrCode = ERROR_FILE_CORRUPT;
}
// Free the frame buffer
@@ -347,67 +444,137 @@ static int LoadEncodedHeaderAndFileFrames(TCascFile * hf)
}
else
{
- nError = ERROR_NOT_ENOUGH_MEMORY;
+ dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
- return nError;
+ return dwErrCode;
}
-static int EnsureFileFramesLoaded(TCascFile * hf)
+static DWORD LoadSpanFrames(TCascFile * hf, PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEntry)
{
- int nError = ERROR_SUCCESS;
+ DWORD dwErrCode = ERROR_SUCCESS;
- // If the encoded frames are not loaded, do it now
- if(hf->pFrames == NULL)
- {
- // We need the data file to be open
- nError = EnsureDataStreamIsOpen(hf);
- if(nError != ERROR_SUCCESS)
- return nError;
+ // Sanity check
+ assert(pFileSpan->pFrames == NULL);
- // Make sure we have header area loaded
- nError = LoadEncodedHeaderAndFileFrames(hf);
+ // Make sure that the data stream is open for that span
+ if(pFileSpan->pStream == NULL)
+ {
+ dwErrCode = OpenDataStream(hf, pFileSpan, pCKeyEntry, hf->bDownloadFileIf);
+ if(dwErrCode != ERROR_SUCCESS)
+ return dwErrCode;
}
- return nError;
+ // Make sure we have header area loaded
+ return LoadEncodedHeaderAndSpanFrames(pFileSpan, pCKeyEntry);
}
-static int LoadEncodedFrame(TFileStream * pStream, PCASC_FILE_FRAME pFrame, LPBYTE pbEncodedFrame, bool bVerifyIntegrity)
+// Loads all file spans to memory
+static DWORD LoadFileSpanFrames(TCascFile * hf)
{
- ULONGLONG FileOffset = pFrame->DataFileOffset;
- int nError = ERROR_SUCCESS;
+ PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry;
+ PCASC_FILE_SPAN pFileSpan = hf->pFileSpan;
+ DWORD dwErrCode = ERROR_SUCCESS;
- // Load the encoded frame to memory
- if(FileStream_Read(pStream, &FileOffset, pbEncodedFrame, pFrame->EncodedSize))
+ // If the ContentSize/EncodedSize is still unknown, we need to get it from the file frames
+ if(hf->ContentSize == CASC_INVALID_SIZE64 || hf->EncodedSize == CASC_INVALID_SIZE64)
{
- if (bVerifyIntegrity)
+ // Set initially to zero
+ hf->ContentSize = 0;
+ hf->EncodedSize = 0;
+
+ // Load file frames for all spans
+ for(DWORD i = 0; i < hf->SpanCount; i++, pCKeyEntry++, pFileSpan++)
{
- if (!CascVerifyDataBlockHash(pbEncodedFrame, pFrame->EncodedSize, pFrame->FrameHash.Value))
- nError = ERROR_FILE_CORRUPT;
+ // Init the range of the file span
+ pFileSpan->StartOffset = hf->ContentSize;
+ pFileSpan->EndOffset = hf->ContentSize;
+
+ // Load the frames of the file span
+ dwErrCode = LoadSpanFrames(hf, pFileSpan, pCKeyEntry);
+ if(dwErrCode != ERROR_SUCCESS)
+ break;
+
+ hf->ContentSize += pCKeyEntry->ContentSize;
+ hf->EncodedSize += pCKeyEntry->EncodedSize;
+ pFileSpan->EndOffset = hf->ContentSize;
}
}
else
{
- nError = GetLastError();
+ // Load file frames for all spans
+ for(DWORD i = 0; i < hf->SpanCount; i++, pCKeyEntry++, pFileSpan++)
+ {
+ // Load the frames of the file span
+ dwErrCode = LoadSpanFrames(hf, pFileSpan, pCKeyEntry);
+ if(dwErrCode != ERROR_SUCCESS)
+ break;
+ }
}
- return nError;
+ return dwErrCode;
}
-static int ProcessFileFrame(
- TCascStorage * hs,
- LPBYTE pbOutBuffer,
- DWORD cbOutBuffer,
- LPBYTE pbInBuffer,
- DWORD cbInBuffer,
- DWORD dwFrameIndex)
+static DWORD EnsureFileSpanFramesLoaded(TCascFile * hf)
{
+ DWORD dwErrCode;
+
+ if(hf->ContentSize == CASC_INVALID_SIZE64 || hf->pFileSpan->pFrames == NULL)
+ {
+ // Load all frames of all file spans
+ dwErrCode = LoadFileSpanFrames(hf);
+ if(dwErrCode != ERROR_SUCCESS)
+ return dwErrCode;
+
+ // Now the content size must be known
+ if(hf->ContentSize == CASC_INVALID_SIZE64)
+ return ERROR_CAN_NOT_COMPLETE;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+static DWORD DecodeFileFrame(
+ TCascFile * hf,
+ PCASC_CKEY_ENTRY pCKeyEntry,
+ PCASC_FILE_FRAME pFrame,
+ LPBYTE pbEncoded,
+ LPBYTE pbDecoded,
+ DWORD FrameIndex)
+{
+ TCascStorage * hs = hf->hs;
LPBYTE pbWorkBuffer = NULL;
- DWORD cbOutBufferExpected = 0;
+ DWORD cbDecodedExpected = 0;
DWORD cbWorkBuffer = 0;
DWORD dwStepCount = 0;
+ DWORD dwErrCode = ERROR_SUCCESS;
+ DWORD cbEncoded = pFrame->EncodedSize;
+ DWORD cbDecoded = pFrame->ContentSize;
bool bWorkComplete = false;
- int nError = ERROR_SUCCESS;
+
+ //if(pFrame->EncodedSize == 0xda001)
+ //{
+ // FILE * fp = fopen("E:\\frame-da001-002.dat", "wb");
+ // fwrite(pbEncoded, 1, pFrame->EncodedSize, fp);
+ // fclose(fp);
+ //}
+
+ // If this is a file span with plain data, just copy the data
+ if(pCKeyEntry->Flags & CASC_CE_PLAIN_DATA)
+ {
+ assert(pCKeyEntry->ContentSize == pCKeyEntry->EncodedSize);
+ assert(pCKeyEntry->ContentSize == pFrame->ContentSize);
+ assert(pFrame->ContentSize == pFrame->EncodedSize);
+ memcpy(pbDecoded, pbEncoded, pCKeyEntry->ContentSize);
+ return ERROR_SUCCESS;
+ }
+
+ // Shall we verify the frame integrity?
+ if(hf->bVerifyIntegrity)
+ {
+ if(!CascVerifyDataBlockHash(pbEncoded, pFrame->EncodedSize, pFrame->FrameHash.Value))
+ return ERROR_FILE_CORRUPT;
+ }
// Perform the loop
while(bWorkComplete == false)
@@ -416,7 +583,7 @@ static int ProcessFileFrame(
assert(dwStepCount < 2);
// Perform the operation specific by the first byte
- switch(pbInBuffer[0])
+ switch(pbEncoded[0])
{
case 'E': // Encrypted files
@@ -425,14 +592,14 @@ static int ProcessFileFrame(
// Allocate temporary buffer to decrypt into
// Example storage: "2016 - WoW/23420", File: "4ee6bc9c6564227f1748abd0b088e950"
- pbWorkBuffer = CASC_ALLOC(BYTE, cbInBuffer - 1);
- cbWorkBuffer = cbInBuffer - 1;
+ pbWorkBuffer = CASC_ALLOC<BYTE>(cbEncoded - 1);
+ cbWorkBuffer = cbEncoded - 1;
if(pbWorkBuffer == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// Decrypt the stream to the work buffer
- nError = CascDecrypt(hs, pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1, dwFrameIndex);
- if(nError != ERROR_SUCCESS)
+ dwErrCode = CascDecrypt(hs, pbWorkBuffer, &cbWorkBuffer, pbEncoded + 1, cbEncoded - 1, FrameIndex);
+ if(dwErrCode != ERROR_SUCCESS)
{
bWorkComplete = true;
break;
@@ -440,32 +607,32 @@ static int ProcessFileFrame(
// When encrypted, there is always one more step after this.
// Setup the work buffer as input buffer for the next operation
- pbInBuffer = pbWorkBuffer;
- cbInBuffer = cbWorkBuffer;
+ pbEncoded = pbWorkBuffer;
+ cbEncoded = cbWorkBuffer;
break;
case 'Z': // ZLIB compressed files
// If we decompressed less than expected, we simply fill the rest with zeros
// Example: INSTALL file from the TACT CASC storage
- cbOutBufferExpected = cbOutBuffer;
- nError = CascDecompress(pbOutBuffer, &cbOutBuffer, pbInBuffer + 1, cbInBuffer - 1);
+ cbDecodedExpected = cbDecoded;
+ dwErrCode = CascDecompress(pbDecoded, &cbDecoded, pbEncoded + 1, cbEncoded - 1);
// We exactly know what the output buffer size will be.
// If the uncompressed data is smaller, fill the rest with zeros
- if(cbOutBuffer < cbOutBufferExpected)
- memset(pbOutBuffer + cbOutBuffer, 0, (cbOutBufferExpected - cbOutBuffer));
+ if(cbDecoded < cbDecodedExpected)
+ memset(pbDecoded + cbDecoded, 0, (cbDecodedExpected - cbDecoded));
bWorkComplete = true;
break;
case 'N': // Normal stored files
- nError = CascDirectCopy(pbOutBuffer, &cbOutBuffer, pbInBuffer + 1, cbInBuffer - 1);
+ dwErrCode = CascDirectCopy(pbDecoded, &cbDecoded, pbEncoded + 1, cbEncoded - 1);
bWorkComplete = true;
break;
- case 'F': // Recursive frames - not supported
- default: // Unrecognized - if we unpacked something, we consider it done
- nError = ERROR_NOT_SUPPORTED;
+ case 'F': // Recursive frames (not supported)
+ default: // Unrecognized. Could be a plain file data
+ dwErrCode = ERROR_NOT_SUPPORTED;
bWorkComplete = true;
assert(false);
break;
@@ -475,9 +642,18 @@ static int ProcessFileFrame(
dwStepCount++;
}
+ // Some people find it handy to extract data from partially encrypted file,
+ // even at the cost of producing corrupt files.
+ // We overcome missing decryption key by zeroing the encrypted portions
+ if(dwErrCode == ERROR_FILE_ENCRYPTED && hf->bOvercomeEncrypted)
+ {
+ memset(pbDecoded, 0, cbDecoded);
+ dwErrCode = ERROR_SUCCESS;
+ }
+
// Free the temporary buffer
CASC_FREE(pbWorkBuffer);
- return nError;
+ return dwErrCode;
}
static bool GetFileFullInfo(TCascFile * hf, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded)
@@ -485,6 +661,15 @@ static bool GetFileFullInfo(TCascFile * hf, void * pvFileInfo, size_t cbFileInfo
PCASC_FILE_FULL_INFO pFileInfo;
PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry;
TCascStorage * hs = hf->hs;
+ DWORD dwErrCode;
+
+ // Make sure that the file spans are loaded
+ dwErrCode = EnsureFileSpanFramesLoaded(hf);
+ if(dwErrCode != ERROR_SUCCESS)
+ {
+ SetLastError(dwErrCode);
+ return false;
+ }
// Verify whether we have enough space in the buffer
pFileInfo = (PCASC_FILE_FULL_INFO)ProbeOutputBuffer(pvFileInfo, cbFileInfo, sizeof(CASC_FILE_FULL_INFO), pcbLengthNeeded);
@@ -498,14 +683,15 @@ static bool GetFileFullInfo(TCascFile * hf, void * pvFileInfo, size_t cbFileInfo
pFileInfo->ContentFlags = CASC_INVALID_ID;
// Supply information not depending on root
- CascStrPrintf(pFileInfo->DataFileName, _countof(pFileInfo->DataFileName), "data.%03u", hf->ArchiveIndex);
+ CascStrPrintf(pFileInfo->DataFileName, _countof(pFileInfo->DataFileName), "data.%03u", hf->pFileSpan->ArchiveIndex);
pFileInfo->StorageOffset = pCKeyEntry->StorageOffset;
- pFileInfo->SegmentOffset = hf->ArchiveOffset;
+ pFileInfo->SegmentOffset = hf->pFileSpan->ArchiveOffs;
pFileInfo->FileNameHash = 0;
pFileInfo->TagBitMask = pCKeyEntry->TagBitMask;
- pFileInfo->SegmentIndex = hf->ArchiveIndex;
pFileInfo->ContentSize = hf->ContentSize;
pFileInfo->EncodedSize = hf->EncodedSize;
+ pFileInfo->SegmentIndex = hf->pFileSpan->ArchiveIndex;
+ pFileInfo->SpanCount = hf->SpanCount;
// Supply the root-specific information
hs->pRootHandler->GetInfo(pCKeyEntry, pFileInfo);
@@ -514,6 +700,257 @@ static bool GetFileFullInfo(TCascFile * hf, void * pvFileInfo, size_t cbFileInfo
return (pFileInfo != NULL);
}
+static bool GetFileSpanInfo(TCascFile * hf, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded)
+{
+ PCASC_FILE_SPAN_INFO pFileInfo;
+ PCASC_FILE_SPAN pFileSpan = hf->pFileSpan;
+ PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry;
+ DWORD dwErrCode = ERROR_SUCCESS;
+
+ // Make sure that the file spans are loaded
+ dwErrCode = EnsureFileSpanFramesLoaded(hf);
+ if(dwErrCode != ERROR_SUCCESS)
+ {
+ SetLastError(dwErrCode);
+ return false;
+ }
+
+ // Verify whether we have enough space in the buffer
+ pFileInfo = (PCASC_FILE_SPAN_INFO)ProbeOutputBuffer(pvFileInfo, cbFileInfo, sizeof(CASC_FILE_SPAN_INFO) * hf->SpanCount, pcbLengthNeeded);
+ if(pFileInfo != NULL)
+ {
+ // Copy all file spans
+ for(DWORD i = 0; i < hf->SpanCount; i++, pFileInfo++, pFileSpan++, pCKeyEntry++)
+ {
+ CopyMemory16(pFileInfo->CKey, pCKeyEntry->CKey);
+ CopyMemory16(pFileInfo->EKey, pCKeyEntry->EKey);
+ pFileInfo->StartOffset = pFileSpan->StartOffset;
+ pFileInfo->EndOffset = pFileSpan->EndOffset;
+ pFileInfo->ArchiveIndex = pFileSpan->ArchiveIndex;
+ pFileInfo->ArchiveOffs = pFileSpan->ArchiveOffs;
+ pFileInfo->HeaderSize = pFileSpan->HeaderSize;
+ pFileInfo->FrameCount = pFileSpan->FrameCount;
+ }
+ }
+
+ return (pFileInfo != NULL);
+}
+
+
+// Reads the file data from cache. Returns the number of bytes read
+static DWORD ReadFile_Cache(TCascFile * hf, LPBYTE pbBuffer, ULONGLONG StartOffset, ULONGLONG EndOffset)
+{
+ // Is there a file cache at all?
+ if(hf->pbFileCache != NULL && hf->FileCacheStart <= StartOffset && StartOffset < hf->FileCacheEnd)
+ {
+ LPBYTE pbStartBlock = hf->pbFileCache + (size_t)(StartOffset - hf->FileCacheStart);
+
+ // Can we handle the entire request from the cache?
+ if(EndOffset <= hf->FileCacheEnd)
+ {
+ DWORD dwBytesToCopy = (DWORD)(EndOffset - StartOffset);
+
+ memcpy(pbBuffer, pbStartBlock, dwBytesToCopy);
+ return dwBytesToCopy;
+ }
+
+ // We copy as much bytes as available. The rest is handled by normal read
+ else
+ {
+ DWORD dwBytesToCopy = (DWORD)(hf->FileCacheEnd - StartOffset);
+
+ memcpy(pbBuffer, pbStartBlock, dwBytesToCopy);
+ return dwBytesToCopy;
+ }
+ }
+
+ // Can't handle the request from the cache
+ return 0;
+}
+
+// No cache at all. The entire file will be read directly to the user buffer
+static DWORD ReadFile_WholeFile(TCascFile * hf, LPBYTE pbBuffer)
+{
+ PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry;
+ PCASC_FILE_SPAN pFileSpan = hf->pFileSpan;
+ LPBYTE pbSaveBuffer = pbBuffer;
+ LPBYTE pbEncoded;
+ LPBYTE pbEncodedPtr;
+ DWORD dwErrCode;
+
+ for(DWORD SpanIndex = 0; SpanIndex < hf->SpanCount; SpanIndex++, pCKeyEntry++, pFileSpan++)
+ {
+ ULONGLONG ByteOffset = pFileSpan->ArchiveOffs + pFileSpan->HeaderSize;
+ DWORD EncodedSize = pCKeyEntry->EncodedSize - pFileSpan->HeaderSize;
+
+ // Allocate the buffer for the entire encoded span
+ pbEncodedPtr = pbEncoded = CASC_ALLOC<BYTE>(EncodedSize);
+ if(pbEncoded == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return 0;
+ }
+
+ // Load the encoded buffer
+ if(FileStream_Read(pFileSpan->pStream, &ByteOffset, pbEncoded, EncodedSize))
+ {
+ PCASC_FILE_FRAME pFileFrame = pFileSpan->pFrames;
+
+ for(DWORD FrameIndex = 0; FrameIndex < pFileSpan->FrameCount; FrameIndex++, pFileFrame++)
+ {
+ // Decode the file frame
+ dwErrCode = DecodeFileFrame(hf, pCKeyEntry, pFileFrame, pbEncodedPtr, pbBuffer, FrameIndex);
+ if(dwErrCode != ERROR_SUCCESS)
+ break;
+
+ // Move pointers
+ pbEncodedPtr += pFileFrame->EncodedSize;
+ pbBuffer += pFileFrame->ContentSize;
+ }
+ }
+
+ CASC_FREE(pbEncoded);
+ }
+
+ // Give the amount of bytes read
+ return (DWORD)(pbBuffer - pbSaveBuffer);
+}
+
+static DWORD ReadFile_FrameCached(TCascFile * hf, LPBYTE pbBuffer, ULONGLONG StartOffset, ULONGLONG EndOffset)
+{
+ PCASC_CKEY_ENTRY pCKeyEntry = hf->pCKeyEntry;
+ PCASC_FILE_SPAN pFileSpan = hf->pFileSpan;
+ PCASC_FILE_FRAME pFileFrame = NULL;
+ ULONGLONG ByteOffset;
+ LPBYTE pbSaveBuffer = pbBuffer;
+ LPBYTE pbEncoded = NULL;
+ LPBYTE pbDecoded = NULL;
+ DWORD dwBytesRead = 0;
+ DWORD dwErrCode = ERROR_SUCCESS;
+ bool bNeedFreeDecoded = true;
+
+ // Parse all file spans
+ for(DWORD SpanIndex = 0; SpanIndex < hf->SpanCount; SpanIndex++, pCKeyEntry++, pFileSpan++)
+ {
+ if(pFileSpan->StartOffset <= StartOffset && StartOffset < pFileSpan->EndOffset)
+ {
+ for(DWORD FrameIndex = 0; FrameIndex < pFileSpan->FrameCount; FrameIndex++)
+ {
+ // Get the current file frame
+ pFileFrame = pFileSpan->pFrames + FrameIndex;
+
+ // Check the frame byte range
+ if(pFileFrame->StartOffset <= StartOffset && StartOffset < pFileFrame->EndOffset)
+ {
+ // Check bytes read overflow
+ if((dwBytesRead + pFileFrame->ContentSize) < dwBytesRead)
+ {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return 0;
+ }
+
+ // Pick the buffer for decoded data. If we are going to read the entire frame,
+ // there is a little chance that the caller will read the same file range again
+ // So we can as well just unpack the entire frame into the output buffer
+ if(pFileFrame->StartOffset < StartOffset || EndOffset < pFileFrame->EndOffset)
+ {
+ if((pbDecoded = CASC_ALLOC<BYTE>(pFileFrame->ContentSize)) == NULL)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return 0;
+ }
+ bNeedFreeDecoded = true;
+ }
+ else
+ {
+ bNeedFreeDecoded = false;
+ pbDecoded = pbBuffer;
+ }
+
+ // Allocate the encoded frame
+ if((pbEncoded = CASC_ALLOC<BYTE>(pFileFrame->EncodedSize)) == NULL)
+ {
+ CASC_FREE(pbDecoded);
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return 0;
+ }
+
+ // Load the frame to the encoded buffer
+ ByteOffset = pFileFrame->DataFileOffset;
+ if(FileStream_Read(pFileSpan->pStream, &ByteOffset, pbEncoded, pFileFrame->EncodedSize))
+ {
+ ULONGLONG EndOfCopy = CASCLIB_MIN(pFileFrame->EndOffset, EndOffset);
+ DWORD dwBytesToCopy = (DWORD)(EndOfCopy - StartOffset);
+
+ // Decode the frame
+ dwErrCode = DecodeFileFrame(hf, pCKeyEntry, pFileFrame, pbEncoded, pbDecoded, FrameIndex);
+ if(dwErrCode == ERROR_SUCCESS)
+ {
+ // Copy the data
+ if(pbDecoded != pbBuffer)
+ memcpy(pbBuffer, pbDecoded + (DWORD)(StartOffset - pFileFrame->StartOffset), dwBytesToCopy);
+ StartOffset += dwBytesToCopy;
+ pbBuffer += dwBytesToCopy;
+ }
+ }
+
+ // Free the encoded buffer
+ CASC_FREE(pbEncoded);
+
+ // If we are at the end of the read area, break all loops
+ if(dwErrCode != ERROR_SUCCESS || StartOffset >= EndOffset)
+ goto __WorkComplete;
+ if(bNeedFreeDecoded)
+ CASC_FREE(pbDecoded);
+ }
+ }
+ }
+ }
+
+ __WorkComplete:
+
+ if(dwErrCode == ERROR_SUCCESS)
+ {
+ // If there is some data left in the frame, we set it as cache
+ if(pFileFrame != NULL && pbDecoded != NULL && EndOffset < pFileFrame->EndOffset)
+ {
+ CASC_FREE(hf->pbFileCache);
+
+ hf->FileCacheStart = pFileFrame->StartOffset;
+ hf->FileCacheEnd = pFileFrame->EndOffset;
+ hf->pbFileCache = pbDecoded;
+ pbDecoded = NULL;
+ }
+ }
+
+ // Final free of the decoded buffer, if needeed
+ if(bNeedFreeDecoded)
+ CASC_FREE(pbDecoded);
+ pbDecoded = NULL;
+
+ // Return the number of bytes read. Always set LastError.
+ SetLastError(dwErrCode);
+ return (DWORD)(pbBuffer - pbSaveBuffer);
+}
+
+// No cache at all. The entire file will be read directly to the user buffer
+static DWORD ReadFile_NonCached(TCascFile * hf, LPBYTE pbBuffer, ULONGLONG StartOffset, ULONGLONG EndOffset)
+{
+ // Reading the whole file?
+ if(StartOffset == 0 && EndOffset == hf->ContentSize)
+ {
+ return ReadFile_WholeFile(hf, pbBuffer);
+ }
+
+ // Reading just a part of the file?
+ else
+ {
+ assert(false);
+ }
+
+ return 0;
+}
+
//-----------------------------------------------------------------------------
// Public functions
@@ -565,6 +1002,9 @@ bool WINAPI CascGetFileInfo(HANDLE hFile, CASC_FILE_INFO_CLASS InfoClass, void *
case CascFileFullInfo:
return GetFileFullInfo(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded);
+ case CascFileSpanInfo:
+ return GetFileSpanInfo(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded);
+
default:
SetLastError(ERROR_INVALID_PARAMETER);
return false;
@@ -598,55 +1038,64 @@ bool WINAPI CascGetFileInfo(HANDLE hFile, CASC_FILE_INFO_CLASS InfoClass, void *
// WoW(18888) (other) 0x000007d0 - 0x000007d0 0x00000397 0x000007d0 0x00000397 n/a
//
-DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh)
+bool WINAPI CascGetFileSize64(HANDLE hFile, PULONGLONG PtrFileSize)
{
TCascFile * hf;
- int nError;
-
- CASCLIB_UNUSED(pdwFileSizeHigh);
+ DWORD dwErrCode;
// Validate the file handle
if((hf = TCascFile::IsValid(hFile)) == NULL)
{
SetLastError(ERROR_INVALID_HANDLE);
- return CASC_INVALID_SIZE;
+ return false;
}
- // Someone may have provided file content size.
- // If yes, do not load the frames, as it's not necessary.
- if(hf->ContentSize == CASC_INVALID_SIZE)
+ // Validate the file pointer
+ if(PtrFileSize == NULL)
{
- // Make sure that the file header area is loaded
- nError = EnsureFileFramesLoaded(hf);
- if(nError != ERROR_SUCCESS)
- {
- SetLastError(nError);
- return CASC_INVALID_SIZE;
- }
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
- // The content size should be loaded from the frames
- assert(hf->ContentSize != CASC_INVALID_SIZE);
+ // ENCODING on older storages: Content size is not present in the BUILD file
+ // For that reason, we need to query the content size from the file frames
+ dwErrCode = EnsureFileSpanFramesLoaded(hf);
+ if(dwErrCode != ERROR_SUCCESS)
+ {
+ SetLastError(dwErrCode);
+ return false;
}
// Give the file size to the caller
- if(pdwFileSizeHigh != NULL)
- *pdwFileSizeHigh = 0;
- return hf->ContentSize;
+ PtrFileSize[0] = hf->ContentSize;
+ return true;
}
-DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod)
+DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD PtrFileSizeHigh)
+{
+ ULONGLONG FileSize = 0;
+
+ // Retrieve the 64-bit file size
+ if(!CascGetFileSize64(hFile, &FileSize))
+ return CASC_INVALID_SIZE;
+
+ // Give the file size to the caller
+ if(PtrFileSizeHigh != NULL)
+ PtrFileSizeHigh[0] = (DWORD)(FileSize >> 32);
+ return (DWORD)(FileSize);
+}
+
+bool WINAPI CascSetFilePointer64(HANDLE hFile, LONGLONG DistanceToMove, PULONGLONG PtrNewPos, DWORD dwMoveMethod)
{
- TCascFile * hf;
ULONGLONG FilePosition;
- ULONGLONG MoveOffset;
- DWORD dwFilePosHi;
+ TCascFile * hf;
// If the hFile is not a valid file handle, return an error.
hf = TCascFile::IsValid(hFile);
if(hf == NULL)
{
SetLastError(ERROR_INVALID_HANDLE);
- return CASC_INVALID_POS;
+ return false;
}
// Get the relative point where to move from
@@ -666,42 +1115,73 @@ DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHig
default:
SetLastError(ERROR_INVALID_PARAMETER);
- return CASC_INVALID_POS;
+ return false;
}
- // Now get the move offset. Note that both values form
- // a signed 64-bit value (a file pointer can be moved backwards)
- if(plFilePosHigh != NULL)
- dwFilePosHi = *plFilePosHigh;
- else
- dwFilePosHi = (lFilePos & 0x80000000) ? 0xFFFFFFFF : 0;
- MoveOffset = MAKE_OFFSET64(dwFilePosHi, lFilePos);
-
// Now calculate the new file pointer
- // Do not allow the file pointer to overflow
- FilePosition = ((FilePosition + MoveOffset) >= FilePosition) ? (FilePosition + MoveOffset) : 0;
+ if(DistanceToMove >= 0)
+ {
+ // Do not allow the file pointer to overflow 64-bit range
+ if((FilePosition + DistanceToMove) < FilePosition)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
- // CASC files can't be bigger than 4 GB.
- // We don't allow to go past 4 GB
- if(FilePosition >> 32)
+ // Do not allow the file pointer to overflow the file size
+ if((FilePosition = FilePosition + DistanceToMove) > hf->ContentSize)
+ FilePosition = hf->ContentSize;
+ hf->FilePointer = FilePosition;
+ }
+ else
{
- SetLastError(ERROR_INVALID_PARAMETER);
- return CASC_INVALID_POS;
+ // Do not allow the file pointer to underflow 64-bit range
+ if((FilePosition + DistanceToMove) > FilePosition)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ // Do not allow the file pointer to move to negative values
+ if((FilePosition = FilePosition + DistanceToMove) < 0)
+ FilePosition = 0;
+ hf->FilePointer = FilePosition;
}
- // Change the file position
- hf->FilePointer = (DWORD)FilePosition;
+ // Give the result size to the caller
+ if(PtrNewPos != NULL)
+ PtrNewPos[0] = hf->FilePointer;
+ return true;
+}
+
+DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * PtrFilePosHigh, DWORD dwMoveMethod)
+{
+ ULONGLONG NewPos = 0;
+ LONGLONG DistanceToMove;
+
+ // Assemble the 64-bit distance to move
+ DistanceToMove = (PtrFilePosHigh != NULL) ? MAKE_OFFSET64(PtrFilePosHigh[0], lFilePos) : (LONGLONG)(LONG)lFilePos;
+
+ // Set the file offset
+ if(!CascSetFilePointer64(hFile, DistanceToMove, &NewPos, dwMoveMethod))
+ return CASC_INVALID_POS;
- // Return the new file position
- if(plFilePosHigh != NULL)
- *plFilePosHigh = 0;
- return hf->FilePointer;
+ // Give the result to the caller
+ if(PtrFilePosHigh != NULL)
+ PtrFilePosHigh[0] = (LONG)(NewPos >> 32);
+ return (DWORD)(NewPos);
}
-bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDWORD pdwBytesRead)
+bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDWORD PtrBytesRead)
{
+ ULONGLONG SaveFilePointer;
+ ULONGLONG StartOffset;
+ ULONGLONG EndOffset;
TCascFile * hf;
- int nError = ERROR_SUCCESS;
+ LPBYTE pbBuffer = (LPBYTE)pvBuffer;
+ DWORD dwBytesRead1 = 0; // From cache
+ DWORD dwBytesRead2 = 0; // From file
+ DWORD dwErrCode;
// The buffer must be valid
if(pvBuffer == NULL)
@@ -717,129 +1197,78 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW
return false;
}
- // If the file frames are not loaded yet, do it now
- if(nError == ERROR_SUCCESS)
+ // If we don't have file frames loaded, we need to do it now.
+ // Need to do it before file range check, as the file size may be unknown at this point
+ dwErrCode = EnsureFileSpanFramesLoaded(hf);
+ if(dwErrCode != ERROR_SUCCESS)
{
- nError = EnsureFileFramesLoaded(hf);
+ SetLastError(dwErrCode);
+ return false;
}
// If the file position is at or beyond end of file, do nothing
- if(nError == ERROR_SUCCESS)
+ SaveFilePointer = StartOffset = hf->FilePointer;
+ if(StartOffset >= hf->ContentSize)
{
- // Check the starting position
- if(hf->FilePointer >= hf->ContentSize)
- {
- *pdwBytesRead = 0;
- return true;
- }
-
- // Check the ending position
- if((hf->FilePointer + dwBytesToRead) > hf->ContentSize)
- {
- dwBytesToRead = hf->ContentSize - hf->FilePointer;
- }
+ PtrBytesRead[0] = 0;
+ return true;
}
- // Allocate cache buffer for the entire file. This is the fastest approach
- // (without reallocations). However, this may consume quite a lot of memory
- // (Storage: "2016 - Starcraft II/45364", file: "3d815f40c0413701aa2bd214070d0062"
- // needs 0x239a09b3 bytes of memory (~600 MB)
- if(nError == ERROR_SUCCESS)
+ // If the read area goes beyond end of the file, cut the number of bytes to read
+ EndOffset = StartOffset + dwBytesToRead;
+ if(EndOffset > hf->ContentSize)
{
- if(hf->pbFileCache == NULL)
- {
- // Allocate buffer
- hf->pbFileCache = CASC_ALLOC(BYTE, hf->ContentSize);
- hf->cbFileCache = hf->ContentSize;
- if(hf->pbFileCache == NULL)
- nError = ERROR_NOT_ENOUGH_MEMORY;
- }
+ EndOffset = hf->ContentSize;
}
- // Load all frames that are not loaded yet
- if(nError == ERROR_SUCCESS)
+ // Can we handle the request (at least partially) from the cache?
+ if((dwBytesRead1 = ReadFile_Cache(hf, pbBuffer, StartOffset, EndOffset)) != 0)
{
- PCASC_FILE_FRAME pFrame = hf->pFrames;
- DWORD StartFrameOffset = 0;
- DWORD StartReadOffset = hf->FilePointer;
- DWORD EndReadOffset = hf->FilePointer + dwBytesToRead;
+ // Move pointers
+ StartOffset = StartOffset + dwBytesRead1;
+ pbBuffer += dwBytesRead1;
- for(DWORD i = 0; (i < hf->FrameCount) && (nError == ERROR_SUCCESS); i++, pFrame++)
+ // Has the read request been fully satisfied?
+ if(StartOffset == EndOffset)
{
- LPBYTE pbDecodedFrame = hf->pbFileCache + StartFrameOffset;
- LPBYTE pbEncodedFrame;
- DWORD EndFrameOffset = StartFrameOffset + pFrame->ContentSize;
-
- // Does that frame belong to the range?
- if(StartReadOffset < EndFrameOffset && EndReadOffset > StartFrameOffset)
- {
- // Is the frame already loaded?
- if (pFrame->FileOffset == CASC_INVALID_POS)
- {
- // Allocate space for the encoded frame
- pbEncodedFrame = CASC_ALLOC(BYTE, pFrame->EncodedSize);
- if (pbEncodedFrame != NULL)
- {
- // Load the encoded frame data
- nError = LoadEncodedFrame(hf->pStream, pFrame, pbEncodedFrame, hf->bVerifyIntegrity);
- if (nError == ERROR_SUCCESS)
- {
- // Decode the frame
- nError = ProcessFileFrame(hf->hs,
- pbDecodedFrame,
- pFrame->ContentSize,
- pbEncodedFrame,
- pFrame->EncodedSize,
- (DWORD)(pFrame - hf->pFrames));
-
- // Some people find it handy to extract data from partially encrypted file,
- // even at the cost producing files that are corrupt.
- // We overcome missing decryption key by zeroing the encrypted portions
- if(nError == ERROR_FILE_ENCRYPTED && hf->bOvercomeEncrypted)
- {
- memset(pbDecodedFrame, 0, pFrame->ContentSize);
- nError = ERROR_SUCCESS;
- }
-
- if (nError == ERROR_SUCCESS)
- {
- // Mark the frame as loaded
- pFrame->FileOffset = StartFrameOffset;
- }
- }
-
- // Free the frame buffer
- CASC_FREE(pbEncodedFrame);
- }
- else
- {
- nError = ERROR_NOT_ENOUGH_MEMORY;
- }
- }
- }
-
- // If the frame start is past the read offset, stop the loop
- if ((StartFrameOffset + pFrame->ContentSize) >= EndReadOffset)
- break;
- StartFrameOffset += pFrame->ContentSize;
+ if(PtrBytesRead != NULL)
+ PtrBytesRead[0] = dwBytesRead1;
+ hf->FilePointer = EndOffset;
+ return true;
}
}
- // Now all frames have been loaded into the cache; copy the entire block to the output buffer
- if(nError == ERROR_SUCCESS)
+ // Perform the cache-strategy-specific read
+ switch(hf->CacheStrategy)
{
- // Copy the entire data
- memcpy(pvBuffer, hf->pbFileCache + hf->FilePointer, dwBytesToRead);
- hf->FilePointer += dwBytesToRead;
+ // No caching at all. The entire file will be read directly to the user buffer
+ // Used for loading internal files, where we need to read the whole file
+ case CascCacheNothing:
+ dwBytesRead2 = ReadFile_NonCached(hf, pbBuffer, StartOffset, EndOffset);
+ break;
- // Give the number of bytes read
- if(pdwBytesRead != NULL)
- *pdwBytesRead = dwBytesToRead;
+ // Read as many frames as we can. The last loaded frame, if not read entirely,
+ // will stay in the cache - We expect the next read to continue from that offset.
+ case CascCacheLastFrame:
+ dwBytesRead2 = ReadFile_FrameCached(hf, pbBuffer, StartOffset, EndOffset);
+ break;
+ }
+
+ // If the second-stage-read failed, we invalidate the entire operation and return 0 bytes read
+ if(dwBytesRead2 != 0)
+ {
+ // Give the result to the caller
+ if(PtrBytesRead != NULL)
+ PtrBytesRead[0] = (dwBytesRead1 + dwBytesRead2);
+ hf->FilePointer = StartOffset + dwBytesRead2;
return true;
}
else
{
- SetLastError(nError);
+ // Give the result to the caller
+ if(PtrBytesRead != NULL)
+ PtrBytesRead[0] = 0;
+ hf->FilePointer = SaveFilePointer;
return false;
}
}
diff --git a/dep/CascLib/src/CascRootFile_Diablo3.cpp b/dep/CascLib/src/CascRootFile_Diablo3.cpp
index bbe369c7646..c29f13f74a9 100644
--- a/dep/CascLib/src/CascRootFile_Diablo3.cpp
+++ b/dep/CascLib/src/CascRootFile_Diablo3.cpp
@@ -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;
}
diff --git a/dep/CascLib/src/CascRootFile_Install.cpp b/dep/CascLib/src/CascRootFile_Install.cpp
index a0b00f73168..86d621e2e47 100644
--- a/dep/CascLib/src/CascRootFile_Install.cpp
+++ b/dep/CascLib/src/CascRootFile_Install.cpp
@@ -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;
+
+ // 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 RootHandler_CreateInstall(TCascStorage * hs, LPBYTE pbInstallFile, DWORD cbInstallFile)
+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;
}
diff --git a/dep/CascLib/src/CascRootFile_MNDX.cpp b/dep/CascLib/src/CascRootFile_MNDX.cpp
index 9dca8d30eb4..c049d813967 100644
--- a/dep/CascLib/src/CascRootFile_MNDX.cpp
+++ b/dep/CascLib/src/CascRootFile_MNDX.cpp
@@ -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;
-
- nError = ItemBits.LoadFromStream(InStream);
- if(nError != ERROR_SUCCESS)
- return nError;
-
- nError = InStream.GetValue<DWORD>(total_count);
- if(nError != ERROR_SUCCESS)
- return nError;
- nError = InStream.GetValue<DWORD>(valid_count);
- if(nError != ERROR_SUCCESS)
- return nError;
+ DWORD dwErrCode;
+
+ dwErrCode = ItemBits.LoadFromStream(InStream);
+ if(dwErrCode != ERROR_SUCCESS)
+ return dwErrCode;
+
+ 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;
}
diff --git a/dep/CascLib/src/CascRootFile_OW.cpp b/dep/CascLib/src/CascRootFile_OW.cpp
index 24f5af81b34..251a67284cb 100644
--- a/dep/CascLib/src/CascRootFile_OW.cpp
+++ b/dep/CascLib/src/CascRootFile_OW.cpp
@@ -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;
}
diff --git a/dep/CascLib/src/CascRootFile_TVFS.cpp b/dep/CascLib/src/CascRootFile_TVFS.cpp
index 38d32120805..25ccf450397 100644
--- a/dep/CascLib/src/CascRootFile_TVFS.cpp
+++ b/dep/CascLib/src/CascRootFile_TVFS.cpp
@@ -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
- //
-
- SpanEntry.dwFileOffset = ConvertBytesToInteger_4(pbVfsSpanEntry);
- SpanEntry.dwSpanSize = ConvertBytesToInteger_4(pbVfsSpanEntry + sizeof(DWORD));
- SpanEntry.dwCftOffset = ConvertBytesToInteger_X(pbVfsSpanEntry + sizeof(DWORD) + sizeof(DWORD), DirHeader.CftOffsSize);
-
- // 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;
+ // Convert all spans
+ for(size_t i = 0; i < SpanCount; i++)
+ {
+ DWORD 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 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;
+ // Find the CKey entry
+ pCKeyEntry = FindCKeyEntry_EKey(hs, SpanEntry.EKey);
+ if(pCKeyEntry != 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.ContentSize);
+ FileTree.InsertByName(pCKeyEntry, PathBuffer);
- // Copy the EKey
- memcpy(EKey.Value, SpanEntry.pbCftFileEntry, DirHeader.EKeySize);
+ // 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;
- // We need to check whether this is another TVFS directory file
- if (IsVfsSubDirectory(hs, DirHeader, SubHeader, EKey, SpanEntry.dwSpanSize) == ERROR_SUCCESS)
+ // Parse all span entries
+ for(DWORD dwSpanIndex = 0; dwSpanIndex < dwSpanCount; dwSpanIndex++)
{
- // Add colon (':')
- PathBuffer_AddChar(PathBuffer, ':');
+ PCASC_CKEY_ENTRY pSpanEntry = pSpanEntries + dwSpanIndex;
- // Insert the file to the file tree
- if((pCKeyEntry = FindCKeyEntry_EKey(hs, EKey.Value)) != NULL)
+ // Find the CKey entry
+ pCKeyEntry = FindCKeyEntry_EKey(hs, pSpanEntries[dwSpanIndex].EKey);
+ if(pCKeyEntry == NULL)
{
- // The file content size should already be there
- assert(pCKeyEntry->ContentSize == SpanEntry.dwSpanSize);
- FileTree.InsertByName(pCKeyEntry, PathBuffer.szBegin);
+ 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;
}
diff --git a/dep/CascLib/src/CascRootFile_Text.cpp b/dep/CascLib/src/CascRootFile_Text.cpp
index e78b0bf086f..7662f85d86d 100644
--- a/dep/CascLib/src/CascRootFile_Text.cpp
+++ b/dep/CascLib/src/CascRootFile_Text.cpp
@@ -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;
}
diff --git a/dep/CascLib/src/CascRootFile_WoW.cpp b/dep/CascLib/src/CascRootFile_WoW.cpp
index 766e226a2b1..3bbd8b81ef3 100644
--- a/dep/CascLib/src/CascRootFile_WoW.cpp
+++ b/dep/CascLib/src/CascRootFile_WoW.cpp
@@ -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;
}
diff --git a/dep/CascLib/src/CascStructs.h b/dep/CascLib/src/CascStructs.h
index f67da810c9e..42478c324f0 100644
--- a/dep/CascLib/src/CascStructs.h
+++ b/dep/CascLib/src/CascStructs.h
@@ -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];
diff --git a/dep/CascLib/src/DllMain.rc b/dep/CascLib/src/DllMain.rc
index 0ae89026266..7cb068f5ada 100644
--- a/dep/CascLib/src/DllMain.rc
+++ b/dep/CascLib/src/DllMain.rc
@@ -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"
diff --git a/dep/CascLib/src/common/Array.h b/dep/CascLib/src/common/Array.h
index 1dc96b7be8e..ea99d10fe22 100644
--- a/dep/CascLib/src/common/Array.h
+++ b/dep/CascLib/src/common/Array.h
@@ -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)
diff --git a/dep/CascLib/src/common/Common.cpp b/dep/CascLib/src/common/Common.cpp
index 2860671227b..1fc6cc977b4 100644
--- a/dep/CascLib/src/common/Common.cpp
+++ b/dep/CascLib/src/common/Common.cpp
@@ -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;
diff --git a/dep/CascLib/src/common/Common.h b/dep/CascLib/src/common/Common.h
index 45a2ec93e6b..d32202413ba 100644
--- a/dep/CascLib/src/common/Common.h
+++ b/dep/CascLib/src/common/Common.h
@@ -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
diff --git a/dep/CascLib/src/common/Csv.cpp b/dep/CascLib/src/common/Csv.cpp
index 24b654daa86..c665fc00c4c 100644
--- a/dep/CascLib/src/common/Csv.cpp
+++ b/dep/CascLib/src/common/Csv.cpp
@@ -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()
diff --git a/dep/CascLib/src/common/Csv.h b/dep/CascLib/src/common/Csv.h
index bf1a7413e13..394eab1e2f0 100644
--- a/dep/CascLib/src/common/Csv.h
+++ b/dep/CascLib/src/common/Csv.h
@@ -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;
diff --git a/dep/CascLib/src/common/FileStream.cpp b/dep/CascLib/src/common/FileStream.cpp
index 845e793a6fa..cd3300a46a7 100644
--- a/dep/CascLib/src/common/FileStream.cpp
+++ b/dep/CascLib/src/common/FileStream.cpp
@@ -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);
}
diff --git a/dep/CascLib/src/common/FileStream.h b/dep/CascLib/src/common/FileStream.h
index a2b0c5b9f7d..e934cca062f 100644
--- a/dep/CascLib/src/common/FileStream.h
+++ b/dep/CascLib/src/common/FileStream.h
@@ -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);
diff --git a/dep/CascLib/src/common/FileTree.cpp b/dep/CascLib/src/common/FileTree.cpp
index f753c25f263..ceb578cf578 100644
--- a/dep/CascLib/src/common/FileTree.cpp
+++ b/dep/CascLib/src/common/FileTree.cpp
@@ -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())
diff --git a/dep/CascLib/src/common/FileTree.h b/dep/CascLib/src/common/FileTree.h
index 904da6c4c46..38bc4e07e9f 100644
--- a/dep/CascLib/src/common/FileTree.h
+++ b/dep/CascLib/src/common/FileTree.h
@@ -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
};
diff --git a/dep/CascLib/src/common/ListFile.cpp b/dep/CascLib/src/common/ListFile.cpp
index 36607d39186..a2239e18fa3 100644
--- a/dep/CascLib/src/common/ListFile.cpp
+++ b/dep/CascLib/src/common/ListFile.cpp
@@ -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;
}
diff --git a/dep/CascLib/src/common/Map.h b/dep/CascLib/src/common/Map.h
index 13799eb0528..a6eface844d 100644
--- a/dep/CascLib/src/common/Map.h
+++ b/dep/CascLib/src/common/Map.h
@@ -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)
diff --git a/dep/CascLib/src/common/Path.h b/dep/CascLib/src/common/Path.h
new file mode 100644
index 00000000000..b77dde0cc3d
--- /dev/null
+++ b/dep/CascLib/src/common/Path.h
@@ -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__
diff --git a/dep/CascLib/src/common/RootHandler.cpp b/dep/CascLib/src/common/RootHandler.cpp
index 89b693f55cb..b33f96209d9 100644
--- a/dep/CascLib/src/common/RootHandler.cpp
+++ b/dep/CascLib/src/common/RootHandler.cpp
@@ -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;
}