mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
1310 lines
49 KiB
C++
1310 lines
49 KiB
C++
//=============================================================================//
|
|
//
|
|
// Purpose: pak file loading and unloading
|
|
//
|
|
//=============================================================================//
|
|
#include "rtech/ipakfile.h"
|
|
#include "rtech/async/asyncio.h"
|
|
|
|
#include "paktools.h"
|
|
#include "pakstate.h"
|
|
#include "pakpatch.h"
|
|
#include "pakalloc.h"
|
|
#include "pakparse.h"
|
|
#include "pakdecode.h"
|
|
#include "pakstream.h"
|
|
|
|
static ConVar pak_debugrelations("pak_debugrelations", "0", FCVAR_DEVELOPMENTONLY | FCVAR_ACCESSIBLE_FROM_THREADS, "Debug RPAK asset dependency resolving");
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// resolve the target guid from lookuo table
|
|
//-----------------------------------------------------------------------------
|
|
static bool Pak_ResolveAssetDependency(const PakFile_s* const pak, PakGuid_t currentGuid,
|
|
const PakGuid_t targetGuid, int& currentIndex, const bool shouldCheckTwo)
|
|
{
|
|
while (true)
|
|
{
|
|
if (shouldCheckTwo && currentGuid == 2)
|
|
{
|
|
if (pak->memoryData.pakHeader.assetCount)
|
|
return false;
|
|
}
|
|
|
|
currentIndex++;
|
|
|
|
if (currentIndex >= PAK_MAX_LOADED_ASSETS)
|
|
return false;
|
|
|
|
currentIndex &= PAK_MAX_LOADED_ASSETS_MASK;
|
|
currentGuid = g_pakGlobals->loadedAssets[currentIndex].guid;
|
|
|
|
if (currentGuid == targetGuid)
|
|
return true;
|
|
}
|
|
|
|
UNREACHABLE();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// resolve guid relations for asset
|
|
//-----------------------------------------------------------------------------
|
|
void Pak_ResolveAssetRelations(PakFile_s* const pak, const PakAsset_s* const asset)
|
|
{
|
|
PakPage_u* const pGuidDescriptors = &pak->memoryData.guidDescriptors[asset->dependenciesIndex];
|
|
uint32_t* const v5 = (uint32_t*)g_pakGlobals->loadedPaks[pak->memoryData.pakId & PAK_MAX_LOADED_PAKS_MASK].qword50;
|
|
|
|
if (pak_debugrelations.GetBool())
|
|
Msg(eDLL_T::RTECH, "Resolving relations for asset: '0x%-16llX', dependencies: %-4u; in pak '%s'\n",
|
|
asset->guid, asset->dependenciesCount, pak->memoryData.fileName);
|
|
|
|
for (uint32_t i = 0; i < asset->dependenciesCount; i++)
|
|
{
|
|
void** const pCurrentGuid = reinterpret_cast<void**>(pak->memoryData.memPageBuffers[pGuidDescriptors[i].index] + pGuidDescriptors[i].offset);
|
|
|
|
// get current guid
|
|
const PakGuid_t targetGuid = reinterpret_cast<uint64_t>(*pCurrentGuid);
|
|
|
|
// get asset index
|
|
int currentIndex = targetGuid & PAK_MAX_LOADED_ASSETS_MASK;
|
|
const PakGuid_t currentGuid = g_pakGlobals->loadedAssets[currentIndex].guid;
|
|
|
|
const int64_t v9 = 2i64 * InterlockedExchangeAdd(v5, 1u);
|
|
*reinterpret_cast<PakGuid_t*>(const_cast<uint32_t*>(&v5[2 * v9 + 2])) = targetGuid;
|
|
*reinterpret_cast<PakGuid_t*>(const_cast<uint32_t*>(&v5[2 * v9 + 4])) = asset->guid;
|
|
|
|
if (currentGuid != targetGuid)
|
|
{
|
|
// are we some special asset with the guid 2?
|
|
if (!Pak_ResolveAssetDependency(pak, currentGuid, targetGuid, currentIndex, true))
|
|
{
|
|
PakAsset_s* assetEntries = pak->memoryData.assetEntries;
|
|
uint64_t a = 0;
|
|
|
|
for (; assetEntries->guid != targetGuid; a++, assetEntries++)
|
|
{
|
|
if (a >= pak->memoryData.pakHeader.assetCount)
|
|
{
|
|
if (!Pak_ResolveAssetDependency(pak, currentGuid, targetGuid, currentIndex, false))
|
|
{
|
|
// the dependency couldn't be resolved, this state is irrecoverable;
|
|
// error out
|
|
Error(eDLL_T::RTECH, EXIT_FAILURE, "Failed to resolve asset dependency %u of %u\n"
|
|
"pak: '%s'\n"
|
|
"asset: '0x%llX'\n"
|
|
"target: '0x%llX'\n",
|
|
i, asset->dependenciesCount,
|
|
pak->memoryData.fileName,
|
|
asset->guid,
|
|
targetGuid);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
currentIndex = pak->memoryData.loadedAssetIndices[a];
|
|
}
|
|
}
|
|
|
|
// finally write the pointer to the guid entry
|
|
*pCurrentGuid = g_pakGlobals->loadedAssets[currentIndex].head;
|
|
}
|
|
}
|
|
|
|
uint32_t Pak_ProcessRemainingPagePointers(PakFile_s* const pak)
|
|
{
|
|
uint32_t processedPointers = 0;
|
|
|
|
for (processedPointers = pak->numProcessedPointers; processedPointers < pak->GetPointerCount(); ++processedPointers)
|
|
{
|
|
PakPage_u* const curPage = &pak->memoryData.virtualPointers[processedPointers];
|
|
int curCount = curPage->index - pak->firstPageIdx;
|
|
|
|
if (curCount < 0)
|
|
curCount += pak->memoryData.pakHeader.memPageCount;
|
|
|
|
if (curCount >= pak->processedPageCount)
|
|
break;
|
|
|
|
PakPage_u* const ptr = reinterpret_cast<PakPage_u*>(pak->GetPointerForPageOffset(curPage));
|
|
ptr->ptr = pak->memoryData.memPageBuffers[ptr->index] + ptr->offset;
|
|
}
|
|
|
|
return processedPointers;
|
|
}
|
|
|
|
void Pak_RunAssetLoadingJobs(PakFile_s* const pak)
|
|
{
|
|
pak->numProcessedPointers = Pak_ProcessRemainingPagePointers(pak);
|
|
|
|
const uint32_t numAssets = pak->processedAssetCount;
|
|
|
|
if (numAssets == pak->memoryData.pakHeader.assetCount)
|
|
return;
|
|
|
|
PakAsset_s* pakAsset = &pak->memoryData.assetEntries[numAssets];
|
|
|
|
if (pakAsset->pageEnd > pak->processedPageCount)
|
|
return;
|
|
|
|
for (uint32_t currentAsset = numAssets; g_pakGlobals->numAssetLoadJobs <= 0xC8u;)
|
|
{
|
|
pak->memoryData.loadedAssetIndices[currentAsset] = Pak_TrackAsset(pak, pakAsset);
|
|
|
|
_InterlockedIncrement16(&g_pakGlobals->numAssetLoadJobs);
|
|
|
|
const uint8_t assetBind = pakAsset->HashTableIndexForAssetType();
|
|
|
|
if (g_pakGlobals->assetBindings[assetBind].loadAssetFunc)
|
|
{
|
|
const JobTypeID_t jobTypeId = g_pakGlobals->assetBindJobTypes[assetBind];
|
|
|
|
// have to cast it to a bigger size to send it as param to JTGuts_AddJob().
|
|
const int64_t pakId = pak->memoryData.pakId;
|
|
|
|
JTGuts_AddJob(jobTypeId, pak->memoryData.assetLoadJobId, (void*)pakId, (void*)(uint64_t)currentAsset);
|
|
}
|
|
else
|
|
{
|
|
if (_InterlockedExchangeAdd16((volatile signed __int16*)&pakAsset->numRemainingDependencies, 0xFFFFu) == 1)
|
|
Pak_ProcessAssetRelationsAndResolveDependencies(pak, pakAsset, currentAsset, assetBind);
|
|
|
|
_InterlockedDecrement16(&g_pakGlobals->numAssetLoadJobs);
|
|
}
|
|
|
|
currentAsset = ++pak->processedAssetCount;
|
|
|
|
if (currentAsset == pak->memoryData.pakHeader.assetCount)
|
|
{
|
|
JT_EndJobGroup(pak->memoryData.assetLoadJobId);
|
|
return;
|
|
}
|
|
|
|
pakAsset = &pak->memoryData.assetEntries[currentAsset];
|
|
|
|
if (pakAsset->pageEnd > pak->processedPageCount)
|
|
return;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// load user-requested pak files on-demand
|
|
//-----------------------------------------------------------------------------
|
|
PakHandle_t Pak_LoadAsync(const char* const fileName, CAlignedMemAlloc* const allocator, const int nIdx, const bool bUnk)
|
|
{
|
|
PakHandle_t pakId = PAK_INVALID_HANDLE;
|
|
|
|
if (Pak_FileExists(fileName))
|
|
{
|
|
Msg(eDLL_T::RTECH, "Loading pak file: '%s'\n", fileName);
|
|
pakId = v_Pak_LoadAsync(fileName, allocator, nIdx, bUnk);
|
|
|
|
if (pakId == PAK_INVALID_HANDLE)
|
|
Error(eDLL_T::RTECH, NO_ERROR, "%s: Failed read '%s' results '%d'\n", __FUNCTION__, fileName, pakId);
|
|
}
|
|
else
|
|
{
|
|
Error(eDLL_T::RTECH, NO_ERROR, "%s: Failed; file '%s' doesn't exist\n", __FUNCTION__, fileName);
|
|
}
|
|
|
|
return pakId;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// unloads loaded pak files
|
|
//-----------------------------------------------------------------------------
|
|
void Pak_UnloadAsync(PakHandle_t handle)
|
|
{
|
|
const PakLoadedInfo_s* const pakInfo = Pak_GetPakInfo(handle);
|
|
|
|
if (pakInfo->fileName)
|
|
Msg(eDLL_T::RTECH, "Unloading pak file: '%s'\n", pakInfo->fileName);
|
|
|
|
v_Pak_UnloadAsync(handle);
|
|
}
|
|
|
|
#define CMD_INVALID -1
|
|
|
|
// only patch cmds 4,5,6 use this array to determine their data size
|
|
static const int s_patchCmdToBytesToProcess[] = { CMD_INVALID, CMD_INVALID, CMD_INVALID, CMD_INVALID, 3, 7, 6, 0 };
|
|
#undef CMD_INVALID
|
|
//----------------------------------------------------------------------------------
|
|
// loads and processes a pak file (handles decompression and patching)
|
|
// TODO: !!! FINISH REBUILD !!!
|
|
//----------------------------------------------------------------------------------
|
|
bool Pak_ProcessPakFile(PakFile_s* const pak)
|
|
{
|
|
PakFileStream_s* const fileStream = &pak->fileStream;
|
|
PakMemoryData_s* const memoryData = &pak->memoryData;
|
|
|
|
// first request is always just the header?
|
|
size_t readStart = sizeof(PakFileHeader_s);
|
|
|
|
if (fileStream->numDataChunks > 0)
|
|
readStart = fileStream->numDataChunks * PAK_READ_DATA_CHUNK_SIZE;
|
|
|
|
for (; fileStream->numDataChunksProcessed != fileStream->numDataChunks; fileStream->numDataChunksProcessed++)
|
|
{
|
|
const int v7 = fileStream->numDataChunksProcessed & 0x1F;
|
|
const uint8_t v8 = fileStream->gap94[v7];
|
|
if (v8 != 1)
|
|
{
|
|
size_t bytesProcessed = 0;
|
|
const char* statusMsg = "(no reason)";
|
|
const uint8_t currentStatus = g_pakLoadApi->CheckAsyncRequest(fileStream->asyncRequestHandles[v7], &bytesProcessed, &statusMsg);
|
|
|
|
if (currentStatus == AsyncHandleStatus_t::FS_ASYNC_ERROR)
|
|
Error(eDLL_T::RTECH, EXIT_FAILURE, "Error reading pak file \"%s\" -- %s\n", pak->memoryData.fileName, statusMsg);
|
|
else if (currentStatus == AsyncHandleStatus_t::FS_ASYNC_PENDING)
|
|
break;
|
|
|
|
fileStream->bytesStreamed += bytesProcessed;
|
|
if (v8)
|
|
{
|
|
const PakFileHeader_s* pakHeader = &pak->memoryData.pakHeader;
|
|
const uint64_t v16 = fileStream->numDataChunksProcessed * PAK_READ_DATA_CHUNK_SIZE;
|
|
|
|
if (v8 == 2)
|
|
{
|
|
fileStream->bytesStreamed = bytesProcessed + v16;
|
|
pakHeader = (PakFileHeader_s*)&fileStream->buffer[v16 & fileStream->bufferMask];
|
|
}
|
|
|
|
const uint8_t fileIndex = fileStream->numLoadedFiles++ & PAK_MAX_ASYNC_STREAMED_LOAD_REQUESTS_MASK;
|
|
|
|
//printf("v16: %lld\n", v16);
|
|
fileStream->descriptors[fileIndex].dataOffset = v16 + sizeof(PakFileHeader_s);
|
|
fileStream->descriptors[fileIndex].compressedSize = v16 + pakHeader->compressedSize;
|
|
fileStream->descriptors[fileIndex].decompressedSize = pakHeader->decompressedSize;
|
|
fileStream->descriptors[fileIndex].compressionMode = pakHeader->GetCompressionMode();
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t qword1D0 = memoryData->processedPatchedDataSize;
|
|
|
|
for (; pak->byte1F8 != fileStream->numLoadedFiles; pak->byte1F8++)
|
|
{
|
|
PakFileStream_s::Descriptor* v22 = &fileStream->descriptors[pak->byte1F8 & PAK_MAX_ASYNC_STREAMED_LOAD_REQUESTS_MASK];
|
|
|
|
if (pak->byte1FD)
|
|
{
|
|
pak->byte1FD = false;
|
|
pak->inputBytePos = v22->dataOffset;
|
|
|
|
if (v22->compressionMode != PakDecodeMode_e::MODE_DISABLED)
|
|
{
|
|
pak->isOffsetted_MAYBE = false;
|
|
pak->isCompressed = true;
|
|
memoryData->processedPatchedDataSize = sizeof(PakFileHeader_s);
|
|
}
|
|
else
|
|
{
|
|
pak->isOffsetted_MAYBE = true;
|
|
pak->isCompressed = false;
|
|
//printf("v22->dataOffset: %lld\n", v22->dataOffset);
|
|
memoryData->processedPatchedDataSize = v22->dataOffset;
|
|
}
|
|
|
|
if (pak->isCompressed)
|
|
{
|
|
const size_t decompressedSize = Pak_InitDecoder(&pak->pakDecoder,
|
|
fileStream->buffer, pak->decompBuffer,
|
|
PAK_DECODE_IN_RING_BUFFER_MASK, PAK_DECODE_OUT_RING_BUFFER_MASK,
|
|
v22->compressedSize - (v22->dataOffset - sizeof(PakFileHeader_s)),
|
|
v22->dataOffset - sizeof(PakFileHeader_s), sizeof(PakFileHeader_s), v22->compressionMode);
|
|
|
|
if (decompressedSize != v22->decompressedSize)
|
|
Error(eDLL_T::RTECH, EXIT_FAILURE,
|
|
"Error reading pak file \"%s\" with decoder \"%s\" -- decompressed size %zu doesn't match expected value %zu\n",
|
|
pak->memoryData.fileName,
|
|
Pak_DecoderToString(v22->compressionMode),
|
|
decompressedSize,
|
|
pak->memoryData.pakHeader.decompressedSize);
|
|
}
|
|
}
|
|
|
|
if (pak->isCompressed)
|
|
{
|
|
qword1D0 = pak->pakDecoder.outBufBytePos;
|
|
|
|
if (qword1D0 != pak->pakDecoder.decompSize)
|
|
{
|
|
const bool didDecode = Pak_StreamToBufferDecode(&pak->pakDecoder,
|
|
fileStream->bytesStreamed, (memoryData->processedPatchedDataSize + PAK_DECODE_OUT_RING_BUFFER_SIZE), v22->compressionMode);
|
|
|
|
qword1D0 = pak->pakDecoder.outBufBytePos;
|
|
pak->inputBytePos = pak->pakDecoder.inBufBytePos;
|
|
|
|
if (didDecode)
|
|
DevMsg(eDLL_T::RTECH, "%s: pak '%s' decoded successfully\n", __FUNCTION__, pak->GetName());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qword1D0 = Min(v22->compressedSize, fileStream->bytesStreamed);
|
|
}
|
|
|
|
if (pak->inputBytePos != v22->compressedSize || memoryData->processedPatchedDataSize != qword1D0)
|
|
break;
|
|
|
|
pak->byte1FD = true;
|
|
qword1D0 = memoryData->processedPatchedDataSize;
|
|
}
|
|
|
|
size_t numBytesToProcess = qword1D0 - memoryData->processedPatchedDataSize;
|
|
|
|
while (memoryData->patchSrcSize + memoryData->field_2A8)
|
|
{
|
|
// if there are no bytes left to process in this patch operation
|
|
if (!memoryData->numBytesToProcess_maybe)
|
|
{
|
|
RBitRead& bitbuf = memoryData->bitBuf;
|
|
bitbuf.ConsumeData(memoryData->patchData, bitbuf.BitsAvailable());
|
|
|
|
// advance patch data buffer by the number of bytes that have just been fetched
|
|
memoryData->patchData = &memoryData->patchData[bitbuf.BitsAvailable() >> 3];
|
|
|
|
// store the number of bits remaining to complete the data read
|
|
bitbuf.m_bitsAvailable = bitbuf.BitsAvailable() & 7; // number of bits above a whole byte
|
|
|
|
const __int8 cmd = memoryData->patchCommands[bitbuf.ReadBits(6)];
|
|
|
|
bitbuf.DiscardBits(memoryData->PATCH_field_68[bitbuf.ReadBits(6)]);
|
|
|
|
// get the next patch function to execute
|
|
memoryData->patchFunc = g_pakPatchApi[cmd];
|
|
|
|
if (cmd <= 3u)
|
|
{
|
|
const uint8_t bitExponent = memoryData->PATCH_unk2[bitbuf.ReadBits(8)]; // number of stored bits for the data size
|
|
|
|
bitbuf.DiscardBits(memoryData->PATCH_unk3[bitbuf.ReadBits(8)]);
|
|
|
|
memoryData->numBytesToProcess_maybe = (1ull << bitExponent) + bitbuf.ReadBits(bitExponent);
|
|
|
|
bitbuf.DiscardBits(bitExponent);
|
|
}
|
|
else
|
|
{
|
|
memoryData->numBytesToProcess_maybe = s_patchCmdToBytesToProcess[cmd];
|
|
}
|
|
}
|
|
|
|
if (!pak->memoryData.patchFunc(pak, &numBytesToProcess))
|
|
break;
|
|
}
|
|
|
|
if (pak->isOffsetted_MAYBE)
|
|
pak->inputBytePos = memoryData->processedPatchedDataSize;
|
|
|
|
if (!fileStream->finishedLoadingPatches)
|
|
{
|
|
const size_t v42 = min(fileStream->numDataChunksProcessed, pak->inputBytePos >> 19);
|
|
|
|
//if ((unsigned int)(pak->inputBytePos >> 19) < v42)
|
|
// v42 = pak->inputBytePos >> 19;
|
|
|
|
while (fileStream->numDataChunks != v42 + 32)
|
|
{
|
|
const int8_t requestIdx = fileStream->numDataChunks & 0x1F;
|
|
const size_t readOffsetEnd = (fileStream->numDataChunks + 1ull) * PAK_READ_DATA_CHUNK_SIZE;
|
|
|
|
if (fileStream->fileReadStatus == 1)
|
|
{
|
|
fileStream->asyncRequestHandles[requestIdx] = FS_ASYNC_REQ_INVALID;
|
|
fileStream->gap94[requestIdx] = 1;
|
|
|
|
if (((requestIdx + 1) & PAK_MAX_ASYNC_STREAMED_LOAD_REQUESTS_MASK) == 0)
|
|
fileStream->fileReadStatus = 2;
|
|
|
|
++fileStream->numDataChunks;
|
|
readStart = readOffsetEnd;
|
|
}
|
|
else
|
|
{
|
|
if (readStart < fileStream->fileSize)
|
|
{
|
|
const size_t lenToRead = Min(fileStream->fileSize, readOffsetEnd);
|
|
|
|
const size_t readOffset = readStart - fileStream->qword0;
|
|
const size_t readSize = lenToRead - readStart;
|
|
|
|
fileStream->asyncRequestHandles[requestIdx] = v_FS_ReadAsyncFile(
|
|
fileStream->fileHandle,
|
|
readOffset,
|
|
readSize,
|
|
&fileStream->buffer[readStart & fileStream->bufferMask],
|
|
0,
|
|
0,
|
|
4);
|
|
|
|
fileStream->gap94[requestIdx] = fileStream->fileReadStatus;
|
|
fileStream->fileReadStatus = 0;
|
|
|
|
++fileStream->numDataChunks;
|
|
readStart = readOffsetEnd;
|
|
}
|
|
else
|
|
{
|
|
if (pak->patchCount >= pak->memoryData.pakHeader.patchIndex)
|
|
{
|
|
FS_CloseAsyncFile(fileStream->fileHandle);
|
|
fileStream->fileHandle = PAK_INVALID_HANDLE;
|
|
fileStream->qword0 = 0;
|
|
fileStream->finishedLoadingPatches = true;
|
|
|
|
return memoryData->patchSrcSize == 0;
|
|
}
|
|
|
|
if (!pak->dword14)
|
|
return memoryData->patchSrcSize == 0;
|
|
|
|
char pakPatchPath[MAX_PATH] = {};
|
|
sprintf(pakPatchPath, PAK_PLATFORM_PATH"%s", pak->memoryData.fileName);
|
|
|
|
// get path of next patch rpak to load
|
|
if (pak->memoryData.patchIndices[pak->patchCount])
|
|
{
|
|
char* pExtension = nullptr;
|
|
|
|
char* it = pakPatchPath;
|
|
while (*it)
|
|
{
|
|
if (*it == '.')
|
|
pExtension = it;
|
|
else if (*it == '\\' || *it == '/')
|
|
pExtension = nullptr;
|
|
|
|
++it;
|
|
}
|
|
|
|
if (pExtension)
|
|
it = pExtension;
|
|
|
|
// replace extension '.rpak' with '(xx).rpak'
|
|
snprintf(it, &pakPatchPath[sizeof(pakPatchPath)] - it,
|
|
"(%02u).rpak", pak->memoryData.patchIndices[pak->patchCount]);
|
|
}
|
|
|
|
const int patchFileHandle = FS_OpenAsyncFile(pakPatchPath, 5, &numBytesToProcess);
|
|
|
|
//printf("[%s] Opened pak '%s' with file handle %i\n", pak->GetName(), pakPatchPath, patchFileHandle);
|
|
|
|
if (patchFileHandle == FS_ASYNC_FILE_INVALID)
|
|
Error(eDLL_T::RTECH, EXIT_FAILURE, "Couldn't open file \"%s\".\n", pakPatchPath);
|
|
|
|
if (numBytesToProcess < pak->memoryData.patchHeaders[pak->patchCount].compressedSize)
|
|
Error(eDLL_T::RTECH, EXIT_FAILURE, "File \"%s\" appears truncated; read size: %zu < expected size: %zu.\n",
|
|
pakPatchPath, numBytesToProcess, pak->memoryData.patchHeaders[pak->patchCount].compressedSize);
|
|
|
|
FS_CloseAsyncFile(fileStream->fileHandle);
|
|
|
|
fileStream->fileHandle = patchFileHandle;
|
|
|
|
const size_t v58 = ALIGN_VALUE(fileStream->numDataChunks, 8ull) * PAK_READ_DATA_CHUNK_SIZE;
|
|
fileStream->fileReadStatus = (fileStream->numDataChunks == ALIGN_VALUE(fileStream->numDataChunks, 8ull)) + 1;
|
|
|
|
//printf("[%s] dwordB8: %i, v58: %lld, byteBC: %i, numFiles: %i\n", pak->GetName(), fileStream->numDataChunks, v58, fileStream->byteBC, fileStream->numLoadedFiles);
|
|
fileStream->qword0 = v58;
|
|
fileStream->fileSize = v58 + pak->memoryData.patchHeaders[pak->patchCount].compressedSize;
|
|
|
|
pak->patchCount++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return memoryData->patchSrcSize == 0;
|
|
}
|
|
|
|
// sets patch variables for copying the next unprocessed page into the relevant segment buffer
|
|
// if this is a header page, fetch info from the next unprocessed asset and copy over the asset's header
|
|
bool SetupNextPageForPatching(PakLoadedInfo_s* a1, PakFile_s* pak)
|
|
{
|
|
Pak_RunAssetLoadingJobs(pak);
|
|
|
|
// numProcessedPointers has just been set in the above function call
|
|
pak->memoryData.numShiftedPointers = pak->numProcessedPointers;
|
|
|
|
if (pak->processedPageCount == pak->GetPageCount())
|
|
return false;
|
|
//break;
|
|
|
|
const uint32_t highestProcessedPageIdx = pak->processedPageCount + pak->firstPageIdx;
|
|
pak->processedPageCount++;
|
|
|
|
int v26 = highestProcessedPageIdx - pak->GetPageCount();
|
|
if (highestProcessedPageIdx < pak->GetPageCount())
|
|
v26 = highestProcessedPageIdx;
|
|
|
|
const PakPageHeader_s* const nextMemPageHeader = &pak->memoryData.pageHeaders[v26];
|
|
if ((pak->memoryData.segmentHeaders[nextMemPageHeader->segmentIdx].typeFlags & (SF_TEMP | SF_CPU)) != 0)
|
|
{
|
|
pak->memoryData.patchSrcSize = nextMemPageHeader->dataSize;
|
|
pak->memoryData.patchDstPtr = reinterpret_cast<char*>(pak->memoryData.memPageBuffers[v26]);
|
|
|
|
return true;
|
|
//continue;
|
|
}
|
|
|
|
// headers
|
|
PakAsset_s* pakAsset = pak->memoryData.ppAssetEntries[pak->memoryData.someAssetCount];
|
|
|
|
pak->memoryData.patchSrcSize = pakAsset->headerSize;
|
|
int assetTypeIdx = pakAsset->HashTableIndexForAssetType();
|
|
|
|
pak->memoryData.patchDstPtr = reinterpret_cast<char*>(a1->segmentBuffers[0]) + pak->memoryData.unkAssetTypeBindingSizes[assetTypeIdx];
|
|
pak->memoryData.unkAssetTypeBindingSizes[assetTypeIdx] += g_pakGlobals->assetBindings[assetTypeIdx].nativeClassSize;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Pak_ProcessAssets(PakLoadedInfo_s* const a1)
|
|
{
|
|
PakFile_s* const pak = a1->pakFile;
|
|
while (pak->processedAssetCount != pak->GetAssetCount())
|
|
{
|
|
// TODO: invert condition and make the branch encompass the whole loop
|
|
if (!(pak->memoryData.patchSrcSize + pak->memoryData.field_2A8))
|
|
{
|
|
const bool res = SetupNextPageForPatching(a1, pak);
|
|
|
|
if (res)
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (!Pak_ProcessPakFile(pak))
|
|
return false;
|
|
|
|
// processedPageCount must be greater than 0 here otherwise the page index will be negative and cause a crash
|
|
// if this happens, something probably went wrong with the patch data condition at the start of the loop, as that
|
|
// function call should increment processedPageCount if it succeeded
|
|
assert(pak->processedPageCount > 0);
|
|
|
|
const uint32_t pageCount = pak->GetPageCount();
|
|
const uint32_t v4 = (pak->firstPageIdx - 1) + pak->processedPageCount;
|
|
|
|
uint32_t shiftedPageIndex = v4;
|
|
|
|
if (v4 >= pageCount)
|
|
shiftedPageIndex -= pageCount;
|
|
|
|
// if "temp_" segment
|
|
if ((pak->memoryData.segmentHeaders[pak->memoryData.pageHeaders[shiftedPageIndex].segmentIdx].typeFlags & (SF_TEMP | SF_CPU)) != 0)
|
|
{
|
|
const bool res = SetupNextPageForPatching(a1, pak);
|
|
|
|
if (res)
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
|
|
PakAsset_s* asset = pak->memoryData.ppAssetEntries[pak->memoryData.someAssetCount];
|
|
const uint32_t headPageOffset = asset->headPtr.offset;
|
|
char* v8 = pak->memoryData.patchDstPtr - asset->headerSize;
|
|
|
|
uint32_t newOffsetFromSegmentBufferToHeader = LODWORD(pak->memoryData.patchDstPtr)
|
|
- asset->headerSize
|
|
- LODWORD(a1->segmentBuffers[0]);
|
|
asset->headPtr.offset = newOffsetFromSegmentBufferToHeader;
|
|
|
|
uint32_t offsetSize = newOffsetFromSegmentBufferToHeader - headPageOffset;
|
|
|
|
for (uint32_t i = pak->memoryData.numShiftedPointers; i < pak->GetPointerCount(); pak->memoryData.numShiftedPointers = i)
|
|
{
|
|
PakPage_u* ptr = &pak->memoryData.virtualPointers[i];
|
|
|
|
ASSERT_PAKPTR_VALID(pak, ptr);
|
|
|
|
if (ptr->index != shiftedPageIndex)
|
|
break;
|
|
|
|
const uint32_t offsetToPointer = ptr->offset - headPageOffset;
|
|
if (offsetToPointer >= asset->headerSize)
|
|
break;
|
|
|
|
PakPage_u* pagePtr = reinterpret_cast<PakPage_u*>(v8 + offsetToPointer);
|
|
|
|
ASSERT_PAKPTR_VALID(pak, ptr);
|
|
|
|
ptr->offset += offsetSize;
|
|
|
|
if (pagePtr->index == shiftedPageIndex)
|
|
pagePtr->offset += offsetSize;
|
|
|
|
i = pak->memoryData.numShiftedPointers + 1;
|
|
}
|
|
|
|
for (uint32_t j = 0; j < asset->dependenciesCount; ++j)
|
|
{
|
|
PakPage_u* descriptor = &pak->memoryData.guidDescriptors[asset->dependenciesIndex + j];
|
|
|
|
if (descriptor->index == shiftedPageIndex)
|
|
descriptor->offset += offsetSize;
|
|
}
|
|
|
|
const uint32_t v16 = ++pak->memoryData.someAssetCount;
|
|
|
|
PakAsset_s* v17 = nullptr;
|
|
if (v16 < pak->GetAssetCount() && (v17 = pak->memoryData.ppAssetEntries[v16], v17->headPtr.index == shiftedPageIndex))
|
|
{
|
|
pak->memoryData.field_2A8 = v17->headPtr.offset - headPageOffset - asset->headerSize;
|
|
pak->memoryData.patchSrcSize = v17->headerSize;
|
|
const uint8_t assetTypeIdx = v17->HashTableIndexForAssetType();
|
|
|
|
pak->memoryData.patchDstPtr = reinterpret_cast<char*>(a1->segmentBuffers[0]) + pak->memoryData.unkAssetTypeBindingSizes[assetTypeIdx];
|
|
|
|
pak->memoryData.unkAssetTypeBindingSizes[assetTypeIdx] += g_pakGlobals->assetBindings[assetTypeIdx].nativeClassSize;
|
|
}
|
|
else
|
|
{
|
|
bool res = SetupNextPageForPatching(a1, pak);
|
|
|
|
if (res)
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!JT_IsJobDone(pak->memoryData.assetLoadJobId))
|
|
return false;
|
|
|
|
uint32_t i = 0;
|
|
PakAsset_s* pAsset = nullptr;
|
|
|
|
for (int j = pak->memoryData.pakId & PAK_MAX_LOADED_PAKS_MASK; i < pak->GetHeader().assetCount; a1->assetGuids[i - 1] = pAsset->guid)
|
|
{
|
|
pAsset = &pak->memoryData.assetEntries[i];
|
|
if (pAsset->numRemainingDependencies)
|
|
{
|
|
//printf("[%s] processing deps for %llX (%.4s)\n", pak->GetName(), pAsset->guid, (char*)&pAsset->magic);
|
|
Pak_ResolveAssetRelations(pak, pAsset);
|
|
|
|
const int assetIndex = pak->memoryData.loadedAssetIndices[i];
|
|
const PakAssetShort_s& loadedAsset = g_pakGlobals->loadedAssets[assetIndex];
|
|
|
|
if (g_pakGlobals->trackedAssets[loadedAsset.trackerIndex].loadedPakIndex == j)
|
|
{
|
|
PakTracker_s* pakTracker = g_pakGlobals->pakTracker;
|
|
|
|
if (pakTracker)
|
|
{
|
|
if (pakTracker->numPaksTracked)
|
|
{
|
|
int* trackerIndices = g_pakGlobals->pakTracker->loadedAssetIndices;
|
|
uint32_t count = 0;
|
|
|
|
while (*trackerIndices != assetIndex)
|
|
{
|
|
++count;
|
|
++trackerIndices;
|
|
|
|
if (count >= pakTracker->numPaksTracked)
|
|
goto LABEL_41;
|
|
}
|
|
|
|
goto LABEL_42;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pakTracker = reinterpret_cast<PakTracker_s*>(AlignedMemAlloc()->Alloc(sizeof(PakTracker_s), 8));
|
|
pakTracker->numPaksTracked = 0;
|
|
pakTracker->unk_4 = 0;
|
|
pakTracker->unk_8 = 0;
|
|
|
|
g_pakGlobals->pakTracker = pakTracker;
|
|
}
|
|
LABEL_41:
|
|
|
|
pakTracker->loadedAssetIndices[pakTracker->numPaksTracked] = assetIndex;
|
|
++pakTracker->numPaksTracked;
|
|
}
|
|
}
|
|
LABEL_42:
|
|
++i;
|
|
}
|
|
|
|
if (g_pakGlobals->pakTracker)
|
|
sub_14043D870(a1, 0);
|
|
|
|
a1->status = PakStatus_e::PAK_STATUS_LOADED;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Pak_StubInvalidAssetBinds(PakFile_s* const pak, PakSegmentDescriptor_s* const desc)
|
|
{
|
|
for (uint32_t i = 0; i < pak->GetAssetCount(); ++i)
|
|
{
|
|
PakAsset_s* const asset = &pak->memoryData.assetEntries[i];
|
|
pak->memoryData.ppAssetEntries[i] = asset;
|
|
|
|
const uint8_t assetTypeIndex = asset->HashTableIndexForAssetType();
|
|
desc->assetTypeCount[assetTypeIndex]++;
|
|
|
|
PakAssetBinding_s* const assetBinding = &g_pakGlobals->assetBindings[assetTypeIndex];
|
|
|
|
if (assetBinding->type == PakAssetBinding_s::NONE)
|
|
{
|
|
assetBinding->extension = asset->magic;
|
|
assetBinding->version = asset->version;
|
|
assetBinding->description = "<unknown>";
|
|
assetBinding->loadAssetFunc = nullptr;
|
|
assetBinding->unloadAssetFunc = nullptr;
|
|
assetBinding->replaceAssetFunc = nullptr;
|
|
assetBinding->allocator = AlignedMemAlloc();
|
|
assetBinding->headerSize = asset->headerSize;
|
|
assetBinding->nativeClassSize = asset->headerSize;
|
|
assetBinding->headerAlignment = pak->memoryData.pageHeaders[asset->headPtr.index].pageAlignment;
|
|
assetBinding->type = PakAssetBinding_s::STUB;
|
|
}
|
|
|
|
// this is dev only because it could spam a lot on older paks
|
|
// which isn't much help to the average user that can't rebuild other people's paks
|
|
if (asset->version != assetBinding->version)
|
|
{
|
|
FourCCString_t assetMagic;
|
|
FourCCToString(assetMagic, asset->magic);
|
|
|
|
DevWarning(eDLL_T::RTECH,
|
|
"Unexpected asset version for \"%s\" (%.4s) asset with guid 0x%llX (asset %u in pakfile '%s'). Expected %u, found %u.\n",
|
|
assetBinding->description,
|
|
assetMagic,
|
|
asset->guid,
|
|
i, pak->GetName(),
|
|
assetBinding->version, asset->version
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Pak_StartLoadingPak(PakLoadedInfo_s* const loadedInfo)
|
|
{
|
|
PakFile_s* const pakFile = loadedInfo->pakFile;
|
|
|
|
if (pakFile->memoryData.patchSrcSize && !Pak_ProcessPakFile(pakFile))
|
|
return false;
|
|
|
|
PakSegmentDescriptor_s pakDescriptor = {};
|
|
|
|
Pak_StubInvalidAssetBinds(pakFile, &pakDescriptor);
|
|
|
|
const uint32_t numAssets = pakFile->GetAssetCount();
|
|
|
|
if (pakFile->memoryData.pakHeader.patchIndex)
|
|
pakFile->firstPageIdx = pakFile->memoryData.patchDataHeader->pageCount;
|
|
|
|
sub_140442740(pakFile->memoryData.ppAssetEntries, &pakFile->memoryData.ppAssetEntries[numAssets], numAssets, pakFile);
|
|
|
|
// pak must have no more than PAK_MAX_SEGMENTS segments as otherwise we will overrun the above "segmentSizes" array
|
|
// and write to arbitrary locations on the stack
|
|
if (pakFile->GetSegmentCount() > PAK_MAX_SEGMENTS)
|
|
{
|
|
Error(eDLL_T::RTECH, EXIT_FAILURE, "Too many segments in pakfile '%s'. Max %i, found %i.\n", pakFile->GetName(), PAK_MAX_SEGMENTS, pakFile->GetSegmentCount());
|
|
return false;
|
|
}
|
|
|
|
Pak_AlignSegmentHeaders(pakFile, &pakDescriptor);
|
|
Pak_AlignSegments(pakFile, &pakDescriptor);
|
|
|
|
// allocate segment buffers with predetermined alignments; pages will be
|
|
// copied into here
|
|
for (int8_t i = 0; i < PAK_SEGMENT_BUFFER_TYPES; ++i)
|
|
{
|
|
if (pakDescriptor.segmentSizeForType[i])
|
|
loadedInfo->segmentBuffers[i] = AlignedMemAlloc()->Alloc(pakDescriptor.segmentSizeForType[i], pakDescriptor.segmentAlignmentForType[i]);
|
|
}
|
|
|
|
Pak_CopyPagesToSegments(pakFile, loadedInfo, &pakDescriptor);
|
|
|
|
const PakFileHeader_s& pakHdr = pakFile->GetHeader();
|
|
|
|
if (Pak_StreamingEnabled())
|
|
Pak_LoadStreamingData(loadedInfo);
|
|
|
|
const __int64 v106 = pakHdr.descriptorCount + 2 * (pakHdr.patchIndex + pakHdr.assetCount + 4ull * pakHdr.assetCount + pakHdr.virtualSegmentCount);
|
|
const __int64 patchDestOffset = pakHdr.GetTotalHeaderSize() + 2 * (pakHdr.patchIndex + 6ull * pakHdr.memPageCount + 4 * v106);
|
|
|
|
pakFile->dword14 = 1;
|
|
|
|
PakMemoryData_s& memoryData = pakFile->memoryData;
|
|
|
|
memoryData.patchSrcSize = pakFile->memoryData.qword2D0 - patchDestOffset;
|
|
memoryData.patchDstPtr = (char*)&pakHdr + patchDestOffset;
|
|
|
|
loadedInfo->status = PakStatus_e::PAK_STATUS_LOAD_PAKHDR;
|
|
|
|
return true;
|
|
}
|
|
|
|
// #STR: "(%02u).rpak", "Couldn't read package file \"%s\".\n", "Error 0x%08x", "paks\\Win64\\%s"
|
|
//bool Pak_SetupBuffersAndLoad(PakHandle_t pakId)
|
|
//{
|
|
// __int64 v2; // rdi
|
|
// const char* pakFilePath; // rsi
|
|
// unsigned int v7; // ebx
|
|
// unsigned int v8; // r14d
|
|
// char** v9; // r12
|
|
// int v10; // eax
|
|
// __int64 v11; // r11
|
|
// char v12; // al
|
|
// char* i; // rdx
|
|
// uint32_t pakFileHandle; // eax
|
|
// bool result; // al
|
|
// int AsyncFile; // bl
|
|
// signed __int64 v19; // rbx
|
|
// char v20; // r14
|
|
// __int64 v21; // rdx
|
|
// const char* v22; // rax
|
|
// __int64 v23; // rbx
|
|
// __int64 asset_entry_count_var; // rax MAPDST
|
|
// __int64 patchIndex; // r11
|
|
// __int64 memPageCount; // r9
|
|
// __int64 virtualSegmentCount; // rcx
|
|
// __int64 v32; // r8
|
|
// __int64 v33; // rcx
|
|
// __int64 v34; // r14
|
|
// __int64 v35; // r12
|
|
// uint64_t ringBufferStreamSize; // rsi
|
|
// uint64_t ringBufferOutSize; // rax
|
|
// PakFile_t* pak; // rax MAPDST
|
|
// _DWORD* v40; // rax
|
|
// __int64 v43; // rdx
|
|
// uint8_t** v44; // rcx
|
|
// PakAsset_t** v45; // rdx
|
|
// PakPatchFileHeader_t* p_patchHeader; // rcx
|
|
// uint16_t v47; // ax
|
|
// PakPatchFileHeader_t* p_decompressedSize; // rdx
|
|
// __int64 v49; // rcx
|
|
// __int64 v50; // rax
|
|
// unsigned __int16* v51; // rcx
|
|
// char* v52; // rcx
|
|
// char* v53; // rdx
|
|
// __int64 v54; // rax
|
|
// PakSegmentHeader_t* v55; // rdx
|
|
// __int64 v56; // rcx
|
|
// __int64 v57; // rax
|
|
// PakPageHeader_t* v58; // rcx
|
|
// __int64 descriptorCount; // rdx
|
|
// PakPage_t* v60; // rcx
|
|
// __int64 assetCount; // rax
|
|
// PakAsset_t* v62; // r8
|
|
// __int64 guidDescriptorCount; // r9
|
|
// PakPage_t* v64; // rdx
|
|
// __int64 relationsCounts; // rcx
|
|
// uint32_t* v66; // rax
|
|
// __int64 v67; // rdx
|
|
// uint32_t* v68; // rcx
|
|
// __int64 v69; // r8
|
|
// uint32_t* v70; // rax
|
|
// __int64 v71; // rcx
|
|
// PakPatchDataHeader_t* patchDataHeader; // rax
|
|
// size_t v73; // rdx
|
|
// uint8_t* ringBuffer; // rax
|
|
// bool v75; // zf
|
|
// __int64 v76; // r10
|
|
// unsigned __int64 v77; // r8
|
|
// __int64 v78; // rax
|
|
// PakFileHeader_t pakHdr; // [rsp+40h] [rbp-C0h] BYREF
|
|
// __int64 v80; // [rsp+C0h] [rbp-40h]
|
|
// __int64 v81; // [rsp+C8h] [rbp-38h]
|
|
// char relativeFilePath[260]; // [rsp+E0h] [rbp-20h] BYREF
|
|
// char v84[12]; // [rsp+1E4h] [rbp+E4h] BYREF
|
|
// CHAR LibFileName[4]; // [rsp+1F0h] [rbp+F0h] BYREF
|
|
// char v86[8252]; // [rsp+1F4h] [rbp+F4h]
|
|
// size_t pak_file_size_var; // [rsp+2248h] [rbp+2148h] BYREF
|
|
// __int64 v89; // [rsp+2250h] [rbp+2150h]
|
|
// uint64_t v90; // [rsp+2258h] [rbp+2158h]
|
|
//
|
|
// v2 = 0i64;
|
|
//
|
|
// PakLoadedInfo_t* const loadedInfo = Pak_GetPakInfo(pakId);
|
|
//
|
|
// loadedInfo->status = PAK_STATUS_LOAD_STARTING;
|
|
// pakFilePath = loadedInfo->fileName;
|
|
//
|
|
// //
|
|
// assert(pakFilePath);
|
|
//
|
|
// const char* nameUnqualified = V_UnqualifiedFileName(pakFilePath);
|
|
//
|
|
// if (nameUnqualified != pakFilePath)
|
|
// goto LABEL_27;
|
|
//
|
|
// snprintf(relativeFilePath, 0x100ui64, "paks\\Win64\\%s", pakFilePath);
|
|
//
|
|
// v7 = g_nPatchEntryCount;
|
|
// v8 = 0;
|
|
//
|
|
// // if this pak is patched, load the last patch file first before proceeding
|
|
// // with any other pak that is getting patched. note that the patch number
|
|
// // does not indicate which pak file is the actual last patch file; a patch
|
|
// // with number (01) can also patch (02) and base, these details are
|
|
// // determined from the pak file header
|
|
// if (g_nPatchEntryCount)
|
|
// {
|
|
// v9 = g_pszPakPatchList;
|
|
//
|
|
// int n;
|
|
// while (1)
|
|
// {
|
|
// n = V_stricmp(pakFilePath, v9[(v7 + v8) >> 1]);
|
|
//
|
|
// if (n >= 0)
|
|
// break;
|
|
//
|
|
// v7 = v11;
|
|
// LABEL_13:
|
|
// if (v8 >= v7)
|
|
// goto SKIP_PATCH_LOADING;
|
|
// }
|
|
//
|
|
// if (n > 0)
|
|
// {
|
|
// v8 = v11 + 1;
|
|
// goto LABEL_13;
|
|
// }
|
|
//
|
|
// const int patchNumber = g_pnPatchNumbers[v11];
|
|
//
|
|
// if (patchNumber)
|
|
// {
|
|
// char* const extension = (char*)V_GetFileExtension(pakFilePath, true);
|
|
// snprintf(extension, &relativeFilePath[sizeof(relativeFilePath)] - extension, "(%02u).rpak", patchNumber);
|
|
// }
|
|
// }
|
|
//
|
|
// if (!g_nPatchEntryCount)
|
|
// goto SKIP_PATCH_LOADING;
|
|
//
|
|
// v9 = g_pszPakPatchList;
|
|
// while (1)
|
|
// {
|
|
// LOBYTE(v10) = stricmp(pakFilePath, v9[(v7 + v8) >> 1]);
|
|
// if (v10 >= 0)
|
|
// break;
|
|
// v7 = v11;
|
|
// LABEL_13:
|
|
// if (v8 >= v7)
|
|
// goto SKIP_PATCH_LOADING;
|
|
// }
|
|
// if (v10 > 0)
|
|
// {
|
|
// v8 = v11 + 1;
|
|
// goto LABEL_13;
|
|
// }
|
|
//
|
|
// const int patchNumber = g_pnPatchNumbers[v11];
|
|
//
|
|
// if (patchNumber)
|
|
// {
|
|
// char* const extension = (char*)V_GetFileExtension(pakFilePath, true);
|
|
// snprintf(extension, &relativeFilePath[sizeof(relativeFilePath)] - extension, "(%02u).rpak", patchNumber);
|
|
// }
|
|
//
|
|
//SKIP_PATCH_LOADING:
|
|
// pakFilePath = relativeFilePath;
|
|
//LABEL_27:
|
|
// pakFileHandle = FS_OpenAsyncFile(pakFilePath, loadedInfo->logLevel, &pak_file_size_var);
|
|
//
|
|
// if (pakFileHandle == FS_ASYNC_FILE_INVALID)
|
|
// {
|
|
// if (async_debug_level.GetInt() >= loadedInfo->logLevel)
|
|
// Error(eDLL_T::RTECH, NO_ERROR, "Couldn't read package file \"%s\".\n", pakFilePath);
|
|
//
|
|
// loadedInfo->status = PAK_STATUS_ERROR;
|
|
// return false;
|
|
// }
|
|
//
|
|
// loadedInfo->fileHandle = pakFileHandle;
|
|
//
|
|
// // file appears truncated/corrupt
|
|
// if (pak_file_size_var < sizeof(PakFileHeader_t))
|
|
// {
|
|
// loadedInfo->status = PAK_STATUS_ERROR;
|
|
// return false;
|
|
// }
|
|
//
|
|
// AsyncFile = v_FS_ReadAsyncFile(pakFileHandle, 0i64, sizeof(PakFileHeader_t), &pakHdr, 0i64, 0i64, 4);
|
|
// v_FS_WaitForAsyncRead(AsyncFile);
|
|
//
|
|
// size_t bytesProcessed = 0;
|
|
// const char* statusMsg = "(no reason)";
|
|
// const uint8_t currentStatus = g_pakLoadApi->CheckAsyncRequest(AsyncFile, &bytesProcessed, &statusMsg);
|
|
//
|
|
// if (currentStatus == AsyncHandleStatus_t::FS_ASYNC_ERROR)
|
|
// {
|
|
// Error(eDLL_T::RTECH, EXIT_FAILURE, "Error reading pak file \"%s\" -- %s\n", pak->memoryData.fileName, statusMsg);
|
|
//
|
|
// loadedInfo->status = PAK_STATUS_ERROR;
|
|
// return false;
|
|
// }
|
|
//
|
|
// if (pakHdr.magic != PAK_HEADER_MAGIC || pakHdr.version != PAK_HEADER_VERSION)
|
|
// {
|
|
// loadedInfo->status = PAK_STATUS_ERROR;
|
|
// return false;
|
|
// }
|
|
//
|
|
// if (pakHdr.flags & PAK_HEADER_FLAGS_HAS_MODULE_EXTENDED)
|
|
// {
|
|
// v22 = V_GetFileExtension(pakFilePath);
|
|
// v23 = v22 - pakFilePath;
|
|
//
|
|
// if ((unsigned __int64)(v22 - pakFilePath + 4) >= 0x2000)
|
|
// {
|
|
// loadedInfo->status = PAK_STATUS_ERROR;
|
|
// return false;
|
|
// }
|
|
//
|
|
// memcpy(LibFileName, pakFilePath, v22 - pakFilePath);
|
|
//
|
|
// *(_DWORD*)&LibFileName[v23] = *(_DWORD*)".dll";
|
|
// v86[v23] = '\0';
|
|
//
|
|
// const HMODULE hModule = LoadLibraryA(LibFileName);
|
|
// loadedInfo->hModule = hModule;
|
|
//
|
|
// if (!hModule)
|
|
// {
|
|
// loadedInfo->status = PAK_STATUS_ERROR;
|
|
// return false;
|
|
// }
|
|
// }
|
|
//
|
|
// loadedInfo->fileTime = pakHdr.fileTime;
|
|
// asset_entry_count_var = pakHdr.assetCount;
|
|
//
|
|
// loadedInfo->assetCount = pakHdr.assetCount;
|
|
//
|
|
// asset_entry_count_var = pakHdr.assetCount;
|
|
// patchIndex = pakHdr.patchIndex;
|
|
// memPageCount = pakHdr.memPageCount;
|
|
// virtualSegmentCount = pakHdr.virtualSegmentCount;
|
|
// v32 = *(unsigned int*)&pakHdr.unk2[4];
|
|
//
|
|
// loadedInfo->assetGuids = (PakGuid_t*)loadedInfo->allocator->Alloc(sizeof(PakGuid_t) * asset_entry_count_var, 8);
|
|
//
|
|
// size_t streamingFilesBuifSize = pakHdr.streamingFilesBufSize[STREAMING_SET_OPTIONAL] + pakHdr.streamingFilesBufSize[STREAMING_SET_MANDATORY];
|
|
//
|
|
// v81 = 8 * memPageCount;
|
|
//
|
|
// pak_file_size_var = streamingFilesBuifSize
|
|
// + ((_WORD)patchIndex != 0 ? 8 : 0)
|
|
// + 2
|
|
// * (patchIndex
|
|
// + 2
|
|
// * (pakHdr.relationsCounts
|
|
// + *(unsigned int*)pakHdr.unk2
|
|
// + 3 * memPageCount
|
|
// + 2 * (pakHdr.descriptorCount + pakHdr.guidDescriptorCount + 16i64 + 2 * (asset_entry_count_var + patchIndex + 4 * asset_entry_count_var + virtualSegmentCount))))
|
|
// + v32;
|
|
//
|
|
// v80 = 4 * asset_entry_count_var;
|
|
// v90 = pak_file_size_var + 2080;
|
|
// v33 = -((_DWORD)pak_file_size_var + 2080 + 4 * (_DWORD)asset_entry_count_var) & 7;
|
|
// v89 = v33;
|
|
// v34 = 4 * asset_entry_count_var + pak_file_size_var + 2080 + v33 + 8 * memPageCount + 12 * asset_entry_count_var;
|
|
// v35 = (-(4 * (_DWORD)asset_entry_count_var + (_DWORD)pak_file_size_var + 2080 + (_DWORD)v33 + 8 * (_DWORD)memPageCount + 12 * (_DWORD)asset_entry_count_var) & 7) + 4088i64;
|
|
//
|
|
// if ((pakHdr.flags & 0x100) != 0)
|
|
// {
|
|
// ringBufferStreamSize = PAK_DECODE_IN_RING_BUFFER_SIZE;
|
|
// ringBufferOutSize = PAK_DECODE_OUT_RING_BUFFER_SIZE;
|
|
//
|
|
// if (pakHdr.compressedSize < PAK_DECODE_IN_RING_BUFFER_SIZE && !(_WORD)patchIndex)
|
|
// ringBufferStreamSize = (pakHdr.compressedSize + PAK_DECODE_IN_RING_BUFFER_SMALL_MASK) & 0xFFFFFFFFFFFFF000ui64;
|
|
// }
|
|
// else
|
|
// {
|
|
// ringBufferStreamSize = 0i64;
|
|
// ringBufferOutSize = PAK_DECODE_IN_RING_BUFFER_SIZE;
|
|
// }
|
|
//
|
|
// if (ringBufferOutSize > pakHdr.decompressedSize && !(_WORD)patchIndex)
|
|
// ringBufferOutSize = (pakHdr.decompressedSize + PAK_DECODE_IN_RING_BUFFER_SMALL_MASK) & 0xFFFFFFFFFFFFF000ui64;
|
|
//
|
|
// pak = (PakFile_t*)AlignedMemAlloc()->Alloc(v34 + v35 + ringBufferOutSize + ringBufferStreamSize, 8);
|
|
//
|
|
// if (pak)
|
|
// {
|
|
// loadedInfo->pakFile = pak;
|
|
//
|
|
// *(_QWORD*)&pak->processedPageCount = 0i64;
|
|
// *(_QWORD*)&pak->patchCount = 0i64;
|
|
// *(_QWORD*)&pak->numProcessedPointers = 0i64;
|
|
// *(_QWORD*)&pak->memoryData.someAssetCount = 0i64;
|
|
//
|
|
// v40 = (_DWORD*)loadedInfo->allocator->Alloc(16i64 * pakHdr.guidDescriptorCount + 8, 8i64);
|
|
// loadedInfo->qword50 = v40;
|
|
//
|
|
// *v40 = 0;
|
|
//
|
|
// *(_DWORD*)(loadedInfo->qword50 + 4i64) = 0;
|
|
//
|
|
// pak->memoryData.pakHeader = pakHdr;
|
|
//
|
|
// pak->memoryData.pakId = pakId;
|
|
// pak->memoryData.qword2D0 = pak_file_size_var;
|
|
//
|
|
// JobID_t jobId = PAK_DEFAULT_JOB_GROUP_ID;
|
|
//
|
|
// if (pakHdr.assetCount)
|
|
// jobId = JT_BeginJobGroup(0);
|
|
//
|
|
// pak->memoryData.unkJobID = jobId;
|
|
//
|
|
//
|
|
// v43 = v81;
|
|
// v44 = (uint8_t**)((char*)pak + v89 + v90 + v80);
|
|
// pak->memoryData.memPageBuffers = v44;
|
|
// v45 = (PakAsset_t**)((char*)v44 + v43);
|
|
// pak->memoryData.ppAssetEntries = v45;
|
|
// pak->memoryData.qword2E0 = (int*)&v45[pakHdr.assetCount];
|
|
// p_patchHeader = &pak->memoryData.patchHeader;
|
|
// v47 = pak->memoryData.pakHeader.patchIndex;
|
|
//
|
|
// p_decompressedSize = (PakPatchFileHeader_t*)&pak->memoryData.patchHeader.decompressedSize;
|
|
// if (!v47)
|
|
// p_decompressedSize = &pak->memoryData.patchHeader;
|
|
// if (!v47)
|
|
// p_patchHeader = 0i64;
|
|
//
|
|
// pak->memoryData.patchDataHeader = (PakPatchDataHeader_t*)p_patchHeader;
|
|
// v49 = pak->memoryData.pakHeader.patchIndex;
|
|
// pak->memoryData.patchHeaders = p_decompressedSize;
|
|
// v50 = pak->memoryData.pakHeader.patchIndex;
|
|
// v51 = (unsigned __int16*)&p_decompressedSize[v49];
|
|
// pak->memoryData.patchIndices = v51;
|
|
// v52 = (char*)&v51[v50];
|
|
// v53 = &v52[pak->memoryData.pakHeader.streamingFilesBufSize[0]];
|
|
// pak->memoryData.streamingFilePaths[0] = v52;
|
|
// v54 = pak->memoryData.pakHeader.streamingFilesBufSize[1];
|
|
// pak->memoryData.streamingFilePaths[1] = v53;
|
|
// v55 = (PakSegmentHeader_t*)&v53[v54];
|
|
// v56 = pak->memoryData.pakHeader.virtualSegmentCount;
|
|
// pak->memoryData.segmentHeaders = v55;
|
|
// v57 = pak->memoryData.pakHeader.memPageCount;
|
|
// v58 = (PakPageHeader_t*)&v55[v56];
|
|
// pak->memoryData.pageHeaders = v58;
|
|
// descriptorCount = pak->memoryData.pakHeader.descriptorCount;
|
|
// v60 = (PakPage_t*)&v58[v57];
|
|
// pak->memoryData.virtualPointers = v60;
|
|
// assetCount = pak->memoryData.pakHeader.assetCount;
|
|
// v62 = (PakAsset_t*)&v60[descriptorCount];
|
|
// pak->memoryData.assetEntries = v62;
|
|
// guidDescriptorCount = pak->memoryData.pakHeader.guidDescriptorCount;
|
|
// v64 = (PakPage_t*)&v62[assetCount];
|
|
// pak->memoryData.guidDescriptors = v64;
|
|
// relationsCounts = pak->memoryData.pakHeader.relationsCounts;
|
|
// v66 = (uint32_t*)&v64[guidDescriptorCount];
|
|
// pak->memoryData.fileRelations = v66;
|
|
// v67 = *(unsigned int*)pak->memoryData.pakHeader.unk2;
|
|
// v68 = &v66[relationsCounts];
|
|
// *(_QWORD*)pak->memoryData.gap5E0 = v68;
|
|
// v69 = *(unsigned int*)&pak->memoryData.pakHeader.unk2[4];
|
|
// *(_QWORD*)&pak->memoryData.gap5E0[16] = 0i64;
|
|
// v70 = &v68[v67];
|
|
// *(_QWORD*)&pak->memoryData.gap5E0[8] = v70;
|
|
// v71 = (__int64)v70 + v69;
|
|
// *(_QWORD*)&pak->memoryData.gap5E0[24] = (char*)v70 + v69;
|
|
// if (pak->memoryData.pakHeader.patchIndex)
|
|
// {
|
|
// patchDataHeader = pak->memoryData.patchDataHeader;
|
|
// if (patchDataHeader->editStreamSize)
|
|
// {
|
|
// *(_QWORD*)&pak->memoryData.gap5E0[16] = v71;
|
|
// *(_QWORD*)&pak->memoryData.gap5E0[24] = v71 + (unsigned int)patchDataHeader->editStreamSize;
|
|
// }
|
|
// }
|
|
// v73 = PAK_DECODE_IN_RING_BUFFER_MASK;
|
|
//
|
|
// pak->memoryData.fileName = loadedInfo->fileName;
|
|
// pak->fileStream.qword0 = 0i64;
|
|
// pak->fileStream.fileSize = pakHdr.compressedSize;
|
|
// pak->fileStream.fileHandle = loadedInfo->fileHandle;
|
|
// loadedInfo->fileHandle = FS_ASYNC_FILE_INVALID;
|
|
//
|
|
// pak->fileStream.bufferMask = PAK_DECODE_IN_RING_BUFFER_MASK;
|
|
//
|
|
// ringBuffer = (uint8_t*)(((unsigned __int64)pak + v35 + v34) & 0xFFFFFFFFFFFFF000ui64);
|
|
//
|
|
// pak->fileStream.buffer = ringBuffer;
|
|
// pak->fileStream.numDataChunksProcessed = 0;
|
|
// pak->fileStream.numDataChunks = 0;
|
|
// pak->fileStream.fileReadStatus = 3;
|
|
// pak->fileStream.finishedLoadingPatches = 0;
|
|
// pak->fileStream.numLoadedFiles = 0;
|
|
// pak->fileStream.bytesStreamed = sizeof(PakFileHeader_t);
|
|
// pak->decompBuffer = &ringBuffer[ringBufferStreamSize];
|
|
// pak->inputBytePos = sizeof(PakFileHeader_t);
|
|
// pak->byte1F8 = 0;
|
|
// pak->byte1FD = 1;
|
|
// pak->isOffsetted_MAYBE = 0;
|
|
// pak->isCompressed = 0;
|
|
//
|
|
// // FINISHME: this means if the pak file is not encoded, but we should also
|
|
// // check on the zstd flags
|
|
// v75 = (pakHdr.flags & 0x100) == 0;
|
|
//
|
|
// pak->qword298 = 128i64;
|
|
//
|
|
// if (!v75)
|
|
// v73 = PAK_DECODE_OUT_RING_BUFFER_MASK;
|
|
//
|
|
// pak->maxCopySize = v73;
|
|
// memset(&pak->pakDecoder, 0, sizeof(pak->pakDecoder));
|
|
//
|
|
// pak->pakDecoder.outBufBytePos = 128i64;
|
|
// pak->pakDecoder.decompSize = 128i64;
|
|
// pak->memoryData.processedPatchedDataSize = 128i64;
|
|
// v76 = pakHdr.patchIndex;
|
|
// v77 = pakHdr.descriptorCount + 2 * (pakHdr.patchIndex + (unsigned __int64)pakHdr.assetCount + 4i64 * pakHdr.assetCount + pakHdr.virtualSegmentCount);
|
|
// v78 = pakHdr.memPageCount;
|
|
//
|
|
// pak->memoryData.field_2A8 = 0i64;
|
|
// pak->memoryData.patchData = 0i64;
|
|
// pak->memoryData.patchDataPtr = 0i64;
|
|
// pak->memoryData.bitBuf.m_dataBuf = 0i64;
|
|
// pak->memoryData.bitBuf.m_bitsAvailable = 0;
|
|
// pak->memoryData.patchDataOffset = 0;
|
|
// pak->memoryData.patchSrcSize = v82 + ((_WORD)v76 != 0 ? 8 : 0) + 2 * (v76 + 6 * v78 + 4 * v77);
|
|
// pak->memoryData.patchDstPtr = (char*)&pak->memoryData.patchHeader;
|
|
// pak->memoryData.patchFunc = g_pakPatchApi[0];
|
|
//
|
|
// LOBYTE(v2) = pakHdr.patchIndex == 0;
|
|
// pak->memoryData.numBytesToProcess_maybe = pak->memoryData.pakHeader.decompressedSize + v2 - 0x80;
|
|
//
|
|
// Pak_ProcessPakFile(pak);
|
|
// return true;
|
|
// }
|
|
// else
|
|
// {
|
|
// loadedInfo->status = PAK_STATUS_ERROR;
|
|
// return false;
|
|
// }
|
|
//
|
|
// return result;
|
|
//}
|
|
|
|
|
|
void V_PakParse::Detour(const bool bAttach) const
|
|
{
|
|
DetourSetup(&v_Pak_LoadAsync, &Pak_LoadAsync, bAttach);
|
|
DetourSetup(&v_Pak_UnloadAsync, &Pak_UnloadAsync, bAttach);
|
|
|
|
DetourSetup(&v_Pak_StartLoadingPak, &Pak_StartLoadingPak, bAttach);
|
|
|
|
DetourSetup(&v_Pak_ProcessPakFile, &Pak_ProcessPakFile, bAttach);
|
|
DetourSetup(&v_Pak_ResolveAssetRelations, &Pak_ResolveAssetRelations, bAttach);
|
|
DetourSetup(&v_Pak_ProcessAssets, &Pak_ProcessAssets, bAttach);
|
|
|
|
DetourSetup(&v_Pak_RunAssetLoadingJobs, &Pak_RunAssetLoadingJobs, bAttach);
|
|
}
|