Untitled

mail@pastecode.io avatar
unknown
c_cpp
a month ago
7.1 kB
3
Indexable
Never
#include <iostream>
#include <type_traits>
#include <utility>


struct Parameters {
	template<typename T>
	void setArg(const uint8_t idx, const T val) const {
		*(T*)getArgPtr(idx) = val;
	}

	template<typename T>
	T getArg(const uint8_t idx) const {
		return *(T*)getArgPtr(idx);
	}

	// asm depends on this specific type
	// we the ILCallback allocates stack space that is set to point here
	volatile uint64_t m_arguments;
private:
	// must be char* for aliasing rules to work when reading back out
	char* getArgPtr(const uint8_t idx) const {
		return ((char*)&m_arguments) + sizeof(uint64_t) * idx;
	}
};

struct ReturnValue {
	template<typename T>
	void setRet(const T val) const {
		*(T*)getRetPtr() = val;
	}

	template<typename T>
	T getRet() const {
		return *(T*)getRetPtr();
	}

	unsigned char* getRetPtr() const {
		return (unsigned char*)&m_retVal;
	}
	uint64_t m_retVal;
};

typedef void(*tUserCallback)(const Parameters* params, const uint8_t count, const ReturnValue* ret);

namespace dyno {
	enum class DataType;

	template<typename T>
	constexpr DataType GetType() {
		if constexpr (std::is_same_v<T, void>) return DataType::Void;
		else if constexpr (std::is_same_v<T, bool>) return DataType::Bool;
		else if constexpr (std::is_same_v<T, int8_t>) return DataType::Int8;
		else if constexpr (std::is_same_v<T, uint8_t>) return DataType::UInt8;
		else if constexpr (std::is_same_v<T, int16_t>) return DataType::Int16;
		else if constexpr (std::is_same_v<T, uint16_t>) return DataType::UInt16;
		else if constexpr (std::is_same_v<T, int32_t>) return DataType::Int32;
		else if constexpr (std::is_same_v<T, uint32_t>) return DataType::UInt32;
		else if constexpr (std::is_same_v<T, int64_t>) return DataType::Int64;
		else if constexpr (std::is_same_v<T, uint64_t> || std::is_same_v<T, unsigned long>) return DataType::UInt64;
		else if constexpr (std::is_same_v<T, float>) return DataType::Float;
		else if constexpr (std::is_same_v<T, double>) return DataType::Double;
		else if constexpr (std::is_same_v<T, const char*>) return DataType::String;
		else if constexpr (std::is_same_v<T, const wchar_t*>) return DataType:: WString;
		else if constexpr (std::is_pointer<T>::value || std::is_reference_v<T>) return DataType::Pointer;
		else {
			if (sizeof(T) > sizeof(uint64_t)) {
				return DataType::Pointer;
			} else if constexpr (sizeof(T) > sizeof(uint32_t)) {
				return DataType::UInt64;
			} else if constexpr (sizeof(T) > sizeof(uint16_t)) {
				return DataType::UInt32;
			} else if constexpr (sizeof(T) > sizeof(uint8_t)) {
				return DataType::UInt16;
			} else if constexpr (sizeof(T) > sizeof(bool)) {
				return DataType::UInt8;
			}
			return DataType::Bool;
		}
	}

	template<typename Function>
	struct function_traits;

	template<typename Ret, typename... Args>
	struct function_traits<Ret(*)(Args...)> {
		static constexpr size_t arity = sizeof...(Args);
		using result_type = Ret;
		using argument_types = std::tuple<Args...>;
		using index_sequence_for = std::index_sequence_for<Args...>;

		static std::array<DataType, arity> args() {
			return { GetType<Args>() ...};
		}

		static DataType ret() {
			return GetType<Ret>();
		}
	};

