feat: added native manager impl

This commit is contained in:
2025-11-18 20:21:10 +01:00
parent 8fc83cde53
commit 989f7f4d73
2 changed files with 252 additions and 41 deletions

View File

@@ -22,10 +22,6 @@ namespace fxn
static void DispatchNative(fxn::NativeContext* context, NativeHash hash); static void DispatchNative(fxn::NativeContext* context, NativeHash hash);
public:
void Initialize();
void Shutdown();
public: public:
const NativeHandler& GetNativeHandler(const NativeHash hash) const; const NativeHandler& GetNativeHandler(const NativeHash hash) const;
void RegisterNativeHandler(const NativeHash hash, const NativeHandler handler); void RegisterNativeHandler(const NativeHash hash, const NativeHandler handler);
@@ -37,42 +33,5 @@ namespace fxn
public: public:
bool Invoke(fxn::NativeContext* context); bool Invoke(fxn::NativeContext* context);
bool Schedule(fxn::NativeContext* context, bool waitForCompletion = true); bool Schedule(fxn::NativeContext* context, bool waitForCompletion = true);
public:
template<typename R, typename... Args>
R Invoke(const NativeHash hash, Args&&... args)
{
fxn::NativeContext context(hash);
(context.PushArgument(std::forward<Args>(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<R, void>)
{
return context.GetResult<R>();
}
}
template<typename R, typename... Args>
R Schedule(const NativeHash hash, Args&&... args)
{
fxn::NativeContext context(hash);
(context.PushArgument(std::forward<Args>(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<R, void>)
{
return context.GetResult<R>();
}
}
}; };
} }

252
src/impl/fxn.cpp Normal file
View File

@@ -0,0 +1,252 @@
#include <stdexcept>
#include <unordered_set>
#include <vector>
#include <thread>
#include <detail/fxn.hpp>
#include <detail/rage.hpp>
#include <detail/dispatcher.hpp>
#include <detail/hooking.hpp>
#include <blackbase/xorstr.hpp>
#include <blackbase/library/library.hpp>
#include <blackbase/pattern/matcher.hpp>
#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<void*> _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<NativeHandler>(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<NativeHash, NativeHandler> m_NativeHandlers;
std::unordered_map<NativeHash, std::vector<NativeHook>> 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<void*>();
auto& hookManager = fxn::HookingManager::GetInstance();
if (!hookManager.InstallHook(targetAddress, reinterpret_cast<void*>(&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;
}
}
}