From 1fbc3291c741bd3d036b8c0e9df5cb48338dabe8 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Mon, 22 May 2023 12:32:49 +0200 Subject: [PATCH] Fix EOF read errors and PAKFILE_LUMP not loading correctly when discrete This commit adds support for loading cubemap lumps from the disk, several patches and hooks had to be implemented to make this work. Since the game was trying to read the pakfile lump from the BSP file (even when absent), it would read EOF (heavily reduces down map loading performance). --- r5dev/filesystem/basefilesystem.cpp | 44 ++++++++++++++++++++++++++++- r5dev/filesystem/basefilesystem.h | 7 +++++ r5dev/resource/patch/r5apex.patch | 37 ++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/r5dev/filesystem/basefilesystem.cpp b/r5dev/filesystem/basefilesystem.cpp index b8c4cdf5..0df4d53d 100644 --- a/r5dev/filesystem/basefilesystem.cpp +++ b/r5dev/filesystem/basefilesystem.cpp @@ -3,6 +3,9 @@ #include "filesystem/basefilesystem.h" #include "filesystem/filesystem.h" +#include "bspfile.h" +#include "engine/modelloader.h" + //--------------------------------------------------------------------------------- // Purpose: prints the output of the filesystem based on the warning level // Input : *this - @@ -13,7 +16,7 @@ void CBaseFileSystem::Warning(CBaseFileSystem* pFileSystem, FileWarningLevel_t l { if (level >= FileWarningLevel_t::FILESYSTEM_WARNING_REPORTALLACCESSES) { - // Logging reads is very verbose! Explicitly toggle.. + // Logging reads are very verbose! Explicitly toggle.. if (!fs_showAllReads->GetBool()) { return; @@ -101,6 +104,43 @@ bool CBaseFileSystem::VReadFromCache(CBaseFileSystem* pFileSystem, char* pszFile return result; } +//--------------------------------------------------------------------------------- +// Purpose: mounts a BSP packfile lump as search path +// Input : *this - +// *pPath - +// *pPathID - +// *addType - +//--------------------------------------------------------------------------------- +void CBaseFileSystem::VAddMapPackFile(CBaseFileSystem* pFileSystem, const char* pPath, const char* pPathID, SearchPathAdd_t addType) +{ + // Since the mounting of the packfile lump is performed before the BSP header + // is loaded and parsed, we have to do it here. The internal 'AddMapPackFile' + // function has been patched to load the fields in the global 's_MapHeader' + // field, instead of the one that is getting initialized (see r5apex.patch). + if (s_MapHeader->ident != IDBSPHEADER || s_MapHeader->version != BSPVERSION) + { + FileHandle_t hBspFile = FileSystem()->Open(pPath, "rb", pPathID); + if (hBspFile != FILESYSTEM_INVALID_HANDLE) + { + memset(s_MapHeader, '\0', sizeof(BSPHeader_t)); + FileSystem()->Read(s_MapHeader, sizeof(BSPHeader_t), hBspFile); + } + } + + // If a lump exists, replace the path pointer with that of the lump so that + // the internal function loads this instead. + char lumpPathBuf[MAX_PATH]; + V_snprintf(lumpPathBuf, sizeof(lumpPathBuf), "%s.%.4X.bsp_lump", pPath, LUMP_PAKFILE); + + // TODO[ AMOS ]: Attempt to read from cache first??? + if (FileSystem()->FileExists(lumpPathBuf, pPathID)) + { + pPath = lumpPathBuf; + } + + v_CBaseFileSystem_AddMapPackFile(pFileSystem, pPath, pPathID, addType); +} + //--------------------------------------------------------------------------------- // Purpose: attempts to mount VPK file for filesystem usage // Input : *this - @@ -173,6 +213,7 @@ void VBaseFileSystem::Attach() const DetourAttach((LPVOID*)&v_CBaseFileSystem_Warning, &CBaseFileSystem::Warning); DetourAttach((LPVOID*)&v_CBaseFileSystem_LoadFromVPK, &CBaseFileSystem::VReadFromVPK); DetourAttach((LPVOID*)&v_CBaseFileSystem_LoadFromCache, &CBaseFileSystem::VReadFromCache); + DetourAttach((LPVOID*)&v_CBaseFileSystem_AddMapPackFile, &CBaseFileSystem::VAddMapPackFile); DetourAttach((LPVOID*)&v_CBaseFileSystem_MountVPKFile, &CBaseFileSystem::VMountVPKFile); DetourAttach((LPVOID*)&v_CBaseFileSystem_UnmountVPKFile, &CBaseFileSystem::VUnmountVPKFile); } @@ -182,6 +223,7 @@ void VBaseFileSystem::Detach() const DetourDetach((LPVOID*)&v_CBaseFileSystem_Warning, &CBaseFileSystem::Warning); DetourDetach((LPVOID*)&v_CBaseFileSystem_LoadFromVPK, &CBaseFileSystem::VReadFromVPK); DetourDetach((LPVOID*)&v_CBaseFileSystem_LoadFromCache, &CBaseFileSystem::VReadFromCache); + DetourDetach((LPVOID*)&v_CBaseFileSystem_AddMapPackFile, &CBaseFileSystem::VAddMapPackFile); DetourDetach((LPVOID*)&v_CBaseFileSystem_MountVPKFile, &CBaseFileSystem::VMountVPKFile); DetourDetach((LPVOID*)&v_CBaseFileSystem_UnmountVPKFile, &CBaseFileSystem::VUnmountVPKFile); } diff --git a/r5dev/filesystem/basefilesystem.h b/r5dev/filesystem/basefilesystem.h index 3f8228c2..133ff60f 100644 --- a/r5dev/filesystem/basefilesystem.h +++ b/r5dev/filesystem/basefilesystem.h @@ -11,6 +11,7 @@ public: static bool VCheckDisk(const char* pszFilePath); static FileHandle_t VReadFromVPK(CBaseFileSystem* pFileSystem, FileHandle_t pResults, char* pszFilePath); static bool VReadFromCache(CBaseFileSystem* pFileSystem, char* pszFilePath, FileSystemCache* pCache); + static void VAddMapPackFile(CBaseFileSystem* pFileSystem, const char* pPath, const char* pPathID, SearchPathAdd_t addType); static VPKData_t* VMountVPKFile(CBaseFileSystem* pFileSystem, const char* pszVpkPath); static const char* VUnmountVPKFile(CBaseFileSystem* pFileSystem, const char* pszVpkPath); @@ -51,6 +52,9 @@ inline auto v_CBaseFileSystem_LoadFromVPK = p_CBaseFileSystem_LoadFromVPK.RCast< inline CMemory p_CBaseFileSystem_LoadFromCache; inline auto v_CBaseFileSystem_LoadFromCache = p_CBaseFileSystem_LoadFromCache.RCast(); +inline CMemory p_CBaseFileSystem_AddMapPackFile; +inline auto v_CBaseFileSystem_AddMapPackFile = p_CBaseFileSystem_AddMapPackFile.RCast(); + inline CMemory p_CBaseFileSystem_MountVPKFile; inline auto v_CBaseFileSystem_MountVPKFile = p_CBaseFileSystem_MountVPKFile.RCast(); @@ -70,6 +74,7 @@ class VBaseFileSystem : public IDetour LogFunAdr("CBaseFileSystem::Warning", p_CBaseFileSystem_Warning.GetPtr()); LogFunAdr("CBaseFileSystem::LoadFromVPK", p_CBaseFileSystem_LoadFromVPK.GetPtr()); LogFunAdr("CBaseFileSystem::LoadFromCache", p_CBaseFileSystem_LoadFromCache.GetPtr()); + LogFunAdr("CBaseFileSystem::AddMapPackFile", p_CBaseFileSystem_AddMapPackFile.GetPtr()); LogFunAdr("CBaseFileSystem::MountVPKFile", p_CBaseFileSystem_MountVPKFile.GetPtr()); LogFunAdr("CBaseFileSystem::UnmountVPKFile", p_CBaseFileSystem_UnmountVPKFile.GetPtr()); LogFunAdr("CBaseFileSystem::GetMountedVPKHandle", p_CBaseFileSystem_GetMountedVPKHandle.GetPtr()); @@ -80,6 +85,7 @@ class VBaseFileSystem : public IDetour p_CBaseFileSystem_Warning = g_GameDll.FindPatternSIMD("4C 89 4C 24 20 C3 CC CC CC CC CC CC CC CC CC CC 48"); p_CBaseFileSystem_LoadFromVPK = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 81 EC ?? ?? ?? ?? 49 8B C0 4C 8D 8C 24 ?? ?? ?? ??"); p_CBaseFileSystem_LoadFromCache = g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 49 8B D8"); + p_CBaseFileSystem_AddMapPackFile = g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 48 89 54 24 ?? 55 ?? 41 54 41 55 48 8D AC 24 ?? ?? ?? ??"); p_CBaseFileSystem_MountVPKFile = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 4C 8D 05 ?? ?? ?? ??"); p_CBaseFileSystem_UnmountVPKFile = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 48 8B DA 48 8B F9 48 8B CB 48 8D 15 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0"); p_CBaseFileSystem_GetMountedVPKHandle = g_GameDll.FindPatternSIMD("48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 4C 8D 05 ?? ?? ?? ??"); @@ -87,6 +93,7 @@ class VBaseFileSystem : public IDetour v_CBaseFileSystem_Warning = p_CBaseFileSystem_Warning.RCast(); /*4C 89 4C 24 20 C3 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 48*/ v_CBaseFileSystem_LoadFromVPK = p_CBaseFileSystem_LoadFromVPK.RCast(); /*48 89 5C 24 ?? 57 48 81 EC ?? ?? ?? ?? 49 8B C0 4C 8D 8C 24 ?? ?? ?? ??*/ v_CBaseFileSystem_LoadFromCache = p_CBaseFileSystem_LoadFromCache.RCast(); /*40 53 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 49 8B D8*/ + v_CBaseFileSystem_AddMapPackFile = p_CBaseFileSystem_AddMapPackFile.RCast(); v_CBaseFileSystem_MountVPKFile = p_CBaseFileSystem_MountVPKFile.RCast(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 4C 8D 05 ?? ?? ?? ??*/ v_CBaseFileSystem_UnmountVPKFile = p_CBaseFileSystem_UnmountVPKFile.RCast(); /*48 89 5C 24 ?? 57 48 83 EC 20 48 8B DA 48 8B F9 48 8B CB 48 8D 15 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0*/ v_CBaseFileSystem_GetMountedVPKHandle = p_CBaseFileSystem_GetMountedVPKHandle.RCast(); /*48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 4C 8D 05 ?? ?? ?? ??*/ diff --git a/r5dev/resource/patch/r5apex.patch b/r5dev/resource/patch/r5apex.patch index 0e2d0dec..753bf41d 100644 --- a/r5dev/resource/patch/r5apex.patch +++ b/r5dev/resource/patch/r5apex.patch @@ -2,6 +2,43 @@ // Base game executable patch file; this executable // contains all patches applied directly to the image. +///////////////////////////// +///////////////////////////// +//// Code adjustments //// +///////////////////////////// +///////////////////////////// +// This patch adds support for loading the 'LUMP_PAKFILE' lump as a discrete file. +// The function originally loaded the BSP, and stored the header in a local variable. +// This means that we need to store the BSP header in the lump too, if we want to load +// the packfile lump separately. To avoid this, we hook this function in SDK code, +// and load the BSP header into the static 's_MapHeader' memory. The patch below makes +// the function read the static fields instead, and uses the descritprs in the header to +// read out the lump path passed in. A separate tool sets the 'fileofs' field to 0, so the +// packfile lump can be loaded without any additional patching. +0x382152: "cmp dword ptr ds:[0x00000001666EC6C0], 0x50534272" // s_MapHeader.ident +0x382162: "mov eax, dword ptr ds:[0x00000001666EC6C4]" // s_MapHeader.version + +0x382174: "cmp dword ptr ds:[0x00000001666EC954], 0x10" // s_MapHeader.lumps[LUMP_PAKFILE].filelen +0x382181: "movsxd rcx, dword ptr ds:[0x00000001666EC950]" // s_MapHeader.lumps[LUMP_PAKFILE].fileofs + +0x382188: "mov rdx, rcx" // Move the operation done at 0x382181 (stored into RCX) into RDX; no need to index into it again. + +// Due to the simplification of the operation above, 2 bytes had to be nopped. +0x38218B: "nop" +0x38218C: "nop" + +0x38226E: "movsxd r8, dword ptr ds:[0x00000001666EC950]" // s_MapHeader.lumps[LUMP_PAKFILE].fileofs +0x382275: "movsxd rdx, dword ptr ds:[0x00000001666EC954]" // s_MapHeader.lumps[LUMP_PAKFILE].filelen + +0x38247B: "cmp dword ptr ds:[0x00000001666EC6C0], 0x50534272" // s_MapHeader.ident +0x38248B: "mov eax, dword ptr ds:[0x00000001666EC6C4]" // s_MapHeader.version + +0x38249D: "cmp dword ptr ds:[0x00000001666EC954], 0x10" // s_MapHeader.lumps[LUMP_PAKFILE].filelen +0x3824B6: "movsxd r8, dword ptr ds:[0x00000001666EC950]" // s_MapHeader.lumps[LUMP_PAKFILE].fileofs + +0x38257E: movsxd r8, dword ptr ds:[0x00000001666EC950] // s_MapHeader.lumps[LUMP_PAKFILE].fileofs +0x382585: movsxd rdx, dword ptr ds:[0x00000001666EC954] // s_MapHeader.lumps[LUMP_PAKFILE].filelen + ///////////////////////////// ///////////////////////////// //// Code defects ////