From e983774f67a8459e89ea916dea55c5321deb53c2 Mon Sep 17 00:00:00 2001 From: Metalit <48568911+Metalit@users.noreply.github.com> Date: Fri, 20 Mar 2026 19:10:52 -0600 Subject: [PATCH 01/52] Rewrite yay --- .clang-format | 70 +- .clangd | 5 +- shared/{utils => }/alphanum.hpp | 0 shared/api.hpp | 449 ++++++++ shared/arrayw.hpp | 249 +++++ shared/binary.hpp | 46 + shared/byref.hpp | 33 + shared/callback.hpp | 213 ++++ shared/capstone.hpp | 353 +++++++ shared/config.hpp | 26 + shared/config/config-utils.hpp | 82 -- shared/config/rapidjson-utils.hpp | 81 -- shared/debug.hpp | 37 + shared/find.hpp | 142 +++ shared/hooking.hpp | 211 ++++ shared/listw.hpp | 387 +++++++ shared/members.hpp | 274 +++++ shared/rapidjson.hpp | 74 ++ shared/safeptr.hpp | 290 ++++++ shared/stringw.hpp | 209 ++++ shared/threading.hpp | 112 ++ shared/types.hpp | 379 +++++++ shared/utils.hpp | 159 +++ shared/utils/accessor-wrapper-types.hpp | 438 -------- shared/utils/base-wrapper-type.hpp | 32 - shared/utils/better_span.hpp | 57 - shared/utils/byref.hpp | 74 -- shared/utils/capstone-utils.hpp | 310 ------ shared/utils/early-mod-checks.hpp | 9 - shared/utils/enum-type.hpp | 9 - shared/utils/enum-wrapper-type.hpp | 47 - shared/utils/gc-alloc.hpp | 70 -- shared/utils/hashing.hpp | 26 - shared/utils/hooking.hpp | 724 ------------- shared/utils/il2cpp-functions.hpp | 528 ---------- shared/utils/il2cpp-type-check.hpp | 651 ------------ shared/utils/il2cpp-utils-boxing.hpp | 60 -- shared/utils/il2cpp-utils-classes.hpp | 279 ----- shared/utils/il2cpp-utils-exceptions.hpp | 169 --- shared/utils/il2cpp-utils-fields.hpp | 147 --- shared/utils/il2cpp-utils-methods.hpp | 986 ------------------ shared/utils/il2cpp-utils-properties.hpp | 94 -- shared/utils/il2cpp-utils.hpp | 650 ------------ shared/utils/logging.hpp | 19 - shared/utils/result.hpp | 200 ---- shared/utils/size-concepts.hpp | 65 -- shared/utils/type-concepts.hpp | 207 ---- shared/utils/typedefs-array.hpp | 598 ----------- shared/utils/typedefs-delegate.hpp | 16 - shared/utils/typedefs-disposal.hpp | 49 - shared/utils/typedefs-list.hpp | 689 ------------ shared/utils/typedefs-object.hpp | 8 - shared/utils/typedefs-string.hpp | 338 ------ shared/utils/typedefs-wrappers.hpp | 871 ---------------- shared/utils/typedefs.h | 217 ---- shared/utils/utils-functions.h | 100 -- shared/utils/utils.h | 309 ------ shared/utils/value-type.hpp | 9 - shared/utils/value-wrapper-type.hpp | 60 -- shared/valuew.hpp | 35 + src/{utils/il2cpp-functions.cpp => api.cpp} | 764 ++++++++------ src/binary.cpp | 294 ++++++ .../capstone-utils.cpp => capstone.cpp} | 55 +- src/config/config-utils.cpp | 114 -- src/debug.cpp | 527 ++++++++++ src/find.cpp | 453 ++++++++ src/safeptr.cpp | 36 + src/stringw.cpp | 269 +++++ src/types.cpp | 390 +++++++ src/utils.cpp | 88 ++ src/utils/gc-alloc.cpp | 37 - src/utils/il2cpp-type-check.cpp | 177 ---- src/utils/il2cpp-utils-classes.cpp | 291 ------ src/utils/il2cpp-utils-exceptions.cpp | 68 -- src/utils/il2cpp-utils-fields.cpp | 76 -- src/utils/il2cpp-utils-methods.cpp | 612 ----------- src/utils/il2cpp-utils-properties.cpp | 83 -- src/utils/il2cpp-utils.cpp | 283 ----- src/utils/typedefs-wrapper.cpp | 344 ------ src/utils/utils.cpp | 384 ------- 80 files changed, 6301 insertions(+), 12075 deletions(-) rename shared/{utils => }/alphanum.hpp (100%) create mode 100644 shared/api.hpp create mode 100644 shared/arrayw.hpp create mode 100644 shared/binary.hpp create mode 100644 shared/byref.hpp create mode 100644 shared/callback.hpp create mode 100644 shared/capstone.hpp create mode 100644 shared/config.hpp delete mode 100644 shared/config/config-utils.hpp delete mode 100644 shared/config/rapidjson-utils.hpp create mode 100644 shared/debug.hpp create mode 100644 shared/find.hpp create mode 100644 shared/hooking.hpp create mode 100644 shared/listw.hpp create mode 100644 shared/members.hpp create mode 100644 shared/rapidjson.hpp create mode 100644 shared/safeptr.hpp create mode 100644 shared/stringw.hpp create mode 100644 shared/threading.hpp create mode 100644 shared/types.hpp create mode 100644 shared/utils.hpp delete mode 100644 shared/utils/accessor-wrapper-types.hpp delete mode 100644 shared/utils/base-wrapper-type.hpp delete mode 100644 shared/utils/better_span.hpp delete mode 100644 shared/utils/byref.hpp delete mode 100644 shared/utils/capstone-utils.hpp delete mode 100644 shared/utils/early-mod-checks.hpp delete mode 100644 shared/utils/enum-type.hpp delete mode 100644 shared/utils/enum-wrapper-type.hpp delete mode 100644 shared/utils/gc-alloc.hpp delete mode 100644 shared/utils/hashing.hpp delete mode 100644 shared/utils/hooking.hpp delete mode 100644 shared/utils/il2cpp-functions.hpp delete mode 100644 shared/utils/il2cpp-type-check.hpp delete mode 100644 shared/utils/il2cpp-utils-boxing.hpp delete mode 100644 shared/utils/il2cpp-utils-classes.hpp delete mode 100644 shared/utils/il2cpp-utils-exceptions.hpp delete mode 100644 shared/utils/il2cpp-utils-fields.hpp delete mode 100644 shared/utils/il2cpp-utils-methods.hpp delete mode 100644 shared/utils/il2cpp-utils-properties.hpp delete mode 100644 shared/utils/il2cpp-utils.hpp delete mode 100644 shared/utils/logging.hpp delete mode 100644 shared/utils/result.hpp delete mode 100644 shared/utils/size-concepts.hpp delete mode 100644 shared/utils/type-concepts.hpp delete mode 100644 shared/utils/typedefs-array.hpp delete mode 100644 shared/utils/typedefs-delegate.hpp delete mode 100644 shared/utils/typedefs-disposal.hpp delete mode 100644 shared/utils/typedefs-list.hpp delete mode 100644 shared/utils/typedefs-object.hpp delete mode 100644 shared/utils/typedefs-string.hpp delete mode 100644 shared/utils/typedefs-wrappers.hpp delete mode 100644 shared/utils/typedefs.h delete mode 100644 shared/utils/utils-functions.h delete mode 100644 shared/utils/utils.h delete mode 100644 shared/utils/value-type.hpp delete mode 100644 shared/utils/value-wrapper-type.hpp create mode 100644 shared/valuew.hpp rename src/{utils/il2cpp-functions.cpp => api.cpp} (51%) create mode 100644 src/binary.cpp rename src/{utils/capstone-utils.cpp => capstone.cpp} (58%) delete mode 100644 src/config/config-utils.cpp create mode 100644 src/debug.cpp create mode 100644 src/find.cpp create mode 100644 src/safeptr.cpp create mode 100644 src/stringw.cpp create mode 100644 src/types.cpp create mode 100644 src/utils.cpp delete mode 100644 src/utils/gc-alloc.cpp delete mode 100644 src/utils/il2cpp-type-check.cpp delete mode 100644 src/utils/il2cpp-utils-classes.cpp delete mode 100644 src/utils/il2cpp-utils-exceptions.cpp delete mode 100644 src/utils/il2cpp-utils-fields.cpp delete mode 100644 src/utils/il2cpp-utils-methods.cpp delete mode 100644 src/utils/il2cpp-utils-properties.cpp delete mode 100644 src/utils/il2cpp-utils.cpp delete mode 100644 src/utils/typedefs-wrapper.cpp delete mode 100644 src/utils/utils.cpp diff --git a/.clang-format b/.clang-format index 1e59dced..f0ce6af9 100644 --- a/.clang-format +++ b/.clang-format @@ -1,13 +1,63 @@ +Language: Cpp BasedOnStyle: Google -AllowShortBlocksOnASingleLine: false -AllowShortFunctionsOnASingleLine: Empty -AllowShortIfStatementsOnASingleLine: true -ColumnLimit: 200 -CommentPragmas: NOLINT:.* +AccessModifierOffset: -1 +AlignAfterOpenBracket: BlockIndent +AlignOperands: AlignAfterOperator +AlignTrailingComments: + Kind: Never +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: Inline +AllowShortLoopsOnASingleLine: false +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakAfterAttributes: Never +BreakBeforeConceptDeclarations: Always +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: false +ColumnLimit: 150 DerivePointerAlignment: false -IncludeBlocks: Preserve +FixNamespaceComments: false +IncludeBlocks: Regroup +IncludeCategories: + - Regex: "^<[^.>]+>$" + Priority: 2 + SortPriority: 3 + CaseSensitive: false + - Regex: '^<.+\.(h|hpp)>$' + Priority: 2 + SortPriority: 4 + CaseSensitive: false + - Regex: '^"[A-Z].*\/.+\.hpp"$' + Priority: 5 + CaseSensitive: true + - Regex: "^.+/shared/.+" + Priority: 1 + SortPriority: 2 + CaseSensitive: true + - Regex: ".*" + Priority: 1 + SortPriority: 1 + CaseSensitive: false +IndentExternBlock: Indent +IndentRequiresClause: false IndentWidth: 4 -PointerAlignment: Left -TabWidth: 4 -UseTab: Never -Cpp11BracedListStyle: false \ No newline at end of file +InsertBraces: true +InsertNewlineAtEOF: true +KeepEmptyLinesAtTheStartOfBlocks: true +LineEnding: LF +NamespaceIndentation: All +PackConstructorInitializers: CurrentLine +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 50 +QualifierAlignment: Right +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SkipMacroDefinitionBody: true +SpaceAfterCStyleCast: true +SpacesBeforeTrailingComments: 2 diff --git a/.clangd b/.clangd index 9e1f4440..c06631a4 100644 --- a/.clangd +++ b/.clangd @@ -1,6 +1,9 @@ +Diagnostics: + UnusedIncludes: None +--- If: # Note: This is a regexp, notice '.*' at the end of PathMatch string. PathMatch: ./extern/.* Index: # Disable slow background indexing of these files. - Background: Skip \ No newline at end of file + Background: Skip diff --git a/shared/utils/alphanum.hpp b/shared/alphanum.hpp similarity index 100% rename from shared/utils/alphanum.hpp rename to shared/alphanum.hpp diff --git a/shared/api.hpp b/shared/api.hpp new file mode 100644 index 00000000..e981489e --- /dev/null +++ b/shared/api.hpp @@ -0,0 +1,449 @@ +#pragma once + +#include "utils.hpp" + +#pragma pack(push) + +#include "il2cpp-api-types.h" +#include "il2cpp-class-internals.h" +#include "il2cpp-metadata.h" +#include "il2cpp-object-internals.h" +#include "il2cpp-tabledefs.h" +#include "vm/GlobalMetadataFileInternals.h" + +#include +#include +#include +#include + +typedef std::vector Il2CppAssemblyVector; + +#define API_FUNC(rt, name, ...) \ + extern rt (*name) __VA_ARGS__; + +// A (once) class which contains all available il2cpp functions +// Created by zoller27osu +namespace i2c::functions { +// These methods autogenerated by Sc2ad: +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(int, init, (const char* domain_name)); + API_FUNC(int, init_utf16, (Il2CppChar const* domain_name)); +#else + API_FUNC(void, init, (const char* domain_name)); + API_FUNC(void, init_utf16, (Il2CppChar const* domain_name)); +#endif + API_FUNC(void, shutdown, ()); + API_FUNC(void, set_config_dir, (char const* config_path)); + API_FUNC(void, set_data_dir, (char const* data_path)); + API_FUNC(void, set_temp_dir, (char const* temp_path)); + API_FUNC(void, set_commandline_arguments, (int argc, char const* const argv[], char const* basedir)); + API_FUNC(void, set_commandline_arguments_utf16, (int argc, Il2CppChar const* const argv[], char const* basedir)); + API_FUNC(void, set_config_utf16, (Il2CppChar const* executablePath)); + API_FUNC(void, set_config, (char const* executablePath)); + API_FUNC(void, set_memory_callbacks, (Il2CppMemoryCallbacks * callbacks)); + API_FUNC(Il2CppImage const*, get_corlib, ()); + API_FUNC(void, add_internal_call, (char const* name, Il2CppMethodPointer method)); + API_FUNC(Il2CppMethodPointer, resolve_icall, (char const* name)); + API_FUNC(void*, alloc, (size_t size)); + API_FUNC(void, free, (void* ptr)); + API_FUNC(Il2CppClass*, array_class_get, (Il2CppClass * element_class, uint32_t rank)); + API_FUNC(uint32_t, array_length, (Il2CppArray * array)); + API_FUNC(uint32_t, array_get_byte_length, (Il2CppArray * array)); + API_FUNC(Il2CppArray*, array_new, (Il2CppClass * elementTypeInfo, il2cpp_array_size_t length)); + API_FUNC(Il2CppArray*, array_new_specific, (Il2CppClass * arrayTypeInfo, il2cpp_array_size_t length)); + API_FUNC(Il2CppArray*, array_new_full, (Il2CppClass * array_class, il2cpp_array_size_t* lengths, il2cpp_array_size_t* lower_bounds)); + API_FUNC(Il2CppClass*, bounded_array_class_get, (Il2CppClass * element_class, uint32_t rank, bool bounded)); + API_FUNC(int, array_element_size, (Il2CppClass const* array_class)); + API_FUNC(Il2CppImage const*, assembly_get_image, (Il2CppAssembly const* assembly)); +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(void, class_for_each, (void (*klassReportFunc)(Il2CppClass* klass, void* userData), void* userData)); +#endif + API_FUNC(const Il2CppType*, class_enum_basetype, (Il2CppClass * klass)); + API_FUNC(bool, class_is_generic, (Il2CppClass const* klass)); + API_FUNC(bool, class_is_inflated, (Il2CppClass const* klass)); + API_FUNC(bool, class_is_assignable_from, (Il2CppClass * klass, Il2CppClass* oklass)); + API_FUNC(bool, class_is_subclass_of, (Il2CppClass * klass, Il2CppClass* klassc, bool check_interfaces)); + API_FUNC(bool, class_has_parent, (Il2CppClass * klass, Il2CppClass* klassc)); + API_FUNC(Il2CppClass*, class_from_il2cpp_type, (Il2CppType const* type)); + API_FUNC(Il2CppClass*, class_from_name, (Il2CppImage const* image, char const* namespaze, char const* name)); + API_FUNC(Il2CppClass*, class_from_system_type, (Il2CppReflectionType * type)); + API_FUNC(Il2CppClass*, class_get_element_class, (Il2CppClass * klass)); + API_FUNC(EventInfo const*, class_get_events, (Il2CppClass * klass, void** iter)); + API_FUNC(FieldInfo*, class_get_fields, (Il2CppClass * klass, void** iter)); + API_FUNC(Il2CppClass*, class_get_nested_types, (Il2CppClass * klass, void** iter)); + API_FUNC(Il2CppClass*, class_get_interfaces, (Il2CppClass * klass, void** iter)); + API_FUNC(PropertyInfo const*, class_get_properties, (Il2CppClass * klass, void** iter)); + API_FUNC(PropertyInfo const*, class_get_property_from_name, (Il2CppClass * klass, char const* name)); + API_FUNC(FieldInfo*, class_get_field_from_name, (Il2CppClass * klass, char const* name)); + API_FUNC(MethodInfo const*, class_get_methods, (Il2CppClass * klass, void** iter)); + API_FUNC(MethodInfo const*, class_get_method_from_name, (Il2CppClass const* klass, char const* name, int argsCount)); + API_FUNC(char const*, class_get_name, (Il2CppClass const* klass)); +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(void, type_get_name_chunked, (const Il2CppType* type, void (*chunkReportFunc)(void* data, void* userData), void* userData)); +#endif + API_FUNC(const char*, class_get_namespace, (const Il2CppClass* klass)); + API_FUNC(Il2CppClass*, class_get_parent, (Il2CppClass * klass)); + API_FUNC(Il2CppClass*, class_get_declaring_type, (Il2CppClass const* klass)); + API_FUNC(int32_t, class_instance_size, (Il2CppClass * klass)); + API_FUNC(size_t, class_num_fields, (Il2CppClass const* enumKlass)); + API_FUNC(bool, class_is_valuetype, (Il2CppClass const* klass)); + API_FUNC(int32_t, class_value_size, (Il2CppClass * klass, uint32_t* align)); + API_FUNC(bool, class_is_blittable, (Il2CppClass const* klass)); + API_FUNC(int, class_get_flags, (Il2CppClass const* klass)); + API_FUNC(bool, class_is_abstract, (Il2CppClass const* klass)); + API_FUNC(bool, class_is_interface, (Il2CppClass const* klass)); + API_FUNC(int, class_array_element_size, (Il2CppClass const* klass)); + API_FUNC(Il2CppClass*, class_from_type, (Il2CppType const* type)); + API_FUNC(Il2CppType const*, class_get_type, (Il2CppClass * klass)); + API_FUNC(uint32_t, class_get_type_token, (Il2CppClass * klass)); + API_FUNC(bool, class_has_attribute, (Il2CppClass * klass, Il2CppClass* attr_class)); + API_FUNC(bool, class_has_references, (Il2CppClass * klass)); + API_FUNC(bool, class_is_enum, (Il2CppClass const* klass)); + API_FUNC(Il2CppImage const*, class_get_image, (Il2CppClass * klass)); + API_FUNC(char const*, class_get_assemblyname, (Il2CppClass const* klass)); + API_FUNC(int, class_get_rank, (Il2CppClass const* klass)); +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(uint32_t, class_get_data_size, (const Il2CppClass* klass)); + API_FUNC(void*, class_get_static_field_data, (Il2CppClass const* klass)); +#endif +#if defined(UNITY_2019) || defined(UNITY_2021) + API_FUNC(size_t, class_get_bitmap_size, (const Il2CppClass* klass)); + API_FUNC(void, class_get_bitmap, (Il2CppClass * klass, size_t* bitmap)); +#endif + API_FUNC(bool, stats_dump_to_file, (const char* path)); + API_FUNC(uint64_t, stats_get_value, (Il2CppStat stat)); + API_FUNC(Il2CppDomain*, domain_get, ()); + API_FUNC(Il2CppAssembly const*, domain_assembly_open, (Il2CppDomain * domain, char const* name)); + API_FUNC(Il2CppAssembly const**, domain_get_assemblies, (Il2CppDomain const* domain, size_t* size)); +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(void, raise_exception, (Il2CppException*) ); +#endif + API_FUNC(Il2CppException*, exception_from_name_msg, (const Il2CppImage* image, const char* name_space, const char* name, const char* msg)); + API_FUNC(Il2CppException*, get_exception_argument_null, (char const* arg)); + API_FUNC(void, format_exception, (Il2CppException const* ex, char* message, int message_size)); + API_FUNC(void, format_stack_trace, (Il2CppException const* ex, char* output, int output_size)); + API_FUNC(void, unhandled_exception, (Il2CppException*) ); + API_FUNC(int, field_get_flags, (FieldInfo * field)); + API_FUNC(char const*, field_get_name, (FieldInfo * field)); + API_FUNC(Il2CppClass*, field_get_parent, (FieldInfo * field)); + API_FUNC(size_t, field_get_offset, (FieldInfo * field)); + API_FUNC(Il2CppType const*, field_get_type, (FieldInfo * field)); + API_FUNC(void, field_get_value, (Il2CppObject * obj, FieldInfo* field, void* value)); + API_FUNC(Il2CppObject*, field_get_value_object, (FieldInfo * field, Il2CppObject* obj)); + API_FUNC(bool, field_has_attribute, (FieldInfo * field, Il2CppClass* attr_class)); + API_FUNC(void, field_set_value, (Il2CppObject * obj, FieldInfo* field, void* value)); + API_FUNC(void, field_static_get_value, (FieldInfo * field, void* value)); + API_FUNC(void, field_static_set_value, (FieldInfo * field, void* value)); + API_FUNC(void, field_set_value_object, (Il2CppObject * instance, FieldInfo* field, Il2CppObject* value)); +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(bool, field_is_literal, (FieldInfo * field)); +#endif + API_FUNC(void, gc_collect, (int maxGenerations)); + API_FUNC(int32_t, gc_collect_a_little, ()); + API_FUNC(void, gc_disable, ()); + API_FUNC(void, gc_enable, ()); + API_FUNC(bool, gc_is_disabled, ()); +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(int64_t, gc_get_max_time_slice_ns, ()); + API_FUNC(void, gc_set_max_time_slice_ns, (int64_t maxTimeSlice)); + API_FUNC(bool, gc_is_incremental, ()); +#endif + API_FUNC(int64_t, gc_get_used_size, ()); + API_FUNC(int64_t, gc_get_heap_size, ()); + API_FUNC(void, gc_wbarrier_set_field, (Il2CppObject * obj, void** targetAddress, void* object)); +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(bool, gc_has_strict_wbarriers, ()); + API_FUNC(void, gc_set_external_allocation_tracker, (void (*func)(void*, size_t, int))); + API_FUNC(void, gc_set_external_wbarrier_tracker, (void (*func)(void**))); + API_FUNC(void, gc_foreach_heap, (void (*func)(void* data, void* userData), void* userData)); + API_FUNC(void*, gc_alloc_fixed, (std::size_t size)); + API_FUNC(void, gc_free_fixed, (void* addr)); + API_FUNC(void, stop_gc_world, ()); + API_FUNC(void, start_gc_world, ()); +#endif + API_FUNC(uint32_t, gchandle_new, (Il2CppObject * obj, bool pinned)); + API_FUNC(uint32_t, gchandle_new_weakref, (Il2CppObject * obj, bool track_resurrection)); + API_FUNC(Il2CppObject*, gchandle_get_target, (uint32_t gchandle)); + API_FUNC(void, gchandle_free, (uint32_t gchandle)); +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(void, gchandle_foreach_get_target, (void (*func)(void* data, void* userData), void* userData)); + API_FUNC(uint32_t, object_header_size, ()); + API_FUNC(uint32_t, array_object_header_size, ()); + API_FUNC(uint32_t, offset_of_array_length_in_array_object_header, ()); + API_FUNC(uint32_t, offset_of_array_bounds_in_array_object_header, ()); + API_FUNC(uint32_t, allocation_granularity, ()); +#endif +#if defined(UNITY_2021) || defined(UNITY_6) + API_FUNC( + void*, + unity_liveness_allocate_struct, + (Il2CppClass * filter, + int max_object_count, + il2cpp_register_object_callback callback, + void* userdata, + il2cpp_liveness_reallocate_callback reallocate) + ); + API_FUNC(void, unity_liveness_finalize, (void* state)); + API_FUNC(void, unity_liveness_free_struct, (void* state)); +#else + API_FUNC( + void*, + unity_liveness_calculation_begin, + (Il2CppClass * filter, + int max_object_count, + il2cpp_register_object_callback callback, + void* userdata, + il2cpp_WorldChangedCallback onWorldStarted, + il2cpp_WorldChangedCallback onWorldStopped) + ); + API_FUNC(void, unity_liveness_calculation_end, (void* state)); +#endif + API_FUNC(void, unity_liveness_calculation_from_root, (Il2CppObject * root, void* state)); + API_FUNC(void, unity_liveness_calculation_from_statics, (void* state)); + API_FUNC(Il2CppType const*, method_get_return_type, (MethodInfo const* method)); + API_FUNC(Il2CppClass*, method_get_declaring_type, (MethodInfo const* method)); + API_FUNC(char const*, method_get_name, (MethodInfo const* method)); + API_FUNC(MethodInfo const*, method_get_from_reflection, (Il2CppReflectionMethod const* method)); + API_FUNC(Il2CppReflectionMethod*, method_get_object, (MethodInfo const* method, Il2CppClass* refclass)); + API_FUNC(bool, method_is_generic, (MethodInfo const* method)); + API_FUNC(bool, method_is_inflated, (MethodInfo const* method)); + API_FUNC(bool, method_is_instance, (MethodInfo const* method)); + API_FUNC(uint32_t, method_get_param_count, (MethodInfo const* method)); + API_FUNC(Il2CppType const*, method_get_param, (MethodInfo const* method, uint32_t index)); + API_FUNC(Il2CppClass*, method_get_class, (MethodInfo const* method)); + API_FUNC(bool, method_has_attribute, (MethodInfo const* method, Il2CppClass* attr_class)); + API_FUNC(uint32_t, method_get_flags, (MethodInfo const* method, uint32_t* iflags)); + API_FUNC(uint32_t, method_get_token, (MethodInfo const* method)); + API_FUNC(char const*, method_get_param_name, (MethodInfo const* method, uint32_t index)); + + // ONLY IF THE PROFILER EXISTS FOR UNITY_2019 + API_FUNC(void, profiler_install, (Il2CppProfiler * prof, Il2CppProfileFunc shutdown_callback)); + API_FUNC(void, profiler_set_events, (Il2CppProfileFlags events)); + API_FUNC(void, profiler_install_enter_leave, (Il2CppProfileMethodFunc enter, Il2CppProfileMethodFunc fleave)); + API_FUNC(void, profiler_install_allocation, (Il2CppProfileAllocFunc callback)); + API_FUNC(void, profiler_install_gc, (Il2CppProfileGCFunc callback, Il2CppProfileGCResizeFunc heap_resize_callback)); + API_FUNC(void, profiler_install_fileio, (Il2CppProfileFileIOFunc callback)); + API_FUNC(void, profiler_install_thread, (Il2CppProfileThreadFunc start, Il2CppProfileThreadFunc end)); + + API_FUNC(uint32_t, property_get_flags, (PropertyInfo const* prop)); + API_FUNC(MethodInfo const*, property_get_get_method, (PropertyInfo const* prop)); + API_FUNC(MethodInfo const*, property_get_set_method, (PropertyInfo const* prop)); + API_FUNC(char const*, property_get_name, (PropertyInfo const* prop)); + API_FUNC(Il2CppClass*, property_get_parent, (PropertyInfo const* prop)); + API_FUNC(Il2CppClass*, object_get_class, (Il2CppObject * obj)); + API_FUNC(uint32_t, object_get_size, (Il2CppObject * obj)); + API_FUNC(MethodInfo const*, object_get_virtual_method, (Il2CppObject * obj, MethodInfo const* method)); + API_FUNC(Il2CppObject*, object_new, (Il2CppClass const* klass)); + // Always returns (void*, (obj + 1) + API_FUNC(void*, object_unbox, (Il2CppObject * obj)); + // If klass is not a ValueType, returns (Il2CppObject*, (*data), else boxes + API_FUNC(Il2CppObject*, value_box, (Il2CppClass * klass, void* data)); + API_FUNC(void, monitor_enter, (Il2CppObject * obj)); + API_FUNC(bool, monitor_try_enter, (Il2CppObject * obj, uint32_t timeout)); + API_FUNC(void, monitor_exit, (Il2CppObject * obj)); + API_FUNC(void, monitor_pulse, (Il2CppObject * obj)); + API_FUNC(void, monitor_pulse_all, (Il2CppObject * obj)); + API_FUNC(void, monitor_wait, (Il2CppObject * obj)); + API_FUNC(bool, monitor_try_wait, (Il2CppObject * obj, uint32_t timeout)); + API_FUNC(Il2CppObject*, runtime_invoke, (MethodInfo const* method, void* obj, void** params, Il2CppException** exc)); + API_FUNC( + Il2CppObject*, + runtime_invoke_convert_args, + (MethodInfo const* method, void* obj, Il2CppObject** params, int paramCount, Il2CppException** exc) + ); + API_FUNC(void, runtime_class_init, (Il2CppClass * klass)); + API_FUNC(void, runtime_object_init, (Il2CppObject * obj)); + API_FUNC(void, runtime_object_init_exception, (Il2CppObject * obj, Il2CppException** exc)); + API_FUNC(void, runtime_unhandled_exception_policy_set, (Il2CppRuntimeUnhandledExceptionPolicy value)); + API_FUNC(int32_t, string_length, (Il2CppString * str)); + API_FUNC(Il2CppChar*, string_chars, (Il2CppString * str)); + API_FUNC(Il2CppString*, string_new, (char const* str)); + API_FUNC(Il2CppString*, string_new_len, (char const* str, uint32_t length)); + API_FUNC(Il2CppString*, string_new_utf16, (Il2CppChar const* text, int32_t len)); + API_FUNC(Il2CppString*, string_new_wrapper, (char const* str)); + API_FUNC(Il2CppString*, string_intern, (Il2CppString * str)); + API_FUNC(Il2CppString*, string_is_interned, (Il2CppString * str)); + API_FUNC(Il2CppThread*, thread_current, ()); + API_FUNC(Il2CppThread*, thread_attach, (Il2CppDomain * domain)); + API_FUNC(void, thread_detach, (Il2CppThread * thread)); +#if defined(UNITY_2019) || defined(UNITY_2021) + API_FUNC(Il2CppThread**, thread_get_all_attached_threads, (size_t* size)); +#endif + API_FUNC(bool, is_vm_thread, (Il2CppThread * thread)); + API_FUNC(void, current_thread_walk_frame_stack, (Il2CppFrameWalkFunc func, void* user_data)); + API_FUNC(void, thread_walk_frame_stack, (Il2CppThread * thread, Il2CppFrameWalkFunc func, void* user_data)); + API_FUNC(bool, current_thread_get_top_frame, (Il2CppStackFrameInfo * frame)); + API_FUNC(bool, thread_get_top_frame, (Il2CppThread * thread, Il2CppStackFrameInfo* frame)); + API_FUNC(bool, current_thread_get_frame_at, (int32_t offset, Il2CppStackFrameInfo* frame)); + API_FUNC(bool, thread_get_frame_at, (Il2CppThread * thread, int32_t offset, Il2CppStackFrameInfo* frame)); + API_FUNC(int32_t, current_thread_get_stack_depth, ()); + API_FUNC(int32_t, thread_get_stack_depth, (Il2CppThread * thread)); +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(void, override_stack_backtrace, (Il2CppBacktraceFunc stackBacktraceFunc)); +#endif + API_FUNC(Il2CppObject*, type_get_object, (const Il2CppType* type)); + API_FUNC(int, type_get_type, (Il2CppType const* type)); + API_FUNC(Il2CppClass*, type_get_class_or_element_class, (Il2CppType const* type)); + API_FUNC(char*, type_get_name, (Il2CppType const* type)); + API_FUNC(bool, type_is_byref, (Il2CppType const* type)); + API_FUNC(uint32_t, type_get_attrs, (Il2CppType const* type)); + API_FUNC(bool, type_equals, (Il2CppType const* type, Il2CppType const* otherType)); + API_FUNC(char*, type_get_assembly_qualified_name, (Il2CppType const* type)); +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(bool, type_is_static, (const Il2CppType* type)); + API_FUNC(bool, type_is_pointer_type, (Il2CppType const* type)); +#endif + API_FUNC(const Il2CppAssembly*, image_get_assembly, (const Il2CppImage* image)); + API_FUNC(char const*, image_get_name, (Il2CppImage const* image)); + API_FUNC(char const*, image_get_filename, (Il2CppImage const* image)); + API_FUNC(MethodInfo const*, image_get_entry_point, (Il2CppImage const* image)); + API_FUNC(size_t, image_get_class_count, (Il2CppImage const* image)); + API_FUNC(Il2CppClass const*, image_get_class, (Il2CppImage const* image, size_t index)); + API_FUNC(Il2CppManagedMemorySnapshot*, capture_memory_snapshot, ()); + API_FUNC(void, free_captured_memory_snapshot, (Il2CppManagedMemorySnapshot * snapshot)); + API_FUNC(void, set_find_plugin_callback, (Il2CppSetFindPlugInCallback method)); + API_FUNC(void, register_log_callback, (Il2CppLogCallback method)); + API_FUNC(void, debugger_set_agent_options, (char const* options)); + API_FUNC(bool, is_debugger_attached, ()); +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(void, register_debugger_agent_transport, (Il2CppDebuggerTransport * debuggerTransport)); + API_FUNC(bool, debug_get_method_info, (MethodInfo const*, Il2CppMethodDebugInfo* methodDebugInfo)); +#endif + API_FUNC(void, unity_install_unitytls_interface, (const void* unitytlsInterfaceStruct)); + API_FUNC(Il2CppCustomAttrInfo*, custom_attrs_from_class, (Il2CppClass * klass)); + API_FUNC(Il2CppCustomAttrInfo*, custom_attrs_from_method, (MethodInfo const* method)); + API_FUNC(Il2CppObject*, custom_attrs_get_attr, (Il2CppCustomAttrInfo * ainfo, Il2CppClass* attr_klass)); + API_FUNC(bool, custom_attrs_has_attr, (Il2CppCustomAttrInfo * ainfo, Il2CppClass* attr_klass)); + API_FUNC(Il2CppArray*, custom_attrs_construct, (Il2CppCustomAttrInfo * cinfo)); + API_FUNC(void, custom_attrs_free, (Il2CppCustomAttrInfo * ainfo)); +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(void, class_set_userdata, (Il2CppClass * klass, void* userdata)); + API_FUNC(int, class_get_userdata_offset, ()); +#endif + + // MANUALLY DEFINED CONST DEFINITIONS + API_FUNC(Il2CppType const*, class_get_type_const, (Il2CppClass const* klass)); + API_FUNC(char const*, class_get_name_const, (Il2CppClass const* klass)); + API_FUNC(Il2CppClass*, type_get_class, (Il2CppType * type)); + + // SELECT NON-API LIBIL2CPP FUNCTIONS: + API_FUNC(bool, Class_Init, (Il2CppClass * klass)); + + API_FUNC(Il2CppClass*, MetadataCache_GetTypeInfoFromHandle, (Il2CppMetadataTypeHandle index)); + API_FUNC(Il2CppClass*, MetadataCache_GetTypeInfoFromTypeIndex, (TypeIndex index)); + + API_FUNC(Il2CppClass*, GlobalMetadata_GetTypeInfoFromTypeDefinitionIndex, (TypeDefinitionIndex index)); + API_FUNC(Il2CppClass*, GlobalMetadata_GetTypeInfoFromHandle, (Il2CppMetadataTypeHandle index)); + +#if defined(UNITY_2019) || defined(UNITY_2021) || defined(UNITY_6) + API_FUNC(std::string, _Type_GetName_, (const Il2CppType* type, Il2CppTypeNameFormat format)); +#else + API_FUNC(gnu_string, _Type_GetName_, (const Il2CppType* type, Il2CppTypeNameFormat format)); +#endif + API_FUNC(void, GC_free, (void* addr)); + + API_FUNC(void, GarbageCollector_SetWriteBarrier, (void** ptr)); + API_FUNC(void*, GarbageCollector_AllocateFixed, (size_t sz, void* descr)); + + API_FUNC(Il2CppClass*, Class_FromIl2CppType, (Il2CppType * typ)); + API_FUNC(Il2CppClass*, Class_GetPtrClass, (Il2CppClass * elementClass)); + API_FUNC(Il2CppClass*, GenericClass_GetClass, (Il2CppGenericClass * gclass)); + API_FUNC(Il2CppClass*, GenericClass_CreateClass, (Il2CppGenericClass * gclass, bool throwOnError)); +#if defined(UNITY_2019) || defined(UNITY_2021) + API_FUNC(AssemblyVector*, Assembly_GetAllAssemblies, ()); +#endif + extern bool has_gc_funcs; + + // You must i2c::functions::free the char* when you are done with it + extern char* Type_GetName(Il2CppType const* type, Il2CppTypeNameFormat format); + extern void const* s_GlobalMetadata; + extern Il2CppGlobalMetadataHeader const* s_GlobalMetadataHeader; + extern Il2CppMetadataRegistration const* s_Il2CppMetadataRegistration; + + extern Il2CppDefaults const* defaults; + extern void find_il2cpp_defaults(Paper::LoggerContext const& logger); + +#if !defined(UNITY_2019) && !defined(UNITY_2021) + extern Il2CppAssemblyVector* s_Assemblies; + extern void find_s_Assemblies(Paper::LoggerContext const& logger); +#endif + + // must be done on-demand because the pointers aren't necessarily correct at the time of il2cpp_functions::Init + void CheckS_GlobalMetadata(); + + // COPIES OF FREQUENTLY INLINED NON-API LIBIL2CPP FUNCTIONS: + char const* MetadataCache_GetStringFromIndex(StringIndex index); + Il2CppTypeDefinition const* MetadataCache_GetTypeDefinitionFromIndex(TypeDefinitionIndex index); + TypeDefinitionIndex MetadataCache_GetExportedTypeFromIndex(TypeDefinitionIndex index); + Il2CppGenericContainer const* MetadataCache_GetGenericContainerFromIndex(GenericContainerIndex index); + Il2CppGenericParameter const* MetadataCache_GetGenericParameterFromIndex(GenericParameterIndex index); + Il2CppClass* MetadataCache_GetNestedTypeFromIndex(NestedTypeIndex index); + TypeDefinitionIndex MetadataCache_GetIndexForTypeDefinition(Il2CppClass const* typeDefinition); + TypeDefinitionIndex MetadataCache_GetIndexForTypeDefinition(Il2CppTypeDefinition const* typeDefinition); + GenericParameterIndex MetadataCache_GetGenericParameterIndexFromParameter(Il2CppMetadataGenericParameterHandle handle); + Il2CppTypeDefinition const* MetadataCache_GetTypeDefinition(Il2CppClass* klass); + GenericParameterIndex MetadataCache_GetGenericContainerIndex(Il2CppClass* klass); + +#if !defined(UNITY_2019) && !defined(UNITY_2021) + Il2CppAssemblyVector* Assembly_GetAllAssemblies(); +#endif + + // Whether all of the il2cpp functions have been initialized or not + extern bool initialized; + // Initializes all of the IL2CPP functions via dlopen and dlsym for use. + void initialize(); +} + +#undef API_FUNC + +// Okay, so because we know that GC isn't overwriting heap (since it just calls the same shared calloc/malloc impls) +// we are confident that a new operator here isn't necessary or useful. +// We can safely say that the heap is shared properly, abeit without references. +// If references ARE desired, use one of the gc allocation functions in here expicitly. + +namespace i2c { + /// @brief Returns an allocated instance of the provided size that will not be written over by future GC allocations and holds references. + /// You MUST use the gc_free_specific function defined here to destroy it. + /// This function fallsback to calloc if no GC_Alloc or GC_Free implementations are found via xref/sigscan. + /// @param sz The size to allocate an instance of. + /// @return The allocated instance. + [[nodiscard]] void* gc_alloc_specific(size_t sz); + + /// @brief Deletes the provided allocated instance from the gc_alloc_specific function defined here. + /// Other pointers will cause undefined behavior. + /// This function will call GC_free if there is both a GC_Alloc and GC_Free implementation available, free otherwise. + /// @param sz The pointer to free explicitly. + /// @return The allocated instance. + void gc_free_specific(void* ptr) noexcept; + + /// @brief Reallocation implementation is equivalent to: alloc + free + /// @param ptr The pointer to resize. + /// @param new_size The new size of the memory. + /// @return The resized instance. + [[nodiscard]] void* gc_realloc_specific(void* ptr, size_t new_size); + + /// @brief EXTREMEMLY UNSAFE ALLOCATION! THIS SHOULD BE AVOIDED UNLESS YOU KNOW WHAT YOU ARE DOING! + /// This function allocates a GC-able object of the size provided by manipulating an existing Il2CppClass' instance_size. + /// This is VERY DANGEROUS (and NOT THREAD SAFE!) and may cause all sorts of race conditions. Use at your own risk. + /// @param size The size to allocate the unsafe object with. + /// @return The returned GC-allocated instance. + [[deprecated("DO NOT USE")]] void* __allocate_unsafe(std::size_t size); + + /// @brief Resolves the provided icall, throwing an std::runtime_error with backtrace information if failed. + /// Does NOT cache the resolved method pointer. + /// Also does NOT perform any type checking of parameters, so make sure you check your parameters and return types! + /// @tparam R The return type of the function to resolve + /// @tparam TArgs The arguments of the function to resolve + /// @param name The name of the icall to resolve + /// @return The resolved function pointer, will always be valid or throws an std::runtime_error. + template + function_ptr_t resolve_icall(std::string_view name) { + functions::initialize(); + if (auto out = reinterpret_cast>(functions::resolve_icall(name.data()))) { + return out; + } + throw std::runtime_error(fmt::format("Failed to resolve_icall for: {}!", name.data())); + } +} + +#pragma pack(pop) diff --git a/shared/arrayw.hpp b/shared/arrayw.hpp new file mode 100644 index 00000000..0960cea5 --- /dev/null +++ b/shared/arrayw.hpp @@ -0,0 +1,249 @@ +#pragma once + +#include "types.hpp" + +/// @brief An Array wrapper type that is responsible for holding an (ideally valid) pointer to an array on the GC heap. +/// Allows for C++ array semantics. Ex, [], begin(), end(), etc... +template +struct ArrayW { + using ptr = Array*; + using const_ptr = Array const*; + + using value = T; + using const_value = T const; + using pointer = T*; + using const_pointer = T const*; + using reference = T&; + using const_reference = T const&; + + using iterator = pointer; + using const_iterator = const_pointer; + + /// @brief Default constructor wraps a nullptr array + constexpr ArrayW() noexcept : val(nullptr) {} + /// @brief Constructs an ArrayW that wraps a null value + constexpr ArrayW(std::nullptr_t nptr) noexcept : val(nptr) {} + /// @brief Create an ArrayW from an arbitrary pointer + constexpr ArrayW(void* inst) noexcept : val(static_cast(inst)) {} + /// @brief Create an ArrayW from a pointer + constexpr ArrayW(ptr inst) noexcept : val(inst) {} + constexpr ArrayW(System::Array* inst) noexcept : val(inst) {} + + constexpr ArrayW(ArrayW const&) noexcept = default; + constexpr ArrayW(ArrayW&&) noexcept = default; + + // Empty with size + ArrayW(il2cpp_array_size_t size) { + i2c::functions::initialize(); + val = reinterpret_cast(i2c::functions::array_new(i2c::class_of(), size)); + } + // From initializer list + template + requires(std::is_convertible_v) + ArrayW(std::initializer_list vals) : ArrayW(vals.size()) { + std::copy(vals.begin(), vals.end(), begin()); + } + // From container + template + requires(std::is_convertible_v) + explicit ArrayW(std::span vals) : ArrayW(vals.size()) { + std::copy(vals.begin(), vals.end(), begin()); + } + // Convenience overload to convert vector to span + template + requires(std::is_convertible_v) + explicit ArrayW(std::vector const& vals) : ArrayW(std::span(vals)) {} + + constexpr bool operator==(ArrayW const&) const noexcept = default; + + constexpr ArrayW& operator=(ArrayW const&) noexcept = default; + constexpr ArrayW& operator=(ArrayW&&) noexcept = default; + + constexpr ArrayW& operator=(ptr rhs) { + this->val = rhs; + return *this; + } + template + requires(std::is_convertible_v) + constexpr ArrayW& operator=(ArrayW rhs) { + return operator=(static_cast(rhs.val)); + } + + constexpr void* convert() const noexcept { return const_cast(static_cast(val)); } + + operator std::span() { return ref_to(); } + operator std::span const() const { return ref_to(); } + + operator ptr() noexcept { return val; } + operator const_ptr() const noexcept { return val; } + + ptr operator->() noexcept { return val; } + const_ptr operator->() const noexcept { return val; } + + operator bool() const noexcept { return val != nullptr; } + + [[nodiscard]] il2cpp_array_size_t size() const noexcept { return val->max_length; } + bool empty() const noexcept { return size() == 0; } + + void assert_bounds(il2cpp_array_size_t i) const { + if (i < 0 || i >= size()) { + throw std::runtime_error(fmt::format("{} is out of bounds for array of length: {}", i, size())); + } + } + + iterator begin() { return val->_values; } + const_iterator begin() const { return val->_values; } + iterator end() { return val->_values + size(); } + const_iterator end() const { return val->_values + size(); } + + auto rbegin() { return std::reverse_iterator(end()); } + auto rbegin() const { return std::reverse_iterator(end()); } + auto rend() { return std::reverse_iterator(begin()); } + auto rend() const { return std::reverse_iterator(begin()); } + + reference operator[](il2cpp_array_size_t i) noexcept { return val->_values[i]; } + const_reference operator[](il2cpp_array_size_t i) const noexcept { return val->_values[i]; } + + /// @brief Get a given index, performs bound checking and throws std::runtime_error on failure. + /// @param i The index to get. + /// @return The reference to the item. + reference at(il2cpp_array_size_t i) { + assert_bounds(i); + return (*this)[i]; + } + /// @brief Get a given index, performs bound checking and throws std::runtime_error on failure. + /// @param i The index to get. + /// @return The const reference to the item. + const_reference at(il2cpp_array_size_t i) const { + assert_bounds(i); + return (*this)[i]; + } + + /// @brief Tries to get a given index, performs bound checking and returns a std::nullopt on failure. + /// @param i The index to get. + /// @return The reference_wrapper to the item, mostly considered to be a T&. + std::optional> try_get(il2cpp_array_size_t i) noexcept { + if (i < 0 || i >= size()) { + return std::nullopt; + } + return (*this)[i]; + } + /// @brief Tries to get a given index, performs bound checking and returns a std::nullopt on failure. + /// @param i The index to get. + /// @return The reference_wrapper to the item, mostly considered to be a const T&. + std::optional> try_get(il2cpp_array_size_t i) const noexcept { + if (i < 0 || i >= size()) { + return std::nullopt; + } + return (*this)[i]; + } + + iterator find(const_reference item) { return std::find(begin(), end(), item); } + const_iterator find(const_reference item) const { return std::find(begin(), end(), item); } + + auto rfind(const_reference item) { return std::find(rbegin(), rend(), item); } + auto rfind(const_reference item) const { return std::find(rbegin(), rend(), item); } + + iterator find_if(auto&& pred) { return std::find_if(begin(), end(), pred); } + const_iterator find_if(auto&& pred) const { return std::find_if(begin(), end(), pred); } + + auto rfind_if(auto&& pred) { return std::find_if(rbegin(), rend(), pred); } + auto rfind_if(auto&& pred) const { return std::find_if(rbegin(), rend(), pred); } + + reference front() { return (*this)[0]; } + const_reference front() const { return (*this)[0]; } + reference front(auto&& pred) { + auto itr = std::find_if(begin(), end(), pred); + return *itr; + } + const_reference front(auto&& pred) const { + auto itr = std::find_if(begin(), end(), pred); + return *itr; + } + + template + requires(std::is_default_constructible_v && std::is_copy_constructible_v) + value front_or_default(TArgs&&... args) const { + return front(std::forward(args...)).value_or(value{}); + } + + reference back() { return (*this)[size() - 1]; } + const_reference back() const { return (*this)[size() - 1]; } + reference back(auto&& pred) { + auto itr = std::find_if(rbegin(), rend(), pred); + return *itr; + } + const_reference back(auto&& pred) const { + auto itr = std::find_if(rbegin(), rend(), pred); + return *itr; + } + + template + requires(std::is_default_constructible_v && std::is_copy_constructible_v) + value back_or_default(TArgs&&... args) const { + return back(std::forward(args...)).value_or(value{}); + } + + bool contains(const_reference item) const { return find(item) != end(); } + + void copy_to(std::span destination, il2cpp_array_size_t index = 0) const { + if (index + size() > destination.size()) { + throw std::runtime_error("Destination span is too short for copy"); + } + std::copy_n(begin(), size(), std::next(destination.begin(), index)); + } + void copy_to(ArrayW destination, il2cpp_array_size_t index = 0) const { copy_to(destination.ref_to(), index); } + + long index_of(const_reference item) const { + auto itr = find(item); + if (itr == end()) { + return -1; + } + return std::distance(begin(), itr); + } + + /// @brief Provides a reference span of the held data within this array. The span should NOT outlive this instance. + /// @return The created span. + std::span ref_to() { return {val->_values, size()}; } + /// @brief Provides a reference span of the held data within this array. The span should NOT outlive this instance. + /// @return The created span. + std::span const ref_to() const { return {val->_values, size()}; } + +#ifdef HAS_CODEGEN + explicit constexpr operator ::System::Collections::ICollection*() noexcept { return static_cast<::System::Collections::ICollection*>(convert()); } + explicit constexpr operator ::System::Collections::IEnumerable*() noexcept { return static_cast<::System::Collections::IEnumerable*>(convert()); } + explicit constexpr operator ::System::Collections::IList*() noexcept { return static_cast<::System::Collections::IList*>(convert()); } + explicit constexpr operator ::System::Collections::IStructuralComparable*() noexcept { + return static_cast<::System::Collections::IStructuralComparable*>(convert()); + } + explicit constexpr operator ::System::Collections::IStructuralEquatable*() noexcept { + return static_cast<::System::Collections::IStructuralEquatable*>(convert()); + } + explicit constexpr operator ::System::ICloneable*() noexcept { return static_cast<::System::ICloneable*>(convert()); } +#endif + + private: + ptr val; +}; + +template +struct i2c::type_check::no_arg_class> { + static inline Il2CppClass* get() { return no_arg_class*>::get(); } +}; +MARK_GEN_REF_T(ArrayW); + +static_assert(sizeof(ArrayW) == sizeof(void*)); +static_assert(i2c::type_check::wrapper_ref_type>); + +template +struct fmt::is_range, Char> { + static constexpr bool value = false; +}; + +template +inline std::string format_as(ArrayW array) { + if (!array) { + return fmt::format("ArrayW<{}>(null)", i2c::type_name()); + } + return fmt::format("{}", fmt::join(array.begin(), array.end(), ", ")); +} diff --git a/shared/binary.hpp b/shared/binary.hpp new file mode 100644 index 00000000..62ed1539 --- /dev/null +++ b/shared/binary.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +namespace i2c::binary { + uintptr_t get_base(void* pc); + ptrdiff_t as_offset(void* pc); + + // Attempts to print what is stored at the given pointer. + // For a given pointer, it will scan 4 void*'s worth of bytes at the location pointed to. + // For each void* of bytes, it will print the raw bytes and interpretations of the bytes as ints and char*s. + // When the bytes look like a valid pointer, it will attempt to follow that pointer, increasing the indentation. + // It will not follow pointers that it has already analyzed as a result of the current call. + void analyze_bytes(void const* ptr); + + // Dumps the 'before' bytes before and 'after' bytes after the given pointer to log + void dump(int before, int after, void* ptr); + + uintptr_t get_real_offset(void const* offset); + uintptr_t base_addr(char const* soname); + + // Only wildcard is ? and ?? - both are handled the same way. They will skip exactly 1 byte (2 hex digits) + uintptr_t find_pattern(uintptr_t dw_addr, char const* pattern, uintptr_t dw_search_len); + // Same as find_pattern but will continue scanning to make sure your pattern is sufficiently specific. + // Each candidate will be logged. label should describe what you're looking for, like "Class::Init". + // Sets "multiple" iff multiple matches are found, and outputs a log warning message. + // Returns the first match, if any. + uintptr_t find_unique_pattern(bool& multiple, uintptr_t dw_addr, char const* pattern, uintptr_t dw_search_len, char const* label = 0); + + /// @brief Attempts to match the pattern provided with all regions of mapped read memory with the file provided + uintptr_t mapped_file_unique_pattern(bool& multiple, char const* pattern, char const* file, char const* label = 0); + + /// @brief Attempts to match the pattern provided with all regions of mapped read memory with the libil2cpp.so + uintptr_t libil2cpp_unique_pattern(bool& multiple, char const* pattern, char const* label = 0); + + /// @brief Attempts to match the pattern provided with all regions of mapped read memory with the libunity.so + uintptr_t libunity_unique_pattern(bool& multiple, char const* pattern, char const* label = 0); + + /// @brief Get the size of the libil2cpp.so file + uintptr_t get_libil2cpp_size(); + + /// @brief Get the build id from a file + std::optional get_build_id(std::string_view filename); +} diff --git a/shared/byref.hpp b/shared/byref.hpp new file mode 100644 index 00000000..c94cb0ec --- /dev/null +++ b/shared/byref.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "types.hpp" + +/// @brief Represents a byref parameter that wraps a reference. +/// This is REQUIRED for codegen invokes, as run_method can't tell the difference between a reference parameter and a byref on constexpr time. +template +requires(!std::is_reference_v) +struct by_ref { + constexpr by_ref(T& val) noexcept : ref(val) {} + explicit constexpr by_ref(void* val) noexcept : ref(*reinterpret_cast(val)) {} + + constexpr void* convert() const noexcept { return reinterpret_cast(&ref); } + + constexpr T& operator*() noexcept { return ref; } + constexpr T const& operator*() const noexcept { return ref; } + constexpr T* operator->() noexcept { return &ref; } + constexpr T const* operator->() const noexcept { return &ref; } + + by_ref& operator=(T const&& other) { + ref = other; + return *this; + } + + T& ref; +}; + +// We do not need a no_arg_class specialization for by_ref, since it will never get to that point. +template +struct BS_HOOK_HIDDEN ::i2c::type_check::no_arg_type> { + static inline Il2CppType const* get() { return &no_arg_class::get()->this_arg; } +}; +MARK_GEN_REF_T(by_ref); diff --git a/shared/callback.hpp b/shared/callback.hpp new file mode 100644 index 00000000..a5f83da0 --- /dev/null +++ b/shared/callback.hpp @@ -0,0 +1,213 @@ +#pragma once + +#include +#include + +namespace detail { + template