RTech: major cleanup of pak globals and improvement

Add missing types to fully mapped out PakGlobals_s struct, which is a 13MiB+ structure! This covers the vast majority of the pakfile system which is why we had to change a bunch of stuff for this patch. This patch also comes with:

- Reversed 'JobFifoLock_s' structure
- Reversed 'PakTracker_s' structure
- Reversed 'PakAssetTracker_s' structure

Many globals have been dropped as they were covered by the large PakGlobals_s singleton.

The pak decoder logic has been changed up as well, we now use a decode mode enumerant which will make it easier to add in more decoders for the pak files in the future.
This commit is contained in:
Kawe Mazidjatari 2024-02-22 02:09:28 +01:00
parent 869741b3b3
commit 6e7bfab4bd
19 changed files with 1223 additions and 682 deletions

View File

@ -303,9 +303,9 @@ void Pak_ListPaks_f(const CCommand& args)
uint32_t nTotalLoaded = 0;
for (int16_t i = 0, n = *g_pLoadedPakCount; i < n; ++i)
for (int16_t i = 0, n = g_pakGlobals->loadedPakCount; i < n; ++i)
{
const PakLoadedInfo_t& info = g_pLoadedPakInfo[i];
const PakLoadedInfo_t& info = g_pakGlobals->loadedPaks[i];
if (info.status == EPakStatus::PAK_STATUS_FREED)
continue;
@ -335,7 +335,7 @@ void Pak_ListTypes_f(const CCommand& args)
for (int8_t i = 0; i < PAK_MAX_TYPES; ++i)
{
PakAssetBinding_t* type = &g_pPakGlobals->assetBindings[i];
PakAssetBinding_t* type = &g_pakGlobals->assetBindings[i];
if (!type->description)
continue;

View File

@ -22,6 +22,10 @@ typedef intptr_t intp;
typedef const unsigned char* rsig_t;
// 32bit and 64bit wide boolean type
typedef int32_t b32;
typedef int64_t b64;
// signed size types
typedef std::make_signed_t<std::size_t> ssize_t;

View File

@ -140,7 +140,7 @@ void Mod_GetAllInstalledMaps()
if (!regexMatches.empty())
{
const string match = regexMatches[1].str();
const std::sub_match<const char*>& match = regexMatches[1];
if (match.compare("frontend") == 0)
continue; // Frontend contains no BSP's.
@ -152,8 +152,12 @@ void Mod_GetAllInstalledMaps()
continue; // Common contains mp_lobby.
}
else if (!g_InstalledMaps.HasElement(match.c_str()))
g_InstalledMaps.AddToTail(match.c_str());
else
{
const string mapName = match.str();
if (!g_InstalledMaps.HasElement(mapName.c_str()))
g_InstalledMaps.AddToTail(mapName.c_str());
}
}
}
}
@ -232,15 +236,18 @@ void Mod_QueuedPakCacheFrame()
{
if (*data->pakName)
{
const int index = data->pakId & PAK_MAX_HANDLES_MASK;
PakLoadedInfo_t* const pakInfo = Pak_GetPakInfo(data->pakId);
EPakStatus status;
// TODO: revisit this, this appears incorrect but also the way
// respawn does this. it this always supposed to be true on
// retail builds?
bool keepLoaded = true;
data->keepLoaded = true;
if (g_pLoadedPakInfo[index].handle == data->pakId)
if (pakInfo->handle == data->pakId)
{
status = g_pLoadedPakInfo[index].status;
status = pakInfo->status;
keepLoaded = data->keepLoaded;
}
else
@ -359,12 +366,14 @@ void Mod_QueuedPakCacheFrame()
{
if (!*(_BYTE*)(pMTVFTaskItem + 4))
{
if (*g_pPakHasPendingUnloadJobs || *g_pLoadedPakCount != *g_pRequestedPakCount)
JobFifoLock_s* const pakFifoLock = &g_pakGlobals->fifoLock;
if (g_pakGlobals->hasPendingUnloadJobs || g_pakGlobals->loadedPakCount != g_pakGlobals->requestedPakCount)
{
if (!JT_AcquireFifoLockOrHelp(g_pPakFifoLock)
&& !JT_HelpWithJobTypes((__int64(__fastcall*)(__int64, _DWORD*, __int64, _QWORD*))g_pPakFifoLockWrapper, g_pPakFifoLock, -1i64, 0i64))
if (!JT_AcquireFifoLockOrHelp(pakFifoLock)
&& !JT_HelpWithJobTypes(g_pPakFifoLockWrapper, pakFifoLock, -1i64, 0i64))
{
JT_HelpWithJobTypesOrSleep((unsigned __int8(__fastcall*)(_QWORD))g_pPakFifoLockWrapper, g_pPakFifoLock, -1i64, 0i64, 0i64, 1);
JT_HelpWithJobTypesOrSleep(g_pPakFifoLockWrapper, pakFifoLock, -1i64, 0i64, 0i64, 1);
}
Mod_UnloadPendingAndPrecacheRequestedPaks();
@ -374,10 +383,11 @@ void Mod_QueuedPakCacheFrame()
if (*g_bPakFifoLockAcquired)
{
*g_bPakFifoLockAcquired = 0;
JT_ReleaseFifoLock(g_pPakFifoLock);
JT_ReleaseFifoLock(pakFifoLock);
}
}
JT_ReleaseFifoLock(g_pPakFifoLock);
JT_ReleaseFifoLock(pakFifoLock);
pMTVFTaskItem = *g_pMTVFTaskItem;
}
@ -413,9 +423,9 @@ CHECK_FOR_FAILURE:
if (commonData->pakId != INVALID_PAK_HANDLE)
{
const int infoIndex = (commonData->pakId & PAK_MAX_HANDLES_MASK);
const PakLoadedInfo_t* const pli = Pak_GetPakInfo(commonData->pakId);
if (g_pLoadedPakInfo[infoIndex].handle != commonData->pakId || ((g_pLoadedPakInfo[infoIndex].status - 9) & 0xFFFFFFFB) != 0)
if (pli->handle != commonData->pakId || ((pli->status - 9) & 0xFFFFFFFB) != 0)
{
*g_pPakPrecacheJobFinished = false;
return;

View File

@ -5,7 +5,6 @@
#include "rtech/iasync.h"
#include "rtech/rstdlib.h"
#include "thirdparty/zstd/zstd.h"
// pak header versions
#define PAK_HEADER_MAGIC (('k'<<24)+('a'<<16)+('P'<<8)+'R')
@ -13,19 +12,25 @@
// pak header flags
#define PAK_HEADER_FLAGS_HAS_MODULE (1<<0)
#define PAK_HEADER_FLAGS_HAS_MODULE_EXTENDED PAK_HEADER_FLAGS_HAS_MODULE | (1<<1)
#define PAK_HEADER_FLAGS_COMPRESSED (1<<8)
// use the ZStd decoder instead of the RTech one
#define PAK_HEADER_FLAGS_ZSTREAM (1<<9)
#define PAK_HEADER_FLAGS_ZSTREAM_ENCODED (1<<9)
// max amount of types at runtime in which assets will be tracked
#define PAK_MAX_TYPES 64
#define PAK_MAX_TYPES_MASK (PAK_MAX_TYPES-1)
// max amount of global pak assets at runtime
#define PAK_MAX_ASSETS 0x40000
#define PAK_MAX_ASSETS 0x40000 // TODO: rename to PAK_MAX_LOADED_ASSETS
#define PAK_MAX_ASSETS_MASK (PAK_MAX_ASSETS-1)
// max amount of global pak assets tracked at runtime
#define PAK_MAX_TRACKED_ASSETS (PAK_MAX_ASSETS/2)
#define PAK_MAX_TRACKED_ASSETS_MASK (PAK_MAX_TRACKED_ASSETS-1)
// max amount of segments a pak file could have
#define PAK_MAX_SEGMENTS 20
@ -37,7 +42,7 @@
#define PAK_MAX_STREAMING_FILE_HANDLES_PER_SET 4
// max amount of paks that could be loaded at runtime
#define PAK_MAX_HANDLES 512
#define PAK_MAX_HANDLES 512 // TODO: rename to PAK_MAX_LOADED_PAKS
#define PAK_MAX_HANDLES_MASK (PAK_MAX_HANDLES-1)
// max amount of async streaming requests that could be made per pak file, if a
@ -51,7 +56,8 @@
// size is below PAK_DECODE_IN_RING_BUFFER_SIZE, the system allocates a
// buffer with the size of the compressed pak + the value of this define
// the system should still use the default ring buffer input size for decoding
// as these pak files get decoded in one-go; there is buffer wrapping going on
// as these pak files get decoded in one-go; there is no input buffer wrapping
// going on
#define PAK_DECODE_IN_RING_BUFFER_SMALL_SIZE 0x1000
#define PAK_DECODE_IN_RING_BUFFER_SMALL_MASK (PAK_DECODE_IN_RING_BUFFER_SMALL_SIZE-1)
@ -64,6 +70,9 @@
#define PAK_DECODE_OUT_RING_BUFFER_SIZE 0x400000
#define PAK_DECODE_OUT_RING_BUFFER_MASK (PAK_DECODE_OUT_RING_BUFFER_SIZE-1)
// max amount to read per async fs read request
#define PAK_READ_DATA_CHUNK_SIZE (1ull << 19)
// base pak directory containing paks sorted in platform specific subdirectories
#define PAK_BASE_PATH "paks\\"
#define PLATFORM_PAK_PATH PAK_BASE_PATH"Win64\\"
@ -73,7 +82,10 @@
#define PLATFORM_PAK_OVERRIDE_PATH PAK_BASE_PATH"Win64_override\\"
// the handle that should be returned when a pak failed to load or process
#define INVALID_PAK_HANDLE -1
#define INVALID_PAK_HANDLE -1 // TODO: rename to PAK_INVALID_HANDLE
#define PAK_MAX_DISPATCH_LOAD_JOBS 4
#define PAK_DEFAULT_JOB_GROUP_ID 0x3000
//-----------------------------------------------------------------------------
// Forward declarations
@ -116,6 +128,24 @@ union PakPage_t
//-----------------------------------------------------------------------------
// Enumerations
//-----------------------------------------------------------------------------
enum EPakDecodeMode : int32_t
{
MODE_DISABLED = -1,
// the default decoder
MODE_RTECH,
MODE_ZSTD
};
enum EPakStreamSet
{
STREAMING_SET_MANDATORY = 0,
STREAMING_SET_OPTIONAL,
// number of streaming sets
STREAMING_SET_COUNT
};
enum EPakStatus : int32_t
{
PAK_STATUS_FREED = 0,
@ -136,15 +166,6 @@ enum EPakStatus : int32_t
PAK_STATUS_BUSY = 15
};
enum EPakStreamSet
{
STREAMING_SET_MANDATORY = 0,
STREAMING_SET_OPTIONAL,
// number of streaming sets
STREAMING_SET_COUNT
};
struct PakAssetBinding_t
{
enum EType
@ -221,17 +242,152 @@ struct PakAsset_t
struct PakAssetShort_t
{
PakGuid_t guid;
uint32_t unk_8;
uint32_t trackerIndex;
uint32_t unk_C;
void* head;
void* cpu;
};
struct PakGlobals_t
struct PakAssetTracker_s
{
PakAssetBinding_t assetBindings[PAK_MAX_TYPES]; // [ PIXIE ]: Max possible registered assets on Season 3, 0-2 I did not check yet.
PakAssetShort_t assets[PAK_MAX_ASSETS];
// end size unknown, but there appears to be stuff below too
void* memPage;
int trackerIndex;
int loadedPakIndex;
uint8_t assetTypeHashIdx;
char unk_11[3];
char unk_10[4];
};
struct PakTracker_s
{
uint32_t numPaksTracked;
int unk_4;
int unk_8;
char gap_C[644100];
int loadedAssetIndices[PAK_MAX_HANDLES];
char gap_9DC04[522240];
};
class PakLoadedInfo_t
{
public:
struct StreamingInfo_t
{
inline void Reset()
{
embeddedStarpakName = nullptr;
streamFileNumber[0] = FS_ASYNC_FILE_INVALID;
streamFileNumber[1] = FS_ASYNC_FILE_INVALID;
streamFileNumber[2] = FS_ASYNC_FILE_INVALID;
streamFileNumber[3] = FS_ASYNC_FILE_INVALID;
streamFileCount = NULL;
streamingDisabled = true;
}
char* embeddedStarpakName;
int streamFileNumber[PAK_MAX_STREAMING_FILE_HANDLES_PER_SET];
int streamFileCount;
bool streamingDisabled;
char padding_maybe[3];
};
PakHandle_t handle;
EPakStatus status;
JobID_t loadJobId;
uint32_t padding_maybe;
// the log level of the pak, this is also used for errors if a pak failed
// to load; the higher the level, the more important this pak file is
int logLevel;
uint32_t assetCount;
const char* fileName;
CAlignedMemAlloc* allocator;
PakGuid_t* assetGuids; //size of the array is m_nAssetCount
void* segmentBuffers[PAK_SEGMENT_BUFFER_TYPES];
_QWORD qword50;
FILETIME fileTime;
PakFile_t* pakFile;
StreamingInfo_t streamInfo[STREAMING_SET_COUNT];
uint32_t fileHandle;
uint8_t m_nUnk5;
HMODULE hModule;
}; //Size: 0x00B8/0x00E8
struct PakGlobals_s
{
// [ PIXIE ]: Max possible registered assets on Season 3, 0-2 I did not check yet.
PakAssetBinding_t assetBindings[PAK_MAX_TYPES];
PakAssetShort_t loadedAssets[PAK_MAX_ASSETS];
// assets that are tracked across all asset types
PakAssetTracker_s trackedAssets[PAK_MAX_TRACKED_ASSETS];
RHashMap trackedAssetMap; // links to 'trackedAssets'
RHashMap loadedPakMap; // links to 'loadedPaks'
// all currently loaded pak handles
PakLoadedInfo_t loadedPaks[PAK_MAX_HANDLES];
RMultiHashMap unkMap2; // links to 'unkIntArray' and 'unkIntArray2'
int unkIntArray[PAK_MAX_TRACKED_ASSETS];
int unkIntArray2[PAK_MAX_ASSETS];
// whether asset streaming (mandatory & optional) is enabled
b64 useStreamingSystem;
// whether we should emulate (fake) our streaming install for debugging
b64 emulateStreamingInstallInit;
b64 emulateStreamingInstall;
// mounted # streamable assets (globally across all paks)
int64_t numStreamableAssets;
b64 hasPendingUnloadJobs;
// paks that contain tracked assets
PakTracker_s* pakTracker;
// called when threads have to be synced (e.g. syncing the render thread
// when we execute the unloading of paks and assets)
void* threadSyncFunc;
// the index to the asset in the trackedAssets array of the last asset we
// tracked
int lastAssetTrackerIndex;
bool updateSplitScreenAnims;
// the current # pending asset loading jobs
int16_t numAssetLoadJobs;
JobFifoLock_s fifoLock;
JobID_t pakLoadJobId;
int16_t loadedPakCount;
int16_t requestedPakCount;
PakHandle_t loadedPakHandles[PAK_MAX_HANDLES];
JobTypeID_t assetBindJobTypes[PAK_MAX_TYPES];
JobTypeID_t jobTypeSlots_Unused[PAK_MAX_TYPES];
JobTypeID_t dispatchLoadJobTypes[PAK_MAX_DISPATCH_LOAD_JOBS];
uint8_t dispathLoadJobPriorities[PAK_MAX_DISPATCH_LOAD_JOBS]; // promoted to JobPriority_e
uint32_t dispatchLoadJobAffinities[PAK_MAX_DISPATCH_LOAD_JOBS];
RTL_SRWLOCK cpuDataLock;
char unknown_or_unused[32];
void* addToMapFunc;
void* removeFromMapFunc;
__int64 qword_167ED8540;
int dword_167ED8548;
int dword_167ED854C;
__int64 qword_167ED8550;
int dword_167ED8558;
int unknown_dword_or_nothing;
int dword_167ED8560;
int numPatchedPaks;
const char** patchedPakFiles;
uint8_t* patchNumbers;
};
struct PakPatchFileHeader_t
@ -275,9 +431,19 @@ struct PakFileHeader_t
return headerSize;
}
inline bool IsCompressed() const
inline EPakDecodeMode GetCompressionMode() const
{
return flags & PAK_HEADER_FLAGS_COMPRESSED;
if (flags & PAK_HEADER_FLAGS_ZSTREAM_ENCODED)
return EPakDecodeMode::MODE_ZSTD;
// NOTE: this should be the first check once we rebuilt function
// 14043F300 and alloc ring buffer for the flags individually instead
// instead of having to define the main compress flag (which really
// just means that the pak is using the RTech decoder)
else if (flags & PAK_HEADER_FLAGS_COMPRESSED)
return EPakDecodeMode::MODE_RTECH;
return EPakDecodeMode::MODE_DISABLED;
}
// file versions
@ -369,29 +535,45 @@ struct PakDecoder_t
uint64_t outputInvMask;
uint32_t headerOffset;
uint32_t padding; // unused data, available for other stuff
// this field was unused, it now contains the decoder mode
EPakDecodeMode decodeMode;
// NOTE: unless you are in the RTech decoder, use the getter if you need to
// get the current pos!!!
uint64_t inBufBytePos;
// NOTE: unless you are in the RTech decoder, use the getter if you need to
// get the current pos!!!
uint64_t outBufBytePos;
size_t bufferSizeNeeded;
// current byte and current bit of byte
uint64_t currentByte;
uint32_t currentBit;
union
{
// current byte and current bit of byte
uint64_t currentByte;
};
uint32_t currentBit;
uint32_t dword6C;
uint64_t qword70;
union
{
size_t compressedStreamSize;
// compressedStreamSize isn't used on ZStd paks, instead, we need to
// store the frame header size
size_t frameHeaderSize;
};
union
{
size_t decompressedStreamSize;
// decompressedStreamSize isn't used on ZStd paks; use this space for
// the decoder
ZSTD_DStream* zstreamContext;
};
};
@ -402,54 +584,6 @@ struct PakRingBufferFrame_t
size_t frameLen;
};
struct PakFile_t;
class PakLoadedInfo_t
{
public:
struct StreamingInfo_t
{
inline void Reset()
{
embeddedStarpakName = nullptr;
streamFileNumber[0] = FS_ASYNC_FILE_INVALID;
streamFileNumber[1] = FS_ASYNC_FILE_INVALID;
streamFileNumber[2] = FS_ASYNC_FILE_INVALID;
streamFileNumber[3] = FS_ASYNC_FILE_INVALID;
streamFileCount = NULL;
streamingDisabled = true;
}
char* embeddedStarpakName;
int streamFileNumber[PAK_MAX_STREAMING_FILE_HANDLES_PER_SET];
int streamFileCount;
bool streamingDisabled;
char padding_maybe[3];
};
PakHandle_t handle;
EPakStatus status;
uint64_t m_nUnk1;
// the log level of the pak, this is also used for errors if a pak failed
// to load; the higher the level, the more important this pak file is
int logLevel;
uint32_t assetCount;
const char* fileName;
CAlignedMemAlloc* allocator;
PakGuid_t* assetGuids; //size of the array is m_nAssetCount
void* segmentBuffers[PAK_SEGMENT_BUFFER_TYPES];
_QWORD qword50;
FILETIME fileTime;
PakFile_t* pakFile;
StreamingInfo_t streamInfo[STREAMING_SET_COUNT];
uint32_t fileHandle;
uint8_t m_nUnk5;
uint64_t hModule;
}; //Size: 0x00B8/0x00E8
struct PakFileStream_t
{
struct Descriptor
@ -460,20 +594,20 @@ struct PakFileStream_t
// NOTE: if this is set, the game sets 'PakMemoryData_t::processedPatchedDataSize'
// to 'dataOffset'; else its getting set to 'sizeof(PakFileHeader_t)'.
bool isCompressed;
EPakDecodeMode compressionMode;
};
_QWORD qword0;
_QWORD qword8;
_QWORD fileSize;
int fileHandle;
int gap14[32];
int asyncRequestHandles[32];
_BYTE gap94[32];
unsigned int unsigned_intB4;
_DWORD dwordB8;
_BYTE byteBC;
unsigned int numDataChunksProcessed;
_DWORD numDataChunks;
_BYTE fileReadStatus;
bool finishedLoadingPatches;
_BYTE gapBE;
_BYTE byteBF;
_BYTE numLoadedFiles;
Descriptor descriptors[PAK_MAX_ASYNC_STREAMED_LOAD_REQUESTS];
uint8_t* buffer;
_QWORD bufferMask;
@ -536,8 +670,8 @@ struct PakMemoryData_t
uint64_t qword2D0;
PakHandle_t pakId;
JobID_t unkJobID;
int* qword2E0;
JobID_t assetLoadJobId;
int* loadedAssetIndices;
uint8_t** memPageBuffers;
PakPatchFileHeader_t* patchHeaders;
@ -684,22 +818,13 @@ struct PakFile_t
}
};
struct UnknownPakStruct_t
{
uint32_t unk_0;
int unk_4;
int unk_8;
char gap_C[0x9D404];
int unk_array_9D410[512];
char gap_9DC04[0x7F800];
};
static_assert(sizeof(UnknownPakStruct_t) == 0x11D410);
static_assert(sizeof(PakTracker_s) == 0x11D410);
static_assert(sizeof(PakFile_t) == 2224); // S3+
static_assert(sizeof(PakLoadedInfo_t) == 184);
static_assert(sizeof(PakDecoder_t) == 136);
static_assert(sizeof(PakPatchFileHeader_t) == 16);
static_assert(sizeof(PakPatchDataHeader_t) == 8);
static_assert(sizeof(PakGlobals_s) == 0xC98A48);
#endif // RTECH_IPACKFILE_H

View File

@ -55,25 +55,64 @@ private:
SRWLOCK m_lock;
};
#pragma pack(push, 4)
class RBitRead
struct RMultiHashMap
{
public:
FORCEINLINE uint64_t ReadBits(const uint32_t numBits)
int globalIndex;
int firstBufSize;
void* firstBuffer;
void* secondBuffer;
int firstSlotsUsed;
int secondSlotsUsed;
int firstIndex;
int secondIndex;
int secondBufMask;
};
#pragma pack(push, 4)
struct RBitRead
{
unsigned __int64 m_dataBuf;
unsigned int m_bitsAvailable;
RBitRead() : m_dataBuf(0), m_bitsAvailable(64) {};
FORCEINLINE void ConsumeData(unsigned __int64 input, unsigned int numBits = 64)
{
Assert(numBits <= 64, "RBitRead::ReadBits: numBits must be less than or equal to 64.");
if (numBits > m_bitsAvailable)
{
assert(false && "RBitRead::ConsumeData: numBits must be less than or equal to m_bitsAvailable.");
return;
}
m_dataBuf |= input << (64 - numBits);
}
FORCEINLINE void ConsumeData(void* input, unsigned int numBits = 64)
{
if (numBits > m_bitsAvailable)
{
assert(false && "RBitRead::ConsumeData: numBits must be less than or equal to m_bitsAvailable.");
return;
}
m_dataBuf |= *reinterpret_cast<unsigned __int64*>(input) << (64 - numBits);
}
FORCEINLINE int BitsAvailable() const { return m_bitsAvailable; };
FORCEINLINE unsigned __int64 ReadBits(unsigned int numBits)
{
assert(numBits <= 64 && "RBitRead::ReadBits: numBits must be less than or equal to 64.");
return m_dataBuf & ((1ull << numBits) - 1);
}
FORCEINLINE void DiscardBits(const uint32_t numBits)
FORCEINLINE void DiscardBits(unsigned int numBits)
{
Assert(numBits <= 64, "RBitRead::DiscardBits: numBits must be less than or equal to 64.");
assert(numBits <= 64 && "RBitRead::DiscardBits: numBits must be less than or equal to 64.");
this->m_dataBuf >>= numBits;
this->m_bitsRemaining += numBits;
this->m_bitsAvailable += numBits;
}
uint64_t m_dataBuf;
int m_bitsRemaining;
};
#pragma pack(pop)

