diff --git a/shared/macros.hpp b/shared/macros.hpp index fa043f0..da735d6 100644 --- a/shared/macros.hpp +++ b/shared/macros.hpp @@ -1,835 +1,148 @@ #pragma once -#include "types.hpp" -#include "register.hpp" -#include #include +#include +#include #include #include -#include "util.hpp" -#include "beatsaber-hook/shared/utils/utils.h" #include "beatsaber-hook/shared/utils/il2cpp-utils.hpp" +#include "beatsaber-hook/shared/utils/utils.h" +#include "logging.hpp" +#include "register.hpp" +#include "types.hpp" +#include "util.hpp" -// Explicit macros defined here are laid out all here, ctrl-f for a specific name to find its impls. - -#ifdef DECLARE_CLASS -#error "DECLARE_CLASS is already defined! Undefine it before including macros.hpp!" -#endif - -#ifdef DECLARE_CLASS_INTERFACES -#error "DECLARE_CLASS_INTERFACES is already defined! Undefine it before including macros.hpp!" -#endif - -#ifdef DECLARE_CLASS_CODEGEN -#error "DECLARE_CLASS_CODEGEN is already defined! Undefine it before including macros.hpp!" -#endif - -#ifdef DECLARE_CLASS_CODEGEN_INTERFACES -#error "DECLARE_CLASS_CODEGEN_INTERFACES is already defined! Undefine it before including macros.hpp!" -#endif - -#ifdef DECLARE_CLASS_CUSTOM -#error "DECLARE_CLASS_CUSTOM is already defined! Undefine it before including macros.hpp!" -#endif - -#ifdef DECLARE_CLASS_CUSTOM_INTERFACES -#error "DECLARE_CLASS_CUSTOM_INTERFACES is already defined! Undefine it before including macros.hpp!" -#endif - -#ifdef DECLARE_VALUE -#error "DECLARE_VALUE is already defined! Undefine it before including macros.hpp!" -#endif - -#ifdef DECLARE_VALUE_CODEGEN -#error "DECLARE_VALUE_CODEGEN is already defined! Undefine it before including macros.hpp!" -#endif - -#ifdef ___DECLARE_TYPE_WRAPPER -#error "___DECLARE_TYPE_WRAPPER is already defined! Undefine it before including macros.hpp!" -#endif - -#ifdef ___DECLARE_TYPE_WRAPPER_INHERITANCE -#error "___DECLARE_TYPE_WRAPPER_INHERITANCE is already defined! Undefine it before including macros.hpp!" -#endif - -#ifndef INTERFACE_LIST -#define INTERFACE_LIST(...) ::custom_types::ExtractClasses<__VA_ARGS__>() -#endif - -#ifndef CUSTOM_TYPES_EXPORT_VISIBILITY -#define CUSTOM_TYPES_EXPORT_VISIBILITY __attribute__((visibility("default"))) -#endif - -// Helper macro for declaring classes with and without interfaces -#define ___DECLARE_TYPE_WRAPPER(namespaze_, name_, typeEnum_, baseNamespaze, baseName, baseSize, dllName_, interfaces_, flags_, ...) \ -namespace namespaze_ { \ - class name_; \ -} \ -MARK_REF_PTR_T(namespaze_::name_);\ -namespace namespaze_ { \ - class CUSTOM_TYPES_EXPORT_VISIBILITY name_ { \ - using ___TargetType = name_; \ - constexpr static auto ___Base__Size = baseSize; \ - friend ::custom_types::Register; \ - public: \ - constexpr static bool __IL2CPP_IS_VALUE_TYPE = typeEnum_ != Il2CppTypeEnum::IL2CPP_TYPE_CLASS;\ - static const int __IL2CPP_REFERENCE_TYPE_SIZE;\ - struct ___TypeRegistration : ::custom_types::TypeRegistration { \ - ___TypeRegistration() { \ - ::custom_types::Register::AddType(this); \ - instance = this; \ - } \ - static inline std::vector<::custom_types::FieldRegistrator*> fields; \ - std::vector<::custom_types::FieldRegistrator*> const getFields() const override { \ - return fields; \ - } \ - static void addField(::custom_types::FieldRegistrator* inst) { \ - fields.push_back(inst); \ - ::custom_types::_logger().debug("Adding instance field: %s.%s new size: %zu", #name_, inst->name(), fields.size()); \ - } \ - static inline std::vector<::custom_types::StaticFieldRegistrator*> staticFields; \ - std::vector<::custom_types::StaticFieldRegistrator*> const getStaticFields() const override { \ - return staticFields; \ - } \ - static void addStaticFieldInstance(::custom_types::StaticFieldRegistrator* inst) { \ - staticFields.push_back(inst); \ - ::custom_types::_logger().debug("Adding static field: %s.%s new size: %zu", #name_, inst->name(), staticFields.size()); \ - } \ - static inline std::vector<::custom_types::MethodRegistrator*> methods; \ - std::vector<::custom_types::MethodRegistrator*> const getMethods() const override { \ - return methods; \ - } \ - static void addMethod(::custom_types::MethodRegistrator* inst) { \ - methods.push_back(inst); \ - ::custom_types::_logger().debug("Adding method: %s.%s new size: %zu", #name_, inst->name(), methods.size()); \ - } \ - static inline size_t staticFieldOffset; \ - static size_t addStaticField(size_t sz) { \ - auto tmp = staticFieldOffset; \ - staticFieldOffset += sz; \ - return tmp; \ - } \ - static char* st_fields; \ - char*& static_fields() override { \ - return st_fields; \ - } \ - size_t static_fields_size() const override { \ - return staticFieldOffset; \ - } \ - constexpr const char* name() const override { \ - return #name_; \ - } \ - constexpr const char* namespaze() const override { \ - return #namespaze_; \ - } \ - constexpr const char* dllName() const override { \ - return dllName_; \ - } \ - Il2CppClass* baseType() const override { \ - return ::il2cpp_utils::GetClassFromName(baseNamespaze, baseName); \ - } \ - std::vector const interfaces() const override { \ - return interfaces_; \ - } \ - constexpr Il2CppTypeEnum typeEnum() const override { \ - return typeEnum_; \ - } \ - constexpr uint32_t typeFlags() const override { \ - return flags_; \ - } \ - static Il2CppClass* klass_ptr; \ - Il2CppClass*& klass() const override { \ - return klass_ptr; \ - } \ - size_t size() const override { \ - return sizeof(___TargetType); \ - } \ - TypeRegistration* customBase() const override { \ - return nullptr; \ - } \ - bool initialized() const override { \ - return init; \ - } \ - void setInitialized() const override { \ - init = true; \ - } \ - static bool init; \ - static TypeRegistration* instance; \ - static TypeRegistration* get() { \ - return instance; \ - } \ - }; \ - uint8_t _baseFields[baseSize]; \ - protected: \ - name_() {}; \ - public: \ - name_(name_&&) = delete;\ - name_(name_ const&) = delete;\ - __VA_ARGS__ \ - }; \ - inline constexpr const int name_::__IL2CPP_REFERENCE_TYPE_SIZE = sizeof(name_);\ -} \ -template<> \ -struct ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class<::namespaze_::name_*> { \ - static inline Il2CppClass* get() { \ - return ::namespaze_::name_::___TypeRegistration::klass_ptr; \ - } \ -}; \ -template<> \ -struct ::il2cpp_utils::il2cpp_type_check::need_box<::namespaze_::name_> { \ - constexpr static bool value = false; \ -}; - -// Helper for declaring classes with and without interfaces with explicit inheritance -#define ___DECLARE_TYPE_WRAPPER_INHERITANCE(namespaze_, name_, typeEnum_, baseT, dllName_, interfaces_, flags_, baseCustom, ...) \ -namespace namespaze_ { \ - class name_; \ -} \ -MARK_REF_PTR_T(namespaze_::name_);\ -namespace namespaze_ { \ - class CUSTOM_TYPES_EXPORT_VISIBILITY name_ : public baseT { \ - using ___TargetType = name_; \ - constexpr static auto ___Base__Size = sizeof(baseT); \ - friend ::custom_types::Register; \ - public: \ - constexpr static bool __IL2CPP_IS_VALUE_TYPE = typeEnum_ != Il2CppTypeEnum::IL2CPP_TYPE_CLASS;\ - static const int __IL2CPP_REFERENCE_TYPE_SIZE;\ - struct ___TypeRegistration : ::custom_types::TypeRegistration { \ - ___TypeRegistration() { \ - ::custom_types::Register::AddType(this); \ - instance = this; \ - } \ - static inline std::vector<::custom_types::FieldRegistrator*> fields; \ - std::vector<::custom_types::FieldRegistrator*> const getFields() const override { \ - return fields; \ - } \ - static void addField(::custom_types::FieldRegistrator* inst) { \ - fields.push_back(inst); \ - ::custom_types::_logger().debug("Adding instance field: %s.%s new size: %lu", #name_, inst->name(), fields.size()); \ - } \ - static inline std::vector<::custom_types::StaticFieldRegistrator*> staticFields; \ - std::vector<::custom_types::StaticFieldRegistrator*> const getStaticFields() const override { \ - return staticFields; \ - } \ - static void addStaticFieldInstance(::custom_types::StaticFieldRegistrator* inst) { \ - staticFields.push_back(inst); \ - ::custom_types::_logger().debug("Adding static field: %s.%s new size: %lu", #name_, inst->name(), staticFields.size()); \ - } \ - static inline std::vector<::custom_types::MethodRegistrator*> methods; \ - std::vector<::custom_types::MethodRegistrator*> const getMethods() const override { \ - return methods; \ - } \ - static void addMethod(::custom_types::MethodRegistrator* inst) { \ - methods.push_back(inst); \ - ::custom_types::_logger().debug("Adding method: %s.%s new size: %lu", #name_, inst->name(), methods.size()); \ - } \ - static inline size_t staticFieldOffset; \ - static size_t addStaticField(size_t sz) { \ - auto tmp = staticFieldOffset; \ - staticFieldOffset += sz; \ - return tmp; \ - } \ - static char* st_fields; \ - char*& static_fields() override { \ - return st_fields; \ - } \ - size_t static_fields_size() const override { \ - return staticFieldOffset; \ - } \ - constexpr const char* name() const override { \ - return #name_; \ - } \ - constexpr const char* namespaze() const override { \ - return #namespaze_; \ - } \ - constexpr const char* dllName() const override { \ - return dllName_; \ - } \ - Il2CppClass* baseType() const override { \ - auto klass = ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get();\ - if (!klass->initialized) il2cpp_functions::Class_Init(klass);\ - return klass;\ - } \ - std::vector const interfaces() const override { \ - return interfaces_; \ - } \ - constexpr Il2CppTypeEnum typeEnum() const override { \ - return typeEnum_; \ - } \ - constexpr uint32_t typeFlags() const override { \ - return flags_; \ - } \ - static Il2CppClass* klass_ptr; \ - Il2CppClass*& klass() const override { \ - return klass_ptr; \ - } \ - size_t size() const override { \ - return sizeof(___TargetType); \ - } \ - TypeRegistration* customBase() const override { \ - return baseCustom; \ - } \ - bool initialized() const override { \ - return init; \ - } \ - void setInitialized() const override { \ - init = true; \ - } \ - static bool init; \ - static TypeRegistration* instance; \ - static TypeRegistration* get() { \ - return instance; \ - } \ - }; \ - protected: \ - name_() {}; \ - public: \ - name_(name_&&) = delete;\ - name_(name_ const&) = delete;\ - __VA_ARGS__ \ - }; \ - inline constexpr const int name_::__IL2CPP_REFERENCE_TYPE_SIZE = sizeof(name_);\ -} \ -template<> \ -struct ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class<::namespaze_::name_*> { \ - static inline Il2CppClass* get() { \ - return ::namespaze_::name_::___TypeRegistration::klass_ptr; \ - } \ -}; \ -template<> \ -struct ::il2cpp_utils::il2cpp_type_check::need_box<::namespaze_::name_> { \ - constexpr static bool value = false; \ -}; - -// Declares a class with the given namespace, name, base namespace, base name, and baseSize. -// Assumes the class being declared is non-abstract. -// impl specifies the implementation of the class, the actual definition of the type. -// It is recommended this holds DECLARE statements, as defined in macros.hpp -#define DECLARE_CLASS(namespaze, name, baseNamespaze, baseName, baseSize, ...) \ -___DECLARE_TYPE_WRAPPER(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, baseNamespaze, baseName, baseSize, #namespaze, {}, 0, __VA_ARGS__) - -// Declares a class with the given namespace, name, base namespace, base name, baseSize, and dll name. -// Assumes the class being declared is non-abstract. -// impl specifies the implementation of the class, the actual definition of the type. -// It is recommended this holds DECLARE statements, as defined in macros.hpp -#define DECLARE_CLASS_DLL(namespaze, name, baseNamespaze, baseName, baseSize, dllName_, ...) \ -___DECLARE_TYPE_WRAPPER(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, baseNamespaze, baseName, baseSize, dllName_, {}, 0, __VA_ARGS__) - -// Declares a class with the given namespace, name, base namespace, base name, baseSize, and interface list. -// Assumes the class being declared is non-abstract. -// impl specifies the implementation of the class, the actual definition of the type. -// It is recommended this holds DECLARE statements, as defined in macros.hpp -#define DECLARE_CLASS_INTERFACES(namespaze, name, baseNamespaze, baseName, baseSize, interfaces, ...) \ -___DECLARE_TYPE_WRAPPER(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, baseNamespaze, baseName, baseSize, #namespaze, {interfaces}, 0, __VA_ARGS__) - -// Declares a class with the given namespace, name, base namespace, base name, baseSize, dll name, and interface list. -// Assumes the class being declared is non-abstract. -// impl specifies the implementation of the class, the actual definition of the type. -// It is recommended this holds DECLARE statements, as defined in macros.hpp -#define DECLARE_CLASS_INTERFACES_DLL(namespaze, name, baseNamespaze, baseName, baseSize, dllName_, interfaces, ...) \ -___DECLARE_TYPE_WRAPPER(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, baseNamespaze, baseName, baseSize, dllName_, {interfaces}, 0, __VA_ARGS__) - -// Declares a class with the given namespace, name, and base type. -// Assumes the class being declared is non-abstract. -// impl specifies the implementation of the class, the actual definition of the type. -// It is recommended this hold other DECLARE statements, as defined in macros.hpp -#define DECLARE_CLASS_CODEGEN(namespaze, name, baseT, ...) \ -___DECLARE_TYPE_WRAPPER_INHERITANCE(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, baseT, #namespaze, {}, 0, nullptr, __VA_ARGS__) - -// Declares a class with the given namespace, name, base type, and dllName. -// Assumes the class being declared is non-abstract. -// impl specifies the implementation of the class, the actual definition of the type. -// It is recommended this hold other DECLARE statements, as defined in macros.hpp -#define DECLARE_CLASS_CODEGEN_DLL(namespaze, name, baseT, dllName_, ...) \ -___DECLARE_TYPE_WRAPPER_INHERITANCE(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, baseT, dllName_, {}, 0, nullptr, __VA_ARGS__) - -// Declares a class with the given namespace, name, base type, and interface types. -// Assumes the class being declared is non-abstract. -// impl specifies the implementation of the class, the actual definition of the type. -// It is recommended this hold other DECLARE statements, as defined in macros.hpp -// TODO: Note that this type does NOT properly inherit its interfaces or provide conversion operators. -// Casts WILL be necessary. -#define DECLARE_CLASS_CODEGEN_INTERFACES(namespaze, name, baseT, interfaceTs, ...) \ -___DECLARE_TYPE_WRAPPER_INHERITANCE(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, baseT, #namespaze, {interfaceTs}, 0, nullptr, __VA_ARGS__) - -// Declares a class with the given namespace, name, base type, dll name, and interface types. -// Assumes the class being declared is non-abstract. -// impl specifies the implementation of the class, the actual definition of the type. -// It is recommended this hold other DECLARE statements, as defined in macros.hpp -// TODO: Note that this type does NOT properly inherit its interfaces or provide conversion operators. -// Casts WILL be necessary. -#define DECLARE_CLASS_CODEGEN_INTERFACES_DLL(namespaze, name, baseT, interfaceTs, dllName_, ...) \ -___DECLARE_TYPE_WRAPPER_INHERITANCE(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, baseT, dllName_, interfaceTs, 0, nullptr, __VA_ARGS__) - -// Declares a class with the given namespace, name, and base type (WHICH MUST BE A DECLARE TYPE OF ITS OWN!) -// Assumes the class being declared is non-abstract. -// impl specifies the implementation of the class, the actual definition of the type. -// It is recommended this hold other DECLARE statements, as defined in macros.hpp -#define DECLARE_CLASS_CUSTOM(namespaze, name, baseCustomT, ...) \ -___DECLARE_TYPE_WRAPPER_INHERITANCE(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, baseCustomT, #namespaze, {}, 0, baseCustomT::___TypeRegistration::get(), __VA_ARGS__) - -// Declares a class with the given namespace, name, base type (WHICH MUST BE A DECLARE TYPE OF ITS OWN!), and dll name. -// Assumes the class being declared is non-abstract. -// impl specifies the implementation of the class, the actual definition of the type. -// It is recommended this hold other DECLARE statements, as defined in macros.hpp -#define DECLARE_CLASS_CUSTOM_DLL(namespaze, name, baseCustomT, dllName_, ...) \ -___DECLARE_TYPE_WRAPPER_INHERITANCE(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, baseCustomT, dllName_, {}, 0, baseCustomT::___TypeRegistration::get(), __VA_ARGS__) - -// Declares a class with the given namespace, name, and base type (WHICH MUST BE A DECLARE TYPE OF ITS OWN!), and interface list. -// Assumes the class being declared is non-abstract. -// impl specifies the implementation of the class, the actual definition of the type. -// It is recommended this hold other DECLARE statements, as defined in macros.hpp -#define DECLARE_CLASS_CUSTOM_INTERFACES(namespaze, name, baseCustomT, interfaceTs, ...) \ -___DECLARE_TYPE_WRAPPER_INHERITANCE(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, baseCustomT, #namespaze, interfaceTs, 0, baseCustomT::___TypeRegistration::get(), __VA_ARGS__) - -// Declares a class with the given namespace, name, and base type (WHICH MUST BE A DECLARE TYPE OF ITS OWN!), dll name, and interface list. -// Assumes the class being declared is non-abstract. -// impl specifies the implementation of the class, the actual definition of the type. -// It is recommended this hold other DECLARE statements, as defined in macros.hpp -#define DECLARE_CLASS_CUSTOM_INTERFACES_DLL(namespaze, name, baseCustomT, dllName_, interfaceTs, ...) \ -___DECLARE_TYPE_WRAPPER_INHERITANCE(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, baseCustomT, dllName_, interfaceTs, 0, baseCustomT::___TypeRegistration::get(), __VA_ARGS__) - -// Declares a value type with the given namespace, name, base namespace, base name, and baseSize. -// Assumes the struct being declared is non-generic/abstract. -// impl specifies the implementation of the class, which is the actual definition of the type. -// It is recommended that this holds DECLARE statements as defined in macros.hpp, but fields can ultimately be of any type. -#define DECLARE_VALUE(namespaze, name, baseNamespaze, baseName, baseSize, ...) \ -___DECLARE_TYPE_WRAPPER(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_VALUETYPE, baseNamespaze, baseName, baseSize, #namespaze, {}, 0, __VA_ARGS__) - -// Declares a value type with the given namespace, name, and base type. -// Assumes the struct being declared is non-abstract. -// impl specifies the implementation of the struct, the actual definition of the type. -// It is recommended that this holds DECLARE statements as defined in macros.hpp, but fields can ultimately be of any type. -#define DECLARE_VALUE_CODEGEN(namespaze, name, baseT, ...) \ -___DECLARE_TYPE_WRAPPER_INHERITANCE(namespaze, name, Il2CppTypeEnum::IL2CPP_TYPE_VALUETYPE, baseT, #namespaze, {}, 0, nullptr, __VA_ARGS__) - -#ifdef DEFINE_TYPE -#error "DEFINE_TYPE is already defined! Undefine it before including macros.hpp!" -#endif - -#define DEFINE_TYPE(namespaze, name) \ -::custom_types::TypeRegistration* namespaze::name::___TypeRegistration::instance; \ -static namespaze::name::___TypeRegistration __registration_instance_##name; \ -char* namespaze::name::___TypeRegistration::st_fields; \ -Il2CppClass* namespaze::name::___TypeRegistration::klass_ptr; \ -bool namespaze::name::___TypeRegistration::init = false; - -// TODO: Add a way of declaring abstract/interface types. -// This requires messing with method slots even more than I do right now. - -// if we compile with declspec, use that to wrap field accesses automatically, -// this then requires the backing fields to have a different name, this achieves that -#ifdef BACKING_FIELD_NAME -#error "BACKING_FIELD_NAME is already defined! Undefine it before including macros.hpp!" -#endif - -#if __has_declspec_attribute(property) && !defined(CT_NO_DECLSPEC_PROPS) -#define BACKING_FIELD_NAME(name_) ___backing_field_##name_ -#else -#define BACKING_FIELD_NAME(name_) name_ -#endif - -#ifdef BACKING_FIELD_OFFSET_OF -#error "BACKING_FIELD_OFFSET_OF is already defined! Undefine it before including macros.hpp!" -#endif - -#if __has_declspec_attribute(property) && !defined(CT_NO_DECLSPEC_PROPS) -#define BACKING_FIELD_OFFSET_OF(name_) offsetof(___TargetType, ___backing_field_##name_) -#else -#define BACKING_FIELD_OFFSET_OF(name_) offsetof(___TargetType, name_) -#endif - -#ifdef DEFINE_INSTANCE_FIELD_REGISTRATOR -#error "DEFINE_INSTANCE_FIELD_REGISTRATOR is already defined! Undefine it before including macros.hpp!" -#endif - -#define DEFINE_INSTANCE_FIELD_REGISTRATOR(type_, name_, flags_)\ -private: \ -struct ___FieldRegistrator_##name_ : ::custom_types::FieldRegistrator { \ - ___FieldRegistrator_##name_() { \ - ___TargetType::___TypeRegistration::addField(this); \ - } \ - constexpr const char* name() const override { \ - return #name_; \ - } \ - const Il2CppType* type() const override { \ - ::il2cpp_functions::Init(); \ - auto klass = ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get();\ - if (!klass->initialized) il2cpp_functions::Class_Init(klass);\ - return ::il2cpp_functions::class_get_type(klass); \ - } \ - constexpr uint16_t fieldAttributes() const override { \ - return flags_; \ - } \ - constexpr size_t size() const override { \ - return sizeof(type_); \ - } \ - int32_t offset() const override { \ - return BACKING_FIELD_OFFSET_OF(name_); \ - } \ -}; \ -static inline ___FieldRegistrator_##name_ ___##name_##_FieldRegistrator - -#ifdef DEFINE_INSTANCE_FIELD_ACCESSORS -#error "DEFINE_INSTANCE_FIELD_ACCESSORS is already defined! Undefine it before including macros.hpp!" -#endif - -// if we have exceptions the getters aren't noexcept -#if __has_feature(cxx_exceptions) -#define CT_FIELD_GET_EXCEPT -#else -#define CT_FIELD_GET_EXCEPT noexcept -#endif - -#define DEFINE_INSTANCE_FIELD_ACCESSORS(type_, name_, visibility_) \ -protected: \ -static inline custom_types::field_accessor ___##name_##_FieldAccessor; \ -visibility_: \ -inline type_& __get_##name_() CT_FIELD_GET_EXCEPT { \ - CT_FIELD_ACCESS_CHECK(this); \ - return ___##name_##_FieldAccessor.read(this, ___##name_##_FieldRegistrator.offset()); \ -} \ -inline type_ const& __get_##name_() const CT_FIELD_GET_EXCEPT { \ - CT_FIELD_ACCESS_CHECK(this); \ - return ___##name_##_FieldAccessor.read(this, ___##name_##_FieldRegistrator.offset()); \ -} \ -inline void __set_##name_(type_ v) { ___##name_##_FieldAccessor.write(this, ___##name_##_FieldRegistrator.offset(), std::forward(v)); } - -// if we have no property support, we should not emit code that tries to be one. instead just emit nothing. -// earlier we defined a special way to have backing fields exist, so you will always be able to access the fields with this->fieldname -#ifdef DECLARE_INSTANCE_CPP_PROPERTY -#error "DECLARE_INSTANCE_CPP_PROPERTY is already defined! Undefine it before including macros.hpp!" -#endif - -#if __has_declspec_attribute(property) && !defined(CT_NO_DECLSPEC_PROPS) -#define DECLARE_INSTANCE_CPP_PROPERTY(type_, name_) __declspec(property(get=__get_##name_,put=__set_##name_)) type_ name_ -#else -#define DECLARE_INSTANCE_CPP_PROPERTY(type_, name_) -#endif - -#ifdef DECLARE_INSTANCE_FIELD_DEFAULT -#error "DECLARE_INSTANCE_FIELD_DEFAULT is already defined! Undefine it before including macros.hpp!" -#endif -// Declares a field with type, name, value. -// Fields declared like this must also be registered via REGISTER_FIELD within the REGISTER_TYPE function. -// Fields like this are ONLY initialized when the C++ constructor is called. See the INVOKE_CTOR macro for more info. -#define DECLARE_INSTANCE_FIELD_DEFAULT(type_, name_, value) \ -DEFINE_INSTANCE_FIELD_REGISTRATOR(type_, name_, FIELD_ATTRIBUTE_PUBLIC);\ -DEFINE_INSTANCE_FIELD_ACCESSORS(type_, name_, public);\ -public: \ -DECLARE_INSTANCE_CPP_PROPERTY(type_, name_);\ -type_ BACKING_FIELD_NAME(name_) = value - -#ifdef DECLARE_INSTANCE_FIELD_PRIVATE_DEFAULT -#error "DECLARE_INSTANCE_FIELD_PRIVATE_DEFAULT is already defined! Undefine it before including macros.hpp!" -#endif -// Declares a field with type, name, value. -// Fields declared like this must also be registered via REGISTER_FIELD within the REGISTER_TYPE function. -// Fields like this are ONLY initialized when the C++ constructor is called. See the INVOKE_CTOR macro for more info. -#define DECLARE_INSTANCE_FIELD_PRIVATE_DEFAULT(type_, name_, value) \ -DEFINE_INSTANCE_FIELD_REGISTRATOR(type_, name_, FIELD_ATTRIBUTE_PRIVATE);\ -DEFINE_INSTANCE_FIELD_ACCESSORS(type_, name_, private);\ -private: \ -DECLARE_INSTANCE_CPP_PROPERTY(type_, name_);\ -type_ BACKING_FIELD_NAME(name_) = value - -#ifdef DECLARE_INSTANCE_FIELD -#error "DECLARE_INSTANCE_FIELD is already defined! Undefine it before including macros.hpp!" -#endif -// Declare a field with type, name. -#define DECLARE_INSTANCE_FIELD(type_, name_) \ -DEFINE_INSTANCE_FIELD_REGISTRATOR(type_, name_, FIELD_ATTRIBUTE_PUBLIC);\ -DEFINE_INSTANCE_FIELD_ACCESSORS(type_, name_, public);\ -public: \ -DECLARE_INSTANCE_CPP_PROPERTY(type_, name_);\ -type_ BACKING_FIELD_NAME(name_) - -#ifdef DECLARE_INSTANCE_FIELD_PRIVATE -#error "DECLARE_INSTANCE_FIELD_PRIVATE is already defined! Undefine it before including macros.hpp!" -#endif -// Declare a field with type, name. -#define DECLARE_INSTANCE_FIELD_PRIVATE(type_, name_) \ -DEFINE_INSTANCE_FIELD_REGISTRATOR(type_, name_, FIELD_ATTRIBUTE_PRIVATE);\ -DEFINE_INSTANCE_FIELD_ACCESSORS(type_, name_, private);\ -private: \ -DECLARE_INSTANCE_CPP_PROPERTY(type_, name_);\ -type_ BACKING_FIELD_NAME(name_) - -#ifdef DECLARE_STATIC_FIELD -#error "DECLARE_STATIC_FIELD is already defined! Undefine it before including macros.hpp!" -#endif -// Declare a static field with type, name. -// Static fields declared like this are GC aware, but are uninitialized on class creation. -// Consider creating a pseudo static initializer in which you initialize these fields to whatever you desire. -#define DECLARE_STATIC_FIELD(type_, name_) \ -private: \ -struct ___StaticFieldRegistrator_##name_ : ::custom_types::StaticFieldRegistrator { \ - size_t oft; \ - ___StaticFieldRegistrator_##name_() { \ - oft = ___TargetType::___TypeRegistration::addStaticField(size()); \ - ___TargetType::___TypeRegistration::addStaticFieldInstance(this); \ - } \ - constexpr const char* name() const override { \ - return #name_; \ - } \ - const Il2CppType* type() const override { \ - ::il2cpp_functions::Init(); \ - return ::il2cpp_functions::class_get_type(::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get()); \ - } \ - constexpr uint16_t fieldAttributes() const override { \ - return FIELD_ATTRIBUTE_PUBLIC | FIELD_ATTRIBUTE_STATIC; \ - } \ - constexpr size_t size() const override { \ - return sizeof(type_); \ - } \ - int32_t offset() const override { \ - return oft; \ - } \ -}; \ -static inline ___StaticFieldRegistrator_##name_ ___##name_##_StaticFieldRegistrator; \ -public: \ -static type_& name_() { \ - CRASH_UNLESS(___TargetType::___TypeRegistration::st_fields); \ - return *reinterpret_cast(&___TargetType::___TypeRegistration::st_fields[___##name_##_StaticFieldRegistrator.offset()]); \ -} \ - -#ifdef ___CREATE_INSTANCE_METHOD -#error "___CREATE_INSTANCE_METHOD is already defined! Undefine it before including macros.hpp!" -#endif -// Helper macro for creating instance methods -#define ___CREATE_INSTANCE_METHOD(name_, csharpName_, flags_, virtualData) \ -private: \ -template \ -struct ___MethodRegistrator_##name_; \ -template \ -struct ___MethodRegistrator_##name_ : ::custom_types::MethodRegistrator { \ - ___MethodRegistrator_##name_() { \ - ___TargetType::___TypeRegistration::addMethod(this); \ - } \ - constexpr const char* name() const override { \ - return #name_; \ - } \ - constexpr const char* csharpName() const override { \ - return csharpName_; \ - } \ - int flags() const override { \ - return flags_; \ - } \ - const MethodInfo* virtualMethod() const override { \ - return virtualData; \ - } \ - const Il2CppType* returnType() const override { \ - il2cpp_functions::Init(); \ - return ::il2cpp_functions::class_get_type(::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get()); \ - } \ - std::vector params() const override { \ - int32_t counter = 0; \ - il2cpp_functions::Init(); \ - return {(::il2cpp_functions::class_get_type(::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get()))...}; \ - } \ - uint8_t params_size() const override { \ - return sizeof...(TArgs); \ - } \ - Il2CppMethodPointer methodPointer() const override { \ - return reinterpret_cast(&::custom_types::invoker_creator::wrap<&___TargetType::name_>); \ - } \ - InvokerMethod invoker() const override { \ - return &::custom_types::invoker_creator::invoke; \ - } \ -}; \ -static inline ___MethodRegistrator_##name_ ___##name_##_MethodRegistrator - -#ifdef DECLARE_INSTANCE_METHOD_SPECIAL -#error "DECLARE_INSTANCE_METHOD_SPECIAL is already defined! Undefine it before including macros.hpp!" -#endif -// Declare an instance method with a special name. -// See DECLARE_INSTANCE_METHOD for more information. -#define DECLARE_INSTANCE_METHOD_SPECIAL(ret, name, specialName, flags, ...) \ -public: \ -ret name(__VA_ARGS__); \ -___CREATE_INSTANCE_METHOD(name, specialName, flags | METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG, nullptr) - -#ifdef DECLARE_CTOR -#error "DECLARE_CTOR is already defined! Undefine it before including macros.hpp!" -#endif -// Declare a method with name that will be called on construction. -#define DECLARE_CTOR(name, ...) \ -public: \ -void name(__VA_ARGS__); \ -template<::il2cpp_utils::CreationType creationType = ::il2cpp_utils::CreationType::Temporary, class... TArgs> \ -static ___TargetType* New_ctor(TArgs&&... args) { \ - static_assert(::custom_types::Decomposer::convertible(), "Arguments provided to New_ctor must be convertible to the constructor!"); \ - ___TargetType* obj; \ - if constexpr (creationType == ::il2cpp_utils::CreationType::Temporary) { \ - obj = reinterpret_cast<___TargetType*>(::il2cpp_functions::object_new(___TypeRegistration::klass_ptr)); \ - } else { \ - obj = reinterpret_cast<___TargetType*>(::il2cpp_utils::createManual(___TypeRegistration::klass_ptr)); \ - } \ - obj->name(std::forward(args)...); \ - return obj; \ -} \ -___CREATE_INSTANCE_METHOD(name, ".ctor", METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_SPECIAL_NAME | METHOD_ATTRIBUTE_RT_SPECIAL_NAME, nullptr) - -#ifdef DECLARE_INSTANCE_METHOD -#error "DECLARE_INSTANCE_METHOD is already defined! Undefine it before including macros.hpp!" -#endif -// Declare an instance method with: return type, name, parameters... -#define DECLARE_INSTANCE_METHOD(ret, name, ...) \ -public: \ -ret name(__VA_ARGS__); \ -___CREATE_INSTANCE_METHOD(name, #name, METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG, nullptr) - -#ifdef DECLARE_OVERRIDE_METHOD -#error "DECLARE_OVERRIDE_METHOD is already defined! Undefine it before including macros.hpp!" -#endif -// Declare an interface method with: return type, name, method it is implementing, parameters... -// This macro matches the DECLARE_INSTANCE_METHOD macro except it overrides the provided overriding MethodInfo*. -#define DECLARE_OVERRIDE_METHOD(ret, name, overridingMethodInfo, ...) \ -public: \ -ret name(__VA_ARGS__); \ -___CREATE_INSTANCE_METHOD(name, #name, (overridingMethodInfo->flags & ~METHOD_ATTRIBUTE_ABSTRACT) | METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG, overridingMethodInfo) - -#ifdef DECLARE_OVERRIDE_METHOD_MATCH -#error "DECLARE_OVERRIDE_METHOD_MATCH is already defined! Undefine it before including macros.hpp!" -#endif -// Declare an overriding method with: return type, name, method it is implementing, parameters... -// This macro matches the DECLARE_OVERRIDE_METHOD macro except it matches the method you pass in with the il2cpp type check. -#define DECLARE_OVERRIDE_METHOD_MATCH(ret, name, overridingMethod, ...) \ -DECLARE_OVERRIDE_METHOD(ret, name, il2cpp_utils::il2cpp_type_check::MetadataGetter::methodInfo() __VA_OPT__(, __VA_ARGS__)) - -#ifdef DECLARE_DTOR -#error "DECLARE_DTOR is already defined! Undefine it before including macros.hpp!" -#endif -// Declares a destructor with a given name for use when destructing non-trivial custom types. -// Should still be registered in the REGISTER_FUNCTION macro, just like any other method. -#define DECLARE_DTOR(name) \ -public: \ -void name(); \ -___CREATE_INSTANCE_METHOD(name, #name, METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG, ::il2cpp_utils::FindMethod("System", "Object", "Finalize")) - -#ifdef DECLARE_SIMPLE_DTOR -#error "DECLARE_SIMPLE_DTOR is already defined! Undefine it before including macros.hpp!" -#endif -// Declares a simple destructor that simply forwards to the C++ destructor. -// This should be used if you do not wish to create your own destructor and explicitly call your own destructor. -// This method is __Finalize, and should not conflict with any existing declarations. -#define DECLARE_SIMPLE_DTOR() \ -void __Finalize() { \ - this->~___TargetType(); \ -} \ -___CREATE_INSTANCE_METHOD(__Finalize, "__Finalize", (::il2cpp_utils::FindMethod("System", "Object", "Finalize")->flags & ~METHOD_ATTRIBUTE_ABSTRACT) | METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG, ::il2cpp_utils::FindMethod("System", "Object", "Finalize")) - -#ifdef INVOKE_CTOR -#error "INVOKE_CTOR is already defined! Undefine it before including macros.hpp!" -#endif -// Invokes the C++ constructor of the provided typeName and arguments. -// Small performance overhead due to placement new. -// This can be done in order to explicitly initialize non-trivially constructible types, such as vectors -// within a C# ctor (declared with DECLARE_CTOR). -// This should ONLY be called on types that inherit Il2CppObject. -// For value types, try placement new instead, or INVOKE_VALUE_CTOR. -#define INVOKE_CTOR(...) \ -do { \ -char ___buff[___Base__Size]; \ -memcpy(___buff, this, ___Base__Size); \ -new (this) ___TargetType(__VA_ARGS__); \ -memcpy(this, ___buff, ___Base__Size); \ -} while (0) - -#ifdef INVOKE_VALUE_CTOR -#error "INVOKE_VALUE_CTOR is already defined! Undefine it before including macros.hpp!" -#endif -// Invokes the C++ constructor of the provided typeName and arguments. -// Small performance overhead due to placement new. -// This can be done in order to explicitly initialize non-trivially constructible types, such as vectors -// within a C# ctor (declared with DECLARE_CTOR). -// Note that value type constructors are much less likely to be called and no existing code will provide them. -// This function simply calls the placement new operator, so should NOT be used for anything that inherits Il2CppObject. -#define INVOKE_VALUE_CTOR(...) new (this) ___TargetType(__VA_ARGS__) - -#ifdef DECLARE_DEFAULT_CTOR -#error "DECLARE_DEFAULT_CTOR is already defined! Undefine it before including macros.hpp!" -#endif -// Declares a default C# constructor that simply forwards to the C++ constructor. -// Note that this ALSO CALLS the 0 arg base constructor (which may or may not be necessary). -// This is ONLY valid if there is a default C++ constructor with 0 args -// AND a 0 arg base constructor. -// Otherwise, consider using DECLARE_CTOR, INVOKE_CTOR, and INVOKE_BASE_CTOR. -#define DECLARE_DEFAULT_CTOR() \ -void __ctor() { \ - INVOKE_CTOR(); \ - INVOKE_BASE_CTOR(___TargetType::___TypeRegistration::get()->baseType()); \ -} \ -template<::il2cpp_utils::CreationType creationType = ::il2cpp_utils::CreationType::Temporary, class... TArgs> \ -static ___TargetType* New_ctor(TArgs&&... args) { \ - static_assert(::custom_types::Decomposer::convertible(), "Arguments provided to New_ctor must be convertible to the constructor!"); \ - return THROW_UNLESS(il2cpp_utils::New<___TargetType*, creationType>(___TypeRegistration::klass_ptr, std::forward(args)...)); \ -} \ -___CREATE_INSTANCE_METHOD(__ctor, ".ctor", METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_SPECIAL_NAME | METHOD_ATTRIBUTE_RT_SPECIAL_NAME, nullptr) - -#ifdef ___CREATE_STATIC_METHOD -#error "___CREATE_STATIC_METHOD is already defined! Undefine it before including macros.hpp!" -#endif -// Helper macro for creating instance methods -#define ___CREATE_STATIC_METHOD(name_, csharpName_, flags_) \ -private: \ -template \ -struct ___MethodRegistrator_##name_; \ -template \ -struct ___MethodRegistrator_##name_ : ::custom_types::MethodRegistrator { \ - ___MethodRegistrator_##name_() { \ - ___TargetType::___TypeRegistration::addMethod(this); \ - } \ - constexpr const char* name() const override { \ - return #name_; \ - } \ - constexpr const char* csharpName() const override { \ - return csharpName_; \ - } \ - int flags() const override { \ - return flags_; \ - } \ - const MethodInfo* virtualMethod() const override { \ - return nullptr; \ - } \ - const Il2CppType* returnType() const override { \ - il2cpp_functions::Init(); \ - return ::il2cpp_functions::class_get_type(::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get()); \ - } \ - std::vector params() const override { \ - int32_t counter = 0; \ - il2cpp_functions::Init(); \ - return {(::il2cpp_functions::class_get_type(::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get()))...}; \ - } \ - uint8_t params_size() const override { \ - return sizeof...(TArgs); \ - } \ - Il2CppMethodPointer methodPointer() const override { \ - return reinterpret_cast(&___TargetType::name_); \ - } \ - InvokerMethod invoker() const override { \ - return &::custom_types::invoker_creator::invoke; \ - } \ -}; \ -static inline ___MethodRegistrator_##name_ ___##name_##_MethodRegistrator - -#ifdef DECLARE_STATIC_METHOD -#error "DECLARE_STATIC_METHOD is already defined! Undefine it before including macros.hpp!" -#endif -// Declare an instance method with: return type, name, parameters... -#define DECLARE_STATIC_METHOD(ret, name, ...) \ -public: \ -static ret name(__VA_ARGS__); \ -___CREATE_STATIC_METHOD(name, #name, METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_STATIC) - +// name +#define CUSTOM_TYPE_NAME(m_namespaze, m_name) \ + namespace m_namespaze { \ + struct m_name; \ + } \ + template <> \ + struct custom_types::CustomTypeInfoTrait final { \ + static constexpr std::string_view namespaze() { \ + return #m_namespaze; \ + } \ + static constexpr std::string_view name() { \ + return #m_name; \ + } \ + static constexpr std::span fields() { \ + return fieldsVec; \ + } \ + static constexpr std::span staticFields() { \ + return staticFieldsVec; \ + } \ + static constexpr std::span methods() { \ + return methodsVec; \ + } \ + \ + private: \ + inline static std::vector fieldsVec; \ + inline static std::vector staticFieldsVec; \ + inline static std::vector methodsVec; \ + friend struct m_namespaze::m_name; \ + void addField(custom_types::FieldDesc&& f) { \ + fieldsVec.emplace_back(std::move(f)); \ + } \ + void addStaticField(custom_types::StaticFieldDesc&& f) { \ + staticFieldsVec.emplace_back(std::move(f)); \ + } \ + void addMethod(custom_types::MethodDesc&& f) { \ + methodsVec.emplace_back(std::move(f)); \ + } \ + }; + +// inherit +#define CUSTOM_TYPE_INHERIT(m_typ, m_ParentNamespace, m_ParentName) \ + template <> \ + struct custom_types::CustomTypeInheritTrait final { \ + static constexpr CustomTypeTypeName parent() { \ + return CustomTypeTypeName(m_ParentNamespace, m_ParentName); \ + } \ + }; + +// codegen inherit +#define CUSTOM_TYPE_INHERIT_CODEGEN(m_typ, m_Parent) \ + template <> \ + struct custom_types::CustomTypeInheritTrait final { \ + static constexpr CustomTypeTypeName parent() { \ + return typeName(); \ + } \ + }; + +// interfaces +#define CUSTOM_TYPE_INTERFACES(m_typ, ...) \ + template <> \ + struct custom_types::CustomTypeInterfacesTrait final { \ + static constexpr auto interfaces() { \ + return std::array({ __VA_ARGS__ }); \ + } \ + }; + +// codegen interfaces +#define CUSTOM_TYPE_INTERFACES_CODEGEN(m_typ, ...) \ + template <> \ + struct custom_types::CustomTypeInterfacesTrait final { \ + static constexpr auto interfaces() { \ + return typeNames<__VA_ARGS__>(); \ + } \ + }; + +// fields +#define REGISTER_FIELD(typ, name) \ + private: \ + /* I have no idea how this gets called properly. `*this` is probably undefined! */ \ + __attribute__((constructor)) void __setup_##name() { \ + CustomTypeTypeNameTrait>::fields.emplace_back(#name, offsetof(std::decay_t, name), \ + ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type>); \ + }; + +#define DECLARE_FIELD(acc, typ, name) \ + acc: \ + typ name; \ + REGISTER_FIELD(typ, name) \ + public: +// static fields +#define REGISTER_STATIC_FIELD(typ, name) \ + private: \ + /* I have no idea how this gets called properly. `*this` is probably undefined! */ \ + __attribute__((constructor)) void __setup_##name() { \ + CustomTypeTypeNameTrait>::staticFields.emplace_back(#name, name, ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type>); \ + }; + +#define DECLARE_STATIC_FIELD(acc, typ, name) \ + acc: \ + typ name; \ + REGISTER_STATIC_FIELD(typ, name) \ + public: +// methods +#define REGISTER_METHOD(typ, name) \ + private: \ + /* I have no idea how this gets called properly. `*this` is probably undefined! */ \ + __attribute__((constructor)) void __setup_##name() { \ + CustomTypeTypeNameTrait>::methods.emplace_back(#name, offsetof(std::decay_t, name), \ + ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type>, nullptr /* TODO:*/); \ + }; + +#define DECLARE_METHOD(acc, typ, name) \ + acc: \ + typ name; \ + REGISTER_METHOD(typ, name) \ + public: + +#define CUSTOM_TYPE_CODEGEN(namespaze, name, parent, ...) \ + CUSTOM_TYPE_NAME(namespaze, name); \ + CUSTOM_TYPE_INHERIT_CODEGEN(namespaze::name, parent); \ + CUSTOM_TYPE_INTERFACES_CODEGEN(namespaze::name, __VA_ARGS__); \ + MARK_REF_PTR_T(namespaze::name) \ + struct namespaze::name : public parent namespace custom_types { - template - void InvokeBaseCtor(Il2CppClass* klass, T* self, TArgs&&... args) { - static auto m = ::il2cpp_utils::FindMethod(klass, ".ctor", std::array{::il2cpp_utils::ExtractIndependentType()...}); - ::il2cpp_utils::RunMethodRethrow(self, m, args...); - } +template +void InvokeBaseCtor(Il2CppClass* klass, T* self, TArgs&&... args) { + static auto m = ::il2cpp_utils::FindMethod(klass, ".ctor", std::array{ ::il2cpp_utils::ExtractIndependentType()... }); + ::il2cpp_utils::RunMethodRethrow(self, m, args...); } +} // namespace custom_types #ifndef INVOKE_BASE_CTOR -#define INVOKE_BASE_CTOR(klass, ...) ::custom_types::InvokeBaseCtor(klass, this __VA_OPT__(,) __VA_ARGS__) +#define INVOKE_BASE_CTOR(klass, ...) ::custom_types::InvokeBaseCtor(klass, this __VA_OPT__(, ) __VA_ARGS__) #endif diff --git a/shared/register.hpp b/shared/register.hpp index 98619b8..dcfe364 100644 --- a/shared/register.hpp +++ b/shared/register.hpp @@ -22,8 +22,6 @@ struct std::hash> { namespace custom_types { /// @class Public API for registering types class Register { - friend TypeRegistration; - private: CUSTOM_TYPES_EXPORT static std::unordered_map assembs; CUSTOM_TYPES_EXPORT static std::unordered_map images; @@ -33,8 +31,8 @@ class Register { CUSTOM_TYPES_EXPORT static std::mutex classMappingMtx; CUSTOM_TYPES_EXPORT static bool installed; - CUSTOM_TYPES_EXPORT static std::vector toRegister; - CUSTOM_TYPES_EXPORT static std::vector registeredTypes; + CUSTOM_TYPES_EXPORT static std::vector toRegister; + CUSTOM_TYPES_EXPORT static std::vector registeredTypes; CUSTOM_TYPES_EXPORT static TypeDefinitionIndex typeIdx; CUSTOM_TYPES_EXPORT static Il2CppAssembly* createAssembly(std::string_view name, Il2CppImage* img); diff --git a/shared/types.hpp b/shared/types.hpp index 9a173e0..e8e134c 100644 --- a/shared/types.hpp +++ b/shared/types.hpp @@ -1,15 +1,17 @@ #pragma once #include -#include "beatsaber-hook/shared/utils/utils.h" +#include +#include +#include +#include +#include +#include +#include "_config.h" #include "beatsaber-hook/shared/utils/il2cpp-functions.hpp" #include "beatsaber-hook/shared/utils/il2cpp-utils.hpp" -#include "beatsaber-hook/shared/utils/typedefs.h" #include "beatsaber-hook/shared/utils/size-concepts.hpp" -#include -#include -#include -#include -#include +#include "beatsaber-hook/shared/utils/typedefs.h" +#include "beatsaber-hook/shared/utils/utils.h" #include "logging.hpp" struct Il2CppType; @@ -19,315 +21,307 @@ struct FieldInfo; struct MethodInfo; namespace custom_types { - class Register; - class ClassWrapper; - - /// @brief An abstract type that holds the information required for an instance field. - struct FieldRegistrator { - virtual const char* name() const = 0; - virtual const Il2CppType* type() const = 0; - virtual size_t size() const = 0; - virtual uint16_t fieldAttributes() const = 0; - virtual int32_t offset() const = 0; - }; - - /// @brief An abstract type that holds the information required for a static field. - struct StaticFieldRegistrator { - virtual const char* name() const = 0; - virtual const Il2CppType* type() const = 0; - virtual size_t size() const = 0; - virtual uint16_t fieldAttributes() const = 0; - virtual int32_t offset() const = 0; - }; - - /// @brief An abstract type that holds the information required for a method. - struct MethodRegistrator { - virtual const char* name() const = 0; - virtual const char* csharpName() const = 0; - virtual int flags() const = 0; - virtual const MethodInfo* virtualMethod() const = 0; - virtual const Il2CppType* returnType() const = 0; - virtual std::vector params() const = 0; - virtual uint8_t params_size() const = 0; - virtual Il2CppMethodPointer methodPointer() const = 0; - virtual InvokerMethod invoker() const = 0; - - // This is an UNOWNED pointer, essentially, the pointer is owned entirely by TypeRegistrator and not by this instance. - MethodInfo* info = nullptr; - MethodInfo* get() { - if (info) { - return info; - } - info = new MethodInfo(); - info->flags = flags(); - info->name = csharpName(); - info->invoker_method = invoker(); - info->methodPointer = methodPointer(); - info->name = csharpName(); - info->return_type = returnType(); - info->slot = kInvalidIl2CppMethodSlot; - auto ps = params(); - info->parameters_count = ps.size(); - auto* paramList = reinterpret_cast(calloc(ps.size(), sizeof(Il2CppType*))); - for (uint8_t pi = 0; pi < info->parameters_count; pi++) { - paramList[pi] = ps[pi]; - } - info->parameters = paramList; - return info; - } - }; - - /// @brief An abstract type that holds all the information required to register a type. - /// The general rationale here is that each of these types contains everything necessary for creating a custom type - /// The custom type will get generated and then needs to be assigned properly - struct __attribute__((visibility("default"))) TypeRegistration { - friend Register; - - virtual std::vector const getFields() const = 0; - virtual std::vector const getStaticFields() const = 0; - virtual std::vector const getMethods() const = 0; - virtual char*& static_fields() = 0; - virtual size_t static_fields_size() const = 0; - - virtual const char* name() const = 0; - virtual const char* namespaze() const = 0; - virtual const char* dllName() const = 0; - virtual Il2CppClass* baseType() const = 0; - virtual std::vector const interfaces() const = 0; - virtual Il2CppTypeEnum typeEnum() const = 0; - virtual uint32_t typeFlags() const = 0; - virtual Il2CppClass*& klass() const = 0; - virtual size_t size() const = 0; - virtual TypeRegistration* customBase() const = 0; - virtual bool initialized() const = 0; - virtual void setInitialized() const = 0; - - uint16_t getVtableSize(); - Il2CppType* createType(); - void createClass(); - void populateFields(); - void populateMethods(); - bool checkVirtualsForMatch(MethodRegistrator* info, std::string_view namespaze, std::string_view name, std::string_view methodName, int paramCount); - /// @brief Populates the vtable and offsets vectors with information from the base type's vtable. - void getVtable(std::vector& vtable, std::vector& offsets); - - void clear(); - }; - - #if __has_include() - #include - #include - template - constexpr bool has_get = requires(const T& t) { - t.get(); - }; - - #ifndef CUSTOM_TYPES_NO_CONCEPTS - #define CUSTOM_TYPES_USE_CONCEPTS - #endif - - #elif __has_include() - #include - template - using get_type = decltype(T::get()); - - template - constexpr bool has_get = std::experimental::is_detected_v; - - #else - #error No libraries for the implementation of "has_" anything available! - #endif - template constexpr std::false_type false_t{}; - - /// @struct A helper structure for getting the name of the type. - template - struct name_registry {}; - - /// @struct A helper structure for getting the invoker function of a method - template - struct invoker_creator {}; - - /// @struct Type mapping struct - template struct type_tag {}; - - /// @struct A helper structure for converting parameter types to il2cpp types. - template - struct parameter_converter; - - // Create a vector of ParameterInfo objects (good ol tail recursion) - // 1 or more parameters - template - struct parameter_converter { - static inline std::vector get() { - std::vector params; - auto& info = params.emplace_back(); - il2cpp_functions::Init(); - const Il2CppType* type = ::il2cpp_functions::class_get_type(::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class

