mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Pak files above 4GB caused the async file system to throw an error about failing to load audio banks, removed casts and promoted everything correctly. Also removed irrelevant comments in the public pakfile header.
937 lines
34 KiB
C++
937 lines
34 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"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// resolve the target guid from lookuo table
|
|
//-----------------------------------------------------------------------------
|
|
static bool Pak_ResolveAssetDependency(const PakFile_t* 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_ASSETS)
|
|
return false;
|
|
|
|
currentIndex &= PAK_MAX_ASSETS_MASK;
|
|
currentGuid = g_pPakGlobals->assets[currentIndex].guid;
|
|
|
|
if (currentGuid == targetGuid)
|
|
return true;
|
|
}
|
|
|
|
UNREACHABLE();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// resolve guid relations for asset
|
|
//-----------------------------------------------------------------------------
|
|
void Pak_ResolveAssetRelations(PakFile_t* const pak, const PakAsset_t* const asset)
|
|
{
|
|
PakPage_t* const pGuidDescriptors = &pak->memoryData.guidDescriptors[asset->dependenciesIndex];
|
|
volatile uint32_t* v5 = reinterpret_cast<volatile uint32_t*>(*(reinterpret_cast<uint64_t*>(g_pPakGlobals) + 0x17 * (pak->memoryData.pakId & PAK_MAX_HANDLES_MASK) + 0x160212));
|
|
|
|
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_ASSETS_MASK;
|
|
const PakGuid_t currentGuid = g_pPakGlobals->assets[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_t* 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.qword2E0[a];
|
|
}
|
|
}
|
|
|
|
// finally write the pointer to the guid entry
|
|
*pCurrentGuid = g_pPakGlobals->assets[currentIndex].head;
|
|
}
|
|
}
|
|
|
|
uint32_t Pak_ProcessRemainingPagePointers(PakFile_t* const pak)
|
|
{
|
|
uint32_t processedPointers = 0;
|
|
|
|
for (processedPointers = pak->numProcessedPointers; processedPointers < pak->GetPointerCount(); ++processedPointers)
|
|
{
|
|
PakPage_t* 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_t* const ptr = reinterpret_cast<PakPage_t*>(pak->GetPointerForPageOffset(curPage));
|
|
ptr->ptr = pak->memoryData.memPageBuffers[ptr->index] + ptr->offset;
|
|
}
|
|
|
|
return processedPointers;
|
|
}
|
|
|
|
void __fastcall Rebuild_14043E030(PakFile_t* const pak)
|
|
{
|
|
__int64 numAssets; // rsi
|
|
PakAsset_t* pakAsset; // rdi
|
|
__int64 _numAssets; // r14
|
|
unsigned int assetBind; // ebp
|
|
__int64 v13; // rcx
|
|
__int64 qword2D8_low; // rdi
|
|
JobID_t qword2D8_high; // ebp
|
|
JobTypeID_t v16; // si
|
|
|
|
pak->numProcessedPointers = Pak_ProcessRemainingPagePointers(pak);
|
|
|
|
numAssets = (unsigned int)pak->processedAssetCount;
|
|
if ((_DWORD)numAssets != pak->memoryData.pakHeader.assetCount)
|
|
{
|
|
pakAsset = &pak->memoryData.assetEntries[numAssets];
|
|
_numAssets = (unsigned int)numAssets;
|
|
if ((int)pakAsset->pageEnd <= pak->processedPageCount)
|
|
{
|
|
while ((unsigned __int16)*word_167ED7BDE <= 0xC8u)
|
|
{
|
|
assetBind = pakAsset->HashTableIndexForAssetType();
|
|
pak->memoryData.qword2E0[_numAssets] = (int)sub_14043D3C0(pak, pakAsset);
|
|
_InterlockedIncrement16(word_167ED7BDE);
|
|
v13 = assetBind;
|
|
if (g_pPakGlobals->assetBindings[(unsigned __int64)assetBind].loadAssetFunc)
|
|
{
|
|
qword2D8_low = pak->memoryData.pakId;
|
|
qword2D8_high = pak->memoryData.unkJobID;
|
|
v16 = *((_BYTE*)&*g_pPakGlobals + v13 + 13207872);
|
|
|
|
JTGuts_AddJob(v16, qword2D8_high, (void*)qword2D8_low, (void*)_numAssets);
|
|
}
|
|
else
|
|
{
|
|
if (_InterlockedExchangeAdd16((volatile signed __int16*)&pakAsset->numRemainingDependencies, 0xFFFFu) == 1)
|
|
sub_14043D150(pak, pakAsset, (unsigned int)numAssets, assetBind);
|
|
_InterlockedDecrement16(word_167ED7BDE);
|
|
}
|
|
numAssets = (unsigned int)++pak->processedAssetCount;
|
|
if ((_DWORD)numAssets == pak->memoryData.pakHeader.assetCount)
|
|
{
|
|
JT_EndJobGroup(pak->memoryData.unkJobID);
|
|
return;
|
|
}
|
|
_numAssets = (unsigned int)numAssets;
|
|
pakAsset = &pak->memoryData.assetEntries[numAssets];
|
|
|
|
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 = INVALID_PAK_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 == INVALID_PAK_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_t* const pakInfo = Pak_GetPakInfo(handle);
|
|
|
|
if (pakInfo && 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_t* const pak)
|
|
{
|
|
PakFileHeader_t* pakHeader; // r8
|
|
PakFileStream_t* fileStream; // rsi
|
|
PakMemoryData_t* memoryData; // r14
|
|
__int64 dwordB8; // rcx
|
|
uint32_t v6; // eax
|
|
__int64 v7; // rax
|
|
char v8; // r13
|
|
uint64_t bytesProcessed; // r12
|
|
unsigned __int64 v16; // r9
|
|
unsigned __int8 v17; // cl
|
|
unsigned __int64 v18; // r8
|
|
uint8_t byte1F8; // al
|
|
uint8_t byte1FD; // cl
|
|
PakFileStream_t::Descriptor* v22; // rdi
|
|
uint64_t dataOffset; // rax
|
|
PakDecoder_t* decodeContext; // rbp
|
|
uint64_t decompressedSize; // rax
|
|
uint64_t compressedSize; // rdx
|
|
uint64_t qword1D0; // rcx
|
|
__int64 v28; // rax
|
|
unsigned int numBitsRemaining; // r8d
|
|
int v35; // ecx
|
|
int v39; // r10d
|
|
int v40; // r9d
|
|
uint64_t v42; // ecx
|
|
unsigned int v43; // r8d
|
|
uint64_t v44; // r12d
|
|
char byteBC; // r15
|
|
__int64 v46; // rbp
|
|
__int64 v47; // r8
|
|
unsigned __int64 v48; // rbp
|
|
unsigned __int64 qword8; // rax
|
|
__int64 patchCount; // rcx
|
|
char c; // al
|
|
char* it; // rcx
|
|
char* i; // rdx
|
|
int v56; // edi
|
|
unsigned __int64 v58; // rdx
|
|
char pakPatchPath[MAX_PATH]; // [rsp+40h] [rbp-148h] BYREF
|
|
unsigned __int64 v62; // [rsp+190h] [rbp+8h]
|
|
size_t numBytesToProcess; // [rsp+198h] [rbp+10h] BYREF
|
|
|
|
fileStream = &pak->fileStream;
|
|
memoryData = &pak->memoryData;
|
|
dwordB8 = (unsigned int)pak->fileStream.dwordB8;
|
|
|
|
if ((_DWORD)dwordB8)
|
|
v62 = dwordB8 << 19;
|
|
else
|
|
v62 = sizeof(PakFileHeader_t);
|
|
|
|
v6 = fileStream->unsigned_intB4;
|
|
if (v6 != dwordB8)
|
|
{
|
|
while (1)
|
|
{
|
|
v7 = v6 & 0x1F;
|
|
v8 = fileStream->gap94[v7];
|
|
if (v8 != 1)
|
|
break;
|
|
LABEL_17:
|
|
v6 = fileStream->unsigned_intB4 + 1;
|
|
fileStream->unsigned_intB4 = v6;
|
|
if (v6 == fileStream->dwordB8)
|
|
goto LABEL_18;
|
|
}
|
|
|
|
const char* statusMsg = "(no reason)";
|
|
const uint8_t currentStatus = g_pakLoadApi->CheckAsyncRequest((unsigned char)fileStream->gap14[v7], &bytesProcessed, &statusMsg);
|
|
|
|
if (currentStatus == AsyncHandleStatus_t::FS_ASYNC_PENDING)
|
|
goto LABEL_18;
|
|
|
|
if (currentStatus == AsyncHandleStatus_t::FS_ASYNC_ERROR)
|
|
Error(eDLL_T::RTECH, EXIT_FAILURE, "Error reading pak file \"%s\" -- %s\n", pak->memoryData.fileName, statusMsg);
|
|
|
|
fileStream->bytesStreamed += bytesProcessed;
|
|
if (v8)
|
|
{
|
|
pakHeader = &pak->memoryData.pakHeader;
|
|
v16 = (unsigned __int64)fileStream->unsigned_intB4 << 19;
|
|
v17 = fileStream->byteBF++ & PAK_MAX_ASYNC_STREAMED_LOAD_REQUESTS_MASK;
|
|
|
|
if (v8 == 2)
|
|
{
|
|
v18 = v16 & fileStream->bufferMask;
|
|
fileStream->bytesStreamed = bytesProcessed + v16;
|
|
pakHeader = (PakFileHeader_t*)&fileStream->buffer[v18];
|
|
}
|
|
|
|
fileStream->descriptors[v17].dataOffset = v16 + sizeof(PakFileHeader_t);
|
|
fileStream->descriptors[v17].compressedSize = v16 + pakHeader->compressedSize;
|
|
fileStream->descriptors[v17].decompressedSize = pakHeader->decompressedSize;
|
|
fileStream->descriptors[v17].isCompressed = pakHeader->IsCompressed();
|
|
}
|
|
goto LABEL_17;
|
|
}
|
|
LABEL_18:
|
|
byte1F8 = pak->byte1F8;
|
|
if (byte1F8 != fileStream->byteBF)
|
|
{
|
|
byte1FD = pak->byte1FD;
|
|
const bool useZStream = pak->GetHeader().flags & PAK_HEADER_FLAGS_ZSTREAM;
|
|
|
|
do
|
|
{
|
|
v22 = &fileStream->descriptors[byte1F8 & PAK_MAX_ASYNC_STREAMED_LOAD_REQUESTS_MASK];
|
|
|
|
if (byte1FD)
|
|
{
|
|
pak->byte1FD = 0;
|
|
pak->inputBytePos = v22->dataOffset;
|
|
|
|
if (v22->isCompressed)
|
|
{
|
|
pak->isOffsetted_MAYBE = 0;
|
|
pak->isCompressed = 1;
|
|
dataOffset = sizeof(PakFileHeader_t);
|
|
}
|
|
else
|
|
{
|
|
pak->isOffsetted_MAYBE = 1;
|
|
pak->isCompressed = 0;
|
|
dataOffset = v22->dataOffset;
|
|
}
|
|
|
|
memoryData->processedPatchedDataSize = dataOffset;
|
|
|
|
if (!pak->isCompressed)
|
|
{
|
|
LABEL_35:
|
|
compressedSize = v22->compressedSize;
|
|
qword1D0 = compressedSize;
|
|
if (fileStream->bytesStreamed < compressedSize)
|
|
qword1D0 = fileStream->bytesStreamed;
|
|
goto LABEL_41;
|
|
}
|
|
|
|
decodeContext = &pak->pakDecoder;
|
|
|
|
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_t)),
|
|
v22->dataOffset - sizeof(PakFileHeader_t), sizeof(PakFileHeader_t), useZStream);
|
|
|
|
if (decompressedSize != v22->decompressedSize)
|
|
Error(eDLL_T::RTECH, EXIT_FAILURE,
|
|
"Error reading pak file \"%s\" -- decompressed size %zu doesn't match expected value %zu\n",
|
|
pak->memoryData.fileName,
|
|
decompressedSize,
|
|
pak->memoryData.pakHeader.decompressedSize);
|
|
}
|
|
else
|
|
{
|
|
decodeContext = &pak->pakDecoder;
|
|
}
|
|
|
|
if (!pak->isCompressed)
|
|
goto LABEL_35;
|
|
|
|
qword1D0 = pak->pakDecoder.outBufBytePos;
|
|
|
|
if (qword1D0 != pak->pakDecoder.decompSize)
|
|
{
|
|
const bool didDecode = Pak_StreamToBufferDecode(decodeContext, fileStream->bytesStreamed, memoryData->processedPatchedDataSize + PAK_DECODE_OUT_RING_BUFFER_SIZE, useZStream);
|
|
|
|
qword1D0 = pak->pakDecoder.outBufBytePos;
|
|
pak->inputBytePos = pak->pakDecoder.inBufBytePos;
|
|
|
|
if (didDecode)
|
|
DevMsg(eDLL_T::RTECH, "%s: pak '%s' decoded successfully\n", __FUNCTION__, pak->GetName());
|
|
}
|
|
|
|
compressedSize = v22->compressedSize;
|
|
|
|
LABEL_41:
|
|
if (pak->inputBytePos != compressedSize || memoryData->processedPatchedDataSize != qword1D0)
|
|
goto LABEL_45;
|
|
|
|
byte1FD = 1;
|
|
byte1F8 = pak->byte1F8 + 1;
|
|
pak->byte1FD = 1;
|
|
pak->byte1F8 = byte1F8;
|
|
} while (byte1F8 != fileStream->byteBF);
|
|
}
|
|
|
|
qword1D0 = memoryData->processedPatchedDataSize;
|
|
LABEL_45:
|
|
v28 = memoryData->field_2A8;
|
|
numBytesToProcess = qword1D0 - memoryData->processedPatchedDataSize;
|
|
|
|
if (memoryData->patchSrcSize + v28)
|
|
{
|
|
do
|
|
{
|
|
// if there are no bytes left to process in this patch operation
|
|
if (!memoryData->numBytesToProcess_maybe)
|
|
{
|
|
RBitRead& bitbuf = memoryData->bitBuf;
|
|
|
|
numBitsRemaining = bitbuf.m_bitsRemaining; // number of "free" bits in the bitbuf (how many to fetch)
|
|
|
|
// fetch remaining bits
|
|
bitbuf.m_dataBuf |= *(_QWORD*)memoryData->patchData << (64 - (unsigned __int8)numBitsRemaining);
|
|
|
|
// advance patch data buffer by the number of bytes that have just been fetched
|
|
memoryData->patchData = &memoryData->patchData[numBitsRemaining >> 3];
|
|
|
|
// store the number of bits remaining to complete the data read
|
|
bitbuf.m_bitsRemaining = numBitsRemaining & 7; // number of bits above a whole byte
|
|
|
|
const unsigned __int8 index1 = static_cast<unsigned __int8>(bitbuf.ReadBits(6));
|
|
v35 = memoryData->PATCH_field_68[index1]; // number of bits to discard from bitbuf
|
|
__int8 cmd = memoryData->patchCommands[index1];
|
|
|
|
bitbuf.DiscardBits(v35);
|
|
|
|
// get the next patch function to execute
|
|
memoryData->patchFunc = g_pakPatchApi[cmd];
|
|
|
|
if (cmd <= 3u)
|
|
{
|
|
const unsigned __int8 index2 = static_cast<unsigned __int8>(bitbuf.ReadBits(8));
|
|
v39 = memoryData->PATCH_unk3[index2];
|
|
v40 = memoryData->PATCH_unk2[index2]; // number of stored bits for the data size
|
|
|
|
bitbuf.DiscardBits(v39);
|
|
|
|
memoryData->numBytesToProcess_maybe = (1ull << v40) + bitbuf.ReadBits(v40);
|
|
|
|
bitbuf.DiscardBits(v40);
|
|
}
|
|
else
|
|
{
|
|
memoryData->numBytesToProcess_maybe = s_patchCmdToBytesToProcess[cmd];
|
|
}
|
|
}
|
|
|
|
} while (pak->memoryData.patchFunc(pak, &numBytesToProcess) && memoryData->patchSrcSize + memoryData->field_2A8);
|
|
}
|
|
|
|
if (pak->isOffsetted_MAYBE)
|
|
pak->inputBytePos = memoryData->processedPatchedDataSize;
|
|
|
|
if (!fileStream->finishedLoadingPatches)
|
|
{
|
|
v42 = fileStream->unsigned_intB4;
|
|
v43 = fileStream->dwordB8;
|
|
|
|
if ((unsigned int)(pak->inputBytePos >> 19) < v42)
|
|
v42 = pak->inputBytePos >> 19;
|
|
|
|
v44 = v42 + 32;
|
|
|
|
if (v43 != v42 + 32)
|
|
{
|
|
while (1)
|
|
{
|
|
byteBC = fileStream->byteBC;
|
|
v46 = v43;
|
|
v47 = v43 & 0x1F;
|
|
v48 = (v46 + 1) << 19;
|
|
|
|
if (byteBC == 1)
|
|
break;
|
|
|
|
qword8 = fileStream->qword8;
|
|
if (v62 < qword8)
|
|
{
|
|
if (v48 < qword8)
|
|
qword8 = v48;
|
|
|
|
fileStream->gap14[v47] = v_FS_ReadAsyncFile(
|
|
fileStream->fileHandle,
|
|
v62 - fileStream->qword0,
|
|
qword8 - v62,
|
|
&fileStream->buffer[v62 & fileStream->bufferMask],
|
|
0i64,
|
|
0i64,
|
|
4);
|
|
|
|
fileStream->gap94[v47] = byteBC;
|
|
fileStream->byteBC = 0;
|
|
goto LABEL_65;
|
|
}
|
|
|
|
if (pak->patchCount >= pak->memoryData.pakHeader.patchIndex)
|
|
{
|
|
FS_CloseAsyncFile((short)fileStream->fileHandle);
|
|
fileStream->fileHandle = INVALID_PAK_HANDLE;
|
|
fileStream->qword0 = 0i64;
|
|
fileStream->finishedLoadingPatches = true;
|
|
|
|
return memoryData->patchSrcSize == 0;
|
|
}
|
|
|
|
if (!pak->dword14)
|
|
return memoryData->patchSrcSize == 0;
|
|
|
|
sprintf(pakPatchPath, PLATFORM_PAK_PATH"%s", pak->memoryData.fileName);
|
|
patchCount = pak->patchCount++;
|
|
|
|
// get path of next patch rpak to load
|
|
if (pak->memoryData.patchIndices[patchCount])
|
|
{
|
|
c = pakPatchPath[0];
|
|
it = pakPatchPath;
|
|
|
|
for (i = nullptr; c; ++it)
|
|
{
|
|
if (c == '.')
|
|
{
|
|
i = it;
|
|
}
|
|
else if (c == '\\' || c == '/')
|
|
{
|
|
i = nullptr;
|
|
}
|
|
c = it[1];
|
|
}
|
|
if (i)
|
|
it = i;
|
|
|
|
// replace extension '.rpak' with '(xx).rpak'
|
|
snprintf(it, &pakPatchPath[sizeof(pakPatchPath)] - it,
|
|
"(%02u).rpak", pak->memoryData.patchIndices[patchCount]);
|
|
}
|
|
|
|
v56 = FS_OpenAsyncFile(pakPatchPath, 5i64, &numBytesToProcess);
|
|
|
|
if (v56 == FS_ASYNC_FILE_INVALID)
|
|
Error(eDLL_T::RTECH, EXIT_FAILURE, "Couldn't open file \"%s\".\n", pakPatchPath);
|
|
|
|
if (numBytesToProcess < pak->memoryData.patchHeaders[patchCount].compressedSize)
|
|
Error(eDLL_T::RTECH, EXIT_FAILURE, "File \"%s\" appears truncated; read size: %zu < expected size: %zu.\n",
|
|
pakPatchPath, numBytesToProcess, pak->memoryData.patchHeaders[patchCount].compressedSize);
|
|
|
|
FS_CloseAsyncFile((short)fileStream->fileHandle);
|
|
|
|
v43 = fileStream->dwordB8;
|
|
fileStream->fileHandle = v56;
|
|
v58 = (unsigned __int64)((v43 + 7) & 0xFFFFFFF8) << 19;
|
|
fileStream->qword0 = v58;
|
|
fileStream->byteBC = (v43 == ((v43 + 7) & 0xFFFFFFF8)) + 1;
|
|
fileStream->qword8 = v58 + pak->memoryData.patchHeaders[patchCount].compressedSize;
|
|
LABEL_84:
|
|
if (v43 == v44)
|
|
return memoryData->patchSrcSize == 0;
|
|
}
|
|
|
|
fileStream->gap14[v47] = -2;
|
|
fileStream->gap94[v47] = 1;
|
|
|
|
if ((((_BYTE)v47 + 1) & 7) == 0)
|
|
fileStream->byteBC = 2;
|
|
|
|
LABEL_65:
|
|
v43 = ++fileStream->dwordB8;
|
|
v62 = v48;
|
|
goto LABEL_84;
|
|
}
|
|
}
|
|
|
|
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_t* a1, PakFile_t* pak)
|
|
{
|
|
Rebuild_14043E030(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_t* 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_t* 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_pPakGlobals->assetBindings[assetTypeIdx].nativeClassSize;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Pak_ProcessAssets(PakLoadedInfo_t* const a1)
|
|
{
|
|
PakFile_t* 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_t* 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_t* 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_t* pagePtr = reinterpret_cast<PakPage_t*>(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_t* descriptor = &pak->memoryData.guidDescriptors[asset->dependenciesIndex + j];
|
|
|
|
if (descriptor->index == shiftedPageIndex)
|
|
descriptor->offset += offsetSize;
|
|
}
|
|
|
|
const uint32_t v16 = ++pak->memoryData.someAssetCount;
|
|
|
|
PakAsset_t* 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_pPakGlobals->assetBindings[assetTypeIdx].nativeClassSize;
|
|
}
|
|
else
|
|
{
|
|
bool res = SetupNextPageForPatching(a1, pak);
|
|
|
|
if (res)
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!JT_IsJobDone(pak->memoryData.unkJobID))
|
|
return false;
|
|
|
|
uint32_t i = 0;
|
|
PakAsset_t* pAsset = nullptr;
|
|
|
|
for (int j = pak->memoryData.pakId & PAK_MAX_HANDLES_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 v36 = pak->memoryData.qword2E0[i];
|
|
|
|
if (dword_167A40B3C[6 * g_pPakGlobals->assets[v36].unk_8] == j)
|
|
{
|
|
if (*qword_167ED7BC8)
|
|
{
|
|
uint64_t v38 = 0;
|
|
if ((*qword_167ED7BC8)->unk_0)
|
|
{
|
|
int* v39 = (*qword_167ED7BC8)->unk_array_9D410;
|
|
while (*v39 != v36)
|
|
{
|
|
++v38;
|
|
++v39;
|
|
if (v38 >= (*qword_167ED7BC8)->unk_0)
|
|
goto LABEL_41;
|
|
}
|
|
goto LABEL_42;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//printf("allocating thing\n");
|
|
*qword_167ED7BC8 = reinterpret_cast<UnknownPakStruct_t*>(AlignedMemAlloc()->Alloc(0x11D410, 8));
|
|
(*qword_167ED7BC8)->unk_0 = 0;
|
|
(*qword_167ED7BC8)->unk_4 = 0;
|
|
(*qword_167ED7BC8)->unk_8 = 0;
|
|
}
|
|
LABEL_41:
|
|
(*qword_167ED7BC8)->unk_array_9D410[(*qword_167ED7BC8)->unk_0] = v36;
|
|
++(*qword_167ED7BC8)->unk_0;
|
|
}
|
|
}
|
|
LABEL_42:
|
|
++i;
|
|
}
|
|
if (*qword_167ED7BC8)
|
|
sub_14043D870(a1, 0);
|
|
a1->status = EPakStatus::PAK_STATUS_LOADED;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Pak_StubInvalidAssetBinds(PakFile_t* const pak, PakSegmentDescriptor_t* const desc)
|
|
{
|
|
for (uint32_t i = 0; i < pak->GetAssetCount(); ++i)
|
|
{
|
|
PakAsset_t* const asset = &pak->memoryData.assetEntries[i];
|
|
pak->memoryData.ppAssetEntries[i] = asset;
|
|
|
|
const uint8_t assetTypeIndex = asset->HashTableIndexForAssetType();
|
|
desc->assetTypeCount[assetTypeIndex]++;
|
|
|
|
PakAssetBinding_t* const assetBinding = &g_pPakGlobals->assetBindings[assetTypeIndex];
|
|
|
|
if (assetBinding->type == PakAssetBinding_t::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_t::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_t* const loadedInfo)
|
|
{
|
|
PakFile_t* const pakFile = loadedInfo->pakFile;
|
|
|
|
if (pakFile->memoryData.patchSrcSize && !Pak_ProcessPakFile(pakFile))
|
|
return false;
|
|
|
|
PakSegmentDescriptor_t 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_t& pakHdr = pakFile->GetHeader();
|
|
|
|
if (*g_pUseAssetStreamingSystem)
|
|
{
|
|
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_t& memoryData = pakFile->memoryData;
|
|
|
|
memoryData.patchSrcSize = pakFile->memoryData.qword2D0 - patchDestOffset;
|
|
memoryData.patchDstPtr = (char*)&pakHdr + patchDestOffset;
|
|
|
|
loadedInfo->status = EPakStatus::PAK_STATUS_LOAD_PAKHDR;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
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(&sub_14043E030, &Rebuild_14043E030, bAttach);
|
|
}
|
|
|
|
// Symbols taken from R2 dll's.
|
|
PakLoadFuncs_t* g_pakLoadApi = nullptr;
|