From 1a14c4231075f0eb7ec39a97e8a7419a22e4645a Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Tue, 23 Dec 2025 15:21:00 -0400 Subject: [PATCH 1/9] Initial flamingo draft --- shared/utils/flamingo-utils.hpp | 98 +++ shared/utils/hooking.hpp | 1174 ++++++++++++++++++++----------- src/tests/hook-tests.cpp | 16 +- 3 files changed, 865 insertions(+), 423 deletions(-) create mode 100644 shared/utils/flamingo-utils.hpp diff --git a/shared/utils/flamingo-utils.hpp b/shared/utils/flamingo-utils.hpp new file mode 100644 index 00000000..4099ff0e --- /dev/null +++ b/shared/utils/flamingo-utils.hpp @@ -0,0 +1,98 @@ +#include +#include "flamingo/shared/target-data.hpp" +#include "flamingo/shared/installer.hpp" +#include "flamingo/shared/hook-installation-result.hpp" +#include "flamingo/shared/hook-data.hpp" + +namespace bs_hook { +struct FlamingoHandleHelper { + FlamingoHandleHelper() = default; + ~FlamingoHandleHelper() = default; + + // disable copy + FlamingoHandleHelper(FlamingoHandleHelper const&) = delete; + FlamingoHandleHelper& operator=(FlamingoHandleHelper const&) = delete; + + // disable move + FlamingoHandleHelper(FlamingoHandleHelper&&) = delete; + FlamingoHandleHelper& operator=(FlamingoHandleHelper&&) = delete; + + flamingo::HookHandle handle; + + FlamingoHandleHelper(flamingo::HookHandle handle) : handle(handle) { + + } + + operator flamingo::HookHandle() const { + return handle; + } + + + flamingo::HookHandle convert() { + return handle; + } + + FlamingoHandleHelper& final(bool isFinal = true) { + handle.hook_location->metadata.priority.is_final = isFinal; + return *this; + } + + FlamingoHandleHelper& after(modloader::ModInfo const& info) { + after(info.id); + return *this; + } + + FlamingoHandleHelper& after(std::string_view modID) { + handle.hook_location->metadata.priority.afters.emplace_back(flamingo::HookNameMetadata{ + .name = std::string(modID) + }); + + return *this; + } + + FlamingoHandleHelper& before(modloader::ModInfo const& info) { + before(info.id); + return *this; + } + + FlamingoHandleHelper& before(std::string_view modID) { + handle.hook_location->metadata.priority.befores.emplace_back(flamingo::HookNameMetadata{ + .name = std::string(modID) + }); + return *this; + } + + /// @brief Moves this hook to be the last in priority (lowest priority) + /// @param n Optional offset from last (0 = last, 1 = second last, etc) + void last(int n = 0) { + + } + + /// @brief Moves this hook to be the first in priority (highest priority) + /// @param n Optional offset from first (0 = first, 1 = second first, etc) + void first(int n = 0) { + + } + + /// @brief Gets the current position of this hook in the priority list (0 = first/highest) + int position() const { + return 0; + } + + void reinstall() { + auto result = flamingo::Reinstall({handle.hook_location->target}); + if (result.has_value()) { + // Reinstall successful + } + } + + // TODO: Allow reinstalling, this currently invalidates the handle though + void uninstall() { + auto result = flamingo::Uninstall(handle); + if (result.has_value()) { + // Uninstall successful + } + // TODO: Error handle + } +}; +} // namespace bs_hook \ No newline at end of file diff --git a/shared/utils/hooking.hpp b/shared/utils/hooking.hpp index 6811220f..b7f58a64 100644 --- a/shared/utils/hooking.hpp +++ b/shared/utils/hooking.hpp @@ -1,40 +1,41 @@ #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 +43,65 @@ 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; + // Flamingo hook info + { T::flamingo() } -> 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 +109,632 @@ 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 +745,64 @@ 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 +813,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 +830,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 +844,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 +858,38 @@ 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 bs_hook::FlamingoHandleHelper& flamingo() { \ + return flamingoHandle; \ + } \ + static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ + 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 +931,124 @@ 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 -inline void __InstallHook(L& logger, void* addr) { - #ifndef SUPPRESS_MACRO_LOGS +template +inline bs_hook::FlamingoHandleHelper& __InstallHook(L& logger, void* addr) { +#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 + T::flamingo() = install_result.value().returned_handle; + return T::flamingo(); } 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 T::flamingo(); } - #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 -void __InstallFinalHook(L& logger, void* addr) { - #ifndef SUPPRESS_MACRO_LOGS +template +bs_hook::FlamingoHandleHelper& __InstallFinalHook(L& logger, void* addr) { +#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 + T::flamingo() = install_result.value().returned_handle; + return T::flamingo(); } 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 T::flamingo(); } - #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) -void InstallHook(L& logger) { +template + requires(is_addr_hook && !is_findCall_hook && is_logger) +bs_hook::FlamingoHandleHelper& InstallHook(L& logger) { // Install T assuming it is an address hook. auto addr = static_cast(getRealOffset(T::addr())); - __InstallHook(logger, addr); + return __InstallHook(logger, addr); } -template -requires (is_findCall_hook && !is_addr_hook && is_logger) -void InstallHook(L& logger) { +template + requires(is_findCall_hook && !is_addr_hook && is_logger) +bs_hook::FlamingoHandleHelper& 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) -void InstallOrigHook(L& logger) { +template + requires(is_findCall_hook && !is_addr_hook && is_logger) +bs_hook::FlamingoHandleHelper& 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) -void InstallHookDirect(L& logger, void* dst) { +template + requires(is_hook && is_logger) +bs_hook::FlamingoHandleHelper& 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); } // 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/src/tests/hook-tests.cpp b/src/tests/hook-tests.cpp index 7c0c556e..8cd92823 100644 --- a/src/tests/hook-tests.cpp +++ b/src/tests/hook-tests.cpp @@ -7,6 +7,8 @@ #pragma clang diagnostic ignored "-Wunused-parameter" #include "../../shared/utils/hooking.hpp" #include "../../shared/utils/base-wrapper-type.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,17 @@ 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() { + ::Hooking ::InstallHook(il2cpp_utils ::Logger).after("test-mod"); + + modloader::ModInfo chroma = { "chroma", "1.0.0", 0 }; + auto& hook = INSTALL_HOOK(il2cpp_utils::Logger, test2_hook).after(chroma).before("test"); + + if (false) { + hook.uninstall(); + } +} + #pragma clang diagnostic pop #endif From 9367e94ca3a48625b93ecd1896ce57559dd79186 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Mon, 29 Dec 2025 16:47:42 -0400 Subject: [PATCH 2/9] Finish flamingo reordering implementation --- qpm.shared.json | 20 +++++------- shared/utils/flamingo-utils.hpp | 57 ++++++++++++--------------------- shared/utils/hooking.hpp | 4 +-- 3 files changed, 30 insertions(+), 51 deletions(-) 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 index 4099ff0e..17d51be6 100644 --- a/shared/utils/flamingo-utils.hpp +++ b/shared/utils/flamingo-utils.hpp @@ -1,8 +1,9 @@ #include -#include "flamingo/shared/target-data.hpp" -#include "flamingo/shared/installer.hpp" -#include "flamingo/shared/hook-installation-result.hpp" +#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" namespace bs_hook { struct FlamingoHandleHelper { @@ -18,17 +19,13 @@ struct FlamingoHandleHelper { FlamingoHandleHelper& operator=(FlamingoHandleHelper&&) = delete; flamingo::HookHandle handle; + FlamingoHandleHelper(flamingo::HookHandle handle) : handle(handle) {} - FlamingoHandleHelper(flamingo::HookHandle handle) : handle(handle) { - - } - - operator flamingo::HookHandle() const { + operator std::optional() const { return handle; } - - flamingo::HookHandle convert() { + std::optional convert() { return handle; } @@ -37,61 +34,47 @@ struct FlamingoHandleHelper { return *this; } - FlamingoHandleHelper& after(modloader::ModInfo const& info) { - after(info.id); + FlamingoHandleHelper& after(modloader::ModInfo const& info, std::string_view name = {}) { + after(info.id, name); return *this; } - FlamingoHandleHelper& after(std::string_view modID) { + FlamingoHandleHelper& after(std::string_view modID, std::string_view name = {}) { handle.hook_location->metadata.priority.afters.emplace_back(flamingo::HookNameMetadata{ - .name = std::string(modID) + .name = std::string(name), + .namespaze = std::string(modID), }); return *this; } - FlamingoHandleHelper& before(modloader::ModInfo const& info) { - before(info.id); + FlamingoHandleHelper& before(modloader::ModInfo const& info, std::string_view name = {}) { + before(info.id, name); return *this; } - FlamingoHandleHelper& before(std::string_view modID) { - handle.hook_location->metadata.priority.befores.emplace_back(flamingo::HookNameMetadata{ - .name = std::string(modID) - }); + FlamingoHandleHelper& before(std::string_view modID, std::string_view name = {}) { + handle.hook_location->metadata.priority.befores.emplace_back(flamingo::HookNameMetadata{ .name = std::string(name), .namespaze = std::string(modID) }); return *this; } - /// @brief Moves this hook to be the last in priority (lowest priority) - /// @param n Optional offset from last (0 = last, 1 = second last, etc) - void last(int n = 0) { - - } - - /// @brief Moves this hook to be the first in priority (highest priority) - /// @param n Optional offset from first (0 = first, 1 = second first, etc) - void first(int n = 0) { - - } - - /// @brief Gets the current position of this hook in the priority list (0 = first/highest) - int position() const { - return 0; - } void reinstall() { - auto result = flamingo::Reinstall({handle.hook_location->target}); + auto result = flamingo::Reinstall({ handle.hook_location->target }); if (result.has_value()) { // Reinstall successful } } // TODO: Allow reinstalling, this currently invalidates the handle though + /// @brief Uninstalls the hook associated with this handle. + /// After uninstalling, the handle is no longer valid. void uninstall() { auto result = flamingo::Uninstall(handle); if (result.has_value()) { // Uninstall successful } + handle = {}; // TODO: Error handle } }; diff --git a/shared/utils/hooking.hpp b/shared/utils/hooking.hpp index b7f58a64..7265eb08 100644 --- a/shared/utils/hooking.hpp +++ b/shared/utils/hooking.hpp @@ -943,7 +943,7 @@ inline bs_hook::FlamingoHandleHelper& __InstallHook(L& logger, void* addr) { #ifndef SUPPRESS_MACRO_LOGS logger.info("Hook: {} installed with flamingo!", (const char*)T::name()); #endif - T::flamingo() = install_result.value().returned_handle; + T::flamingo().handle = install_result.value().returned_handle; return T::flamingo(); } else { #ifndef SUPPRESS_MACRO_LOGS @@ -990,7 +990,7 @@ template requires(is_addr_hook && !is_findCall_hook && is_logger) bs_hook::FlamingoHandleHelper& InstallHook(L& logger) { // Install T assuming it is an address hook. - auto addr = static_cast(getRealOffset(T::addr())); + auto addr = reinterpret_cast(getRealOffset(T::addr())); return __InstallHook(logger, addr); } template From 8921f921762313632cf098aac714fef07c438595 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Mon, 29 Dec 2025 17:05:59 -0400 Subject: [PATCH 3/9] Refactor FlamingoHandleHelper to use std::optional for handle management and update hook-tests to ensure proper hook installation and uninstallation flow --- shared/utils/flamingo-utils.hpp | 34 +++++++++++++++++++++++++-------- src/tests/hook-tests.cpp | 8 +++++--- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/shared/utils/flamingo-utils.hpp b/shared/utils/flamingo-utils.hpp index 17d51be6..d8f1cb1d 100644 --- a/shared/utils/flamingo-utils.hpp +++ b/shared/utils/flamingo-utils.hpp @@ -1,4 +1,5 @@ #include +#include #include #include "flamingo/shared/hook-data.hpp" #include "flamingo/shared/hook-installation-result.hpp" @@ -18,7 +19,7 @@ struct FlamingoHandleHelper { FlamingoHandleHelper(FlamingoHandleHelper&&) = delete; FlamingoHandleHelper& operator=(FlamingoHandleHelper&&) = delete; - flamingo::HookHandle handle; + std::optional handle; FlamingoHandleHelper(flamingo::HookHandle handle) : handle(handle) {} operator std::optional() const { @@ -30,7 +31,10 @@ struct FlamingoHandleHelper { } FlamingoHandleHelper& final(bool isFinal = true) { - handle.hook_location->metadata.priority.is_final = isFinal; + if (!handle.has_value()) { + return *this; + } + handle->hook_location->metadata.priority.is_final = isFinal; return *this; } @@ -40,7 +44,10 @@ struct FlamingoHandleHelper { } FlamingoHandleHelper& after(std::string_view modID, std::string_view name = {}) { - handle.hook_location->metadata.priority.afters.emplace_back(flamingo::HookNameMetadata{ + if (!handle.has_value()) { + return *this; + } + handle->hook_location->metadata.priority.afters.emplace_back(flamingo::HookNameMetadata{ .name = std::string(name), .namespaze = std::string(modID), }); @@ -54,27 +61,38 @@ struct FlamingoHandleHelper { } FlamingoHandleHelper& before(std::string_view modID, std::string_view name = {}) { - handle.hook_location->metadata.priority.befores.emplace_back(flamingo::HookNameMetadata{ .name = std::string(name), .namespaze = std::string(modID) }); + if (!handle.has_value()) { + return *this; + } + handle->hook_location->metadata.priority.befores.emplace_back(flamingo::HookNameMetadata{ .name = std::string(name), .namespaze = std::string(modID) }); return *this; } - void reinstall() { - auto result = flamingo::Reinstall({ handle.hook_location->target }); + FlamingoHandleHelper& reinstall() { + if (!handle.has_value()) { + return *this; + } + auto result = flamingo::Reinstall({ handle->hook_location->target }); if (result.has_value()) { // Reinstall successful } + + return *this; } // TODO: Allow reinstalling, this currently invalidates the handle though /// @brief Uninstalls the hook associated with this handle. /// After uninstalling, the handle is no longer valid. void uninstall() { - auto result = flamingo::Uninstall(handle); + if (!handle.has_value()) { + return; + } + auto result = flamingo::Uninstall(*handle); if (result.has_value()) { // Uninstall successful } - handle = {}; + handle = std::nullopt; // TODO: Error handle } }; diff --git a/src/tests/hook-tests.cpp b/src/tests/hook-tests.cpp index 8cd92823..e8f15f31 100644 --- a/src/tests/hook-tests.cpp +++ b/src/tests/hook-tests.cpp @@ -5,11 +5,10 @@ #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); } @@ -40,9 +39,12 @@ void install_a_hook() { ::Hooking ::InstallHook(il2cpp_utils ::Logger).after("test-mod"); modloader::ModInfo chroma = { "chroma", "1.0.0", 0 }; - auto& hook = INSTALL_HOOK(il2cpp_utils::Logger, test2_hook).after(chroma).before("test"); + // reinstall after setting priorities + auto& hook = INSTALL_HOOK(il2cpp_utils::Logger, test2_hook).after(chroma).before("test").reinstall(); if (false) { + // uninstall the hook, which will also remove it from flamingo + // hook is now invalid after this call hook.uninstall(); } } From 5e38f2f2efb9c7d0f2163505bc50bab2098c7cb1 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:41:25 -0400 Subject: [PATCH 4/9] Redo hook API for better ergonomics --- shared/utils/flamingo-utils.hpp | 137 ++++++++++++--------- shared/utils/hooking.hpp | 205 ++++++++++---------------------- shared/utils/result.hpp | 13 ++ src/tests/hook-tests.cpp | 15 ++- 4 files changed, 172 insertions(+), 198 deletions(-) diff --git a/shared/utils/flamingo-utils.hpp b/shared/utils/flamingo-utils.hpp index d8f1cb1d..a16b9bfb 100644 --- a/shared/utils/flamingo-utils.hpp +++ b/shared/utils/flamingo-utils.hpp @@ -1,53 +1,43 @@ #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" -namespace bs_hook { -struct FlamingoHandleHelper { - FlamingoHandleHelper() = default; - ~FlamingoHandleHelper() = default; - - // disable copy - FlamingoHandleHelper(FlamingoHandleHelper const&) = delete; - FlamingoHandleHelper& operator=(FlamingoHandleHelper const&) = delete; +#include "./result.hpp" - // disable move - FlamingoHandleHelper(FlamingoHandleHelper&&) = delete; - FlamingoHandleHelper& operator=(FlamingoHandleHelper&&) = delete; +namespace bs_hook { +struct FlamingoHandle; - std::optional handle; - FlamingoHandleHelper(flamingo::HookHandle handle) : handle(handle) {} +struct FlamingoHandleBuilder { + Paper::LoggerContext logger; + flamingo::HookInfo hookInfo; - operator std::optional() const { - return handle; + 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; - std::optional convert() { - return handle; - } + FlamingoHandleBuilder& operator=(FlamingoHandleBuilder const&) = default; + FlamingoHandleBuilder& operator=(FlamingoHandleBuilder&&) = default; - FlamingoHandleHelper& final(bool isFinal = true) { - if (!handle.has_value()) { - return *this; - } - handle->hook_location->metadata.priority.is_final = isFinal; + FlamingoHandleBuilder& final(bool isFinal = true) { + hookInfo.metadata.priority.is_final = isFinal; return *this; } - FlamingoHandleHelper& after(modloader::ModInfo const& info, std::string_view name = {}) { + FlamingoHandleBuilder& after(modloader::ModInfo const& info, std::string_view name = {}) { after(info.id, name); return *this; } - FlamingoHandleHelper& after(std::string_view modID, std::string_view name = {}) { - if (!handle.has_value()) { - return *this; - } - handle->hook_location->metadata.priority.afters.emplace_back(flamingo::HookNameMetadata{ + 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), }); @@ -55,45 +45,84 @@ struct FlamingoHandleHelper { return *this; } - FlamingoHandleHelper& before(modloader::ModInfo const& info, std::string_view name = {}) { + FlamingoHandleBuilder& before(modloader::ModInfo const& info, std::string_view name = {}) { before(info.id, name); return *this; } - FlamingoHandleHelper& before(std::string_view modID, std::string_view name = {}) { - if (!handle.has_value()) { - return *this; - } - handle->hook_location->metadata.priority.befores.emplace_back(flamingo::HookNameMetadata{ .name = std::string(name), .namespaze = std::string(modID) }); + 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]] + il2cpp_utils::Result installOrError() noexcept; - FlamingoHandleHelper& reinstall() { - if (!handle.has_value()) { - return *this; - } - auto result = flamingo::Reinstall({ handle->hook_location->target }); - if (result.has_value()) { - // Reinstall successful - } + [[nodiscard]] + FlamingoHandle install(); +}; - return *this; +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; } - // TODO: Allow reinstalling, this currently invalidates the handle though /// @brief Uninstalls the hook associated with this handle. - /// After uninstalling, the handle is no longer valid. - void uninstall() { - if (!handle.has_value()) { - return; - } - auto result = flamingo::Uninstall(*handle); + [[nodiscard]] + il2cpp_utils::Result uninstall() { + using Result = il2cpp_utils::Result; + auto result = flamingo::Uninstall(handle); if (result.has_value()) { - // Uninstall successful + return Result::Ok(FlamingoHandleBuilder(logger, info)); } - handle = std::nullopt; - // TODO: Error handle + return Result::Err(std::monostate{}); + } + + [[nodiscard]] + auto reinstall() { + auto result = flamingo::Reinstall({ handle.hook_location->target }); + return result; } }; + +inline il2cpp_utils::Result FlamingoHandleBuilder::installOrError() noexcept { + using Result = il2cpp_utils::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 7265eb08..68fadab8 100644 --- a/shared/utils/hooking.hpp +++ b/shared/utils/hooking.hpp @@ -12,6 +12,10 @@ #include "./flamingo-utils.hpp" +#ifndef BS_HOOKS_HOOK_NAMESPACE +#define BS_HOOKS_HOOK_NAMESPACE MOD_ID +#endif + namespace Hooking { // For use in MAKE_HOOK_AUTO bodies. // Currently unused. @@ -51,8 +55,6 @@ concept is_hook = requires { { T::trampoline() } -> std::same_as; // Must have a hook that returns the funcType { T::hook() } -> std::same_as; - // Flamingo hook info - { T::flamingo() } -> std::same_as; }; template @@ -125,37 +127,29 @@ struct HookCatchWrapper { static funcType hook() { \ return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ 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 bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ - static retval 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. @@ -175,10 +169,6 @@ struct HookCatchWrapper { static funcType hook() { \ return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -200,10 +190,6 @@ struct HookCatchWrapper { static funcType hook() { \ return hook_##name_; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -225,10 +211,6 @@ struct HookCatchWrapper { static funcType hook() { \ return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -250,10 +232,6 @@ struct HookCatchWrapper { static funcType hook() { \ return hook_##name_; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -275,10 +253,6 @@ struct HookCatchWrapper { static funcType hook() { \ return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -300,10 +274,6 @@ struct HookCatchWrapper { static funcType hook() { \ return hook_##name_; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -327,10 +297,6 @@ struct HookCatchWrapper { static funcType hook() { \ return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -353,10 +319,6 @@ struct HookCatchWrapper { static funcType hook() { \ return hook_##name_; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -380,10 +342,6 @@ struct HookCatchWrapper { static funcType hook() { \ return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -406,10 +364,6 @@ struct HookCatchWrapper { static funcType hook() { \ return hook_##name_; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -433,10 +387,6 @@ struct HookCatchWrapper { static funcType hook() { \ return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -459,10 +409,6 @@ struct HookCatchWrapper { static funcType hook() { \ return hook_##name_; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -486,10 +432,6 @@ struct HookCatchWrapper { static funcType hook() { \ return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -512,10 +454,6 @@ struct HookCatchWrapper { static funcType hook() { \ return hook_##name_; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -539,10 +477,6 @@ struct HookCatchWrapper { static funcType hook() { \ return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -565,10 +499,6 @@ struct HookCatchWrapper { static funcType hook() { \ return hook_##name_; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -592,10 +522,6 @@ struct HookCatchWrapper { static funcType hook() { \ return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -618,10 +544,6 @@ struct HookCatchWrapper { static funcType hook() { \ return hook_##name_; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -642,10 +564,6 @@ struct HookCatchWrapper { return &name_; \ } \ static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static funcType hook() { \ return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ } \ @@ -667,10 +585,6 @@ struct HookCatchWrapper { static funcType* trampoline() { \ return &name_; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static inline retval (*name_)(__VA_ARGS__) = nullptr; \ static funcType hook() { \ return hook_##name_; \ @@ -696,10 +610,6 @@ struct HookCatchWrapper { static funcType* trampoline() { \ return &name_; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static inline retval (*name_)(__VA_ARGS__) = nullptr; \ static funcType hook() { \ return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ @@ -725,10 +635,6 @@ struct HookCatchWrapper { return &name_; \ } \ static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static funcType hook() { \ return hook_##name_; \ } \ @@ -763,10 +669,6 @@ struct HookCatchWrapper { static funcType hook() { \ return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -791,10 +693,6 @@ struct HookCatchWrapper { static funcType hook() { \ return hook_##name_; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static retval hook_##name_(__VA_ARGS__); \ }; \ retval Hook_##name_::hook_##name_(__VA_ARGS__) @@ -872,10 +770,6 @@ struct HookWrapperInvoke { static funcType* trampoline() { \ return &orig_base; \ } \ - static bs_hook::FlamingoHandleHelper& flamingo() { \ - return flamingoHandle; \ - } \ - static inline bs_hook::FlamingoHandleHelper flamingoHandle; \ static inline funcType orig_base = nullptr; \ template \ static inline retval name_(TArgs... args) { \ @@ -932,7 +826,7 @@ struct HookWrapperInvoke { // }; template -inline bs_hook::FlamingoHandleHelper& __InstallHook(L& logger, void* addr) { +inline void __InstallHook(L& logger, void* addr) { #ifndef SUPPRESS_MACRO_LOGS logger.info("Installing hook: {} to offset: {}", T::name(), fmt::ptr(addr)); #endif @@ -943,14 +837,13 @@ inline bs_hook::FlamingoHandleHelper& __InstallHook(L& logger, void* addr) { #ifndef SUPPRESS_MACRO_LOGS logger.info("Hook: {} installed with flamingo!", (const char*)T::name()); #endif - T::flamingo().handle = install_result.value().returned_handle; - return T::flamingo(); + return; } else { #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 - return T::flamingo(); + return; } #else registerInlineHook((uint32_t)addr, (uint32_t)T::hook(), (uint32_t**)T::trampoline()); @@ -959,7 +852,7 @@ inline bs_hook::FlamingoHandleHelper& __InstallHook(L& logger, void* addr) { } template -bs_hook::FlamingoHandleHelper& __InstallFinalHook(L& logger, void* addr) { +void __InstallFinalHook(L& logger, void* addr) { #ifndef SUPPRESS_MACRO_LOGS logger.info("Installing final priority hook: {} to offset: {}", T::name(), fmt::ptr(addr)); #endif @@ -972,13 +865,12 @@ bs_hook::FlamingoHandleHelper& __InstallFinalHook(L& logger, void* addr) { #ifndef SUPPRESS_MACRO_LOGS logger.info("Final hook: {} installed with flamingo!", T::name()); #endif - T::flamingo() = install_result.value().returned_handle; - return T::flamingo(); + return; } else { #ifndef SUPPRESS_MACRO_LOGS logger.critical("Failed to install final hook: {} with flamingo: {}", T::name(), install_result.error()); #endif - return T::flamingo(); + return; } #else registerInlineHook((uint32_t)addr, (uint32_t)T::hook(), (uint32_t**)T::trampoline()); @@ -988,14 +880,14 @@ bs_hook::FlamingoHandleHelper& __InstallFinalHook(L& logger, void* addr) { template requires(is_addr_hook && !is_findCall_hook && is_logger) -bs_hook::FlamingoHandleHelper& InstallHook(L& logger) { +void InstallHook(L& logger) { // Install T assuming it is an address hook. auto addr = reinterpret_cast(getRealOffset(T::addr())); return __InstallHook(logger, addr); } template requires(is_findCall_hook && !is_addr_hook && is_logger) -bs_hook::FlamingoHandleHelper& InstallHook(L& logger) { +void InstallHook(L& logger) { // Install T assuming it is a hook that should call FindMethod. auto info = T::getInfo(); if (!info) { @@ -1009,7 +901,7 @@ bs_hook::FlamingoHandleHelper& InstallHook(L& logger) { } template requires(is_findCall_hook && !is_addr_hook && is_logger) -bs_hook::FlamingoHandleHelper& InstallOrigHook(L& logger) { +void InstallOrigHook(L& logger) { // Install T assuming it is a hook that should call FindMethod. auto info = T::getInfo(); if (!info) { @@ -1023,7 +915,7 @@ bs_hook::FlamingoHandleHelper& InstallOrigHook(L& logger) { } template requires(is_hook && is_logger) -bs_hook::FlamingoHandleHelper& InstallHookDirect(L& logger, void* dst) { +void InstallHookDirect(L& logger, void* dst) { // Install T into the specified address. Null checks dst. if (!dst) { #ifndef SUPPRESS_MACRO_LOGS @@ -1034,6 +926,39 @@ bs_hook::FlamingoHandleHelper& InstallHookDirect(L& logger, void* 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, void* addr) { + flamingo::HookInfo hookInfo((void*)T::hook(), addr, (void**)T::trampoline(), flamingo::HookNameMetadata{ .name = T::name(), .namespaze = BS_HOOKS_HOOK_NAMESPACE }); + 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) { + auto addr = reinterpret_cast(getRealOffset(T::addr())); + return MakeFlamingoHook(logger, 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) { + 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, addr); +} + +#define FLAMINGO_HOOK(logger, name) ::Hooking::MakeFlamingoHook(logger) +#define FLAMINGO_HOOK_DIRECT(logger, name, addr) ::Hooking::MakeFlamingoHook(logger, 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) diff --git a/shared/utils/result.hpp b/shared/utils/result.hpp index 6490a439..dcbf7469 100644 --- a/shared/utils/result.hpp +++ b/shared/utils/result.hpp @@ -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; } diff --git a/src/tests/hook-tests.cpp b/src/tests/hook-tests.cpp index e8f15f31..10c3b0dc 100644 --- a/src/tests/hook-tests.cpp +++ b/src/tests/hook-tests.cpp @@ -36,16 +36,23 @@ MAKE_HOOK_WRAPPER(test2_hook, &test2, bs_hook::Il2CppWrapperType, bs_hook::Il2Cp } void install_a_hook() { - ::Hooking ::InstallHook(il2cpp_utils ::Logger).after("test-mod"); + // legacy hook API + ::Hooking ::InstallHook(il2cpp_utils ::Logger); + INSTALL_HOOK(il2cpp_utils::Logger, test2_hook); + + // new Hook API! modloader::ModInfo chroma = { "chroma", "1.0.0", 0 }; - // reinstall after setting priorities - auto& hook = INSTALL_HOOK(il2cpp_utils::Logger, test2_hook).after(chroma).before("test").reinstall(); + auto hook = FLAMINGO_HOOK(il2cpp_utils::Logger, test2_hook).after(chroma).before("test-mod").install(); if (false) { // uninstall the hook, which will also remove it from flamingo // hook is now invalid after this call - hook.uninstall(); + auto builder = hook.uninstall().get_result(); + + builder.after("another-mod").before("test-mod-2").final(); + // reinstall the hook + hook = builder.install(); } } From 52fb59e82fb562b19b31a8becb782e0b818254c6 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:43:21 -0400 Subject: [PATCH 5/9] Pragma once header --- shared/utils/flamingo-utils.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/utils/flamingo-utils.hpp b/shared/utils/flamingo-utils.hpp index a16b9bfb..f22f9e0c 100644 --- a/shared/utils/flamingo-utils.hpp +++ b/shared/utils/flamingo-utils.hpp @@ -1,3 +1,5 @@ +#pragma once + #include #include #include From a6da168ddb43f6ec092be74cc4dca78751dda812 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Mon, 29 Dec 2025 23:10:03 -0400 Subject: [PATCH 6/9] Move Result to bs_hook, alias for temporary measures --- shared/utils/flamingo-utils.hpp | 10 +++++----- shared/utils/result.hpp | 22 ++++++++++++++-------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/shared/utils/flamingo-utils.hpp b/shared/utils/flamingo-utils.hpp index f22f9e0c..6c66b957 100644 --- a/shared/utils/flamingo-utils.hpp +++ b/shared/utils/flamingo-utils.hpp @@ -58,7 +58,7 @@ struct FlamingoHandleBuilder { } [[nodiscard]] - il2cpp_utils::Result installOrError() noexcept; + bs_hook::Result installOrError() noexcept; [[nodiscard]] FlamingoHandle install(); @@ -90,8 +90,8 @@ struct FlamingoHandle { /// @brief Uninstalls the hook associated with this handle. [[nodiscard]] - il2cpp_utils::Result uninstall() { - using Result = il2cpp_utils::Result; + bs_hook::Result uninstall() { + using Result = bs_hook::Result; auto result = flamingo::Uninstall(handle); if (result.has_value()) { return Result::Ok(FlamingoHandleBuilder(logger, info)); @@ -106,8 +106,8 @@ struct FlamingoHandle { } }; -inline il2cpp_utils::Result FlamingoHandleBuilder::installOrError() noexcept { - using Result = il2cpp_utils::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)); diff --git a/shared/utils/result.hpp b/shared/utils/result.hpp index dcbf7469..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 { @@ -71,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); } @@ -210,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 From 678bac593023814ddbc2b9639118bcf939b84977 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Fri, 2 Jan 2026 21:12:43 -0400 Subject: [PATCH 7/9] Namespaced hooks by default --- shared/utils/hooking.hpp | 25 ++++++++++++++----------- src/tests/hook-tests.cpp | 3 ++- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/shared/utils/hooking.hpp b/shared/utils/hooking.hpp index 68fadab8..16355bfa 100644 --- a/shared/utils/hooking.hpp +++ b/shared/utils/hooking.hpp @@ -12,9 +12,6 @@ #include "./flamingo-utils.hpp" -#ifndef BS_HOOKS_HOOK_NAMESPACE -#define BS_HOOKS_HOOK_NAMESPACE MOD_ID -#endif namespace Hooking { // For use in MAKE_HOOK_AUTO bodies. @@ -928,23 +925,23 @@ void InstallHookDirect(L& logger, void* dst) { /// Makes a flamingo hook builder for the provided hook type, finding the method via address. template -bs_hook::FlamingoHandleBuilder MakeFlamingoHook(Paper::LoggerContext logger, void* addr) { - flamingo::HookInfo hookInfo((void*)T::hook(), addr, (void**)T::trampoline(), flamingo::HookNameMetadata{ .name = T::name(), .namespaze = BS_HOOKS_HOOK_NAMESPACE }); +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) { +bs_hook::FlamingoHandleBuilder MakeFlamingoHook(L& logger, std::string_view namespaze) { auto addr = reinterpret_cast(getRealOffset(T::addr())); - return MakeFlamingoHook(logger, 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) { +bs_hook::FlamingoHandleBuilder MakeFlamingoHook(L& logger, std::string_view namespaze) { auto info = T::getInfo(); if (!info) { #ifndef SUPPRESS_MACRO_LOGS @@ -953,11 +950,17 @@ bs_hook::FlamingoHandleBuilder MakeFlamingoHook(L& logger) { SAFE_ABORT(); } auto addr = (void*)info->methodPointer; - return MakeFlamingoHook(logger, addr); + 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, name) ::Hooking::MakeFlamingoHook(logger) -#define FLAMINGO_HOOK_DIRECT(logger, name, addr) ::Hooking::MakeFlamingoHook(logger, addr) +#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. diff --git a/src/tests/hook-tests.cpp b/src/tests/hook-tests.cpp index 10c3b0dc..aa8a2b17 100644 --- a/src/tests/hook-tests.cpp +++ b/src/tests/hook-tests.cpp @@ -42,8 +42,9 @@ void install_a_hook() { INSTALL_HOOK(il2cpp_utils::Logger, test2_hook); // new Hook API! + modloader::ModInfo bs_hooks = { MOD_ID, "1.0.0", 0 }; modloader::ModInfo chroma = { "chroma", "1.0.0", 0 }; - auto hook = FLAMINGO_HOOK(il2cpp_utils::Logger, test2_hook).after(chroma).before("test-mod").install(); + auto hook = FLAMINGO_HOOK(il2cpp_utils::Logger, bs_hooks, test2_hook).after(chroma).before("test-mod").install(); if (false) { // uninstall the hook, which will also remove it from flamingo From 709108e15e9f8cad02e9419fa471a0cc9ab626f7 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Fri, 2 Jan 2026 21:28:31 -0400 Subject: [PATCH 8/9] Add example using string namespace --- src/tests/hook-tests.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tests/hook-tests.cpp b/src/tests/hook-tests.cpp index aa8a2b17..bb91a994 100644 --- a/src/tests/hook-tests.cpp +++ b/src/tests/hook-tests.cpp @@ -42,11 +42,13 @@ void install_a_hook() { INSTALL_HOOK(il2cpp_utils::Logger, test2_hook); // new Hook API! - modloader::ModInfo bs_hooks = { MOD_ID, "1.0.0", 0 }; + 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, test2_hook).after(chroma).before("test-mod").install(); + 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(); - if (false) { + 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(); From 6632ae357cf4448c16a41e05f072110e974b7d33 Mon Sep 17 00:00:00 2001 From: Fernthedev <15272073+Fernthedev@users.noreply.github.com> Date: Wed, 8 Apr 2026 18:14:35 -0400 Subject: [PATCH 9/9] More hook toys --- src/tests/hook-tests.cpp | 87 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/tests/hook-tests.cpp b/src/tests/hook-tests.cpp index bb91a994..095a4b1c 100644 --- a/src/tests/hook-tests.cpp +++ b/src/tests/hook-tests.cpp @@ -1,4 +1,5 @@ // #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 @@ -59,5 +60,91 @@ void install_a_hook() { } } +#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