#pragma once /** * Adapted from https://gist.github.com/altalk23/29b97969e9f0624f783b673f6c1cd279 */ #include "../utils/casts.hpp" #include "casts.hpp" #include #include #include #include #include #include namespace geode::addresser { template inline intptr_t getVirtual(T func); template inline intptr_t getNonVirtual(T func); template inline F thunkAdjust(T func, F self); template inline F rthunkAdjust(T func, F self); template Class* friendCreate(typename std::void_t(&Class::create))>*) { auto ret = Class::create(); ret->retain(); return ret; } template concept HasCreate = requires() { { friendCreate(nullptr) } -> std::same_as; }; class GEODE_DLL Addresser final { template struct SingleInheritance { virtual ~SingleInheritance() {} }; struct MultipleInheritance : SingleInheritance<'L'>, SingleInheritance<'F'> { virtual ~MultipleInheritance() {} }; static MultipleInheritance* instance(); template static ptrdiff_t indexOf(R (T::*func)(Ps...)) { using method_t = ptrdiff_t (T::*)(); return (reinterpret_cast(instance())->*reinterpret_cast(func))(); } template static ptrdiff_t indexOf(R (T::*func)(Ps...) const) { return indexOf(reinterpret_cast(func)); } template static ptrdiff_t thunkOf(T ptr) { // msvc if (sizeof(T) == sizeof(ptrdiff_t)) return 0; // all auto thunk = *(reinterpret_cast(&ptr) + 1); // arm if (thunk & 1) thunk >>= 1; return thunk; } // I gave up // template // static Class* generateInstance(Class*) { // return friendCreate(nullptr); // } // I extra gave up template static cocos2d::extension::CCScrollView* generateInstance(cocos2d::extension::CCScrollView*) { return cocos2d::extension::CCScrollView::create({0.0f, 0.0f}, cocos2d::CCLayer::create()); } template static cocos2d::CCFileUtils* generateInstance(cocos2d::CCFileUtils*) { return cocos2d::CCFileUtils::sharedFileUtils(); } template static Class* generateInstance(Class*) { if constexpr (std::is_abstract_v) { // 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(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; } } template static Class* cachedInstance() { static auto ret = generateInstance(static_cast(nullptr)); return ret; } /** * Specialized functionss */ template static intptr_t addressOfVirtual(R (T::*func)(Ps...)) { using geode::cast::reference_cast; auto ins = cachedInstance(); // generateInstance will return nullptr on most abstract classes, // so dont bother getting the address if (ins == nullptr) { return 0; } auto index = indexOf(func); auto thunk = thunkOf(func); // [[this + thunk] + offset] is the function we want auto address = *reinterpret_cast(*reinterpret_cast(reinterpret_cast(ins) + thunk) + index); address = followThunkFunction(address); return address; } template static intptr_t addressOfVirtual(R (T::*func)(Ps...) const) { return addressOfVirtual(reinterpret_cast(func)); } template static intptr_t addressOfNonVirtual(R (T::*func)(Ps...) const) { return addressOfNonVirtual(reinterpret_cast(func)); } static intptr_t followThunkFunction(intptr_t address); template static intptr_t addressOfNonVirtual(R (T::*func)(Ps...)) { return followThunkFunction(geode::cast::reference_cast(func)); } template static intptr_t addressOfNonVirtual(R (*func)(Ps...)) { return followThunkFunction(geode::cast::reference_cast(func)); } template friend intptr_t getVirtual(T func); template friend intptr_t getNonVirtual(T func); template friend F thunkAdjust(T func, F self); template friend F rthunkAdjust(T func, F self); }; #ifdef GEODE_ADDRESSER_TEST template inline intptr_t getVirtual(T func) { return Addresser::addressOfVirtual(func); } template inline intptr_t getNonVirtual(T func) { return Addresser::addressOfNonVirtual(func); } #else template inline intptr_t getVirtual(T func) { // log::debug( // "Get virtual function address from {}", // utils::intToHex(geode::cast::reference_cast(func)) // ); auto addr = Addresser::addressOfVirtual(func); // log::debug("The address is: {}", utils::intToHex(addr)); return addr; } template inline intptr_t getNonVirtual(T func) { // log::debug( // "Get non-virtual function address from {}", // utils::intToHex(geode::cast::reference_cast(func)) // ); auto addr = Addresser::addressOfNonVirtual(func); // log::debug("The address is: {}", utils::intToHex(addr)); return addr; } template inline F thunkAdjust(T func, F self) { // do NOT delete the line below. // doing so breaks thunk adjusting on windows. // why? bruh idk auto _ = *geode::cast::template union_cast(&func); return (F)((intptr_t)self + Addresser::thunkOf(func)); } template inline F rthunkAdjust(T func, F self) { // do NOT delete the line below. // doing so breaks thunk adjusting on windows. // why? bruh idk auto _ = *geode::cast::template union_cast(&func); return (F)((intptr_t)self - Addresser::thunkOf(func)); } #endif }