::get()); - // Ignore name, it will be set when we iterate over all of them (param_1, param_2, etc.) - // Ignore position, it will also be set on iteration. - // TODO: Maybe some day we can actually use the parameters names themselves! - if (type == nullptr) { - _logger().warning("Failed to get type of parameter!"); - } - info = type; - for (const auto& q : parameter_converter::get()) { - params.push_back(q); - } - return params; +class Register; +class ClassWrapper; + +struct FieldDesc { + std::string_view name; + std::size_t offset; + Il2CppType const* type; + + constexpr FieldDesc(std::string_view name, std::size_t offset, Il2CppType const* type) : name(name), offset(offset), type(type) {} +}; +struct StaticFieldDesc { + std::string_view name; + Il2CppType const* type; + + StaticFieldDesc(std::string_view name, Il2CppType const* type) : name(name), type(type) {} +}; +struct MethodDesc { + std::string_view name; + void* address; + Il2CppType const* retType; + std::span parameters; + bool isStatic; + bool isVirtual; + // TODO: How to handle override? + MethodInfo const* override; + + MethodDesc(std::string_view name, void* address, Il2CppType const* retType, std::span parameters) + : name(name), address(address), retType(retType), parameters(parameters) {} + + std::unique_ptr makeMethodInfo() const { + auto info = std::make_unique(); + info->flags = 0; // TODO: + info->name = name.data(); + info->invoker_method = nullptr; // TODO: + info->methodPointer = reinterpret_cast(address); + info->return_type = retType; + info->slot = kInvalidIl2CppMethodSlot; // TODO: + auto ps = parameters; + info->parameters_count = ps.size(); + auto* paramList = reinterpret_cast(calloc(ps.size(), sizeof(Il2CppType*))); + for (uint8_t pi = 0; pi < info->parameters_count; pi++) { + paramList[pi] = ps[pi]; } - }; + info->parameters = paramList; + return info; + } +}; + +struct CustomTypeInfo { + constexpr CustomTypeInfo(std::string_view name, std::string_view namespaze, std::size_t size, std::span fields, std::span staticFields, + std::span methods) + : name(name), + namespaze(namespaze), + size(size), + fields(fields), + staticFields(staticFields), + methods(methods){ + + }; + + constexpr CustomTypeInfo(CustomTypeInfo&&) = default; + constexpr CustomTypeInfo(CustomTypeInfo const&) = default; + + std::string_view name; + std::string_view namespaze; + std::size_t size; + std::uint32_t typeFlags; + std::optional dllName; + + std::span fields; + std::span staticFields; + std::span methods; +}; + +template +struct CustomTypeInfoTrait; + +template +struct CustomTypeInheritTrait; + +template +struct CustomTypeInterfacesTrait; + +template +concept convertible_to = std::is_convertible_v; + +// TODO: add require is marked as value type or not +template +concept IsCustomType = ::il2cpp_utils::il2cpp_value_type_requirements && requires { + // immutable + { CustomTypeInfoTrait::name() } -> convertible_to; + { CustomTypeInfoTrait::namespaze() } -> convertible_to; + + // mutable since late registration + { CustomTypeInfoTrait::fields() } -> std::same_as>; + { CustomTypeInfoTrait::staticFields() } -> std::same_as>; + { CustomTypeInfoTrait::methods() } -> std::same_as>; +}; - // 0 parameters - template - struct parameter_converter { - static inline std::vector get() { - return std::vector(); +template +constexpr CustomTypeInfo typeInfo() { + return CustomTypeInfo(CustomTypeInfoTrait::namespaze(), CustomTypeInfoTrait::name(), sizeof(T), + std::span(CustomTypeInfoTrait::fields()), // + std::span(CustomTypeInfoTrait::staticFields()), // + std::span(CustomTypeInfoTrait::methods())); +} +template +constexpr std::array typeInfo() { + return std::array({ typeInfo()... }); +} + +// ret -> std::array +template +constexpr auto interfaces() { + return CustomTypeInterfacesTrait::interfaces(); +} + +template +constexpr CustomTypeInfo baseType() { + return CustomTypeInheritTrait::parent(); +} +} + +#if __has_include() +#include +#include +template +constexpr bool has_get = requires(const T& t) { t.get(); }; + +#ifndef CUSTOM_TYPES_NO_CONCEPTS +#define CUSTOM_TYPES_USE_CONCEPTS +#endif + +#elif __has_include() +#include +template +using get_type = decltype(T::get()); + +template +constexpr bool has_get = std::experimental::is_detected_v; + +#else +#error No libraries for the implementation of "has_" anything available! +#endif +template +constexpr std::false_type false_t{}; + +/// @struct A helper structure for getting the name of the type. +template +struct name_registry {}; + +/// @struct A helper structure for getting the invoker function of a method +template +struct invoker_creator {}; + +/// @struct Type mapping struct +template +struct type_tag {}; + +/// @struct A helper structure for converting parameter types to il2cpp types. +template +struct parameter_converter; + +// Create a vector of ParameterInfo objects (good ol tail recursion) +// 1 or more parameters +template +struct parameter_converter { + static inline std::vector get() { + std::vector params; + auto& info = params.emplace_back(); + il2cpp_functions::Init(); + const Il2CppType* type = ::il2cpp_functions::class_get_type(::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class

