geode/loader/include/Geode/utils/addresser.hpp

233 lines
7.7 KiB
C++
Raw Normal View History

2022-07-30 12:24:03 -04:00
#pragma once
/**
* Adapted from https://gist.github.com/altalk23/29b97969e9f0624f783b673f6c1cd279
*/
2023-01-30 01:53:20 -05:00
#include "../utils/casts.hpp"
2022-10-30 14:59:20 -04:00
#include "casts.hpp"
#include <Geode/DefaultInclude.hpp>
2023-01-30 06:10:39 -05:00
#include <cocos-ext.h>
2022-07-30 12:24:03 -04:00
#include <cstdlib>
#include <stddef.h>
#include <type_traits>
2023-01-30 09:29:41 -05:00
#include <concepts>
2022-07-30 12:24:03 -04:00
namespace geode::addresser {
2022-10-30 14:59:20 -04:00
template <typename T>
inline intptr_t getVirtual(T func);
template <typename T>
inline intptr_t getNonVirtual(T func);
template <typename T, typename F>
inline F thunkAdjust(T func, F self);
template <typename T, typename F>
inline F rthunkAdjust(T func, F self);
2023-01-30 02:06:58 -05:00
template <class Class>
2023-01-30 03:07:18 -05:00
Class* friendCreate(typename std::void_t<decltype(static_cast<Class* (*)()>(&Class::create))>*) {
2023-01-30 03:36:31 -05:00
auto ret = Class::create();
ret->retain();
return ret;
}
template <class Class>
2023-01-30 03:02:32 -05:00
concept HasCreate = requires() {
{ friendCreate<Class>(nullptr) } -> std::same_as<Class*>;
};
2023-01-30 02:06:58 -05:00
2022-10-30 14:59:20 -04:00
class GEODE_DLL Addresser final {
template <char C>
struct SingleInheritance {
virtual ~SingleInheritance() {}
};
struct MultipleInheritance : SingleInheritance<'L'>, SingleInheritance<'F'> {
virtual ~MultipleInheritance() {}
};
static MultipleInheritance* instance();
template <typename R, typename T, typename... Ps>
static ptrdiff_t indexOf(R (T::*func)(Ps...)) {
using method_t = ptrdiff_t (T::*)();
return (reinterpret_cast<T*>(instance())->*reinterpret_cast<method_t>(func))();
}
template <typename R, typename T, typename... Ps>
static ptrdiff_t indexOf(R (T::*func)(Ps...) const) {
return indexOf(reinterpret_cast<R (T::*)(Ps...)>(func));
}
template <typename T>
static ptrdiff_t thunkOf(T ptr) {
// msvc
if (sizeof(T) == sizeof(ptrdiff_t)) return 0;
// all
auto thunk = *(reinterpret_cast<ptrdiff_t*>(&ptr) + 1);
// arm
if (thunk & 1) thunk >>= 1;
return thunk;
}
2023-01-30 02:06:58 -05:00
// I gave up
// template <HasCreate Class>
// static Class* generateInstance(Class*) {
// return friendCreate<Class>(nullptr);
// }
2023-01-30 02:06:58 -05:00
2023-01-30 06:10:39 -05:00
// I extra gave up
2023-02-25 13:25:12 -05:00
template <class = void>
2023-01-30 06:10:39 -05:00
static cocos2d::extension::CCScrollView* generateInstance(cocos2d::extension::CCScrollView*) {
return cocos2d::extension::CCScrollView::create({0.0f, 0.0f}, cocos2d::CCLayer::create());
}
2023-02-25 13:25:12 -05:00
template <class = void>
static cocos2d::CCFileUtils* generateInstance(cocos2d::CCFileUtils*) {
return cocos2d::CCFileUtils::sharedFileUtils();
}
2023-01-30 02:06:58 -05:00
template <class Class>
2023-01-30 06:10:39 -05:00
static Class* generateInstance(Class*) {
2023-02-25 13:25:12 -05:00
if constexpr (std::is_abstract_v<Class>) {
// Cant construct abstract classes, so fail early
return nullptr;
} else {
// Create a random memory block with the size of Class
// Assign a pointer to that block and cast it to type Class*
uint8_t dum[sizeof(Class)]{};
auto ptr = reinterpret_cast<Class*>(dum);
// Now you have a object of Class that actually isn't an object of Class and is just a
// random memory But C++ doesn't know that of course So now you can copy an object
// that wasn't there in the first place
// ((oh also get the offsets of the virtual tables))
auto ins = new Class(*ptr);
// this is how the first human was made
return ins;
}
2023-01-30 02:06:58 -05:00
}
template <class Class>
2023-01-30 03:36:31 -05:00
static Class* cachedInstance() {
2023-02-25 13:25:12 -05:00
static auto ret = generateInstance(static_cast<Class*>(nullptr));
2023-01-30 03:36:31 -05:00
return ret;
2023-01-30 02:06:58 -05:00
}
2022-10-30 14:59:20 -04:00
/**
* Specialized functionss
*/
template <typename R, typename T, typename... Ps>
2023-02-25 13:25:12 -05:00
static intptr_t addressOfVirtual(R (T::*func)(Ps...)) {
2022-10-30 14:59:20 -04:00
using geode::cast::reference_cast;
2023-02-25 13:25:12 -05:00
auto ins = cachedInstance<T>();
// generateInstance will return nullptr on most abstract classes,
// so dont bother getting the address
if (ins == nullptr) {
return 0;
}
2022-10-30 14:59:20 -04:00
auto index = indexOf(func);
auto thunk = thunkOf(func);
2023-02-25 13:25:12 -05:00
// [[this + thunk] + offset] is the function we want
auto address = *reinterpret_cast<intptr_t*>(*reinterpret_cast<intptr_t*>(reinterpret_cast<intptr_t>(ins) + thunk) + index);
2022-10-30 14:59:20 -04:00
address = followThunkFunction(address);
2022-10-30 14:59:20 -04:00
return address;
}
template <typename R, typename T, typename... Ps>
2023-02-25 13:25:12 -05:00
static intptr_t addressOfVirtual(R (T::*func)(Ps...) const) {
2022-10-30 14:59:20 -04:00
return addressOfVirtual(reinterpret_cast<R (T::*)(Ps...)>(func));
}
template <typename R, typename T, typename... Ps>
static intptr_t addressOfNonVirtual(R (T::*func)(Ps...) const) {
return addressOfNonVirtual(reinterpret_cast<R (T::*)(Ps...)>(func));
}
static intptr_t followThunkFunction(intptr_t address);
2022-10-30 14:59:20 -04:00
template <typename R, typename T, typename... Ps>
static intptr_t addressOfNonVirtual(R (T::*func)(Ps...)) {
return followThunkFunction(geode::cast::reference_cast<intptr_t>(func));
2022-10-30 14:59:20 -04:00
}
template <typename R, typename... Ps>
static intptr_t addressOfNonVirtual(R (*func)(Ps...)) {
return followThunkFunction(geode::cast::reference_cast<intptr_t>(func));
2022-10-30 14:59:20 -04:00
}
template <typename T>
friend intptr_t getVirtual(T func);
template <typename T>
friend intptr_t getNonVirtual(T func);
template <typename T, typename F>
friend F thunkAdjust(T func, F self);
template <typename T, typename F>
friend F rthunkAdjust(T func, F self);
};
#ifdef GEODE_ADDRESSER_TEST
template <typename T>
inline intptr_t getVirtual(T func) {
return Addresser::addressOfVirtual(func);
}
template <typename T>
inline intptr_t getNonVirtual(T func) {
return Addresser::addressOfNonVirtual(func);
}
#else
template <typename T>
inline intptr_t getVirtual(T func) {
// log::debug(
// "Get virtual function address from {}",
// utils::intToHex(geode::cast::reference_cast<intptr_t>(func))
// );
2022-10-30 14:59:20 -04:00
auto addr = Addresser::addressOfVirtual(func);
// log::debug("The address is: {}", utils::intToHex(addr));
2022-10-30 14:59:20 -04:00
return addr;
}
template <typename T>
inline intptr_t getNonVirtual(T func) {
// log::debug(
// "Get non-virtual function address from {}",
// utils::intToHex(geode::cast::reference_cast<intptr_t>(func))
// );
2022-10-30 14:59:20 -04:00
auto addr = Addresser::addressOfNonVirtual(func);
// log::debug("The address is: {}", utils::intToHex(addr));
2022-10-30 14:59:20 -04:00
return addr;
}
template <typename T, typename F>
inline F thunkAdjust(T func, F self) {
2023-01-30 01:53:20 -05:00
// do NOT delete the line below.
2023-01-23 12:31:38 -05:00
// doing so breaks thunk adjusting on windows.
// why? bruh idk
auto _ = *geode::cast::template union_cast<ptrdiff_t*>(&func);
2022-10-30 14:59:20 -04:00
return (F)((intptr_t)self + Addresser::thunkOf(func));
}
template <typename T, typename F>
inline F rthunkAdjust(T func, F self) {
2023-01-30 01:53:20 -05:00
// do NOT delete the line below.
2023-01-23 12:31:38 -05:00
// doing so breaks thunk adjusting on windows.
// why? bruh idk
auto _ = *geode::cast::template union_cast<ptrdiff_t*>(&func);
2022-10-30 14:59:20 -04:00
return (F)((intptr_t)self - Addresser::thunkOf(func));
}
#endif
2022-07-30 12:24:03 -04:00
}