From 25f763ec55de142711ded9989556227691ef285f Mon Sep 17 00:00:00 2001 From: illusion0001 <37698908+illusion0001@users.noreply.github.com> Date: Tue, 2 Jul 2024 01:52:49 -0500 Subject: [PATCH] Crysis 3 Benchmark Patch --- Windows-Game-Patches-x64.sln | 14 ++ .../Crysis3Remastered.Patches.vcxproj | 122 ++++++++++++ .../Crysis3Remastered.Patches.vcxproj.filters | 41 +++++ source/Crysis3Remastered.Patches/IConsole.h | 15 ++ source/Crysis3Remastered.Patches/dllmain.cpp | 173 ++++++++++++++++++ 5 files changed, 365 insertions(+) create mode 100644 source/Crysis3Remastered.Patches/Crysis3Remastered.Patches.vcxproj create mode 100644 source/Crysis3Remastered.Patches/Crysis3Remastered.Patches.vcxproj.filters create mode 100644 source/Crysis3Remastered.Patches/IConsole.h create mode 100644 source/Crysis3Remastered.Patches/dllmain.cpp diff --git a/Windows-Game-Patches-x64.sln b/Windows-Game-Patches-x64.sln index dc85056..e4c4602 100644 --- a/Windows-Game-Patches-x64.sln +++ b/Windows-Game-Patches-x64.sln @@ -47,6 +47,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RDR3.Patches", "source\RDR3 EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KittyHawk.Patches", "source\KittyHawk.Patches\KittyHawk.Patches.vcxproj", "{0E5D915F-4905-4496-8024-648E215FC947}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Crysis3Remastered.Patches", "source\Crysis3Remastered.Patches\Crysis3Remastered.Patches.vcxproj", "{426FD79A-652A-4ECA-8146-FA8DD65EDA57}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -321,6 +323,18 @@ Global {0E5D915F-4905-4496-8024-648E215FC947}.Release|x64.Build.0 = Release|x64 {0E5D915F-4905-4496-8024-648E215FC947}.Release|x86.ActiveCfg = Release|x64 {0E5D915F-4905-4496-8024-648E215FC947}.Release|x86.Build.0 = Release|x64 + {426FD79A-652A-4ECA-8146-FA8DD65EDA57}.Debug|Win32.ActiveCfg = Debug|x64 + {426FD79A-652A-4ECA-8146-FA8DD65EDA57}.Debug|Win32.Build.0 = Debug|x64 + {426FD79A-652A-4ECA-8146-FA8DD65EDA57}.Debug|x64.ActiveCfg = Debug|x64 + {426FD79A-652A-4ECA-8146-FA8DD65EDA57}.Debug|x64.Build.0 = Debug|x64 + {426FD79A-652A-4ECA-8146-FA8DD65EDA57}.Debug|x86.ActiveCfg = Debug|x64 + {426FD79A-652A-4ECA-8146-FA8DD65EDA57}.Debug|x86.Build.0 = Debug|x64 + {426FD79A-652A-4ECA-8146-FA8DD65EDA57}.Release|Win32.ActiveCfg = Release|x64 + {426FD79A-652A-4ECA-8146-FA8DD65EDA57}.Release|Win32.Build.0 = Release|x64 + {426FD79A-652A-4ECA-8146-FA8DD65EDA57}.Release|x64.ActiveCfg = Release|x64 + {426FD79A-652A-4ECA-8146-FA8DD65EDA57}.Release|x64.Build.0 = Release|x64 + {426FD79A-652A-4ECA-8146-FA8DD65EDA57}.Release|x86.ActiveCfg = Release|x64 + {426FD79A-652A-4ECA-8146-FA8DD65EDA57}.Release|x86.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/source/Crysis3Remastered.Patches/Crysis3Remastered.Patches.vcxproj b/source/Crysis3Remastered.Patches/Crysis3Remastered.Patches.vcxproj new file mode 100644 index 0000000..d380fa2 --- /dev/null +++ b/source/Crysis3Remastered.Patches/Crysis3Remastered.Patches.vcxproj @@ -0,0 +1,122 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {426FD79A-652A-4ECA-8146-FA8DD65EDA57} + Crysis3RemasteredPatches + 10.0.22621.0 + Crysis3Remastered.Patches + + + + DynamicLibrary + true + ClangCL + Unicode + + + DynamicLibrary + false + ClangCL + true + Unicode + + + + + + + + + + + + + + + \..\include\;$(IncludePath) + .asi + $(SolutionDir)$(Configuration)\$(Platform)\$(ProjectName)\ + $(Configuration)\ + + + \..\include\;$(IncludePath) + .asi + $(SolutionDir)$(Configuration)\$(Platform)\$(ProjectName)\ + $(Configuration)\ + + + + Level3 + true + UNICODE;_UNICODE;_USRDLL + true + NotUsing + pch.h + ..\..\include\;..\..\external\;%(AdditionalIncludeDirectories) + Default + + + NotSet + + + Windows + true + false + + + + + Level3 + true + false + true + UNICODE;_UNICODE;_USRDLL + true + NotUsing + pch.h + ..\..\include\;..\..\external\;%(AdditionalIncludeDirectories) + Default + + + Disabled + NotSet + + + Windows + true + true + true + false + + + call $(MSBuildStartupDirectory)\set_git_ver.cmd + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/Crysis3Remastered.Patches/Crysis3Remastered.Patches.vcxproj.filters b/source/Crysis3Remastered.Patches/Crysis3Remastered.Patches.vcxproj.filters new file mode 100644 index 0000000..e90bcc4 --- /dev/null +++ b/source/Crysis3Remastered.Patches/Crysis3Remastered.Patches.vcxproj.filters @@ -0,0 +1,41 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/source/Crysis3Remastered.Patches/IConsole.h b/source/Crysis3Remastered.Patches/IConsole.h new file mode 100644 index 0000000..fceac3d --- /dev/null +++ b/source/Crysis3Remastered.Patches/IConsole.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +struct IConsole +{ + void* filler[0x22]; + //! Execute a string in the console. + //! \param command Console command e.g. "map testy" - no leading slash + //! \param bSilentMode true=suppresses log in error case and logging the command to the console + //! \param bDeferExecution true=the command is stored in special fifo that allows delayed execution by using wait_seconds and wait_frames commands + //! \par Example + //! \include CrySystem/Examples/ConsoleCommand.cpp + void (*ExecuteString)(void* _this, const char* command, const bool bSilentMode, const bool bDeferExecution); +}; diff --git a/source/Crysis3Remastered.Patches/dllmain.cpp b/source/Crysis3Remastered.Patches/dllmain.cpp new file mode 100644 index 0000000..9f73c17 --- /dev/null +++ b/source/Crysis3Remastered.Patches/dllmain.cpp @@ -0,0 +1,173 @@ +#include "stdafx.h" +#include "helper.hpp" +#include "memory.hpp" +#include "git_ver.h" +#include "IConsole.h" + +HMODULE baseModule{}; + +#define wstr(s) L#s +#define wxstr(s) wstr(s) +#define PROJECT_NAME "Crysis3Remastered.Patches" + +enum eBenchmarkTypes +{ + eCPUBenchmark = 1, + eGPUBenchmark, +}; + +// INI Variables +bool bBenchmarkingOnly{}; +bool bVerboseConsoleOutput{}; +int iBenchmarkType{}; + +void ReadConfig(void) +{ + inipp::Ini ini; + // Initialize config + std::wstring config_path = L"" PROJECT_NAME ".ini"; + std::wifstream iniFile(config_path); + if (!iniFile) + { + // no ini, lets generate one. + std::wstring ini_defaults = L"[Settings]\n" + wstr(bBenchmarkingOnly)" false\n" + wstr(bVerboseConsoleOutput)" = false\n" + "; Benchmark types:\n" + "; 1 = CPU Benchmark\n" + "; 2 = GPU Benchmark\n" + "; Savefiles from `/data/` must be installed to `%userprofile%\\Saved Games\\Crysis3Remastered\\SaveGames`\n" + wstr(iBenchmarkType)" = 1\n"; + std::wofstream iniFile(config_path); + iniFile << ini_defaults; + bBenchmarkingOnly = false; + bVerboseConsoleOutput = false; + iBenchmarkType = 1; + } + else + { + ini.parse(iniFile); + inipp::get_value(ini.sections[L"Settings"], wstr(bBenchmarkingOnly), bBenchmarkingOnly); + inipp::get_value(ini.sections[L"Settings"], wstr(bVerboseConsoleOutput), bVerboseConsoleOutput); + inipp::get_value(ini.sections[L"Settings"], wstr(iBenchmarkType), iBenchmarkType); + } +} + +#define TYPEDEF_FUNCTION_PTR(ret_type, func_name, ...) \ + typedef ret_type (*func_name##_ptr)(__VA_ARGS__); \ + extern func_name##_ptr func_name; + +#define INIT_FUNCTION_PTR(func_name) \ + func_name##_ptr func_name = nullptr + +TYPEDEF_FUNCTION_PTR(void, CryLogAlways_, const char* fmt, ...); +INIT_FUNCTION_PTR(CryLogAlways_); + +struct ISystem +{ + IConsole* pConsole; +}; + +ISystem** g_System; +bool saveloaded{}; + +namespace CRichPresence +{ + static bool SetRichPresence_Hook(const char* msg) + { + CryLogAlways_(msg); + if (!saveloaded) + { + CryLogAlways_("CRichPresence::SetRichPresence_Hook: Executing benchmark\n"); + g_System[0]->pConsole->ExecuteString(g_System[0], iBenchmarkType == eCPUBenchmark ? "load default_CPU.CSF" : "load default_GPU.CSF", true, false); + g_System[0]->pConsole->ExecuteString(g_System[0], "con_restricted 0", true, false); + // why is this considered restricted?? + g_System[0]->pConsole->ExecuteString(g_System[0], "g_skipAfterLoadingScreen 1", true, false); + saveloaded = true; + } + return 1; + } +}; + + +static bool CallFunction32(void* src, void* dst, int len) +{ + if (len < 5) + { + return false; + } + DWORD curProtection; + VirtualProtect(src, len, PAGE_EXECUTE_READWRITE, &curProtection); + + memset(src, 0x90, len); + + uintptr_t relativeAddress = ((uintptr_t)dst - (uintptr_t)src) - 5; + + *(BYTE*)src = 0xE8; + *(uint32_t*)((uintptr_t)src + 1) = relativeAddress; + + DWORD temp; + VirtualProtect(src, len, curProtection, &temp); + + return true; +} + +static void DoPatches() +{ + // kills two bird with one stone + // patches to load our custom save + // and redirect CryLog to CryLogAlways + // https://github.com/ValtoGameEngines/CryEngine/blob/d9d2c9f000836f0676e65a90bed40dcc3b1451eb/Code/GameSDK/GameDll/RichPresence.cpp#L75-L79 + // if (!g_pGame->HasExclusiveControllerIndex()) + // { + // CryLog("[Rich Presence] not setting rich presence, no player set"); + // return true; + // } + uintptr_t AfterLogosHook = FindAndPrintPatternW(L"48 8d 0d ? ? ? ? e8 ? ? ? ? b0 01 48 81 c4 ? ? ? ? 41 5e 5f 5d c3", L"RichPersistence Pattern"); + uintptr_t g_SystemAddr = FindAndPrintPatternW(L"48 8b 0d ? ? ? ? 48 8d 15 ? ? ? ? 45 33 c9 45 33 c0 48 8b 01 ff 90 10 01 00 00", L"ISystem Global Ptr"); + uintptr_t CryLogAlways_Addr = FindAndPrintPatternW(L"48 8d 0d ? ? ? ? e8 ? ? ? ? 0f b6 83 ? ? ? ?", L"CryLogAlways()"); + void* CryLogAlways_Ptr = (void*)ReadLEA32(CryLogAlways_Addr + 7, L"CryLog()", 0, 1, 5); + g_System = (ISystem**)ReadLEA32(g_SystemAddr, L"IConsole**", 0, 3, 7); + CryLogAlways_ = (CryLogAlways__ptr)CryLogAlways_Ptr; + if (bVerboseConsoleOutput) + { + void* CryLogPtr = (void*)ReadLEA32(AfterLogosHook + 7, L"CryLog()", 0, 1, 5); + Memory::DetourFunction32(CryLogPtr, CryLogAlways_Ptr, 5); + } + if (bBenchmarkingOnly) + { + if (AfterLogosHook && g_System) + { + void* JumpPattern = (void*)FindAndPrintPatternW(L"CC CC CC CC CC CC CC CC CC CC CC CC CC", L"Int3 Arrray"); + CallFunction32((void*)(AfterLogosHook + 7), JumpPattern, 5); + Memory::DetourFunction64(JumpPattern, (void*)&CRichPresence::SetRichPresence_Hook, 14); + } + } +} + +DWORD __stdcall Main(void*) +{ + baseModule = GetModuleHandle(NULL); + ReadConfig(); + DoPatches(); + return true; +} + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved +) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + { + CreateThread(NULL, 0, Main, 0, NULL, 0); + } + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +}