mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-14 19:15:05 -05:00
a lot of changes
This commit is contained in:
parent
988418fbd6
commit
65deabea53
28 changed files with 811 additions and 1052 deletions
2
bin
2
bin
|
@ -1 +1 @@
|
|||
Subproject commit 651a2fd305f408caf619794c781e19c57fa9cd29
|
||||
Subproject commit d4318890cdcd8ba95ddd3abf2c102af30e46b5a0
|
|
@ -12,14 +12,9 @@
|
|||
namespace geode::core::meta {}
|
||||
|
||||
template<auto F>
|
||||
struct address_of_t {
|
||||
struct AddressOf {
|
||||
static inline auto value = geode::base::get();
|
||||
};
|
||||
|
||||
template<auto F>
|
||||
inline auto address_of = address_of_t<F>::value;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,65 +1,43 @@
|
|||
#ifndef GEODE_CORE_META_CALLCONV_HPP
|
||||
#define GEODE_CORE_META_CALLCONV_HPP
|
||||
|
||||
#include "tuple.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
namespace geode::core::meta {
|
||||
/* CRTP class for creating calling conventions for Function and Hook.
|
||||
* Provides some utilities for less verbose filtering of parameters, and
|
||||
* some wrappers to require the custom calling convention to supply an
|
||||
* invoker and a way to get a wrapper for hooks.
|
||||
*/
|
||||
* Provides some utilities for less verbose filtering of parameters, and
|
||||
* some wrappers to require the custom calling convention to supply an
|
||||
* invoker and a way to get a wrapper for hooks.
|
||||
*/
|
||||
template <class Ret, class... Args>
|
||||
class CallConv {
|
||||
protected:
|
||||
using MyTuple = Tuple<Args...>;
|
||||
|
||||
template <bool, size_t i>
|
||||
struct type_at_wrap_impl {
|
||||
using result = void;
|
||||
template <auto>
|
||||
class arr_to_seq_impl;
|
||||
|
||||
template <class Type, size_t length, const std::array<Type, length>* arr>
|
||||
class arr_to_seq_impl<arr> {
|
||||
private:
|
||||
template <class>
|
||||
class getter;
|
||||
|
||||
template <size_t... indices>
|
||||
class getter<std::index_sequence<indices...>> {
|
||||
public:
|
||||
using result = std::index_sequence<arr->at(indices)...>;
|
||||
};
|
||||
|
||||
public:
|
||||
using result = typename getter<std::make_index_sequence<length>>::result;
|
||||
};
|
||||
|
||||
template <size_t i>
|
||||
struct type_at_wrap_impl<true, i> {
|
||||
using result = typename MyTuple::template type_at<i>;
|
||||
};
|
||||
|
||||
template <size_t i>
|
||||
using type_at_wrap = typename type_at_wrap_impl<i < MyTuple::size, i>::result;
|
||||
|
||||
template <size_t i>
|
||||
static constexpr decltype(auto) at_wrap(const MyTuple& tuple) {
|
||||
if constexpr (i < MyTuple::size) {
|
||||
return tuple.template at<i>();
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
size_t i,
|
||||
template <class> class Pred,
|
||||
class Else
|
||||
>
|
||||
using type_if =
|
||||
typename ternary<
|
||||
(MyTuple::size > i) &&
|
||||
Pred<type_at_wrap<i>>::value
|
||||
>::template type<
|
||||
type_at_wrap<i>,
|
||||
Else
|
||||
>;
|
||||
|
||||
template <
|
||||
size_t i,
|
||||
template <class> class Pred,
|
||||
class Else
|
||||
>
|
||||
static constexpr decltype(auto) value_if(const MyTuple& tuple, const Else e) {
|
||||
return ternary<
|
||||
(MyTuple::size > i) &&
|
||||
Pred<type_at_wrap<i>>::value
|
||||
>::val(at_wrap<i>(tuple), e);
|
||||
}
|
||||
template <auto& arr>
|
||||
using arr_to_seq = typename arr_to_seq_impl<&arr>::result;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -5,22 +5,22 @@ namespace geode::core::meta::x86 {
|
|||
template <class Ret, class... Args>
|
||||
class Cdecl {
|
||||
private:
|
||||
template <Ret(* detour)(Args...)>
|
||||
template <Ret (*detour)(Args...)>
|
||||
static Ret __cdecl wrapper(Args... all) {
|
||||
return detour(all...);
|
||||
}
|
||||
|
||||
public:
|
||||
static Ret invoke(void* address, Args... all) {
|
||||
Ret(__cdecl* raw)(Args...) = reinterpret_cast<decltype(raw)>(address);
|
||||
Ret(__cdecl * raw)(Args...) = reinterpret_cast<decltype(raw)>(address);
|
||||
return raw(all...);
|
||||
}
|
||||
|
||||
template <Ret(* detour)(Args...)>
|
||||
template <Ret (*detour)(Args...)>
|
||||
static constexpr decltype(auto) get_wrapper() {
|
||||
return &wrapper<detour>;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* GEODE_CORE_META_CDECL_HPP */
|
||||
#endif /* GEODE_CORE_META_THISCALL_HPP */
|
||||
|
|
|
@ -4,34 +4,8 @@
|
|||
#include <type_traits>
|
||||
|
||||
namespace geode::core::meta {
|
||||
template <bool cond>
|
||||
class ternary {
|
||||
public:
|
||||
template <class T, class F>
|
||||
static constexpr decltype(auto) val(
|
||||
const T t,
|
||||
const F f
|
||||
) { return f; }
|
||||
|
||||
template <class T, class F>
|
||||
using type = F;
|
||||
};
|
||||
|
||||
template <>
|
||||
class ternary<true> {
|
||||
public:
|
||||
template <class T, class F>
|
||||
static constexpr decltype(auto) val(
|
||||
const T t,
|
||||
const F f
|
||||
) { return t; }
|
||||
|
||||
template <class T, class F>
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <class Class, class... Compare>
|
||||
static constexpr bool any_of = (std::is_same_v<Class, Compare> || ...);
|
||||
template <class Type, class... Compare>
|
||||
static constexpr bool any_of = (std::is_same_v<Type, Compare> || ...);
|
||||
|
||||
template <class... Classes>
|
||||
static constexpr bool always_false = false;
|
||||
|
|
|
@ -6,11 +6,11 @@ namespace geode::core::meta {
|
|||
class DefaultConv {
|
||||
public:
|
||||
static Ret invoke(void* address, Args... all) {
|
||||
Ret(*raw)(Args...) = reinterpret_cast<decltype(raw)>(address);
|
||||
Ret (*raw)(Args...) = reinterpret_cast<decltype(raw)>(address);
|
||||
return raw(all...);
|
||||
}
|
||||
|
||||
template <Ret(*detour)(Args...)>
|
||||
template <Ret (*detour)(Args...)>
|
||||
static constexpr decltype(auto) get_wrapper() {
|
||||
return detour;
|
||||
}
|
||||
|
|
|
@ -1,46 +1,35 @@
|
|||
#ifndef GEODE_CORE_META_FUNCTION_HPP
|
||||
#define GEODE_CORE_META_FUNCTION_HPP
|
||||
|
||||
#include "tuple.hpp"
|
||||
#include "common.hpp"
|
||||
#include "callconv.hpp"
|
||||
#include "common.hpp"
|
||||
#include "tuple.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace geode::core::meta {
|
||||
/* The Geode Function class wraps functions with unconventional
|
||||
* calling conventions (how ironic).
|
||||
*/
|
||||
template <
|
||||
class Func,
|
||||
template <class, class...> class Conv
|
||||
>
|
||||
* calling conventions (how ironic).
|
||||
*/
|
||||
template <class Func, template <class, class...> class Conv>
|
||||
class Function {
|
||||
static_assert(always_false<Func>, "Not a valid function pointer!");
|
||||
};
|
||||
|
||||
template <
|
||||
class Ret, class... Args,
|
||||
template <class, class...> class Conv
|
||||
>
|
||||
template <class Ret, class... Args, template <class, class...> class Conv>
|
||||
class Function<Ret(Args...), Conv> {
|
||||
private:
|
||||
using MyConv = Conv<Ret, Args...>;
|
||||
using MyTuple = Tuple<Args...>;
|
||||
|
||||
private:
|
||||
void* addr;
|
||||
|
||||
public:
|
||||
template <class T>
|
||||
Function(const T& addr)
|
||||
: addr(reinterpret_cast<void*>(addr)) {}
|
||||
template <class Pointer>
|
||||
Function(const Pointer& addr) : addr(reinterpret_cast<void*>(addr)) {}
|
||||
|
||||
decltype(auto) operator()(Args... all) const {
|
||||
return MyConv::invoke(
|
||||
addr,
|
||||
all...
|
||||
);
|
||||
return MyConv::invoke(addr, all...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
#ifndef GEODE_CORE_META_HOOK_HPP
|
||||
#define GEODE_CORE_META_HOOK_HPP
|
||||
|
||||
#include "tuple.hpp"
|
||||
#include "common.hpp"
|
||||
#include "callconv.hpp"
|
||||
|
||||
#include "../hook/hook.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace geode::core::meta {
|
||||
template <
|
||||
auto address,
|
||||
auto detour,
|
||||
template <class, class...> class Conv
|
||||
>
|
||||
class Hook {
|
||||
static_assert(always_false<decltype(address)>,
|
||||
"Not a valid function pointer, or hook and detour aren't compatible!");
|
||||
};
|
||||
|
||||
template <
|
||||
class Ret,
|
||||
class... Args,
|
||||
// TODO: Fix this!!! I hate type qualifiers.
|
||||
auto address,
|
||||
Ret(* detour)(Args...),
|
||||
template <class, class...> class Conv
|
||||
>
|
||||
class Hook<address, detour, Conv> {
|
||||
private:
|
||||
using MyConv = Conv<Ret, Args...>;
|
||||
using MyTuple = Tuple<Args...>;
|
||||
|
||||
private:
|
||||
static inline geode::core::hook::Handle handle;
|
||||
|
||||
public:
|
||||
Hook() {
|
||||
auto wrapper = MyConv::template get_wrapper<detour>();
|
||||
this->handle = geode::core::hook::add(reinterpret_cast<void*>(address), reinterpret_cast<void*>(wrapper));
|
||||
}
|
||||
};
|
||||
|
||||
// member functions.
|
||||
template <
|
||||
class Ret, class Parent, class... Args,
|
||||
Ret(Parent::* address)(Args...),
|
||||
Ret(Parent::* detour)(Args...),
|
||||
template <class, class...> class Conv
|
||||
>
|
||||
class Hook<address, detour, Conv> {
|
||||
// deal with this later lol
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif /* GEODE_CORE_META_HOOK_HPP */
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef GEODE_CORE_META_MEMBERCALL_HPP
|
||||
#define GEODE_CORE_META_MEMBERCALL_HPP
|
||||
|
||||
#include "callconv.hpp"
|
||||
#include "tuple.hpp"
|
||||
#include "x86.hpp"
|
||||
|
||||
|
@ -10,113 +11,174 @@ namespace geode::core::meta::x86 {
|
|||
private:
|
||||
// Metaprogramming / typedefs we need for the rest of the class.
|
||||
using MyConv = CallConv<Ret, Args...>;
|
||||
// for some reason using definitions dont get inherited properly
|
||||
using MyTuple = typename MyConv::MyTuple;
|
||||
|
||||
private:
|
||||
// Filters that will be passed to Tuple::filter.
|
||||
template <size_t i, class Current, size_t c>
|
||||
class filter_to {
|
||||
public:
|
||||
static constexpr bool result =
|
||||
(!gpr_passable<Current>::value || i != 0) &&
|
||||
(!sse_passable<Current>::value || i > 3);
|
||||
|
||||
static constexpr size_t index = i;
|
||||
static constexpr size_t counter = c;
|
||||
};
|
||||
|
||||
template <size_t i, class Current, size_t stack_offset>
|
||||
class filter_from {
|
||||
class Sequences {
|
||||
private:
|
||||
static constexpr bool sse = sse_passable<Current>::value && i <= 3 && i != 0;
|
||||
static constexpr bool gpr = gpr_passable<Current>::value && i == 0;
|
||||
// These are required for proper reordering.
|
||||
static constexpr size_t length = sizeof...(Args);
|
||||
|
||||
static constexpr size_t SSES = 3;
|
||||
static constexpr bool is_sse[length] = { sse_passable<Args>... };
|
||||
|
||||
static constexpr bool is_gpr[length] = { gpr_passable<Args>... };
|
||||
|
||||
static constexpr auto reordered_arr = reorder_pack<Args...>();
|
||||
|
||||
// Setup call from our caller, to "foreign" function
|
||||
static constexpr auto filter_to() {
|
||||
/* The size of our output may be longer than the input.
|
||||
* For the third time, annoyingly, we need to make a lambda for this.
|
||||
*/
|
||||
constexpr auto arr_size = []() -> size_t {
|
||||
// Magic constant 5: XMM0, XMM4, XMM5, ECX, and EDX.
|
||||
size_t size = length + SSES + 5;
|
||||
|
||||
// Check our only GPR.
|
||||
if (0 < length && is_gpr[reordered_arr[0]]) {
|
||||
--size;
|
||||
}
|
||||
|
||||
/* We assume there are no SSES initially.
|
||||
* Any SSES we encounter, we have to remove a "duplicate".
|
||||
*/
|
||||
for (size_t i = 1; i < SSES + 1; ++i) {
|
||||
if (i < length && is_sse[reordered_arr[i]]) {
|
||||
--size;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
};
|
||||
std::array<size_t, arr_size()> to = {};
|
||||
|
||||
/* These are the indices of the placeholder float and int, for clobbering SSEs and
|
||||
* GPRs, respectively.
|
||||
*/
|
||||
constexpr size_t CLOBBER_SSE = length;
|
||||
constexpr size_t CLOBBER_GPR = length + 1;
|
||||
|
||||
// Put the SSEs into the right XMM registers, if they exist.
|
||||
for (size_t i = 1; i < SSES + 1; ++i) {
|
||||
if (i < length && is_sse[reordered_arr[i]]) {
|
||||
to[i] = reordered_arr[i];
|
||||
}
|
||||
else {
|
||||
to[i] = CLOBBER_SSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Clobber XMM0, XMM4, XMM5, and EDX.
|
||||
to[0] = CLOBBER_SSE;
|
||||
to[4] = CLOBBER_SSE;
|
||||
to[5] = CLOBBER_SSE;
|
||||
to[7] = CLOBBER_GPR;
|
||||
|
||||
// Handle our GPR and put it in ECX if we can.
|
||||
if (length > 0 && is_gpr[reordered_arr[0]]) {
|
||||
to[6] = reordered_arr[0];
|
||||
}
|
||||
else {
|
||||
to[6] = CLOBBER_GPR;
|
||||
}
|
||||
|
||||
for (size_t in = 1, out = SSES + 5; in < length; ++in) {
|
||||
// Put all non SSEs and non GPRs in their places.
|
||||
size_t current = reordered_arr[in];
|
||||
if (!(is_sse[current] && in < SSES + 1)) {
|
||||
to[out] = current;
|
||||
++out;
|
||||
}
|
||||
}
|
||||
|
||||
return to;
|
||||
}
|
||||
|
||||
// Setup call from "foreign" function, to one of ours.
|
||||
static constexpr auto filter_from() {
|
||||
std::array<size_t, length> from = {};
|
||||
|
||||
constexpr size_t CLOBBER_SSE = length;
|
||||
constexpr size_t CLOBBER_GPR = length + 1;
|
||||
|
||||
if (length > 0 && is_gpr[reordered_arr[0]]) {
|
||||
// SSES + 3 = ECX
|
||||
from[reordered_arr[0]] = SSES + 3;
|
||||
}
|
||||
|
||||
for (size_t i = 1, offset = 0; i < length; ++i) {
|
||||
size_t current = reordered_arr[i];
|
||||
if (is_sse[current] && i < SSES + 1) {
|
||||
// If in SSE, retain index
|
||||
from[current] = i;
|
||||
}
|
||||
else {
|
||||
// If on stack, offset by 8 (6 SSE + 2 GPR registers available)
|
||||
from[current] = offset + SSES + 5;
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
|
||||
return from;
|
||||
}
|
||||
|
||||
// Annoyingly, unless we're using C++20, we can't eliminate these intermediates. (afaik)
|
||||
static constexpr auto to_arr = filter_to();
|
||||
static constexpr auto from_arr = filter_from();
|
||||
|
||||
public:
|
||||
// We're not even really filtering, just reordering.
|
||||
static constexpr bool result = true;
|
||||
|
||||
static constexpr size_t index =
|
||||
// If in GPR or SSE, retain index
|
||||
(gpr || sse) ? i
|
||||
// If on stack, offset by 4 (3 SSE + 1 GPR register(s) available)
|
||||
: stack_offset + 4;
|
||||
|
||||
// If our output index is greater than 4, it has to be on stack. Increment.
|
||||
static constexpr size_t counter = stack_offset + static_cast<size_t>(index >= 4);
|
||||
using to = typename MyConv::template arr_to_seq<to_arr>;
|
||||
using from = typename MyConv::template arr_to_seq<from_arr>;
|
||||
};
|
||||
|
||||
private:
|
||||
// Where all the logic is actually implemented. Needs to be instantiated by Optcall, though.
|
||||
// Where all the logic is actually implemented.
|
||||
template <class Class, class>
|
||||
class Impl {
|
||||
static_assert(always_false<Class>,
|
||||
static_assert(
|
||||
always_false<Class>,
|
||||
"Please report a bug to the Geode developers! This should never be reached.\n"
|
||||
"SFINAE didn't reach the right overload!");
|
||||
"SFINAE didn't reach the right overload!"
|
||||
);
|
||||
};
|
||||
|
||||
template <size_t... to, size_t... from>
|
||||
class Impl<std::index_sequence<to...>, std::index_sequence<from...>> {
|
||||
public:
|
||||
static Ret invoke(void* address, const Tuple<Args...>& all) {
|
||||
Ret(__vectorcall* raw)(
|
||||
float,
|
||||
typename MyConv::template type_if<1, sse_passable, float>,
|
||||
typename MyConv::template type_if<2, sse_passable, float>,
|
||||
typename MyConv::template type_if<3, sse_passable, float>,
|
||||
float,
|
||||
float,
|
||||
typename MyConv::template type_if<0, gpr_passable, int>,
|
||||
int,
|
||||
typename MyTuple::template type_at<to>...
|
||||
) = reinterpret_cast<decltype(raw)>(address);
|
||||
|
||||
return raw(
|
||||
1907.0f,
|
||||
MyConv::template value_if<1, sse_passable>(all, 1907.0f),
|
||||
MyConv::template value_if<2, sse_passable>(all, 1907.0f),
|
||||
MyConv::template value_if<3, sse_passable>(all, 1907.0f),
|
||||
1907.0f,
|
||||
1907.0f,
|
||||
MyConv::template value_if<0, gpr_passable>(all, 1907),
|
||||
1907,
|
||||
all.template at<to>()...
|
||||
);
|
||||
static Ret invoke(void* address, const Tuple<Args..., float, int>& all) {
|
||||
return reinterpret_cast<Ret(__vectorcall*)(
|
||||
typename Tuple<Args..., float, int>::template type_at<to>...
|
||||
)>(address)(all.template at<to>()...);
|
||||
}
|
||||
|
||||
template <Ret(* detour)(Args...)>
|
||||
template <Ret (*detour)(Args...)>
|
||||
static Ret __vectorcall wrapper(
|
||||
float,
|
||||
typename MyConv::template type_if<1, sse_passable, float> f1,
|
||||
typename MyConv::template type_if<2, sse_passable, float> f2,
|
||||
typename MyConv::template type_if<3, sse_passable, float> f3,
|
||||
float,
|
||||
float,
|
||||
typename MyConv::template type_if<0, gpr_passable, int> i0,
|
||||
int,
|
||||
// Not sure why this doesn't work otherwise, but oh well...
|
||||
typename MyConv::template type_at_wrap<to>... rest
|
||||
/* It's wrapped to stop MSVC from giving me error messages with internal compiler
|
||||
* info. WTF.
|
||||
*/
|
||||
typename Tuple<Args..., float, int>::template type_at_wrap<to>... raw
|
||||
) {
|
||||
auto all = Tuple<>::make(i0, f1, f2, f3, rest...);
|
||||
auto all = Tuple<>::make(raw...);
|
||||
return detour(all.template at<from>()...);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
// Putting it all together: instantiating Impl with our filters.
|
||||
using MyImpl =
|
||||
Impl<
|
||||
typename MyTuple::template filter<filter_to>,
|
||||
typename MyTuple::template filter<filter_from>
|
||||
>;
|
||||
using MyImpl = Impl<typename Sequences::to, typename Sequences::from>;
|
||||
|
||||
public:
|
||||
// Just wrapping MyImpl.
|
||||
static Ret invoke(void* address, Args... all) {
|
||||
return MyImpl::invoke(address, { all... });
|
||||
/* The extra float and int here are so that we can grab placeholders.
|
||||
* If we don't have anything in XMM0 - 5 or ECX / EDX, we will use
|
||||
* these placeholders instead. The values are 314 to avoid unintentional
|
||||
* bugs (since 0 may work coincidentally).
|
||||
*/
|
||||
return MyImpl::invoke(address, { all..., 314.0f, 314 });
|
||||
}
|
||||
|
||||
template <Ret(* detour)(Args...)>
|
||||
template <Ret (*detour)(Args...)>
|
||||
static constexpr decltype(auto) get_wrapper() {
|
||||
return &MyImpl::template wrapper<detour>;
|
||||
}
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
#ifndef GEODE_CORE_META_META_HPP
|
||||
#define GEODE_CORE_META_META_HPP
|
||||
|
||||
namespace geode::core::meta {
|
||||
namespace x86 {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include "callconv.hpp"
|
||||
#include "common.hpp"
|
||||
#include "function.hpp"
|
||||
#include "preproc.hpp"
|
||||
#include "defaultconv.hpp"
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
// #include "hook.hpp"
|
||||
#include "cdecl.hpp"
|
||||
#include "optcall.hpp"
|
||||
#include "thiscall.hpp"
|
||||
#include "membercall.hpp"
|
||||
namespace geode::core::meta {}
|
||||
namespace geode::core::meta::x86 {}
|
||||
|
||||
#if defined(GEODE_IS_WINDOWS)
|
||||
#include "cdecl.hpp"
|
||||
#include "membercall.hpp"
|
||||
#include "optcall.hpp"
|
||||
#include "thiscall.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* GEODE_CORE_META_META_HPP */
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef GEODE_CORE_META_OPTCALL_HPP
|
||||
#define GEODE_CORE_META_OPTCALL_HPP
|
||||
|
||||
#include "callconv.hpp"
|
||||
#include "tuple.hpp"
|
||||
#include "x86.hpp"
|
||||
|
||||
|
@ -10,86 +11,173 @@ namespace geode::core::meta::x86 {
|
|||
private:
|
||||
// Metaprogramming / typedefs we need for the rest of the class.
|
||||
using MyConv = CallConv<Ret, Args...>;
|
||||
// for some reason using definitions dont get inherited properly
|
||||
using MyTuple = typename MyConv::MyTuple;
|
||||
|
||||
private:
|
||||
// Filters that will be passed to Tuple::filter.
|
||||
template <size_t i, class Current, size_t c>
|
||||
class filter_to {
|
||||
public:
|
||||
static constexpr bool result =
|
||||
(!gpr_passable<Current>::value || i > 1) &&
|
||||
(!sse_passable<Current>::value || i > 3);
|
||||
|
||||
static constexpr size_t index = i;
|
||||
static constexpr size_t counter = c;
|
||||
};
|
||||
|
||||
template <size_t i, class Current, size_t stack_offset>
|
||||
class filter_from {
|
||||
// These go in a class to not pollute the namespace.
|
||||
class Sequences {
|
||||
private:
|
||||
static constexpr bool sse = sse_passable<Current>::value && i <= 3;
|
||||
static constexpr bool gpr = gpr_passable<Current>::value && i <= 1;
|
||||
// These are required for proper reordering.
|
||||
static constexpr size_t length = sizeof...(Args);
|
||||
|
||||
static constexpr size_t SSES = 4;
|
||||
static constexpr bool is_sse[length] = { sse_passable<Args>... };
|
||||
|
||||
static constexpr size_t GPRS = 2;
|
||||
static constexpr bool is_gpr[length] = { gpr_passable<Args>... };
|
||||
|
||||
static constexpr auto reordered_arr = reorder_pack<Args...>();
|
||||
|
||||
// Setup call from our caller, to "foreign" function
|
||||
static constexpr auto filter_to() {
|
||||
/* The size of our output may be longer than the input.
|
||||
* Also, annoyingly, we must make a lambda to calculate this.
|
||||
*/
|
||||
constexpr auto arr_size = []() -> size_t {
|
||||
// Magic constant 2 is to pad XMM4 and XMM5: can't be used.
|
||||
size_t size = length + SSES + 2;
|
||||
|
||||
/* We assume there are no SSES initially.
|
||||
* Any SSES we encounter, we have to remove a "duplicate".
|
||||
*/
|
||||
for (size_t i = 0; i < SSES; ++i) {
|
||||
if (i < length && is_sse[reordered_arr[i]]) {
|
||||
--size;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
};
|
||||
std::array<size_t, arr_size()> to = {};
|
||||
|
||||
// This is the index of the placeholder float, for clobbering SSEs.
|
||||
constexpr size_t CLOBBER_SSE = length;
|
||||
|
||||
// Put the SSEs into the right XMM registers, if they exist.
|
||||
for (size_t i = 0; i < SSES; ++i) {
|
||||
if (i < length && is_sse[reordered_arr[i]]) {
|
||||
to[i] = reordered_arr[i];
|
||||
}
|
||||
else {
|
||||
to[i] = CLOBBER_SSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Clobber XMM4 and XMM5.
|
||||
to[4] = CLOBBER_SSE;
|
||||
to[5] = CLOBBER_SSE;
|
||||
|
||||
for (size_t in = 0, out = SSES + 2; in < length; ++in) {
|
||||
// Put all the non SSEs in their correct places. Skip SSEs.
|
||||
size_t current = reordered_arr[in];
|
||||
if (!(is_sse[current] && in < SSES)) {
|
||||
to[out] = current;
|
||||
++out;
|
||||
}
|
||||
}
|
||||
|
||||
return to;
|
||||
}
|
||||
|
||||
// Setup call from "foreign" function, to one of ours.
|
||||
static constexpr auto filter_from() {
|
||||
std::array<size_t, length> from = {};
|
||||
|
||||
for (size_t i = 0, gprs = 0, offset = 0; i < length; ++i) {
|
||||
size_t current = reordered_arr[i];
|
||||
if (is_sse[current] && i < SSES) {
|
||||
// If in SSE, retain index
|
||||
from[current] = i;
|
||||
}
|
||||
else if (is_gpr[current] && gprs < GPRS) {
|
||||
// If in GPR, offset by 4 (4 SSE registers available)
|
||||
from[current] = gprs + SSES;
|
||||
++gprs;
|
||||
}
|
||||
else {
|
||||
// If on stack, offset by 6 (4 SSE + 2 GPR registers available)
|
||||
from[current] = offset + SSES + GPRS;
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
|
||||
return from;
|
||||
}
|
||||
|
||||
// Grab only the stack values. For determining stack fixup.
|
||||
static constexpr auto filter_stack() {
|
||||
/* The size of our output may be shorter than the input.
|
||||
* Again, annoyingly, we must make a lambda to calculate this.
|
||||
*/
|
||||
constexpr auto arr_size = []() -> size_t {
|
||||
size_t size = length;
|
||||
|
||||
for (size_t i = 0, gprs = 0; i < length; ++i) {
|
||||
size_t current = reordered_arr[i];
|
||||
if (is_sse[current] && i < SSES) {
|
||||
--size;
|
||||
}
|
||||
else if (is_gpr[current] && gprs < GPRS) {
|
||||
--size;
|
||||
++gprs;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
};
|
||||
std::array<size_t, arr_size()> stack = {};
|
||||
|
||||
for (size_t in = 0, out = 0, gprs = 0; in < length; ++in) {
|
||||
size_t current = reordered_arr[in];
|
||||
if ((!is_sse[current] || in >= SSES) && (!is_gpr[current] || gprs >= GPRS)) {
|
||||
stack[out] = current;
|
||||
++out;
|
||||
}
|
||||
|
||||
if (is_gpr[current]) {
|
||||
++gprs;
|
||||
}
|
||||
}
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
// Annoyingly, unless we're using C++20, we can't eliminate these intermediates. (afaik)
|
||||
static constexpr auto to_arr = filter_to();
|
||||
static constexpr auto from_arr = filter_from();
|
||||
static constexpr auto stack_arr = filter_stack();
|
||||
|
||||
public:
|
||||
// We're not even really filtering, just reordering.
|
||||
static constexpr bool result = true;
|
||||
|
||||
static constexpr size_t index =
|
||||
// If in SSE, retain index
|
||||
sse ? i
|
||||
// If in GPR, offset by 4 (4 SSE registers available)
|
||||
: (gpr ? i + 4
|
||||
// If on stack, offset by 6 (4 SSE + 2 GPR registers available)
|
||||
: stack_offset + 6);
|
||||
|
||||
// If our output index is greater than 6, it has to be on stack. Increment.
|
||||
static constexpr size_t counter = stack_offset + static_cast<size_t>(index >= 6);
|
||||
using to = typename MyConv::template arr_to_seq<to_arr>;
|
||||
using from = typename MyConv::template arr_to_seq<from_arr>;
|
||||
using stack = typename MyConv::template arr_to_seq<stack_arr>;
|
||||
};
|
||||
|
||||
private:
|
||||
// Where all the logic is actually implemented. Needs to be instantiated by Optcall, though.
|
||||
template <class Class, class>
|
||||
// Where all the logic is actually implemented.
|
||||
template <class Class, class, class>
|
||||
class Impl {
|
||||
static_assert(always_false<Class>,
|
||||
static_assert(
|
||||
always_false<Class>,
|
||||
"Please report a bug to the Geode developers! This should never be reached.\n"
|
||||
"SFINAE didn't reach the right overload!");
|
||||
"SFINAE didn't reach the right overload!"
|
||||
);
|
||||
};
|
||||
|
||||
template <size_t... to, size_t... from>
|
||||
class Impl<std::index_sequence<to...>, std::index_sequence<from...>> {
|
||||
template <size_t... to, size_t... from, size_t... stack>
|
||||
class Impl<
|
||||
std::index_sequence<to...>, std::index_sequence<from...>,
|
||||
std::index_sequence<stack...>> {
|
||||
private:
|
||||
static constexpr size_t fix =
|
||||
(std::is_class_v<Ret> ? stack_fix<Ret> : 0)
|
||||
+ stack_fix<typename MyTuple::template type_at<to>...>;
|
||||
|
||||
public:
|
||||
static Ret invoke(void* address, const Tuple<Args...>& all) {
|
||||
Ret(__vectorcall* raw)(
|
||||
typename MyConv::template type_if<0, sse_passable, float>,
|
||||
typename MyConv::template type_if<1, sse_passable, float>,
|
||||
typename MyConv::template type_if<2, sse_passable, float>,
|
||||
typename MyConv::template type_if<3, sse_passable, float>,
|
||||
float,
|
||||
float,
|
||||
typename MyConv::template type_if<0, gpr_passable, int>,
|
||||
typename MyConv::template type_if<1, gpr_passable, int>,
|
||||
typename MyTuple::template type_at<to>...
|
||||
) = reinterpret_cast<decltype(raw)>(address);
|
||||
static constexpr size_t fix = (std::is_class_v<Ret> ? stack_fix<Ret> : 0)
|
||||
+ stack_fix<typename Tuple<Args...>::template type_at<stack>...>;
|
||||
|
||||
public:
|
||||
static Ret invoke(void* address, const Tuple<Args..., float>& all) {
|
||||
if constexpr (!std::is_same_v<Ret, void>) {
|
||||
Ret ret = raw(
|
||||
MyConv::template value_if<0, sse_passable>(all, 1907.0f),
|
||||
MyConv::template value_if<1, sse_passable>(all, 1907.0f),
|
||||
MyConv::template value_if<2, sse_passable>(all, 1907.0f),
|
||||
MyConv::template value_if<3, sse_passable>(all, 1907.0f),
|
||||
1907.0f,
|
||||
1907.0f,
|
||||
MyConv::template value_if<0, gpr_passable>(all, 1907),
|
||||
MyConv::template value_if<1, gpr_passable>(all, 1907),
|
||||
all.template at<to>()...
|
||||
);
|
||||
Ret ret =
|
||||
reinterpret_cast<Ret(__vectorcall*)(decltype(all.template at<to>())...)>(
|
||||
address
|
||||
)(all.template at<to>()...);
|
||||
|
||||
if constexpr (fix != 0) {
|
||||
__asm add esp, [fix]
|
||||
|
@ -98,17 +186,8 @@ namespace geode::core::meta::x86 {
|
|||
return ret;
|
||||
}
|
||||
else {
|
||||
raw(
|
||||
MyConv::template value_if<0, sse_passable>(all, 1907.0f),
|
||||
MyConv::template value_if<1, sse_passable>(all, 1907.0f),
|
||||
MyConv::template value_if<2, sse_passable>(all, 1907.0f),
|
||||
MyConv::template value_if<3, sse_passable>(all, 1907.0f),
|
||||
1907.0f,
|
||||
1907.0f,
|
||||
MyConv::template value_if<0, gpr_passable>(all, 1907),
|
||||
MyConv::template value_if<1, gpr_passable>(all, 1907),
|
||||
all.template at<to>()...
|
||||
);
|
||||
reinterpret_cast<Ret(__vectorcall*)(decltype(all.template at<to>())...)>(address
|
||||
)(all.template at<to>()...);
|
||||
|
||||
if constexpr (fix != 0) {
|
||||
__asm add esp, [fix]
|
||||
|
@ -116,18 +195,15 @@ namespace geode::core::meta::x86 {
|
|||
}
|
||||
}
|
||||
|
||||
template <Ret(* detour)(Args...)>
|
||||
template <Ret (*detour)(Args...)>
|
||||
static Ret __cdecl wrapper(
|
||||
// Not sure why this doesn't work otherwise, but oh well...
|
||||
typename MyConv::template type_at_wrap<to>... rest
|
||||
/* It's wrapped to stop MSVC from giving me error messages with internal compiler
|
||||
* info. WTF.
|
||||
*/
|
||||
typename Tuple<Args...>::template type_at_wrap<stack>... rest
|
||||
) {
|
||||
Register<double, typename MyConv::template type_if<0, sse_passable, float>> f0;
|
||||
Register<double, typename MyConv::template type_if<1, sse_passable, float>> f1;
|
||||
Register<double, typename MyConv::template type_if<2, sse_passable, float>> f2;
|
||||
Register<double, typename MyConv::template type_if<3, sse_passable, float>> f3;
|
||||
|
||||
Register<void*, typename MyConv::template type_if<0, gpr_passable, int>> i0;
|
||||
Register<void*, typename MyConv::template type_if<1, gpr_passable, int>> i1;
|
||||
Register<double> f0, f1, f2, f3;
|
||||
Register<void*> i0, i1;
|
||||
|
||||
__asm {
|
||||
movlpd f0, xmm0
|
||||
|
@ -139,30 +215,30 @@ namespace geode::core::meta::x86 {
|
|||
mov i1, edx
|
||||
}
|
||||
|
||||
auto all = Tuple<>::make(
|
||||
f0.get(), f1.get(), f2.get(), f3.get(),
|
||||
i0.get(), i1.get(), rest...
|
||||
);
|
||||
auto all = Tuple<>::make(f0, f1, f2, f3, i0, i1, rest...);
|
||||
|
||||
return detour(all.template at<from>()...);
|
||||
// Register<Type> has a explicit conversion operator we can take advantage of.
|
||||
return detour(static_cast<Args>(all.template at<from>())...);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
// Putting it all together: instantiating Impl with our filters.
|
||||
using MyImpl =
|
||||
Impl<
|
||||
typename MyTuple::template filter<filter_to>,
|
||||
typename MyTuple::template filter<filter_from>
|
||||
>;
|
||||
using MyImpl =
|
||||
Impl<typename Sequences::to, typename Sequences::from, typename Sequences::stack>;
|
||||
|
||||
public:
|
||||
// Just wrapping MyImpl.
|
||||
static Ret invoke(void* address, Args... all) {
|
||||
return MyImpl::invoke(address, { all... });
|
||||
/* The extra float here is so that we can grab placeholders.
|
||||
* If we don't have anything in XMM0 - 5, we will use
|
||||
* this placeholder instead. The value is 314 to avoid unintentional
|
||||
* bugs (since 0 may work coincidentally).
|
||||
*/
|
||||
return MyImpl::invoke(address, { all..., 314.0f });
|
||||
}
|
||||
|
||||
template <Ret(* detour)(Args...)>
|
||||
template <Ret (*detour)(Args...)>
|
||||
static constexpr decltype(auto) get_wrapper() {
|
||||
return &MyImpl::template wrapper<detour>;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,69 @@
|
|||
#ifndef GEODE_CORE_META_PREPROC_HPP
|
||||
#define GEODE_CORE_META_PREPROC_HPP
|
||||
|
||||
#include "../macros/platform.hpp"
|
||||
|
||||
namespace geode::core::meta {
|
||||
#if defined(NDEBUG)
|
||||
static constexpr bool debug = false;
|
||||
#if !defined(GEODE_CALL)
|
||||
// Windows
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
|
||||
#define GEODE_WINDOWS(...) __VA_ARGS__
|
||||
#define GEODE_IS_WINDOWS
|
||||
#define GEODE_IS_DESKTOP
|
||||
#define GEODE_PLATFORM_NAME "Windows"
|
||||
#define GEODE_PLATFORM_TARGET PlatformID::Windows
|
||||
#define GEODE_CALL __stdcall
|
||||
#define GEODE_PLATFORM_EXTENSION ".dll"
|
||||
#else
|
||||
static constexpr bool debug = true;
|
||||
#define GEODE_WINDOWS(...)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Darwin
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#if TARGET_OS_IPHONE
|
||||
#define GEODE_MACOS(...)
|
||||
#define GEODE_IOS(...) __VA_ARGS__
|
||||
#define GEODE_IS_IOS
|
||||
#define GEODE_IS_MOBILE
|
||||
#define GEODE_PLATFORM_NAME "iOS"
|
||||
#define GEODE_PLATFORM_TARGET PlatformID::iOS
|
||||
#define GEODE_PLATFORM_EXTENSION ".dylib"
|
||||
#else
|
||||
#define GEODE_IOS(...)
|
||||
#define GEODE_MACOS(...) __VA_ARGS__
|
||||
#define GEODE_IS_MACOS
|
||||
#define GEODE_IS_DESKTOP
|
||||
#define GEODE_PLATFORM_NAME "MacOS"
|
||||
#define GEODE_PLATFORM_TARGET PlatformID::MacOS
|
||||
#define GEODE_PLATFORM_EXTENSION ".dylib"
|
||||
#endif
|
||||
#define GEODE_CALL
|
||||
#else
|
||||
#define GEODE_MACOS(...)
|
||||
#define GEODE_IOS(...)
|
||||
#endif
|
||||
|
||||
// Android
|
||||
#if defined(__ANDROID__)
|
||||
#define GEODE_ANDROID(...) __VA_ARGS__
|
||||
#define GEODE_IS_ANDROID
|
||||
#define GEODE_IS_MOBILE
|
||||
#define GEODE_PLATFORM_NAME "Android"
|
||||
#define GEODE_PLATFORM_TARGET PlatformID::Android
|
||||
#define GEODE_CALL
|
||||
#define GEODE_PLATFORM_EXTENSION ".so"
|
||||
#else
|
||||
#define GEODE_ANDROID(...)
|
||||
#endif
|
||||
|
||||
#ifndef GEODE_PLATFORM_NAME
|
||||
#error "Unsupported PlatformID!"
|
||||
#endif
|
||||
|
||||
#ifdef GEODE_EXPORTING
|
||||
#define GEODE_DLL GEODE_WINDOWS(__declspec(dllexport))
|
||||
#else
|
||||
#define GEODE_DLL GEODE_WINDOWS(__declspec(dllimport))
|
||||
#endif
|
||||
#define GEODE_API GEODE_WINDOWS() // idk where this is
|
||||
#endif
|
||||
|
||||
#endif /* GEODE_CORE_META_PREPROC_HPP */
|
||||
|
|
|
@ -5,18 +5,18 @@ namespace geode::core::meta::x86 {
|
|||
template <class Ret, class... Args>
|
||||
class Thiscall {
|
||||
private:
|
||||
template <Ret(* detour)(Args...)>
|
||||
template <Ret (*detour)(Args...)>
|
||||
static Ret __thiscall wrapper(Args... all) {
|
||||
return detour(all...);
|
||||
}
|
||||
|
||||
public:
|
||||
static Ret invoke(void* address, Args... all) {
|
||||
Ret(__thiscall* raw)(Args...) = reinterpret_cast<decltype(raw)>(address);
|
||||
Ret(__thiscall * raw)(Args...) = reinterpret_cast<decltype(raw)>(address);
|
||||
return raw(all...);
|
||||
}
|
||||
|
||||
template <Ret(* detour)(Args...)>
|
||||
template <Ret (*detour)(Args...)>
|
||||
static constexpr decltype(auto) get_wrapper() {
|
||||
return &wrapper<detour>;
|
||||
}
|
||||
|
|
|
@ -7,29 +7,29 @@
|
|||
#include <utility>
|
||||
|
||||
namespace geode::core::meta {
|
||||
/* The Tuple class presents a nicer way to interact with parameter packs.
|
||||
* While this container is technically usable in other code, this is mostly
|
||||
* used for static reflection in function wrapping. Other applications will
|
||||
* usually be better covered by std::tuple.
|
||||
*/
|
||||
/* The Tuple class presents a nicer way to interact with parameter packs.
|
||||
* While this container is technically usable in other code, this is mostly
|
||||
* used for static reflection in function wrapping. Other applications will
|
||||
* usually be better covered by std::tuple.
|
||||
*/
|
||||
template <class...>
|
||||
class Tuple;
|
||||
|
||||
template <>
|
||||
class Tuple<> {
|
||||
private:
|
||||
template <class T, size_t i>
|
||||
template <class Type, size_t i>
|
||||
class Element {
|
||||
private:
|
||||
T value;
|
||||
Type value;
|
||||
|
||||
protected:
|
||||
constexpr T at(std::integral_constant<size_t, i>&&) const {
|
||||
constexpr Type at(std::integral_constant<size_t, i>&&) const {
|
||||
return this->value;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
constexpr Element(T value) : value(value) {}
|
||||
constexpr Element(Type value) : value(value) {}
|
||||
};
|
||||
|
||||
template <class... Parents>
|
||||
|
@ -39,6 +39,7 @@ namespace geode::core::meta {
|
|||
|
||||
public:
|
||||
static constexpr size_t size = sizeof...(Parents);
|
||||
|
||||
template <size_t i>
|
||||
constexpr decltype(auto) at() const {
|
||||
static_assert(i < size, "Out of range access!");
|
||||
|
@ -47,19 +48,18 @@ namespace geode::core::meta {
|
|||
};
|
||||
|
||||
template <class, class...>
|
||||
struct elements_for_impl;
|
||||
class elements_for_impl;
|
||||
|
||||
template <size_t... indices, class... Classes>
|
||||
struct elements_for_impl<std::index_sequence<indices...>, Classes...> {
|
||||
class elements_for_impl<std::index_sequence<indices...>, Classes...> {
|
||||
public:
|
||||
using result = Elements<Element<Classes, indices>...>;
|
||||
};
|
||||
|
||||
public:
|
||||
template <class... Classes>
|
||||
using elements_for = typename elements_for_impl<
|
||||
std::make_index_sequence<sizeof...(Classes)>,
|
||||
Classes...
|
||||
>::result;
|
||||
std::make_index_sequence<sizeof...(Classes)>, Classes...>::result;
|
||||
|
||||
template <class... Classes>
|
||||
static auto make(Classes&&... values) {
|
||||
|
@ -76,66 +76,23 @@ namespace geode::core::meta {
|
|||
template <size_t i>
|
||||
using type_at = decltype(std::declval<MyElements>().template at<i>());
|
||||
|
||||
protected:
|
||||
// Haskell (false case)
|
||||
template <
|
||||
template <size_t, class, size_t> class Pred,
|
||||
bool, size_t i, size_t counter, size_t... seq
|
||||
>
|
||||
class filter_impl {
|
||||
private:
|
||||
using NextPred = Pred<i + 1, type_at<i + 1>, counter>;
|
||||
|
||||
private:
|
||||
template <size_t i, bool>
|
||||
class type_at_wrap_impl {
|
||||
public:
|
||||
using result = typename filter_impl<
|
||||
Pred, NextPred::result, i + 1, counter, seq...
|
||||
>::result;
|
||||
using result = void;
|
||||
};
|
||||
|
||||
// Haskell (true case)
|
||||
template <
|
||||
template <size_t, class, size_t> class Pred,
|
||||
size_t i, size_t counter, size_t... seq
|
||||
>
|
||||
class filter_impl<Pred, true, i, counter, seq...> {
|
||||
private:
|
||||
using MyPred = Pred<i, type_at<i>, counter>;
|
||||
using NextPred = Pred<i + 1, type_at<i + 1>, counter>;
|
||||
|
||||
template <size_t i>
|
||||
class type_at_wrap_impl<i, true> {
|
||||
public:
|
||||
using result = typename filter_impl<
|
||||
Pred, NextPred::result, i + 1,
|
||||
MyPred::counter, seq..., MyPred::index
|
||||
>::result;
|
||||
using result = type_at<i>;
|
||||
};
|
||||
|
||||
// Haskell (final case, if true)
|
||||
template <
|
||||
template <size_t, class, size_t> class Pred,
|
||||
size_t counter, size_t... seq
|
||||
>
|
||||
class filter_impl<Pred, false, sizeof...(Rest), counter, seq...> {
|
||||
public:
|
||||
using result = std::index_sequence<seq...>;
|
||||
};
|
||||
|
||||
// Haskell (final case, if false)
|
||||
template <
|
||||
template <size_t, class, size_t> class Pred,
|
||||
size_t counter, size_t... seq
|
||||
>
|
||||
class filter_impl<Pred, true, sizeof...(Rest), counter, seq...> {
|
||||
private:
|
||||
static constexpr size_t i = sizeof...(Rest);
|
||||
using MyPred = Pred<i, type_at<i>, counter>;
|
||||
|
||||
public:
|
||||
using result = std::index_sequence<seq..., MyPred::index>;
|
||||
};
|
||||
|
||||
public:
|
||||
template <template <size_t, class, size_t> class Pred>
|
||||
using filter = typename filter_impl<Pred, Pred<0, Current, 0>::result, 0, 0>::result;
|
||||
// MSVC literally shows internal compiler structures if I don't wrap this sometimes.
|
||||
template <size_t i>
|
||||
using type_at_wrap = typename type_at_wrap_impl<i, (i < sizeof...(Rest) + 1)>::result;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,36 +1,54 @@
|
|||
#ifndef GEODE_CORE_META_X86_HPP
|
||||
#define GEODE_CORE_META_X86_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace geode::core::meta::x86 {
|
||||
// Logic needed by x86 calling conventions for stack fixing / filtering.
|
||||
template <class Class>
|
||||
struct gpr_passable {
|
||||
static constexpr bool value =
|
||||
any_of<
|
||||
std::remove_cv_t<Class>,
|
||||
bool, signed char, signed short, signed int, signed long,
|
||||
unsigned char, unsigned short, unsigned int, unsigned long,
|
||||
char, short, int, long
|
||||
> ||
|
||||
std::is_pointer_v<Class> ||
|
||||
std::is_reference_v<Class> ||
|
||||
std::is_member_function_pointer_v<Class>;
|
||||
};
|
||||
static constexpr bool sse_passable = any_of<std::remove_cv_t<Class>, float, double>;
|
||||
|
||||
template <class Class>
|
||||
struct sse_passable {
|
||||
static constexpr bool value =
|
||||
any_of<
|
||||
std::remove_cv_t<Class>,
|
||||
float, double
|
||||
>;
|
||||
};
|
||||
|
||||
static constexpr bool gpr_passable =
|
||||
((sizeof(Class) <= sizeof(void*) && !std::is_class_v<Class> && !sse_passable<Class>)
|
||||
|| std::is_member_function_pointer_v<Class>);
|
||||
|
||||
template <class... Classes>
|
||||
static inline constexpr std::array<size_t, sizeof...(Classes)> reorder_pack() {
|
||||
constexpr size_t length = sizeof...(Classes);
|
||||
constexpr bool should_reorder[] = {
|
||||
std::is_class_v<Classes> && sizeof(Classes) > sizeof(void*)...
|
||||
};
|
||||
|
||||
std::array<size_t, length> reordered = {};
|
||||
size_t out = 0;
|
||||
// Non-reordered go first.
|
||||
for (size_t in = 0; in < length; ++in) {
|
||||
if (!should_reorder[in]) {
|
||||
reordered[out] = in;
|
||||
++out;
|
||||
}
|
||||
}
|
||||
|
||||
// Reordered go last.
|
||||
for (size_t in = 0; in < length; ++in) {
|
||||
if (should_reorder[in]) {
|
||||
reordered[out] = in;
|
||||
++out;
|
||||
}
|
||||
}
|
||||
|
||||
return reordered;
|
||||
}
|
||||
|
||||
template <class... Stack>
|
||||
static constexpr size_t stack_fix =
|
||||
(((sizeof(Stack) % sizeof(void*) == 0) ?
|
||||
sizeof(Stack) :
|
||||
sizeof(Stack) - (sizeof(Stack) % sizeof(void*)) + sizeof(void*)) + ...);
|
||||
static constexpr size_t stack_fix =
|
||||
(((sizeof(Stack) % sizeof(void*) == 0)
|
||||
? sizeof(Stack)
|
||||
: sizeof(Stack) - (sizeof(Stack) % sizeof(void*)) + sizeof(void*))
|
||||
+ ...);
|
||||
|
||||
template <>
|
||||
static constexpr size_t stack_fix<> = 0;
|
||||
|
@ -38,24 +56,60 @@ namespace geode::core::meta::x86 {
|
|||
template <>
|
||||
static constexpr size_t stack_fix<void> = 0;
|
||||
|
||||
template <class From, class To>
|
||||
template <class From>
|
||||
class Register {
|
||||
public:
|
||||
From raw;
|
||||
|
||||
public:
|
||||
To get() {
|
||||
static_assert(sizeof(From) >= sizeof(To),
|
||||
template <class To>
|
||||
explicit operator To() {
|
||||
static_assert(
|
||||
sizeof(From) >= sizeof(To),
|
||||
"Please report a bug to the Geode developers! This should never be reached.\n"
|
||||
"Size of Register is smaller than the size of the destination type!");
|
||||
"Size of Register is smaller than the size of the destination type!"
|
||||
);
|
||||
|
||||
union {
|
||||
From from;
|
||||
To to;
|
||||
} u;
|
||||
|
||||
u.from = raw;
|
||||
return u.to;
|
||||
}
|
||||
};
|
||||
|
||||
// Ignore this for now, it's for discarding calling convention qualifier later.
|
||||
#if 0
|
||||
template <class Func>
|
||||
class remove_conv {
|
||||
public:
|
||||
using result = Func;
|
||||
};
|
||||
|
||||
// We all hate macros but I'd say this is a good use case.
|
||||
#define REMOVE_FOR(CC) \
|
||||
template <class Ret, class... Args> \
|
||||
class remove_conv<Ret(CC*)(Args...)> { \
|
||||
public: \
|
||||
using result = Ret (*)(Args...); \
|
||||
}; \
|
||||
\
|
||||
template <class Ret, class Parent, class... Args> \
|
||||
class remove_conv<Ret (CC Parent::*)(Args...)> { \
|
||||
public: \
|
||||
using result = Ret (Parent::*)(Args...); \
|
||||
}
|
||||
|
||||
REMOVE_FOR(__cdecl);
|
||||
REMOVE_FOR(__stdcall);
|
||||
REMOVE_FOR(__thiscall);
|
||||
REMOVE_FOR(__fastcall);
|
||||
REMOVE_FOR(__vectorcall);
|
||||
|
||||
#undef REMOVE_FOR
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* GEODE_CORE_META_X86_HPP */
|
|
@ -1,31 +0,0 @@
|
|||
#pragma once
|
||||
#include "Traits.hpp"
|
||||
|
||||
namespace geode::modifier {
|
||||
struct compare {
|
||||
template <class, class, class, class = void>
|
||||
struct constructor {
|
||||
constexpr static inline bool value = false;
|
||||
};
|
||||
|
||||
template <class Derived, class Base, class Ret, class ...Parameters>
|
||||
struct constructor<Derived, Base, Ret(Parameters...), std::void_t<
|
||||
decltype(substitute<Ret, Base, Derived, Parameters...>(&Derived::constructor))
|
||||
>> {
|
||||
constexpr static inline bool value = true;
|
||||
};
|
||||
|
||||
template <class, class, class, class = void>
|
||||
struct destructor {
|
||||
constexpr static inline bool value = false;
|
||||
};
|
||||
|
||||
template <class Derived, class Base, class Ret, class ...Parameters>
|
||||
struct destructor<Derived, Base, Ret(Parameters...), std::void_t<
|
||||
decltype(substitute<Ret, Base, Derived, Parameters...>(&Derived::destructor))
|
||||
>> {
|
||||
constexpr static inline bool value = true;
|
||||
};
|
||||
#include <gen/Compare.hpp>
|
||||
};
|
||||
}
|
|
@ -1,11 +1,24 @@
|
|||
#pragma once
|
||||
#include "Comparer.hpp"
|
||||
#include "Wrapper.hpp"
|
||||
#include "Types.hpp"
|
||||
#include "Addresses.hpp"
|
||||
#include "../meta/meta.hpp"
|
||||
#include <iostream>
|
||||
|
||||
#define GEODE_APPLY_MODIFY_FOR_FUNCTION(index, convention, className, functionName) \
|
||||
static constexpr auto base##index = wrap::functionName<Base, types::pure##index>::value; \
|
||||
static constexpr auto derived##index = wrap::functionName<Derived, types::pure##index>::value; \
|
||||
if constexpr ((void*)base##index == (void*)derived##index) { \
|
||||
Interface::get()->logInfo( \
|
||||
"Adding hook at function " #className "::" #functionName, \
|
||||
Severity::Debug \
|
||||
); \
|
||||
Interface::get()->addHook<derived##index, convention>( \
|
||||
#className "::" #functionName, \
|
||||
(void*)addresses::address##index() \
|
||||
); \
|
||||
} \
|
||||
|
||||
namespace geode::modifier {
|
||||
|
||||
template <class Derived, class Base>
|
||||
|
@ -13,6 +26,7 @@ namespace geode::modifier {
|
|||
|
||||
template <class Derived>
|
||||
class ModifyBase {
|
||||
// unordered_map<handles> idea
|
||||
ModifyBase() {
|
||||
Derived::apply();
|
||||
}
|
||||
|
|
|
@ -8,29 +8,19 @@ namespace geode::modifier {
|
|||
* The unevaluated function that gets the appropriate
|
||||
* version of a function type from its return, parameters, and classes.
|
||||
*/
|
||||
template <class Ret, class Base, class Derived, class ...Parameters>
|
||||
auto substitute(Ret(Base::*)(Parameters...)) -> Ret(Base::*)(Parameters...) {
|
||||
static_assert(always_false<Ret>, "This function is for unevaluated context");
|
||||
template <class Return, class Class, class ...Parameters>
|
||||
auto substitute(Return(Class::*)(Parameters...)) -> Return(Class::*)(Parameters...) {
|
||||
// static_assert(always_false<Ret>, "This function is for unevaluated context");
|
||||
}
|
||||
|
||||
template <class Return, class Class, class ...Parameters>
|
||||
auto substitute(Return(Class::*)(Parameters...) const) -> Return(Class::*)(Parameters...) const {
|
||||
// static_assert(always_false<Ret>, "This function is for unevaluated context");
|
||||
}
|
||||
|
||||
template <class Ret, class Base, class Derived, class ...Parameters>
|
||||
auto substitute(Ret(Derived::*)(Parameters...)) -> Ret(Derived::*)(Parameters...) {
|
||||
static_assert(always_false<Ret>, "This function is for unevaluated context");
|
||||
}
|
||||
|
||||
template <class Ret, class Base, class Derived, class ...Parameters>
|
||||
auto substitute(Ret(Base::*)(Parameters...) const) -> Ret(Base::*)(Parameters...) const {
|
||||
static_assert(always_false<Ret>, "This function is for unevaluated context");
|
||||
}
|
||||
|
||||
template <class Ret, class Base, class Derived, class ...Parameters>
|
||||
auto substitute(Ret(Derived::*)(Parameters...) const) -> Ret(Derived::*)(Parameters...) const {
|
||||
static_assert(always_false<Ret>, "This function is for unevaluated context");
|
||||
}
|
||||
|
||||
template <class Ret, class Base, class Derived, class ...Parameters>
|
||||
auto substitute(Ret(*)(Parameters...)) -> Ret(*)(Parameters...) {
|
||||
static_assert(always_false<Ret>, "This function is for unevaluated context");
|
||||
template <class Return, class Class, class ...Parameters>
|
||||
auto substitute(Return(*)(Parameters...)) -> Return(*)(Parameters...) {
|
||||
// static_assert(always_false<Ret>, "This function is for unevaluated context");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,51 +1,66 @@
|
|||
#pragma once
|
||||
#include "Traits.hpp"
|
||||
|
||||
#define GEODE_WRAPPER_FOR_IDENTIFIER(identifier) \
|
||||
/* Default - function Return Class::identifier(Parameters...) does not exist */ \
|
||||
template < \
|
||||
class Class, \
|
||||
class FunctionType, \
|
||||
class = void \
|
||||
> \
|
||||
struct identifier { \
|
||||
private: \
|
||||
static void wrapperImpl(...) {} \
|
||||
public: \
|
||||
constexpr static inline auto value = &wrapperImpl; \
|
||||
}; \
|
||||
/* Specialization - function Return Class::identifier(Parameters...) is a member function */ \
|
||||
template < \
|
||||
class Class, \
|
||||
class Return, \
|
||||
class ...Parameters \
|
||||
> \
|
||||
struct identifier< \
|
||||
Class, \
|
||||
Return(Parameters...), std::enable_if_t< \
|
||||
std::is_member_function_pointer_v< \
|
||||
decltype(substitute<Return, Class, Parameters...>(&Class::identifier)) \
|
||||
> \
|
||||
> \
|
||||
> { \
|
||||
private: \
|
||||
static Return wrapperImpl(Class* self, Parameters... ps) { \
|
||||
return self->Class::identifier(ps...); \
|
||||
} \
|
||||
public: \
|
||||
constexpr static inline auto value = &wrapperImpl; \
|
||||
}; \
|
||||
/* Specialization - function Return Class::identifier(Parameters...) is a static function */ \
|
||||
template < \
|
||||
class Class, \
|
||||
class Return, \
|
||||
class ...Parameters \
|
||||
> \
|
||||
struct identifier< \
|
||||
Class, \
|
||||
Return(Parameters...), std::enable_if_t< \
|
||||
std::is_pointer_v< \
|
||||
decltype(substitute<Return, Class, Parameters...>(&Class::identifier)) \
|
||||
> \
|
||||
> \
|
||||
> { \
|
||||
private: \
|
||||
static Return wrapperImpl(Parameters... ps) { \
|
||||
return Class::identifier(ps...); \
|
||||
} \
|
||||
public: \
|
||||
constexpr static inline auto value = &wrapperImpl; \
|
||||
}; \
|
||||
|
||||
namespace geode::modifier {
|
||||
|
||||
struct wrap {
|
||||
template <template <class, class...> class, class, class, class, class = void>
|
||||
struct constructor {
|
||||
private:
|
||||
static void wrapper(...) {}
|
||||
public:
|
||||
constexpr static inline auto value = &wrapper;
|
||||
};
|
||||
|
||||
template <template <class, class...> class Conv, class Derived, class Base, class Ret, class ...Parameters>
|
||||
struct constructor<Conv, Derived, Base, Ret(Parameters...), std::enable_if_t<
|
||||
std::is_member_function_pointer_v<decltype(substitute<Ret, Base, Derived, Parameters...>(&Derived::constructor))>
|
||||
>> {
|
||||
private:
|
||||
static Ret wrapper(Derived* self, Parameters... ps) {
|
||||
return self->Derived::constructor(ps...);
|
||||
}
|
||||
public:
|
||||
using MyConv = Conv<Ret, Derived*, Parameters...>;
|
||||
constexpr static inline auto value = MyConv::template get_wrapper<&wrapper>();
|
||||
};
|
||||
|
||||
template <template <class, class...> class, class, class, class, class = void>
|
||||
struct destructor {
|
||||
private:
|
||||
static void wrapper(...) {}
|
||||
public:
|
||||
constexpr static inline auto value = &wrapper;
|
||||
};
|
||||
|
||||
template <template <class, class...> class Conv, class Derived, class Base, class Ret, class ...Parameters>
|
||||
struct destructor<Conv, Derived, Base, Ret(Parameters...), std::enable_if_t<
|
||||
std::is_member_function_pointer_v<decltype(substitute<Ret, Base, Derived, Parameters...>(&Derived::destructor))>
|
||||
>> {
|
||||
private:
|
||||
static Ret wrapper(Derived* self, Parameters... ps) {
|
||||
return self->Derived::destructor(ps...);
|
||||
}
|
||||
public:
|
||||
using MyConv = Conv<Ret, Derived*, Parameters...>;
|
||||
constexpr static inline auto value = MyConv::template get_wrapper<&wrapper>();
|
||||
};
|
||||
|
||||
#include <gen/Wrap.hpp>
|
||||
GEODE_WRAPPER_FOR_IDENTIFIER(constructor)
|
||||
GEODE_WRAPPER_FOR_IDENTIFIER(destructor)
|
||||
#include <gen/Wrapper.hpp>
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
namespace format_strings {
|
||||
|
||||
char const* declare_address = R"CAC(
|
||||
char const* declare_address = R"GEN(
|
||||
GEODE_NOINLINE GEODE_HIDDEN inline static uintptr_t address{global_index}() {{
|
||||
static uintptr_t ret = {address};
|
||||
return ret;
|
||||
}}
|
||||
)CAC";
|
||||
)GEN";
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ add_gen(Header Header.hpp)
|
|||
# add_gen(TempName TempName.hpp)
|
||||
add_gen(Tidy Tidy.bro)
|
||||
add_gen(Compare Compare.hpp)
|
||||
add_gen(Wrap Wrap.hpp)
|
||||
add_gen(Wrapper Wrapper.hpp)
|
||||
add_gen(Modify Modify.hpp)
|
||||
add_gen(Type Type.hpp)
|
||||
add_gen(Address Address.hpp)
|
||||
|
|
36
kit/gen/DataGen.cpp
Normal file
36
kit/gen/DataGen.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "SharedGen.hpp"
|
||||
|
||||
namespace format_strings {
|
||||
|
||||
char const* declare_address = R"GEN(
|
||||
GEODE_NOINLINE GEODE_HIDDEN inline static uintptr_t address{global_index}() {{
|
||||
static uintptr_t ret = {address};
|
||||
return ret;
|
||||
}}
|
||||
)GEN";
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
string output("");
|
||||
Root root = CacShare::init(argc, argv);
|
||||
set<string> used;
|
||||
|
||||
for (auto& [name, c] : root.classes) {
|
||||
for (auto& f : c.functions) {
|
||||
switch (f.function_type) {
|
||||
case kConstructor: [[fallthrough]];
|
||||
case kDestructor: continue;
|
||||
default: break;
|
||||
}
|
||||
if (used.find(f.name) != used.end()) continue;
|
||||
|
||||
output += fmt::format(format_strings::declare_member_type,
|
||||
fmt::arg("function_name", f.name)
|
||||
);
|
||||
used.insert(f.name);
|
||||
}
|
||||
}
|
||||
|
||||
CacShare::writeFile(output);
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
#include "SharedGen.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace format_strings {
|
||||
// requires: class_name
|
||||
char const* interface_start = R"CAC(
|
||||
template<template <auto, typename> class D = BlankBase, typename UUID = void>
|
||||
struct ${class_name} : {raw_class_name}, ModifierBase {{
|
||||
${class_name}(const ${class_name}& c) : {class_name}(c) {{}}
|
||||
${class_name}() : ${class_name}(*this) {{}}
|
||||
~${class_name}() {{
|
||||
cocos2d::CCDestructor::lock(this) = true;
|
||||
}}
|
||||
GEODE_NOINLINE static inline auto& getAdditionalFields() {{
|
||||
static std::unordered_map<uintptr_t, container_t<>*> ret;
|
||||
return ret;
|
||||
}}
|
||||
GEODE_NOINLINE static inline auto& getOriginalDestructor() {{
|
||||
static uintptr_t ret;
|
||||
return ret;
|
||||
}}
|
||||
static void fieldCleanup(uintptr_t self) {{
|
||||
const uintptr_t begin = self + sizeof(${class_name});
|
||||
const uintptr_t end = self + sizeof(D<0, UUID>);
|
||||
for (uintptr_t i = begin; i < end; i += sizeof(uintptr_t)) {{
|
||||
if (getAdditionalFields().find(i) != getAdditionalFields().end()) {{
|
||||
delete getAdditionalFields().at(i);
|
||||
getAdditionalFields().erase(i);
|
||||
}}
|
||||
}}
|
||||
reinterpret_cast<void(*)(uintptr_t)>(getOriginalDestructor())(self);
|
||||
}}
|
||||
)CAC";
|
||||
|
||||
char const* declare_structor = R"CAC(
|
||||
GEODE_DUPABLE void {function_name}({raw_args}) {{
|
||||
reinterpret_cast<void(*)(decltype(this){arg_types})>(addresser::address{global_index})(this{parameters});
|
||||
}})CAC";
|
||||
|
||||
char const* apply_start = R"CAC(
|
||||
static bool _apply() {
|
||||
)CAC";
|
||||
// requires: index, class_name, arg_types, function_name, raw_arg_types, non_virtual
|
||||
char const* apply_function_member = R"CAC(
|
||||
using baseType{global_index} = temp_name_find_better::ret{global_index}({class_name}::*)({raw_arg_types}) {const};
|
||||
constexpr auto baseAddress{global_index} = (baseType{global_index})(&{class_name}::{function_name});
|
||||
using derivedType{global_index} = temp_name_find_better::ret{global_index}(D<baseAddress{global_index}, UUID>::*)({raw_arg_types}) {const};
|
||||
GEODE_VIRTUAL_CONSTEXPR auto derivedAddress{global_index} = (derivedType{global_index})(&D<baseAddress{global_index}, UUID>::{function_name});
|
||||
if (baseAddress{global_index} != derivedAddress{global_index}) {{
|
||||
Interface::get()->logInfo("Adding hook at function {class_name}::{function_name}", Severity::Debug);
|
||||
Interface::get()->addHook("{class_name}::{function_name}", (void*)addresser::address{global_index}(), (void*)addresser::get{non_virtual}Virtual(derivedAddress{global_index}));
|
||||
}}
|
||||
)CAC";
|
||||
|
||||
char const* apply_function_structor = R"CAC(
|
||||
using baseType{global_index} = temp_name_find_better::ret{global_index}(${class_name}::*)({raw_arg_types}) {const};
|
||||
constexpr auto baseAddress{global_index} = (baseType{global_index})(&${class_name}::{function_name});
|
||||
using derivedType{global_index} = temp_name_find_better::ret{global_index}(D<baseAddress{global_index}, UUID>::*)({raw_arg_types}) {const};
|
||||
GEODE_VIRTUAL_CONSTEXPR auto derivedAddress{global_index} = (derivedType{global_index})(&D<baseAddress{global_index}, UUID>::{function_name});
|
||||
if (baseAddress{global_index} != derivedAddress{global_index}) {{
|
||||
Interface::get()->logInfo("Adding hook at function {class_name}::{function_name}", Severity::Debug);
|
||||
Interface::get()->addHook("{class_name}::{function_name}", (void*)addresser::address{global_index}(), (void*)addresser::get{non_virtual}Virtual(derivedAddress{global_index}));
|
||||
}}
|
||||
)CAC";
|
||||
|
||||
char const* apply_function_static = R"CAC(
|
||||
using baseType{global_index} = temp_name_find_better::ret{global_index}(*)({raw_arg_types});
|
||||
constexpr auto baseAddress{global_index} = (baseType{global_index})(&{class_name}::{function_name});
|
||||
using derivedType{global_index} = temp_name_find_better::ret{global_index}(*)({raw_arg_types});
|
||||
GEODE_VIRTUAL_CONSTEXPR auto derivedAddress{global_index} = (derivedType{global_index})(&D<baseAddress{global_index}, UUID>::{function_name});
|
||||
if (baseAddress{global_index} != derivedAddress{global_index}) {{
|
||||
Interface::get()->logInfo("Adding hook at function {class_name}::{function_name}", Severity::Debug);
|
||||
Interface::get()->addHook("{class_name}::{function_name}", (void*)temp_name_find_better::address{global_index}(), (void*)addresser::getNonVirtual(derivedAddress{global_index}));
|
||||
}}
|
||||
)CAC";
|
||||
|
||||
char const* apply_end = R"CAC(
|
||||
return true;
|
||||
}
|
||||
)CAC";
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
auto root = CacShare::init(argc, argv);
|
||||
string output;
|
||||
|
||||
for (auto& [name, c] : root.classes) {
|
||||
string unqualifiedName = CacShare::toUnqualified(name);
|
||||
|
||||
output += fmt::format(format_strings::interface_start, fmt::arg("class_name", unqualifiedName), fmt::arg("raw_class_name", name));
|
||||
|
||||
for (auto& f : c.functions) {
|
||||
if (!CacShare::functionDefined(f))
|
||||
continue; // Function not supported for this platform, skip it
|
||||
string name;
|
||||
switch (f.function_type) {
|
||||
case kDestructor:
|
||||
name = "destructor";
|
||||
case kConstructor:
|
||||
if (name != "destructor") name = "constructor";
|
||||
output += fmt::format(format_strings::declare_structor,
|
||||
fmt::arg("arg_types", CacShare::formatArgTypes(f.args)),
|
||||
fmt::arg("raw_arg_types", CacShare::formatRawArgTypes(f.args)),
|
||||
fmt::arg("class_name", unqualifiedName),
|
||||
fmt::arg("const", f.is_const ? "const " : ""),
|
||||
fmt::arg("convention", CacShare::getConvention(f)),
|
||||
fmt::arg("function_name",name),
|
||||
fmt::arg("global_index",f.hash()),
|
||||
fmt::arg("parameters", CacShare::formatParameters(f.args.size())),
|
||||
fmt::arg("raw_args", CacShare::formatRawArgs(f.args)),
|
||||
fmt::arg("raw_parameters", CacShare::formatRawParameters(f.args.size()))
|
||||
);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
output += format_strings::apply_start;
|
||||
|
||||
for (auto& f : c.functions) {
|
||||
if (!CacShare::functionDefined(f))
|
||||
continue; // Function not supported for this platform, skip it
|
||||
|
||||
char const* used_format;
|
||||
string name = f.name;
|
||||
switch (f.function_type) {
|
||||
case kStaticFunction:
|
||||
used_format = format_strings::apply_function_static;
|
||||
break;
|
||||
case kDestructor:
|
||||
name = "destructor";
|
||||
case kConstructor:
|
||||
if (name != "destructor") name = "constructor";
|
||||
used_format = format_strings::apply_function_structor;
|
||||
break;
|
||||
default:
|
||||
used_format = format_strings::apply_function_member;
|
||||
break;
|
||||
}
|
||||
|
||||
output += fmt::format(used_format,
|
||||
fmt::arg("global_index",f.hash()),
|
||||
fmt::arg("class_name", unqualifiedName),
|
||||
fmt::arg("arg_types", CacShare::formatArgTypes(f.args)),
|
||||
fmt::arg("function_name", name),
|
||||
fmt::arg("raw_arg_types", CacShare::formatRawArgTypes(f.args)),
|
||||
fmt::arg("non_virtual", f.function_type == kVirtualFunction ? "" : "Non"),
|
||||
fmt::arg("const", f.is_const ? "const " : "")
|
||||
);
|
||||
}
|
||||
|
||||
output += format_strings::apply_end;
|
||||
output += "};\n";
|
||||
}
|
||||
|
||||
// fmt::print("{}", output);
|
||||
CacShare::writeFile(output);
|
||||
}
|
|
@ -15,16 +15,7 @@ struct Modify<Derived, {class_name}> : ModifyBase<Modify<Derived, {class_name}>>
|
|||
|
||||
// requires: index, class_name, arg_types, function_name, raw_arg_types, non_virtual
|
||||
char const* apply_function = R"RAW(
|
||||
if constexpr (compare::{function_name}<Derived, Base, types::pure{global_index}>::value) {{
|
||||
Interface::get()->logInfo(
|
||||
"Adding hook at function {class_name}::{function_name}",
|
||||
Severity::Debug
|
||||
);
|
||||
Interface::get()->addHook<wrap::{function_name}<DefaultConv, Derived, Base, types::pure{global_index}>::value, {function_convention}>(
|
||||
"{class_name}::{function_name}",
|
||||
(void*)addresses::address{global_index}()
|
||||
);
|
||||
}}
|
||||
GEODE_APPLY_MODIFY_FOR_FUNCTION({global_index}, {function_convention}, {class_name}, {function_name})
|
||||
)RAW";
|
||||
|
||||
char const* modify_end = R"RAW(
|
||||
|
|
|
@ -141,6 +141,59 @@ struct CacShare {
|
|||
return types;
|
||||
}
|
||||
|
||||
static void editArguments(Function& f) {
|
||||
for (size_t i = 0; i < f.argnames.size(); ++i) {
|
||||
if (f.argnames[i] == "") {
|
||||
f.argnames[i] = fmt::format("p{}", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static string getIndex(Function const& f) {
|
||||
return fmt::format("{}", f.global_index);
|
||||
}
|
||||
|
||||
static string getClassName(Function const& f) {
|
||||
return fmt::format("{}", f.parent_class->name);
|
||||
}
|
||||
|
||||
static string getFunctionName(Function const& f) {
|
||||
return fmt::format("{}", f.name);
|
||||
}
|
||||
|
||||
static string getConst(Function const& f) {
|
||||
return fmt::format("{}", f.is_const ? "const" : "");
|
||||
}
|
||||
|
||||
static string getConstWhitespace(Function const& f) {
|
||||
return fmt::format("{}", f.is_const ? " " : "");
|
||||
}
|
||||
|
||||
static string getParameters(Function const& f) { // int p0, float p1
|
||||
// return fmt::format("{} {}", fmt::join(f.args, ""), fmt::join(f.argnames, ", "));
|
||||
return formatRawArgs(f.args, f.argnames);
|
||||
}
|
||||
|
||||
static string getParameterTypes(Function const& f) { //int, float
|
||||
return fmt::format("{}", fmt::join(f.args, ", "));
|
||||
}
|
||||
|
||||
static string getArguments(Function const& f) { // p0, p1
|
||||
return fmt::format("{}", fmt::join(f.argnames, ", "));
|
||||
}
|
||||
|
||||
static string getParameterComma(Function const& f) { // int p0, float p1
|
||||
return fmt::format("{}", f.args.size() > 0 ? ", " : "");
|
||||
}
|
||||
|
||||
static string getParameterTypeComma(Function const& f) { //int, float
|
||||
return fmt::format("{}", f.args.size() > 0 ? ", " : "");
|
||||
}
|
||||
|
||||
static string getArgumentComma(Function const& f) { // p0, p1
|
||||
return fmt::format("{}", f.argnames.size() > 0 ? ", " : "");
|
||||
}
|
||||
|
||||
static string formatArgTypes(vector<string> args) {
|
||||
return args.size() > 0 ? fmt::format(", {}", fmt::join(removeStruct(args), ", ")) : string("");
|
||||
}
|
||||
|
@ -189,6 +242,8 @@ struct CacShare {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static string formatParameters(vector<int> const& params, vector<string> const& names) {
|
||||
if (params.size()) {
|
||||
vector<string> c;
|
||||
|
|
|
@ -9,92 +9,53 @@ namespace format_strings {
|
|||
using namespace geode;
|
||||
using namespace geode::cast;
|
||||
using cocos2d::CCDestructor;
|
||||
using namespace geode::core::meta;
|
||||
using namespace geode::modifier;
|
||||
using namespace geode::core::meta; // Default convention
|
||||
using namespace geode::core::meta::x86; // Windows x86 conventions, Function
|
||||
using namespace geode::modifier; // types
|
||||
)CAC";
|
||||
|
||||
char const* declare_member_type = R"CAC(
|
||||
using ret{global_index} = types::ret{global_index};
|
||||
using func{global_index} = ret{global_index}(*)({const}{constw}{class_name}*{arg_types});
|
||||
using pure{global_index} = ret{global_index}({class_name}*{arg_types});
|
||||
using member{global_index} = ret{global_index}({class_name}::*)({raw_arg_types}){const};)CAC";
|
||||
|
||||
char const* declare_member_type_fixed_orig = R"CAC(
|
||||
using fixori_func{global_index} = ret{global_index}(*)({const}{constw}{class_name}*{arg_types});
|
||||
using fixori_member{global_index} = ret{global_index}({class_name}::*)({raw_arg_types}){const};
|
||||
)CAC";
|
||||
|
||||
char const* declare_static_type = R"CAC(
|
||||
using ret{global_index} = types::ret{global_index};
|
||||
using func{global_index} = ret{global_index}(*)({raw_arg_types});
|
||||
using pure{global_index} = ret{global_index}({raw_arg_types});
|
||||
using member{global_index} = func{global_index};)CAC";
|
||||
|
||||
char const* declare_static_type_fixed_orig = R"CAC(
|
||||
using fixori_func{global_index} = ret{global_index}(*)({raw_arg_types});
|
||||
using fixori_member{global_index} = fixori_func{global_index};
|
||||
)CAC";
|
||||
|
||||
char const* declare_structor_type = R"CAC(
|
||||
using ret{global_index} = void;
|
||||
using func{global_index} = ret{global_index}(*)({class_name}*{arg_types});
|
||||
using pure{global_index} = ret{global_index}({class_name}*{arg_types});
|
||||
using member{global_index} = func{global_index};)CAC";
|
||||
|
||||
char const* declare_structor_type_fixed_orig = R"CAC(
|
||||
using func{global_index} = ret{global_index}(*)({class_name}*{arg_types});
|
||||
using fixori_member{global_index} = fixori_func{global_index};
|
||||
)CAC";
|
||||
|
||||
char const* declare_address_of = R"CAC(
|
||||
template<>
|
||||
struct address_of_t<(member{global_index})(&{class_name}::{function_name})> {{
|
||||
static inline auto value = addresses::address{global_index}();
|
||||
}};)CAC";
|
||||
|
||||
char const* declare_address_of_fixed = R"CAC(
|
||||
template<>
|
||||
struct address_of_t<(fixori_member{global_index})(&{class_name}::{function_name})> {{
|
||||
static inline auto value = addresses::address{global_index}();
|
||||
}};)CAC";
|
||||
|
||||
char const* thunk_adjust = "addresser::thunkAdjust(({is_fixori}member{global_index})(&{class_name}::{function_name}), this)";
|
||||
|
||||
char const* declare_member_function = "reinterpret_cast<func{global_index}>(addresses::address{global_index}())(this{parameters})";
|
||||
char const* declare_virtual_function = "reinterpret_cast<func{global_index}>(addresses::address{global_index}())(this{parameters})";
|
||||
char const* declare_static_function = "reinterpret_cast<func{global_index}>(addresses::address{global_index}())({raw_parameters})";
|
||||
char const* declare_meta_member_function = "Function<pure{global_index}, x86::{convention}>({{addresses::address{global_index}()}})({const_cast}(this){parameters})";
|
||||
char const* declare_meta_virtual_function = "Function<pure{global_index}, x86::{convention}>({{addresses::address{global_index}()}})({const_cast}({thunk_adjusted_this}){parameters})";
|
||||
char const* declare_meta_static_function = "Function<pure{global_index}, x86::{convention}>({{addresses::address{global_index}()}})({raw_parameters})";
|
||||
|
||||
char const* declare_member = R"CAC(
|
||||
ret{global_index} {class_name}::{function_name}({raw_args}){constw}{const} {{
|
||||
return {function_implementation};
|
||||
char const* declare_member = R"GEN(
|
||||
types::ret{index} {class_name}::{function_name}({parameters}){const_whitespace}{const} {{
|
||||
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
|
||||
return func(this{argument_comma}{arguments});
|
||||
}}
|
||||
)CAC";
|
||||
)GEN";
|
||||
|
||||
char const* declare_static = R"CAC(
|
||||
ret{global_index} {class_name}::{function_name}({raw_args}){constw}{const} {{
|
||||
return {function_implementation};
|
||||
char const* declare_virtual = R"GEN(
|
||||
types::ret{index} {class_name}::{function_name}({parameters}){const_whitespace}{const} {{
|
||||
auto self = addresser::thunkAdjust((member{index})(&{class_name}::{function_name}), this);
|
||||
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
|
||||
return func(self{argument_comma}{arguments});
|
||||
}}
|
||||
)CAC";
|
||||
)GEN";
|
||||
|
||||
char const* declare_destructor = R"CAC(
|
||||
{class_name}::{function_name}() {{
|
||||
char const* declare_static = R"GEN(
|
||||
types::ret{index} {class_name}::{function_name}({parameters}){const_whitespace}{const} {{
|
||||
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
|
||||
return func({arguments});
|
||||
}}
|
||||
)GEN";
|
||||
|
||||
char const* declare_destructor = R"GEN(
|
||||
{class_name}::{function_name}({parameters}) {{
|
||||
if (CCDestructor::lock(this)) return;
|
||||
CCDestructor::lock(this) = true;
|
||||
{function_implementation};
|
||||
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
|
||||
func(this{argument_comma}{arguments});
|
||||
}}
|
||||
)CAC";
|
||||
)GEN";
|
||||
|
||||
char const* declare_constructor = R"CAC(
|
||||
{class_name}::{function_name}({raw_args}) : {class_name}(*this) {{
|
||||
{function_implementation};
|
||||
char const* declare_constructor = R"GEN(
|
||||
{class_name}::{function_name}({parameters}) : {class_name}(*this) {{
|
||||
auto func = Function<types::meta{index}, {convention}>({{addresses::address{index}()}});
|
||||
func(this{argument_comma}{arguments});
|
||||
}}
|
||||
)CAC";
|
||||
)GEN";
|
||||
|
||||
// requires: static, return_type, function_name, raw_parameters, const, class_name, definition
|
||||
char const* ool_function_definition = "{return_type} {class_name}::{function_name}({raw_params}){constw}{const} {definition}\n";
|
||||
char const* ool_function_definition = R"GEN(
|
||||
types::ret{index} {class_name}::{function_name}({parameters}){const_whitespace}{const} {definition}
|
||||
)GEN";
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
@ -103,35 +64,20 @@ int main(int argc, char** argv) {
|
|||
Root root = CacShare::init(argc, argv);
|
||||
|
||||
for (auto& [name, c] : root.classes) {
|
||||
string unqualifiedName = CacShare::toUnqualified(name);
|
||||
|
||||
|
||||
for (auto& f : c.functions) {
|
||||
auto function_name = f.name;
|
||||
auto raw_args = CacShare::formatRawArgs(f.args);
|
||||
auto raw_arg_types = CacShare::formatRawArgTypes(f.args);
|
||||
auto arg_types = CacShare::formatArgTypes(f.args);
|
||||
auto raw_parameters = CacShare::formatRawParameters(f.args.size());
|
||||
auto parameters = CacShare::formatParameters(f.args.size());
|
||||
|
||||
auto [reordered_args, fix_args] = CacShare::reorderStructs(f);
|
||||
|
||||
if (reordered_args.size() && CacShare::platform == kWindows) {
|
||||
function_name = "_" + f.name;
|
||||
raw_args = CacShare::formatRawArgs(fix_args);
|
||||
raw_arg_types = CacShare::formatRawArgTypes(fix_args);
|
||||
arg_types = CacShare::formatArgTypes(fix_args);
|
||||
raw_parameters = CacShare::formatRawParameters(fix_args.size());
|
||||
parameters = CacShare::formatParameters(fix_args.size());
|
||||
}
|
||||
|
||||
CacShare::editArguments(f);
|
||||
|
||||
if (f.is_defined) {
|
||||
output += fmt::format(format_strings::ool_function_definition,
|
||||
fmt::arg("return_type", CacShare::getReturn(f)),
|
||||
fmt::arg("function_name", function_name),
|
||||
fmt::arg("raw_params", CacShare::formatRawArgs(f.args, f.argnames)),
|
||||
fmt::arg("const", f.is_const ? "const" : ""),
|
||||
fmt::arg("constw", f.is_const ? " " : ""),
|
||||
fmt::arg("class_name", f.parent_class->name),
|
||||
fmt::arg("function_name", CacShare::getFunctionName(f)),
|
||||
fmt::arg("const", CacShare::getConst(f)),
|
||||
fmt::arg("const_whitespace", CacShare::getConstWhitespace(f)),
|
||||
fmt::arg("class_name", CacShare::getClassName(f)),
|
||||
fmt::arg("parameters", CacShare::getParameters(f)),
|
||||
fmt::arg("index", CacShare::getIndex(f)),
|
||||
fmt::arg("definition", f.definition)
|
||||
);
|
||||
continue;
|
||||
|
@ -139,12 +85,11 @@ int main(int argc, char** argv) {
|
|||
if (f.binds[CacShare::platform].size() == 0) continue; // Function not implemented, skip
|
||||
|
||||
char const* used_declare_format;
|
||||
char const* used_function_format;
|
||||
char const* used_type_format;
|
||||
char const* used_type_format_fixed_orig;
|
||||
|
||||
switch (f.function_type) {
|
||||
case kVirtualFunction:
|
||||
used_declare_format = format_strings::declare_virtual;
|
||||
break;
|
||||
case kRegularFunction:
|
||||
used_declare_format = format_strings::declare_member;
|
||||
break;
|
||||
|
@ -158,163 +103,21 @@ int main(int argc, char** argv) {
|
|||
used_declare_format = format_strings::declare_constructor;
|
||||
break;
|
||||
}
|
||||
switch (f.function_type) {
|
||||
case kVirtualFunction:
|
||||
used_function_format = CacShare::platform == kWindows ?
|
||||
format_strings::declare_meta_virtual_function :
|
||||
format_strings::declare_virtual_function;
|
||||
break;
|
||||
case kRegularFunction:
|
||||
case kDestructor:
|
||||
case kConstructor:
|
||||
used_function_format = CacShare::platform == kWindows ?
|
||||
format_strings::declare_meta_member_function :
|
||||
format_strings::declare_member_function;
|
||||
break;
|
||||
case kStaticFunction:
|
||||
used_function_format = CacShare::platform == kWindows ?
|
||||
format_strings::declare_meta_static_function :
|
||||
format_strings::declare_static_function;
|
||||
break;
|
||||
}
|
||||
switch (f.function_type) {
|
||||
case kVirtualFunction:
|
||||
case kRegularFunction:
|
||||
used_type_format = format_strings::declare_member_type;
|
||||
used_type_format_fixed_orig = format_strings::declare_member_type_fixed_orig;
|
||||
break;
|
||||
case kStaticFunction:
|
||||
used_type_format = format_strings::declare_static_type;
|
||||
used_type_format_fixed_orig = format_strings::declare_static_type_fixed_orig;
|
||||
break;
|
||||
case kDestructor:
|
||||
case kConstructor:
|
||||
used_type_format = format_strings::declare_structor_type;
|
||||
used_type_format_fixed_orig = format_strings::declare_structor_type_fixed_orig;
|
||||
break;
|
||||
}
|
||||
|
||||
if (name == "" || name == "cocos2d::") {
|
||||
used_type_format = format_strings::declare_static_type;
|
||||
used_type_format_fixed_orig = format_strings::declare_static_type_fixed_orig;
|
||||
used_function_format = CacShare::platform == kWindows ?
|
||||
format_strings::declare_meta_static_function :
|
||||
format_strings::declare_static_function;
|
||||
used_declare_format = format_strings::declare_static;
|
||||
}
|
||||
|
||||
// cout << "352947u" << endl;
|
||||
|
||||
output += fmt::format(used_type_format,
|
||||
fmt::arg("arg_types", arg_types),
|
||||
fmt::arg("raw_arg_types", raw_arg_types),
|
||||
fmt::arg("class_name", name),
|
||||
fmt::arg("unqualified_name", unqualifiedName),
|
||||
fmt::arg("const", f.is_const ? "const " : ""),
|
||||
fmt::arg("constw", f.is_const ? " " : ""),
|
||||
fmt::arg("convention", CacShare::getConvention(f)),
|
||||
fmt::arg("function_name", function_name),
|
||||
fmt::arg("index",f.index),
|
||||
fmt::arg("global_index", f.hash()),
|
||||
fmt::arg("raw_args", raw_args),
|
||||
fmt::arg("raw_parameters", raw_parameters),
|
||||
fmt::arg("parameters", parameters),
|
||||
fmt::arg("return", CacShare::getReturn(f))
|
||||
);
|
||||
|
||||
// address_of doesn't make sense to be defined
|
||||
// for the platform _function version (since
|
||||
// that's private so the user can't access that;
|
||||
// however address_of also needs some type info,
|
||||
// so this is providing the bare minimum so you
|
||||
// can get the address of the function using
|
||||
// address_of through the "overload" (i.e.
|
||||
// android sig version)
|
||||
|
||||
const char* address_of = format_strings::declare_address_of;
|
||||
if (reordered_args.size()) {
|
||||
output += fmt::format(used_type_format_fixed_orig,
|
||||
fmt::arg("arg_types", CacShare::formatArgTypes(f.args)),
|
||||
fmt::arg("raw_arg_types", CacShare::formatRawArgTypes(f.args)),
|
||||
fmt::arg("class_name", name),
|
||||
fmt::arg("unqualified_name", unqualifiedName),
|
||||
fmt::arg("const", f.is_const ? "const " : ""),
|
||||
fmt::arg("constw", f.is_const ? " " : ""),
|
||||
fmt::arg("convention", CacShare::getConvention(f)),
|
||||
fmt::arg("function_name", f.name),
|
||||
fmt::arg("index",f.index),
|
||||
fmt::arg("global_index", f.hash()),
|
||||
fmt::arg("raw_args", CacShare::formatRawArgs(f.args)),
|
||||
fmt::arg("raw_parameters", CacShare::formatRawParameters(f.args.size())),
|
||||
fmt::arg("parameters", CacShare::formatParameters(f.args.size())),
|
||||
fmt::arg("return", CacShare::getReturn(f))
|
||||
);
|
||||
address_of = format_strings::declare_address_of_fixed;
|
||||
}
|
||||
// cout << "4tter" << endl;
|
||||
|
||||
switch (f.function_type) {
|
||||
case kVirtualFunction:
|
||||
case kRegularFunction:
|
||||
case kStaticFunction:
|
||||
output += fmt::format(address_of,
|
||||
fmt::arg("global_index",f.hash()),
|
||||
fmt::arg("class_name", name),
|
||||
fmt::arg("index",f.index),
|
||||
fmt::arg("unqualified_name", unqualifiedName),
|
||||
fmt::arg("function_name", f.name)
|
||||
);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// cout << "rwerwe" << endl
|
||||
|
||||
std::string this_thunked = "this";
|
||||
|
||||
if (f.function_type != kConstructor && f.function_type != kDestructor) {
|
||||
this_thunked = fmt::format(format_strings::thunk_adjust,
|
||||
fmt::arg("class_name", name),
|
||||
fmt::arg("function_name", function_name),
|
||||
fmt::arg("global_index", f.hash()),
|
||||
fmt::arg("is_fixori", reordered_args.size() ? "fixori_" : "")
|
||||
);
|
||||
}
|
||||
|
||||
auto function_implementation = fmt::format(used_function_format,
|
||||
fmt::arg("arg_types", arg_types),
|
||||
fmt::arg("raw_arg_types", raw_arg_types),
|
||||
fmt::arg("class_name", name),
|
||||
fmt::arg("unqualified_name", unqualifiedName),
|
||||
fmt::arg("const", f.is_const ? "const " : ""),
|
||||
fmt::arg("constw", f.is_const ? " " : ""),
|
||||
fmt::arg("const_cast", f.is_const ? "const_cast<" + name + "*>" : ""),
|
||||
fmt::arg("convention", CacShare::getConvention(f)),
|
||||
fmt::arg("function_name", function_name),
|
||||
fmt::arg("index",f.index),
|
||||
fmt::arg("global_index", f.hash()),
|
||||
fmt::arg("raw_args", raw_args),
|
||||
fmt::arg("raw_parameters", raw_parameters),
|
||||
fmt::arg("parameters", parameters),
|
||||
fmt::arg("return", CacShare::getReturn(f)),
|
||||
fmt::arg("thunk_adjusted_this", this_thunked)
|
||||
);
|
||||
|
||||
// cout << "dsffdssd" << endl;
|
||||
output += fmt::format(used_declare_format,
|
||||
fmt::arg("arg_types", arg_types),
|
||||
fmt::arg("raw_arg_types", raw_arg_types),
|
||||
fmt::arg("class_name", name),
|
||||
fmt::arg("unqualified_name", unqualifiedName),
|
||||
fmt::arg("const", f.is_const ? "const " : ""),
|
||||
fmt::arg("constw", f.is_const ? " " : ""),
|
||||
fmt::arg("class_name", CacShare::getClassName(f)),
|
||||
fmt::arg("const", CacShare::getConst(f)),
|
||||
fmt::arg("const_whitespace", CacShare::getConstWhitespace(f)),
|
||||
fmt::arg("convention", CacShare::getConvention(f)),
|
||||
fmt::arg("function_name",function_name),
|
||||
fmt::arg("index",f.index),
|
||||
fmt::arg("global_index", f.hash()),
|
||||
fmt::arg("raw_args", raw_args),
|
||||
fmt::arg("raw_parameters", raw_parameters),
|
||||
fmt::arg("function_implementation", function_implementation)
|
||||
fmt::arg("function_name", CacShare::getFunctionName(f)),
|
||||
fmt::arg("index", CacShare::getIndex(f)),
|
||||
fmt::arg("parameters", CacShare::getParameters(f)),
|
||||
fmt::arg("parameter_types", CacShare::getParameterTypes(f)),
|
||||
fmt::arg("arguments", CacShare::getArguments(f)),
|
||||
fmt::arg("parameter_comma", CacShare::getParameterComma(f)),
|
||||
fmt::arg("parameter_type_comma", CacShare::getParameterTypeComma(f)),
|
||||
fmt::arg("argument_comma", CacShare::getArgumentComma(f))
|
||||
);
|
||||
}
|
||||
if (CacShare::platform == kMac || CacShare::platform == kIos) {
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
#include "SharedGen.hpp"
|
||||
#include <set>
|
||||
|
||||
namespace format_strings {
|
||||
|
||||
char const* declare_member_type = R"RAW(
|
||||
template <template <class, class...> class, class, class, class, class = void>
|
||||
struct {function_name} {{
|
||||
private:
|
||||
static void wrapper(...) {{}}
|
||||
public:
|
||||
constexpr static inline auto value = &wrapper;
|
||||
}};
|
||||
|
||||
template <template <class, class...> class Conv, class Derived, class Base, class Ret, class ...Parameters>
|
||||
struct {function_name}<Conv, Derived, Base, Ret(Parameters...), std::enable_if_t<
|
||||
std::is_member_function_pointer_v<decltype(substitute<Ret, Base, Derived, Parameters...>(&Derived::{function_name}))>
|
||||
>> {{
|
||||
private:
|
||||
static Ret wrapper(Derived* self, Parameters... ps) {{
|
||||
return self->Derived::{function_name}(ps...);
|
||||
}}
|
||||
public:
|
||||
using MyConv = Conv<Ret, Derived*, Parameters...>;
|
||||
constexpr static inline auto value = MyConv::template get_wrapper<&wrapper>();
|
||||
}};
|
||||
|
||||
template <template <class, class...> class Conv, class Derived, class Base, class Ret, class ...Parameters>
|
||||
struct {function_name}<Conv, Derived, Base, Ret(Parameters...), std::enable_if_t<
|
||||
is_function_pointer_v<decltype(substitute<Ret, Base, Derived, Parameters...>(&Derived::{function_name}))>
|
||||
>> {{
|
||||
private:
|
||||
static Ret wrapper(Parameters... ps) {{
|
||||
return Derived::{function_name}(ps...);
|
||||
}}
|
||||
public:
|
||||
using MyConv = Conv<Ret, Parameters...>;
|
||||
constexpr static inline auto value = MyConv::template get_wrapper<&wrapper>();
|
||||
}};
|
||||
|
||||
)RAW";
|
||||
}
|
||||
|
||||
using std::set;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
string output("");
|
||||
Root root = CacShare::init(argc, argv);
|
||||
set<string> used;
|
||||
|
||||
for (auto& [name, c] : root.classes) {
|
||||
for (auto& f : c.functions) {
|
||||
switch (f.function_type) {
|
||||
case kConstructor: [[fallthrough]];
|
||||
case kDestructor: continue;
|
||||
default: break;
|
||||
}
|
||||
if (used.find(f.name) != used.end()) continue;
|
||||
output += fmt::format(format_strings::declare_member_type,
|
||||
fmt::arg("function_name", f.name)
|
||||
);
|
||||
used.insert(f.name);
|
||||
}
|
||||
}
|
||||
|
||||
CacShare::writeFile(output);
|
||||
}
|
34
kit/gen/WrapperGen.cpp
Normal file
34
kit/gen/WrapperGen.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include "SharedGen.hpp"
|
||||
#include <set>
|
||||
|
||||
namespace format_strings {
|
||||
|
||||
char const* declare_member_type = R"GEN(
|
||||
GEODE_WRAPPER_FOR_IDENTIFIER({function_name})
|
||||
)GEN";
|
||||
}
|
||||
|
||||
using std::set;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
string output("");
|
||||
Root root = CacShare::init(argc, argv);
|
||||
set<string> used;
|
||||
|
||||
for (auto& [name, c] : root.classes) {
|
||||
for (auto& f : c.functions) {
|
||||
switch (f.function_type) {
|
||||
case kConstructor: [[fallthrough]];
|
||||
case kDestructor: continue;
|
||||
default: break;
|
||||
}
|
||||
if (used.find(f.name) != used.end()) continue;
|
||||
output += fmt::format(format_strings::declare_member_type,
|
||||
fmt::arg("function_name", CacShare::getFunctionName(f))
|
||||
);
|
||||
used.insert(f.name);
|
||||
}
|
||||
}
|
||||
|
||||
CacShare::writeFile(output);
|
||||
}
|
Loading…
Reference in a new issue