From 57f17a3aad73694fc1b7d7877e393ed454bfe829 Mon Sep 17 00:00:00 2001
From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com>
Date: Fri, 25 Nov 2022 00:11:17 +0100
Subject: [PATCH] Use engine's FileSystem API for decompressing and patching
 RPak files

---
 r5dev/rtech/rtech_utils.h  |   4 +-
 r5dev/vstdlib/callback.cpp | 136 ++++++++++++++++++++++++-------------
 2 files changed, 89 insertions(+), 51 deletions(-)

diff --git a/r5dev/rtech/rtech_utils.h b/r5dev/rtech/rtech_utils.h
index 2e8010e7..7e2061c9 100644
--- a/r5dev/rtech/rtech_utils.h
+++ b/r5dev/rtech/rtech_utils.h
@@ -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];                    //
diff --git a/r5dev/vstdlib/callback.cpp b/r5dev/vstdlib/callback.cpp
index 10aa25b4..4b6deaa9 100644
--- a/r5dev/vstdlib/callback.cpp
+++ b/r5dev/vstdlib/callback.cpp
@@ -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);
 }
 
 /*