::get()); + // Ignore name, it will be set when we iterate over all of them (param_1, param_2, etc.) + // Ignore position, it will also be set on iteration. + // TODO: Maybe some day we can actually use the parameters names themselves! + if (type == nullptr) { + _logger().warning("Failed to get type of parameter!"); } - }; - - /// @struct Helper structure for unpacking and packing arguments/return types for invoker function creation - struct arg_helper { - template - static inline Q unpack_arg(void* arg, type_tag) { - if constexpr (std::is_pointer_v) { - return reinterpret_cast(arg); - } else if constexpr (il2cpp_utils::il2cpp_reference_type_wrapper) { - return Q(arg); - } else { - return *reinterpret_cast(arg); - } + info = type; + for (const auto& q : parameter_converter::get()) { + params.push_back(q); } - template - static inline void* pack_result(Q&& thing) { - if constexpr (std::is_pointer_v) { - return reinterpret_cast(std::forward(thing)); - } - else if constexpr (il2cpp_utils::has_il2cpp_conversion) { - return thing.convert(); - } - else { - // We SHOULD simply be able to grab the class and box our result - // Once boxed, we should just be able to return without any issue - // I DO wonder if our invoke functions miss registration with GC... - auto* klass = il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get(); - if (!klass) { - _logger().critical("Failed to get non-null Il2CppClass* during invoke of custom function!"); - return nullptr; - } - il2cpp_functions::Init(); - return static_cast(il2cpp_functions::value_box(klass, static_cast(&thing))); + return params; + } +}; + +// 0 parameters +template +struct parameter_converter { + static inline std::vector get() { + return std::vector(); + } +}; + +/// @struct Helper structure for unpacking and packing arguments/return types for invoker function creation +struct arg_helper { + template + static inline Q unpack_arg(void* arg, type_tag) { + if constexpr (std::is_pointer_v) { + return reinterpret_cast(arg); + } else if constexpr (il2cpp_utils::il2cpp_reference_type_wrapper) { + return Q(arg); + } else { + return *reinterpret_cast(arg); + } + } + template + static inline void* pack_result(Q&& thing) { + if constexpr (std::is_pointer_v) { + return reinterpret_cast(std::forward(thing)); + } else if constexpr (il2cpp_utils::has_il2cpp_conversion) { + return thing.convert(); + } else { + // We SHOULD simply be able to grab the class and box our result + // Once boxed, we should just be able to return without any issue + // I DO wonder if our invoke functions miss registration with GC... + auto* klass = il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get(); + if (!klass) { + _logger().critical("Failed to get non-null Il2CppClass* during invoke of custom function!"); + return nullptr; } + il2cpp_functions::Init(); + return static_cast(il2cpp_functions::value_box(klass, static_cast(&thing))); } + } - template - static inline void pack_result_into(Q&& thing, void* retval) { - if constexpr (std::is_pointer_v) { - *static_cast(retval) = std::forward(thing); - } else if constexpr (il2cpp_utils::il2cpp_reference_type_wrapper) { - *static_cast(retval) = thing.convert(); - } else { - auto* klass = il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get(); - if (!klass) { - _logger().critical("Failed to get non-null Il2CppClass* during invoke of custom function!"); - return; - } - // the void* retval is a buffer created as being klass->instance_size - sizeof(Il2CppObject), see Runtime::InvokeWithThrow - auto sz = sizeof(std::decay_t); - std::memcpy(retval, &thing, sz); + template + static inline void pack_result_into(Q&& thing, void* retval) { + if constexpr (std::is_pointer_v) { + *static_cast(retval) = std::forward(thing); + } else if constexpr (il2cpp_utils::il2cpp_reference_type_wrapper) { + *static_cast(retval) = thing.convert(); + } else { + auto* klass = il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::get(); + if (!klass) { + _logger().critical("Failed to get non-null Il2CppClass* during invoke of custom function!"); + return; } + // the void* retval is a buffer created as being klass->instance_size - sizeof(Il2CppObject), see Runtime::InvokeWithThrow + auto sz = sizeof(std::decay_t); + std::memcpy(retval, &thing, sz); } - }; - - template - struct invoker_creator { - template - static void instance_invoke(TRet(*func)(T*, TArgs...), T* self, void** args, std::index_sequence, void* retval) { - IL2CPP_CATCH_HANDLER( - if constexpr (std::is_same_v) { - func(self, - arg_helper::unpack_arg(args[Ns], type_tag{})... - ); - } else { - arg_helper::pack_result_into( - func(self, - arg_helper::unpack_arg(args[Ns], type_tag{})... - ), - retval - ); - } - ) - } - [[gnu::noinline]] - static void invoke(Il2CppMethodPointer ptr, [[maybe_unused]] const MethodInfo* m, void* obj, void** args, void* retval) { - // We also don't need to use anything from m so it is ignored. - // Perhaps far in the future we will check attributes on it - auto func = reinterpret_cast(ptr); - T* self = static_cast(obj); - - auto seq = std::make_index_sequence(); - instance_invoke(func, self, args, seq, retval); - } - template - [[gnu::noinline]] - static inline TRet wrap(T* self, TArgs ...args) { - return (self->*member)(args...); - } - }; - - template - struct invoker_creator { - template - static void static_invoke(TRet(*func)(TArgs...), void** args, std::index_sequence, void* retval) { - IL2CPP_CATCH_HANDLER( - if constexpr (std::is_same_v) { - func( - arg_helper::unpack_arg(args[Ns], type_tag{})... - ); - } else { - arg_helper::pack_result_into( - func( - arg_helper::unpack_arg(args[Ns], type_tag{})... - ), - retval - ); - } - ) - } - template - static void static_invoke_method(TRet(*func)(TArgs..., const MethodInfo*), void** args, const MethodInfo* m, std::index_sequence, void* retval) { - IL2CPP_CATCH_HANDLER( - if constexpr (std::is_same_v) { - func( - arg_helper::unpack_arg(args[Ns], type_tag{})..., m - ); - } else { - arg_helper::pack_result_into( - func( - arg_helper::unpack_arg(args[Ns], type_tag{})..., m - ), - retval - ); - } - ) - } - [[gnu::noinline]] - static void invoke(Il2CppMethodPointer ptr, [[maybe_unused]] const MethodInfo* m, [[maybe_unused]] void* obj, void** args, void* retval) { - // We also don't need to use anything from m so it is ignored. - // Perhaps far in the future we will check attributes on it + } +}; - auto seq = std::make_index_sequence(); +template +struct invoker_creator { + template + static void instance_invoke(TRet (*func)(T*, TArgs...), T* self, void** args, std::index_sequence, void* retval) { + IL2CPP_CATCH_HANDLER( + if constexpr (std::is_same_v) { func(self, arg_helper::unpack_arg(args[Ns], type_tag{})...); } else { + arg_helper::pack_result_into(func(self, arg_helper::unpack_arg(args[Ns], type_tag{})...), retval); + }) + } + [[gnu::noinline]] static void invoke(Il2CppMethodPointer ptr, [[maybe_unused]] const MethodInfo* m, void* obj, void** args, void* retval) { + // We also don't need to use anything from m so it is ignored. + // Perhaps far in the future we will check attributes on it + auto func = reinterpret_cast(ptr); + T* self = static_cast(obj); - // post unity update delegates changed which use this invoke method - // they get passed a nullptr ptr arg, so if they do we just take the method pointer from the method info instead! - auto func = ptr ? reinterpret_cast(ptr) : reinterpret_cast(m->methodPointer); + auto seq = std::make_index_sequence(); + instance_invoke(func, self, args, seq, retval); + } + template + [[gnu::noinline]] static inline TRet wrap(T* self, TArgs... args) { + return (self->*member)(args...); + } +}; - static_invoke(func, args, seq, retval); - } - [[gnu::noinline]] - static void* invoke_method(Il2CppMethodPointer ptr, const MethodInfo* m, [[maybe_unused]] void* obj, void** args, void* retval) { - auto func = reinterpret_cast(ptr); - auto seq = std::make_index_sequence(); - static_invoke_method(func, args, m, seq, retval); - } - }; -} +template +struct invoker_creator { + template + static void static_invoke(TRet (*func)(TArgs...), void** args, std::index_sequence, void* retval) { + IL2CPP_CATCH_HANDLER( + if constexpr (std::is_same_v) { func(arg_helper::unpack_arg(args[Ns], type_tag{})...); } else { + arg_helper::pack_result_into(func(arg_helper::unpack_arg(args[Ns], type_tag{})...), retval); + }) + } + template + static void static_invoke_method(TRet (*func)(TArgs..., const MethodInfo*), void** args, const MethodInfo* m, std::index_sequence, void* retval) { + IL2CPP_CATCH_HANDLER( + if constexpr (std::is_same_v) { func(arg_helper::unpack_arg(args[Ns], type_tag{})..., m); } else { + arg_helper::pack_result_into(func(arg_helper::unpack_arg(args[Ns], type_tag{})..., m), retval); + }) + } + [[gnu::noinline]] static void invoke(Il2CppMethodPointer ptr, [[maybe_unused]] const MethodInfo* m, [[maybe_unused]] void* obj, void** args, void* retval) { + // We also don't need to use anything from m so it is ignored. + // Perhaps far in the future we will check attributes on it + + auto seq = std::make_index_sequence(); + + // post unity update delegates changed which use this invoke method + // they get passed a nullptr ptr arg, so if they do we just take the method pointer from the method info instead! + auto func = ptr ? reinterpret_cast(ptr) : reinterpret_cast(m->methodPointer); + + static_invoke(func, args, seq, retval); + } + [[gnu::noinline]] static void* invoke_method(Il2CppMethodPointer ptr, const MethodInfo* m, [[maybe_unused]] void* obj, void** args, void* retval) { + auto func = reinterpret_cast(ptr); + auto seq = std::make_index_sequence(); + static_invoke_method(func, args, m, seq, retval); + } +}; +} // namespace custom_types