diff --git a/qpm.shared.json b/qpm.shared.json index 8a5d6a26..2e9ac1a7 100644 --- a/qpm.shared.json +++ b/qpm.shared.json @@ -144,29 +144,25 @@ { "dependency": { "id": "flamingo", - "versionRange": "=1.1.2", + "versionRange": "=1.2.0", "additionalData": { - "soLink": "https://github.com/sc2ad/Flamingo/releases/download/v1.1.2/libflamingo.so", - "debugSoLink": "https://github.com/sc2ad/Flamingo/releases/download/v1.1.2/debug_libflamingo.so", - "overrideSoName": "libflamingo.so", - "modLink": "https://github.com/sc2ad/Flamingo/releases/download/v1.1.2/flamingo.qmod", - "branchName": "version/v1_1_2" + "overrideSoName": "libflamingo.so" } }, - "version": "1.1.2" + "version": "1.2.0" }, { "dependency": { "id": "scotland2", - "versionRange": "=0.1.6", + "versionRange": "=0.1.7", "additionalData": { - "soLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.6/libsl2.so", - "debugSoLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.6/debug_libsl2.so", + "soLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.7/libsl2.so", + "debugSoLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.7/debug_libsl2.so", "overrideSoName": "libsl2.so", - "branchName": "version/v0_1_6" + "branchName": "version/v0_1_7" } }, - "version": "0.1.6" + "version": "0.1.7" } ] } \ No newline at end of file diff --git a/shared/utils/flamingo-utils.hpp b/shared/utils/flamingo-utils.hpp new file mode 100644 index 00000000..6c66b957 --- /dev/null +++ b/shared/utils/flamingo-utils.hpp @@ -0,0 +1,130 @@ +#pragma once + +#include +#include +#include +#include +#include "flamingo/shared/hook-data.hpp" +#include "flamingo/shared/hook-installation-result.hpp" +#include "flamingo/shared/installer.hpp" +#include "flamingo/shared/target-data.hpp" + +#include "./result.hpp" + +namespace bs_hook { +struct FlamingoHandle; + +struct FlamingoHandleBuilder { + Paper::LoggerContext logger; + flamingo::HookInfo hookInfo; + + FlamingoHandleBuilder(Paper::LoggerContext const& log, flamingo::HookInfo info) : logger(log), hookInfo(std::move(info)) { + hookInfo.metadata.priority.is_final = false; + } + FlamingoHandleBuilder(FlamingoHandleBuilder const&) = default; + FlamingoHandleBuilder(FlamingoHandleBuilder&&) = default; + ~FlamingoHandleBuilder() = default; + + FlamingoHandleBuilder& operator=(FlamingoHandleBuilder const&) = default; + FlamingoHandleBuilder& operator=(FlamingoHandleBuilder&&) = default; + + FlamingoHandleBuilder& final(bool isFinal = true) { + hookInfo.metadata.priority.is_final = isFinal; + return *this; + } + + FlamingoHandleBuilder& after(modloader::ModInfo const& info, std::string_view name = {}) { + after(info.id, name); + return *this; + } + + FlamingoHandleBuilder& after(std::string_view modID, std::string_view name = {}) { + hookInfo.metadata.priority.afters.emplace_back(flamingo::HookNameMetadata{ + .name = std::string(name), + .namespaze = std::string(modID), + }); + + return *this; + } + + FlamingoHandleBuilder& before(modloader::ModInfo const& info, std::string_view name = {}) { + before(info.id, name); + return *this; + } + + FlamingoHandleBuilder& before(std::string_view modID, std::string_view name = {}) { + hookInfo.metadata.priority.befores.emplace_back(flamingo::HookNameMetadata{ .name = std::string(name), .namespaze = std::string(modID) }); + return *this; + } + + [[nodiscard]] + bs_hook::Result installOrError() noexcept; + + [[nodiscard]] + FlamingoHandle install(); +}; + +struct FlamingoHandle { + Paper::LoggerContext logger; + flamingo::HookInfo info; + flamingo::HookHandle handle; + + FlamingoHandle(Paper::LoggerContext logger, flamingo::HookHandle h, flamingo::HookInfo i) : logger(logger), info(i), handle(h) {} + + FlamingoHandle(FlamingoHandle const&) = delete; + FlamingoHandle(FlamingoHandle&&) = default; + + FlamingoHandle& operator=(FlamingoHandle const&) = delete; + FlamingoHandle& operator=(FlamingoHandle&&) = default; + + ~FlamingoHandle() = default; + + operator flamingo::HookHandle() const { + return handle; + } + + [[nodiscard]] + flamingo::HookHandle const& get_handle() const { + return handle; + } + + /// @brief Uninstalls the hook associated with this handle. + [[nodiscard]] + bs_hook::Result uninstall() { + using Result = bs_hook::Result; + auto result = flamingo::Uninstall(handle); + if (result.has_value()) { + return Result::Ok(FlamingoHandleBuilder(logger, info)); + } + return Result::Err(std::monostate{}); + } + + [[nodiscard]] + auto reinstall() { + auto result = flamingo::Reinstall({ handle.hook_location->target }); + return result; + } +}; + +inline bs_hook::Result FlamingoHandleBuilder::installOrError() noexcept { + using Result = bs_hook::Result; + + logger.info("Installing hook: {} to offset: {}", hookInfo.metadata.name_info, fmt::ptr(hookInfo.target)); + auto install_result = flamingo::Install(std::move(hookInfo)); + if (install_result.has_value()) { + return Result::Ok(FlamingoHandle(logger, install_result.value().returned_handle, std::move(hookInfo))); + } else { + return Result::Err(install_result.error()); + } +} + +inline FlamingoHandle FlamingoHandleBuilder::install() { + auto result = installOrError(); + if (!result.has_result()) { + logger.critical("Failed to install hook: {} with flamingo: {}", hookInfo.metadata.name_info, result.get_exception()); + SAFE_ABORT(); + } + + return result.move_result(); +} +} // namespace bs_hook \ No newline at end of file diff --git a/shared/utils/hooking.hpp b/shared/utils/hooking.hpp index 6811220f..16355bfa 100644 --- a/shared/utils/hooking.hpp +++ b/shared/utils/hooking.hpp @@ -1,40 +1,42 @@ #pragma once -#include "flamingo/shared/installer.hpp" -#include "flamingo/shared/hook-data.hpp" -#include "flamingo/shared/hook-metadata.hpp" #include #include -#include "utils.h" -#include "typedefs.h" -#include "logging.hpp" +#include "flamingo/shared/hook-data.hpp" +#include "flamingo/shared/hook-metadata.hpp" +#include "flamingo/shared/installer.hpp" #include "il2cpp-utils.hpp" +#include "logging.hpp" +#include "typedefs.h" +#include "utils.h" + +#include "./flamingo-utils.hpp" + namespace Hooking { // For use in MAKE_HOOK_AUTO bodies. // Currently unused. -template -requires (sizeof...(TArgs) == 0 && N >= 0) +template + requires(sizeof...(TArgs) == 0 && N >= 0) decltype(auto) param_get([[maybe_unused]] TArgs&&... as) noexcept { static_assert(N < 0, "Parameter index out of bounds!"); } -template...> +template ...> decltype(auto) param_get(Tfirst&& first, [[maybe_unused]] Args&&... as) noexcept { return std::forward(first); } -template...> +template ...> decltype(auto) param_get([[maybe_unused]] Tfirst&& first, Args&&... as) noexcept { return param_get(std::forward(as)...); } // Gets the parameter at the given index (starting at 0, not including self) to return in a MAKE_HOOK_AUTO body. -#define PARAM(i) \ -::Hooking::param_get(__args...) +#define PARAM(i) ::Hooking::param_get(__args...) -template -concept is_logger = requires (L& l) { +template +concept is_logger = requires(L& l) { l.info(""); l.debug(""); l.error(""); @@ -42,65 +44,63 @@ concept is_logger = requires (L& l) { l.critical(""); }; -template +template concept is_hook = requires { // Must have a name - {T::name()} -> std::same_as; + { T::name() } -> std::same_as; // Must have a trampoline that returns the funcType - {T::trampoline()} -> std::same_as; + { T::trampoline() } -> std::same_as; // Must have a hook that returns the funcType - {T::hook()} -> std::same_as; + { T::hook() } -> std::same_as; }; -template +template concept has_addr = requires { - {T::addr()} -> std::same_as; + { T::addr() } -> std::same_as; }; -template +template concept is_addr_hook = is_hook && has_addr; -template +template concept is_findCall_hook = is_hook && requires { // Must have a getInfo() method - {T::getInfo()} -> std::same_as; + { T::getInfo() } -> std::same_as; }; /// @brief Represents a type that provides the funcType from a provided method pointer type. /// This type is used in determining hook checks. -template +template struct InternalMethodCheck; -template +template struct InternalMethodCheck { using funcType = R (*)(TArgs...); }; -template +template struct InternalMethodCheck { using funcType = R (*)(T*, TArgs...); }; /// @brief Exposes the instance type of the provided instance method. -template +template struct InternalClassGetter { using instanceType = void; }; -template +template struct InternalClassGetter { using instanceType = T; }; -template +template /// @brief Exposes a static wrapper method that forwards to the provided function pointer, wrapping it in IL2CPP_CATCH_HANDLER. struct HookCatchWrapper; -template +template struct HookCatchWrapper { static R wrapper(TArgs... args) { - IL2CPP_CATCH_HANDLER( - return Func(args...); - ) + IL2CPP_CATCH_HANDLER(return Func(args...);) } }; @@ -108,344 +108,536 @@ struct HookCatchWrapper { // Then, walk this at load time and install (could do so after il2cpp_functions::Init) // Make an address-specified hook, that has a catch handler. -#define MAKE_HOOK(name_, addr_, retval, ...) \ -struct Hook_##name_ { \ - constexpr static const char* name() { return #name_; } \ - constexpr static void* addr() { return (void*) addr_; } \ - using funcType = retval (*)(__VA_ARGS__); \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK(name_, addr_, retval, ...) \ + struct Hook_##name_ { \ + constexpr static const char* name() { \ + return #name_; \ + } \ + constexpr static void* addr() { \ + return (void*)addr_; \ + } \ + using funcType = retval (*)(__VA_ARGS__); \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make an address-specified hook. -#define MAKE_HOOK_NO_CATCH(name_, addr_, retval, ...) \ -struct Hook_##name_ { \ - constexpr static const char* name() { return #name_; } \ - constexpr static void* addr() { return (void*) addr_; } \ - using funcType = retval (*)(__VA_ARGS__); \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return hook_##name_; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_NO_CATCH(name_, addr_, retval, ...) \ + struct Hook_##name_ { \ + constexpr static const char* name() { \ + return #name_; \ + } \ + constexpr static void* addr() { \ + return (void*)addr_; \ + } \ + using funcType = retval (*)(__VA_ARGS__); \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return hook_##name_; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that resolves the 'infoGet' expression an installs the hook to that MethodInfo*, that has a catch handler. -#define MAKE_HOOK_FIND_VERBOSE(name_, infoGet, retval, ...) \ -struct Hook_##name_ { \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return infoGet; } \ - using funcType = retval (*)(__VA_ARGS__); \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_FIND_VERBOSE(name_, infoGet, retval, ...) \ + struct Hook_##name_ { \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return infoGet; \ + } \ + using funcType = retval (*)(__VA_ARGS__); \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that resolves the 'infoGet' expression an installs the hook to that MethodInfo*. #define MAKE_HOOK_FIND_VERBOSE_NO_CATCH(name_, infoGet, retval, ...) \ -struct Hook_##name_ { \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return infoGet; } \ - using funcType = retval (*)(__VA_ARGS__); \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return hook_##name_; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) + struct Hook_##name_ { \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return infoGet; \ + } \ + using funcType = retval (*)(__VA_ARGS__); \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return hook_##name_; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that finds a method that matches the signature provided and exists on the provided Il2CppClass* with the provided method name, that has a catch handler -#define MAKE_HOOK_FIND(name_, klass, mName, retval, ...) \ -struct Hook_##name_ { \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find(klass, mName); } \ - using funcType = retval (*)(__VA_ARGS__); \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_FIND(name_, klass, mName, retval, ...) \ + struct Hook_##name_ { \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find(klass, mName); \ + } \ + using funcType = retval (*)(__VA_ARGS__); \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that finds a method that matches the signature provided and exists on the provided Il2CppClass* with the provided method name. -#define MAKE_HOOK_FIND_NO_CATCH(name_, klass, mName, retval, ...) \ -struct Hook_##name_ { \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find(klass, mName); } \ - using funcType = retval (*)(__VA_ARGS__); \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return hook_##name_; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_FIND_NO_CATCH(name_, klass, mName, retval, ...) \ + struct Hook_##name_ { \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find(klass, mName); \ + } \ + using funcType = retval (*)(__VA_ARGS__); \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return hook_##name_; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that finds a method that matches the signature provided and exists on the provided namespace and type name, with the provided method name, that has a catch handler. -#define MAKE_HOOK_FIND_CLASS(name_, namespaze, klassName, mName, retval, ...) \ -struct Hook_##name_ { \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find(namespaze, klassName, mName); } \ - using funcType = retval (*)(__VA_ARGS__); \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_FIND_CLASS(name_, namespaze, klassName, mName, retval, ...) \ + struct Hook_##name_ { \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find(namespaze, klassName, mName); \ + } \ + using funcType = retval (*)(__VA_ARGS__); \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that finds a method that matches the signature provided and exists on the provided namespace and type name, with the provided method name. -#define MAKE_HOOK_FIND_CLASS_NO_CATCH(name_, namespaze, klassName, mName, retval, ...) \ -struct Hook_##name_ { \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find(namespaze, klassName, mName); } \ - using funcType = retval (*)(__VA_ARGS__); \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return hook_##name_; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_FIND_CLASS_NO_CATCH(name_, namespaze, klassName, mName, retval, ...) \ + struct Hook_##name_ { \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find(namespaze, klassName, mName); \ + } \ + using funcType = retval (*)(__VA_ARGS__); \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return hook_##name_; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that finds a method that matches the signature provided and exists on the provided Il2CppClass* with the provided method name. // Ignores matching the first parameter, assuming it is an instance method. // Also includes a catch handler. -#define MAKE_HOOK_FIND_INSTANCE(name_, klass, mName, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::fType>::find(klass, mName); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_FIND_INSTANCE(name_, klass, mName, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::fType>::find(klass, mName); \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that finds a method that matches the signature provided and exists on the provided Il2CppClass* with the provided method name. // Ignores matching the first parameter, assuming it is an instance method. -#define MAKE_HOOK_FIND_INSTANCE_NO_CATCH(name_, klass, mName, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::fType>::find(klass, mName); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return hook_##name_; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_FIND_INSTANCE_NO_CATCH(name_, klass, mName, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::fType>::find(klass, mName); \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return hook_##name_; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that finds a method that matches the signature provided and exists on the provided namespace and type name, with the provided method name. // Ignores matching the first parameter, assuming it is an instance method. // Also includes a catch handler. -#define MAKE_HOOK_FIND_CLASS_INSTANCE(name_, namespaze, klassName, mName, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::fType>::find(namespaze, klassName, mName); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_FIND_CLASS_INSTANCE(name_, namespaze, klassName, mName, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::fType>::find(namespaze, klassName, mName); \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that finds a method that matches the signature provided and exists on the provided namespace and type name, with the provided method name. // Ignores matching the first parameter, assuming it is an instance method. -#define MAKE_HOOK_FIND_CLASS_INSTANCE_NO_CATCH(name_, namespaze, klassName, mName, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::fType>::find(namespaze, klassName, mName); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return hook_##name_; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_FIND_CLASS_INSTANCE_NO_CATCH(name_, namespaze, klassName, mName, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::fType>::find(namespaze, klassName, mName); \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return hook_##name_; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that finds a method that matches the signature provided and exists on the provided namespace and type name, with the provided method name. // THIS FUNCTION IS THE UNSAFE VARIANT, SUBTRACTS ONE FOR INSTANCE METHODS! // Also includes a catch handler. -#define MAKE_HOOK_FIND_CLASS_UNSAFE_INSTANCE(name_, namespaze, klassName, mName, retval, ...) \ -struct Hook_##name_ { \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find_unsafe(namespaze, klassName, mName, true); } \ - using funcType = retval (*)(__VA_ARGS__); \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_FIND_CLASS_UNSAFE_INSTANCE(name_, namespaze, klassName, mName, retval, ...) \ + struct Hook_##name_ { \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find_unsafe(namespaze, klassName, mName, true); \ + } \ + using funcType = retval (*)(__VA_ARGS__); \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that finds a method that matches the signature provided and exists on the provided namespace and type name, with the provided method name. // THIS FUNCTION IS THE UNSAFE VARIANT, SUBTRACTS ONE FOR INSTANCE METHODS! -#define MAKE_HOOK_FIND_CLASS_UNSAFE_INSTANCE_NO_CATCH(name_, namespaze, klassName, mName, retval, ...) \ -struct Hook_##name_ { \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find_unsafe(namespaze, klassName, mName, true); } \ - using funcType = retval (*)(__VA_ARGS__); \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return hook_##name_; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_FIND_CLASS_UNSAFE_INSTANCE_NO_CATCH(name_, namespaze, klassName, mName, retval, ...) \ + struct Hook_##name_ { \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find_unsafe(namespaze, klassName, mName, true); \ + } \ + using funcType = retval (*)(__VA_ARGS__); \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return hook_##name_; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that finds a method that matches the signature provided and exists on the provided namespace and type name, with the provided method name. // THIS FUNCTION IS THE UNSAFE VARIANT! // Also includes a catch handler. -#define MAKE_HOOK_FIND_CLASS_UNSAFE_STATIC(name_, namespaze, klassName, mName, retval, ...) \ -struct Hook_##name_ { \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find_unsafe(namespaze, klassName, mName); } \ - using funcType = retval (*)(__VA_ARGS__); \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_FIND_CLASS_UNSAFE_STATIC(name_, namespaze, klassName, mName, retval, ...) \ + struct Hook_##name_ { \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find_unsafe(namespaze, klassName, mName); \ + } \ + using funcType = retval (*)(__VA_ARGS__); \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that finds a method that matches the signature provided and exists on the provided namespace and type name, with the provided method name. // THIS FUNCTION IS THE UNSAFE VARIANT! -#define MAKE_HOOK_FIND_CLASS_UNSAFE_STATIC_NO_CATCH(name_, namespaze, klassName, mName, retval, ...) \ -struct Hook_##name_ { \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find_unsafe(namespaze, klassName, mName); } \ - using funcType = retval (*)(__VA_ARGS__); \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return hook_##name_; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_FIND_CLASS_UNSAFE_STATIC_NO_CATCH(name_, namespaze, klassName, mName, retval, ...) \ + struct Hook_##name_ { \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find_unsafe(namespaze, klassName, mName); \ + } \ + using funcType = retval (*)(__VA_ARGS__); \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return hook_##name_; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that would be installed in a particular address, but ensures the signature matches the provided method pointer. // Also includes a catch handler. -#define MAKE_HOOK_CHECKED_ADDR(name_, mPtr, addr_, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ - constexpr static const char* name() { return #name_; } \ - constexpr static void* addr() { return addr_; } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_CHECKED_ADDR(name_, mPtr, addr_, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + constexpr static void* addr() { \ + return addr_; \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that would be installed in a particular address, but ensures the signature matches the provided method pointer. -#define MAKE_HOOK_CHECKED_ADDR_NO_CATCH(name_, mPtr, addr_, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ - constexpr static const char* name() { return #name_; } \ - constexpr static void* addr() { return addr_; } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return hook_##name_; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_CHECKED_ADDR_NO_CATCH(name_, mPtr, addr_, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + constexpr static void* addr() { \ + return addr_; \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return hook_##name_; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that ensures the signature matches the provided method pointer and finds a matching method from a class and method name. // Also includes a catch handler. -#define MAKE_HOOK_CHECKED_FIND(name_, mPtr, klass, mName, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find(klass, mName); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_CHECKED_FIND(name_, mPtr, klass, mName, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find(klass, mName); \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that ensures the signature matches the provided method pointer and finds a matching method from a class and method name. -#define MAKE_HOOK_CHECKED_FIND_NO_CATCH(name_, mPtr, klass, mName, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find(klass, mName); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return hook_##name_; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_CHECKED_FIND_NO_CATCH(name_, mPtr, klass, mName, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find(klass, mName); \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return hook_##name_; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that ensures the signature matches the provided method pointer and finds a matching method from namespace, name, and method name. // Also includes a catch handler. -#define MAKE_HOOK_CHECKED_FIND_CLASS(name_, mPtr, namespaze, klassName, mName, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find(namespaze, klassName, mName); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_CHECKED_FIND_CLASS(name_, mPtr, namespaze, klassName, mName, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find(namespaze, klassName, mName); \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that ensures the signature matches the provided method pointer and finds a matching method from namespace, name, and method name. -#define MAKE_HOOK_CHECKED_FIND_CLASS_NO_CATCH(name_, mPtr, namespaze, klassName, mName, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find(namespaze, klassName, mName); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return hook_##name_; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_CHECKED_FIND_CLASS_NO_CATCH(name_, mPtr, namespaze, klassName, mName, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find(namespaze, klassName, mName); \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return hook_##name_; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that ensures the signature matches the provided method pointer and finds a matching method from a class and method name. // Also includes a catch handler. -#define MAKE_HOOK_CHECKED_INSTANCE_FIND(name_, mPtr, mName, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - using classType = ::Hooking::InternalClassGetter::instanceType; \ - static_assert(!std::is_same_v, "MAKE_HOOK_INSTANCE_FIND was not provided an instance method pointer!"); \ - static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find(classof(classType), mName); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_CHECKED_INSTANCE_FIND(name_, mPtr, mName, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + using classType = ::Hooking::InternalClassGetter::instanceType; \ + static_assert(!std::is_same_v, "MAKE_HOOK_INSTANCE_FIND was not provided an instance method pointer!"); \ + static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find(classof(classType), mName); \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that ensures the signature matches the provided method pointer and finds a matching method from a class and method name. -#define MAKE_HOOK_CHECKED_INSTANCE_FIND_NO_CATCH(name_, mPtr, mName, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - using classType = ::Hooking::InternalClassGetter::instanceType; \ - static_assert(!std::is_same_v, "MAKE_HOOK_INSTANCE_FIND was not provided an instance method pointer!"); \ - static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::MethodTypeCheck::find(classof(classType), mName); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return hook_##name_; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_CHECKED_INSTANCE_FIND_NO_CATCH(name_, mPtr, mName, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + using classType = ::Hooking::InternalClassGetter::instanceType; \ + static_assert(!std::is_same_v, "MAKE_HOOK_INSTANCE_FIND was not provided an instance method pointer!"); \ + static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::MethodTypeCheck::find(classof(classType), mName); \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return hook_##name_; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) #ifndef BS_HOOK_MATCH_UNSAFE #define MATCH_HOOKABLE_ASSERT(mPtr) ::il2cpp_utils::il2cpp_type_check::MetadataGetter::size >= 0x5 * sizeof(uint32_t) && ::il2cpp_utils::il2cpp_type_check::MetadataGetter::addrs != 0x0 @@ -456,40 +648,56 @@ retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that uses the provided method pointer in a match an ensures the signature matches. // This should be your go-to hook macro when hooking anything that has a codegen equivalent. // Also includes a catch handler. -#define MAKE_HOOK_MATCH(name_, mPtr, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - static_assert(MATCH_HOOKABLE_ASSERT(mPtr)); \ - static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::il2cpp_type_check::MetadataGetter::methodInfo(); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_MATCH(name_, mPtr, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + static_assert(MATCH_HOOKABLE_ASSERT(mPtr)); \ + static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::il2cpp_type_check::MetadataGetter::methodInfo(); \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // Make a hook that uses the provided method pointer in a match an ensures the signature matches. // This should be your go-to hook macro when hooking anything that has a codegen equivalent. -#define MAKE_HOOK_MATCH_NO_CATCH(name_, mPtr, retval, ...) \ -struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - static_assert(MATCH_HOOKABLE_ASSERT(mPtr)); \ - static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::il2cpp_type_check::MetadataGetter::methodInfo(); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return hook_##name_; } \ - static retval hook_##name_(__VA_ARGS__); \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_MATCH_NO_CATCH(name_, mPtr, retval, ...) \ + struct Hook_##name_ { \ + using funcType = retval (*)(__VA_ARGS__); \ + static_assert(MATCH_HOOKABLE_ASSERT(mPtr)); \ + static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::il2cpp_type_check::MetadataGetter::methodInfo(); \ + } \ + static funcType* trampoline() { \ + return &name_; \ + } \ + static inline retval (*name_)(__VA_ARGS__) = nullptr; \ + static funcType hook() { \ + return hook_##name_; \ + } \ + static retval hook_##name_(__VA_ARGS__); \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // TODO: Remove all of these macros and replace it with just one or MAYBE two-- if people want to do it themselves // they can implement the structure themselves -template +template struct TypeConv { using type = T; static T make(T p) { @@ -500,13 +708,13 @@ struct TypeConv { } }; -template<> +template <> struct TypeConv { using type = void; }; -template -requires (il2cpp_utils::has_il2cpp_conversion) +template + requires(il2cpp_utils::has_il2cpp_conversion) struct TypeConv { using type = void*; static T make(void* p) { @@ -517,10 +725,10 @@ struct TypeConv { } }; -template +template struct HookWrapperCompose; -template +template struct HookWrapperCompose { static typename TypeConv::type wrapper(typename TypeConv::type... args) { if constexpr (std::is_same_v::type, void>) { @@ -531,10 +739,10 @@ struct HookWrapperCompose { } }; -template +template struct HookWrapperInvoke; -template +template struct HookWrapperInvoke { static R wrapper(typename TypeConv::type (*func)(typename TypeConv::type...), TArgs... args) { if constexpr (std::is_same_v) { @@ -545,26 +753,34 @@ struct HookWrapperInvoke { } }; -#define MAKE_HOOK_WRAPPER(name_, mPtr, retval, ...) \ -struct Hook_##name_ { \ - static retval hook_##name_(__VA_ARGS__); \ - using funcType = decltype(&::Hooking::HookWrapperCompose<&hook_##name_>::wrapper); \ - /* static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); */ \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::il2cpp_type_check::MetadataGetter::methodInfo(); } \ - static funcType* trampoline() { return &orig_base; } \ - static inline funcType orig_base = nullptr; \ - template \ - static inline retval name_(TArgs... args) { \ - if constexpr (std::is_same_v) { \ - ::Hooking::HookWrapperInvoke::wrapper(orig_base, args...); \ - } else { \ - return ::Hooking::HookWrapperInvoke::wrapper(orig_base, args...); \ - } \ - } \ - static funcType hook() { return &::Hooking::HookWrapperCompose<::Hooking::HookCatchWrapper<&hook_##name_, decltype(&hook_##name_)>::wrapper>::wrapper; } \ -}; \ -retval Hook_##name_::hook_##name_(__VA_ARGS__) +#define MAKE_HOOK_WRAPPER(name_, mPtr, retval, ...) \ + struct Hook_##name_ { \ + static retval hook_##name_(__VA_ARGS__); \ + using funcType = decltype(&::Hooking::HookWrapperCompose<&hook_##name_>::wrapper); \ + /* static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); */ \ + constexpr static const char* name() { \ + return #name_; \ + } \ + static const MethodInfo* getInfo() { \ + return ::il2cpp_utils::il2cpp_type_check::MetadataGetter::methodInfo(); \ + } \ + static funcType* trampoline() { \ + return &orig_base; \ + } \ + static inline funcType orig_base = nullptr; \ + template \ + static inline retval name_(TArgs... args) { \ + if constexpr (std::is_same_v) { \ + ::Hooking::HookWrapperInvoke::wrapper(orig_base, args...); \ + } else { \ + return ::Hooking::HookWrapperInvoke::wrapper(orig_base, args...); \ + } \ + } \ + static funcType hook() { \ + return &::Hooking::HookWrapperCompose<::Hooking::HookCatchWrapper<&hook_##name_, decltype(&hook_##name_)>::wrapper>::wrapper; \ + } \ + }; \ + retval Hook_##name_::hook_##name_(__VA_ARGS__) // TODO: IMPLEMENT AUTO HOOKS! #define MAKE_HOOK_AUTO(...) void @@ -606,119 +822,161 @@ retval Hook_##name_::hook_##name_(__VA_ARGS__) // static_assert(!std::is_same_v, "Attempting to MAKE_HOOK_INSTANCE_AUTO with a static method! See MAKE_HOOK_STATIC_AUTO instead!"); \ // }; -template +template inline void __InstallHook(L& logger, void* addr) { - #ifndef SUPPRESS_MACRO_LOGS +#ifndef SUPPRESS_MACRO_LOGS logger.info("Installing hook: {} to offset: {}", T::name(), fmt::ptr(addr)); - #endif - #ifdef __aarch64__ - auto install_result = flamingo::Install(flamingo::HookInfo{(void*)T::hook(), addr, (void**)T::trampoline(), flamingo::HookNameMetadata{.name = T::name()}}); +#endif +#ifdef __aarch64__ + auto install_result = flamingo::Install(flamingo::HookInfo{ (void*)T::hook(), addr, (void**)T::trampoline(), flamingo::HookNameMetadata{ .name = T::name() } }); if (install_result.has_value()) { - // TODO: Attach the returned handle to a member on T. - #ifndef SUPPRESS_MACRO_LOGS +// TODO: Attach the returned handle to a member on T. +#ifndef SUPPRESS_MACRO_LOGS logger.info("Hook: {} installed with flamingo!", (const char*)T::name()); - #endif +#endif + return; } else { - #ifndef SUPPRESS_MACRO_LOGS +#ifndef SUPPRESS_MACRO_LOGS // FIX: Error is removed as it causes formatting errors logger.critical("Failed to install hook: {} with flamingo: ", (const char*)T::name()); - #endif +#endif + return; } - #else - registerInlineHook((uint32_t) addr, (uint32_t) T::hook(), (uint32_t **) T::trampoline()); - inlineHook((uint32_t) addr); - #endif +#else + registerInlineHook((uint32_t)addr, (uint32_t)T::hook(), (uint32_t**)T::trampoline()); + inlineHook((uint32_t)addr); +#endif } -template +template void __InstallFinalHook(L& logger, void* addr) { - #ifndef SUPPRESS_MACRO_LOGS +#ifndef SUPPRESS_MACRO_LOGS logger.info("Installing final priority hook: {} to offset: {}", T::name(), fmt::ptr(addr)); - #endif - #ifdef __aarch64__ +#endif +#ifdef __aarch64__ // TODO: We force the hook priority here to final, but ideally we should allow arbitrary priorities via some call. - auto install_result = flamingo::Install( - flamingo::HookInfo{(void*) T::hook(), addr, (void**) T::trampoline(), flamingo::HookNameMetadata{.name = T::name()}, flamingo::HookPriority{.is_final = true} - }); + auto install_result = + flamingo::Install(flamingo::HookInfo{ (void*)T::hook(), addr, (void**)T::trampoline(), flamingo::HookNameMetadata{ .name = T::name() }, flamingo::HookPriority{ .is_final = true } }); if (install_result.has_value()) { - // TODO: Attach the returned handle to a member on T. - #ifndef SUPPRESS_MACRO_LOGS +// TODO: Attach the returned handle to a member on T. +#ifndef SUPPRESS_MACRO_LOGS logger.info("Final hook: {} installed with flamingo!", T::name()); - #endif +#endif + return; } else { - #ifndef SUPPRESS_MACRO_LOGS +#ifndef SUPPRESS_MACRO_LOGS logger.critical("Failed to install final hook: {} with flamingo: {}", T::name(), install_result.error()); - #endif +#endif + return; } - #else - registerInlineHook((uint32_t) addr, (uint32_t) T::hook(), (uint32_t **) T::trampoline()); - inlineHook((uint32_t) addr); - #endif +#else + registerInlineHook((uint32_t)addr, (uint32_t)T::hook(), (uint32_t**)T::trampoline()); + inlineHook((uint32_t)addr); +#endif } -template -requires (is_addr_hook && !is_findCall_hook && is_logger) +template + requires(is_addr_hook && !is_findCall_hook && is_logger) void InstallHook(L& logger) { // Install T assuming it is an address hook. - auto addr = static_cast(getRealOffset(T::addr())); - __InstallHook(logger, addr); + auto addr = reinterpret_cast(getRealOffset(T::addr())); + return __InstallHook(logger, addr); } -template -requires (is_findCall_hook && !is_addr_hook && is_logger) +template + requires(is_findCall_hook && !is_addr_hook && is_logger) void InstallHook(L& logger) { // Install T assuming it is a hook that should call FindMethod. auto info = T::getInfo(); if (!info) { - #ifndef SUPPRESS_MACRO_LOGS +#ifndef SUPPRESS_MACRO_LOGS logger.critical("Attempting to install hook: {}, but method could not be found!", T::name()); - #endif +#endif SAFE_ABORT(); } - auto addr = (void*) info->methodPointer; - __InstallHook(logger, addr); + auto addr = (void*)info->methodPointer; + return __InstallHook(logger, addr); } -template -requires (is_findCall_hook && !is_addr_hook && is_logger) +template + requires(is_findCall_hook && !is_addr_hook && is_logger) void InstallOrigHook(L& logger) { // Install T assuming it is a hook that should call FindMethod. auto info = T::getInfo(); if (!info) { - #ifndef SUPPRESS_MACRO_LOGS +#ifndef SUPPRESS_MACRO_LOGS logger.critical("Attempting to install final (orig) hook: {}, but method could not be found!", T::name()); - #endif +#endif SAFE_ABORT(); } - auto addr = (void*) info->methodPointer; - __InstallFinalHook(logger, addr); + auto addr = (void*)info->methodPointer; + return __InstallFinalHook(logger, addr); } -template -requires (is_hook && is_logger) +template + requires(is_hook && is_logger) void InstallHookDirect(L& logger, void* dst) { // Install T into the specified address. Null checks dst. if (!dst) { - #ifndef SUPPRESS_MACRO_LOGS +#ifndef SUPPRESS_MACRO_LOGS logger.critical("Attempting to install direct hook: {}, but was installing to an invalid destination!", T::name()); - #endif +#endif SAFE_ABORT(); } - __InstallHook(logger, dst); + return __InstallHook(logger, dst); } +/// Makes a flamingo hook builder for the provided hook type, finding the method via address. +template +bs_hook::FlamingoHandleBuilder MakeFlamingoHook(Paper::LoggerContext logger, std::string_view namespaze, void* addr) { + flamingo::HookInfo hookInfo((void*)T::hook(), addr, (void**)T::trampoline(), flamingo::HookNameMetadata{ .name = T::name(), .namespaze = std::string(namespaze) }); + return bs_hook::FlamingoHandleBuilder(logger, hookInfo); +} + +/// Makes a flamingo hook builder for the provided hook type, finding the method via address. +template + requires(is_logger && is_addr_hook) +bs_hook::FlamingoHandleBuilder MakeFlamingoHook(L& logger, std::string_view namespaze) { + auto addr = reinterpret_cast(getRealOffset(T::addr())); + return MakeFlamingoHook(logger, namespaze, addr); +} + +/// Makes a flamingo hook builder for the provided hook type, finding the method via il2cpp_utils. +template + requires(is_logger && is_findCall_hook) +bs_hook::FlamingoHandleBuilder MakeFlamingoHook(L& logger, std::string_view namespaze) { + auto info = T::getInfo(); + if (!info) { +#ifndef SUPPRESS_MACRO_LOGS + logger.critical("Attempting to make flamingo hook: {}, but method could not be found!", T::name()); +#endif + SAFE_ABORT(); + } + auto addr = (void*)info->methodPointer; + return MakeFlamingoHook(logger, namespaze, addr); +} + +/// Modloader modinfo version of MakeFlamingoHook +template +bs_hook::FlamingoHandleBuilder MakeFlamingoHook(L& logger, modloader::ModInfo const& modInfo, TArgs... args) { + return MakeFlamingoHook(logger, std::string_view(modInfo.id), std::forward(args)...); +} + +#define FLAMINGO_HOOK(logger, namespaze, name) ::Hooking::MakeFlamingoHook(logger, namespaze) +#define FLAMINGO_HOOK_DIRECT(logger, namespaze, addr) ::Hooking::MakeFlamingoHook(logger, namespaze, addr) + // Installs the provided hook using the logger provided. // This properly specializes based off of whichever MAKE_HOOK macro you used, but is only valid if the name is from a MAKE_HOOK... macro. -#define INSTALL_HOOK(logger, name) ::Hooking::InstallHook(logger); +#define INSTALL_HOOK(logger, name) ::Hooking::InstallHook(logger) // Installs the provided hook using the logger provided to the address specified directly. // This is only valid if the name is from a MAKE_HOOK... macro. -#define INSTALL_HOOK_DIRECT(logger, name, addr) ::Hooking::InstallHookDirect(logger, addr); +#define INSTALL_HOOK_DIRECT(logger, name, addr) ::Hooking::InstallHookDirect(logger, addr) // Installs the provided hook using the logger provided to the 'orig' point of the hook. // This is only valid if the name is from a MAKE_HOOK macro that does not use a fixed offset. -#define INSTALL_HOOK_ORIG(logger, name) ::Hooking::InstallOrigHook(logger); +#define INSTALL_HOOK_ORIG(logger, name) ::Hooking::InstallOrigHook(logger) // TODO: Not yet implemented #define UNINSTALL_HOOK(logger, name) void // TODO: Not yet implemented #define UNINSTALL_HOOK_DIRECT(logger, name, addr) void -} +} // namespace Hooking diff --git a/shared/utils/result.hpp b/shared/utils/result.hpp index 6490a439..cbed7b4c 100644 --- a/shared/utils/result.hpp +++ b/shared/utils/result.hpp @@ -6,7 +6,7 @@ #include "il2cpp-utils-exceptions.hpp" -namespace il2cpp_utils { +namespace bs_hook { namespace exceptions { struct ResultException : il2cpp_utils::exceptions::StackTraceException { @@ -50,6 +50,19 @@ struct Result { } } + template + static Result Ok(Args&&... args) noexcept { + return Result(SuccessValue(std::forward(args)...)); + } + + + template + static Result Err(Args&&... args) noexcept { + return Result(ExceptionValue(std::forward(args)...)); + } + + + [[nodiscard]] inline bool has_result() const noexcept { return success; } @@ -58,38 +71,38 @@ struct Result { } [[nodiscard]] constexpr SuccessValue const& get_result() const { - if (!success) throw il2cpp_utils::exceptions::ResultException("Result does not contain a success result!"); + if (!success) throw bs_hook::exceptions::ResultException("Result does not contain a success result!"); return result; } [[nodiscard]] constexpr SuccessValue& get_result() { - if (!success) throw il2cpp_utils::exceptions::ResultException("Result does not contain a success result!"); + if (!success) throw bs_hook::exceptions::ResultException("Result does not contain a success result!"); return result; } /// move result value out of this wrapper [[nodiscard]] constexpr SuccessValue move_result() { - if (!success) throw il2cpp_utils::exceptions::ResultException("Result does not contain a success result!"); + if (!success) throw bs_hook::exceptions::ResultException("Result does not contain a success result!"); return std::move(result); } [[nodiscard]] constexpr ExceptionValue const& get_exception() const { - if (success) throw il2cpp_utils::exceptions::ResultException("Result does not contain an exception result!"); + if (success) throw bs_hook::exceptions::ResultException("Result does not contain an exception result!"); return exception; } [[nodiscard]] constexpr ExceptionValue& get_exception() { - if (success) throw il2cpp_utils::exceptions::ResultException("Result does not contain an exception result!"); + if (success) throw bs_hook::exceptions::ResultException("Result does not contain an exception result!"); return exception; } /// move result value out of this wrapper [[nodiscard]] constexpr ExceptionValue move_exception() { - if (success) throw il2cpp_utils::exceptions::ResultException("Result does not contain an exception result!"); + if (success) throw bs_hook::exceptions::ResultException("Result does not contain an exception result!"); return std::move(exception); } @@ -197,4 +210,10 @@ struct Result { }; }; } // namespace -} // namespace il2cpp_utils \ No newline at end of file +} // namespace bs_hook + +namespace il2cpp_utils { + using bs_hook::exceptions::ResultException; + using bs_hook::Result; + using bs_hook::TypeOrMonostate; +} \ No newline at end of file diff --git a/src/tests/hook-tests.cpp b/src/tests/hook-tests.cpp index 7c0c556e..095a4b1c 100644 --- a/src/tests/hook-tests.cpp +++ b/src/tests/hook-tests.cpp @@ -1,12 +1,14 @@ // #define TEST_HOOK +#include "utils/flamingo-utils.hpp" #ifdef TEST_HOOK #pragma clang diagnostic push // these warnings are not relevant here because we are causing them "on purpose" so we disable the warnings here #pragma clang diagnostic ignored "-Wunused-value" #pragma clang diagnostic ignored "-Wunused-variable" #pragma clang diagnostic ignored "-Wunused-parameter" -#include "../../shared/utils/hooking.hpp" #include "../../shared/utils/base-wrapper-type.hpp" +#include "../../shared/utils/hooking.hpp" +#include "utils/logging.hpp" MAKE_HOOK(test, 0x0, void, int arg) { throw il2cpp_utils::RunMethodException("lol rekt", nullptr); @@ -17,7 +19,7 @@ void* test2(void* one, void*) { return one; } -template<> +template <> struct ::il2cpp_utils::il2cpp_type_check::MetadataGetter<&test2> { static const MethodInfo* methodInfo() { return nullptr; @@ -33,5 +35,116 @@ MAKE_HOOK_WRAPPER(test2_hook, &test2, bs_hook::Il2CppWrapperType, bs_hook::Il2Cp return ret; // Return from overall hook is converted to a void* } + +void install_a_hook() { + // legacy hook API + ::Hooking ::InstallHook(il2cpp_utils ::Logger); + + INSTALL_HOOK(il2cpp_utils::Logger, test2_hook); + + // new Hook API! + modloader::ModInfo bs_hooks_mod_info = { MOD_ID, "1.0.0", 0 }; + modloader::ModInfo chroma = { "chroma", "1.0.0", 0 }; + auto hook = FLAMINGO_HOOK(il2cpp_utils::Logger, bs_hooks_mod_info, test2_hook).after(chroma).before("test-mod").install(); + auto hook2 = FLAMINGO_HOOK(il2cpp_utils::Logger, "bs_hooks", test2_hook).after(chroma).before("test-mod").install(); + + auto doUninstall = false; + if (doUninstall) { + // uninstall the hook, which will also remove it from flamingo + // hook is now invalid after this call + auto builder = hook.uninstall().get_result(); + + builder.after("another-mod").before("test-mod-2").final(); + // reinstall the hook + hook = builder.install(); + } +} + +#pragma region TemporaryHookSetup +std::vector to_install_hooks; +std::vector installed_hooks; + + +void install_all_hooks() { + for (auto& builder : to_install_hooks) { + auto result = builder.installOrError(); + if (result.has_exception()) { + // Handle installation error (e.g., log it) + il2cpp_utils::Logger.error("Failed to install hook: {}", builder.hookInfo.metadata.name_info); + } else { + // Successfully installed the hook, we can track the handle for potential uninstallation + installed_hooks.emplace_back(std::move(result.get_result())); + } + } + to_install_hooks.clear(); +} + +void uninstall_all_hooks() { + for (auto& hook : installed_hooks) { + auto result = hook.uninstall(); + if (result.has_exception()) { + // Handle uninstallation error (e.g., log it) + il2cpp_utils::Logger.error("Failed to uninstall hook: {}", hook.info.metadata.name_info); + } else { + // Successfully uninstalled the hook, we can track the builder for potential reinstallation + to_install_hooks.emplace_back(std::move(result.get_result())); + } + } + installed_hooks.clear(); +} + +// Install a Flamingo hook and track it. The trailing chaining methods +// (like `.after(...)` / `.before(...)`) are optional — pass them as +// additional tokens starting with a dot, e.g. +// `TEMP_FLAMINGO_INSTALL(logger, owner, hook_fn, .after(chroma).before("mod"))` +#define TEMP_FLAMINGO_INSTALL(logger, owner, hook_fn, ...) to_install_hooks.emplace_back((FLAMINGO_HOOK(logger, owner, hook_fn) __VA_ARGS__)) + + +// Converts FROM void* instances --> the wrapper types when the hook is invoked +MAKE_HOOK_WRAPPER(test3_install_hooks, &test2, bs_hook::Il2CppWrapperType, bs_hook::Il2CppWrapperType one, bs_hook::Il2CppWrapperType two) { + // Converts from wrapper types --> void* instances to invoke orig + // Return is a wrapper type + auto ret = test3_install_hooks(one, two); + static_assert(std::is_same_v); + + install_all_hooks(); + + return ret; + // Return from overall hook is converted to a void* +} +// Converts FROM void* instances --> the wrapper types when the hook is invoked +MAKE_HOOK_WRAPPER(test3_uninstall_hooks, &test2, void*, void* one, void* two) { + // Converts from wrapper types --> void* instances to invoke orig + // Return is a wrapper type + auto ret = test3_uninstall_hooks(one, two); + + uninstall_all_hooks(); + + return ret; + // Return from overall hook is converted to a void* +} + +void build_temporary_hooks() { + // new Hook API! + + modloader::ModInfo bs_hooks_mod_info = { MOD_ID, "1.0.0", 0 }; + modloader::ModInfo chroma = { "chroma", "1.0.0", 0 }; + TEMP_FLAMINGO_INSTALL(il2cpp_utils::Logger, bs_hooks_mod_info, test2_hook, .after(chroma).before("test-mod")); + TEMP_FLAMINGO_INSTALL(il2cpp_utils::Logger, "bs_hooks", test2_hook, .after(chroma).before("test-mod")); + TEMP_FLAMINGO_INSTALL(il2cpp_utils::Logger, "bs_hooks", test2_hook, .final()); + TEMP_FLAMINGO_INSTALL(il2cpp_utils::Logger, "bs_hooks", test2_hook); + + auto installer = FLAMINGO_HOOK(il2cpp_utils::Logger, bs_hooks_mod_info, test3_install_hooks) + .after(chroma) + .before("test-mod") + .install(); + auto uninstaller = FLAMINGO_HOOK(il2cpp_utils::Logger, bs_hooks_mod_info, test3_uninstall_hooks) + .after(chroma) + .before("test-mod") + .install(); +} + +#pragma endregion + #pragma clang diagnostic pop #endif