init
This commit is contained in:
173
include/fxn/context.hpp
Normal file
173
include/fxn/context.hpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <atomic>
|
||||
|
||||
namespace fxn
|
||||
{
|
||||
struct Vector4
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float w;
|
||||
};
|
||||
|
||||
struct alignas(16) Vector3 // input
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
struct scrVector3 // output
|
||||
{
|
||||
alignas(8) float x;
|
||||
alignas(8) float y;
|
||||
alignas(8) float z;
|
||||
};
|
||||
|
||||
struct Vector2
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct alignas(16) scrVector3N
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
struct VectorSpace
|
||||
{
|
||||
scrVector3* outVectors[4];
|
||||
scrVector3N inVectors[4];
|
||||
};
|
||||
|
||||
struct NativeContext
|
||||
{
|
||||
private:
|
||||
void* m_ReturnBuffer;
|
||||
std::uint32_t m_ArgumentCount;
|
||||
void* m_Arguments;
|
||||
std::uint32_t m_DataCount;
|
||||
|
||||
VectorSpace m_VectorSpace;
|
||||
scrVector3 m_Vectors[4];
|
||||
|
||||
enum
|
||||
{
|
||||
MAX_ARGUMENTS = 32,
|
||||
ARGUMENT_SIZE = sizeof(void*)
|
||||
};
|
||||
|
||||
std::uint8_t m_Stack[MAX_ARGUMENTS * ARGUMENT_SIZE];
|
||||
std::uint64_t m_NativeHash;
|
||||
std::atomic_bool m_Executed;
|
||||
|
||||
public:
|
||||
inline NativeContext()
|
||||
: m_ReturnBuffer(nullptr), m_ArgumentCount(0), m_Arguments(nullptr), m_DataCount(0), m_NativeHash(0), m_VectorSpace{}, m_Vectors{}, m_Stack{}, m_Executed(false)
|
||||
{
|
||||
for (std::size_t i = 0; i < sizeof(m_Stack); i++)
|
||||
{
|
||||
m_Stack[i] = 0;
|
||||
}
|
||||
|
||||
m_Arguments = &m_Stack;
|
||||
m_ReturnBuffer = &m_Stack;
|
||||
|
||||
m_ArgumentCount = 0;
|
||||
m_DataCount = 0;
|
||||
}
|
||||
|
||||
inline NativeContext(std::uint64_t hash)
|
||||
: NativeContext()
|
||||
{
|
||||
m_NativeHash = hash;
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
inline void PushArgument(const T& arg)
|
||||
{
|
||||
if (m_ArgumentCount >= MAX_ARGUMENTS)
|
||||
{
|
||||
throw std::runtime_error("Exceeded maximum number of arguments.");
|
||||
}
|
||||
|
||||
if constexpr (sizeof(T) < ARGUMENT_SIZE)
|
||||
{
|
||||
// Zero out the memory to avoid garbage values
|
||||
*reinterpret_cast<std::uintptr_t*>(m_Stack + (m_ArgumentCount * ARGUMENT_SIZE)) = 0;
|
||||
}
|
||||
|
||||
*reinterpret_cast<T*>(m_Stack + (m_ArgumentCount * ARGUMENT_SIZE)) = arg;
|
||||
m_ArgumentCount++;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void PushArgument<Vector3>(const Vector3& arg)
|
||||
{
|
||||
PushArgument(arg.x);
|
||||
PushArgument(arg.y);
|
||||
PushArgument(arg.z);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T GetResult()
|
||||
{
|
||||
return *reinterpret_cast<T*>(m_ReturnBuffer);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline Vector3 GetResult<Vector3>()
|
||||
{
|
||||
auto vec = *reinterpret_cast<scrVector3*>(m_ReturnBuffer);
|
||||
return Vector3{ vec.x, vec.y, vec.z }; // fix for alignment issues
|
||||
}
|
||||
|
||||
inline void SetExecuted(bool executed)
|
||||
{
|
||||
m_Executed.store(executed, std::memory_order_release);
|
||||
}
|
||||
|
||||
inline bool IsExecuted() const
|
||||
{
|
||||
return m_Executed.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
inline void SetVectorResults()
|
||||
{
|
||||
for (size_t i = 0; i < m_DataCount; i++)
|
||||
{
|
||||
auto outVector = m_VectorSpace.outVectors[i];
|
||||
const auto& inVector = m_VectorSpace.inVectors[i];
|
||||
|
||||
outVector->x = inVector.x;
|
||||
outVector->y = inVector.y;
|
||||
outVector->z = inVector.z;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline const T& GetArgument(std::size_t index) const
|
||||
{
|
||||
if (index >= m_ArgumentCount)
|
||||
{
|
||||
throw std::out_of_range("Argument index out of range.");
|
||||
}
|
||||
|
||||
auto functionData = reinterpret_cast<uintptr_t*>(m_Arguments);
|
||||
return *reinterpret_cast<T*>(&functionData[index]);
|
||||
}
|
||||
|
||||
inline std::uint64_t GetNativeHash() const
|
||||
{
|
||||
return m_NativeHash;
|
||||
}
|
||||
};
|
||||
}
|
||||
92
include/fxn/invoker.hpp
Normal file
92
include/fxn/invoker.hpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#include <string_view>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
#include <fxn/context.hpp>
|
||||
|
||||
namespace fxn
|
||||
{
|
||||
inline constexpr char ToLower(const char c) noexcept
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c;
|
||||
}
|
||||
|
||||
inline constexpr unsigned int HashString(std::string_view str) noexcept
|
||||
{
|
||||
uint32_t hash = 0;
|
||||
|
||||
for (char ch : str)
|
||||
{
|
||||
hash += ToLower(ch);
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
class NativeInvoker
|
||||
{
|
||||
public:
|
||||
static NativeInvoker& GetInstance();
|
||||
static void AddTickEvent(const std::function<void()>& callback);
|
||||
|
||||
public:
|
||||
void Initialize();
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Invokes the native on current thread
|
||||
* @note MAYBE UNSAFE IF NOT ON GAME THREAD
|
||||
* @param hash native hash to invoke
|
||||
* @param context native context to use
|
||||
* @return whether the native could be found and executed
|
||||
*/
|
||||
bool Invoke(fxn::NativeContext* context);
|
||||
|
||||
/**
|
||||
* @brief Invokes the native on a special game thread
|
||||
* @param hash native hash to invoke
|
||||
* @param context native context to use
|
||||
* @return whether the native could be found and executed
|
||||
*/
|
||||
bool Schedule(fxn::NativeContext* context, bool waitForCompletion = true);
|
||||
|
||||
public:
|
||||
template<typename R, typename... Args>
|
||||
R Invoke(std::uint64_t hash, Args&&... args)
|
||||
{
|
||||
fxn::NativeContext context { hash };
|
||||
|
||||
(context.PushArgument(std::forward<Args>(args)), ...);
|
||||
Invoke(&context);
|
||||
|
||||
if constexpr (!std::is_same_v<R, void>)
|
||||
{
|
||||
return context.GetResult<R>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
R Schedule(std::uint64_t hash, Args&&... args)
|
||||
{
|
||||
fxn::NativeContext context { hash };
|
||||
|
||||
(context.PushArgument(std::forward<Args>(args)), ...);
|
||||
Schedule(&context, true);
|
||||
|
||||
if constexpr (!std::is_same_v<R, void>)
|
||||
{
|
||||
return context.GetResult<R>();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void HookNative(std::uint64_t hash, std::function<bool(fxn::NativeContext* context, bool& shouldCallOriginal)> hook);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user