feat: added native manager impl
This commit is contained in:
@@ -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
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