From 989f7f4d73482565e93453e9048510ff34cccf53 Mon Sep 17 00:00:00 2001 From: slayercio Date: Tue, 18 Nov 2025 20:21:10 +0100 Subject: [PATCH] feat: added native manager impl --- src/detail/fxn.hpp | 41 -------- src/impl/fxn.cpp | 252 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+), 41 deletions(-) create mode 100644 src/impl/fxn.cpp diff --git a/src/detail/fxn.hpp b/src/detail/fxn.hpp index 42bec69..4e7ea0e 100644 --- a/src/detail/fxn.hpp +++ b/src/detail/fxn.hpp @@ -22,10 +22,6 @@ namespace fxn static void DispatchNative(fxn::NativeContext* context, NativeHash hash); - public: - void Initialize(); - void Shutdown(); - public: const NativeHandler& GetNativeHandler(const NativeHash hash) const; void RegisterNativeHandler(const NativeHash hash, const NativeHandler handler); @@ -37,42 +33,5 @@ namespace fxn public: bool Invoke(fxn::NativeContext* context); bool Schedule(fxn::NativeContext* context, bool waitForCompletion = true); - - public: - template - R Invoke(const NativeHash hash, Args&&... args) - { - fxn::NativeContext context(hash); - - (context.PushArgument(std::forward(args)), ...); - - if (!this->Invoke(&context)) - { - throw std::runtime_error("Failed to invoke native with hash: " + std::to_string(hash)); - } - - if constexpr (!std::is_same_v) - { - return context.GetResult(); - } - } - - template - R Schedule(const NativeHash hash, Args&&... args) - { - fxn::NativeContext context(hash); - - (context.PushArgument(std::forward(args)), ...); - - if (!this->Schedule(&context, true)) - { - throw std::runtime_error("Failed to schedule native with hash: " + std::to_string(hash)); - } - - if constexpr (!std::is_same_v) - { - return context.GetResult(); - } - } }; } \ No newline at end of file diff --git a/src/impl/fxn.cpp b/src/impl/fxn.cpp new file mode 100644 index 0000000..ac8ce72 --- /dev/null +++ b/src/impl/fxn.cpp @@ -0,0 +1,252 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define HOOKED_INSERT_PATTERN xorstr_("89 05 ? ? ? ? 89 42 ? 8D 42 ? 33 05 ? ? ? ? 89 42 ? 48 89 13 8B 05 ? ? ? ? 4C 8D 42 ?") +#define HOOK_INSERT_OFFSET -0xA0 + +namespace fxn +{ + namespace + { + FxnManager* g_FxnManager = nullptr; + + void HookedInsert(rage::scrCommandHash _this, std::uint64_t hash, void* handler) + { + static auto& fxnManager = FxnManager::GetInstance(); + + auto dispatcherStub = fxn::CreateNativeDispatcher(hash); + if (dispatcherStub) + { + _this.Insert(hash, dispatcherStub); + fxnManager.RegisterNativeHandler(hash, reinterpret_cast(handler)); + } + else + { + // If we couldn't create a dispatcher, fall back to the original behavior + // and don't register the handler. + _this.Insert(hash, handler); + } + } + } + + struct FxnManager::Impl + { + private: + void* m_HookedInsert = nullptr; + std::unordered_map m_NativeHandlers; + std::unordered_map> m_NativeHooks; + + public: + Impl() + { + auto library = blackbase::Library::GetCurrent(); + if (!library.has_value()) + { + throw std::runtime_error("Failed to get current library for hooking."); + } + + blackbase::Pattern p(HOOKED_INSERT_PATTERN); + blackbase::Matcher m(library->GetBaseAddress(), library->GetEndAddress()); + + auto match = m.FindFirst(p); + if (!match.has_value()) + { + throw std::runtime_error("Failed to find pattern for hooking Insert function."); + } + + auto targetAddress = match->Move(HOOK_INSERT_OFFSET).As(); + + auto& hookManager = fxn::HookingManager::GetInstance(); + if (!hookManager.InstallHook(targetAddress, reinterpret_cast(&HookedInsert))) + { + throw std::runtime_error("Failed to install hook on Insert function."); + } + + m_HookedInsert = targetAddress; + } + + ~Impl() + { + if (m_HookedInsert) + { + auto& hookManager = fxn::HookingManager::GetInstance(); + hookManager.RemoveHook(m_HookedInsert); + } + } + + public: + const NativeHandler& GetNativeHandler(const NativeHash hash) const + { + static NativeHandler nullHandler = nullptr; + + auto it = m_NativeHandlers.find(hash); + if (it != m_NativeHandlers.end()) + { + return it->second; + } + + return nullHandler; + } + + void RegisterNativeHandler(const NativeHash hash, const NativeHandler handler) + { + m_NativeHandlers[hash] = handler; + } + + void RegisterNativeHook(const NativeHash hash, const NativeHook& hook) + { + m_NativeHooks[hash].push_back(hook); + } + + void UnregisterNativeHook(const NativeHash hash) + { + m_NativeHooks.erase(hash); + } + + void DispatchNative(fxn::NativeContext* context, NativeHash hash) + { + auto hooks = m_NativeHooks.find(hash); + if (hooks != m_NativeHooks.end()) + { + bool callOriginal = true; + for (const auto& hook : hooks->second) + { + if (hook(context, callOriginal) == false) + { + return; // Hook has decided to skip further processing + } + } + + if (!callOriginal) + { + return; // Skip original handler + } + } + + auto handlerIt = m_NativeHandlers.find(hash); + if (handlerIt != m_NativeHandlers.end() && handlerIt->second) + { + handlerIt->second(context); + } + } + + void InvokeNative(fxn::NativeContext* context) + { + DispatchNative(context, context->GetNativeHash()); + } + + void ScheduleNative(fxn::NativeContext* context, bool waitForCompletion) + { + auto job = [this, context]() + { + this->InvokeNative(context); + }; + + // TODO: Implement scheduling logic here. + + // END OF TODO + + if (waitForCompletion) + { + while (!context->IsExecuted()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + } + } + }; + + FxnManager& FxnManager::GetInstance() + { + if (!g_FxnManager) + { + g_FxnManager = new FxnManager(); + } + + return *g_FxnManager; + } + + void FxnManager::Destroy() + { + if (g_FxnManager) + { + delete g_FxnManager; + } + + g_FxnManager = nullptr; + } + + FxnManager::FxnManager() + : m_Impl(new Impl()) + { + } + + FxnManager::~FxnManager() + { + delete m_Impl; + } + + void FxnManager::DispatchNative(fxn::NativeContext* context, NativeHash hash) + { + static auto& instance = FxnManager::GetInstance(); + + instance.m_Impl->DispatchNative(context, hash); + } + + const NativeHandler& FxnManager::GetNativeHandler(const NativeHash hash) const + { + return m_Impl->GetNativeHandler(hash); + } + + void FxnManager::RegisterNativeHandler(const NativeHash hash, const NativeHandler handler) + { + m_Impl->RegisterNativeHandler(hash, handler); + } + + void FxnManager::RegisterNativeHook(const NativeHash hash, const NativeHook& hook) + { + m_Impl->RegisterNativeHook(hash, hook); + } + + void FxnManager::UnregisterNativeHook(const NativeHash hash) + { + m_Impl->UnregisterNativeHook(hash); + } + + bool FxnManager::Invoke(fxn::NativeContext* context) + { + try + { + m_Impl->InvokeNative(context); + return true; + } + catch (...) + { + return false; + } + } + + bool FxnManager::Schedule(fxn::NativeContext* context, bool waitForCompletion) + { + try + { + m_Impl->ScheduleNative(context, waitForCompletion); + return true; + } + catch (...) + { + return false; + } + } +} \ No newline at end of file