diff --git a/src/CET.cpp b/src/CET.cpp index 98977725..c118117a 100644 --- a/src/CET.cpp +++ b/src/CET.cpp @@ -8,9 +8,9 @@ using namespace std::chrono_literals; static std::unique_ptr s_pInstance{nullptr}; static bool s_isRunning{true}; -void CET::Initialize(const RED4ext::Sdk* aSdk) +void CET::Initialize() { - s_pInstance.reset(new CET(aSdk)); + s_pInstance.reset(new CET); s_pInstance->GetVM().Prepare(); } @@ -67,7 +67,7 @@ bool CET::IsRunning() noexcept return s_isRunning; } -CET::CET(const RED4ext::Sdk* aSdk) +CET::CET() : m_options(m_paths) , m_persistentState(m_paths, m_options) , m_bindings(m_paths, m_options) diff --git a/src/CET.h b/src/CET.h index ae8470e2..e5d0940c 100644 --- a/src/CET.h +++ b/src/CET.h @@ -6,14 +6,13 @@ #include "VKBindings.h" #include "d3d12/D3D12.h" #include "overlay/Overlay.h" -#include "RED4ext/Api/Sdk.hpp" #include "scripting/LuaVM.h" struct CET { ~CET(); - static void Initialize(const RED4ext::Sdk* aSdk); + static void Initialize(); static void Shutdown(); static CET& Get(); @@ -28,7 +27,7 @@ struct CET static bool IsRunning() noexcept; private: - CET(const RED4ext::Sdk* aSdk); + CET(); Paths m_paths; Options m_options; diff --git a/src/dllmain.cpp b/src/dllmain.cpp index e1e75df6..4a318dad 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -21,15 +21,13 @@ static HANDLE s_modInstanceMutex = nullptr; using namespace std::chrono_literals; -static bool Initialize(RED4ext::PluginHandle aHandle, const RED4ext::Sdk* aSdk) +static bool Initialize() { try { MH_Initialize(); - GameMainThread::Create(aHandle, aSdk); - - CET::Initialize(aSdk); + CET::Initialize(); const auto& options = CET::Get().GetOptions(); @@ -92,12 +90,13 @@ static void Shutdown() RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::PluginHandle aHandle, RED4ext::EMainReason aReason, const RED4ext::Sdk* aSdk) { RED4EXT_UNUSED_PARAMETER(aHandle); + RED4EXT_UNUSED_PARAMETER(aSdk); switch (aReason) { case RED4ext::EMainReason::Load: { - return Initialize(aHandle, aSdk); + return Initialize(); break; } case RED4ext::EMainReason::Unload: diff --git a/src/scripting/GameHooks.cpp b/src/scripting/GameHooks.cpp index 354cb931..f08d86de 100644 --- a/src/scripting/GameHooks.cpp +++ b/src/scripting/GameHooks.cpp @@ -23,27 +23,42 @@ void GameMainThread::RepeatedTaskQueue::Drain() ++taskIt; } } -GameMainThread::StateTickOverride::StateTickOverride(const char* acpRealFunctionName) +GameMainThread::StateTickOverride::StateTickOverride(uint32_t aHash, const char* acpRealFunctionName) { + const RED4ext::UniversalRelocPtr func(aHash); + Location = func.GetAddr(); + + if (Location) + { + if (MH_CreateHook(Location, reinterpret_cast(&GameMainThread::HookStateTick), reinterpret_cast(&RealFunction)) != MH_OK || MH_EnableHook(Location) != MH_OK) + Log::Error("Could not hook main thread function {}! Main thread is not completely hooked!", acpRealFunctionName); + else + Log::Info("Main thread function {} hook complete!", acpRealFunctionName); + } + else + Log::Error("Could not locate {}! Main thread is not completely hooked!", acpRealFunctionName); } GameMainThread::StateTickOverride::~StateTickOverride() { + MH_DisableHook(Location); + + Location = nullptr; + RealFunction = nullptr; } bool GameMainThread::StateTickOverride::OnTick(RED4ext::IGameState* apThisState, RED4ext::CGameApplication* apGameApplication) { Tasks.Drain(); - return true; + return RealFunction(apThisState, apGameApplication); } -static GameMainThread* s_gameMainThread = nullptr; - GameMainThread& GameMainThread::Get() { - assert(s_gameMainThread); - return *s_gameMainThread; + static GameMainThread s_gameMainThread; + + return s_gameMainThread; } void GameMainThread::AddBaseInitializationTask(const std::function& aFunction) @@ -75,76 +90,14 @@ void GameMainThread::AddGenericTask(const std::function& aFunction) m_genericQueue.AddTask(aFunction); } -void GameMainThread::Create(RED4ext::PluginHandle aHandle, const RED4ext::Sdk* aSdk) -{ - if (!s_gameMainThread) - s_gameMainThread = new GameMainThread(aHandle, aSdk); -} - -GameMainThread::GameMainThread(RED4ext::PluginHandle aHandle, const RED4ext::Sdk* sdk) -{ - { - RED4ext::GameState state{nullptr, &HookBaseInitStateTick, nullptr}; - sdk->gameStates->Add(aHandle, RED4ext::EGameStateType::BaseInitialization, &state); - } - { - RED4ext::GameState state{nullptr, &HookInitStateTick, nullptr}; - sdk->gameStates->Add(aHandle, RED4ext::EGameStateType::Initialization, &state); - } - { - RED4ext::GameState state{nullptr, &HookRunningStateTick, nullptr}; - sdk->gameStates->Add(aHandle, RED4ext::EGameStateType::Running, &state); - } - { - RED4ext::GameState state{nullptr, &HookShutdownStateTick, nullptr}; - sdk->gameStates->Add(aHandle, RED4ext::EGameStateType::Shutdown, &state); - } -} - -bool GameMainThread::HookBaseInitStateTick(RED4ext::CGameApplication* apGameApplication) +bool GameMainThread::HookStateTick(RED4ext::IGameState* apThisState, RED4ext::CGameApplication* apGameApplication) { auto& gmt = Get(); // drain generic tasks gmt.m_genericQueue.Drain(); - gmt.m_stateTickOverrides[0].OnTick(apGameApplication->currState, apGameApplication); - - return true; -} - -bool GameMainThread::HookInitStateTick(RED4ext::CGameApplication* apGameApplication) -{ - auto& gmt = Get(); - - // drain generic tasks - gmt.m_genericQueue.Drain(); - - gmt.m_stateTickOverrides[1].OnTick(apGameApplication->currState, apGameApplication); - - return true; -} - -bool GameMainThread::HookRunningStateTick(RED4ext::CGameApplication* apGameApplication) -{ - auto& gmt = Get(); - - // drain generic tasks - gmt.m_genericQueue.Drain(); - - gmt.m_stateTickOverrides[2].OnTick(apGameApplication->currState, apGameApplication); - - return false; -} - -bool GameMainThread::HookShutdownStateTick(RED4ext::CGameApplication* apGameApplication) -{ - auto& gmt = Get(); - - // drain generic tasks - gmt.m_genericQueue.Drain(); - - gmt.m_stateTickOverrides[3].OnTick(apGameApplication->currState, apGameApplication); - - return true; + // execute specific state tasks, including original function + const auto cStateIndex = static_cast(apThisState->GetType()); + return gmt.m_stateTickOverrides[cStateIndex].OnTick(apThisState, apGameApplication); } diff --git a/src/scripting/GameHooks.h b/src/scripting/GameHooks.h index edf255ea..b0c84364 100644 --- a/src/scripting/GameHooks.h +++ b/src/scripting/GameHooks.h @@ -1,9 +1,7 @@ #pragma once -#include "RED4ext/Api/Sdk.hpp" struct GameMainThread { - static void Create(RED4ext::PluginHandle aHandle, const RED4ext::Sdk* aSdk); static GameMainThread& Get(); void AddBaseInitializationTask(const std::function& aFunction); @@ -13,14 +11,11 @@ struct GameMainThread void AddGenericTask(const std::function& aFunction); private: - GameMainThread(RED4ext::PluginHandle aHandle, const RED4ext::Sdk* sdk); + GameMainThread() = default; using TStateTick = bool(RED4ext::IGameState*, RED4ext::CGameApplication*); - static bool HookBaseInitStateTick(RED4ext::CGameApplication* apGameApplication); - static bool HookInitStateTick(RED4ext::CGameApplication* apGameApplication); - static bool HookRunningStateTick(RED4ext::CGameApplication* apGameApplication); - static bool HookShutdownStateTick(RED4ext::CGameApplication* apGameApplication); + static bool HookStateTick(RED4ext::IGameState* apThisState, RED4ext::CGameApplication* apGameApplication); // helper task queue which executes added tasks each drain until they are finished struct RepeatedTaskQueue @@ -35,19 +30,21 @@ struct GameMainThread struct StateTickOverride { - StateTickOverride(const char* acpRealFunctionName); + StateTickOverride(uint32_t aHash, const char* acpRealFunctionName); ~StateTickOverride(); bool OnTick(RED4ext::IGameState*, RED4ext::CGameApplication*); + uint8_t* Location = nullptr; + TStateTick* RealFunction = nullptr; RepeatedTaskQueue Tasks; }; std::array m_stateTickOverrides{ - StateTickOverride("CBaseInitializationState::OnTick"), - StateTickOverride("CInitializationState::OnTick"), - StateTickOverride( "CRunningState::OnTick"), - StateTickOverride("CShutdownState::OnTick")}; + StateTickOverride(CyberEngineTweaks::AddressHashes::CBaseInitializationState_OnTick, "CBaseInitializationState::OnTick"), + StateTickOverride(CyberEngineTweaks::AddressHashes::CInitializationState_OnTick, "CInitializationState::OnTick"), + StateTickOverride(CyberEngineTweaks::AddressHashes::CRunningState_OnTick, "CRunningState::OnTick"), + StateTickOverride(CyberEngineTweaks::AddressHashes::CShutdownState_OnTick, "CShutdownState::OnTick")}; RepeatedTaskQueue m_genericQueue; };