r5sdk/r5dev/rtech/pak/pakparse.cpp
Kawe Mazidjatari f5a8ca3e7a RTech: fix numeric overflow caused by massive pak files
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.
2024-04-05 17:56:49 +02:00

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;