geode/loader/include/Geode/modify/Modify.hpp

211 lines
9.7 KiB
C++
Raw Normal View History

2022-07-30 12:24:03 -04:00
#pragma once
#include "AsStaticFunction.hpp"
2022-11-09 12:11:50 -05:00
#include "Field.hpp"
2022-12-12 10:42:56 -05:00
#include "IDManager.hpp"
2022-10-30 14:59:20 -04:00
2022-10-08 09:55:40 -04:00
#include <Geode/loader/Loader.hpp>
#include <Geode/loader/Mod.hpp>
2022-07-30 12:24:03 -04:00
#include <iostream>
2022-12-12 10:42:56 -05:00
#include <tulip/TulipHook.hpp>
2022-07-30 12:24:03 -04:00
#define GEODE_APPLY_MODIFY_FOR_FUNCTION(AddressIndex_, Convention_, ClassName_, FunctionName_, ...) \
do { \
if constexpr (Unique::different< \
Resolve<__VA_ARGS__>::func(&Base::FunctionName_), \
Resolve<__VA_ARGS__>::func(&Derived::FunctionName_)>()) { \
if (address<AddressIndex_>() == 0) { \
log::error( \
"Address of {} returned nullptr, can't hook", #ClassName_ "::" #FunctionName_ \
); \
break; \
} \
auto hook = Hook::create( \
Mod::get(), \
reinterpret_cast<void*>(address<AddressIndex_>()), \
AsStaticFunction_##FunctionName_< \
Derived, \
decltype(Resolve<__VA_ARGS__>::func(&Derived::FunctionName_))>::value, \
#ClassName_ "::" #FunctionName_, \
tulip::hook::TulipConvention::Convention_ \
); \
this->m_hooks[#ClassName_ "::" #FunctionName_] = hook; \
} \
} while (0);
#define GEODE_APPLY_MODIFY_FOR_CONSTRUCTOR(AddressIndex_, Convention_, ClassName_, ...) \
do { \
if constexpr (HasConstructor<Derived>) { \
auto hook = Hook::create( \
Mod::get(), \
reinterpret_cast<void*>(address<AddressIndex_>()), \
AsStaticFunction_##constructor< \
Derived, \
decltype(Resolve<__VA_ARGS__>::func(&Derived::constructor))>::value, \
#ClassName_ "::" #ClassName_, \
tulip::hook::TulipConvention::Convention_ \
); \
this->m_hooks[#ClassName_ "::" #ClassName_] = hook; \
} \
} while (0);
#define GEODE_APPLY_MODIFY_FOR_DESTRUCTOR(AddressIndex_, Convention_, ClassName_) \
do { \
if constexpr (HasDestructor<Derived>) { \
auto hook = Hook::create( \
Mod::get(), \
reinterpret_cast<void*>(address<AddressIndex_>()), \
AsStaticFunction_##destructor<Derived, decltype(Resolve<>::func(&Derived::destructor))>::value, \
#ClassName_ "::" #ClassName_, \
tulip::hook::TulipConvention::Convention_ \
); \
this->m_hooks[#ClassName_ "::" #ClassName_] = hook; \
} \
2022-12-13 08:32:49 -05:00
} while (0);
2022-07-30 12:24:03 -04:00
namespace geode::modifier {
2022-10-30 14:59:20 -04:00
template <class Derived, class Base>
2022-11-09 12:11:50 -05:00
class ModifyDerive;
2022-07-30 12:24:03 -04:00
2022-12-12 10:42:56 -05:00
template <class ModifyDerived>
2022-10-30 14:59:20 -04:00
class ModifyBase {
public:
2022-12-13 08:32:49 -05:00
std::map<std::string, Hook*> m_hooks;
2022-12-12 10:42:56 -05:00
2022-12-13 08:32:49 -05:00
Result<Hook*> getHook(std::string const& name) {
if (m_hooks.find(name) == m_hooks.end()) {
2022-12-12 10:42:56 -05:00
return Err("Hook not in this modify");
}
2022-12-13 08:32:49 -05:00
return Ok(m_hooks[name]);
2022-12-12 10:42:56 -05:00
}
2023-01-24 11:52:29 -05:00
Result<> setHookPriority(std::string const& name, int32_t priority) {
auto res = this->getHook(name);
if (!res) {
return Err(res.unwrapErr());
}
res.unwrap()->setPriority(priority);
return Ok();
}
2022-10-30 14:59:20 -04:00
// unordered_map<handles> idea
ModifyBase() {
2022-12-13 08:32:49 -05:00
// i really dont want to recompile codegen
auto test = static_cast<ModifyDerived*>(this);
test->ModifyDerived::apply();
2022-12-13 05:30:34 -05:00
ModifyDerived::Derived::onModify(*this);
for (auto& [uuid, hook] : m_hooks) {
auto res = Mod::get()->addHook(hook);
if (!res) {
2022-12-13 08:32:49 -05:00
log::error("Failed to add hook {}: {}", hook->getDisplayName(), res.error());
2022-12-12 10:42:56 -05:00
}
2022-12-13 05:30:34 -05:00
}
2022-10-30 14:59:20 -04:00
}
2022-12-12 10:42:56 -05:00
virtual void apply() {}
2022-10-30 14:59:20 -04:00
template <class, class>
2022-11-09 12:11:50 -05:00
friend class ModifyDerive;
2022-10-30 14:59:20 -04:00
// explicit Modify(Property property) idea
};
2022-07-30 12:24:03 -04:00
2022-10-30 14:59:20 -04:00
template <class Derived, class Base>
2022-11-09 12:11:50 -05:00
class ModifyDerive {
2022-10-30 14:59:20 -04:00
public:
2022-11-09 12:11:50 -05:00
ModifyDerive() {
static_assert(
alwaysFalse<Derived>,
"Modified class not recognized, please include <Geode/modify/ClassName.hpp> to be "
"able to use it."
);
2022-10-30 14:59:20 -04:00
}
};
2022-07-30 12:24:03 -04:00
}
2022-11-09 12:11:50 -05:00
namespace geode {
template <class Derived, class Base>
class Modify : public Base {
private:
static inline modifier::ModifyDerive<Derived, Base> s_apply;
// because for some reason we need it
static inline auto s_applyRef = &Modify::s_apply;
public:
// abusing the internal stuff
// basically we dont want modify to invoke base ctors and dtors
// we already have utilities for these, which are ccdestructor
2023-03-06 13:24:38 -05:00
// and the cutoff constructor
Modify() : Base(CutoffConstructor, sizeof(Base)) {}
~Modify() {
cocos2d::CCDestructor::lock(this) = true;
}
Modify(Modify const&) = delete;
Modify(Modify&&) = delete;
Modify& operator=(Modify const&) = delete;
Modify& operator=(Modify&&) = delete;
2022-11-09 12:11:50 -05:00
modifier::FieldIntermediate<Derived, Base> m_fields;
2022-12-12 10:42:56 -05:00
static void onModify(auto& self) {}
2022-11-09 12:11:50 -05:00
};
}
2023-02-08 08:42:34 -05:00
/**
* Main class implementation, it has the structure
*
* class hook0Dummy;
* template<typename>
* struct hook0 {};
* namespace {
* struct hook0Parent {};
* }
* template<>
* struct GEODE_HIDDEN hook0<hook0Parent> : Modify<hook0<hook0Parent>, MenuLayer> {
* // code stuff idk
* };
*
* I tried to make the macro as verbose as it can be but
* I am bad at this stuff
*/
#define GEODE_MODIFY_DECLARE_ANONYMOUS(base, derived) \
derived##Dummy; \
template <class> \
struct derived {}; \
namespace { \
struct derived##Parent {}; \
} \
template <> \
struct GEODE_HIDDEN derived<derived##Parent> : geode::Modify<derived<derived##Parent>, base>
#define GEODE_MODIFY_DECLARE(base, derived) \
derived##Dummy; \
struct GEODE_HIDDEN derived : geode::Modify<derived, base>
#define GEODE_MODIFY_REDIRECT4(base, derived) GEODE_MODIFY_DECLARE(base, derived)
#define GEODE_MODIFY_REDIRECT3(base, derived) GEODE_MODIFY_DECLARE_ANONYMOUS(base, derived)
#define GEODE_MODIFY_REDIRECT2(base) GEODE_MODIFY_REDIRECT3(base, GEODE_CONCAT(hook, __LINE__))
#define GEODE_MODIFY_REDIRECT1(base) GEODE_MODIFY_REDIRECT2(base)
/**
* Interfaces for the class implementation
*
* class $modify(MenuLayer) {};
* class $modify(MyMenuLayerInterface, MenuLayer) {};
*/
#define GEODE_CRTP1(base) GEODE_MODIFY_REDIRECT1(base)
#define GEODE_CRTP2(derived, base) GEODE_MODIFY_REDIRECT4(base, derived)
#define $modify(...) \
GEODE_INVOKE(GEODE_CONCAT(GEODE_CRTP, GEODE_NUMBER_OF_ARGS(__VA_ARGS__)), __VA_ARGS__)
#define $(...) $modify(__VA_ARGS__)
/**
* Get current hook class without needing to name it.
* Useful for callbacks
* this is a camila moment if you ask me
*/
#define $cls std::remove_pointer<decltype(this)>::type