a lot of changes

This commit is contained in:
altalk23 2022-04-22 12:47:54 +03:00
parent 988418fbd6
commit 65deabea53
28 changed files with 811 additions and 1052 deletions

2
bin

@ -1 +1 @@
Subproject commit 651a2fd305f408caf619794c781e19c57fa9cd29
Subproject commit d4318890cdcd8ba95ddd3abf2c102af30e46b5a0

View file

@ -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;

View file

@ -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;
};
}

View file

@ -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 */

View file

@ -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;

View file

@ -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;
}

View file

@ -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...);
}
};
}

View file

@ -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 */

View file

@ -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>;
}

View file

@ -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 */

View file

@ -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>;
}

View file

@ -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 */

View file

@ -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>;
}

View file

@ -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;
};
}

View file

@ -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 */

View file

@ -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>
};
}

View file

@ -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();
}

View file

@ -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");
}
/**

View file

@ -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>
};
}

View file

@ -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";
}

View file

@ -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
View 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);
}

View file

@ -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);
}

View file

@ -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(

View file

@ -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;

View file

@ -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) {

View file

@ -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
View 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);
}