Use engine's FileSystem API for decompressing and patching RPak files

This commit is contained in:
Kawe Mazidjatari 2022-11-25 00:11:17 +01:00
parent 1e76624c9b
commit 57f17a3aad
2 changed files with 89 additions and 51 deletions

View File

@ -148,8 +148,8 @@ struct RPakHeader_t
uint32_t m_nMagic; // 'RPak'
uint16_t m_nVersion; // R2 = '7' R5 = '8'
uint8_t m_nFlags[0x2]; //
uint8_t m_nHash0[0x8]; //
uint8_t m_nHash1[0x8]; //
uint64_t m_nFileTime; //
uint64_t m_nHash; //
uint64_t m_nSizeDisk; // Compressed size
uint64_t m_nEmbeddedStarpakOffset; //
uint8_t unk0[0x8]; //

View File

@ -55,7 +55,6 @@
#ifndef DEDICATED
#include "game/client/viewrender.h"
#endif // !DEDICATED
#include "public/utility/binstream.h"
/*
@ -419,103 +418,142 @@ void RTech_Decompress_f(const CCommand& args)
return;
}
static const string modDir = "paks\\Win32\\";
static const string baseDir = "paks\\Win64\\";
static const string svModDir = "paks\\Win32\\";
static const string svBaseDir = "paks\\Win64\\";
const string pakNameOut = modDir + args.Arg(1);
const string pakNameIn = baseDir + args.Arg(1);
CreateDirectories(pakNameOut);
const string svPakNameOut = svModDir + args.Arg(1);
const string svPakNameIn = svBaseDir + args.Arg(1);
DevMsg(eDLL_T::RTECH, "______________________________________________________________\n");
DevMsg(eDLL_T::RTECH, "-+ RTech decompress ------------------------------------------\n");
if (!FileExists(pakNameIn))
if (!FileSystem()->FileExists(svPakNameIn.c_str(), "GAME"))
{
Error(eDLL_T::RTECH, NO_ERROR, "%s - pak file '%s' does not exist!\n", __FUNCTION__, pakNameIn.c_str());
Error(eDLL_T::RTECH, NO_ERROR, "%s - pak file '%s' does not exist!\n", __FUNCTION__, svPakNameIn.c_str());
return;
}
DevMsg(eDLL_T::RTECH, " |-+ Processing: '%s'\n", pakNameIn.c_str());
CIOStream reader(pakNameIn, CIOStream::Mode_t::READ);
DevMsg(eDLL_T::RTECH, " |-+ Processing: '%s'\n", svPakNameIn.c_str());
FileHandle_t hPakFile = FileSystem()->Open(svPakNameIn.c_str(), "rb", "GAME");
RPakHeader_t rheader = reader.Read<RPakHeader_t>();
uint16_t flags = (rheader.m_nFlags[0] << 8) | rheader.m_nFlags[1];
if (!hPakFile)
{
Error(eDLL_T::RTECH, NO_ERROR, "%s - Unable to open '%s' (insufficient rights?)\n", __FUNCTION__, svPakNameIn.c_str());
return;
}
uint32_t nPakLen = FileSystem()->Size(hPakFile);
uint8_t* pPakBuf = MemAllocSingleton()->Alloc<uint8_t>(nPakLen);
FileSystem()->Read(pPakBuf, nPakLen, hPakFile);
FileSystem()->Close(hPakFile);
RPakHeader_t* pHeader = reinterpret_cast<RPakHeader_t*>(pPakBuf);
uint32_t nLen = FileSystem()->Size(hPakFile);
uint16_t flags = (pHeader->m_nFlags[0] << 8) | pHeader->m_nFlags[1];
DevMsg(eDLL_T::RTECH, " | |-+ Header ------------------------------------------------\n");
DevMsg(eDLL_T::RTECH, " | |-- Magic : '%08X'\n", rheader.m_nMagic);
DevMsg(eDLL_T::RTECH, " | |-- Version : '%hu'\n", rheader.m_nVersion);
DevMsg(eDLL_T::RTECH, " | |-- Magic : '%08X'\n", pHeader->m_nMagic);
DevMsg(eDLL_T::RTECH, " | |-- Version : '%hu'\n", pHeader->m_nVersion);
DevMsg(eDLL_T::RTECH, " | |-- Flags : '%04hX'\n", flags);
DevMsg(eDLL_T::RTECH, " | |-- Hash : '%llu%llu'\n", rheader.m_nHash0, rheader.m_nHash1);
DevMsg(eDLL_T::RTECH, " | |-- Entries : '%u'\n", rheader.m_nAssetEntryCount);
DevMsg(eDLL_T::RTECH, " | |-- Hash : '%llu%llu'\n", pHeader->m_nFileTime, pHeader->m_nHash);
DevMsg(eDLL_T::RTECH, " | |-- Entries : '%u'\n", pHeader->m_nAssetEntryCount);
DevMsg(eDLL_T::RTECH, " | |-+ Compression -----------------------------------------\n");
DevMsg(eDLL_T::RTECH, " | |-- Size disk: '%llu'\n", rheader.m_nSizeDisk);
DevMsg(eDLL_T::RTECH, " | |-- Size decp: '%llu'\n", rheader.m_nSizeMemory);
DevMsg(eDLL_T::RTECH, " | |-- Size disk: '%llu'\n", pHeader->m_nSizeDisk);
DevMsg(eDLL_T::RTECH, " | |-- Size decp: '%llu'\n", pHeader->m_nSizeMemory);
if (rheader.m_nMagic != RPAKHEADER)
if (pHeader->m_nMagic != RPAKHEADER)
{
Error(eDLL_T::RTECH, NO_ERROR, "%s - pak file '%s' has invalid magic!\n", __FUNCTION__, pakNameIn.c_str());
Error(eDLL_T::RTECH, NO_ERROR, "%s - pak file '%s' has invalid magic!\n", __FUNCTION__, svPakNameIn.c_str());
MemAllocSingleton()->Free(pPakBuf);
return;
}
if ((rheader.m_nFlags[1] & 1) != 1)
if ((pHeader->m_nFlags[1] & 1) != 1)
{
Error(eDLL_T::RTECH, NO_ERROR, "%s - pak file '%s' already decompressed!\n", __FUNCTION__, pakNameIn.c_str());
Error(eDLL_T::RTECH, NO_ERROR, "%s - pak file '%s' already decompressed!\n", __FUNCTION__, svPakNameIn.c_str());
MemAllocSingleton()->Free(pPakBuf);
return;
}
if (rheader.m_nSizeDisk != reader.GetSize())
if (pHeader->m_nSizeDisk != nPakLen)
{
Error(eDLL_T::RTECH, NO_ERROR, "%s - pak file '%s' decompressed size '%zu' doesn't match expected size '%llu'!\n", __FUNCTION__, pakNameIn.c_str(), reader.GetSize(), rheader.m_nSizeMemory);
Error(eDLL_T::RTECH, NO_ERROR, "%s - pak file '%s' decompressed size '%llu' doesn't match expected size '%llu'!\n", __FUNCTION__, svPakNameIn.c_str(), nPakLen, pHeader->m_nSizeMemory);
MemAllocSingleton()->Free(pPakBuf);
return;
}
RPakDecompState_t state;
uint64_t decompSize = g_pRTech->DecompressPakFileInit(&state, const_cast<uint8_t*>(reader.GetData()), reader.GetSize(), NULL, sizeof(RPakHeader_t));
RPakDecompState_t decompState;
uint64_t nDecompSize = g_pRTech->DecompressPakFileInit(&decompState, pPakBuf, nPakLen, NULL, sizeof(RPakHeader_t));
if (decompSize == rheader.m_nSizeDisk)
if (nDecompSize == pHeader->m_nSizeDisk)
{
Error(eDLL_T::RTECH, NO_ERROR, "%s - calculated size: '%llu' expected: '%llu'!\n", __FUNCTION__, decompSize, rheader.m_nSizeMemory);
Error(eDLL_T::RTECH, NO_ERROR, "%s - calculated size: '%llu' expected: '%llu'!\n", __FUNCTION__, nDecompSize, pHeader->m_nSizeMemory);
MemAllocSingleton()->Free(pPakBuf);
return;
}
else
{
DevMsg(eDLL_T::RTECH, " | |-- Size calc: '%llu'\n", decompSize);
DevMsg(eDLL_T::RTECH, " | |-- Size calc: '%llu'\n", nDecompSize);
}
DevMsg(eDLL_T::RTECH, " | |-- Ratio : '%.02f'\n", (rheader.m_nSizeDisk * 100.f) / rheader.m_nSizeMemory);
vector<uint8_t> pakBuf(rheader.m_nSizeMemory, 0);
DevMsg(eDLL_T::RTECH, " | |-- Ratio : '%.02f'\n", (pHeader->m_nSizeDisk * 100.f) / pHeader->m_nSizeMemory);
uint8_t* pDecompBuf = MemAllocSingleton()->Alloc<uint8_t>(pHeader->m_nSizeMemory);
state.m_nOutMask = UINT64_MAX;
state.m_nOut = uint64_t(pakBuf.data());
decompState.m_nOutMask = UINT64_MAX;
decompState.m_nOut = uint64_t(pDecompBuf);
uint8_t decompResult = g_pRTech->DecompressPakFile(&state, reader.GetSize(), pakBuf.size());
if (decompResult != 1)
uint8_t nDecompResult = g_pRTech->DecompressPakFile(&decompState, nPakLen, pHeader->m_nSizeMemory);
if (nDecompResult != 1)
{
Error(eDLL_T::RTECH, NO_ERROR, "%s - decompression failed for '%s' return value: '%hu'!\n", __FUNCTION__, pakNameIn.c_str(), decompResult);
Error(eDLL_T::RTECH, NO_ERROR, "%s - decompression failed for '%s' return value: '%hu'!\n", __FUNCTION__, svPakNameIn.c_str(), nDecompResult);
MemAllocSingleton()->Free(pPakBuf);
MemAllocSingleton()->Free(pDecompBuf);
return;
}
rheader.m_nFlags[1] = 0x0; // Set compressed flag to false for the decompressed pak file.
rheader.m_nSizeDisk = rheader.m_nSizeMemory; // Equal compressed size with decompressed.
pHeader->m_nFlags[1] = 0x0; // Set compressed flag to false for the decompressed pak file.
pHeader->m_nSizeDisk = pHeader->m_nSizeMemory; // Equal compressed size with decompressed.
CIOStream writer(pakNameOut, CIOStream::Mode_t::WRITE);
FileSystem()->CreateDirHierarchy(svModDir.c_str(), "GAME");
FileHandle_t hDecompFile = FileSystem()->Open(svPakNameOut.c_str(), "wb", "GAME");
if (rheader.m_nPatchIndex > 0) // Check if its an patch rpak.
if (!hDecompFile)
{
Error(eDLL_T::RTECH, NO_ERROR, "%s - Unable to write to '%s' (read-only?)\n", __FUNCTION__, svPakNameOut.c_str());
MemAllocSingleton()->Free(pPakBuf);
MemAllocSingleton()->Free(pDecompBuf);
return;
}
if (pHeader->m_nPatchIndex > 0) // Check if its an patch rpak.
{
// Loop through all the structs and patch their compress size.
for (uint32_t i = 1, patchOffset = (sizeof(RPakHeader_t) + sizeof(uint64_t));
i <= rheader.m_nPatchIndex; i++, patchOffset += sizeof(RPakPatchCompressedHeader_t))
for (uint32_t i = 1, nPatchOffset = (sizeof(RPakHeader_t) + sizeof(uint64_t));
i <= pHeader->m_nPatchIndex; i++, nPatchOffset += sizeof(RPakPatchCompressedHeader_t))
{
RPakPatchCompressedHeader_t* patchHeader = (RPakPatchCompressedHeader_t*)((uintptr_t)pakBuf.data() + patchOffset);
patchHeader->m_nSizeDisk = patchHeader->m_nSizeMemory; // Fix size for decompress.
RPakPatchCompressedHeader_t* pPatchHeader = (RPakPatchCompressedHeader_t*)((uintptr_t)pPakBuf + nPatchOffset);
pPatchHeader->m_nSizeDisk = pPatchHeader->m_nSizeMemory; // Fix size for decompress.
}
}
memcpy_s(pakBuf.data(), state.m_nDecompSize, &rheader, sizeof(RPakHeader_t)); // Overwrite first 0x80 bytes which are NULL with the header data.
writer.Write(pakBuf.data(), state.m_nDecompSize);
memcpy_s(pDecompBuf, sizeof(RPakHeader_t), pPakBuf, sizeof(RPakHeader_t));// Overwrite first 0x80 bytes which are NULL with the header data.
FileSystem()->Write(pDecompBuf, decompState.m_nDecompSize, hDecompFile);
DevMsg(eDLL_T::RTECH, " | |-- Checksum : '%08X'\n", crc32::update(NULL, pakBuf.data(), state.m_nDecompSize));
DevMsg(eDLL_T::RTECH, " |-+ Decompressed pak file to: '%s'\n", pakNameOut.c_str());
DevMsg(eDLL_T::RTECH, " | |-- Checksum : '%08X'\n", crc32::update(NULL, pDecompBuf, decompState.m_nDecompSize));
DevMsg(eDLL_T::RTECH, " |-+ Decompressed pak file to: '%s'\n", svPakNameOut.c_str());
DevMsg(eDLL_T::RTECH, "--------------------------------------------------------------\n");
MemAllocSingleton()->Free(pPakBuf);
MemAllocSingleton()->Free(pDecompBuf);
FileSystem()->Close(hDecompFile);
}
/*