	template<typename Class, typename Ret, typename... Args>
	struct function_traits<Ret(Class::*)(Args...)> {
		static constexpr size_t arity = sizeof...(Args);
		using result_type = Ret;
		using argument_types = std::tuple<Class*, Args...>;
		using index_sequence_for = std::index_sequence_for<Class*, Args...>;

		static std::array<DataType, arity + 1> args() {
			std::array<DataType, arity> originalArgs = { GetType<Args>() ... };
			std::array<DataType, arity + 1> result;
			result[0] = DataType::Pointer;
			std::copy(originalArgs.begin(), originalArgs.end(), result.begin() + 1);
			return result;
		}

		static DataType ret() {
			return GetType<Ret>();
		}
	};

	template <typename Class, typename Ret, typename... Args>
	class function_traits<Ret (Class::*)(Args...) const> : public function_traits<Ret (Class::*)(Args...)> {};
	template <typename Class, typename Ret, typename... Args>
	class function_traits<Ret (Class::*)(Args...) volatile> : public function_traits<Ret (Class::*)(Args...)> {};
	template <typename Class, typename Ret, typename... Args>
	class function_traits<Ret (Class::*)(Args...) const volatile> : public function_traits<Ret (Class::*)(Args...)> {};

	template<typename Ret, typename... Args>
	struct function_traits<Ret(Args...)> {
		static constexpr size_t arity = sizeof...(Args);
		using result_type = Ret;
		using argument_types = std::tuple<Args...>;
		using index_sequence_for = std::index_sequence_for<Args...>;

		static std::array<DataType, arity> args() {
			return { GetType<Args>() ...};
		}

		static DataType ret() {
			return GetType<Ret>();
		}
	};

	template<class Ret, class... Args>
	class function_traits<Ret(Args...) const> : public function_traits<Ret(Args...)> {};
	template<class Ret, class... Args>
	class function_traits<Ret(Args...) volatile> : public function_traits<Ret(Args...)> {};
	template<class Ret, class... Args>
	class function_traits<Ret(Args...) const volatile> : public function_traits<Ret(Args...)> {};
}

// Helper function to cast void* to the correct type and fill the tuple
template <typename T, typename Tuple, std::size_t I>
void fill_tuple_element(Tuple& t, const Parameters& params)
{
	std::get<I>(t) = params.getArg<T>(I);
}

// Recursive template to unpack and fill each element of the tuple
template <typename Tuple, std::size_t... I>
void fill_tuple(Tuple& t, const Parameters& params, std::index_sequence<I...>)
{
	(fill_tuple_element<std::tuple_element_t<I, Tuple>, Tuple, I>(t, params), ...);
}

enum class ReturnAction : int {
	Override = 3,
};

template<typename F, typename C>
auto make_callback(F, C callback)  {
	using traits = dyno::function_traits<F>;
	return [&](const Parameters& params, const uint8_t count, const ReturnValue& ret) -> ReturnAction  {
		typename traits::argument_types args;
		fill_tuple(args, params, typename traits::index_sequence_for{});
		if constexpr (std::is_same_v<traits::result_type, void>) {
			return std::apply(callback, args);
		} else {
			auto [action, output] = std::apply(callback, args);
			if (action >= ReturnAction::Override) {
				ret.setRet(output);
			}
			return action;
		}
	};
}

/* Hook Signature */


class Ex {
public:
	int func(int,  float, double, void*);
};

int func(int,  float, double, void*);

/* Hook Callback */

std::pair<ReturnAction, int> FunctionWhichShouldCalledInside(int,  float, double, void*) {}
std::pair<ReturnAction, int> FunctionWhichShouldCalledInside2(Ex* ex, int,  float, double, void*) {}

int main()
{
	auto cb = make_callback(&func, &FunctionWhichShouldCalledInside);

	Parameters p;
	int count;
	ReturnValue r;

	cb(p, count, r);


	auto cb2 = make_callback(&Ex::func, &FunctionWhichShouldCalledInside2);

	cb2(p, count, r);

	return 0;
}
Leave a Comment