View File

@ -4,8 +4,13 @@
typedef uint32_t JobID_t;
typedef uint8_t JobTypeID_t;
typedef bool(*JobHelpCallback_t)(__int64, _DWORD*, __int64, _QWORD*);
struct JobFifoLock_s
{
int id;
int depth;
short tls[64];
};
struct JobContext_s
@ -35,13 +40,14 @@ extern JobID_t JTGuts_AddJob(JobTypeID_t jobTypeId, JobID_t jobId, void* callbac
inline void(*JT_ParallelCall)(void);
inline void*(*JT_HelpWithAnything)(bool bShouldLoadPak);
inline bool(*JT_HelpWithJobTypes)(__int64(__fastcall* callback)(__int64, _DWORD*, __int64, _QWORD*), JobFifoLock_s* pFifoLock, __int64 a3, __int64 a4);
inline __int64(*JT_HelpWithJobTypesOrSleep)(unsigned __int8(__fastcall* a1)(_QWORD), JobFifoLock_s* pFifoLock, __int64 a3, __int64 a4, volatile signed __int64* a5, char a6);
inline bool(*JT_HelpWithJobTypes)(JobHelpCallback_t, JobFifoLock_s* pFifoLock, __int64 a3, __int64 a4);
inline __int64(*JT_HelpWithJobTypesOrSleep)(JobHelpCallback_t, JobFifoLock_s* pFifoLock, __int64 a3, __int64 a4, volatile signed __int64* a5, char a6);
inline bool(*JT_AcquireFifoLockOrHelp)(struct JobFifoLock_s* pFifo);
inline void(*JT_ReleaseFifoLock)(struct JobFifoLock_s* pFifo);
inline void(*JT_EndJobGroup)(const JobID_t jobId);
inline unsigned int (*JT_AllocateJob)(); // Returns an index to the 'job_JT_Context' array
inline JobID_t(*JTGuts_AddJob_Internal)(JobTypeID_t jobTypeId, JobID_t jobId, void* callbackfunc, void* callbackArg, int jobIndex, JobContext_s* context);

View File

@ -53,7 +53,7 @@ extern void FS_CloseAsyncFile(const int fileHandle);
inline int(*v_FS_OpenAsyncFile)(const char* const filePath, const int logLevel, size_t* const outFileSize);
inline void(*v_FS_CloseAsyncFile)(const int fileHandle);
inline int(*v_FS_ReadAsyncFile)(int a1, __int64 a2, unsigned __int64 a3, void* a4, void* a5, void* a6, int a7);
inline int(*v_FS_ReadAsyncFile)(const int fileHandle, __int64 readOffset, unsigned __int64 readSize, void* a4, void* a5, void* a6, int a7);
inline uint8_t(*v_FS_CheckAsyncRequest)(AsyncHandleStatus_t* pakStatus, size_t* bytesProcessed, const char** stateString);
inline AsyncHandleTracker_t* g_pAsyncFileSlots; // bufSize=1024*sizeof(FileHandleTracker_t).

View File

@ -17,7 +17,7 @@ void Pak_AlignSegmentHeaders(PakFile_t* const pak, PakSegmentDescriptor_t* const
for (uint8_t i = 0; i < PAK_MAX_TYPES; ++i)
{
const PakAssetBinding_t& binding = g_pPakGlobals->assetBindings[i];
const PakAssetBinding_t& binding = g_pakGlobals->assetBindings[i];
if (desc->assetTypeCount[i])
{

View File

@ -6,9 +6,6 @@
#include "tier0/binstream.h"
#include "tier1/fmtstr.h"
#include "thirdparty/zstd/zstd.h"
#include "thirdparty/zstd/decompress/zstd_decompress_internal.h"
#include "rtech/ipakfile.h"
#include "paktools.h"
@ -140,6 +137,8 @@ static const unsigned char /*141313180*/ s_defaultDecoderLUT[] =
//-----------------------------------------------------------------------------
bool Pak_HasEnoughDecodeBufferAvailable(PakDecoder_t* const decoder, const size_t outLen)
{
// make sure caller has copied all data out the ring buffer first before
// overwriting it with new decoded data
const uint64_t bytesWritten = (decoder->outBufBytePos & ~decoder->outputInvMask);
return (outLen >= decoder->outputInvMask + (bytesWritten +1) || outLen >= decoder->decompSize);
}
@ -149,8 +148,11 @@ bool Pak_HasEnoughDecodeBufferAvailable(PakDecoder_t* const decoder, const size_
//-----------------------------------------------------------------------------
bool Pak_HasEnoughStreamedDataForDecode(PakDecoder_t* const decoder, const size_t inLen)
{
// the decoder needs at least this many bytes to decode the current block
// buffer contiguously
// the decoder needs at least this amount of input data streamed in order
// to decode the rest of the pak file, as this is where reading has stopped
// this value may equal the currently streamed input size, as its possible
// this function is getting called to flush the remainder decoded data into
// the out buffer which got truncated off on the call prior due to wrapping
return (inLen >= decoder->bufferSizeNeeded);
}
@ -176,7 +178,7 @@ PakRingBufferFrame_t Pak_DetermineRingBufferFrame(const uint64_t bufMask, const
//-----------------------------------------------------------------------------
// initializes the RTech decoder
//-----------------------------------------------------------------------------
size_t Pak_RStreamDecoderInit(PakDecoder_t* const decoder, const uint8_t* const fileBuffer,
size_t Pak_RTechDecoderInit(PakDecoder_t* const decoder, const uint8_t* const fileBuffer,
const uint64_t inputMask, const size_t dataSize, const size_t dataOffset, const size_t headerSize)
{
uint64_t frameHeader = *(_QWORD*)((inputMask & (dataOffset + headerSize)) + fileBuffer);
@ -241,7 +243,7 @@ size_t Pak_RStreamDecoderInit(PakDecoder_t* const decoder, const uint8_t* const
//-----------------------------------------------------------------------------
// decodes the RTech data stream up to available buffer or data
//-----------------------------------------------------------------------------
bool Pak_RStreamDecode(PakDecoder_t* const decoder, const size_t inLen, const size_t outLen)
bool Pak_RTechStreamDecode(PakDecoder_t* const decoder, const size_t inLen, const size_t outLen)
{
bool result; // al
uint64_t outBufBytePos; // r15
@ -568,15 +570,9 @@ LABEL_69:
//-----------------------------------------------------------------------------
// initializes the ZStd decoder
//-----------------------------------------------------------------------------
size_t Pak_ZStreamDecoderInit(PakDecoder_t* const decoder, const uint8_t* const fileBuffer,
const uint64_t inputMask, const size_t dataSize, const size_t dataOffset, const size_t headerSize)
size_t Pak_ZStdDecoderInit(PakDecoder_t* const decoder, const uint8_t* frameHeader,
const size_t dataSize, const size_t headerSize)
{
// NOTE: on original paks, this data is parsed out of the frame header,
// but for ZStd encoded paks we are always limiting this to the ring
// buffer size
decoder->inputInvMask = PAK_DECODE_OUT_RING_BUFFER_MASK;
decoder->outputInvMask = PAK_DECODE_OUT_RING_BUFFER_MASK;
ZSTD_DStream* const dctx = ZSTD_createDStream();
assert(dctx);
@ -586,16 +582,7 @@ size_t Pak_ZStreamDecoderInit(PakDecoder_t* const decoder, const uint8_t* const
decoder->zstreamContext = dctx;
// this points to the first byte of the frame header, takes dataOffset
// into account which is the offset in the ring buffer to the patched
// data as we parse it contiguously after the base pak data, which
// might have ended somewhere in the middle of the ring buffer
const uint8_t* const frameHeaderData = (inputMask & (dataOffset + headerSize)) + fileBuffer;
// this is the offset to the ZStd header in the input buffer
decoder->headerOffset = static_cast<uint32_t>(frameHeaderData - fileBuffer);
if (ZSTD_getFrameHeader(&dctx->fParams, frameHeaderData, dataSize) != 0)
if (ZSTD_getFrameHeader(&dctx->fParams, frameHeader, dataSize) != 0)
{
ZSTD_freeDStream(decoder->zstreamContext);
decoder->zstreamContext = nullptr;
@ -619,51 +606,46 @@ size_t Pak_ZStreamDecoderInit(PakDecoder_t* const decoder, const uint8_t* const
//-----------------------------------------------------------------------------
// decodes the ZStd data stream up to available buffer or data, whichever ends
// first (determined by Pak_DetermineRingBufferFrame())
// first
//-----------------------------------------------------------------------------
bool Pak_ZStreamDecode(PakDecoder_t* const decoder, const size_t inLen, const size_t outLen)
bool Pak_ZStdStreamDecode(PakDecoder_t* const decoder, const PakRingBufferFrame_t& outFrame, const PakRingBufferFrame_t& inFrame)
{
// must have a ZStd decoder at this point, and input seek pos may not exceed
// inLen as we can't read past currently streamed data; this should've been
// checked before calling this function
assert(decoder->zstreamContext && decoder->inBufBytePos <= inLen);
const PakRingBufferFrame_t outFrame = Pak_DetermineRingBufferFrame(decoder->outputMask, decoder->outBufBytePos, outLen);
ZSTD_outBuffer outBuffer = {
&decoder->outputBuf[outFrame.bufIndex],
outFrame.frameLen,
NULL
outFrame.frameLen, NULL
};
const PakRingBufferFrame_t inFrame = Pak_DetermineRingBufferFrame(decoder->inputMask, decoder->inBufBytePos, inLen);
ZSTD_inBuffer inBuffer = {
&decoder->inputBuf[inFrame.bufIndex],
inFrame.frameLen,
NULL
inFrame.frameLen, NULL
};
const size_t ret = ZSTD_decompressStream(decoder->zstreamContext, &outBuffer, &inBuffer);
if (ZSTD_isError(ret))
{
DevWarning(eDLL_T::RTECH, "%s: decode error: %s\n", __FUNCTION__, ZSTD_getErrorName(ret));
// NOTE: obtained here and not in the error formatter as we could check
// the error string during the assertion
const char* const decodeError = ZSTD_getErrorName(ret);
assert(0);
Error(eDLL_T::RTECH, EXIT_FAILURE, "%s: decode error: %s\n", __FUNCTION__, decodeError);
return false;
}
// advance buffer io positions, required so the main parser could already
// start parsing the headers while the rest is getting decoded still
decoder->inBufBytePos += inBuffer.pos;
decoder->outBufBytePos += outBuffer.pos;
decoder->inBufBytePos += inBuffer.pos;
// on the next call, we need at least this amount of data streamed in order
// to decode the rest of the pak file, as this is where reading has stopped
// this value may equal the currently streamed input size, as its possible
// this function is getting called to flush the remainder decoded data into
// the out buffer which got truncated off on the call prior due to wrapping
//
// if the input stream has fully decoded, this should equal the size of the
// encoded pak file
decoder->bufferSizeNeeded = decoder->inBufBytePos;
const bool decoded = ret == NULL;
@ -683,10 +665,10 @@ bool Pak_ZStreamDecode(PakDecoder_t* const decoder, const size_t inLen, const si
//-----------------------------------------------------------------------------
size_t Pak_InitDecoder(PakDecoder_t* const decoder, const uint8_t* const inputBuf, uint8_t* const outputBuf,
const uint64_t inputMask, const uint64_t outputMask, const size_t dataSize, const size_t dataOffset,
const size_t headerSize, const bool useZStream)
const size_t headerSize, const EPakDecodeMode decodeMode)
{
// buffer size must be power of two as we index into buffers using a bit
// mask rather than modulo, the mask provided must be bufferSize-1
// mask rather than a modulo, the mask provided must be bufferSize-1
assert(IsPowerOfTwo(inputMask + 1));
assert(IsPowerOfTwo(outputMask + 1));
@ -697,7 +679,7 @@ size_t Pak_InitDecoder(PakDecoder_t* const decoder, const uint8_t* const inputBu
// the actual file size, which consists of dataOffset (anything up to the
// frame header, like the file header) and the actual encoded data itself
decoder->fileSize = dataOffset + dataSize;
decoder->padding = NULL;
decoder->decodeMode = decodeMode;
// buffer masks, which essentially gets used to index into the input and
// output buffers, similar to 'idx % bufSize', where bufSize = bufMask+1
@ -712,10 +694,26 @@ size_t Pak_InitDecoder(PakDecoder_t* const decoder, const uint8_t* const inputBu
decoder->inBufBytePos = dataOffset + headerSize;
decoder->outBufBytePos = headerSize;
if (useZStream)
return Pak_ZStreamDecoderInit(decoder, inputBuf, inputMask, dataSize, dataOffset, headerSize);
// if we use the default RTech decoder, return from here as the stuff below
// is handled by the RTech decoder internally
if (decodeMode == EPakDecodeMode::MODE_RTECH)
return Pak_RTechDecoderInit(decoder, inputBuf, inputMask, dataSize, dataOffset, headerSize);
return Pak_RStreamDecoderInit(decoder, inputBuf, inputMask, dataSize, dataOffset, headerSize);
// NOTE: on RTech encoded paks this data is parsed out of the frame header,
// but for ZStd encoded paks we are always limiting this to the ring buffer
// size
decoder->outputInvMask = PAK_DECODE_OUT_RING_BUFFER_MASK;
// this points to the first byte of the frame header, takes dataOffset
// into account which is the offset in the ring buffer to the patched
// data as we parse it contiguously after the base pak data, which
// might have ended somewhere in the middle of the ring buffer
const uint8_t* const frameHeaderData = &inputBuf[inputMask & (dataOffset + headerSize)];
const size_t decodeSize = Pak_ZStdDecoderInit(decoder, frameHeaderData, dataSize, headerSize);
assert(decodeSize);
return decodeSize;
}
//-----------------------------------------------------------------------------
@ -723,7 +721,7 @@ size_t Pak_InitDecoder(PakDecoder_t* const decoder, const uint8_t* const inputBu
// from where the base pak had ended as patch pak files are considered part of
// the pak file that's currently getting loaded
//-----------------------------------------------------------------------------
bool Pak_StreamToBufferDecode(PakDecoder_t* const decoder, const size_t inLen, const size_t outLen, const bool useZStream)
bool Pak_StreamToBufferDecode(PakDecoder_t* const decoder, const size_t inLen, const size_t outLen, const EPakDecodeMode decodeMode)
{
if (!Pak_HasEnoughStreamedDataForDecode(decoder, inLen))
return false;
@ -731,22 +729,33 @@ bool Pak_StreamToBufferDecode(PakDecoder_t* const decoder, const size_t inLen, c
if (!Pak_HasEnoughDecodeBufferAvailable(decoder, outLen))
return false;
if (useZStream)
return Pak_ZStreamDecode(decoder, inLen, outLen);
if (decodeMode == EPakDecodeMode::MODE_RTECH)
return Pak_RTechStreamDecode(decoder, inLen, outLen);
return Pak_RStreamDecode(decoder, inLen, outLen);
// must have a decoder at this point
//
// also, input seek pos may not exceed inLen as we can't read past
// currently streamed data; this should've been checked before reaching
// this position in code
assert(decoder->zstreamContext && decoder->inBufBytePos <= inLen);
const PakRingBufferFrame_t outFrame = Pak_DetermineRingBufferFrame(decoder->outputMask, decoder->outBufBytePos , outLen);
const PakRingBufferFrame_t inFrame = Pak_DetermineRingBufferFrame(decoder->inputMask, decoder->inBufBytePos, inLen);
return Pak_ZStdStreamDecode(decoder, outFrame, inFrame);
}
//-----------------------------------------------------------------------------
// decodes buffered input pak data
//-----------------------------------------------------------------------------
bool Pak_BufferToBufferDecode(uint8_t* const inBuf, uint8_t* const outBuf, const size_t pakSize)
bool Pak_BufferToBufferDecode(uint8_t* const inBuf, uint8_t* const outBuf, const size_t pakSize, const EPakDecodeMode decodeMode)
{
PakFileHeader_t* const inHeader = reinterpret_cast<PakFileHeader_t*>(inBuf);
const bool usesZStream = inHeader->flags & PAK_HEADER_FLAGS_ZSTREAM;
assert(decodeMode != EPakDecodeMode::MODE_DISABLED);
PakDecoder_t decoder{};
const size_t decompressedSize = Pak_InitDecoder(&decoder, inBuf, outBuf, UINT64_MAX, UINT64_MAX, pakSize, NULL, sizeof(PakFileHeader_t), usesZStream);
const size_t decompressedSize = Pak_InitDecoder(&decoder, inBuf, outBuf, UINT64_MAX, UINT64_MAX, pakSize, NULL, sizeof(PakFileHeader_t), decodeMode);
PakFileHeader_t* const inHeader = reinterpret_cast<PakFileHeader_t*>(inBuf);
if (decompressedSize != inHeader->decompressedSize)
{
@ -757,7 +766,7 @@ bool Pak_BufferToBufferDecode(uint8_t* const inBuf, uint8_t* const outBuf, const
}
// we should always have enough buffer room at this point
if (!Pak_StreamToBufferDecode(&decoder, inHeader->compressedSize, inHeader->decompressedSize, usesZStream))
if (!Pak_StreamToBufferDecode(&decoder, inHeader->compressedSize, inHeader->decompressedSize, decodeMode))
{
Error(eDLL_T::RTECH, NO_ERROR, "%s: decompression failed!\n",
__FUNCTION__);
@ -772,7 +781,7 @@ bool Pak_BufferToBufferDecode(uint8_t* const inBuf, uint8_t* const outBuf, const
// remove compress flags
outHeader->flags &= ~PAK_HEADER_FLAGS_COMPRESSED;
outHeader->flags &= ~PAK_HEADER_FLAGS_ZSTREAM;
outHeader->flags &= ~PAK_HEADER_FLAGS_ZSTREAM_ENCODED;
// equal compressed size with decompressed
outHeader->compressedSize = outHeader->decompressedSize;
@ -841,7 +850,9 @@ bool Pak_DecodePakFile(const char* const inPakFile, const char* const outPakFile
return false;
}
if (!inHeader->IsCompressed())
const EPakDecodeMode decodeMode = inHeader->GetCompressionMode();
if (decodeMode == EPakDecodeMode::MODE_DISABLED)
{
Error(eDLL_T::RTECH, NO_ERROR, "%s: pak '%s' is already decompressed!\n",
__FUNCTION__, inPakFile);
@ -862,7 +873,7 @@ bool Pak_DecodePakFile(const char* const inPakFile, const char* const outPakFile
std::unique_ptr<uint8_t[]> outPakBufContainer(new uint8_t[inHeader->decompressedSize]);
uint8_t* const outPakBuf = outPakBufContainer.get();
if (!Pak_BufferToBufferDecode(inPakBuf, outPakBuf, fileSize))
if (!Pak_BufferToBufferDecode(inPakBuf, outPakBuf, fileSize, decodeMode))
{
Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to decompress pak file '%s'!\n",
__FUNCTION__, inPakFile);

View File

@ -4,10 +4,10 @@
extern size_t Pak_InitDecoder(PakDecoder_t* const decoder, const uint8_t* const inputBuf, uint8_t* const outputBuf,
const uint64_t inputMask, const uint64_t outputMask, const size_t dataSize, const size_t dataOffset,
const size_t headerSize, const bool useZStream);
const size_t headerSize, const EPakDecodeMode decodeMode);
extern bool Pak_StreamToBufferDecode(PakDecoder_t* const decoder, const size_t inLen, const size_t outLen, const bool useCustom);
extern bool Pak_BufferToBufferDecode(uint8_t* const inBuf, uint8_t* const outBuf, const size_t pakSize);
extern bool Pak_StreamToBufferDecode(PakDecoder_t* const decoder, const size_t inLen, const size_t outLen, const EPakDecodeMode decodeMode);
extern bool Pak_BufferToBufferDecode(uint8_t* const inBuf, uint8_t* const outBuf, const size_t pakSize, const EPakDecodeMode decodeMode);
extern bool Pak_DecodePakFile(const char* const inPakFile, const char* const outPakFile);

View File

@ -4,11 +4,26 @@
//
//=============================================================================//
#include "tier0/binstream.h"
#include "thirdparty/zstd/zstd.h"
#include "rtech/ipakfile.h"
#include "paktools.h"
#include "pakencode.h"
//-----------------------------------------------------------------------------
// determines whether encoding had failed
//-----------------------------------------------------------------------------
static bool Pak_HasEncodeFailed(const size_t result)
{
return static_cast<bool>(ZSTD_isError(result));
}
//-----------------------------------------------------------------------------
// returns the encode error as string
//-----------------------------------------------------------------------------
static const char* Pak_GetEncodeError(const size_t result)
{
return ZSTD_getErrorName(result);
}
//-----------------------------------------------------------------------------
// encodes the pak file from buffer, we can't do streamed compression as we
// need to know the actual decompress size ahead of time, else the runtime will
@ -22,17 +37,20 @@ bool Pak_BufferToBufferEncode(const uint8_t* const inBuf, const uint64_t inLen,
// compressed
const size_t dataOffset = sizeof(PakFileHeader_t);
const size_t compressSize = ZSTD_compress(
outBuf + dataOffset,
outLen - dataOffset,
inBuf + dataOffset,
inLen - dataOffset,
level);
uint8_t* const dstBuf = outBuf + dataOffset;
const size_t dstLen = outLen - dataOffset;
if (ZSTD_isError(compressSize))
const uint8_t* const srcBuf = inBuf + dataOffset;
const size_t srcLen = inLen - dataOffset;
size_t compressSize = NULL;
compressSize = ZSTD_compress(dstBuf, dstLen, srcBuf, srcLen, level);
if (Pak_HasEncodeFailed(compressSize))
{
Error(eDLL_T::RTECH, NO_ERROR, "%s: compression failed! [%s]\n",
__FUNCTION__, ZSTD_getErrorName(compressSize));
__FUNCTION__, Pak_GetEncodeError(compressSize));
return false;
}
@ -46,7 +64,7 @@ bool Pak_BufferToBufferEncode(const uint8_t* const inBuf, const uint64_t inLen,
// these flags are required for the game's runtime to decide whether or not to
// decompress the pak, and how; see Pak_ProcessPakFile() for more details
outHeader->flags |= PAK_HEADER_FLAGS_COMPRESSED;
outHeader->flags |= PAK_HEADER_FLAGS_ZSTREAM;
outHeader->flags |= PAK_HEADER_FLAGS_ZSTREAM_ENCODED;
return true;
}
@ -111,7 +129,7 @@ bool Pak_EncodePakFile(const char* const inPakFile, const char* const outPakFile
return false;
}
if (inHeader->IsCompressed())
if (inHeader->GetCompressionMode() != EPakDecodeMode::MODE_DISABLED)
{
Error(eDLL_T::RTECH, NO_ERROR, "%s: pak '%s' is already compressed!\n",
__FUNCTION__, inPakFile);
@ -135,7 +153,9 @@ bool Pak_EncodePakFile(const char* const inPakFile, const char* const outPakFile
__FUNCTION__, inPakFile);
}
std::unique_ptr<uint8_t[]> outPakBufContainer(new uint8_t[inHeader->decompressedSize]);
const size_t outBufSize = inHeader->decompressedSize;
std::unique_ptr<uint8_t[]> outPakBufContainer(new uint8_t[outBufSize]);
uint8_t* const outPakBuf = outPakBufContainer.get();
PakFileHeader_t* const outHeader = reinterpret_cast<PakFileHeader_t* const>(outPakBuf);
@ -144,7 +164,7 @@ bool Pak_EncodePakFile(const char* const inPakFile, const char* const outPakFile
*outHeader = *inHeader;
// encoding failed
if (!Pak_BufferToBufferEncode(inPakBuf, inHeader->compressedSize, outPakBuf, inHeader->decompressedSize, level))
if (!Pak_BufferToBufferEncode(inPakBuf, fileSize, outPakBuf, outBufSize, level))
{
Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to compress pak file '%s'!\n",
__FUNCTION__, inPakFile);

View File

@ -1,5 +1,6 @@
#ifndef RTECH_PAKENCODE_H
#define RTECH_PAKENCODE_H
#include "rtech/ipakfile.h"
bool Pak_BufferToBufferEncode(const uint8_t* const inBuf, const uint64_t inLen,
uint8_t* const outBuf, const uint64_t outLen, const int level);

File diff suppressed because it is too large Load Diff

View File

@ -20,19 +20,14 @@ inline bool(*v_Pak_ProcessPakFile)(PakFile_t* const pak);
inline bool(*v_Pak_ProcessAssets)(PakLoadedInfo_t* pakInfo);
inline void(*v_Pak_ResolveAssetRelations)(PakFile_t* const pak, const PakAsset_t* const asset);
inline void (*v_Pak_RunAssetLoadingJobs)(PakFile_t* pak);
inline void (*Pak_ProcessAssetRelationsAndResolveDependencies)(PakFile_t* pak_arg, PakAsset_t* asset_arg, unsigned int asset_idx_arg, unsigned int a4);
inline int (*Pak_TrackAsset)(PakFile_t* const a1, PakAsset_t* a2);
// TODO: name these!
inline void (*sub_14043E030)(PakFile_t* pak);
inline __int64 (*sub_14043D3C0)(PakFile_t* a1, PakAsset_t* a2);
inline void (*sub_14043D150)(PakFile_t* pak_arg, PakAsset_t* asset_arg, unsigned int asset_idx_arg, unsigned int a4);
inline void (*sub_14045B310)(unsigned int a1, __int64 a2);
inline void (*sub_14043D870)(PakLoadedInfo_t* a1, int a2);
inline short* word_167ED7BDE = nullptr;
// potentially PakAssetShort_t
inline int* dword_167A40B3C = nullptr;
inline UnknownPakStruct_t** qword_167ED7BC8 = nullptr; // ptr to buffer with size 0x11D410
typedef struct PakLoadFuncs_s
{
void* Initialize; // Returns the pak handle of the patch master RPak once initialized.
@ -66,8 +61,8 @@ typedef struct PakLoadFuncs_s
void* Func25;
void* ReadAsyncFile;
void* ReadAsyncFileWithUserData;
uint8_t (*CheckAsyncRequest)(unsigned char idx, size_t* const bytesProcessed, const char** const statusMsg);
uint8_t (*WaitAndCheckAsyncRequest)(unsigned char idx, size_t* const bytesProcessed, const char** const statusMsg);
uint8_t (*CheckAsyncRequest)(int idx, size_t* const bytesProcessed, const char** const statusMsg);
uint8_t (*WaitAndCheckAsyncRequest)(int idx, size_t* const bytesProcessed, const char** const statusMsg);
void* WaitForAsyncFileRead;
void* Func31;
void* Func32;
@ -95,7 +90,6 @@ class V_PakParse : public IDetour
LogFunAdr("Pak_ProcessAssets", v_Pak_ProcessAssets);
LogFunAdr("Pak_ResolveAssetRelations", v_Pak_ResolveAssetRelations);
LogVarAdr("word_167ED7BDE", word_167ED7BDE);
LogVarAdr("g_pakLoadApi", g_pakLoadApi);
}
virtual void GetFun(void) const
@ -111,22 +105,15 @@ class V_PakParse : public IDetour
g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 84 C0 0F 84 ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 44 0F B7 05 ?? ?? ?? ??").FollowNearCallSelf().GetPtr(v_Pak_ProcessAssets);
g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 48 8B 86 ?? ?? ?? ?? 42 8B 0C B0").FollowNearCallSelf().GetPtr(v_Pak_ResolveAssetRelations);
g_GameDll.FindPatternSIMD("40 53 56 48 83 EC 58 44 8B 09").GetPtr(sub_14043E030);
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 83 EC 20 44 8B 0D ?? ?? ?? ?? 4C 8B E9").GetPtr(sub_14043D3C0);
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 41 8B E9").GetPtr(sub_14043D150);
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 83 7A 14 01").GetPtr(sub_14045B310);
g_GameDll.FindPatternSIMD("40 53 56 48 83 EC 58 44 8B 09").GetPtr(v_Pak_RunAssetLoadingJobs);
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 83 EC 20 44 8B 0D ?? ?? ?? ?? 4C 8B E9").GetPtr(Pak_TrackAsset);
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 41 8B E9").GetPtr(Pak_ProcessAssetRelationsAndResolveDependencies);
g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? EB 14 48 8D 0D ?? ?? ?? ??").FollowNearCallSelf().GetPtr(sub_14043D870);
}
virtual void GetVar(void) const
{
g_pakLoadApi = CMemory(v_LauncherMain).Offset(0x820).FindPatternSelf("48 89").ResolveRelativeAddressSelf(0x3, 0x7).RCast<PakLoadFuncs_t*>();
CMemory(sub_14043E030).FindPatternSelf("66 44 39").ResolveRelativeAddressSelf(0x4, 0x8).GetPtr(word_167ED7BDE);
int* va_dword_167A40B3C = CMemory(v_Pak_ProcessAssets).Offset(0x200).FindPatternSelf("44 39 BC").Offset(4).RCast<int*>();
dword_167A40B3C = reinterpret_cast<int*>(g_GameDll.GetModuleBase() + *va_dword_167A40B3C);
CMemory(v_Pak_ProcessAssets).Offset(0x200).FindPatternSelf("48 8B 15").ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(qword_167ED7BC8);
}
virtual void GetCon(void) const { }
virtual void Detour(const bool bAttach) const;

View File

@ -6,63 +6,34 @@
#include "rtech/ipakfile.h"
inline PakGlobals_t* g_pPakGlobals;
inline PakLoadedInfo_t* g_pLoadedPakInfo;
inline PakGlobals_s* g_pakGlobals;
inline JobHelpCallback_t g_pPakFifoLockWrapper; // Pointer to functor that takes the global pak fifolock as argument.
inline JobID_t* g_pPakLoadJobID;
inline int16_t* g_pLoadedPakCount;
inline int16_t* g_pRequestedPakCount;
inline JobFifoLock_s* g_pPakFifoLock;
inline void* g_pPakFifoLockWrapper; // Pointer to functor that takes the global pak fifolock as argument.
// TODO: rename to 'g_bPakFifoLockAcquiredInMainThread'
// if this is set, JT_ReleaseFifoLock has to be called
// twice as the depth goes up to the thread that
// acquired the lock + the main thread
inline bool* g_bPakFifoLockAcquired;
// bool as int64
inline int64_t* g_pPakHasPendingUnloadJobs;
///////////////////////////////////////////////////////////////////////////////
class V_PakState : public IDetour
{
virtual void GetAdr(void) const
{
LogVarAdr("g_pakGlobals", g_pPakGlobals);
LogVarAdr("g_loadedPakInfo", g_pLoadedPakInfo);
LogVarAdr("g_pakGlobals", g_pakGlobals);
LogVarAdr("g_pakLoadJobID", g_pPakLoadJobID);
LogVarAdr("g_loadedPakCount", g_pLoadedPakCount);
LogVarAdr("g_requestedPakCount", g_pRequestedPakCount);
LogVarAdr("g_pakFifoLock", g_pPakFifoLock);
LogVarAdr("g_pakFifoLockWrapper", g_pPakFifoLockWrapper);
LogVarAdr("g_pakFifoLockAcquired", g_bPakFifoLockAcquired);
LogVarAdr("g_pakHasPendingUnloadJobs", g_pPakHasPendingUnloadJobs);
}
virtual void GetFun(void) const { }
virtual void GetVar(void) const
{
extern void(*v_Pak_UnloadAsync)(PakHandle_t);
const CMemory pakUnloadBase(v_Pak_UnloadAsync);
g_pLoadedPakInfo = pakUnloadBase.FindPattern("48 8D 05", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast<PakLoadedInfo_t*>();
g_pRequestedPakCount = pakUnloadBase.FindPattern("66 89", CMemory::Direction::DOWN, 450).ResolveRelativeAddressSelf(0x3, 0x7).RCast<int16_t*>();
g_pLoadedPakCount = &*g_pRequestedPakCount - 1; // '-1' shifts it back with sizeof(int16_t).
g_pPakGlobals = g_GameDll.FindPatternSIMD("48 8D 1D ?? ?? ?? ?? 45 8D 5A 0E").ResolveRelativeAddressSelf(0x3, 0x7).RCast<PakGlobals_t*>(); /*48 8D 1D ? ? ? ? 45 8D 5A 0E*/
g_pPakLoadJobID = reinterpret_cast<JobID_t*>(&*g_pLoadedPakCount - 2);
g_pakGlobals = g_GameDll.FindPatternSIMD("48 8D 1D ?? ?? ?? ?? 45 8D 5A 0E").ResolveRelativeAddressSelf(0x3, 0x7).RCast<PakGlobals_s*>(); /*48 8D 1D ? ? ? ? 45 8D 5A 0E*/
const CMemory jtBase(JT_HelpWithAnything);
g_pPakFifoLock = jtBase.Offset(0x155).FindPatternSelf("48 8D 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast<JobFifoLock_s*>();
g_pPakFifoLockWrapper = jtBase.Offset(0x1BC).FindPatternSelf("48 8D 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast<void*>();
g_pPakFifoLockWrapper = jtBase.Offset(0x1BC).FindPatternSelf("48 8D 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast<JobHelpCallback_t>();
g_bPakFifoLockAcquired = jtBase.Offset(0x50).FindPatternSelf("C6 05").ResolveRelativeAddressSelf(0x2, 0x7).RCast<bool*>();
extern EPakStatus(*v_Pak_WaitAsync)(PakHandle_t, void*);
const CMemory pakWaitBase(v_Pak_WaitAsync);
pakWaitBase.Offset(0x80).FindPatternSelf("48 39").ResolveRelativeAddressSelf(3, 7).GetPtr(g_pPakHasPendingUnloadJobs);
}
virtual void GetCon(void) const { }
virtual void Detour(const bool bAttach) const { };

View File

@ -6,8 +6,10 @@
#include "tier0/commandline.h"
#include "rtech/async/asyncio.h"
#include "rtech/ipakfile.h"
#include "pakstream.h"
#include "pakparse.h"
#include "pakstate.h"
#include "pakstream.h"
//-----------------------------------------------------------------------------
// determines whether or not to emulate the streaming install, this basically
@ -16,23 +18,20 @@
//-----------------------------------------------------------------------------
static bool Pak_ShouldEmulateStreamingInstall()
{
static bool initialized = false;
static bool shouldEmulate = false;
// don't run the command line check every query
if (initialized)
return shouldEmulate;
if (g_pakGlobals->emulateStreamingInstallInit)
return g_pakGlobals->emulateStreamingInstall;
const char* value = nullptr;
if (CommandLine()->CheckParm("-emulate_streaming_install", &value))
{
if (value && atoi(value))
shouldEmulate = true;
g_pakGlobals->emulateStreamingInstall = true;
}
initialized = true;
return shouldEmulate;
g_pakGlobals->emulateStreamingInstallInit = true;
return g_pakGlobals->emulateStreamingInstall;
}
//-----------------------------------------------------------------------------
@ -95,7 +94,7 @@ void Pak_EnableEmbeddedStreamingData(PakLoadedInfo_t* const loadedInfo, PakLoade
const size_t basePathLen = hasPath ? 0 : strlen(PAK_BASE_PATH);
const size_t totalBufLen = basePathLen + baseNameLen + 1;
char* const embeddedName = reinterpret_cast<char* const>(loadedInfo->allocator->Alloc(totalBufLen, sizeof(char)));
char* const embeddedName = reinterpret_cast<char* const>(loadedInfo->allocator->Alloc(totalBufLen, 1));
assert(embeddedName);
// copy the base path if none was found in the file name

View File

@ -1,6 +1,7 @@
#ifndef RTECH_PAKSTREAM_H
#define RTECH_PAKSTREAM_H
#include "rtech/ipakfile.h"
#include "pakstate.h"
extern void Pak_OpenAssociatedStreamingFiles(PakLoadedInfo_t* const loadedInfo, PakLoadedInfo_t::StreamingInfo_t& streamInfo,
const uint16_t fileNamesBufSize, const EPakStreamSet set);
@ -8,46 +9,31 @@ extern void Pak_OpenAssociatedStreamingFiles(PakLoadedInfo_t* const loadedInfo,
extern void Pak_EnableEmbeddedStreamingData(PakLoadedInfo_t* const loadedInfo, PakLoadedInfo_t::StreamingInfo_t& streamInfo);
extern void Pak_LoadStreamingData(PakLoadedInfo_t* const loadedInfo);
// bool set as int64.
inline int64_t* g_pUseAssetStreamingSystem = nullptr;
inline int64_t* g_pNumStreamableAssets = nullptr;
// the current download progress of optional streaming assets
inline float* g_pStreamingDownloadProgress = nullptr;
// inlines
inline void(*v_Pak_IncrementStreamingAssetCount)(void);
inline void(*v_Pak_DecrementStreamingAssetCount)(void);
// NOTE: must use these when incrementing asset counts !!!
inline void Pak_IncrementStreamingAssetCount() { ThreadInterlockedIncrement64(&g_pakGlobals->numStreamableAssets); }
inline void Pak_DecrementStreamingAssetCount() { ThreadInterlockedDecrement64(&g_pakGlobals->numStreamableAssets); }
inline bool Pak_StreamingEnabled() { return *g_pUseAssetStreamingSystem != NULL; }
inline int64_t Pak_GetNumStreamableAssets() { return *g_pNumStreamableAssets; }
inline int64_t Pak_GetNumStreamableAssets() { return g_pakGlobals->numStreamableAssets; }
inline float Pak_GetStreamingDownloadProgress() { return *g_pStreamingDownloadProgress; }
inline bool Pak_StreamingDownloadFinished() { return Pak_GetStreamingDownloadProgress() == 1.0f; }
inline bool Pak_StreamingEnabled() { return g_pakGlobals->useStreamingSystem != NULL; }
class V_PakStream : public IDetour
{
virtual void GetAdr(void) const
{
LogFunAdr("Pak_IncrementStreamingAssetCount", v_Pak_IncrementStreamingAssetCount);
LogFunAdr("Pak_DecrementStreamingAssetCount", v_Pak_DecrementStreamingAssetCount);
LogVarAdr("g_useAssetStreamingSystem", g_pUseAssetStreamingSystem);
LogVarAdr("g_numStreamableAssets", g_pNumStreamableAssets);
LogVarAdr("g_streamingDownloadProgress", g_pStreamingDownloadProgress);
}
virtual void GetFun(void) const
{
g_GameDll.FindPatternSIMD("F0 48 FF 05 ?? ?? ?? ?? C3").GetPtr(v_Pak_IncrementStreamingAssetCount);
g_GameDll.FindPatternSIMD("F0 48 FF 0D ?? ?? ?? ?? C3").GetPtr(v_Pak_DecrementStreamingAssetCount);
}
virtual void GetFun(void) const { }
virtual void GetVar(void) const
{
extern PakHandle_t(*v_Pak_Initialize)(int mode);
CMemory(v_Pak_Initialize).Offset(0x120).FindPatternSelf("48 89 05").ResolveRelativeAddressSelf(3, 7).GetPtr(g_pUseAssetStreamingSystem);
g_GameDll.FindPatternSIMD("F3 0F 10 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC 48 89 5C 24 ?? 57 48 81 EC ?? ?? ?? ??")
.ResolveRelativeAddress(0x4, 0x8).GetPtr(g_pStreamingDownloadProgress);
CMemory(v_Pak_IncrementStreamingAssetCount).ResolveRelativeAddress(4, 8).GetPtr(g_pNumStreamableAssets); // 167ED7BB8
}
virtual void GetCon(void) const { }
virtual void Detour(const bool bAttach) const { };

View File

@ -101,6 +101,20 @@ const char* Pak_StatusToString(const EPakStatus status)
}
}
//-----------------------------------------------------------------------------
// returns pak decoder as string
//-----------------------------------------------------------------------------
const char* Pak_DecoderToString(const EPakDecodeMode mode)
{
switch (mode)
{
case EPakDecodeMode::MODE_RTECH: return "RTech";
case EPakDecodeMode::MODE_ZSTD: return "ZStd";
}
UNREACHABLE();
}
//-----------------------------------------------------------------------------
// compute a guid from input string data
//-----------------------------------------------------------------------------
@ -146,22 +160,9 @@ PakGuid_t Pak_StringToGuid(const char* const string)
//-----------------------------------------------------------------------------
// gets information about loaded pak file via pak id
//-----------------------------------------------------------------------------
const PakLoadedInfo_t* Pak_GetPakInfo(const PakHandle_t pakId)
PakLoadedInfo_t* Pak_GetPakInfo(const PakHandle_t pakId)
{
for (int16_t i = 0; i < *g_pLoadedPakCount; ++i)
{
const PakLoadedInfo_t* const info = &g_pLoadedPakInfo[i];
if (!info)
continue;
if (info->handle != pakId)
continue;
return info;
}
Warning(eDLL_T::RTECH, "%s - Failed to retrieve pak info for handle '%d'\n", __FUNCTION__, pakId);
return nullptr;
return &g_pakGlobals->loadedPaks[pakId & PAK_MAX_HANDLES_MASK];
}
//-----------------------------------------------------------------------------
@ -169,9 +170,9 @@ const PakLoadedInfo_t* Pak_GetPakInfo(const PakHandle_t pakId)
//-----------------------------------------------------------------------------
const PakLoadedInfo_t* Pak_GetPakInfo(const char* const pakName)
{
for (int16_t i = 0; i < *g_pLoadedPakCount; ++i)
for (int16_t i = 0; i < g_pakGlobals->loadedPakCount; ++i)
{
const PakLoadedInfo_t* const info = &g_pLoadedPakInfo[i];
const PakLoadedInfo_t* const info = &g_pakGlobals->loadedPaks[i];
if (!info)
continue;

View File

@ -13,9 +13,11 @@ extern bool Pak_FileOverrideExists(const char* const pakFilePath, char* const ou
extern int Pak_FileExists(const char* const pakFilePath);
extern const char* Pak_StatusToString(const EPakStatus status);
const char* Pak_DecoderToString(const EPakDecodeMode mode);
extern PakGuid_t Pak_StringToGuid(const char* const string);
extern const PakLoadedInfo_t* Pak_GetPakInfo(const PakHandle_t pakId);
extern PakLoadedInfo_t* Pak_GetPakInfo(const PakHandle_t pakId);
extern const PakLoadedInfo_t* Pak_GetPakInfo(const char* const pakName);
extern PakPatchDataHeader_t* Pak_GetPatchDataHeader(PakFileHeader_t* const pakHeader);