feat: added native manager impl
This commit is contained in:
@@ -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<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
252
src/impl/fxn.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user