diff --git a/.editorconfig b/.editorconfig index 3478ef3a..b6c6c3fb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,10 +11,29 @@ insert_final_newline = true indent_style = space indent_size = 4 trim_trailing_whitespace = true +end_of_line = lf # Generated code [*{_AssemblyInfo.cs,.notsupported.cs,AsmOffsets.cs}] generated_code = true +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +end_of_line = lf +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion # C# files [*.cs] @@ -54,7 +73,7 @@ dotnet_style_predefined_type_for_member_access = true:suggestion # name all constant fields using PascalCase dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style dotnet_naming_symbols.constant_fields.applicable_kinds = field dotnet_naming_symbols.constant_fields.required_modifiers = const dotnet_naming_style.pascal_case_style.capitalization = pascal_case @@ -62,7 +81,7 @@ dotnet_naming_style.pascal_case_style.capitalization = pascal_case # static fields should have s_ prefix dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields -dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style +dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style dotnet_naming_symbols.static_fields.applicable_kinds = field dotnet_naming_symbols.static_fields.required_modifiers = static dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected @@ -72,7 +91,7 @@ dotnet_naming_style.static_prefix_style.capitalization = camel_case # internal and private fields should be _camelCase dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields -dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style dotnet_naming_symbols.private_internal_fields.applicable_kinds = field dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal dotnet_naming_style.camel_case_underscore_style.required_prefix = _ @@ -150,6 +169,11 @@ csharp_space_between_method_declaration_name_and_open_parenthesis = false csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false csharp_space_between_square_brackets = false +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion # C++ Files [*.{cpp,h,in}] diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 04038ad1..fb4a849a 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - global-json-file: global.json + dotnet-version: 10.0.x - name: Extract Release Version id: get_version diff --git a/.github/workflows/format_check.yml b/.github/workflows/format_check.yml index ac60880e..bacf0397 100644 --- a/.github/workflows/format_check.yml +++ b/.github/workflows/format_check.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - global-json-file: global.json + dotnet-version: 10.0.x - name: Check format run: | diff --git a/Directory.Build.props b/Directory.Build.props index c121dfab..20829406 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,7 +7,6 @@ latest disable true - Il2CppInterop latest logo_icon.png LGPL-3.0-only diff --git a/Documentation/Design/Arrays.md b/Documentation/Design/Arrays.md new file mode 100644 index 00000000..dbf4919d --- /dev/null +++ b/Documentation/Design/Arrays.md @@ -0,0 +1,10 @@ +# Arrays + +## Array Types + +Il2Cpp arrays are represented with a closed type hierarchy. + +* `Il2CppArrayBase` +* `Il2CppArrayBase` + +`Il2CppArrayBase` is the type used everywhere. `Il2CppArrayBase` is just for unstripping certain op codes. diff --git a/Documentation/Design/Attributes.md b/Documentation/Design/Attributes.md new file mode 100644 index 00000000..9dd34133 --- /dev/null +++ b/Documentation/Design/Attributes.md @@ -0,0 +1,3 @@ +# Attributes + +Attributes are not applied to types. diff --git a/Documentation/Design/CorLibTypes.md b/Documentation/Design/CorLibTypes.md new file mode 100644 index 00000000..b99950c4 --- /dev/null +++ b/Documentation/Design/CorLibTypes.md @@ -0,0 +1,187 @@ +# Core Library Types + +## Numeric Primitives, `char`, and `bool` + +These are blittable and should be directly reused, kind of. Signatures will use the Il2Cpp types, but implicit conversions will be used inside unstripped method bodies. + +```cs +public static Il2CppSystem.Int32 Add(Il2CppSystem.Int32 x, Il2CppSystem.Int32 y) +{ + // Conversions to Managed are applied on function entry + ref + int x2 = (int)x; + int y2 = (int)y; + + // All operations are done with managed types + int z = x2 + y2; + + // If an Il2Cpp primitive must be returned, conversion back is applied right before the return instruction. + return (Il2CppSystem.Int32)z; +} +``` + +This ensures that CIL opcodes function as expected. However, it can cause some complexities with instance methods on the corlib types. For that, unsafe helpers are used. + +```cs +public static Il2CppSystem.String Sum(Il2CppSystem.Int32 x, Il2CppSystem.Int32 y) +{ + int z = (int)x + (int)y; + return Unsafe.As(ref z).ToString(); +} +``` + +## `string` + +Strings should not be implicitly marshalled. In other words, `Il2CppSystem.String` should be used. + +## `object` + +For compatibility with interfaces and value types, this should be emitted as-is, despite the more complicated marshalling involved. + +## `Attribute` + +## `ValueType` and `Enum` + +Boxing to these types is invalid. + +## `Exception` + +## Counterargument to all of the above + +```cs +// Original code +public static void DoSomething(T value) where T : System.IConvertible +{ +} +public static void DoSomethingElse() +{ + DoSomething(System.StringComparison.Ordinal); + DoSomething(default); + DoSomething(default); +} + +// Unstripped code +public static void DoSomething() where T : Il2CppSystem.IConvertible +{ +} +public static void DoSomethingElse() +{ + // Which is correct? + // System.Enum fails the constraint check. + // Il2CppSystem.Enum makes the method unusable because boxed enums inherit from `System.Enum` not `Il2CppSystem.Enum`. + DoSomething(Il2CppSystem.StringComparison.Ordinal); + DoSomething(Il2CppSystem.StringComparison.Ordinal); + DoSomething(Cast(Il2CppSystem.StringComparison.Ordinal)); // Maybe this is the way it should be emitted? + + // If the type is a real enum, it fails the constraint check. + DoSomething(default); + + // Which is correct? + // int fails the constraint check, but is what we currently do. + DoSomething(default); + DoSomething(default); +} +private static Il2CppSystem.Enum Cast(object value) +{ + if (value is Il2CppSystem.Enum il2cppEnum) + { + return il2cppEnum; + } + if (value is System.Enum sysEnum) + { + throw new NotImplementedException("Cannot cast System.Enum to Il2CppSystem.Enum"); + } + throw new InvalidCastException("Cannot cast to Il2CppSystem.Enum"); +} + +// Proposal +public static void DoSomething() where T : Il2CppSystem.IConvertible +{ +} +public static void DoSomethingElse() +{ + DoSomething(Il2CppSystem.StringComparison.Ordinal); + DoSomething(default); + DoSomething(default); +} +namespace Il2CppSystem +{ + public interface IObject + { + // Instance members of Il2CppSystem.Object + } + public interface IValueType : IObject + { + // No members + } + public interface IEnum : IValueType, Il2CppSystem.IComparable, Il2CppSystem.IFormattable, Il2CppSystem.IConvertible + { + // Instance members of Il2CppSystem.Enum, except for interface implementations + } + public class Object : IObject + { + // A static method should be generated for each instance method + } + public abstract class ValueType : Object, IValueType + { + // A static method should be generated for each instance method + } + public abstract class Enum : ValueType, IEnum + { + // A static method should be generated for each instance method + } + public readonly struct StringComparison : IEnum // Maybe inject other interfaces like System.IEquatable<> for user convenience + { + // [System.Flags] // Only if the Il2Cpp enum has the Flags attribute + private enum __Internal + { + CurrentCulture = 0, + CurrentCultureIgnoreCase = 1, + InvariantCulture = 2, + InvariantCultureIgnoreCase = 3, + Ordinal = 4, + OrdinalIgnoreCase = 5 + } + + // Might make this `int` instead. The only reason to use `__Internal` is to have a more efficient ToString implementation. + private readonly __Internal value__; + + // Sacrifice the ability to use Il2Cpp enums in constants. + public static readonly StringComparison CurrentCulture = new StringComparison(__Internal.CurrentCulture); + public static readonly StringComparison CurrentCultureIgnoreCase = new StringComparison(__Internal.CurrentCultureIgnoreCase); + public static readonly StringComparison InvariantCulture = new StringComparison(__Internal.InvariantCulture); + public static readonly StringComparison InvariantCultureIgnoreCase = new StringComparison(__Internal.InvariantCultureIgnoreCase); + public static readonly StringComparison Ordinal = new StringComparison(__Internal.Ordinal); + public static readonly StringComparison OrdinalIgnoreCase = new StringComparison(__Internal.OrdinalIgnoreCase); + + private StringComparison(__Internal value) => value__ = value; + public StringComparison(int value) => value__ = unchecked((__Internal)value); + public static explicit operator int(StringComparison value) => unchecked((int)value.value__); + public static explicit operator StringComparison(int value) => new StringComparison(value); + + // Numerical operators like shift + + // Override ToString, GetHashCode, Equals, etc. + public override int GetHashCode() + { + // Use the static method from Il2CppSystem.Enum + // We need to ensure that behavior is consistent with the native method. + return Il2CppSystem.Enum.GetHashCode(this); + } + + static StringComparison() + { + // OriginalNameAttribute no longer needed. + } + } + public interface ICloneable : IObject, System.ICloneable + { + IObject Clone(); + + object System.ICloneable.Clone() + { + return Clone(); + } + } +} +``` diff --git a/Documentation/Design/Delegates.md b/Documentation/Design/Delegates.md new file mode 100644 index 00000000..90bedea7 --- /dev/null +++ b/Documentation/Design/Delegates.md @@ -0,0 +1,3 @@ +# Delegates + +All Il2Cpp delegates have a generated conversion to a system delegate, which might be generated if necessary. diff --git a/Documentation/Design/Enums.md b/Documentation/Design/Enums.md new file mode 100644 index 00000000..715ebaee --- /dev/null +++ b/Documentation/Design/Enums.md @@ -0,0 +1,29 @@ +# Enums + +Enums are converted to readonly structs. + +```cs +// Original +public enum ElectricityType +{ + Off = 0, + On = 1 +} + +// Converted +public struct ElectricityType : IObject, IValueType, IEnum, IComparable, IFormattable, IConvertible +{ + private readonly Int32 value__; + + public static readonly ElectricityType Off = (ElectricityType)0; + public static readonly ElectricityType On = (ElectricityType)1; +} +``` + +## Generic constraint + +`Il2CppSystem.Enum` should be replaced with `Il2CppSystem.IEnum` in generic constraints. + +## Interfaces + +Additional interfaces like `IEquatable<>`, `IEqualityOperators<,,>`, and `IBitwiseOperators<,,>` could be introduced for user convenience. diff --git a/Documentation/Design/Exceptions.md b/Documentation/Design/Exceptions.md new file mode 100644 index 00000000..11aac1e7 --- /dev/null +++ b/Documentation/Design/Exceptions.md @@ -0,0 +1,7 @@ +# Exceptions + +Il2Cpp exceptions each have a cooresponding system exception generated, making up a full hierarchy and enabling try catch support in unstripped code. + +## Runtime exceptions + +Unstripped code currently allows exceptions (such as `NullReferenceException`) to be thrown by the .NET runtime. Ideally, all such exceptions would be handled. diff --git a/Documentation/Design/Fields.md b/Documentation/Design/Fields.md new file mode 100644 index 00000000..aa93e62f --- /dev/null +++ b/Documentation/Design/Fields.md @@ -0,0 +1,19 @@ +# Fields + +## Generated code + +```cs +// Reference type +public FieldType fieldName +{ + get => throw null; + set => throw null; +} +public static ByReference UnsafeField_fieldName(Class obj) => throw null; +private static readonly IntPtr FieldInfoPtr_fieldIndex; + +// Value type +public FieldType fieldName; +public static ByReference UnsafeField_fieldName(ByRerefence obj) => throw null; +private static readonly IntPtr FieldInfoPtr_fieldIndex; +``` diff --git a/Documentation/Design/GenericMethods.md b/Documentation/Design/GenericMethods.md new file mode 100644 index 00000000..00c7e4a5 --- /dev/null +++ b/Documentation/Design/GenericMethods.md @@ -0,0 +1,22 @@ +# Generic Methods + +## Pointers + +Pointers are resolved as needed using Il2Cpp reflection. It's fine to do this initialization lazily because all generic type instances are known in advance. + +```cs +private static class MethodInfoStoreGeneric_Aggregate +{ + internal static System.IntPtr Pointer = IL2CPP.il2cpp_method_get_from_reflection(IL2CPP.Il2CppObjectBaseToPtrNotNull(new MethodInfo(IL2CPP.il2cpp_method_get_object(NativeMethodInfoPtr_Aggregate, Il2CppClassPointerStore.NativeClassPtr)) + .MakeGenericMethod(new Il2CppReferenceArray(new Type[2] + { + Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(Il2CppClassPointerStore.NativeClassPtr)), + Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(Il2CppClassPointerStore.NativeClassPtr)) + })))); +} +``` + +If a modder tries to use a generic method instantiation that doesn't exist, an exception is thrown. + +* +* diff --git a/Documentation/Design/GenericTypes.md b/Documentation/Design/GenericTypes.md new file mode 100644 index 00000000..04efa215 --- /dev/null +++ b/Documentation/Design/GenericTypes.md @@ -0,0 +1,10 @@ +# Generic Types + +```cs +Il2CppClassPointerStore>.NativeClassPtr = IL2CPP.il2cpp_class_from_type(Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(IL2CPP.GetIl2CppClass("mscorlib.dll", "System.Collections.Generic", "Dictionary`2"))).MakeGenericType(new Il2CppReferenceArray(new Type[2] +{ + Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(Il2CppClassPointerStore.NativeClassPtr)), + Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(Il2CppClassPointerStore.NativeClassPtr)) +})).TypeHandle.value); +IL2CPP.il2cpp_runtime_class_init(Il2CppClassPointerStore>.NativeClassPtr); +``` diff --git a/Documentation/Design/Initialization.md b/Documentation/Design/Initialization.md new file mode 100644 index 00000000..0333624d --- /dev/null +++ b/Documentation/Design/Initialization.md @@ -0,0 +1,11 @@ +# Il2Cpp Initialization + +Every Il2Cpp class has a generated `Il2CppInternals` class. + +## Module Initialization + +The static constructor for a type triggers its Il2Cpp initialization. + +## Global Initialization + +A global initialization assembly ensures that all types get initialized on startup. This is essential for ensuring that the object pool can create objects. diff --git a/Documentation/Design/Injection.md b/Documentation/Design/Injection.md new file mode 100644 index 00000000..fcf88489 --- /dev/null +++ b/Documentation/Design/Injection.md @@ -0,0 +1,113 @@ +# Injection + +## Major Flaw + +Current injection has a major flaw. It waits until the last minute to allocate a new type. This can cause problems with: + +* Cyclical field types (two classes have a field on each other) +* Self-referential field type +* Injected type used in base type or interfaces + +## Class + +```cs +// User code + +// This attribute is for source generation and has no runtime impact. +[InjectInIl2Cpp(Assembly = "OptionalAssemblyName")] // By default, types are injected into Assembly-CSharp +public partial class MyMonoBehaviour : MonoBehaviour +{ + [Il2CppField] + public static partial Int32 staticField { get; set; } + [Il2CppField] + public partial Boolean instanceField { get; set; } + + // Note: Injected classes cannot have uninjected instance fields and static fields are always ignored. + + [Il2CppMethod] + public static void DoSomething(String s) + { + } + + public static void DoAnything(string s) // If this wasn't hidden, it would be invalid because string is not a valid Il2Cpp parameter type. + { + } + + [Il2CppProperty] + public static Int32 MyProperty { get => default; set {} } +} + +// Source generated + +partial class MyMonoBehaviour +{ + static MyMonoBehaviour() + { + ClassInjector.RegisterTypeInIl2Cpp(); + } + + // Required + public MyMonoBehaviour(ObjectPointer ptr) : base(ptr) + { + } + + public static partial int staticField { get => Il2CppInternals.staticField.Get(); set => Il2CppInternals.staticField.Set(value); } + public partial bool instanceField { get => Il2CppInternals.instanceField.Get(this); set => Il2CppInternals.instanceField.Set(this, value); } +} +file static class Il2CppInternals // If the injected class is generic, this will also be generic. +{ + internal static readonly Il2CppStaticField staticField = new("staticField", GetClassPointer); + internal static readonly Il2CppField instanceField = new("instanceField", GetClassPointer); + private static IntPtr GetClassPointer() => Il2CppClassPointerStore.NativeClassPtr; +} +``` + +## Struct + +```cs +// User code + +[InjectInIl2Cpp] +public partial struct MyStruct +{ + [Il2CppField] + public static partial T staticField { get; set; } + + // [Il2CppField] is optional for struct instance fields. + public bool instanceField; +} + +// Source generated + +partial struct MyStruct : IIl2CppType> + where T : IIl2CppType +{ + static MyStruct() + { + ClassInjector.RegisterTypeInIl2Cpp()); + } + + public static partial T staticField { get => Il2CppInternals.staticField.Get(); set => Il2CppInternals.staticField.Set(value); } +} +file static class Il2CppInternals +{ + internal static readonly Il2CppStaticField staticField = new("staticField", GetClassPointer); + private static IntPtr GetClassPointer() => Il2CppClassPointerStore.NativeClassPtr; +} +``` + +## New Design + +### Design Constraints + +A class pointer needs registered in `Il2CppClassPointerStore` before anything else can reference this type. In other words, the class pointer needs registered before setting: + +* Declaring type +* Nested types +* Base type +* Interface implementations +* Types in member signatures + +In addition, we need to know the virtual table slot count when allocating data for the class because the virtual table is always positioned in method directly after the class struct. + +Ideally, no Il2Cpp APIs should be called while class structs are partially initialized. diff --git a/Documentation/Design/Interfaces.md b/Documentation/Design/Interfaces.md new file mode 100644 index 00000000..4900224f --- /dev/null +++ b/Documentation/Design/Interfaces.md @@ -0,0 +1,3 @@ +# Interfaces + +Interfaces are generated as actual interfaces. diff --git a/Documentation/Design/Marshalling.md b/Documentation/Design/Marshalling.md new file mode 100644 index 00000000..0560b8cc --- /dev/null +++ b/Documentation/Design/Marshalling.md @@ -0,0 +1,3 @@ +# Marshalling + +All Il2Cpp types implement the `Il2CppType<>` interface, which enables marshalling. diff --git a/Documentation/Design/MethodInvokers.md b/Documentation/Design/MethodInvokers.md new file mode 100644 index 00000000..f2a6e8d4 --- /dev/null +++ b/Documentation/Design/MethodInvokers.md @@ -0,0 +1,180 @@ +# Method Invokers + +Every normal (user-facing) Il2Cpp method has an unsafe implementation. + +* Implementation methods are static. +* Implementation methods are private, so they can only be called from within the type. +* If the Il2Cpp method is instance, the first parameter is the object. +* All other implementation parameter types are wrapped in `ByReference<>`, including parameters that are already `ByReference<>`. +* Similarly, local variable types are also wrapped in `ByReference<>`. +* The data for local variables is stack allocated at the beginning of the method. + +Some methods have an unsafe invoker. + +* Invoker methods are static. +* Invoker methods are public, so they can be called from other generated assemblies where desirable. + +This can be verbose, but it preserves semantics exactly for indirect memory access of reference types. + +## Example Output + +```cs +// Original +public string GetString(int param1, T param2, IInterface param3); + +// Il2Cpp +public String GetString(Int32 param1, T param2, IInterface param3) where T : IIl2CppType +{ + ByReference data_this = new ByReference(stackalloc byte[Il2CppTypeHelper.SizeOf()]); + + // Value type + data_this.CopyFrom(this); + + // Reference type + data_this.SetValue(this); + + String result = UnsafeInvoke_GetString(data_this, param1, param2, param3); + + // Value type + data_this.CopyTo(this); + + return result; +} + +// Invoker +public static String UnsafeInvoke_GetString(ByReference @this, Int32 param1, T param2, IInterface param3) where T : IIl2CppType +{ + // Param 1 + ByReference data_param1 = new ByReference(stackalloc byte[Il2CppTypeHelper.SizeOf()]); + data_param1.SetValue(param1); + + // Param 2 + ByReference data_param2 = new ByReference(stackalloc byte[Il2CppTypeHelper.SizeOf()]); + data_param2.SetValue(param2); + + // Param 3 + ByReference data_param3 = new ByReference(stackalloc byte[Il2CppTypeHelper.SizeOf()]); + data_param3.SetValue(param3); + + return UnsafeImplementation_GetString(@this, data_param1, data_param2, data_param3); +} +private static String UnsafeImplementation_GetString(ByReference @this, ByReference param1, ByReference param2, ByReference param3) where T : IIl2CppType +{ + IntPtr* arguments = stackalloc IntPtr[3]; + arguments[0] = RuntimeInvokeHelper.GetPointerForParameter(param1); + arguments[1] = RuntimeInvokeHelper.GetPointerForParameter(param2); + arguments[2] = RuntimeInvokeHelper.GetPointerForParameter(param3); + return RuntimeInvokeHelper.InvokeFunction(/* method info */, RuntimeInvokeHelper.GetPointerForThis(@this), (void**)arguments); +} + +// Unstripped Original +public static bool Compare(Self param1, Self param2) +{ + string local1 = param1.GetString(0, 0, null); + string local2 = param2.GetString(0, 0, null); + return local1 == local2; +} + +// Unstripped Invoker +public static Boolean Compare(Self param1, Self param2) +{ + ByReference data_param1 = new ByReference(stackalloc byte[Il2CppTypeHelper.SizeOf()]); + data_param1.SetValue(param1); + ByReference data_param2 = new ByReference(stackalloc byte[Il2CppTypeHelper.SizeOf()]); + data_param1.SetValue(param2); + + return UnsafeImplementation_Compare(data_param1, data_param2) +} +private static Boolean UnsafeImplementation_Compare(ByReference param1, ByReference param2) +{ + ByReference data_local1 = new ByReference(stackalloc byte[Il2CppTypeHelper.SizeOf()]); + ByReference data_local2 = new ByReference(stackalloc byte[Il2CppTypeHelper.SizeOf()]); + + data_local1.SetValue(param1.GetValue().GetString((Int32)0, (Int64)0, (IInterface?)null)); + data_local2.SetValue(param2.GetValue().GetString((Int32)0, (Int64)0, (IInterface?)null)); + + return data_local1.GetValue() == data_local2.GetValue(); +} +``` + +## Unstripped Instruction Translations + +### ldind_Ref + +* ldind_I +* `Il2CppObjectPool.Get` + +### stind_Ref + +* `Box` +* stind_I + +### ldarga + +* ldarg +* `ByReference.ToPointer()` + +### ldarg + +* ldarg +* `ByReference.GetValue()` + +### starg + +* ldarg +* `ByReference.SetValue` + +### ldloca + +* ldloc +* `ByReference.ToPointer()` + +### ldloc + +* ldloc +* `ByReference.GetValue()` + +### stloc + +* ldloc +* `ByReference.SetValue` + +### call / callvirt + +Since argument data is handled by the caller, additional locals need to be created to store those arguments. + +* Arguments are converted from Mono to Il2Cpp and popped off 1 by 1 into the data. +* Data variables are loaded onto the stack. +* Call the target's invoker method + +## `ref` parameters + +```cs +// Il2Cpp method +public ByReference Method(Int32 param_normal, [In] ByReference param_in, ByReference param_ref, [Out] ByReference param_out); + +// Injected overload +public ByReference Method(Int32 param_normal, in Int32 param_in, ref Int32 param_ref, out Int32 param_out) +{ + ByReference data_param_in = new ByReference(stackalloc byte[Il2CppTypeHelper.SizeOf()]); + data_param_in.CopyFrom(in param_in); + + ByReference data_param_ref = new ByReference(stackalloc byte[Il2CppTypeHelper.SizeOf()]); + data_param_ref.CopyFrom(ref param_ref); + + ByReference data_param_out = new ByReference(stackalloc byte[Il2CppTypeHelper.SizeOf()]); + data_param_out.Clear(); + + ByReference result = Method(param_normal, data_param_in, data_param_ref, data_param_out); + + data_param_ref.CopyTo(out param_ref); + data_param_out.CopyTo(out param_out); + + return result; +} +``` + +Return types are unchanged for two reasons: + +* There's no way to convert `ByReference` to a managed reference for all type parameters. +* Overloads can't differ only on the return type. diff --git a/Documentation/Design/NativeStaticConstructors.md b/Documentation/Design/NativeStaticConstructors.md new file mode 100644 index 00000000..447e39ef --- /dev/null +++ b/Documentation/Design/NativeStaticConstructors.md @@ -0,0 +1,7 @@ +# Native Static Constructors + +We give users a way to interact with static constructors. + +* They are renamed from `.cctor` to `StaticConstructor`. If this is unavailable, additional underscores are added. +* Like other methods, they are publicized. +* The `specialname` and `rtspecialname` attributes are removed. diff --git a/Documentation/Design/NestedTypes.md b/Documentation/Design/NestedTypes.md new file mode 100644 index 00000000..d7f6dbf8 --- /dev/null +++ b/Documentation/Design/NestedTypes.md @@ -0,0 +1,6 @@ +# Nested Types + +```cs +Il2CppClassPointerStore.NativeClassPtr = IL2CPP.GetIl2CppNestedType(Il2CppClassPointerStore.NativeClassPtr, "NestedClass"); +IL2CPP.il2cpp_runtime_class_init(Il2CppClassPointerStore.NativeClassPtr); +``` diff --git a/Documentation/Design/ReturnObjects.md b/Documentation/Design/ReturnObjects.md new file mode 100644 index 00000000..803f8511 --- /dev/null +++ b/Documentation/Design/ReturnObjects.md @@ -0,0 +1,34 @@ +# Return Objects + +## Value Types + +These should be returned "as-is". + +```cs +return *(bool*)IL2CPP.il2cpp_object_unbox(intPtr); +``` + +## Sealed Classes + +Since these can't be inherited from, we can call the pointer constructor directly. However, we may want to treat all classes the same, for simplicity. + +## Normal Classes + +During initialization, we cache delegates (or function pointers) for creating an object. + +```cs +private static object Create(ObjectPointer ptr) => new Class(ptr); +internal static void Initialize() +{ + Il2CppObjectPool.RegisterFactoryMethod(Il2CppClassPointerStore.NativeClassPtr, (Func)Create); +} +``` + +When returning from a method, we check the pool to see if the object already exists. If the object doesn't yet exist in managed code, we use the cached factory method to create a new one. + +```cs +// Maybe instead Il2CppObjectPool.Get should perform this null check? +return (ReturnType?)Il2CppObjectPool.Get(intPtr); +``` + +In the event that a factory method has not been registered, we throw an exception. If we pre-register factory methods for all generic type instances this should never happen. diff --git a/Documentation/Design/SimpleClasses.md b/Documentation/Design/SimpleClasses.md new file mode 100644 index 00000000..5d390006 --- /dev/null +++ b/Documentation/Design/SimpleClasses.md @@ -0,0 +1,59 @@ +# Simple Classes + +This document outlines the high level emission of simple classes. + +## Universal Base Type + +All reference classes inherit from `Il2CppObjectBase`. + +## Constructors + +All reference classes have an injected primary constructor. This carries the object pointer from derived classes to their base + +```cs +public class Derived(ObjectPointer ptr) : MonoBehaviour(ptr) +{ + static Derived() + { + Il2CppClassPointerStore.NativeClassPtr = IL2CPP.GetIl2CppClass("Assembly-CSharp.dll", "", "Derived"); + IL2CPP.il2cpp_runtime_class_init(Il2CppClassPointerStore.NativeClassPtr); + NativeMethodInfoPtr__ctor_Public_Void_0 = IL2CPP.GetIl2CppMethodByToken(Il2CppClassPointerStore.NativeClassPtr, 100666213); + } + + public unsafe Derived() : this(IL2CPP.il2cpp_object_new(Il2CppClassPointerStore.NativeClassPtr)) + { + Unsafe.SkipInit(out IntPtr intPtr2); + IntPtr intPtr = IL2CPP.il2cpp_runtime_invoke(NativeMethodInfoPtr__ctor_Public_Void_0, IL2CPP.Il2CppObjectBaseToPtrNotNull(this), (void**)null, ref intPtr2); + Il2CppException.RaiseExceptionIfNecessary(intPtr2); + } +} +``` + +A wrapper struct in the common library prevents conflicts. + +```cs +public readonly record struct ObjectPointer(IntPtr Value) +{ + public static implicit operator ObjectPointer(IntPtr value) => new(value); + public static implicit operator IntPtr(ObjectPointer value) => value.Value; +} +``` + +## Static classes + +We make the class abstract but not sealed and inject a private constructor. + +```cs +public abstract class StaticClass +{ + static StaticClass() + { + Il2CppClassPointerStore.NativeClassPtr = IL2CPP.GetIl2CppClass("Assembly-CSharp.dll", "", "StaticClass"); + IL2CPP.il2cpp_runtime_class_init(Il2CppClassPointerStore.NativeClassPtr); + } + + private StaticClass() + { + } +} +``` diff --git a/Documentation/Design/Unstripping.md b/Documentation/Design/Unstripping.md new file mode 100644 index 00000000..cb8e2d01 --- /dev/null +++ b/Documentation/Design/Unstripping.md @@ -0,0 +1,92 @@ +# Unstripping + +Certain assemblies can be unstripped because we have source code available, eg mscorlib and UnityEngine. + +## Injection + +Unstripped types and members get injected, similar to custom user classes. + +## Replace Native Implementation + +If the success rate for unstripping is high, and if a certain method can be unstripped, its implementation should be moved to managed land. This enables several things: + +* Transpilers (assuming that native is patched to use this implementation) +* Generic instances not present in the game + +However, unstripping semantics would have to be perfect. Little flaws are unacceptable. + +```cs +static IntPtr GeneratedDynamicMethod(IntPtr @this, FixedSizeStruct_8b param0) +{ + IntPtr result; + try + { + result = Il2CppTypeHelper.ReadFromPointer(&@this).ManagedMethod(Il2CppTypeHelper.ReadFromPointer(¶m0)).Box(); + } + catch (Il2CppException e) + { + result = default; + il2cpp_raise_exception(e.Il2CppObject.Pointer) + } + return result; +} +``` + +## ICalls + +Unity internal calls can be recovered. + +```cs +// Ignore the use of string, rather than Il2CppSystem.String +public static class SceneUtility +{ + private delegate IntPtr GetScenePathByBuildIndexDelegate(int buildIndex); + + private delegate int GetBuildIndexByScenePathDelegate(IntPtr scenePath); + + private static readonly GetScenePathByBuildIndexDelegate GetScenePathByBuildIndexDelegateField = IL2CPP.ResolveICall("UnityEngine.SceneManagement.SceneUtility::GetScenePathByBuildIndex"); + + private static readonly GetBuildIndexByScenePathDelegate GetBuildIndexByScenePathDelegateField = IL2CPP.ResolveICall("UnityEngine.SceneManagement.SceneUtility::GetBuildIndexByScenePath"); + + public static string GetScenePathByBuildIndex(int buildIndex) + { + return IL2CPP.Il2CppStringToManaged(GetScenePathByBuildIndexDelegateField(buildIndex)); + } + + public static int GetBuildIndexByScenePath(string scenePath) + { + return GetBuildIndexByScenePathDelegateField(IL2CPP.ManagedStringToIl2Cpp(scenePath)); + } +} +``` + +## callvirt + +```cs +public static T* ThrowIfNull(this T* pointer) where T : struct +{ + if (pointer is null) + { + throw new Il2CppSystem.NullReferenceException(); + } + return pointer; +} +public static T ThrowIfNull(this T obj) where T : class +{ + if (obj is null) + { + throw new Il2CppSystem.NullReferenceException(); + } + return obj; +} +``` + +Inserting this before any `callvirt` instruction ensures that correct semantics are maintained. + +## ldelem/stelem/ldlen + +Null checking and bounds checking are handled in the array helper methods. + +## Numeric op codes + +These are untyped, so some rudimentary analysis is needed to determine the type. diff --git a/Documentation/Design/ValueTypes.md b/Documentation/Design/ValueTypes.md new file mode 100644 index 00000000..4e0bc3ef --- /dev/null +++ b/Documentation/Design/ValueTypes.md @@ -0,0 +1,9 @@ +# Value Types + +## `readonly` + +Structs that were originally readonly no longer are. + +## `IValueType` + +All value types implement this interface. It replaces `ValueType` anywhere that would normally be used, like in generic constraints. diff --git a/Il2CppInterop.CLI/Il2CppInterop.CLI.csproj b/Il2CppInterop.CLI/Il2CppInterop.CLI.csproj index 1e5a507e..c4c0b04e 100644 --- a/Il2CppInterop.CLI/Il2CppInterop.CLI.csproj +++ b/Il2CppInterop.CLI/Il2CppInterop.CLI.csproj @@ -1,7 +1,7 @@ - net6.0 + net10.0 Exe true il2cppinterop @@ -15,14 +15,13 @@ - - + - - - + + + diff --git a/Il2CppInterop.CLI/Program.cs b/Il2CppInterop.CLI/Program.cs index 5796435c..a61d0019 100644 --- a/Il2CppInterop.CLI/Program.cs +++ b/Il2CppInterop.CLI/Program.cs @@ -1,316 +1,66 @@ -using System.CommandLine; -using System.CommandLine.NamingConventionBinder; -using System.Text.RegularExpressions; -using Il2CppInterop; -using Il2CppInterop.Common; +using Cpp2IL.Core.ProcessingLayers; using Il2CppInterop.Generator; -using Il2CppInterop.Generator.Runners; -using Il2CppInterop.StructGenerator; -using Microsoft.Extensions.Logging; -var command = new RootCommand { new Option("--verbose", "Produce more verbose output") }; -command.Description = "Generate Managed<->IL2CPP interop assemblies from Cpp2IL's output."; - -var generateCommand = new Command("generate") -{ - new Option("--input", "Directory with Il2CppDumper's dummy assemblies") {IsRequired = true} - .ExistingOnly(), - new Option("--output", "Directory to write generated assemblies to") {IsRequired = true}, - new Option("--unity", "Directory with original Unity assemblies for unstripping").ExistingOnly(), - new Option("--game-assembly", "Path to GameAssembly.dll. Used for certain analyses").ExistingOnly(), - new Option("--no-xref-cache", "Don't generate xref scanning cache. All scanning will be done at runtime."), - new Option("--add-prefix-to", - "Assemblies and namespaces starting with these will get an Il2Cpp prefix in generated assemblies. Allows multiple values. Obsolete."), - new Option("--dont-add-prefix-to", - "Assemblies and namespaces starting with these will not get an Il2Cpp prefix in generated assemblies. Allows multiple values."), - new Option("--use-opt-out-prefixing", - "Assemblies and namespaces will get an Il2Cpp prefix in generated assemblies unless otherwise specified. Obsolete."), - new Option("--deobf-map", - "Specifies a file specifying deobfuscation map for obfuscated types and members.").ExistingOnly(), - new Option("--deobf-uniq-chars", "How many characters per unique token to use during deobfuscation"), - new Option("--deobf-uniq-max", "How many maximum unique tokens per type are allowed during deobfuscation"), - new Option("--blacklist-assembly", "Don't write specified assembly to output. Allows multiple values."), - new Option("--obf-regex", - "Specifies a regex for obfuscated names. All types and members matching will be renamed."), - new Option("--passthrough-names", - "If specified, names will be copied from input assemblies as-is without renaming or deobfuscation."), - new Option("--no-parallel", "Disable parallel processing when writing assemblies. Use if you encounter stability issues when generating assemblies."), -}; -generateCommand.Description = "Generate wrapper assemblies that can be used to interop with Il2Cpp"; -generateCommand.Handler = CommandHandler.Create((GenerateCommandOptions opts) => -{ - var buildResult = opts.Build(); - Il2CppInteropGenerator.Create(buildResult.Options) - .AddLogger(buildResult.Logger) - .AddInteropAssemblyGenerator() - .Run(); -}); - -var deobfCommand = new Command("deobf"); -deobfCommand.Description = "Tools for deobfuscating assemblies"; -var deobfAnalyzeCommand = new Command("analyze") -{ - new Option("--input", "Directory of assemblies to deobfuscate") {IsRequired = true}.ExistingOnly(), - new Option("--add-prefix-to", - "Assemblies and namespaces starting with these will get an Il2Cpp prefix in generated assemblies. Allows multiple values. Obsolete."), - new Option("--dont-add-prefix-to", - "Assemblies and namespaces starting with these will not get an Il2Cpp prefix in generated assemblies. Allows multiple values."), - new Option("--use-opt-out-prefixing", - "Assemblies and namespaces will get an Il2Cpp prefix in generated assemblies unless otherwise specified. Obsolete.") -}; -deobfAnalyzeCommand.Description = - "Analyze deobfuscation performance with different parameter values. Will not generate assemblies."; - -deobfAnalyzeCommand.Handler = CommandHandler.Create((DeobfAnalyzeCommandOptions opts) => -{ - var buildResult = opts.Build(); - Il2CppInteropGenerator.Create(buildResult.Options) - .AddLogger(buildResult.Logger) - .AddDeobfuscationAnalyzer() - .Run(); -}); - -var deobfGenerateCommand = new Command("generate") -{ - new Option("--old-assemblies", "Directory with old unobfuscated assemblies") {IsRequired = true} - .ExistingOnly(), - new Option("--new-assemblies", "Directory to write obfuscation maps to") {IsRequired = true} - .ExistingOnly(), - new Option("--output", "Directory to write obfuscation maps to") {IsRequired = true}, - new Option("--include", - "Include these assemblies for deobfuscation map generation. If none are specified, all assemblies will be included."), - new Option("--deobf-uniq-chars", "How many characters per unique token to use during deobfuscation"), - new Option("--deobf-uniq-max", "How many maximum unique tokens per type are allowed during deobfuscation"), - new Option("--obf-regex", - "Specifies a regex for obfuscated names. All types and members matching will be renamed.") -}; -deobfGenerateCommand.Description = - "Generate a deobfuscation map from original unobfuscated assemblies. Will not generate assemblies."; -deobfGenerateCommand.Handler = CommandHandler.Create((DeobfGenerateCommandOptions opts) => -{ - var buildResult = opts.Build(); - Il2CppInteropGenerator.Create(buildResult.Options) - .AddLogger(buildResult.Logger) - .AddDeobfuscationMapGenerator() - .Run(); -}); - -var wrapperCommand = new Command("wrapper-gen") -{ - new Option("--headers", - "Directory that contains libil2cpp headers. Directory must contains subdirectories named after libil2cpp version.") - { - IsRequired = true - }.ExistingOnly(), - new Option("--output", "Directory to write managed struct wrapper sources to") {IsRequired = true} -}; -wrapperCommand.Description = "Tools for generating Il2Cpp struct wrappers from libi2lcpp source"; -wrapperCommand.Handler = CommandHandler.Create((WrapperCommandOptions opts) => -{ - Il2CppStructWrapperGenerator.Generate(opts.Build()); -}); - -deobfCommand.Add(deobfAnalyzeCommand); -deobfCommand.Add(deobfGenerateCommand); -command.Add(deobfCommand); -command.Add(generateCommand); -command.Add(wrapperCommand); - -return command.Invoke(args); - - -internal record CmdOptionsResult(GeneratorOptions Options, ILogger Logger); - -internal record BaseCmdOptions(bool Verbose) -{ - public virtual CmdOptionsResult Build() - { - var loggerFactory = LoggerFactory.Create(builder => - { - builder - .AddFilter("Il2CppInterop", Verbose ? LogLevel.Trace : LogLevel.Information) - .AddSimpleConsole(opt => { opt.SingleLine = true; }); - }); - - var logger = loggerFactory.CreateLogger("Il2CppInterop"); - - return new CmdOptionsResult(new GeneratorOptions { Verbose = Verbose }, logger); - } -} - -internal record WrapperCommandOptions(DirectoryInfo Headers, DirectoryInfo Output, bool Verbose) -{ - public virtual Il2CppStructWrapperGeneratorOptions Build() - { - var loggerFactory = LoggerFactory.Create(builder => - { - builder - .AddFilter("Il2CppInterop", Verbose ? LogLevel.Trace : LogLevel.Information) - .AddSimpleConsole(opt => { opt.SingleLine = true; }); - }); - var logger = loggerFactory.CreateLogger("Il2CppInterop"); - return new Il2CppStructWrapperGeneratorOptions(Headers.FullName, Output.FullName, logger); - } -} - -internal record GenerateCommandOptions( - bool Verbose, - DirectoryInfo Input, - DirectoryInfo Output, - DirectoryInfo? Unity, - FileInfo? GameAssembly, - bool NoXrefCache, - string[]? AddPrefixTo, - string[]? DontAddPrefixTo, - bool UseOptOutPrefixing, - FileInfo? DeobfMap, - int DeobfUniqChars, - int DeobfUniqMax, - string[]? BlacklistAssembly, - Regex? ObfRegex, - bool PassthroughNames, - bool NoParallel -) : BaseCmdOptions(Verbose) -{ - public override CmdOptionsResult Build() - { - var res = base.Build(); - var opts = res.Options; - - opts.Source = Utils.LoadAssembliesFrom(Input); - opts.OutputDir = Output.FullName; - opts.UnityBaseLibsDir = Unity?.FullName; - opts.GameAssemblyPath = GameAssembly?.FullName ?? ""; - opts.NoXrefCache = NoXrefCache; - opts.TypeDeobfuscationCharsPerUniquifier = DeobfUniqChars; - opts.TypeDeobfuscationMaxUniquifiers = DeobfUniqMax; - opts.AdditionalAssembliesBlacklist.AddRange(BlacklistAssembly ?? Array.Empty()); - opts.ObfuscatedNamesRegex = ObfRegex; - opts.PassthroughNames = PassthroughNames; - opts.Parallel = !NoParallel; - - if (AddPrefixTo is not null && AddPrefixTo.Length > 0) - { - if (DontAddPrefixTo is not null && DontAddPrefixTo.Length > 0) - { - throw new Exception("--add-prefix-to cannot be used with --dont-add-prefix-to"); - } - else if (UseOptOutPrefixing) - { - throw new Exception("--add-prefix-to cannot be used with --use-opt-out-prefixing"); - } - - opts.Il2CppPrefixMode = GeneratorOptions.PrefixMode.OptIn; - foreach (var s in AddPrefixTo) - { - opts.NamespacesAndAssembliesToPrefix.Add(s); - } - } - else if (DontAddPrefixTo is not null && DontAddPrefixTo.Length > 0) - { - opts.Il2CppPrefixMode = GeneratorOptions.PrefixMode.OptOut; - foreach (var s in DontAddPrefixTo) - { - opts.NamespacesAndAssembliesToNotPrefix.Add(s); - } - } - else if (UseOptOutPrefixing) - { - opts.Il2CppPrefixMode = GeneratorOptions.PrefixMode.OptOut; - } - - if (opts.Il2CppPrefixMode == GeneratorOptions.PrefixMode.OptIn) - { - res.Logger.LogWarning("Opt-In prefixing is obsolete and will be removed in a future version."); - } - - if (DeobfMap is not null) - { - opts.ReadRenameMap(DeobfMap.FullName); - } - - return res; - } -} - -internal record DeobfAnalyzeCommandOptions( - bool Verbose, - string[]? AddPrefixTo, - string[]? DontAddPrefixTo, - bool UseOptOutPrefixing, - DirectoryInfo Input -) : BaseCmdOptions(Verbose) -{ - public override CmdOptionsResult Build() - { - var res = base.Build(); - var opts = res.Options; - - opts.Source = Utils.LoadAssembliesFrom(Input); - - if (AddPrefixTo is not null && AddPrefixTo.Length > 0) - { - if (DontAddPrefixTo is not null && DontAddPrefixTo.Length > 0) - { - throw new Exception("--add-prefix-to cannot be used with --dont-add-prefix-to"); - } - else if (UseOptOutPrefixing) - { - throw new Exception("--add-prefix-to cannot be used with --use-opt-out-prefixing"); - } - - opts.Il2CppPrefixMode = GeneratorOptions.PrefixMode.OptIn; - foreach (var s in AddPrefixTo) - { - opts.NamespacesAndAssembliesToPrefix.Add(s); - } - } - else if (DontAddPrefixTo is not null && DontAddPrefixTo.Length > 0) - { - opts.Il2CppPrefixMode = GeneratorOptions.PrefixMode.OptOut; - foreach (var s in DontAddPrefixTo) - { - opts.NamespacesAndAssembliesToNotPrefix.Add(s); - } - } - else if (UseOptOutPrefixing) - { - opts.Il2CppPrefixMode = GeneratorOptions.PrefixMode.OptOut; - } - - if (opts.Il2CppPrefixMode == GeneratorOptions.PrefixMode.OptIn) - { - res.Logger.LogWarning("Opt-In prefixing is obsolete and will be removed in a future version."); - } - - return res; - } -} - -internal record DeobfGenerateCommandOptions( - bool Verbose, - DirectoryInfo OldAssemblies, - DirectoryInfo NewAssemblies, - DirectoryInfo Output, - string[]? Include, - int DeobfUniqChars, - int DeobfUniqMax, - Regex? ObfRegex -) : BaseCmdOptions(Verbose) -{ - public override CmdOptionsResult Build() - { - var res = base.Build(); - var opts = res.Options; - - opts.Source = Utils.LoadAssembliesFrom(OldAssemblies); - opts.OutputDir = Output.FullName; - opts.DeobfuscationNewAssembliesPath = NewAssemblies.FullName; - opts.DeobfuscationGenerationAssemblies.AddRange(Include ?? Array.Empty()); - opts.TypeDeobfuscationCharsPerUniquifier = DeobfUniqChars; - opts.TypeDeobfuscationMaxUniquifiers = DeobfUniqMax; - opts.ObfuscatedNamesRegex = ObfRegex; - - return res; - } -} +string gameExePath = args[0]; +string outputFolder = args[1]; +string unstripDirectory = args[2]; + +// Unstrip directory needs to contain all files recursively contained in these directories: +// \Editor\Data\MonoBleedingEdge\lib\mono\unityaot-win32 +// \Editor\Data\PlaybackEngines\windowsstandalonesupport\Variations\win64_player_nondevelopment_il2cpp\Data\Managed + +Il2CppGame.Process( + gameExePath, + outputFolder, + new AsmResolverDllOutputFormatBinding(), + [ + new AttributeAnalysisProcessingLayer(), // Needed for recovery of unmanaged constraints + //new StableRenamingProcessingLayer(), + new UnstripProcessingLayer(), // Can be disabled for performance during development + new InterfaceOverrideProcessingLayer(), + new InvalidFieldRemovalProcessingLayer(), + new Il2CppRenamingProcessingLayer(), + new CleanRenamingProcessingLayer(), + new ConflictRenamingProcessingLayer(), + new AttributesOverrideProcessingLayer(), + new PublicizerProcessingLayer(), + new MscorlibAssemblyInjectionProcessingLayer(), + new KnownTypeAssignmentProcessingLayer(), + new ReferenceAssemblyInjectionProcessingLayer(), + new InvisibleInterfaceProcessingLayer(), + new ObjectInterfaceProcessingLayer(), + new ReferenceReplacementProcessingLayer(), + new AttributeRemovalProcessingLayer(), + new IndexerAttributeInjectionProcessingLayer(), + new PointerConstructorProcessingLayer(), + new Il2CppTypeConstraintProcessingLayer(), + new InitializationClassProcessingLayer(), + new MarshallingProcessingLayer(), + new PrimitiveImplicitConversionProcessingLayer(), + new EnumProcessingLayer(), + new ObjectOverridesProcessingLayer(), + new ObjectInternalsProcessingLayer(), + new MemberAttributeProcessingLayer(), + new FieldAccessorProcessingLayer(), + new EventProcessingLayer(), + new ExceptionHierarchyProcessingLayer(), + new MethodInvokerProcessingLayer(), + new MethodBodyTranslationProcessingLayer(), + new NativeMethodBodyProcessingLayer(), + new DelegateConversionProcessingLayer(), + new ByRefParameterOverloadProcessingLayer(), + new UserFriendlyOverloadProcessingLayer(), + // new SystemInterfaceRecoveryProcessingLayer(), // Should handle INotifyCompletion, IEnumerable, IEquatable, etc + new ConstantInitializationProcessingLayer(), + new StaticConstructorProcessingLayer(), + ], + [new(UnstripBaseProcessingLayer.DirectoryKey, unstripDirectory)]); +Console.WriteLine("Done!"); + +/* +Todo +- System interfaces +- overloads with delegates, primitives +- Source generation for user-injected types +- Add attributes to "Unsafe" methods so that users cannot see them +*/ diff --git a/Il2CppInterop.CLI/Utils.cs b/Il2CppInterop.CLI/Utils.cs deleted file mode 100644 index 6fa7fa1e..00000000 --- a/Il2CppInterop.CLI/Utils.cs +++ /dev/null @@ -1,14 +0,0 @@ -using AsmResolver.DotNet; - -namespace Il2CppInterop; - -internal static class Utils -{ - public static List LoadAssembliesFrom(DirectoryInfo directoryInfo) - { - var inputAssemblies = directoryInfo.EnumerateFiles("*.dll").Select(f => AssemblyDefinition.FromFile( - f.FullName)).ToList(); - - return inputAssemblies; - } -} diff --git a/Il2CppInterop.Common/AssemblyInfo.cs b/Il2CppInterop.Common/AssemblyInfo.cs deleted file mode 100644 index 9fbd1411..00000000 --- a/Il2CppInterop.Common/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Il2CppInterop.Runtime")] -[assembly: InternalsVisibleTo("Il2CppInterop.Generator")] -[assembly: InternalsVisibleTo("Il2CppInterop.HarmonySupport")] diff --git a/Il2CppInterop.Common/Attributes/Il2CppEventAttribute.cs b/Il2CppInterop.Common/Attributes/Il2CppEventAttribute.cs new file mode 100644 index 00000000..e4fb36a9 --- /dev/null +++ b/Il2CppInterop.Common/Attributes/Il2CppEventAttribute.cs @@ -0,0 +1,9 @@ +namespace Il2CppInterop.Common.Attributes; + +/// +/// Indicates that the attributed method is associated with an event in the IL2CPP runtime. +/// +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] +public sealed class Il2CppEventAttribute : Il2CppMemberAttribute +{ +} diff --git a/Il2CppInterop.Common/Attributes/Il2CppFieldAttribute.cs b/Il2CppInterop.Common/Attributes/Il2CppFieldAttribute.cs new file mode 100644 index 00000000..e9cdf889 --- /dev/null +++ b/Il2CppInterop.Common/Attributes/Il2CppFieldAttribute.cs @@ -0,0 +1,10 @@ +namespace Il2CppInterop.Common.Attributes; + +/// +/// Indicates that the attributed field is associated with a field in the IL2CPP runtime. +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] +public sealed class Il2CppFieldAttribute : Il2CppMemberAttribute +{ + public int Index { get; init; } = -1; +} diff --git a/Il2CppInterop.Common/Attributes/Il2CppFinalizerAttribute.cs b/Il2CppInterop.Common/Attributes/Il2CppFinalizerAttribute.cs new file mode 100644 index 00000000..c6ed8772 --- /dev/null +++ b/Il2CppInterop.Common/Attributes/Il2CppFinalizerAttribute.cs @@ -0,0 +1,9 @@ +namespace Il2CppInterop.Common.Attributes; + +/// +/// Indicates that the attributed method should be called in the source generated finalizer for an injected class. +/// +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] +public sealed class Il2CppFinalizerAttribute : Attribute +{ +} diff --git a/Il2CppInterop.Common/Attributes/Il2CppMemberAttribute.cs b/Il2CppInterop.Common/Attributes/Il2CppMemberAttribute.cs new file mode 100644 index 00000000..80151a27 --- /dev/null +++ b/Il2CppInterop.Common/Attributes/Il2CppMemberAttribute.cs @@ -0,0 +1,9 @@ +namespace Il2CppInterop.Common.Attributes; + +public abstract class Il2CppMemberAttribute : Attribute +{ + public string? Name { get; init; } + private protected Il2CppMemberAttribute() + { + } +} diff --git a/Il2CppInterop.Common/Attributes/Il2CppMethodAttribute.cs b/Il2CppInterop.Common/Attributes/Il2CppMethodAttribute.cs new file mode 100644 index 00000000..d140b768 --- /dev/null +++ b/Il2CppInterop.Common/Attributes/Il2CppMethodAttribute.cs @@ -0,0 +1,10 @@ +namespace Il2CppInterop.Common.Attributes; + +/// +/// Indicates that the attributed method is associated with a method in the IL2CPP runtime. +/// +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] +public sealed class Il2CppMethodAttribute : Il2CppMemberAttribute +{ + public int Index { get; init; } = -1; +} diff --git a/Il2CppInterop.Common/Attributes/Il2CppPropertyAttribute.cs b/Il2CppInterop.Common/Attributes/Il2CppPropertyAttribute.cs new file mode 100644 index 00000000..aca20c33 --- /dev/null +++ b/Il2CppInterop.Common/Attributes/Il2CppPropertyAttribute.cs @@ -0,0 +1,12 @@ +namespace Il2CppInterop.Common.Attributes; + +/// +/// Indicates that the attributed property is associated with a property in the IL2CPP runtime. +/// +/// +/// Marking the accessor methods with is optional. +/// +[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] +public sealed class Il2CppPropertyAttribute : Il2CppMemberAttribute +{ +} diff --git a/Il2CppInterop.Common/Attributes/Il2CppTypeAttribute.cs b/Il2CppInterop.Common/Attributes/Il2CppTypeAttribute.cs new file mode 100644 index 00000000..b4ca89a2 --- /dev/null +++ b/Il2CppInterop.Common/Attributes/Il2CppTypeAttribute.cs @@ -0,0 +1,7 @@ +namespace Il2CppInterop.Common.Attributes; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] +public sealed class Il2CppTypeAttribute(Type internals) : Attribute +{ + public Type Internals { get; } = internals; +} diff --git a/Il2CppInterop.Common/Attributes/InjectedTypeAttribute.cs b/Il2CppInterop.Common/Attributes/InjectedTypeAttribute.cs new file mode 100644 index 00000000..c2989a5d --- /dev/null +++ b/Il2CppInterop.Common/Attributes/InjectedTypeAttribute.cs @@ -0,0 +1,18 @@ +namespace Il2CppInterop.Common.Attributes; + +/// +/// Indicates that a type should be source generated with Il2Cpp injection code. +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] +public sealed class InjectedTypeAttribute : Attribute +{ + /// + /// The name of the type. If not specified, it will be inferred from the managed type's name. + /// + public string? Name { get; init; } + /// + /// The file name of the assembly that the type is defined in. + /// If not specified, the assembly name will be inferred from the type's containing assembly. + /// + public string? Assembly { get; init; } +} diff --git a/Il2CppInterop.Common/Attributes/ManagedFieldAttribute.cs b/Il2CppInterop.Common/Attributes/ManagedFieldAttribute.cs new file mode 100644 index 00000000..3d5b3ff6 --- /dev/null +++ b/Il2CppInterop.Common/Attributes/ManagedFieldAttribute.cs @@ -0,0 +1,14 @@ +namespace Il2CppInterop.Common.Attributes; + +/// +/// Indicates that the attributed partial property is an injected field with an uninjected reference type. +/// Code will be source generated to properly reference it using a managed GCHandle, which is freed when the object is collected by the Il2Cpp GC. +/// +/// +/// Storing an Il2Cpp object in this field can cause memory leaks in some cases. +/// +[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] +public sealed class ManagedFieldAttribute : Attribute +{ + public string? Name { get; init; } +} diff --git a/Il2CppInterop.Common/Attributes/ObfuscatedNameAttribute.cs b/Il2CppInterop.Common/Attributes/ObfuscatedNameAttribute.cs deleted file mode 100644 index a0b6c54f..00000000 --- a/Il2CppInterop.Common/Attributes/ObfuscatedNameAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Il2CppInterop.Common.Attributes; - -[AttributeUsage(AttributeTargets.All, Inherited = false)] -public class ObfuscatedNameAttribute : Attribute -{ - public readonly string ObfuscatedName; - - public ObfuscatedNameAttribute(string obfuscatedName) - { - ObfuscatedName = obfuscatedName; - } -} diff --git a/Il2CppInterop.Common/Attributes/OriginalNameAttribute.cs b/Il2CppInterop.Common/Attributes/OriginalNameAttribute.cs deleted file mode 100644 index ff798fca..00000000 --- a/Il2CppInterop.Common/Attributes/OriginalNameAttribute.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Il2CppInterop.Common.Attributes; - -// This attribute is only applied to enums so the class pointer -// can be resolved correctly at runtime -// https://github.com/BepInEx/Il2CppInterop/issues/66 -[AttributeUsage(AttributeTargets.Enum, Inherited = false)] -public class OriginalNameAttribute : Attribute -{ - public readonly string AssemblyName; - public readonly string Namespace; - public readonly string Name; - - public OriginalNameAttribute(string assemblyName, string @namespace, string name) - { - AssemblyName = assemblyName; - Namespace = @namespace; - Name = name; - } -} diff --git a/Il2CppInterop.Common/Host/BaseHost.cs b/Il2CppInterop.Common/Host/BaseHost.cs index eee92bc2..f95652d3 100644 --- a/Il2CppInterop.Common/Host/BaseHost.cs +++ b/Il2CppInterop.Common/Host/BaseHost.cs @@ -2,7 +2,7 @@ public abstract class BaseHost : IDisposable { - private static BaseHost s_instance; + private static BaseHost? s_instance; protected static T GetInstance() where T : BaseHost { diff --git a/Il2CppInterop.Common/IIl2CppType.cs b/Il2CppInterop.Common/IIl2CppType.cs new file mode 100644 index 00000000..63688169 --- /dev/null +++ b/Il2CppInterop.Common/IIl2CppType.cs @@ -0,0 +1,48 @@ +namespace Il2CppInterop.Common; + +public interface IIl2CppType +{ + IntPtr ObjectClass { get; } +} +public interface IIl2CppType : IIl2CppType where TSelf : notnull, IIl2CppType +{ + /// + /// The native size of the type in bytes + /// + static abstract int Size { get; } + /// + /// The file name of the assembly that the type is defined in + /// + static virtual string AssemblyName + { + get + { + var result = typeof(TSelf).Assembly.GetName().Name; + return string.IsNullOrEmpty(result) + ? "Assembly-CSharp.dll" + : result.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) + ? result + : $"{result}.dll"; + } + } + /// + /// The namespace of type + /// + static virtual string Namespace => typeof(TSelf).Namespace ?? ""; + /// + /// The class name of the type + /// + static virtual string Name => typeof(TSelf).Name; + /// + /// Writes the native representation of the value to the provided span. The span is required to be at least bytes long. + /// + /// The value to write. + /// The span to write the value to. + static abstract void WriteToSpan(TSelf? value, Span span); + /// + /// Reads the native representation of the value from the provided span. The span is required to be at least bytes long. + /// + /// The span to read the value from. + /// The value read from the span. + static abstract TSelf? ReadFromSpan(ReadOnlySpan span); +} diff --git a/Il2CppInterop.Common/Il2CppInterop.Common.csproj b/Il2CppInterop.Common/Il2CppInterop.Common.csproj index d9320b4d..e703cb0f 100644 --- a/Il2CppInterop.Common/Il2CppInterop.Common.csproj +++ b/Il2CppInterop.Common/Il2CppInterop.Common.csproj @@ -1,16 +1,25 @@ - netstandard2.0 + net10.0 BepInEx, knah et al. Common API for Il2CppInterop packages - Il2CppInterop.Common + preview + enable - - - + + <_Parameter1>Il2CppInterop.HarmonySupport + + + <_Parameter1>Il2CppInterop.Runtime + + + + + + diff --git a/Il2CppInterop.Common/Il2CppInteropUtils.cs b/Il2CppInterop.Common/Il2CppInteropUtils.cs index 99948d3c..956373d4 100644 --- a/Il2CppInterop.Common/Il2CppInteropUtils.cs +++ b/Il2CppInterop.Common/Il2CppInteropUtils.cs @@ -1,38 +1,61 @@ using System.Reflection; -using System.Reflection.Emit; +using Il2CppInterop.Common.Attributes; namespace Il2CppInterop.Common; public static class Il2CppInteropUtils { - private static FieldInfo? GetFieldInfoFromMethod(MethodBase method, string prefix) + private static FieldInfo? GetFieldInfo(Type declaringType, string prefix, int index) { - var body = method.GetMethodBody(); - if (body == null) throw new ArgumentException("Target method may not be abstract"); - var methodModule = method.DeclaringType.Assembly.Modules.Single(); - foreach (var (opCode, opArg) in MiniIlParser.Decode(body.GetILAsByteArray())) + if (index < 0) + return null; + + var internalsType = ResolveInternals(declaringType); + if (internalsType == null) + return null; + + return internalsType.GetField($"{prefix}{index}", BindingFlags.Static | BindingFlags.NonPublic); + + static Type? ResolveInternals(Type declaringType) { - if (opCode != OpCodes.Ldsfld) continue; + var attr = declaringType.GetCustomAttribute(); + if (attr == null) + return null; - var fieldInfo = methodModule.ResolveField((int)opArg, method.DeclaringType.GenericTypeArguments, method.GetGenericArguments()); - if (fieldInfo?.FieldType != typeof(IntPtr)) continue; + var internals = attr.Internals; - if (fieldInfo.Name.StartsWith(prefix)) return fieldInfo; + if (internals.IsGenericTypeDefinition && declaringType.IsConstructedGenericType) + { + internals = internals.MakeGenericType(declaringType.GetGenericArguments()); + } - // Resolve generic method info pointer fields - if (method.IsGenericMethod && fieldInfo.DeclaringType.Name.StartsWith("MethodInfoStoreGeneric_") && fieldInfo.Name == "Pointer") return fieldInfo; + return internals; } - - return null; } - public static FieldInfo GetIl2CppMethodInfoPointerFieldForGeneratedMethod(MethodBase method) + public static FieldInfo? GetIl2CppMethodInfoPointerFieldForGeneratedMethod(MethodBase method) { - return GetFieldInfoFromMethod(method, "NativeMethodInfoPtr_"); + var declaringType = method.DeclaringType; + if (declaringType == null) + return null; + + var index = method.GetCustomAttribute()?.Index ?? -1; + + return GetFieldInfo(declaringType, "MethodInfoPtr_", index); } - public static FieldInfo GetIl2CppFieldInfoPointerFieldForGeneratedFieldAccessor(MethodBase method) + public static FieldInfo? GetIl2CppFieldInfoPointerFieldForGeneratedFieldAccessor(MethodBase method) { - return GetFieldInfoFromMethod(method, "NativeFieldInfoPtr_"); + var declaringType = method.DeclaringType; + if (declaringType == null) + return null; + + var prop = declaringType + .GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) + .FirstOrDefault(p => p.GetMethod == method || p.SetMethod == method); + + var index = prop?.GetCustomAttribute()?.Index ?? -1; + + return GetFieldInfo(declaringType, "FieldInfoPtr_", index); } } diff --git a/Il2CppInterop.Common/Maps/MethodAddressToTokenMap.cs b/Il2CppInterop.Common/Maps/MethodAddressToTokenMap.cs index b993e551..46043cac 100644 --- a/Il2CppInterop.Common/Maps/MethodAddressToTokenMap.cs +++ b/Il2CppInterop.Common/Maps/MethodAddressToTokenMap.cs @@ -1,5 +1,3 @@ -#nullable enable - using System.Reflection; namespace Il2CppInterop.Common.Maps; diff --git a/Il2CppInterop.Common/Maps/MethodAddressToTokenMapBase.cs b/Il2CppInterop.Common/Maps/MethodAddressToTokenMapBase.cs index b57b66f8..ca95c7eb 100644 --- a/Il2CppInterop.Common/Maps/MethodAddressToTokenMapBase.cs +++ b/Il2CppInterop.Common/Maps/MethodAddressToTokenMapBase.cs @@ -1,5 +1,3 @@ -#nullable enable - using System.Collections; using System.IO.MemoryMappedFiles; using System.Runtime.InteropServices; diff --git a/Il2CppInterop.Common/MiniILParser.cs b/Il2CppInterop.Common/MiniILParser.cs deleted file mode 100644 index 40a1f2dc..00000000 --- a/Il2CppInterop.Common/MiniILParser.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System.Reflection; -using System.Reflection.Emit; - -namespace Il2CppInterop.Common; - -internal static class MiniIlParser -{ - private static readonly Dictionary OpCodesMap = new(); - private static readonly HashSet PrefixCodes = new(); - - static MiniIlParser() - { - foreach (var fieldInfo in typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public)) - { - if (fieldInfo.FieldType != typeof(OpCode)) continue; - - var fieldValue = (OpCode)fieldInfo.GetValue(null); - OpCodesMap[fieldValue.Value] = fieldValue; - if ((ushort)fieldValue.Value > byte.MaxValue) - PrefixCodes.Add((byte)((ushort)fieldValue.Value >> 8)); - } - } - - public static IEnumerable<(OpCode, long)> Decode(byte[] ilBytes) - { - var index = 0; - while (index < ilBytes.Length) - { - short currentOp = ilBytes[index++]; - if (PrefixCodes.Contains(currentOp)) - currentOp = (short)((ushort)(currentOp << 8) | ilBytes[index++]); - - if (!OpCodesMap.TryGetValue(currentOp, out var opCode)) - throw new NotSupportedException($"Unknown opcode {currentOp} encountered"); - - var argLength = GetOperandSize(opCode); - - switch (argLength) - { - case 0: - yield return (opCode, 0); - break; - case 1: - yield return (opCode, ilBytes[index]); - break; - case 2: - yield return (opCode, BitConverter.ToInt16(ilBytes, index)); - break; - case 4: - yield return (opCode, BitConverter.ToInt32(ilBytes, index)); - break; - case 8: - yield return (opCode, BitConverter.ToInt64(ilBytes, index)); - break; - default: - throw new NotSupportedException($"Unsupported opcode argument length {argLength}"); - } - - index += argLength; - } - } - - private static int GetOperandSize(OpCode opCode) - { - switch (opCode.OperandType) - { - case OperandType.InlineField: - case OperandType.InlineBrTarget: - case OperandType.InlineI: - case OperandType.InlineMethod: - case OperandType.InlineSig: - case OperandType.InlineString: - case OperandType.InlineSwitch: - case OperandType.InlineTok: - case OperandType.InlineType: - case OperandType.ShortInlineR: - return 4; - case OperandType.InlineI8: - case OperandType.InlineR: - return 8; - case OperandType.InlineNone: - return 0; - case OperandType.InlineVar: - return 2; - case OperandType.ShortInlineBrTarget: - case OperandType.ShortInlineVar: - case OperandType.ShortInlineI: - return 1; - default: - throw new ArgumentOutOfRangeException(); - } - } -} diff --git a/Il2CppInterop.Common/ObjectPointer.cs b/Il2CppInterop.Common/ObjectPointer.cs new file mode 100644 index 00000000..03544bbf --- /dev/null +++ b/Il2CppInterop.Common/ObjectPointer.cs @@ -0,0 +1,12 @@ +namespace Il2CppInterop.Common; + +public readonly record struct ObjectPointer(IntPtr Value) +{ + public static explicit operator ObjectPointer(IntPtr value) => new(value); + public static explicit operator IntPtr(ObjectPointer value) => value.Value; + + public static unsafe explicit operator ObjectPointer(void* value) => new((IntPtr)value); + public static unsafe explicit operator void*(ObjectPointer value) => (void*)value.Value; + + public static ObjectPointer Null => new(IntPtr.Zero); +} diff --git a/Il2CppInterop.Common/XrefScans/GeneratedDatabasesUtil.cs b/Il2CppInterop.Common/XrefScans/GeneratedDatabasesUtil.cs index f8538077..2eac06ab 100644 --- a/Il2CppInterop.Common/XrefScans/GeneratedDatabasesUtil.cs +++ b/Il2CppInterop.Common/XrefScans/GeneratedDatabasesUtil.cs @@ -1,4 +1,3 @@ -#nullable enable using System.Reflection; namespace Il2CppInterop.Common.XrefScans; diff --git a/Il2CppInterop.Common/XrefScans/XrefScanMethodDb.cs b/Il2CppInterop.Common/XrefScans/XrefScanMethodDb.cs index 3958f4a7..c5d96b58 100644 --- a/Il2CppInterop.Common/XrefScans/XrefScanMethodDb.cs +++ b/Il2CppInterop.Common/XrefScans/XrefScanMethodDb.cs @@ -12,7 +12,7 @@ public static class XrefScanMethodDb private static readonly MethodXrefScanCache XrefScanCache; private static readonly long GameAssemblyBase; - private static XrefScanUtil.InitMetadataForMethod ourMetadataInitForMethodDelegate; + private static XrefScanUtil.InitMetadataForMethod? ourMetadataInitForMethodDelegate; static XrefScanMethodDb() { @@ -28,7 +28,7 @@ static XrefScanMethodDb() } } - public static MethodBase TryResolvePointer(IntPtr methodStart) + public static MethodBase? TryResolvePointer(IntPtr methodStart) { return MethodMap.Lookup((long)methodStart - GameAssemblyBase); } diff --git a/Il2CppInterop.Common/XrefScans/XrefScanUtil.cs b/Il2CppInterop.Common/XrefScans/XrefScanUtil.cs index 10f9b238..25680158 100644 --- a/Il2CppInterop.Common/XrefScans/XrefScanUtil.cs +++ b/Il2CppInterop.Common/XrefScans/XrefScanUtil.cs @@ -5,10 +5,10 @@ namespace Il2CppInterop.Common.XrefScans; internal static class XrefScanUtil { - private static InitMetadataForMethod ourMetadataInitForMethodDelegate; + private static InitMetadataForMethod? ourMetadataInitForMethodDelegate; private static IntPtr ourMetadataInitForMethodPointer; - internal static event Func<(InitMetadataForMethod, IntPtr)> InitRuntimeUtils; + internal static event Func<(InitMetadataForMethod, IntPtr)>? InitRuntimeUtils; internal static unsafe bool CallMetadataInitForMethod(MethodBase method) { @@ -43,7 +43,7 @@ internal static unsafe bool CallMetadataInitForMethod(MethodBase method) if (Marshal.ReadByte(initFlagPointer) == 0) { - ourMetadataInitForMethodDelegate(Marshal.ReadInt32(tokenPointer)); + ourMetadataInitForMethodDelegate?.Invoke(Marshal.ReadInt32(tokenPointer)); Marshal.WriteByte(initFlagPointer, 1); } diff --git a/Il2CppInterop.Common/XrefScans/XrefScannerManager.cs b/Il2CppInterop.Common/XrefScans/XrefScannerManager.cs index 8025a517..aa727152 100644 --- a/Il2CppInterop.Common/XrefScans/XrefScannerManager.cs +++ b/Il2CppInterop.Common/XrefScans/XrefScannerManager.cs @@ -13,7 +13,7 @@ internal static class XrefScannerManagerExtensions internal class XrefScannerManager : IHostComponent { - private static IXrefScannerImpl s_xrefScanner; + private static IXrefScannerImpl? s_xrefScanner; public static IXrefScannerImpl Impl { diff --git a/Il2CppInterop.Generator/AsmResolverDllOutputFormatBinding.cs b/Il2CppInterop.Generator/AsmResolverDllOutputFormatBinding.cs new file mode 100644 index 00000000..2411e89e --- /dev/null +++ b/Il2CppInterop.Generator/AsmResolverDllOutputFormatBinding.cs @@ -0,0 +1,78 @@ +using AsmResolver.DotNet; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Core.OutputFormats; + +namespace Il2CppInterop.Generator; + +public class AsmResolverDllOutputFormatBinding : AsmResolverDllOutputFormatThrowNull +{ + public override string OutputFormatId => "dll_binding"; + + public override string OutputFormatName => "DLL files with method bodies containing a binding for modding."; + + protected override void FillMethodBody(MethodDefinition methodDefinition, MethodAnalysisContext methodContext) + { + if (methodContext.TryGetExtraData(out TranslatedMethodBody? translatedBody)) + { + translatedBody.FillMethodBody(methodDefinition); + methodContext.RemoveExtraData(); // Free up memory + } + else if (methodContext.TryGetExtraData(out NativeMethodBody? nativeBody)) + { + nativeBody.FillMethodBody(methodDefinition); + methodContext.RemoveExtraData(); // Free up memory + } + else + { + // This gets called when the body of an unstripped method could not be translated. + // We could have it throw a custom exception with a message, but throwing null is sufficient for now. + base.FillMethodBody(methodDefinition, methodContext); + } + } + + public override List BuildAssemblies(ApplicationAnalysisContext context) + { + var list = base.BuildAssemblies(context); + + var referenceAssemblies = context.Assemblies.Where(a => a.IsReferenceAssembly).Select(a => a.Name).ToHashSet(); + + // Remove injected reference assemblies from the output + for (var i = list.Count - 1; i >= 0; i--) + { + if (referenceAssemblies.Contains(list[i].Name ?? "")) + list.RemoveAt(i); + } + + // Replace mscorlib references with .NET Core references + var dotNetCorLib = KnownCorLibs.SystemRuntime_v10_0_0_0; + foreach (var assembly in list) + { + foreach (var module in assembly.Modules) + { + foreach (var reference in module.AssemblyReferences) + { + if (reference.Name == "mscorlib") + { + reference.Name = dotNetCorLib.Name; + reference.Version = dotNetCorLib.Version; + reference.Attributes = dotNetCorLib.Attributes; + reference.PublicKeyOrToken = dotNetCorLib.PublicKeyOrToken; + reference.HashValue = dotNetCorLib.HashValue; + reference.Culture = dotNetCorLib.Culture; + } + } + + // Temporary fix + foreach (var type in module.GetAllTypes()) + { + if (type.IsInterface) + { + type.BaseType = null; + } + } + } + } + + return list; + } +} diff --git a/Il2CppInterop.Generator/AsmResolverDllOutputFormatUnstripped.cs b/Il2CppInterop.Generator/AsmResolverDllOutputFormatUnstripped.cs new file mode 100644 index 00000000..10341a63 --- /dev/null +++ b/Il2CppInterop.Generator/AsmResolverDllOutputFormatUnstripped.cs @@ -0,0 +1,24 @@ +using AsmResolver.DotNet; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Core.OutputFormats; + +namespace Il2CppInterop.Generator; + +public class AsmResolverDllOutputFormatUnstripped : AsmResolverDllOutputFormatThrowNull +{ + public override string OutputFormatId => "dll_unstripped"; + + public override string OutputFormatName => "DLL files with method bodies containing an unstripped implementation if available."; + + protected override void FillMethodBody(MethodDefinition methodDefinition, MethodAnalysisContext methodContext) + { + if (methodContext.TryGetExtraData(out OriginalMethodBody? originalBody)) + { + originalBody.FillMethodBody(methodDefinition); + } + else + { + base.FillMethodBody(methodDefinition, methodContext); + } + } +} diff --git a/Il2CppInterop.Generator/AttributeRemovalProcessingLayer.cs b/Il2CppInterop.Generator/AttributeRemovalProcessingLayer.cs new file mode 100644 index 00000000..e9de3683 --- /dev/null +++ b/Il2CppInterop.Generator/AttributeRemovalProcessingLayer.cs @@ -0,0 +1,94 @@ +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Logging; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Core.Model.CustomAttributes; + +namespace Il2CppInterop.Generator; + +public class AttributeRemovalProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Id => "attribute_removal"; + public override string Name => "Attribute Removal"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var isUnmanagedAttributeType = appContext.GetAssemblyByName("mscorlib")?.GetTypeByFullName("System.Runtime.CompilerServices.IsUnmanagedAttribute"); + + if (isUnmanagedAttributeType is null) + { + Logger.WarnNewline("IsUnmanagedAttribute not found. They cannot be recovered.", nameof(AttributeRemovalProcessingLayer)); + } + + var isUnmanagedAttributeConstructor = isUnmanagedAttributeType?.Methods.First(m => m.Name == ".ctor" && m.Parameters.Count == 0); + + foreach (var assembly in appContext.Assemblies) + { + ClearAttributes(assembly); + } + + foreach (var type in appContext.AllTypes) + { + ClearAttributes(type); + + foreach (var genericParameter in type.GenericParameters) + { + ClearGenericParameterAttributes(genericParameter, isUnmanagedAttributeConstructor); + } + + foreach (var field in type.Fields) + { + ClearAttributes(field); + } + + foreach (var method in type.Methods) + { + ClearAttributes(method); + + foreach (var parameter in method.Parameters) + { + ClearAttributes(parameter); + } + + foreach (var genericParameter in method.GenericParameters) + { + ClearGenericParameterAttributes(genericParameter, isUnmanagedAttributeConstructor); + } + } + + foreach (var property in type.Properties) + { + ClearAttributes(property); + } + + foreach (var @event in type.Events) + { + ClearAttributes(@event); + } + } + } + + private static void ClearGenericParameterAttributes(GenericParameterTypeAnalysisContext genericParameter, MethodAnalysisContext? isUnmanagedAttributeConstructor) + { + if (isUnmanagedAttributeConstructor is not null && HasIsUnmanagedAttribute(genericParameter)) + { + ClearAttributes(genericParameter); + genericParameter.CustomAttributes ??= new(1); + genericParameter.CustomAttributes.Add(new AnalyzedCustomAttribute(isUnmanagedAttributeConstructor)); + } + else + { + ClearAttributes(genericParameter); + } + + static bool HasIsUnmanagedAttribute(GenericParameterTypeAnalysisContext genericParameter) + { + return genericParameter.HasCustomAttributeWithFullName("System.Runtime.CompilerServices.IsUnmanagedAttribute") + || genericParameter.HasCustomAttributeWithFullName("Il2CppSystem.Runtime.CompilerServices.IsUnmanagedAttribute"); + } + } + + private static void ClearAttributes(HasCustomAttributes context) + { + context.AttributeTypes?.Clear(); + context.CustomAttributes?.Clear(); + } +} diff --git a/Il2CppInterop.Generator/AttributesOverrideProcessingLayer.cs b/Il2CppInterop.Generator/AttributesOverrideProcessingLayer.cs new file mode 100644 index 00000000..b55aa3f4 --- /dev/null +++ b/Il2CppInterop.Generator/AttributesOverrideProcessingLayer.cs @@ -0,0 +1,136 @@ +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator; + +public class AttributesOverrideProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Id => "attributes_override"; + public override string Name => "Attributes Override"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + { + continue; + } + + assembly.Culture = null; + assembly.Version = new(0, 0, 0, 0); + assembly.PublicKey = null; + assembly.PublicKeyToken = null; + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + { + continue; + } + + if (type.IsStatic) + { + type.OverrideAttributes = type.Attributes & ~TypeAttributes.Sealed; + } + + // Remove bad flags from type + { +#pragma warning disable SYSLIB0050 // Type or member is obsolete + const TypeAttributes TypeFlagsToRemove = + TypeAttributes.AutoClass | + TypeAttributes.HasSecurity | + TypeAttributes.Import | + TypeAttributes.RTSpecialName | + TypeAttributes.Serializable | + TypeAttributes.UnicodeClass | + TypeAttributes.WindowsRuntime; +#pragma warning restore SYSLIB0050 // Type or member is obsolete + + type.OverrideAttributes = type.Attributes & ~TypeFlagsToRemove; + } + + if (!type.IsValueType) + { + type.OverrideAttributes = type.Attributes & ~TypeAttributes.LayoutMask; + } + + foreach (var genericParameter in type.GenericParameters) + { + const GenericParameterAttributes GenericParamFlagsToRemove = + GenericParameterAttributes.Covariant | + GenericParameterAttributes.Contravariant; + genericParameter.OverrideAttributes = genericParameter.Attributes & ~GenericParamFlagsToRemove; + } + + foreach (var method in type.Methods) + { + if (method.IsInjected) + { + continue; + } + + const MethodAttributes FlagsToRemove = + MethodAttributes.HasSecurity | + MethodAttributes.RequireSecObject | + MethodAttributes.UnmanagedExport | + MethodAttributes.PinvokeImpl; + + method.OverrideAttributes = method.Attributes & ~FlagsToRemove; + + method.OverrideImplAttributes = MethodImplAttributes.Managed; + + foreach (var parameter in method.Parameters) + { + const ParameterAttributes ParamFlagsToRemove = + ParameterAttributes.Optional | + ParameterAttributes.HasDefault | + ParameterAttributes.HasFieldMarshal; + parameter.OverrideAttributes = parameter.Attributes & ~ParamFlagsToRemove; + } + } + + foreach (var field in type.Fields) + { + if (field.IsInjected) + { + continue; + } + + if (field.Attributes.HasFlag(FieldAttributes.Literal)) + { + // Change constant fields to static readonly + field.OverrideAttributes = (field.Attributes & ~FieldAttributes.Literal) | FieldAttributes.InitOnly; + } + else + { + // Remove readonly from non-constant fields + field.OverrideAttributes = field.Attributes & ~FieldAttributes.InitOnly; + } + + const FieldAttributes FlagsToRemove = + FieldAttributes.HasFieldRVA | + FieldAttributes.HasDefault | + FieldAttributes.HasFieldMarshal; + + field.OverrideAttributes = field.Attributes & ~FlagsToRemove; + } + + foreach (var property in type.Properties) + { + if (property.IsInjected) + { + continue; + } + + const PropertyAttributes FlagsToRemove = + PropertyAttributes.HasDefault; + + property.OverrideAttributes = property.Attributes & ~FlagsToRemove; + } + + // There are no event attributes that need to be modified. + } + } + } +} diff --git a/Il2CppInterop.Generator/ByRefParameterOverloadProcessingLayer.cs b/Il2CppInterop.Generator/ByRefParameterOverloadProcessingLayer.cs new file mode 100644 index 00000000..7d5bdef4 --- /dev/null +++ b/Il2CppInterop.Generator/ByRefParameterOverloadProcessingLayer.cs @@ -0,0 +1,188 @@ +using System.Diagnostics; +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; +using Il2CppInterop.Generator.Visitors; +using Il2CppInterop.Runtime.InteropTypes; + +namespace Il2CppInterop.Generator; + +public sealed class ByRefParameterOverloadProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "ByRef Parameter Overloads"; + public override string Id => "byref_parameter_overloads"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var byReference = appContext.ResolveTypeOrThrow(typeof(ByReference<>)); + var byReference_Constructor = byReference.GetMethodByName(".ctor"); + var byReference_CopyFrom = byReference.GetMethodByName(nameof(ByReference<>.CopyFrom)); + var byReference_CopyTo = byReference.GetMethodByName(nameof(ByReference<>.CopyTo)); + var byReference_Clear = byReference.GetMethodByName(nameof(ByReference<>.Clear)); + + var il2CppTypeHelper = appContext.ResolveTypeOrThrow(typeof(Il2CppType)); + var il2CppTypeHelper_SizeOf = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.SizeOf)); + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + + if (type.IsInterface) + { + continue; // We don't add method overloads to interfaces + } + + // for instead of foreach because we might be modifying the collection + for (var methodIndex = 0; methodIndex < type.Methods.Count; methodIndex++) + { + var method = type.Methods[methodIndex]; + if (method.IsInjected || !method.IsPublic || method.IsSpecialName) + continue; + + if (!method.Parameters.Any(p => p.DefaultParameterType is ByRefTypeAnalysisContext)) + continue; + + var newMethod = new InjectedMethodAnalysisContext(type, method.Name, appContext.SystemTypes.SystemVoidType, method.Attributes, []) + { + IsInjected = true, + }; + type.Methods.Add(newMethod); + + Debug.Assert(method.MostUserFriendlyOverload == method); + method.MostUserFriendlyOverload = newMethod; + + newMethod.CopyGenericParameters(method, true); + + var visitor = TypeReplacementVisitor.CreateForMethodCopying(method, newMethod); + + newMethod.SetDefaultReturnType(visitor.Replace(method.ReturnType)); + + foreach (var parameter in method.Parameters) + { + TypeAnalysisContext parameterType; + if (parameter.DefaultParameterType is ByRefTypeAnalysisContext) + { + Debug.Assert(parameter.ParameterType is GenericInstanceTypeAnalysisContext { GenericArguments.Count: 1 }); + var underlyingType = ((GenericInstanceTypeAnalysisContext)parameter.ParameterType).GenericArguments[0]; + parameterType = visitor.Replace(underlyingType).MakeByReferenceType(); + } + else + { + parameterType = visitor.Replace(parameter.ParameterType); + } + + var newParameter = new InjectedParameterAnalysisContext(parameter.Name, parameterType, parameter.Attributes, parameter.ParameterIndex, newMethod); + newMethod.Parameters.Add(newParameter); + } + + List instructions = new(); + List variables = new(); + + LocalVariable?[] variableMap = new LocalVariable?[newMethod.Parameters.Count]; + + for (var i = 0; i < newMethod.Parameters.Count; i++) + { + var parameter = newMethod.Parameters[i]; + if (parameter.ParameterType is ByRefTypeAnalysisContext { ElementType: { } underlyingType }) + { + LocalVariable local = new(byReference.MakeGenericInstanceType([underlyingType])); + variables.Add(local); + + instructions.Add(new Instruction(CilOpCodes.Call, il2CppTypeHelper_SizeOf.MakeGenericInstanceMethod(underlyingType))); + instructions.Add(new Instruction(CilOpCodes.Conv_U)); + instructions.Add(new Instruction(CilOpCodes.Localloc)); + instructions.Add(new Instruction(CilOpCodes.Newobj, new ConcreteGenericMethodAnalysisContext(byReference_Constructor, [underlyingType], []))); + instructions.Add(new Instruction(CilOpCodes.Stloc, local)); + + if (parameter.Attributes.HasFlag(ParameterAttributes.Out)) + { + instructions.Add(new Instruction(CilOpCodes.Ldloca, local)); + instructions.Add(new Instruction(CilOpCodes.Call, new ConcreteGenericMethodAnalysisContext(byReference_Clear, [underlyingType], []))); + } + else + { + instructions.Add(new Instruction(CilOpCodes.Ldloca, local)); + instructions.Add(new Instruction(CilOpCodes.Ldarg, parameter)); + instructions.Add(new Instruction(CilOpCodes.Call, new ConcreteGenericMethodAnalysisContext(byReference_CopyFrom, [underlyingType], []))); + } + + variableMap[i] = local; + } + } + + if (!newMethod.IsStatic) + { + instructions.Add(new Instruction(CilOpCodes.Ldarg, This.Instance)); + } + + for (var i = 0; i < newMethod.Parameters.Count; i++) + { + var local = variableMap[i]; + if (local is not null) + { + instructions.Add(new Instruction(CilOpCodes.Ldloc, local)); + } + else + { + instructions.Add(new Instruction(CilOpCodes.Ldarg, newMethod.Parameters[i])); + } + } + + instructions.Add(new Instruction(newMethod.IsStatic || type.IsValueType ? CilOpCodes.Call : CilOpCodes.Callvirt, method.MaybeMakeConcreteGeneric(type.GenericParameters, newMethod.GenericParameters))); + + LocalVariable? resultLocal; + if (newMethod.IsVoid) + { + resultLocal = null; + } + else + { + resultLocal = new(newMethod.ReturnType); + variables.Add(resultLocal); + instructions.Add(new Instruction(CilOpCodes.Stloc, resultLocal)); + } + + for (var i = 0; i < newMethod.Parameters.Count; i++) + { + var local = variableMap[i]; + if (local is null) + { + continue; + } + + var parameter = newMethod.Parameters[i]; + if (parameter.Attributes.HasFlag(ParameterAttributes.In)) + { + continue; + } + + var underlyingType = ((ByRefTypeAnalysisContext)parameter.ParameterType).ElementType; + + instructions.Add(new Instruction(CilOpCodes.Ldloca, local)); + instructions.Add(new Instruction(CilOpCodes.Ldarg, parameter)); + instructions.Add(new Instruction(CilOpCodes.Call, new ConcreteGenericMethodAnalysisContext(byReference_CopyTo, [underlyingType], []))); + } + + if (resultLocal is not null) + { + instructions.Add(new Instruction(CilOpCodes.Ldloc, resultLocal)); + } + + instructions.Add(new Instruction(CilOpCodes.Ret)); + + newMethod.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + LocalVariables = variables, + }); + } + } + } + } +} diff --git a/Il2CppInterop.Generator/CleanRenamingProcessingLayer.cs b/Il2CppInterop.Generator/CleanRenamingProcessingLayer.cs new file mode 100644 index 00000000..cd9ec205 --- /dev/null +++ b/Il2CppInterop.Generator/CleanRenamingProcessingLayer.cs @@ -0,0 +1,278 @@ +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Text.RegularExpressions; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator; + +public partial class CleanRenamingProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Clean Name Changes"; + + public override string Id => "cleanrenamer"; + + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + for (var i = 0; i < appContext.Assemblies.Count; i++) + { + var assembly = appContext.Assemblies[i]; + + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + RenameTypes(assembly.TopLevelTypes); + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + + RenameMembers(type); + } + + progressCallback?.Invoke(i, appContext.Assemblies.Count); + } + } + + private static void RenameTypes(IEnumerable types, string? declaringTypeName = null) + { + HashSet<(string, string)> reservedFullNames = []; + foreach (var type in types) + { + if (type.IsInjected) + { + reservedFullNames.Add((type.Namespace, type.Name)); + continue; + } + + if (GenericTypeName.TryMatch(type.Name, out var typeName, out var genericCount)) + { + type.OverrideName = $"{typeName.MakeValidCSharpName()}`{genericCount}"; + } + else if (type.IsModuleType) + { + type.OverrideName = $"Module_{type.DeclaringAssembly.DefaultName.MakeValidCSharpName()}"; + } + else if (type.IsPrivateImplementationDetailsType) + { + type.OverrideName = $"PrivateImplementationDetails_{type.DeclaringAssembly.DefaultName.MakeValidCSharpName()}"; + } + else + { + type.OverrideName = type.Name.MakeValidCSharpName(); + } + } + + // Resolve any name conflicts + foreach (var type in types) + { + if (type.IsInjected) + continue; + + var @namespace = type.Namespace; + var name = type.Name; + while (name == declaringTypeName || !reservedFullNames.Add((@namespace, name))) + { + name = $"_{name}"; + } + + type.OverrideName = name; + } + + foreach (var type in types) + { + RenameTypes(type.NestedTypes, type.Name); + } + } + + private static void RenameMembers(TypeAnalysisContext type) + { + // Virtual method lookup depends on the name being consistent with the base type. + // Special names also have special meaning and should not be changed. + const MethodAttributes FlagsWhichRequireNameConsistency = + MethodAttributes.SpecialName | + MethodAttributes.Abstract | + MethodAttributes.Virtual | + MethodAttributes.RTSpecialName; + + var typeName = GetCSharpName(type); + + // Events and properties do not get renamed + + // Collect all reserved names + HashSet reservedNames = + [ + "", + "_", + typeName, + .. GetConflictingNames(type.Properties), + .. GetConflictingNames(type.Events), + ]; + if (StartsWithGetSet(typeName)) + { + reservedNames.Add(typeName.Substring(4)); + } + + HashSet<(string Name, int SignatureHash)> existingMethods = []; + + // Injected and special methods + foreach (var method in type.Methods) + { + if (method.IsInjected) + { + existingMethods.Add((method.Name, GetMethodSignatureHash(method))); + } + else if ((method.Attributes & FlagsWhichRequireNameConsistency) != 0) + { + if (method.IsStaticConstructor) + { + // Rename static constructor, but allow it to be renamed again if needed. + // Also remove special name flags, since it's no longer a special name. + method.OverrideName = "StaticConstructor"; + method.OverrideAttributes = method.Attributes & ~(MethodAttributes.SpecialName | MethodAttributes.RTSpecialName); + } + else + { + existingMethods.Add((method.Name, GetMethodSignatureHash(method))); + } + } + } + + // Normal methods + foreach (var method in type.Methods) + { + if (method.IsInjected) + continue; + + if ((method.Attributes & FlagsWhichRequireNameConsistency) != 0) + continue; + + var methodName = method.Name.MakeValidCSharpName(); + var signatureHash = GetMethodSignatureHash(method); + + while (reservedNames.Contains(methodName) || !existingMethods.Add((methodName, signatureHash))) + { + methodName = $"_{methodName}"; + } + method.OverrideName = methodName; + } + + reservedNames.AddRange(GetConflictingNames(type.Methods)); + + // Fields + foreach (var field in type.Fields) + { + if (field.IsInjected) + continue; + + string fieldName; + if (TryMatchPropertyBackingField(field.Name, out var propertyName)) + { + fieldName = $"{propertyName}_BackingField"; + } + else if (type.Events.Any(e => e.Name == field.Name)) + { + fieldName = $"{field.Name}_BackingField"; + } + else + { + fieldName = field.Name.MakeValidCSharpName(); + } + + while (reservedNames.Contains(fieldName)) + { + fieldName = $"_{fieldName}"; + } + field.OverrideName = fieldName; + reservedNames.Add(field.Name); + } + + // Parameters + var parameterNames = new HashSet(); + foreach (var method in type.Methods) + { + if (method.IsInjected) + continue; + + if (method.Parameters.Count == 0) + continue; + + parameterNames.Clear(); + + for (var i = 0; i < method.Parameters.Count; i++) + { + var parameter = method.Parameters[i]; + var parameterName = string.IsNullOrEmpty(parameter.Name) + ? $"parameter{i}" + : parameter.Name.MakeValidCSharpName(); + + while (reservedNames.Contains(parameterName) || !parameterNames.Add(parameterName)) + { + parameterName = $"_{parameterName}"; + } + parameter.OverrideName = parameterName; + } + } + } + + private static IEnumerable GetConflictingNames(IEnumerable members) + { + foreach (var member in members) + { + yield return member.Name; + if (StartsWithGetSet(member.Name)) + { + yield return member.Name.Substring(4); + } + } + } + + private static bool StartsWithGetSet(string name) + { + return name.StartsWith("get_", StringComparison.Ordinal) + || name.StartsWith("set_", StringComparison.Ordinal); + } + + private static string GetCSharpName(TypeAnalysisContext type) + { + if (GenericTypeName.TryMatch(type.Name, out var typeName, out _)) + { + return typeName; + } + else + { + return type.Name; + } + } + + private static int GetMethodSignatureHash(MethodAnalysisContext method) + { + HashCode hash = new(); + hash.Add(method.GenericParameters.Count); + hash.Add(TypeAnalysisContextEqualityComparer.Instance.GetHashCode(method.ReturnType)); + foreach (var param in method.Parameters) + { + hash.Add(TypeAnalysisContextEqualityComparer.Instance.GetHashCode(param.ParameterType)); + } + return hash.ToHashCode(); + } + + private static bool TryMatchPropertyBackingField(string fieldName, [NotNullWhen(true)] out string? propertyName) + { + var match = PropertyBackingFieldRegex.Match(fieldName); + if (match.Success) + { + propertyName = match.Groups[1].Value; + return true; + } + else + { + propertyName = null; + return false; + } + } + + [GeneratedRegex(@"^<(\w+)>k__BackingField$")] + private static partial Regex PropertyBackingFieldRegex { get; } +} diff --git a/Il2CppInterop.Generator/ConflictRenamingProcessingLayer.cs b/Il2CppInterop.Generator/ConflictRenamingProcessingLayer.cs new file mode 100644 index 00000000..ace52f64 --- /dev/null +++ b/Il2CppInterop.Generator/ConflictRenamingProcessingLayer.cs @@ -0,0 +1,116 @@ +using System.Diagnostics; +using System.Text.RegularExpressions; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator; + +/// +/// 3 virtual methods in Il2CppSystem.Object conflict with their System.Object counterparts. +/// +public partial class ConflictRenamingProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Conflict Renaming"; + public override string Id => "conflictrenamer"; + + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + // ToString => ToIl2CppString + // GetHashCode => GetIl2CppHashCode + // Finalize => Il2CppFinalize + + var il2CppSystemObject = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Object"); + + for (var i = 0; i < appContext.Assemblies.Count; i++) + { + var assembly = appContext.Assemblies[i]; + + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + + MaybeAppendUnderscore(type.Name, type); + + foreach (var field in type.Fields) + { + MaybeAppendUnderscore(field.Name, field); + } + + foreach (var property in type.Properties) + { + MaybeAppendUnderscore(property.Name, property); + } + + foreach (var @event in type.Events) + { + MaybeAppendUnderscore(@event.Name, @event); + } + + foreach (var method in type.Methods) + { + var name = method.Name; + switch (name) + { + case "ToString": + if (method.Parameters.Count == 0 && method.GenericParameters.Count == 0) + { + Debug.Assert(!method.IsInjected); + method.Name = "ToIl2CppString"; + } + break; + case "GetHashCode": + if (method.Parameters.Count == 0 && method.GenericParameters.Count == 0) + { + Debug.Assert(!method.IsInjected); + method.Name = "GetIl2CppHashCode"; + } + break; + case "Finalize": + if (method.Parameters.Count == 0 && method.GenericParameters.Count == 0) + { + Debug.Assert(!method.IsInjected); + method.Name = "Il2CppFinalize"; + method.Overrides.Clear(); // Since this is no longer the Finalize method, it shouldn't have an explicit override. + } + break; + default: + MaybeAppendUnderscore(name, method); + break; + } + } + } + + progressCallback?.Invoke(i, appContext.Assemblies.Count); + } + } + + private static void MaybeAppendUnderscore(string name, HasCustomAttributesAndName context) + { + // If the name matches any of the patterns, append an underscore to avoid conflicts. + if (ToStringRegex.IsMatch(name)) + { + context.Name = $"{name}_"; + } + else if (GetHashCodeRegex.IsMatch(name)) + { + context.Name = $"{name}_"; + } + else if (FinalizeRegex.IsMatch(name)) + { + context.Name = $"{name}_"; + } + } + + [GeneratedRegex(@"^ToIl2CppString_*$")] + private static partial Regex ToStringRegex { get; } + + [GeneratedRegex(@"^GetIl2CppHashCode_*$")] + private static partial Regex GetHashCodeRegex { get; } + + [GeneratedRegex(@"^Il2CppFinalize_*$")] + private static partial Regex FinalizeRegex { get; } +} diff --git a/Il2CppInterop.Generator/ConstantInitializationProcessingLayer.cs b/Il2CppInterop.Generator/ConstantInitializationProcessingLayer.cs new file mode 100644 index 00000000..5840c40a --- /dev/null +++ b/Il2CppInterop.Generator/ConstantInitializationProcessingLayer.cs @@ -0,0 +1,72 @@ +using System.Diagnostics; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; + +namespace Il2CppInterop.Generator; + +public class ConstantInitializationProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Constant Initialization Processor"; + public override string Id => "constant_initialization_processor"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + + foreach (var field in type.Fields) + { + if (field.ConstantValue is null || field.IsInjected) + continue; + + Debug.Assert(field.IsStatic); + + var instructions = type.GetOrCreateStaticConstructorInstructions(); + + object operandCast; + unchecked + { + operandCast = field.ConstantValue switch + { + bool value => value ? 1 : 0, + char value => (int)value, + byte value => (int)value, + sbyte value => (int)value, + ushort value => (int)value, + short value => (int)value, + uint value => (int)value, + ulong value => (long)value, + _ => field.ConstantValue, + }; + } + + var opCode = operandCast switch + { + long => CilOpCodes.Ldc_I8, + float => CilOpCodes.Ldc_R4, + double => CilOpCodes.Ldc_R8, + string => CilOpCodes.Ldstr, + _ => CilOpCodes.Ldc_I4 + }; + + instructions.Add(new Instruction(opCode, operandCast)); + if (opCode == CilOpCodes.Ldstr) + { + MonoIl2CppConversion.AddMonoToIl2CppStringConversion(instructions, appContext); + } + else + { + MonoIl2CppConversion.AddMonoToIl2CppConversion(instructions, field.FieldType); + } + instructions.Add(new Instruction(CilOpCodes.Stsfld, field.MaybeMakeConcreteGeneric(type.GenericParameters))); + } + } + } + } +} diff --git a/Il2CppInterop.Generator/ContextResolver.cs b/Il2CppInterop.Generator/ContextResolver.cs new file mode 100644 index 00000000..37abd641 --- /dev/null +++ b/Il2CppInterop.Generator/ContextResolver.cs @@ -0,0 +1,326 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using AsmResolver.DotNet; +using AsmResolver.DotNet.Signatures; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; + +namespace Il2CppInterop.Generator; + +public readonly struct ContextResolver +{ + private readonly AssemblyAnalysisContext referencedFrom; + private readonly TypeAnalysisContext? referencingType; + private readonly MethodAnalysisContext? referencingMethod; + private readonly RuntimeContext runtimeContext; + + public ContextResolver(AssemblyAnalysisContext referencedFrom, RuntimeContext runtimeContext) + { + this.referencedFrom = referencedFrom; + this.runtimeContext = runtimeContext; + } + + public ContextResolver(TypeAnalysisContext referencingType, RuntimeContext runtimeContext) + { + if (referencingType is ReferencedTypeAnalysisContext) + throw new ArgumentException("Must be a simple type", nameof(referencingType)); + referencedFrom = referencingType.DeclaringAssembly; + this.referencingType = referencingType; + this.runtimeContext = runtimeContext; + } + + public ContextResolver(MethodAnalysisContext referencingMethod, RuntimeContext runtimeContext) + { + if (referencingMethod is ConcreteGenericMethodAnalysisContext) + throw new ArgumentException("Must be a simple method", nameof(referencingMethod)); + referencedFrom = referencingMethod.CustomAttributeAssembly; + referencingType = referencingMethod.DeclaringType; + this.referencingMethod = referencingMethod; + this.runtimeContext = runtimeContext; + } + + public TypeAnalysisContext? Resolve(TypeSignature? type) => type switch + { + // Ordered roughly by frequency + TypeDefOrRefSignature typeDefOrRef => Resolve(typeDefOrRef), + CorLibTypeSignature primitive => Resolve(primitive), + GenericInstanceTypeSignature genericInstance => Resolve(genericInstance), + SzArrayTypeSignature szArray => Resolve(szArray.BaseType)?.MakeSzArrayType(), + GenericParameterSignature genericParameter => genericParameter.ParameterType switch + { + GenericParameterType.Type => TryGetGenericParameter(referencingType?.GenericParameters, genericParameter.Index), + _ => TryGetGenericParameter(referencingMethod?.GenericParameters, genericParameter.Index), + }, + ByReferenceTypeSignature byRef => Resolve(byRef.BaseType)?.MakeByReferenceType(), + PointerTypeSignature pointer => Resolve(pointer.BaseType)?.MakePointerType(), + ArrayTypeSignature array => Resolve(array.BaseType)?.MakeArrayType(array.Rank), + PinnedTypeSignature pinned => Resolve(pinned.BaseType)?.MakePinnedType(), + CustomModifierTypeSignature customModifier => Resolve(customModifier), + BoxedTypeSignature boxed => Resolve(boxed.BaseType)?.MakeBoxedType(), + SentinelTypeSignature => new SentinelTypeAnalysisContext(referencedFrom), + _ => null + }; + + private static GenericParameterTypeAnalysisContext? TryGetGenericParameter(List? genericParameters, int index) + { + if (genericParameters is null || index < 0 || index >= genericParameters.Count) + return null; + return genericParameters[index]; + } + + public bool TryResolve(TypeSignature? type, [NotNullWhen(true)] out TypeAnalysisContext? result) + { + result = Resolve(type); + return result is not null; + } + + private GenericInstanceTypeAnalysisContext? Resolve(GenericInstanceTypeSignature genericInstance) + { + return TryResolve(genericInstance.GenericType, out var genericType) && TryResolve(genericInstance.TypeArguments, out var genericArguments) + ? new GenericInstanceTypeAnalysisContext(genericType, genericArguments, referencedFrom) + : null; + } + + private CustomModifierTypeAnalysisContext? Resolve(CustomModifierTypeSignature customModifier) + { + return TryResolve(customModifier.BaseType, out var baseType) && TryResolve(customModifier.ModifierType, out var modifier) + ? new CustomModifierTypeAnalysisContext(baseType, modifier, customModifier.IsRequired, referencedFrom) + : null; + } + + private TypeAnalysisContext? Resolve(TypeDefOrRefSignature typeDefOrRef) + { + return Resolve(typeDefOrRef.Type); + } + + private TypeAnalysisContext? Resolve(ITypeDefOrRef typeDefOrRef) + { + if (typeDefOrRef is TypeSpecification typeSpecification) + { + return Resolve(typeSpecification.Signature); + } + + if (typeDefOrRef.DeclaringType is not null) + { + if (!TryResolve(typeDefOrRef.DeclaringType, out var declaringType)) + return null; + + foreach (var nestedType in declaringType.NestedTypes) + { + if (nestedType.Name == typeDefOrRef.Name) + { + return nestedType; + } + } + + return null; + } + + if (typeDefOrRef is not TypeDefinition) + { + typeDefOrRef = typeDefOrRef.TryResolve(runtimeContext) ?? typeDefOrRef; + } + + var assemblyName = GetName(typeDefOrRef.Scope); + if (assemblyName == null) + return null; + + var targetAssembly = referencedFrom.AppContext.Assemblies.FirstOrDefault(a => a.Name == assemblyName); + if (targetAssembly == null) + return null; + + return targetAssembly.GetTypeByFullName(typeDefOrRef.FullName); + + static string? GetName(IResolutionScope? scope) => scope switch + { + ModuleDefinition module => module.Assembly!.Name, + AssemblyReference assembly => assembly.Name, + _ => throw new NotImplementedException(), + }; + } + + private TypeAnalysisContext? Resolve(CorLibTypeSignature corLibType) + { + return referencedFrom.AppContext.Mscorlib.GetTypeByFullName(corLibType.FullName); + } + + public TypeAnalysisContext? Resolve(ITypeDescriptor? type) + { + return type switch + { + TypeSignature signature => Resolve(signature), + ITypeDefOrRef typeDefOrRef => Resolve(typeDefOrRef), + _ => null, + }; + } + + public bool TryResolve(ITypeDescriptor? type, [NotNullWhen(true)] out TypeAnalysisContext? result) + { + result = Resolve(type); + return result is not null; + } + + public IEnumerable Resolve(IEnumerable types) + { + foreach (var type in types) + { + yield return Resolve(type); + } + } + + public bool TryResolve(IEnumerable types, [NotNullWhen(true)] out List? result) + { + result = []; + foreach (var type in types) + { + if (TryResolve(type, out var resolvedType)) + { + result.Add(resolvedType); + } + else + { + result = null; + return false; + } + } + return true; + } + + public FieldAnalysisContext? Resolve(IFieldDescriptor fieldDescriptor) + { + var declaringType = Resolve(fieldDescriptor.DeclaringType); + if (declaringType is null) + return null; + + if (declaringType is GenericInstanceTypeAnalysisContext genericInstanceType) + { + var baseField = genericInstanceType.GenericType.TryGetFieldByName(fieldDescriptor.Name); + if (baseField is null) + return null; + + return new ConcreteGenericFieldAnalysisContext(baseField, genericInstanceType); + } + else + { + return declaringType.TryGetFieldByName(fieldDescriptor.Name); + } + } + + public bool TryResolve(IFieldDescriptor fieldDescriptor, [NotNullWhen(true)] out FieldAnalysisContext? result) + { + result = Resolve(fieldDescriptor); + return result is not null; + } + + public object? Resolve(IMethodDescriptor methodDescriptor) + { + if (methodDescriptor is MethodSpecification specification) + { + return Resolve(specification); + } + + var methodDefOrRef = (IMethodDefOrRef)methodDescriptor; + + if (!TryResolve(methodDefOrRef.DeclaringType, out var declaringType)) + return null; + + var nonGenericDeclaringType = (declaringType as GenericInstanceTypeAnalysisContext)?.GenericType ?? declaringType; + + if (nonGenericDeclaringType is ArrayTypeAnalysisContext arrayDeclaringType) + { + Debug.Assert(nonGenericDeclaringType == declaringType, "Array types should not be generic instances"); + return methodDefOrRef.Name?.Value switch + { + "Get" => new MultiDimensionalArrayMethod(arrayDeclaringType, MultiDimensionalArrayMethodType.Get), + "Set" => new MultiDimensionalArrayMethod(arrayDeclaringType, MultiDimensionalArrayMethodType.Set), + "Address" => new MultiDimensionalArrayMethod(arrayDeclaringType, MultiDimensionalArrayMethodType.Address), + ".ctor" => new MultiDimensionalArrayMethod(arrayDeclaringType, MultiDimensionalArrayMethodType.Constructor), + _ => null, + }; + } + + Debug.Assert(nonGenericDeclaringType is not ReferencedTypeAnalysisContext); + + var targetMethod = new ContextResolver(nonGenericDeclaringType, runtimeContext).ResolveInType(methodDefOrRef); + if (targetMethod is null) + return null; + + if (declaringType is GenericInstanceTypeAnalysisContext genericInstanceType) + { + return new ConcreteGenericMethodAnalysisContext(targetMethod, genericInstanceType.GenericArguments, []); + } + else + { + return targetMethod; + } + } + + public bool TryResolve(IMethodDescriptor methodDescriptor, [NotNullWhen(true)] out object? result) + { + result = Resolve(methodDescriptor); + return result is not null; + } + + public MethodAnalysisContext? Resolve(MethodDefinition methodDefinition) + { + // The declaring type can be resolved with nothing, but resolution for the method itself requires a context. + return TryResolve(methodDefinition.DeclaringType, out var declaringType) ? new ContextResolver(declaringType, runtimeContext).ResolveInType(methodDefinition) : null; + } + + public MethodAnalysisContext? ResolveInType(IMethodDefOrRef methodDefOrRef) + { + if (referencingType is null) + throw new InvalidOperationException("Cannot resolve method in type without a referencing type"); + + if (methodDefOrRef.Signature is null || methodDefOrRef.Signature.SentinelParameterTypes.Count > 0) + return null; + + foreach (var methodContext in referencingType.Methods) + { + if (methodContext.Name != methodDefOrRef.Name) + continue; + + if (methodContext.Parameters.Count != methodDefOrRef.Signature.ParameterTypes.Count) + continue; + + if (methodContext.GenericParameters.Count != methodDefOrRef.Signature.GenericParameterCount) + continue; + + if (methodContext.IsStatic == methodDefOrRef.Signature.HasThis) + continue; + + if (methodContext.IsVoid == methodDefOrRef.Signature.ReturnsValue) + continue; + + // We need to use a resolver for the method context to resolve potential method generic parameters correctly. + var methodResolver = new ContextResolver(methodContext, runtimeContext); + + if (!methodResolver.TryResolve(methodDefOrRef.Signature.ReturnType, out var returnType) || + !TypeAnalysisContextEqualityComparer.Instance.Equals(methodContext.ReturnType, returnType)) + continue; + + if (!methodResolver.TryResolve(methodDefOrRef.Signature.ParameterTypes, out var parameterTypes) || + !methodContext.Parameters.Select(p => p.ParameterType).SequenceEqual(parameterTypes, TypeAnalysisContextEqualityComparer.Instance)) + continue; + + return methodContext; + } + + return null; + } + + public ConcreteGenericMethodAnalysisContext? Resolve(MethodSpecification specification) + { + if (specification.Method is null || specification.Signature is null) + return null; + + var baseMethod = (MethodAnalysisContext?)Resolve(specification.Method); + if (baseMethod is null or { DeclaringType: null }) + return null; + + if (!TryResolve(specification.Signature.TypeArguments, out var methodTypeArguments)) + return null; + + return baseMethod.MakeGenericInstanceMethod(methodTypeArguments); + } +} diff --git a/Il2CppInterop.Generator/Contexts/AssemblyRewriteContext.cs b/Il2CppInterop.Generator/Contexts/AssemblyRewriteContext.cs deleted file mode 100644 index a5e8f2fd..00000000 --- a/Il2CppInterop.Generator/Contexts/AssemblyRewriteContext.cs +++ /dev/null @@ -1,155 +0,0 @@ -using System.Diagnostics; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Contexts; - -[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")] -public class AssemblyRewriteContext -{ - public readonly RewriteGlobalContext GlobalContext; - - public readonly RuntimeAssemblyReferences Imports; - private readonly Dictionary myNameTypeMap = new(); - private readonly Dictionary myNewTypeMap = new(); - - private readonly Dictionary myOldTypeMap = new(); - public readonly AssemblyDefinition NewAssembly; - - public readonly AssemblyDefinition OriginalAssembly; - - public AssemblyRewriteContext(RewriteGlobalContext globalContext, AssemblyDefinition originalAssembly, - AssemblyDefinition newAssembly) - { - OriginalAssembly = originalAssembly; - NewAssembly = newAssembly; - GlobalContext = globalContext; - - Imports = globalContext.ImportsMap.GetOrCreate(newAssembly.ManifestModule!, - mod => new RuntimeAssemblyReferences(mod, globalContext)); - } - - public IEnumerable Types => myOldTypeMap.Values; - - public TypeRewriteContext GetContextForOriginalType(TypeDefinition type) - { - return myOldTypeMap[type]; - } - - public TypeRewriteContext? TryGetContextForOriginalType(TypeDefinition type) - { - return myOldTypeMap.TryGetValue(type, out var result) ? result : null; - } - - public TypeRewriteContext GetContextForNewType(TypeDefinition type) - { - return myNewTypeMap[type]; - } - - public void RegisterTypeRewrite(TypeRewriteContext context) - { - if (context.OriginalType != null) - myOldTypeMap[context.OriginalType] = context; - myNewTypeMap[context.NewType] = context; - myNameTypeMap[(context.OriginalType ?? context.NewType).FullName] = context; - } - - public IMethodDefOrRef RewriteMethodRef(IMethodDefOrRef methodRef) - { - var newType = GlobalContext.GetNewTypeForOriginal(methodRef.DeclaringType!.Resolve()!); - var newMethod = newType.GetMethodByOldMethod(methodRef.Resolve()!).NewMethod; - return NewAssembly.ManifestModule!.DefaultImporter.ImportMethod(newMethod); - } - - public ITypeDefOrRef RewriteTypeRef(ITypeDescriptor typeRef) - { - return RewriteTypeRef(typeRef?.ToTypeSignature()).ToTypeDefOrRef(); - } - - public TypeSignature RewriteTypeRef(TypeSignature? typeRef) - { - if (typeRef == null) - return Imports.Il2CppObjectBase; - - var sourceModule = NewAssembly.ManifestModule!; - - if (typeRef is ArrayBaseTypeSignature arrayType) - { - if (arrayType.Rank != 1) - return Imports.Il2CppObjectBase; - - var elementType = arrayType.BaseType; - if (elementType.FullName == "System.String") - return Imports.Il2CppStringArray; - - var convertedElementType = RewriteTypeRef(elementType); - if (elementType is GenericParameterSignature) - return new GenericInstanceTypeSignature(Imports.Il2CppArrayBase.ToTypeDefOrRef(), false, convertedElementType); - - return new GenericInstanceTypeSignature(convertedElementType.IsValueType() - ? Imports.Il2CppStructArray.ToTypeDefOrRef() - : Imports.Il2CppReferenceArray.ToTypeDefOrRef(), false, convertedElementType); - } - - if (typeRef is GenericParameterSignature genericParameter) - { - return new GenericParameterSignature(sourceModule, genericParameter.ParameterType, genericParameter.Index); - } - - if (typeRef is ByReferenceTypeSignature byRef) - return new ByReferenceTypeSignature(RewriteTypeRef(byRef.BaseType)); - - if (typeRef is PointerTypeSignature pointerType) - return new PointerTypeSignature(RewriteTypeRef(pointerType.BaseType)); - - if (typeRef is GenericInstanceTypeSignature genericInstance) - { - var genericType = RewriteTypeRef(genericInstance.GenericType.ToTypeSignature()).ToTypeDefOrRef(); - var newRef = new GenericInstanceTypeSignature(genericType, genericType.IsValueType()); - foreach (var originalParameter in genericInstance.TypeArguments) - newRef.TypeArguments.Add(RewriteTypeRef(originalParameter)); - - return newRef; - } - - if (typeRef.IsPrimitive() || typeRef.FullName == "System.TypedReference") - return sourceModule.ImportCorlibReference(typeRef.FullName); - - if (typeRef.FullName == "System.Void") - return Imports.Module.Void(); - - if (typeRef.FullName == "System.String") - return Imports.Module.String(); - - if (typeRef.FullName == "System.Object") - return sourceModule.DefaultImporter.ImportType(GlobalContext.GetAssemblyByName("mscorlib") - .GetTypeByName("System.Object").NewType).ToTypeSignature(); - - if (typeRef.FullName == "System.Attribute") - return sourceModule.DefaultImporter.ImportType(GlobalContext.GetAssemblyByName("mscorlib") - .GetTypeByName("System.Attribute").NewType).ToTypeSignature(); - - var originalTypeDef = typeRef.Resolve()!; - var targetAssembly = GlobalContext.GetNewAssemblyForOriginal(originalTypeDef.DeclaringModule!.Assembly!); - var target = targetAssembly.GetContextForOriginalType(originalTypeDef).NewType; - - return sourceModule.DefaultImporter.ImportType(target).ToTypeSignature(); - } - - public TypeRewriteContext GetTypeByName(string name) - { - return myNameTypeMap[name]; - } - - public TypeRewriteContext? TryGetTypeByName(string name) - { - return myNameTypeMap.TryGetValue(name, out var result) ? result : null; - } - - private string GetDebuggerDisplay() - { - return NewAssembly.FullName; - } -} diff --git a/Il2CppInterop.Generator/Contexts/FieldRewriteContext.cs b/Il2CppInterop.Generator/Contexts/FieldRewriteContext.cs deleted file mode 100644 index a2420e68..00000000 --- a/Il2CppInterop.Generator/Contexts/FieldRewriteContext.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System.Diagnostics; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Contexts; - -[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")] -public class FieldRewriteContext -{ - private static readonly string[] MethodAccessTypeLabels = - {"CompilerControlled", "Private", "FamAndAssem", "Internal", "Protected", "FamOrAssem", "Public"}; - - public readonly TypeRewriteContext DeclaringType; - public readonly FieldDefinition OriginalField; - - public readonly MemberReference PointerField; - public readonly string UnmangledName; - - public FieldRewriteContext(TypeRewriteContext declaringType, FieldDefinition originalField, - Dictionary? renamedFieldCounts = null) - { - DeclaringType = declaringType; - OriginalField = originalField; - - UnmangledName = UnmangleFieldName(originalField, declaringType.AssemblyContext.GlobalContext.Options, - renamedFieldCounts); - var pointerField = new FieldDefinition("NativeFieldInfoPtr_" + UnmangledName, - FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly, - declaringType.AssemblyContext.Imports.Module.IntPtr()); - - declaringType.NewType.Fields.Add(pointerField); - - Debug.Assert(pointerField.Signature is not null); - PointerField = new MemberReference(DeclaringType.SelfSubstitutedRef, pointerField.Name, new FieldSignature(pointerField.Signature!.FieldType)); - } - - private string UnmangleFieldNameBase(FieldDefinition field, GeneratorOptions options) - { - if (options.PassthroughNames) - return field.Name!; - - if (!field.Name.IsObfuscated(options)) - { - return field.Name.MakeValidInSource(); - } - - Debug.Assert(field.Signature is not null); - var accessModString = MethodAccessTypeLabels[(int)(field.Attributes & FieldAttributes.FieldAccessMask)]; - var staticString = field.IsStatic ? "_Static" : ""; - return "field_" + accessModString + staticString + "_" + - DeclaringType.AssemblyContext.RewriteTypeRef(field.Signature!.FieldType).GetUnmangledName(field.DeclaringType); - } - - private string UnmangleFieldName(FieldDefinition field, GeneratorOptions options, - Dictionary? renamedFieldCounts) - { - if (options.PassthroughNames) - return field.Name!; - - if (!field.Name.IsObfuscated(options)) - { - return field.Name.MakeValidInSource(); - } - - if (renamedFieldCounts == null) throw new ArgumentNullException(nameof(renamedFieldCounts)); - - var unmangleFieldNameBase = UnmangleFieldNameBase(field, options); - - renamedFieldCounts.TryGetValue(unmangleFieldNameBase, out var count); - renamedFieldCounts[unmangleFieldNameBase] = count + 1; - - unmangleFieldNameBase += "_" + count; - - if (DeclaringType.AssemblyContext.GlobalContext.Options.RenameMap.TryGetValue( - DeclaringType.NewType.GetNamespacePrefix() + "." + DeclaringType.NewType.Name + "::" + - unmangleFieldNameBase, out var newName)) - unmangleFieldNameBase = newName; - - return unmangleFieldNameBase; - } - - private string GetDebuggerDisplay() - { - return DeclaringType.NewType.FullName + "::" + UnmangledName; - } -} diff --git a/Il2CppInterop.Generator/Contexts/MethodRewriteContext.cs b/Il2CppInterop.Generator/Contexts/MethodRewriteContext.cs deleted file mode 100644 index efdcd85c..00000000 --- a/Il2CppInterop.Generator/Contexts/MethodRewriteContext.cs +++ /dev/null @@ -1,316 +0,0 @@ -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Text; -using AsmResolver; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Common.XrefScans; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Passes; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Contexts; - -[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")] -public class MethodRewriteContext -{ - private static readonly string[] MethodAccessTypeLabels = - {"CompilerControlled", "Private", "FamAndAssem", "Internal", "Protected", "FamOrAssem", "Public"}; - - private static readonly (MethodSemanticsAttributes, string)[] SemanticsToCheck = - { - (MethodSemanticsAttributes.Setter, "_set"), - (MethodSemanticsAttributes.Getter, "_get"), - (MethodSemanticsAttributes.Other, "_oth"), - (MethodSemanticsAttributes.AddOn, "_add"), - (MethodSemanticsAttributes.RemoveOn, "_rem"), - (MethodSemanticsAttributes.Fire, "_fire") - }; - - public readonly TypeRewriteContext DeclaringType; - - public readonly long FileOffset; - public readonly MethodDefinition NewMethod; - public readonly MethodDefinition OriginalMethod; - - public readonly bool OriginalNameObfuscated; - public readonly long Rva; - - public readonly List XrefScanResults = new(); - - public long MetadataInitFlagRva; - public long MetadataInitTokenRva; - - public MethodRewriteContext(TypeRewriteContext declaringType, MethodDefinition originalMethod) - { - DeclaringType = declaringType; - OriginalMethod = originalMethod; - - var passthroughNames = declaringType.AssemblyContext.GlobalContext.Options.PassthroughNames; - - OriginalNameObfuscated = !passthroughNames && - (OriginalMethod.Name?.IsObfuscated(declaringType.AssemblyContext.GlobalContext - .Options) ?? false); - - var newAttributes = AdjustAttributes(originalMethod.Attributes, originalMethod.Name == "Finalize"); - var newSignature = (newAttributes & MethodAttributes.Static) != 0 - ? MethodSignature.CreateStatic(declaringType.AssemblyContext.Imports.Module.Void(), originalMethod.GenericParameters.Count) - : MethodSignature.CreateInstance(declaringType.AssemblyContext.Imports.Module.Void(), originalMethod.GenericParameters.Count); - var newMethod = new MethodDefinition("", newAttributes, newSignature); - newMethod.CilMethodBody = new(); - NewMethod = newMethod; - - HasExtensionAttribute = - originalMethod.CustomAttributes.Any(x => x.AttributeType()?.FullName == typeof(ExtensionAttribute).FullName); - - if (HasExtensionAttribute) - newMethod.CustomAttributes.Add( - new CustomAttribute(declaringType.AssemblyContext.Imports.Module.ExtensionAttributeCtor())); - - if (originalMethod.HasGenericParameters()) - { - var genericParams = originalMethod.GenericParameters; - - foreach (var oldParameter in genericParams) - { - newMethod.GenericParameters.Add(new GenericParameter( - oldParameter.Name.MakeValidInSource(), - oldParameter.Attributes.StripValueTypeConstraint())); - } - } - - if (!Pass15GenerateMemberContexts.HasObfuscatedMethods && !passthroughNames && - originalMethod.Name.IsObfuscated(declaringType.AssemblyContext.GlobalContext.Options)) - Pass15GenerateMemberContexts.HasObfuscatedMethods = true; - - FileOffset = originalMethod.ExtractOffset(); - // Workaround for garbage file offsets passed by Cpp2IL - if (FileOffset < 0) FileOffset = 0; - Rva = originalMethod.ExtractRva(); - if (FileOffset != 0) - declaringType.AssemblyContext.GlobalContext.MethodStartAddresses.Add(FileOffset); - } - - public Utf8String? UnmangledName { get; private set; } - public string? UnmangledNameWithSignature { get; private set; } - - public TypeDefinition? GenericInstantiationsStore { get; private set; } - public ITypeDefOrRef? GenericInstantiationsStoreSelfSubstRef { get; private set; } - public ITypeDefOrRef? GenericInstantiationsStoreSelfSubstMethodRef { get; private set; } - public MemberReference NonGenericMethodInfoPointerField { get; private set; } = null!; // Initialized in CtorPhase2 - - public bool HasExtensionAttribute { get; } - - public void CtorPhase2() - { - UnmangledName = UnmangleMethodName(); - UnmangledNameWithSignature = UnmangleMethodNameWithSignature(); - - NewMethod.Name = UnmangledName; - NewMethod.Signature!.ReturnType = DeclaringType.AssemblyContext.RewriteTypeRef(OriginalMethod.Signature?.ReturnType); - - var nonGenericMethodInfoPointerField = new FieldDefinition( - "NativeMethodInfoPtr_" + UnmangledNameWithSignature, - FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly, - DeclaringType.AssemblyContext.Imports.Module.IntPtr()); - DeclaringType.NewType.Fields.Add(nonGenericMethodInfoPointerField); - - NonGenericMethodInfoPointerField = new MemberReference(DeclaringType.SelfSubstitutedRef, nonGenericMethodInfoPointerField.Name, - new FieldSignature(nonGenericMethodInfoPointerField.Signature!.FieldType)); - - if (OriginalMethod.HasGenericParameters()) - { - var genericParams = OriginalMethod.GenericParameters; - var genericMethodInfoStoreType = new TypeDefinition("", - "MethodInfoStoreGeneric_" + UnmangledNameWithSignature + "`" + genericParams.Count, - TypeAttributes.NestedPrivate | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit, - DeclaringType.AssemblyContext.Imports.Module.Object().ToTypeDefOrRef()); - DeclaringType.NewType.NestedTypes.Add(genericMethodInfoStoreType); - GenericInstantiationsStore = genericMethodInfoStoreType; - - var selfSubstRef = new GenericInstanceTypeSignature(genericMethodInfoStoreType, false); - var selfSubstMethodRef = new GenericInstanceTypeSignature(genericMethodInfoStoreType, false); - - for (var index = 0; index < genericParams.Count; index++) - { - var oldParameter = genericParams[index]; - var genericParameter = new GenericParameter(oldParameter.Name.MakeValidInSource()); - genericMethodInfoStoreType.GenericParameters.Add(genericParameter); - selfSubstRef.TypeArguments.Add(genericParameter.ToTypeSignature()); - var newParameter = NewMethod.GenericParameters[index]; - selfSubstMethodRef.TypeArguments.Add(newParameter.ToTypeSignature()); - - foreach (var oldConstraint in oldParameter.Constraints) - { - if (oldConstraint.IsSystemValueType() || oldConstraint.IsInterface()) - continue; - - if (oldConstraint.IsSystemEnum()) - { - newParameter.Constraints.Add(new GenericParameterConstraint( - DeclaringType.AssemblyContext.Imports.Module.Enum().ToTypeDefOrRef())); - continue; - } - - newParameter.Constraints.Add(new GenericParameterConstraint( - DeclaringType.AssemblyContext.RewriteTypeRef(oldConstraint.Constraint?.ToTypeSignature()).ToTypeDefOrRef())); - } - } - - var pointerField = new FieldDefinition("Pointer", FieldAttributes.Assembly | FieldAttributes.Static, - DeclaringType.AssemblyContext.Imports.Module.IntPtr()); - genericMethodInfoStoreType.Fields.Add(pointerField); - - GenericInstantiationsStoreSelfSubstRef = DeclaringType.NewType.DeclaringModule!.DefaultImporter.ImportType(selfSubstRef.ToTypeDefOrRef()); - GenericInstantiationsStoreSelfSubstMethodRef = - DeclaringType.NewType.DeclaringModule.DefaultImporter.ImportType(selfSubstMethodRef.ToTypeDefOrRef()); - } - - DeclaringType.NewType.Methods.Add(NewMethod); - } - - private MethodAttributes AdjustAttributes(MethodAttributes original, bool stripVirtual) - { - original &= ~MethodAttributes.MemberAccessMask; // todo: handle Object overload correctly - original &= ~MethodAttributes.PInvokeImpl; - original &= ~MethodAttributes.Abstract; - if (stripVirtual) original &= ~MethodAttributes.Virtual; - original &= ~MethodAttributes.Final; - if (stripVirtual) original &= ~MethodAttributes.NewSlot; - original &= ~MethodAttributes.ReuseSlot; - original &= ~MethodAttributes.CheckAccessOnOverride; - original |= MethodAttributes.Public; - return original; - } - - private string UnmangleMethodName() - { - var method = OriginalMethod; - - if (method.Name == "GetType" && method.Parameters.Count == 0) - return "GetIl2CppType"; - - if (DeclaringType.AssemblyContext.GlobalContext.Options.PassthroughNames) - return method.Name!; - - if (method.Name == ".ctor") - return ".ctor"; - - if (method.Name.IsObfuscated(DeclaringType.AssemblyContext.GlobalContext.Options)) - return UnmangleMethodNameWithSignature(); - - return method.Name.MakeValidInSource(); - } - - private string ProduceMethodSignatureBase() - { - var method = OriginalMethod; - - string name; - if (method.Name.IsObfuscated(DeclaringType.AssemblyContext.GlobalContext.Options)) - name = "Method"; - else - name = method.Name.MakeValidInSource(); - - if (method.Name == "GetType" && method.Parameters.Count == 0) - name = "GetIl2CppType"; - - var builder = new StringBuilder(); - builder.Append(name); - builder.Append('_'); - builder.Append(MethodAccessTypeLabels[(int)(method.Attributes & MethodAttributes.MemberAccessMask)]); - if (method.IsAbstract) builder.Append("_Abstract"); - if (method.IsVirtual) builder.Append("_Virtual"); - if (method.IsStatic) builder.Append("_Static"); - if (method.IsFinal) builder.Append("_Final"); - if (method.IsNewSlot) builder.Append("_New"); - if (method.Semantics is not null) - foreach (var (semantic, str) in SemanticsToCheck) - if ((semantic & method.Semantics.Attributes) != 0) - builder.Append(str); - - builder.Append('_'); - builder.Append(DeclaringType.AssemblyContext.RewriteTypeRef(method.Signature?.ReturnType).GetUnmangledName(method.DeclaringType, method)); - - foreach (var param in method.Parameters) - { - builder.Append('_'); - builder.Append(DeclaringType.AssemblyContext.RewriteTypeRef(param.ParameterType).GetUnmangledName(method.DeclaringType, method)); - } - - var address = Rva; - if (address != 0 && Pass15GenerateMemberContexts.HasObfuscatedMethods && - !Pass16ScanMethodRefs.NonDeadMethods.Contains(address)) builder.Append("_PDM"); - - return builder.ToString(); - } - - - private string UnmangleMethodNameWithSignature() - { - var unmangleMethodNameWithSignature = ProduceMethodSignatureBase() + "_" + DeclaringType.Methods - .Where(ParameterSignatureMatchesThis).TakeWhile(it => it != this).Count(); - - if (DeclaringType.AssemblyContext.GlobalContext.Options.RenameMap.TryGetValue( - DeclaringType.NewType.GetNamespacePrefix() + "." + DeclaringType.NewType.Name + "::" + unmangleMethodNameWithSignature, out var newNameByType)) - { - unmangleMethodNameWithSignature = newNameByType; - } - else if (DeclaringType.AssemblyContext.GlobalContext.Options.RenameMap.TryGetValue( - DeclaringType.NewType.GetNamespacePrefix() + "::" + unmangleMethodNameWithSignature, out var newName)) - { - unmangleMethodNameWithSignature = newName; - } - - return unmangleMethodNameWithSignature; - } - - private bool ParameterSignatureMatchesThis(MethodRewriteContext otherRewriteContext) - { - var aM = otherRewriteContext.OriginalMethod; - var bM = OriginalMethod; - - if (!otherRewriteContext.OriginalNameObfuscated) - return false; - - var comparisonMask = MethodAttributes.MemberAccessMask | MethodAttributes.Static | MethodAttributes.Final | - MethodAttributes.Abstract | MethodAttributes.Virtual | MethodAttributes.NewSlot; - if ((aM.Attributes & comparisonMask) != - (bM.Attributes & comparisonMask)) - return false; - - if (aM.Semantics?.Attributes != bM.Semantics?.Attributes) - return false; - - if (aM.Signature?.ReturnType.FullName != bM.Signature?.ReturnType.FullName) - return false; - - var a = aM.Parameters; - var b = bM.Parameters; - - if (a.Count != b.Count) - return false; - - for (var i = 0; i < a.Count; i++) - if (a[i].ParameterType.FullName != b[i].ParameterType.FullName) - return false; - - if (Pass15GenerateMemberContexts.HasObfuscatedMethods) - { - var addressA = otherRewriteContext.Rva; - var addressB = Rva; - if (addressA != 0 && addressB != 0) - if (Pass16ScanMethodRefs.NonDeadMethods.Contains(addressA) != - Pass16ScanMethodRefs.NonDeadMethods.Contains(addressB)) - return false; - } - - return true; - } - - private string GetDebuggerDisplay() - { - return NewMethod.FullName; - } -} diff --git a/Il2CppInterop.Generator/Contexts/RewriteGlobalContext.cs b/Il2CppInterop.Generator/Contexts/RewriteGlobalContext.cs deleted file mode 100644 index 76c1ea2b..00000000 --- a/Il2CppInterop.Generator/Contexts/RewriteGlobalContext.cs +++ /dev/null @@ -1,243 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.MetadataAccess; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Contexts; - -public class RewriteGlobalContext : IDisposable -{ - internal readonly List MethodStartAddresses = new(); - - private readonly Dictionary myAssemblies = new(); - private readonly Dictionary myAssembliesByOld = new(); - private readonly Dictionary myAssembliesByNew = new(); - internal readonly Dictionary PreviousRenamedTypes = new(); - internal readonly Dictionary RenamedTypes = new(); - - internal readonly Dictionary<(object?, string, int), List> RenameGroups = new(); - - internal readonly Dictionary ImportsMap = new(); - - public RewriteGlobalContext(GeneratorOptions options, IIl2CppMetadataAccess gameAssemblies, - IMetadataAccess unityAssemblies) - { - Options = options; - GameAssemblies = gameAssemblies; - UnityAssemblies = unityAssemblies; - - Il2CppAssemblyResolver assemblyResolver = new(); - - foreach (var sourceAssembly in gameAssemblies.Assemblies) - { - var assemblyName = sourceAssembly.Name!; - if (assemblyName == "Il2CppDummyDll") - { - continue; - } - - var newAssembly = new AssemblyDefinition(sourceAssembly.Name.UnSystemify(options), sourceAssembly.Version); - var newModule = new ModuleDefinition(sourceAssembly.ManifestModule?.Name.UnSystemify(options), CorlibReferences.TargetCorlib); - newAssembly.Modules.Add(newModule); - - newModule.MetadataResolver = new DefaultMetadataResolver(assemblyResolver); - assemblyResolver.AddToCache(newAssembly); - - var assemblyRewriteContext = new AssemblyRewriteContext(this, sourceAssembly, newAssembly); - AddAssemblyContext(assemblyName, assemblyRewriteContext); - } - } - - public GeneratorOptions Options { get; } - public IIl2CppMetadataAccess GameAssemblies { get; } - public IMetadataAccess UnityAssemblies { get; } - - public IEnumerable Assemblies => myAssemblies.Values; - public AssemblyRewriteContext CorLib => myAssemblies["mscorlib"]; - - internal bool HasGcWbarrierFieldWrite { get; set; } - - public void Dispose() - { - UnityAssemblies.Dispose(); - } - - internal void AddAssemblyContext(string assemblyName, AssemblyRewriteContext context) - { - myAssemblies[assemblyName] = context; - if (context.OriginalAssembly != null) - myAssembliesByOld[context.OriginalAssembly] = context; - myAssembliesByNew[context.NewAssembly] = context; - } - - public AssemblyRewriteContext GetNewAssemblyForOriginal(AssemblyDefinition oldAssembly) - { - return myAssembliesByOld[oldAssembly]; - } - - public TypeRewriteContext GetNewTypeForOriginal(TypeDefinition originalType) - { - return GetNewAssemblyForOriginal(originalType.DeclaringModule!.Assembly!) - .GetContextForOriginalType(originalType); - } - - public TypeRewriteContext? TryGetNewTypeForOriginal(TypeDefinition originalType) - { - if (!myAssembliesByOld.TryGetValue(originalType.DeclaringModule!.Assembly!, out var assembly)) - return null; - return assembly.TryGetContextForOriginalType(originalType); - } - - public TypeRewriteContext.TypeSpecifics JudgeSpecificsByOriginalType(TypeSignature typeRef) - { - if (typeRef.IsPrimitive() || typeRef is PointerTypeSignature || typeRef.FullName == "System.TypedReference") - return TypeRewriteContext.TypeSpecifics.BlittableStruct; - if (typeRef - is CorLibTypeSignature { ElementType: ElementType.String or ElementType.Object } - or ArrayBaseTypeSignature - or ByReferenceTypeSignature - or GenericParameterSignature - or GenericInstanceTypeSignature) - return TypeRewriteContext.TypeSpecifics.ReferenceType; - - var fieldTypeContext = GetNewTypeForOriginal(typeRef.Resolve() ?? throw new($"Could not resolve {typeRef.FullName}")); - return fieldTypeContext.ComputedTypeSpecifics; - } - - public AssemblyRewriteContext GetAssemblyByName(string name) - { - return myAssemblies[name]; - } - - public AssemblyRewriteContext? TryGetAssemblyByName(string? name) - { - if (name is null) - return null; - - if (myAssemblies.TryGetValue(name, out var result)) - return result; - - if (name == "netstandard") - return myAssemblies.TryGetValue("mscorlib", out var result2) ? result2 : null; - - return null; - } - - public AssemblyRewriteContext GetContextForNewAssembly(AssemblyDefinition assembly) - { - return myAssembliesByNew[assembly]; - } - - public TypeRewriteContext GetContextForNewType(TypeDefinition type) - { - return GetContextForNewAssembly(type.DeclaringModule!.Assembly!).GetContextForNewType(type); - } - - public MethodDefinition? CreateParamsMethod(MethodDefinition originalMethod, MethodDefinition newMethod, - RuntimeAssemblyReferences imports, Func resolve) - { - if (newMethod.Name == "Invoke") - return null; - - var paramsParameters = originalMethod.Parameters.Where(parameter => - parameter.IsParamsArray() && resolve(((ArrayBaseTypeSignature)parameter.ParameterType).BaseType) is not null and not GenericParameterSignature - ).ToArray(); - - if (paramsParameters.Any()) - { - var paramsMethod = new MethodDefinition(newMethod.Name, newMethod.Attributes, MethodSignatureCreator.CreateMethodSignature(newMethod.Attributes, newMethod.Signature!.ReturnType, newMethod.Signature.GenericParameterCount)); - foreach (var genericParameter in originalMethod.GenericParameters) - { - var newGenericParameter = new GenericParameter(genericParameter.Name.MakeValidInSource(), genericParameter.Attributes); - - foreach (var constraint in genericParameter.Constraints) - { - var newConstraintType = constraint.Constraint != null ? resolve(constraint.Constraint.ToTypeSignature())?.ToTypeDefOrRef() : null; - var newConstraint = new GenericParameterConstraint(newConstraintType); - - // We don't need to copy custom attributes on constraints for generic parameters because Il2Cpp doesn't support them. - - newGenericParameter.Constraints.Add(newConstraint); - } - - // Similarly, custom attributes on generic parameters are also stripped by Il2Cpp, so we don't need to copy them. - - paramsMethod.GenericParameters.Add(newGenericParameter); - } - - foreach (var originalParameter in originalMethod.Parameters) - { - var isParams = paramsParameters.Contains(originalParameter); - - TypeSignature? convertedType; - if (isParams && originalParameter.ParameterType is ArrayBaseTypeSignature arrayType) - { - var resolvedElementType = resolve(arrayType.GetElementType()); - convertedType = arrayType is SzArrayTypeSignature - ? resolvedElementType?.MakeSzArrayType() - : resolvedElementType?.MakeArrayType(arrayType.Rank); - } - else - { - convertedType = resolve(originalParameter.ParameterType); - } - - if (convertedType == null) - { - throw new($"Could not resolve parameter type {originalParameter.ParameterType.FullName}"); - } - - var parameter = paramsMethod.AddParameter(convertedType, originalParameter.Name, originalParameter.Definition?.Attributes ?? default); - - if (isParams) - parameter.Definition!.CustomAttributes.Add(new CustomAttribute(newMethod.DeclaringModule!.ParamArrayAttributeCtor())); - } - - paramsMethod.CilMethodBody = new(); - var body = paramsMethod.CilMethodBody.Instructions; - - if (!newMethod.IsStatic) - { - body.Add(OpCodes.Ldarg_0); - } - - for (var i = 0; i < newMethod.Parameters.Count; i++) - { - body.Add(OpCodes.Ldarg, newMethod.Parameters[i]); - - var parameter = originalMethod.Parameters[i]; - if (paramsParameters.Contains(parameter)) - { - var parameterType = (ArrayBaseTypeSignature)parameter.ParameterType; - - IMethodDescriptor constructorReference; - - var elementType = parameterType.GetElementType(); - if (elementType.FullName == "System.String") - { - constructorReference = imports.Il2CppStringArrayctor.Value; - } - else - { - var convertedElementType = resolve(elementType)!; - - constructorReference = imports.Module.DefaultImporter.ImportMethod(convertedElementType.IsValueType() - ? imports.Il2CppStructArrayctor.Get(convertedElementType) - : imports.Il2CppRefrenceArrayctor.Get(convertedElementType)); - } - - body.Add(OpCodes.Newobj, constructorReference); - } - } - - body.Add(OpCodes.Call, newMethod); - body.Add(OpCodes.Ret); - - return paramsMethod; - } - - return null; - } -} diff --git a/Il2CppInterop.Generator/Contexts/TypeRewriteContext.cs b/Il2CppInterop.Generator/Contexts/TypeRewriteContext.cs deleted file mode 100644 index 23b9e8b9..00000000 --- a/Il2CppInterop.Generator/Contexts/TypeRewriteContext.cs +++ /dev/null @@ -1,190 +0,0 @@ -using System.Diagnostics; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Contexts; - -[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")] -public class TypeRewriteContext -{ - public enum TypeSpecifics - { - NotComputed, - Computing, - ReferenceType, - BlittableStruct, - NonBlittableStruct - } - - public readonly AssemblyRewriteContext AssemblyContext; - - private readonly Dictionary myFieldContexts = new(); - private readonly Dictionary myMethodContexts = new(); - private readonly Dictionary myMethodContextsByName = new(); - public readonly TypeDefinition NewType; - - public readonly bool OriginalNameWasObfuscated; -#nullable disable - // OriginalType is null for unstripped types, but we don't want to warn anywhere, - // including in the constructor, so we disable all null tracking for this field. - public readonly TypeDefinition OriginalType; -#nullable enable - - public TypeSpecifics ComputedTypeSpecifics; - - public TypeRewriteContext(AssemblyRewriteContext assemblyContext, TypeDefinition? originalType, - TypeDefinition newType) - { - AssemblyContext = assemblyContext ?? throw new ArgumentNullException(nameof(assemblyContext)); - OriginalType = originalType; - NewType = newType ?? throw new ArgumentNullException(nameof(newType)); - - if (OriginalType == null) return; - - OriginalNameWasObfuscated = OriginalType.Name != NewType.Name; - if (OriginalNameWasObfuscated) - NewType.CustomAttributes.Add(new CustomAttribute( - (ICustomAttributeType)assemblyContext.Imports.ObfuscatedNameAttributector.Value, - new CustomAttributeSignature(new CustomAttributeArgument(assemblyContext.Imports.Module.String(), OriginalType.FullName)))); - - if (!OriginalType.IsValueType()) - ComputedTypeSpecifics = TypeSpecifics.ReferenceType; - else if (OriginalType.IsEnum) - ComputedTypeSpecifics = TypeSpecifics.BlittableStruct; - else if (OriginalType.HasGenericParameters()) - ComputedTypeSpecifics = TypeSpecifics.NonBlittableStruct; // not reference type, covered by first if - } - - // These are initialized in AddMembers, which is called from an early rewrite pass. - public IFieldDescriptor ClassPointerFieldRef { get; private set; } = null!; - public ITypeDefOrRef SelfSubstitutedRef { get; private set; } = null!; - - public IEnumerable Fields => myFieldContexts.Values; - public IEnumerable Methods => myMethodContexts.Values; - - public void AddMembers() - { - if (NewType.HasGenericParameters()) - { - var genericInstanceType = new GenericInstanceTypeSignature(NewType, NewType.IsValueType()); - foreach (var newTypeGenericParameter in NewType.GenericParameters) - genericInstanceType.TypeArguments.Add(newTypeGenericParameter.ToTypeSignature()); - SelfSubstitutedRef = NewType.DeclaringModule!.DefaultImporter.ImportTypeSignature(genericInstanceType).ToTypeDefOrRef(); - var genericTypeRef = new GenericInstanceTypeSignature( - AssemblyContext.Imports.Il2CppClassPointerStore.ToTypeDefOrRef(), - AssemblyContext.Imports.Il2CppClassPointerStore.IsValueType(), - SelfSubstitutedRef.ToTypeSignature()); - ClassPointerFieldRef = ReferenceCreator.CreateFieldReference("NativeClassPtr", AssemblyContext.Imports.Module.IntPtr(), - NewType.DeclaringModule.DefaultImporter.ImportType(genericTypeRef.ToTypeDefOrRef())); - } - else - { - SelfSubstitutedRef = NewType; - var genericTypeRef = new GenericInstanceTypeSignature( - AssemblyContext.Imports.Il2CppClassPointerStore.ToTypeDefOrRef(), - AssemblyContext.Imports.Il2CppClassPointerStore.IsValueType()); - if (OriginalType.ToTypeSignature().IsPrimitive() || OriginalType.FullName == "System.String") - genericTypeRef.TypeArguments.Add( - NewType.DeclaringModule!.ImportCorlibReference(OriginalType.FullName)); - else - genericTypeRef.TypeArguments.Add(SelfSubstitutedRef.ToTypeSignature()); - ClassPointerFieldRef = ReferenceCreator.CreateFieldReference("NativeClassPtr", AssemblyContext.Imports.Module.IntPtr(), - NewType.DeclaringModule!.DefaultImporter.ImportType(genericTypeRef.ToTypeDefOrRef())); - } - - if (OriginalType.IsEnum) return; - - var renamedFieldCounts = new Dictionary(); - - foreach (var originalTypeField in OriginalType.Fields) - myFieldContexts[originalTypeField] = new FieldRewriteContext(this, originalTypeField, renamedFieldCounts); - - var hasExtensionMethods = false; - - foreach (var originalTypeMethod in OriginalType.Methods) - { - if (originalTypeMethod.IsStatic && originalTypeMethod.IsConstructor) - continue; - if (originalTypeMethod.IsConstructor - && originalTypeMethod.Parameters is [{ ParameterType: CorLibTypeSignature { ElementType: ElementType.I } }]) - continue; - var modules = this.AssemblyContext.GlobalContext.Assemblies.Select(a => a.OriginalAssembly.ManifestModule!); - - var methodRewriteContext = new MethodRewriteContext(this, originalTypeMethod); - myMethodContexts[originalTypeMethod] = methodRewriteContext; - myMethodContextsByName[originalTypeMethod.Name!] = methodRewriteContext; - - if (methodRewriteContext.HasExtensionAttribute) hasExtensionMethods = true; - } - - if (hasExtensionMethods) - NewType.CustomAttributes.Add(new CustomAttribute(AssemblyContext.Imports.Module.ExtensionAttributeCtor())); - } - - public FieldRewriteContext GetFieldByOldField(FieldDefinition field) - { - return myFieldContexts[field]; - } - - public MethodRewriteContext GetMethodByOldMethod(MethodDefinition method) - { - return myMethodContexts[method]; - } - - public MethodRewriteContext? TryGetMethodByOldMethod(MethodDefinition method) - { - return myMethodContexts.TryGetValue(method, out var result) ? result : null; - } - - public MethodRewriteContext? TryGetMethodByName(string name) - { - return myMethodContextsByName.TryGetValue(name, out var result) ? result : null; - } - - public MethodRewriteContext? TryGetMethodByUnityAssemblyMethod(MethodDefinition method) - { - foreach (var methodRewriteContext in myMethodContexts) - { - var originalMethod = methodRewriteContext.Value.OriginalMethod; - if (originalMethod.Name != method.Name) continue; - if (originalMethod.Parameters.Count != method.Parameters.Count) continue; - var badMethod = false; - for (var i = 0; i < originalMethod.Parameters.Count; i++) - if (originalMethod.Parameters[i].ParameterType.FullName != method.Parameters[i].ParameterType.FullName) - { - badMethod = true; - break; - } - - if (badMethod) continue; - - return methodRewriteContext.Value; - } - - return null; - } - - public FieldRewriteContext? TryGetFieldByUnityAssemblyField(FieldDefinition field) - { - foreach (var fieldRewriteContext in myFieldContexts) - { - var originalField = fieldRewriteContext.Value.OriginalField; - if (originalField.Name != field.Name) continue; - - if (originalField.Signature?.FieldType.FullName != field.Signature?.FieldType.FullName) - continue; - - return fieldRewriteContext.Value; - } - - return null; - } - - private string GetDebuggerDisplay() - { - return NewType.FullName; - } -} diff --git a/Il2CppInterop.Generator/DelegateConversionProcessingLayer.cs b/Il2CppInterop.Generator/DelegateConversionProcessingLayer.cs new file mode 100644 index 00000000..1b96ad77 --- /dev/null +++ b/Il2CppInterop.Generator/DelegateConversionProcessingLayer.cs @@ -0,0 +1,317 @@ +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; +using Il2CppInterop.Generator.Visitors; +using Il2CppInterop.Runtime; + +namespace Il2CppInterop.Generator; + +public class DelegateConversionProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Delegate Conversion"; + + public override string Id => "delegate_conversion"; + + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + PolyfillActionFuncDelegates(appContext); + + var multicastDelegateType = appContext.Mscorlib.GetTypeByFullNameOrThrow("System.MulticastDelegate"); + var asyncCallbackType = appContext.Mscorlib.GetTypeByFullNameOrThrow("System.AsyncCallback"); + var iasyncResultType = appContext.Mscorlib.GetTypeByFullNameOrThrow("System.IAsyncResult"); + + var delegateSupportType = appContext.ResolveTypeOrThrow(typeof(DelegateSupport)); + var delegateSupportMethod = delegateSupportType.GetMethodByName(nameof(DelegateSupport.ConvertDelegate)); + + var il2CppSystemDelegate = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Delegate"); + var il2CppSystemDelegateCombine = il2CppSystemDelegate.Methods.Single(m => m.Name == "Combine" && m.Parameters.Count == 2); + var il2CppSystemDelegateRemove = il2CppSystemDelegate.GetMethodByName("Remove"); + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + // for rather than foreach, as we will be adding items to the collection + for (var typeIndex = 0; typeIndex < assembly.Types.Count; typeIndex++) + { + var type = assembly.Types[typeIndex]; + if (type.BaseType is not { Namespace: "Il2CppSystem", Name: "MulticastDelegate" }) + continue; + + // Remove variance on generic parameters because only interfaces and (real) delegates can have variance. + // https://github.com/dotnet/csharplang/discussions/2498 + // https://learn.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance + { + // Before: Func + // After: Func + // + // This inherently makes some code invalid, both normal and unstripped. + // There's probably no (good) way to fix that because the runtime doesn't actually change the type. + // https://lab.razor.fyi/#hZDBSsRADIaRetCcxCfIcXrpA7giqIsFURFcEDw5zoYamM6UmbSwLD36BB68-wC-nyels3VVFM0hhPzk-38CzxnAZfBV0HVh4u5j1kZ2FV4tolA9AWjaO8sGjdUx4pGOhEvov2-nFLijOe596ith5MISEBGjaGGDnec5nmt2Kk_rlTjUsXfRWyquAwudsSNVkoxwlRclyWzRkMqLk9baC11TPkm3fepN4E4LfdgcGmHv9odEB1iSDMMPx0DSBodTP7tnV_1PG8Mk4DrY78y15R_U9IrRXKXfiQ4VyVdkDz2cZqF1N9tbLw9vT6-0s3m78Q4 + // One way the problem might be solved is to: + // * Generate a nested interface for every Il2Cpp delegate type, eg IFunc. + // * Replace all occurances of the class type with the interface type, everywhere. + // * During unstripping, redirect references to the class methods to the interface methods. + + foreach (var genericParameter in type.GenericParameters) + { + genericParameter.OverrideAttributes = genericParameter.Attributes & ~GenericParameterAttributes.VarianceMask; + } + } + + var invokeMethod = type.TryGetMethodByName("Invoke"); + if (invokeMethod is null) + { + // The delegate does not have an Invoke method. + // This can happen if the delegate is unstripped, but a parameter type could not unstripped. + continue; + } + + TypeAnalysisContext managedDelegateType; + + if (invokeMethod.Parameters.Count <= 16 + && invokeMethod.ReturnType is not PointerTypeAnalysisContext and not ByRefTypeAnalysisContext + && invokeMethod.Parameters.All(p => p.ParameterType is not PointerTypeAnalysisContext and not ByRefTypeAnalysisContext)) + { + // We can use a System delegate + + if (!invokeMethod.IsVoid) + { + var systemType = invokeMethod.Parameters.Count switch + { + 0 => typeof(Func<>), + 1 => typeof(Func<,>), + 2 => typeof(Func<,,>), + 3 => typeof(Func<,,,>), + 4 => typeof(Func<,,,,>), + 5 => typeof(Func<,,,,,>), + 6 => typeof(Func<,,,,,,>), + 7 => typeof(Func<,,,,,,,>), + 8 => typeof(Func<,,,,,,,,>), + 9 => typeof(Func<,,,,,,,,,>), + 10 => typeof(Func<,,,,,,,,,,>), + 11 => typeof(Func<,,,,,,,,,,,>), + 12 => typeof(Func<,,,,,,,,,,,,>), + 13 => typeof(Func<,,,,,,,,,,,,,>), + 14 => typeof(Func<,,,,,,,,,,,,,,>), + 15 => typeof(Func<,,,,,,,,,,,,,,,>), + 16 => typeof(Func<,,,,,,,,,,,,,,,,>), + _ => default!, // unreachable + }; + managedDelegateType = appContext.ResolveTypeOrThrow(systemType) + .MakeGenericInstanceType(invokeMethod.Parameters.Select(p => p.ParameterType).Append(invokeMethod.ReturnType)); + } + else if (invokeMethod.Parameters.Count > 0) + { + var systemType = invokeMethod.Parameters.Count switch + { + 1 => typeof(Action<>), + 2 => typeof(Action<,>), + 3 => typeof(Action<,,>), + 4 => typeof(Action<,,,>), + 5 => typeof(Action<,,,,>), + 6 => typeof(Action<,,,,,>), + 7 => typeof(Action<,,,,,,>), + 8 => typeof(Action<,,,,,,,>), + 9 => typeof(Action<,,,,,,,,>), + 10 => typeof(Action<,,,,,,,,,>), + 11 => typeof(Action<,,,,,,,,,,>), + 12 => typeof(Action<,,,,,,,,,,,>), + 13 => typeof(Action<,,,,,,,,,,,,>), + 14 => typeof(Action<,,,,,,,,,,,,,>), + 15 => typeof(Action<,,,,,,,,,,,,,,>), + 16 => typeof(Action<,,,,,,,,,,,,,,,>), + _ => default!, // unreachable + }; + managedDelegateType = appContext.ResolveTypeOrThrow(systemType).MakeGenericInstanceType(invokeMethod.Parameters.Select(p => p.ParameterType)); + } + else + { + managedDelegateType = appContext.ResolveTypeOrThrow(typeof(Action)); + } + } + else + { + // We need to create a new delegate type + + var name = type.Name is "Delegate" ? "Converted" : "Delegate"; // Name can't be the same as the declaring type + managedDelegateType = type.InjectNestedType( + name, + multicastDelegateType); + + managedDelegateType.CopyGenericParameters(type, true); + + TypeAnalysisContext returnType; + List parameterTypes = invokeMethod.Parameters.Select(p => p.ParameterType).ToList(); + { + var genericParameterDictionary = Enumerable.Range(0, type.GenericParameters.Count) + .ToDictionary(i => type.GenericParameters[i], i => managedDelegateType.GenericParameters[i]); + var replacementVisitor = new TypeReplacementVisitor(genericParameterDictionary); + replacementVisitor.Modify(parameterTypes); + returnType = replacementVisitor.Replace(invokeMethod.ReturnType); + } + + // Constructor + { + managedDelegateType.Methods.Add(new InjectedMethodAnalysisContext( + managedDelegateType, + ".ctor", + appContext.SystemTypes.SystemVoidType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, + [appContext.SystemTypes.SystemObjectType, appContext.SystemTypes.SystemIntPtrType], + defaultImplAttributes: MethodImplAttributes.Runtime)); + } + + // Invoke + { + managedDelegateType.Methods.Add(new InjectedMethodAnalysisContext( + managedDelegateType, + "Invoke", + returnType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + parameterTypes.ToArray(), + defaultImplAttributes: MethodImplAttributes.Runtime)); + } + + // BeginInvoke + { + managedDelegateType.Methods.Add(new InjectedMethodAnalysisContext( + managedDelegateType, + "BeginInvoke", + iasyncResultType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + parameterTypes.Append(asyncCallbackType).Append(appContext.SystemTypes.SystemObjectType).ToArray(), + defaultImplAttributes: MethodImplAttributes.Runtime)); + } + + // EndInvoke + { + managedDelegateType.Methods.Add(new InjectedMethodAnalysisContext( + managedDelegateType, + "EndInvoke", + returnType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + [iasyncResultType], + defaultImplAttributes: MethodImplAttributes.Runtime)); + } + } + + var concreteType = type.HasGenericParameters ? type.MakeGenericInstanceType(type.GenericParameters) : type; + var explicitConversion = new InjectedMethodAnalysisContext( + type, + "op_Explicit", + concreteType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName, + [managedDelegateType]); + type.Methods.Add(explicitConversion); + explicitConversion.PutExtraData(new() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Call, delegateSupportMethod.MakeGenericInstanceMethod(concreteType)), + new Instruction(CilOpCodes.Ret), + ] + }); + + var addition = new InjectedMethodAnalysisContext( + type, + "op_Addition", + concreteType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName, + [concreteType, concreteType]); + type.Methods.Add(addition); + addition.PutExtraData(new() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Ldarg_1), + new Instruction(CilOpCodes.Call, il2CppSystemDelegateCombine), + new Instruction(CilOpCodes.Castclass, concreteType), + new Instruction(CilOpCodes.Ret), + ] + }); + + var subtraction = new InjectedMethodAnalysisContext( + type, + "op_Subtraction", + concreteType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName, + [concreteType, concreteType]); + type.Methods.Add(subtraction); + subtraction.PutExtraData(new() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Ldarg_1), + new Instruction(CilOpCodes.Call, il2CppSystemDelegateRemove), + new Instruction(CilOpCodes.Castclass, concreteType), + new Instruction(CilOpCodes.Ret), + ] + }); + } + } + } + + /// + /// mscorlib only contains Action and Func delegates with up to 8 parameters. + /// However, System.Private.CoreLib contains Action and Func delegates with up to 16 parameters. + /// This method will create the Action and Func delegates with more than 8 parameters, + /// so that they can be used when the mscorlib assembly references are replaced with System.Private.CoreLib references. + /// + /// + private static void PolyfillActionFuncDelegates(ApplicationAnalysisContext appContext) + { + var mscorlib = appContext.AssembliesByName["mscorlib"]; + + ReadOnlySpan types = + [ + typeof(Action), + typeof(Action<>), + typeof(Action<,>), + typeof(Action<,,>), + typeof(Action<,,,>), + typeof(Action<,,,,>), + typeof(Action<,,,,,>), + typeof(Action<,,,,,,>), + typeof(Action<,,,,,,,>), + typeof(Action<,,,,,,,,>), + typeof(Action<,,,,,,,,,>), + typeof(Action<,,,,,,,,,,>), + typeof(Action<,,,,,,,,,,,>), + typeof(Action<,,,,,,,,,,,,>), + typeof(Action<,,,,,,,,,,,,,>), + typeof(Action<,,,,,,,,,,,,,,>), + typeof(Action<,,,,,,,,,,,,,,,>), + typeof(Func<>), + typeof(Func<,>), + typeof(Func<,,>), + typeof(Func<,,,>), + typeof(Func<,,,,>), + typeof(Func<,,,,,>), + typeof(Func<,,,,,,>), + typeof(Func<,,,,,,,>), + typeof(Func<,,,,,,,,>), + typeof(Func<,,,,,,,,,>), + typeof(Func<,,,,,,,,,,>), + typeof(Func<,,,,,,,,,,,>), + typeof(Func<,,,,,,,,,,,,>), + typeof(Func<,,,,,,,,,,,,,>), + typeof(Func<,,,,,,,,,,,,,,>), + typeof(Func<,,,,,,,,,,,,,,,>), + typeof(Func<,,,,,,,,,,,,,,,,>), + ]; + + foreach (var type in types) + { + if (mscorlib.GetTypeByFullName(type.FullName!) is not null) + continue; + + mscorlib.InjectType(type).InjectContentFromSourceType(); + } + } +} diff --git a/Il2CppInterop.Generator/EnumProcessingLayer.cs b/Il2CppInterop.Generator/EnumProcessingLayer.cs new file mode 100644 index 00000000..c0195472 --- /dev/null +++ b/Il2CppInterop.Generator/EnumProcessingLayer.cs @@ -0,0 +1,287 @@ +using System.Diagnostics; +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; + +namespace Il2CppInterop.Generator; + +public class EnumProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Enum Processor"; + public override string Id => "enum_processor"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var il2CppSystemObject = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Object"); + var il2CppSystemValueType = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.ValueType"); + var il2CppSystemEnum = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Enum"); + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.DefaultBaseType != il2CppSystemEnum) + continue; + + // Sequential layout is required to ensure that this struct has the same memory layout as a C# enum. + type.OverrideAttributes = (type.Attributes & ~TypeAttributes.LayoutMask) | TypeAttributes.SequentialLayout; + + var valueField = type.Fields.First(f => f.Name == "value__"); + + valueField.OverrideAttributes = FieldAttributes.Private; + + Debug.Assert(valueField.FieldType == valueField.DefaultFieldType, "Field type should not be overriden."); + + var il2CppType = valueField.FieldType; + var monoType = appContext.Mscorlib.GetTypeByFullNameOrThrow($"System.{il2CppType.Name}"); + + Debug.Assert(monoType != il2CppType); + + type.EnumIl2CppUnderlyingType = il2CppType; + type.EnumMonoUnderlyingType = monoType; + + // Constructor + var constructor = new InjectedMethodAnalysisContext( + type, + ".ctor", + appContext.SystemTypes.SystemVoidType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, + [il2CppType], + ["value"]) + { + IsInjected = true, + }; + { + type.Methods.Add(constructor); + + var methodBody = new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Ldarg_1), + new Instruction(CilOpCodes.Stfld, valueField), + new Instruction(CilOpCodes.Ret) + ] + }; + + constructor.PutExtraData(methodBody); + } + + #region Conversions + // Conversion: enum -> il2cpp + var conversionEnumIl2Cpp = new InjectedMethodAnalysisContext( + type, + "op_Explicit", + il2CppType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName, + [type], + ["value"]) + { + IsInjected = true, + }; + { + type.Methods.Add(conversionEnumIl2Cpp); + var methodBody = new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarga, conversionEnumIl2Cpp.Parameters[0]), + new Instruction(CilOpCodes.Ldfld, valueField), + new Instruction(CilOpCodes.Ret) + ] + }; + conversionEnumIl2Cpp.PutExtraData(methodBody); + } + + // Conversion: il2cpp -> enum + var conversionIl2CppEnum = new InjectedMethodAnalysisContext( + type, + "op_Explicit", + type, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName, + [il2CppType], + ["value"]) + { + IsInjected = true, + }; + { + type.Methods.Add(conversionIl2CppEnum); + var methodBody = new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Newobj, constructor), + new Instruction(CilOpCodes.Ret) + ] + }; + conversionIl2CppEnum.PutExtraData(methodBody); + } + + // Conversion: enum -> mono + var conversionEnumMono = new InjectedMethodAnalysisContext( + type, + "op_Explicit", + monoType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName, + [type], + ["value"]) + { + IsInjected = true, + }; + { + type.Methods.Add(conversionEnumMono); + var methodBody = new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Call, conversionEnumIl2Cpp), + new Instruction(CilOpCodes.Call, il2CppType.GetImplicitConversionTo(monoType)), + new Instruction(CilOpCodes.Ret) + ] + }; + conversionEnumMono.PutExtraData(methodBody); + } + + // Conversion: mono -> enum + var conversionMonoEnum = new InjectedMethodAnalysisContext( + type, + "op_Explicit", + type, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName, + [monoType], + ["value"]) + { + IsInjected = true, + }; + { + type.Methods.Add(conversionMonoEnum); + var methodBody = new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Call, il2CppType.GetImplicitConversionFrom(monoType)), + new Instruction(CilOpCodes.Call, conversionIl2CppEnum), + new Instruction(CilOpCodes.Ret) + ] + }; + conversionMonoEnum.PutExtraData(methodBody); + } + #endregion + + #region Bitwise Operators + // & + { + var method = new InjectedMethodAnalysisContext( + type, + "op_BitwiseAnd", + type, + MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName, + [type, type]) + { + IsInjected = true, + }; + + type.Methods.Add(method); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Call, conversionEnumMono), + new Instruction(CilOpCodes.Ldarg_1), + new Instruction(CilOpCodes.Call, conversionEnumMono), + new Instruction(CilOpCodes.And), + new Instruction(CilOpCodes.Call, conversionMonoEnum), + new Instruction(CilOpCodes.Ret) + ] + }); + } + + // | + { + var method = new InjectedMethodAnalysisContext( + type, + "op_BitwiseOr", + type, + MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName, + [type, type]) + { + IsInjected = true, + }; + + type.Methods.Add(method); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Call, conversionEnumMono), + new Instruction(CilOpCodes.Ldarg_1), + new Instruction(CilOpCodes.Call, conversionEnumMono), + new Instruction(CilOpCodes.Or), + new Instruction(CilOpCodes.Call, conversionMonoEnum), + new Instruction(CilOpCodes.Ret) + ] + }); + } + + // ^ + { + var method = new InjectedMethodAnalysisContext( + type, + "op_ExclusiveOr", + type, + MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName, + [type, type]) + { + IsInjected = true, + }; + + type.Methods.Add(method); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Call, conversionEnumMono), + new Instruction(CilOpCodes.Ldarg_1), + new Instruction(CilOpCodes.Call, conversionEnumMono), + new Instruction(CilOpCodes.Xor), + new Instruction(CilOpCodes.Call, conversionMonoEnum), + new Instruction(CilOpCodes.Ret) + ] + }); + } + + // ~ + { + var method = new InjectedMethodAnalysisContext( + type, + "op_OnesComplement", + type, + MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName, + [type]) + { + IsInjected = true, + }; + + type.Methods.Add(method); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Call, conversionEnumMono), + new Instruction(CilOpCodes.Not), + new Instruction(CilOpCodes.Call, conversionMonoEnum), + new Instruction(CilOpCodes.Ret) + ] + }); + } + #endregion + } + } + } +} diff --git a/Il2CppInterop.Generator/EventProcessingLayer.cs b/Il2CppInterop.Generator/EventProcessingLayer.cs new file mode 100644 index 00000000..9d04b70a --- /dev/null +++ b/Il2CppInterop.Generator/EventProcessingLayer.cs @@ -0,0 +1,66 @@ +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Core.Model.CustomAttributes; +using Il2CppInterop.Common.Attributes; + +namespace Il2CppInterop.Generator; + +public class EventProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Event Processor"; + + public override string Id => "event_processor"; + + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + // C# requires events to have a delegate type and will not allow event syntax without it. + // https://github.com/ds5678/Il2CppEventTest + // We remove the event definitions and add attributes to the add/remove/invoke methods instead. + + var il2CppEventAttribute = appContext.ResolveTypeOrThrow(typeof(Il2CppEventAttribute)); + var il2CppEventAttributeConstructor = il2CppEventAttribute.GetMethodByName(".ctor"); + + var il2CppMemberAttribute = appContext.ResolveTypeOrThrow(typeof(Il2CppMemberAttribute)); + var il2CppMemberAttributeName = il2CppMemberAttribute.GetPropertyByName(nameof(Il2CppMemberAttribute.Name)); + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + + for (var i = 0; i < type.Events.Count; i++) + { + var @event = type.Events[i]; + if (@event.IsInjected) + continue; + + AddAttribute(@event.Adder, @event.DefaultName, il2CppEventAttributeConstructor, il2CppMemberAttributeName); + AddAttribute(@event.Remover, @event.DefaultName, il2CppEventAttributeConstructor, il2CppMemberAttributeName); + AddAttribute(@event.Invoker, @event.DefaultName, il2CppEventAttributeConstructor, il2CppMemberAttributeName); + + type.Events.RemoveAt(i); + } + } + } + } + + private static void AddAttribute(MethodAnalysisContext? method, string name, MethodAnalysisContext il2CppEventAttributeConstructor, PropertyAnalysisContext il2CppMemberAttributeName) + { + if (method is not null) + { + var attribute = new AnalyzedCustomAttribute(il2CppEventAttributeConstructor); + var parameter = new CustomAttributePrimitiveParameter(name, attribute, CustomAttributeParameterKind.Property, 0); + attribute.Properties.Add(new CustomAttributeProperty(il2CppMemberAttributeName, parameter)); + method.CustomAttributes ??= new(1); + method.CustomAttributes.Add(attribute); + + method.OverrideAttributes = method.Attributes & ~MethodAttributes.SpecialName; + } + } +} diff --git a/Il2CppInterop.Generator/ExceptionHierarchyProcessingLayer.cs b/Il2CppInterop.Generator/ExceptionHierarchyProcessingLayer.cs new file mode 100644 index 00000000..da424e28 --- /dev/null +++ b/Il2CppInterop.Generator/ExceptionHierarchyProcessingLayer.cs @@ -0,0 +1,190 @@ +using System.Diagnostics; +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Visitors; +using Il2CppInterop.Runtime.Exceptions; + +namespace Il2CppInterop.Generator; + +public class ExceptionHierarchyProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Exception Hierarchy"; + public override string Id => "exception_hierarchy"; + + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var il2CppSystemExceptionType = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Exception"); + + var il2CppExceptionInterface = appContext.ResolveTypeOrThrow(typeof(IIl2CppException)); + var il2CppExceptionInterfaceMethod = il2CppExceptionInterface.GetMethodByName(nameof(IIl2CppException.CreateSystemException)); + var il2CppExceptionInterfaceMethodName = $"{il2CppExceptionInterface.FullName}.{il2CppExceptionInterfaceMethod.Name}"; + + var exceptionTypes = GetExceptionTypes(appContext); + + foreach (var exceptionType in exceptionTypes) + { + var nestedExceptionType = exceptionType.InjectNestedType(GetUniqueNameForNestedClass(exceptionType), null, TypeAttributes.NestedPublic); + nestedExceptionType.IsInjected = true; + exceptionType.SystemExceptionType = nestedExceptionType; + + // Copy generic parameters + nestedExceptionType.CopyGenericParameters(exceptionType, true); + + // Inject constructor + nestedExceptionType.InjectMethodContext( + ".ctor", + appContext.SystemTypes.SystemVoidType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, + il2CppSystemExceptionType); + } + + foreach (var exceptionType in exceptionTypes) + { + var nestedExceptionType = exceptionType.SystemExceptionType!; + + // Base type + Debug.Assert(exceptionType.BaseType is not null); + if (exceptionType == il2CppSystemExceptionType) + { + nestedExceptionType.SetDefaultBaseType(appContext.ResolveTypeOrThrow(typeof(Il2CppException))); + } + else if (exceptionType.BaseType is GenericInstanceTypeAnalysisContext exceptionBaseTypeGenericInstance) + { + // Build replacements dictionary + Dictionary replacements = new(); + for (var i = 0; i < exceptionType.GenericParameters.Count; i++) + { + replacements.Add(exceptionType.GenericParameters[i], nestedExceptionType.GenericParameters[i]); + } + replacements.Add(exceptionBaseTypeGenericInstance.GenericType, exceptionBaseTypeGenericInstance.GenericType.SystemExceptionType!); + + nestedExceptionType.SetDefaultBaseType(new TypeReplacementVisitor(replacements).Replace(exceptionBaseTypeGenericInstance)); + } + else + { + nestedExceptionType.SetDefaultBaseType(exceptionType.BaseType.SystemExceptionType); + } + + // Constructor + Debug.Assert(nestedExceptionType.Methods.Count is 1); + var constructor = nestedExceptionType.Methods[0]; + Debug.Assert(nestedExceptionType.BaseType is not null); + MethodAnalysisContext baseConstructor; + if (nestedExceptionType.BaseType is GenericInstanceTypeAnalysisContext nestedExceptionBaseTypeGenericInstance) + { + baseConstructor = new ConcreteGenericMethodAnalysisContext( + nestedExceptionBaseTypeGenericInstance.GenericType.GetMethodByName(".ctor"), + nestedExceptionBaseTypeGenericInstance.GenericArguments, + []); + } + else + { + baseConstructor = nestedExceptionType.BaseType.GetMethodByName(".ctor"); + } + constructor.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new(CilOpCodes.Ldarg_0), // this + new(CilOpCodes.Ldarg_1), // object + new(CilOpCodes.Call, baseConstructor), + new(CilOpCodes.Ret), + ], + }); + + // Interface implementation + exceptionType.InterfaceContexts.Add(il2CppExceptionInterface); + { + var interfaceMethod = new InjectedMethodAnalysisContext( + exceptionType, + il2CppExceptionInterfaceMethodName, + il2CppExceptionInterfaceMethod.ReturnType, + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot, + []) + { + IsInjected = true + }; + exceptionType.Methods.Add(interfaceMethod); + interfaceMethod.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new(CilOpCodes.Ldarg_0), // this + new(CilOpCodes.Newobj, exceptionType.HasGenericParameters ? new ConcreteGenericMethodAnalysisContext(constructor, exceptionType.GenericParameters, []) : constructor), + new(CilOpCodes.Ret), + ], + }); + interfaceMethod.Overrides.Add(il2CppExceptionInterfaceMethod); + } + } + } + + private static HashSet GetExceptionTypes(ApplicationAnalysisContext appContext) + { + var exceptionTypes = new HashSet(); + var nonExceptionTypes = new HashSet(); + + // Add Il2CppSystem.Exception + { + exceptionTypes.Add(appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Exception")); + } + + // Add all derived types of Il2CppSystem.Exception + foreach (var type in appContext.AllTypes) + { + AddTypeToSet(type, exceptionTypes, nonExceptionTypes); + } + + return exceptionTypes; + + static bool AddTypeToSet(TypeAnalysisContext? type, HashSet exceptionTypes, HashSet nonExceptionTypes) + { + if (type is GenericInstanceTypeAnalysisContext genericInstance) + { + return AddTypeToSet(genericInstance.GenericType, exceptionTypes, nonExceptionTypes); + } + Debug.Assert(type is not ReferencedTypeAnalysisContext); + if (type == null) + { + return false; + } + if (exceptionTypes.Contains(type)) + { + return true; + } + if (nonExceptionTypes.Contains(type)) + { + return false; + } + var isException = AddTypeToSet(type.BaseType, exceptionTypes, nonExceptionTypes); + if (isException) + { + exceptionTypes.Add(type); + } + else + { + nonExceptionTypes.Add(type); + } + return isException; + } + } + + private static string GetUniqueNameForNestedClass(TypeAnalysisContext declaringType) + { + var genericParameterCount = declaringType.GenericParameters.Count; + var nonGenericName = "Exception"; + while (true) + { + var name = genericParameterCount > 0 ? $"{nonGenericName}`{genericParameterCount}" : nonGenericName; + if (declaringType.Name == name || declaringType.NestedTypes.Any(t => t.Name == name)) + { + nonGenericName += "_"; + } + else + { + return name; + } + } + } +} diff --git a/Il2CppInterop.Generator/Extensions/ApplicationAnalysisContextExtensions.cs b/Il2CppInterop.Generator/Extensions/ApplicationAnalysisContextExtensions.cs new file mode 100644 index 00000000..7f69a0a2 --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/ApplicationAnalysisContextExtensions.cs @@ -0,0 +1,59 @@ +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using Cpp2IL.Core.Model.Contexts; +using LibCpp2IL; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class ApplicationAnalysisContextExtensions +{ + extension(ApplicationAnalysisContext appContext) + { + public InjectedAssemblyAnalysisContext InjectAssembly(Assembly assembly) + { + return appContext.InjectAssembly(assembly.GetName()); + } + + public InjectedAssemblyAnalysisContext InjectAssembly(AssemblyName assemblyName) + { +#pragma warning disable SYSLIB0037 // Type or member is obsolete + return appContext.InjectAssembly( + assemblyName.Name!, + assemblyName.Version, + (uint)assemblyName.HashAlgorithm, + (uint)assemblyName.Flags, + assemblyName.CultureName, + assemblyName.GetPublicKeyToken(), + assemblyName.GetPublicKey()); +#pragma warning restore SYSLIB0037 // Type or member is obsolete + } + + public TypeAnalysisContext ResolveTypeOrThrow(Type? type) + { + return appContext.ResolveType(type) ?? throw new($"Unable to resolve type {type?.FullName}"); + } + + public TypeAnalysisContext? ResolveType(Type? type) + { + if (type is null) + return null; + + var assemblyName = type.Assembly.GetName().Name!; + if (assemblyName == "System.Private.CoreLib") + assemblyName = "mscorlib"; + var assembly = appContext.GetAssemblyByName(assemblyName); + return assembly?.GetTypeByFullName(type.FullName!); + } + + public AssemblyAnalysisContext Mscorlib => appContext.AssembliesByName["mscorlib"]; + public AssemblyAnalysisContext Il2CppMscorlib => appContext.AssembliesByName["Il2Cppmscorlib"]; + + [return: NotNullIfNotNull(nameof(methodReference))] + public ConcreteGenericMethodAnalysisContext? ResolveContextForMethod(Cpp2IlMethodRef? methodReference) + { + return methodReference is not null + ? appContext.ConcreteGenericMethodsByRef.TryGetValue(methodReference, out var context) ? context : new(methodReference, appContext) + : null; + } + } +} diff --git a/Il2CppInterop.Generator/Extensions/AsmResolverExtensions.cs b/Il2CppInterop.Generator/Extensions/AsmResolverExtensions.cs index 7fc9f869..15b221b8 100644 --- a/Il2CppInterop.Generator/Extensions/AsmResolverExtensions.cs +++ b/Il2CppInterop.Generator/Extensions/AsmResolverExtensions.cs @@ -1,107 +1,19 @@ using AsmResolver.DotNet; -using AsmResolver.DotNet.Collections; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; namespace Il2CppInterop.Generator.Extensions; internal static class AsmResolverExtensions { - /// - /// Boolean, Char, or numeric type - /// - public static bool IsPrimitive(this TypeSignature type) + extension(CilOpCode opCode) { - return (type as CorLibTypeSignature)?.ElementType.IsPrimitive() ?? false; + public bool IsLoadConstantI4 + { + get => opCode.Code is (>= CilCode.Ldc_I4_M1 and <= CilCode.Ldc_I4_8) or CilCode.Ldc_I4_S or CilCode.Ldc_I4; + } } - /// - /// Boolean, Char, or numeric type - /// - public static bool IsPrimitive(this ElementType elementType) + public static TypeDefinition? TryResolve(this ITypeDescriptor typeDescriptor, RuntimeContext? runtimeContext) { - return elementType is >= ElementType.Boolean and <= ElementType.R8 or ElementType.I or ElementType.U; - } - - public static TypeSignature GetElementType(this TypeSignature type) => type switch - { - TypeSpecificationSignature specification => specification.BaseType, - _ => type, - }; - - public static void AddLoadArgument(this ILProcessor instructions, int argumentIndex) - { - instructions.Add(OpCodes.Ldarg, instructions.GetArgument(argumentIndex)); - } - - public static void AddLoadArgumentAddress(this ILProcessor instructions, int argumentIndex) - { - instructions.Add(OpCodes.Ldarga, instructions.GetArgument(argumentIndex)); - } - - private static Parameter GetArgument(this ILProcessor instructions, int argumentIndex) - { - var method = instructions.Owner.Owner; - return method.IsStatic - ? method.Parameters[argumentIndex] - : argumentIndex == 0 - ? method.Parameters.ThisParameter! - : method.Parameters[argumentIndex - 1]; - } - - public static bool IsReferenceType(this TypeDefinition type) => !type.IsValueType(); - - public static bool HasGenericParameters(this TypeDefinition type) => type.GenericParameters.Count > 0; - - public static bool HasGenericParameters(this MethodDefinition method) => method.GenericParameters.Count > 0; - - public static bool HasConstant(this FieldDefinition field) => field.Constant is not null; - - public static bool IsInstance(this FieldDefinition field) => !field.IsStatic; - - public static bool HasMethods(this TypeDefinition type) => type.Methods.Count > 0; - - public static bool HasFields(this TypeDefinition type) => type.Fields.Count > 0; - - public static bool IsNested(this ITypeDefOrRef type) => type.DeclaringType is not null; - - public static bool IsNested(this TypeSignature type) => type.DeclaringType is not null; - - public static bool IsPointerLike(this TypeSignature type) => type is PointerTypeSignature or ByReferenceTypeSignature; - - public static bool IsValueTypeLike(this TypeSignature type) => type.IsValueType() || type.IsPointerLike(); - - public static bool IsSystemEnum(this GenericParameterConstraint constraint) => constraint.Constraint?.FullName is "System.Enum"; - - public static bool IsSystemValueType(this GenericParameterConstraint constraint) => constraint.Constraint?.FullName is "System.ValueType"; - - public static bool IsInterface(this GenericParameterConstraint constraint) => constraint.Constraint?.Resolve()?.IsInterface == true; - - public static ITypeDefOrRef? AttributeType(this CustomAttribute attribute) => attribute.Constructor?.DeclaringType; - - public static Parameter AddParameter(this MethodDefinition method, TypeSignature parameterSignature, string parameterName, ParameterAttributes parameterAttributes = default) - { - var parameter = method.AddParameter(parameterSignature); - var parameterDefinition = parameter.GetOrCreateDefinition(); - parameterDefinition.Name = parameterName; - parameterDefinition.Attributes = parameterAttributes; - return parameter; - } - - public static Parameter AddParameter(this MethodDefinition method, TypeSignature parameterSignature) - { - method.Signature!.ParameterTypes.Add(parameterSignature); - method.Parameters.PullUpdatesFromMethodSignature(); - return method.Parameters[method.Parameters.Count - 1]; - } - - public static TypeDefinition GetType(this ModuleDefinition module, string fullName) - { - return module.TopLevelTypes.First(t => t.FullName == fullName); - } - - public static GenericParameterSignature ToTypeSignature(this GenericParameter genericParameter) - { - return new GenericParameterSignature(genericParameter.Owner is ITypeDescriptor ? GenericParameterType.Type : GenericParameterType.Method, genericParameter.Number); + return typeDescriptor.TryResolve(runtimeContext, out var resolvedType) ? resolvedType : null; } } diff --git a/Il2CppInterop.Generator/Extensions/AssemblyAnalysisContextExtensions.cs b/Il2CppInterop.Generator/Extensions/AssemblyAnalysisContextExtensions.cs new file mode 100644 index 00000000..eb530e84 --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/AssemblyAnalysisContextExtensions.cs @@ -0,0 +1,47 @@ +using Cpp2IL.Core.Model.Contexts; +using LibCpp2IL.BinaryStructures; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class AssemblyAnalysisContextExtensions +{ + extension(AssemblyAnalysisContext assembly) + { + public bool IsReferenceAssembly + { + get => assembly.GetExtraData("ReferenceAssembly") is true; + set => assembly.PutExtraData("ReferenceAssembly", value); + } + + public InjectedTypeAnalysisContext InjectType(Type type) + { + var result = assembly.InjectType(type.Namespace ?? "", type.Name, null, type.Attributes); + if (type.ContainsGenericParameters) + { + foreach (var genericParameter in type.GetGenericArguments()) + { + var genericParameterContext = new GenericParameterTypeAnalysisContext(genericParameter.Name, genericParameter.GenericParameterPosition, Il2CppTypeEnum.IL2CPP_TYPE_VAR, genericParameter.GenericParameterAttributes, result); + result.GenericParameters.Add(genericParameterContext); + } + } + result.SourceType = type; + return result; + } + + public TypeAnalysisContext GetTypeByFullNameOrThrow(string fullName) + { + return assembly.GetTypeByFullName(fullName) ?? throw new($"Unable to find type by full name {fullName}"); + } + + public TypeAnalysisContext? GetTypeByFullName(Type type) + { + var fullName = type.FullName; + return string.IsNullOrEmpty(fullName) ? null : assembly.GetTypeByFullName(fullName); + } + + public TypeAnalysisContext GetTypeByFullNameOrThrow(Type type) + { + return assembly.GetTypeByFullName(type) ?? throw new($"Unable to find type by full name {type.FullName}"); + } + } +} diff --git a/Il2CppInterop.Generator/Extensions/CilInstructionCollectionBackport.cs b/Il2CppInterop.Generator/Extensions/CilInstructionCollectionBackport.cs new file mode 100644 index 00000000..79a3cd38 --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/CilInstructionCollectionBackport.cs @@ -0,0 +1,139 @@ +using AsmResolver.DotNet.Code.Cil; + +namespace Il2CppInterop.Generator.Extensions; + +// https://github.com/Washi1337/AsmResolver/issues/678 +// https://github.com/Washi1337/AsmResolver/pull/679 +internal static class CilInstructionCollectionBackport +{ + extension(CilInstructionCollection instructions) + { + public void ExpandMacrosBackport() + { + int currentOffset = 0; + foreach (var instruction in instructions) + { + instruction.Offset = currentOffset; + instructions.ExpandMacroBackport(instruction); + currentOffset += instruction.Size; + } + } + private void ExpandMacroBackport(CilInstruction instruction) + { + // operand changes must come before opcode changes to not overwrite needed data + switch (instruction.OpCode.Code) + { + case CilCode.Ldc_I4_0: + case CilCode.Ldc_I4_1: + case CilCode.Ldc_I4_2: + case CilCode.Ldc_I4_3: + case CilCode.Ldc_I4_4: + case CilCode.Ldc_I4_5: + case CilCode.Ldc_I4_6: + case CilCode.Ldc_I4_7: + case CilCode.Ldc_I4_8: + case CilCode.Ldc_I4_M1: + case CilCode.Ldc_I4_S: + instruction.Operand = instruction.GetLdcI4Constant(); + instruction.OpCode = CilOpCodes.Ldc_I4; + break; + + case CilCode.Ldarg_0: + case CilCode.Ldarg_1: + case CilCode.Ldarg_2: + case CilCode.Ldarg_3: + case CilCode.Ldarg_S: + instruction.Operand = instructions.Owner.Owner?.Parameters is { } parameters + ? instruction.GetParameter(parameters) + : (ushort)instruction.GetParameterIndex(); + instruction.OpCode = CilOpCodes.Ldarg; + break; + + case CilCode.Ldarga_S: + { + if (instruction.Operand is byte index) + instruction.Operand = (ushort)index; + instruction.OpCode = CilOpCodes.Ldarga; + break; + } + + case CilCode.Starg_S: + { + if (instruction.Operand is byte index) + instruction.Operand = (ushort)index; + instruction.OpCode = CilOpCodes.Starg; + break; + } + + case CilCode.Ldloc_0: + case CilCode.Ldloc_1: + case CilCode.Ldloc_2: + case CilCode.Ldloc_3: + case CilCode.Ldloc_S: + instruction.Operand = instruction.GetLocalVariable(instructions.Owner.LocalVariables); + instruction.OpCode = CilOpCodes.Ldloc; + break; + + case CilCode.Ldloca_S: + { + if (instruction.Operand is byte index) + instruction.Operand = (ushort)index; + instruction.OpCode = CilOpCodes.Ldloca; + break; + } + + case CilCode.Stloc_0: + case CilCode.Stloc_1: + case CilCode.Stloc_2: + case CilCode.Stloc_3: + case CilCode.Stloc_S: + instruction.Operand = instruction.GetLocalVariable(instructions.Owner.LocalVariables); + instruction.OpCode = CilOpCodes.Stloc; + break; + + case CilCode.Beq_S: + instruction.OpCode = CilOpCodes.Beq; + break; + case CilCode.Bge_S: + instruction.OpCode = CilOpCodes.Bge; + break; + case CilCode.Bgt_S: + instruction.OpCode = CilOpCodes.Bgt; + break; + case CilCode.Ble_S: + instruction.OpCode = CilOpCodes.Ble; + break; + case CilCode.Blt_S: + instruction.OpCode = CilOpCodes.Blt; + break; + case CilCode.Br_S: + instruction.OpCode = CilOpCodes.Br; + break; + case CilCode.Brfalse_S: + instruction.OpCode = CilOpCodes.Brfalse; + break; + case CilCode.Brtrue_S: + instruction.OpCode = CilOpCodes.Brtrue; + break; + case CilCode.Bge_Un_S: + instruction.OpCode = CilOpCodes.Bge_Un; + break; + case CilCode.Bgt_Un_S: + instruction.OpCode = CilOpCodes.Bgt_Un; + break; + case CilCode.Ble_Un_S: + instruction.OpCode = CilOpCodes.Ble_Un; + break; + case CilCode.Blt_Un_S: + instruction.OpCode = CilOpCodes.Blt_Un; + break; + case CilCode.Bne_Un_S: + instruction.OpCode = CilOpCodes.Bne_Un; + break; + case CilCode.Leave_S: + instruction.OpCode = CilOpCodes.Leave; + break; + } + } + } +} diff --git a/Il2CppInterop.Generator/Extensions/CollectionEx.cs b/Il2CppInterop.Generator/Extensions/CollectionEx.cs deleted file mode 100644 index 818cc58b..00000000 --- a/Il2CppInterop.Generator/Extensions/CollectionEx.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Il2CppInterop.Generator.Extensions; - -public static class CollectionEx -{ - public static TV GetOrCreate(this IDictionary dict, TK key, Func valueFactory) - where TK : notnull - { - if (!dict.TryGetValue(key, out var result)) - { - result = valueFactory(key); - dict[key] = result; - } - - return result; - } - - public static void AddLocked(this List list, T value) - { - lock (list) - { - list.Add(value); - } - } -} diff --git a/Il2CppInterop.Generator/Extensions/ContextWithDataStorageExtensions.cs b/Il2CppInterop.Generator/Extensions/ContextWithDataStorageExtensions.cs new file mode 100644 index 00000000..7c9ef8ea --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/ContextWithDataStorageExtensions.cs @@ -0,0 +1,94 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class ContextWithDataStorageExtensions +{ + // Prevent boxing a large number of booleans. + private static readonly object True = true; + private static readonly object False = false; + + extension(ContextWithDataStorage context) + { + public void PutExtraData(T data) where T : class + { + context.PutExtraData(typeof(T).Name, data); + } + + public T? GetExtraData() where T : class + { + return context.GetExtraData(typeof(T).Name); + } + + public void RemoveExtraData() where T : class + { + context.RemoveExtraData(typeof(T).Name); + } + + public void RemoveExtraData(string key) + { + context.GetDataStorage().Remove(key); + } + + public bool TryGetExtraData([NotNullWhen(true)] out T? data) where T : class + { + data = context.GetExtraData(); + return data is not null; + } + + public bool HasExtraData() where T : class + { + return context.TryGetExtraData(out _); + } + + /// + /// This type or member was stripped by the compiler, but we restored it using external assemblies. + /// + public bool IsUnstripped + { + get => context.GetExtraBoolean("IsUnstripped"); + set => context.PutExtraBoolean("IsUnstripped", value); + } + + /// + /// This type or member is injected by the generator as a helper method. For example, static constructors to trigger Il2Cpp initialization. + /// + /// + /// For memory efficiency, this is only set at the top-level. If a type is injected, its members are not marked as injected, even though they obviously are. + /// For the purpose of this property, nested types are not considered "members" and must be marked as injected separately. + /// + public bool IsInjected + { + get => context.GetExtraBoolean("IsInjected") || (context.Parent?.IsInjected ?? false); + set => context.PutExtraBoolean("IsInjected", value); + } + + public ContextWithDataStorage? Parent => context switch + { + MethodAnalysisContext method => method.DeclaringType, + FieldAnalysisContext @field => @field.DeclaringType, + PropertyAnalysisContext property => property.DeclaringType, + EventAnalysisContext @event => @event.DeclaringType, + TypeAnalysisContext type => (ContextWithDataStorage?)type.DeclaringType ?? type.DeclaringAssembly, + _ => null, + }; + + public bool GetExtraBoolean(string key) => context.GetExtraData(key) is true; + public void PutExtraBoolean(string key, bool value) => context.PutExtraData(key, value ? True : False); + + public T GetExtraStruct(string key, T defaultValue = default) where T : struct + { + var data = context.GetExtraData(key); + return data is T t ? t : defaultValue; + } + public void PutExtraStruct(string key, T value) where T : struct + { + context.PutExtraData(key, (object)value); + } + } + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_dataStorage")] + private static extern ref Dictionary GetDataStorage(this ContextWithDataStorage context); +} diff --git a/Il2CppInterop.Generator/Extensions/CustomAttributeEx.cs b/Il2CppInterop.Generator/Extensions/CustomAttributeEx.cs deleted file mode 100644 index fba48706..00000000 --- a/Il2CppInterop.Generator/Extensions/CustomAttributeEx.cs +++ /dev/null @@ -1,52 +0,0 @@ -using AsmResolver; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; - -namespace Il2CppInterop.Generator.Extensions; - -public static class CustomAttributeEx -{ - public static long ExtractOffset(this IHasCustomAttribute originalMethod) - { - return ExtractLong(originalMethod, "AddressAttribute", "Offset"); - } - - public static long ExtractRva(this IHasCustomAttribute originalMethod) - { - return ExtractLong(originalMethod, "AddressAttribute", "RVA"); - } - - public static long ExtractToken(this IHasCustomAttribute originalMethod) - { - return ExtractLong(originalMethod, "TokenAttribute", "Token"); - } - - public static int ExtractFieldOffset(this IHasCustomAttribute originalField) - { - return ExtractInt(originalField, "FieldOffsetAttribute", "Offset"); - } - - public static string? GetElementAsString(this CustomAttributeArgument argument) - { - return argument.Element as Utf8String ?? argument.Element as string; - } - - private static string? Extract(this IHasCustomAttribute originalMethod, string attributeName, - string parameterName) - { - var attribute = originalMethod.CustomAttributes.SingleOrDefault(it => it.Constructor?.DeclaringType?.Name == attributeName); - var field = attribute?.Signature?.NamedArguments.SingleOrDefault(it => it.MemberName == parameterName); - - return field?.Argument.GetElementAsString(); - } - - private static long ExtractLong(this IHasCustomAttribute originalMethod, string attributeName, string parameterName) - { - return Convert.ToInt64(Extract(originalMethod, attributeName, parameterName), 16); - } - - private static int ExtractInt(this IHasCustomAttribute originalMethod, string attributeName, string parameterName) - { - return Convert.ToInt32(Extract(originalMethod, attributeName, parameterName), 16); - } -} diff --git a/Il2CppInterop.Generator/Extensions/DictionaryExtensions.cs b/Il2CppInterop.Generator/Extensions/DictionaryExtensions.cs new file mode 100644 index 00000000..8f81f6b5 --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/DictionaryExtensions.cs @@ -0,0 +1,18 @@ +namespace Il2CppInterop.Generator.Extensions; + +internal static class DictionaryExtensions +{ + public static TValue? TryGetValue(this Dictionary dictionary, TKey? key) + where TKey : class + where TValue : class + { + return key is not null && dictionary.TryGetValue(key, out var value) ? value : null; + } + + public static TValue? GetValue(this Dictionary dictionary, TKey? key) + where TKey : class + where TValue : class + { + return key is not null ? dictionary[key] : null; + } +} diff --git a/Il2CppInterop.Generator/Extensions/EnumEx.cs b/Il2CppInterop.Generator/Extensions/EnumEx.cs deleted file mode 100644 index b305c61d..00000000 --- a/Il2CppInterop.Generator/Extensions/EnumEx.cs +++ /dev/null @@ -1,19 +0,0 @@ -using AsmResolver.PE.DotNet.Metadata.Tables; - -namespace Il2CppInterop.Generator.Extensions; - -public static class EnumEx -{ - public static FieldAttributes ForcePublic(this FieldAttributes fieldAttributes) - { - return (fieldAttributes & ~FieldAttributes.FieldAccessMask & ~FieldAttributes.HasFieldMarshal) | - FieldAttributes.Public; - } - - public static GenericParameterAttributes StripValueTypeConstraint( - this GenericParameterAttributes parameterAttributes) - { - return parameterAttributes & ~(GenericParameterAttributes.NotNullableValueTypeConstraint | - GenericParameterAttributes.VarianceMask); - } -} diff --git a/Il2CppInterop.Generator/Extensions/FieldAnalysisContextExtensions.cs b/Il2CppInterop.Generator/Extensions/FieldAnalysisContextExtensions.cs new file mode 100644 index 00000000..4bdc89a6 --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/FieldAnalysisContextExtensions.cs @@ -0,0 +1,96 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class FieldAnalysisContextExtensions +{ + extension(FieldAnalysisContext field) + { + [MaybeNull] + public PropertyAnalysisContext PropertyAccessor + { + get => @field.GetExtraData("PropertyAccessor"); + set => @field.PutExtraData("PropertyAccessor", value); + } + + [MaybeNull] + public FieldAnalysisContext FieldInfoAddressStorage + { + get => @field.GetExtraData("FieldInfoAddressStorage"); + set => @field.PutExtraData("FieldInfoAddressStorage", value); + } + + [MaybeNull] + public FieldAnalysisContext OffsetStorage + { + get => @field.GetExtraData("OffsetStorage"); + set => @field.PutExtraData("OffsetStorage", value); + } + + [MaybeNull] + public MethodAnalysisContext FieldAddressAccessor + { + get => @field.GetExtraData("FieldAddressAccessor"); + set => @field.PutExtraData("FieldAddressAccessor", value); + } + + [MaybeNull] + public int InitializationClassIndex + { + get => @field.GetExtraStruct("InitializationClassIndex", -1); + set => @field.PutExtraStruct("InitializationClassIndex", value); + } + + /// + /// Gets the field info address storage for this field, and instantiate for use in this field's declaring type. + /// + /// The instantiated storage field + public FieldAnalysisContext GetInstantiatedFieldInfoAddressStorage() + { + return field.GetInstantiatedStorageField(field.FieldInfoAddressStorage); + } + + public FieldAnalysisContext GetInstantiatedOffsetStorage() + { + return field.GetInstantiatedStorageField(field.OffsetStorage); + } + + private FieldAnalysisContext GetInstantiatedStorageField(FieldAnalysisContext? storageField) + { + Debug.Assert(storageField is not null); + Debug.Assert(storageField.DeclaringType.GenericParameters.Count == field.DeclaringType.GenericParameters.Count); + + if (storageField.DeclaringType.GenericParameters.Count == 0) + { + return storageField; + } + else + { + return storageField.MakeConcreteGeneric(field.DeclaringType.GenericParameters); + } + } + + public ConcreteGenericFieldAnalysisContext MakeConcreteGeneric(IEnumerable declaringTypeGenericArguments) + { + return new ConcreteGenericFieldAnalysisContext( + field, + field.DeclaringType.MakeGenericInstanceType(declaringTypeGenericArguments) + ); + } + + public FieldAnalysisContext MaybeMakeConcreteGeneric(IEnumerable declaringTypeGenericArguments) + { + if (field.DeclaringType.GenericParameters.Count == 0) + return field; + else + return field.MakeConcreteGeneric(declaringTypeGenericArguments); + } + + public FieldAnalysisContext SelfInstantiateIfNecessary() + { + return field.MaybeMakeConcreteGeneric(field.DeclaringType.GenericParameters); + } + } +} diff --git a/Il2CppInterop.Generator/Extensions/GenericParameterTypeAnalysisContextExtensions.cs b/Il2CppInterop.Generator/Extensions/GenericParameterTypeAnalysisContextExtensions.cs new file mode 100644 index 00000000..d88fddcc --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/GenericParameterTypeAnalysisContextExtensions.cs @@ -0,0 +1,36 @@ +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Visitors; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class GenericParameterTypeAnalysisContextExtensions +{ + public static void CopyConstraintsFrom(this GenericParameterTypeAnalysisContext destination, GenericParameterTypeAnalysisContext source, TypeReplacementVisitor visitor) + { + foreach (var constraint in source.ConstraintTypes) + { + destination.ConstraintTypes.Add(visitor.Replace(constraint)); + } + } + + public static void CopyConstraintsFrom(this IReadOnlyList destination, IReadOnlyList source) + { + if (destination.Count != source.Count) + throw new ArgumentException("Source and destination lists must have the same number of elements."); + + if (source.Count == 0) + return; + + var replacements = new Dictionary(source.Count); + for (var i = source.Count - 1; i >= 0; i--) + { + replacements.Add(source[i], destination[i]); + } + + var visitor = new TypeReplacementVisitor(replacements); + for (var i = source.Count - 1; i >= 0; i--) + { + destination[i].CopyConstraintsFrom(source[i], visitor); + } + } +} diff --git a/Il2CppInterop.Generator/Extensions/HasGenericParametersExtensions.cs b/Il2CppInterop.Generator/Extensions/HasGenericParametersExtensions.cs new file mode 100644 index 00000000..1c11843f --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/HasGenericParametersExtensions.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using Cpp2IL.Core.Model.Contexts; +using LibCpp2IL.BinaryStructures; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class HasGenericParametersExtensions +{ + public static void CopyGenericParameters(this HasGenericParameters destination, HasGenericParameters source, bool copyConstraints = false, bool clearVarianceAttributes = false) + { + var type = destination is TypeAnalysisContext ? Il2CppTypeEnum.IL2CPP_TYPE_VAR : Il2CppTypeEnum.IL2CPP_TYPE_MVAR; + foreach (var genericParameter in source.GenericParameters) + { + var attributes = clearVarianceAttributes ? genericParameter.Attributes & ~GenericParameterAttributes.VarianceMask : genericParameter.Attributes; + destination.GenericParameters.Add(new GenericParameterTypeAnalysisContext(genericParameter.Name, destination.GenericParameters.Count, type, attributes, destination)); + } + if (copyConstraints) + { + destination.GenericParameters.CopyConstraintsFrom(source.GenericParameters); + } + } +} diff --git a/Il2CppInterop.Generator/Extensions/HashSetExtensions.cs b/Il2CppInterop.Generator/Extensions/HashSetExtensions.cs new file mode 100644 index 00000000..18682620 --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/HashSetExtensions.cs @@ -0,0 +1,12 @@ +namespace Il2CppInterop.Generator.Extensions; + +internal static class HashSetExtensions +{ + public static void AddRange(this HashSet set, IEnumerable values) + { + foreach (var value in values) + { + set.Add(value); + } + } +} diff --git a/Il2CppInterop.Generator/Extensions/ILGeneratorEx.cs b/Il2CppInterop.Generator/Extensions/ILGeneratorEx.cs deleted file mode 100644 index 36a4a16c..00000000 --- a/Il2CppInterop.Generator/Extensions/ILGeneratorEx.cs +++ /dev/null @@ -1,447 +0,0 @@ -using System.Diagnostics; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Code.Cil; -using AsmResolver.DotNet.Collections; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Cil; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Extensions; - -public static class ILGeneratorEx -{ - public static void EmitObjectStore(this ILProcessor body, TypeSignature originalType, TypeSignature newType, - TypeRewriteContext enclosingType, int argumentIndex) - { - // input stack: object address, target address - // output: nothing - if (originalType is GenericParameterSignature) - { - EmitObjectStoreGeneric(body, originalType, newType, enclosingType, argumentIndex); - return; - } - - var imports = enclosingType.AssemblyContext.Imports; - - if (originalType.FullName == "System.String") - { - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Call, imports.IL2CPP_ManagedStringToIl2Cpp.Value); - body.Add(OpCodes.Call, imports.WriteFieldWBarrier); - } - else if (originalType.IsPointerLike()) - { - Debug.Assert(newType.IsPointerLike()); - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Stobj, newType.ToTypeDefOrRef()); - body.Add(OpCodes.Pop); - } - else if (originalType.IsValueType()) - { - var typeSpecifics = enclosingType.AssemblyContext.GlobalContext.JudgeSpecificsByOriginalType(originalType); - if (typeSpecifics == TypeRewriteContext.TypeSpecifics.BlittableStruct) - { - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Stobj, newType.ToTypeDefOrRef()); - body.Add(OpCodes.Pop); - } - else - { - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Call, imports.IL2CPP_Il2CppObjectBaseToPtr.Value); - body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_object_unbox.Value); - var classPointerTypeRef = new GenericInstanceTypeSignature(imports.Il2CppClassPointerStore.ToTypeDefOrRef(), imports.Il2CppClassPointerStore.IsValueType(), newType); - var classPointerFieldRef = - ReferenceCreator.CreateFieldReference("NativeClassPtr", imports.Module.IntPtr(), classPointerTypeRef.ToTypeDefOrRef()); - body.Add(OpCodes.Ldsfld, enclosingType.NewType.DeclaringModule!.DefaultImporter.ImportField(classPointerFieldRef)); - body.Add(OpCodes.Ldc_I4_0); - body.Add(OpCodes.Conv_U); - body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_class_value_size.Value); - body.Add(OpCodes.Cpblk); - body.Add(OpCodes.Pop); - } - } - else - { - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Call, imports.IL2CPP_Il2CppObjectBaseToPtr.Value); - body.Add(OpCodes.Call, imports.WriteFieldWBarrier); - } - } - - private static void EmitObjectStoreGeneric(ILProcessor body, TypeSignature originalType, TypeSignature newType, - TypeRewriteContext enclosingType, int argumentIndex) - { - // input stack: object address, target address - // output: nothing - - var imports = enclosingType.AssemblyContext.Imports; - - Debug.Assert(enclosingType.NewType.DeclaringModule is not null); - body.Add(OpCodes.Ldtoken, newType.ToTypeDefOrRef()); - body.Add(OpCodes.Call, enclosingType.NewType.DeclaringModule!.TypeGetTypeFromHandle()); - body.Add(OpCodes.Dup); - body.Add(OpCodes.Callvirt, enclosingType.NewType.DeclaringModule!.TypeGetIsValueType()); - - var finalNop = new CilInstructionLabel(); - var stringNop = new CilInstructionLabel(); - var valueTypeNop = new CilInstructionLabel(); - var storePointerNop = new CilInstructionLabel(); - - body.Add(OpCodes.Brtrue, valueTypeNop); - - body.Add(OpCodes.Callvirt, enclosingType.NewType.DeclaringModule!.TypeGetFullName()); - body.Add(OpCodes.Ldstr, "System.String"); - body.Add(OpCodes.Call, enclosingType.NewType.DeclaringModule!.StringEquals()); - body.Add(OpCodes.Brtrue_S, stringNop); - - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Box, newType.ToTypeDefOrRef()); - body.Add(OpCodes.Isinst, imports.Il2CppObjectBase.ToTypeDefOrRef()); - body.Add(OpCodes.Call, imports.IL2CPP_Il2CppObjectBaseToPtr.Value); - body.Add(OpCodes.Dup); - body.Add(OpCodes.Brfalse_S, storePointerNop); - - body.Add(OpCodes.Dup); - body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_object_get_class.Value); - body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_class_is_valuetype.Value); - body.Add(OpCodes.Brfalse_S, storePointerNop); - - body.Add(OpCodes.Dup); - var tempLocal = new CilLocalVariable(imports.Module.IntPtr()); - body.Owner.LocalVariables.Add(tempLocal); - body.Add(OpCodes.Stloc, tempLocal); - body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_object_unbox.Value); - body.Add(OpCodes.Ldloc, tempLocal); - body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_object_get_class.Value); - body.Add(OpCodes.Ldc_I4_0); - body.Add(OpCodes.Conv_U); - body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_class_value_size.Value); - body.Add(OpCodes.Cpblk); - body.Add(OpCodes.Pop); - body.Add(OpCodes.Br_S, finalNop); - - storePointerNop.Instruction = body.Add(OpCodes.Nop); - body.Add(OpCodes.Call, imports.WriteFieldWBarrier); - body.Add(OpCodes.Br_S, finalNop); - - stringNop.Instruction = body.Add(OpCodes.Nop); - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Box, newType.ToTypeDefOrRef()); - body.Add(OpCodes.Isinst, imports.Module.String().ToTypeDefOrRef()); - body.Add(OpCodes.Call, imports.IL2CPP_ManagedStringToIl2Cpp.Value); - body.Add(OpCodes.Call, imports.WriteFieldWBarrier); - body.Add(OpCodes.Br_S, finalNop); - - valueTypeNop.Instruction = body.Add(OpCodes.Nop); - body.Add(OpCodes.Pop); // pop extra typeof(T) - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Stobj, newType.ToTypeDefOrRef()); - body.Add(OpCodes.Pop); - - finalNop.Instruction = body.Add(OpCodes.Nop); - } - - public static void EmitObjectToPointer(this ILProcessor body, TypeSignature originalType, TypeSignature newType, - TypeRewriteContext enclosingType, int argumentIndex, bool valueTypeArgument0IsAPointer, bool allowNullable, - bool unboxNonBlittableType, bool unboxNonBlittableGeneric, out CilLocalVariable? refVariable) - { - // input stack: not used - // output stack: IntPtr to either Il2CppObject or IL2CPP value type - refVariable = null; - - if (originalType is GenericParameterSignature) - { - EmitObjectToPointerGeneric(body, originalType, newType, enclosingType, argumentIndex, - valueTypeArgument0IsAPointer, allowNullable, unboxNonBlittableGeneric); - return; - } - - var imports = enclosingType.AssemblyContext.Imports; - if (originalType is ByReferenceTypeSignature) - { - if (newType.GetElementType().IsValueType()) - { - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Conv_I); - } - else if (originalType.GetElementType().IsValueType()) - { - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Ldind_Ref); - body.Add(OpCodes.Call, imports.IL2CPP_Il2CppObjectBaseToPtrNotNull.Value); - body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_object_unbox.Value); - } - else - { - var pointerVar = new CilLocalVariable(imports.Module.IntPtr()); - refVariable = pointerVar; - body.Owner.LocalVariables.Add(pointerVar); - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Ldind_Ref); - if (originalType.GetElementType().FullName == "System.String") - body.Add(OpCodes.Call, imports.IL2CPP_ManagedStringToIl2Cpp.Value); - else - body.Add(OpCodes.Call, imports.IL2CPP_Il2CppObjectBaseToPtr.Value); - body.Add(OpCodes.Stloc, pointerVar); - body.Add(OpCodes.Ldloca, pointerVar); - body.Add(OpCodes.Conv_I); - } - } - else if (originalType.IsPointerLike()) - { - Debug.Assert(newType.IsPointerLike()); - body.AddLoadArgument(argumentIndex); - } - else if (originalType.IsValueType()) - { - if (newType.IsValueType()) - { - if (argumentIndex == 0 && valueTypeArgument0IsAPointer) - body.Add(OpCodes.Ldarg_0); - else - body.AddLoadArgumentAddress(argumentIndex); - } - else - { - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Call, imports.IL2CPP_Il2CppObjectBaseToPtrNotNull.Value); - if (unboxNonBlittableType) - body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_object_unbox.Value); - } - } - else if (originalType.FullName == "System.String") - { - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Call, imports.IL2CPP_ManagedStringToIl2Cpp.Value); - } - else - { - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Call, - allowNullable - ? imports.IL2CPP_Il2CppObjectBaseToPtr.Value - : imports.IL2CPP_Il2CppObjectBaseToPtrNotNull.Value); - } - } - - private static void EmitObjectToPointerGeneric(ILProcessor body, TypeSignature originalType, - TypeSignature newType, TypeRewriteContext enclosingType, int argumentIndex, - bool valueTypeArgument0IsAPointer, bool allowNullable, bool unboxNonBlittableType) - { - var imports = enclosingType.AssemblyContext.Imports; - - Debug.Assert(enclosingType.NewType.DeclaringModule is not null); - body.Add(OpCodes.Ldtoken, newType.ToTypeDefOrRef()); - body.Add(OpCodes.Call, enclosingType.NewType.DeclaringModule!.TypeGetTypeFromHandle()); - body.Add(OpCodes.Callvirt, enclosingType.NewType.DeclaringModule!.TypeGetIsValueType()); - - var finalNop = new CilInstructionLabel(); - var valueTypeNop = new CilInstructionLabel(); - var stringNop = new CilInstructionLabel(); - - body.Add(OpCodes.Brtrue, valueTypeNop); - - body.AddLoadArgument(argumentIndex); - body.Add(OpCodes.Box, newType.ToTypeDefOrRef()); - body.Add(OpCodes.Dup); - body.Add(OpCodes.Isinst, imports.Module.String().ToTypeDefOrRef()); - body.Add(OpCodes.Brtrue_S, stringNop); - - body.Add(OpCodes.Isinst, imports.Il2CppObjectBase.ToTypeDefOrRef()); - body.Add(OpCodes.Call, - allowNullable - ? imports.IL2CPP_Il2CppObjectBaseToPtr.Value - : imports.IL2CPP_Il2CppObjectBaseToPtrNotNull.Value); - if (unboxNonBlittableType) - { - body.Add(OpCodes.Dup); - body.Add(OpCodes.Brfalse_S, finalNop); // return null immediately - body.Add(OpCodes.Dup); - body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_object_get_class.Value); - body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_class_is_valuetype.Value); - body.Add(OpCodes.Brfalse_S, finalNop); // return reference types immediately - body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_object_unbox.Value); - } - - body.Add(OpCodes.Br, finalNop); - - stringNop.Instruction = body.Add(OpCodes.Nop); - body.Add(OpCodes.Isinst, imports.Module.String().ToTypeDefOrRef()); - body.Add(OpCodes.Call, imports.IL2CPP_ManagedStringToIl2Cpp.Value); - body.Add(OpCodes.Br_S, finalNop); - - valueTypeNop.Instruction = body.Add(OpCodes.Nop); - body.AddLoadArgumentAddress(argumentIndex); - - finalNop.Instruction = body.Add(OpCodes.Nop); - } - - public static void EmitPointerToObject(this ILProcessor body, TypeSignature originalReturnType, - TypeSignature convertedReturnType, TypeRewriteContext enclosingType, CilLocalVariable pointerVariable, - bool extraDerefForNonValueTypes, bool unboxValueType) - { - // input stack: not used - // output stack: converted result - - if (originalReturnType is GenericParameterSignature) - { - EmitPointerToObjectGeneric(body, originalReturnType, convertedReturnType, enclosingType, pointerVariable, - extraDerefForNonValueTypes, unboxValueType); - return; - } - - var imports = enclosingType.AssemblyContext.Imports; - if (originalReturnType.FullName == "System.Void") - { - // do nothing - } - else if (originalReturnType.IsPointerLike()) - { - Debug.Assert(convertedReturnType.IsPointerLike()); - body.Add(OpCodes.Ldloc, pointerVariable); - } - else if (originalReturnType.IsValueType()) - { - if (convertedReturnType.IsValueType()) - { - body.Add(OpCodes.Ldloc, pointerVariable); - if (unboxValueType) body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_object_unbox.Value); - body.Add(OpCodes.Ldobj, convertedReturnType.ToTypeDefOrRef()); - } - else - { - if (unboxValueType) - { - body.Add(OpCodes.Ldloc, pointerVariable); - } - else - { - Debug.Assert(enclosingType.NewType.DeclaringModule is not null); - var classPointerTypeRef = new GenericInstanceTypeSignature(imports.Il2CppClassPointerStore.ToTypeDefOrRef(), imports.Il2CppClassPointerStore.IsValueType(), convertedReturnType); - var classPointerFieldRef = - ReferenceCreator.CreateFieldReference("NativeClassPtr", imports.Module.IntPtr(), - classPointerTypeRef.ToTypeDefOrRef()); - body.Add(OpCodes.Ldsfld, enclosingType.NewType.DeclaringModule!.DefaultImporter.ImportField(classPointerFieldRef)); - body.Add(OpCodes.Ldloc, pointerVariable); - body.Add(OpCodes.Call, imports.IL2CPP_il2cpp_value_box.Value); - } - - body.Add(OpCodes.Newobj, - ReferenceCreator.CreateInstanceMethodReference(".ctor", imports.Module.Void(), convertedReturnType.ToTypeDefOrRef(), imports.Module.IntPtr())); - } - } - else if (originalReturnType.FullName == "System.String") - { - body.Add(OpCodes.Ldloc, pointerVariable); - if (extraDerefForNonValueTypes) body.Add(OpCodes.Ldind_I); - body.Add(OpCodes.Call, imports.IL2CPP_Il2CppStringToManaged.Value); - } - else if (originalReturnType is ArrayBaseTypeSignature && originalReturnType.GetElementType() is GenericParameterSignature genericParameterSignature) - { - // Note: - // The method reference parent is constructed relative to the calling method. - // The return type and parameter types are constructed relative to the called method. - body.Add(OpCodes.Ldloc, pointerVariable); - if (extraDerefForNonValueTypes) body.Add(OpCodes.Ldind_I); - var actualReturnType = imports.Module.DefaultImporter.ImportTypeSignature(imports.Il2CppArrayBase.MakeGenericInstanceType(new GenericParameterSignature(GenericParameterType.Type, 0))); - var methodRef = ReferenceCreator.CreateStaticMethodReference("WrapNativeGenericArrayPointer", - actualReturnType, - convertedReturnType.ToTypeDefOrRef(), - imports.Module.IntPtr()); - body.Add(OpCodes.Call, methodRef); - } - else - { - var createPoolObject = new CilInstructionLabel(); - var endNop = new CilInstructionLabel(); - - body.Add(OpCodes.Ldloc, pointerVariable); - if (extraDerefForNonValueTypes) body.Add(OpCodes.Ldind_I); - body.Add(OpCodes.Dup); - body.Add(OpCodes.Brtrue_S, createPoolObject); - body.Add(OpCodes.Pop); - body.Add(OpCodes.Ldnull); - body.Add(OpCodes.Br, endNop); - - createPoolObject.Instruction = body.Add(OpCodes.Call, - imports.Module.DefaultImporter.ImportMethod(imports.Il2CppObjectPool_Get.Value.MakeGenericInstanceMethod(convertedReturnType))); - endNop.Instruction = body.Add(OpCodes.Nop); - } - } - - private static void EmitPointerToObjectGeneric(ILProcessor body, TypeSignature originalReturnType, - TypeSignature newReturnType, - TypeRewriteContext enclosingType, CilLocalVariable pointerVariable, bool extraDerefForNonValueTypes, - bool unboxValueType) - { - var imports = enclosingType.AssemblyContext.Imports; - - body.Add(OpCodes.Ldloc, pointerVariable); - - body.Add(extraDerefForNonValueTypes ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - body.Add(unboxValueType ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - body.Add(OpCodes.Call, - imports.Module.DefaultImporter.ImportMethod(imports.IL2CPP_PointerToValueGeneric.Value.MakeGenericInstanceMethod(newReturnType))); - } - - public static void GenerateBoxMethod(RuntimeAssemblyReferences imports, TypeDefinition targetType, - IFieldDescriptor classHandle, TypeSignature il2CppObjectTypeDef) - { - Debug.Assert(targetType.DeclaringModule is not null); - var method = new MethodDefinition("BoxIl2CppObject", MethodAttributes.Public | MethodAttributes.HideBySig, - MethodSignature.CreateInstance(targetType.DeclaringModule!.DefaultImporter.ImportTypeSignature(il2CppObjectTypeDef))); - targetType.Methods.Add(method); - - method.CilMethodBody = new CilMethodBody(); - var methodBody = method.CilMethodBody.Instructions; - methodBody.Add(OpCodes.Ldsfld, classHandle); - methodBody.Add(OpCodes.Ldarg_0); - methodBody.Add(OpCodes.Call, targetType.DeclaringModule.DefaultImporter.ImportMethod(imports.IL2CPP_il2cpp_value_box.Value)); - - methodBody.Add(OpCodes.Newobj, - new MemberReference(il2CppObjectTypeDef.ToTypeDefOrRef(), ".ctor", MethodSignature.CreateInstance(targetType.DeclaringModule.Void(), targetType.DeclaringModule.IntPtr()))); - - methodBody.Add(OpCodes.Ret); - } - - public static void EmitUpdateRef(this ILProcessor body, Parameter newMethodParameter, int argIndex, - CilLocalVariable paramVariable, RuntimeAssemblyReferences imports) - { - body.AddLoadArgument(argIndex); - body.Add(OpCodes.Ldloc, paramVariable); - if (newMethodParameter.ParameterType.GetElementType().FullName == "System.String") - { - body.Add(OpCodes.Call, imports.IL2CPP_Il2CppStringToManaged.Value); - } - else - { - body.Add(OpCodes.Dup); - var nullbr = new CilInstructionLabel(); - var stnop = new CilInstructionLabel(); - body.Add(OpCodes.Brfalse_S, nullbr); - - if (newMethodParameter.ParameterType.GetElementType() is GenericParameterSignature) - { - body.Add(OpCodes.Ldc_I4_0); - body.Add(OpCodes.Ldc_I4_0); - body.Add(OpCodes.Call, - imports.Module.DefaultImporter.ImportMethod(imports.IL2CPP_PointerToValueGeneric.Value.MakeGenericInstanceMethod(newMethodParameter.ParameterType.GetElementType()))); - } - else - { - body.Add(OpCodes.Newobj, - ReferenceCreator.CreateInstanceMethodReference(".ctor", imports.Module.Void(), newMethodParameter.ParameterType.GetElementType().ToTypeDefOrRef(), imports.Module.IntPtr())); - } - body.Add(OpCodes.Br_S, stnop); - - nullbr.Instruction = body.Add(OpCodes.Pop); - body.Add(OpCodes.Ldnull); - stnop.Instruction = body.Add(OpCodes.Nop); - } - - body.Add(OpCodes.Stind_Ref); - } -} diff --git a/Il2CppInterop.Generator/Extensions/InjectedMethodAnalysisContextExtensions.cs b/Il2CppInterop.Generator/Extensions/InjectedMethodAnalysisContextExtensions.cs new file mode 100644 index 00000000..6f536ae0 --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/InjectedMethodAnalysisContextExtensions.cs @@ -0,0 +1,15 @@ +using System.Runtime.CompilerServices; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class InjectedMethodAnalysisContextExtensions +{ + public static void SetDefaultReturnType(this InjectedMethodAnalysisContext context, TypeAnalysisContext type) + { + GetDefaultReturnType(context) = type; + } + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name = $"<{nameof(InjectedMethodAnalysisContext.DefaultReturnType)}>k__BackingField")] + private static extern ref TypeAnalysisContext GetDefaultReturnType(InjectedMethodAnalysisContext context); +} diff --git a/Il2CppInterop.Generator/Extensions/InjectedParameterAnalysisContextExtensions.cs b/Il2CppInterop.Generator/Extensions/InjectedParameterAnalysisContextExtensions.cs new file mode 100644 index 00000000..fafe48ba --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/InjectedParameterAnalysisContextExtensions.cs @@ -0,0 +1,15 @@ +using System.Runtime.CompilerServices; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class InjectedParameterAnalysisContextExtensions +{ + public static void SetDefaultParameterType(this InjectedParameterAnalysisContext context, TypeAnalysisContext type) + { + GetDefaultParameterType(context) = type; + } + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name = $"<{nameof(InjectedParameterAnalysisContext.DefaultParameterType)}>k__BackingField")] + private static extern ref TypeAnalysisContext GetDefaultParameterType(InjectedParameterAnalysisContext context); +} diff --git a/Il2CppInterop.Generator/Extensions/InjectedTypeAnalysisContextExtensions.cs b/Il2CppInterop.Generator/Extensions/InjectedTypeAnalysisContextExtensions.cs new file mode 100644 index 00000000..aa8784cf --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/InjectedTypeAnalysisContextExtensions.cs @@ -0,0 +1,158 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using Cpp2IL.Core.Model.Contexts; +using LibCpp2IL.BinaryStructures; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class InjectedTypeAnalysisContextExtensions +{ + extension(InjectedTypeAnalysisContext type) + { + public void InjectContentFromSourceType() + { + var sourceType = type.SourceType; + ArgumentNullException.ThrowIfNull(sourceType); + + var appContext = type.AppContext; + + type.SetDefaultBaseType(sourceType.BaseType != null + ? new SystemTypeResolver(type).ResolveOrThrow(sourceType.BaseType) + : null); + + foreach (var fieldInfo in sourceType.GetFields()) + { + if (fieldInfo.DeclaringType != sourceType) + continue; + + var resolver = new SystemTypeResolver(type); + + type.InjectFieldContext( + fieldInfo.Name, + resolver.ResolveOrThrow(fieldInfo.FieldType), + fieldInfo.Attributes); + } + + Dictionary methodMap = new(); + foreach (var method in GetPublicAndProtectedMethods(sourceType)) + { + if (method.DeclaringType != sourceType) + continue; + + var parameterInfoArray = method.GetParameters(); + var parameterNames = parameterInfoArray + .Select(x => x.Name!) + .ToArray(); + var parameterAttributes = parameterInfoArray + .Select(x => x.Attributes) + .ToArray(); + var methodContext = new InjectedMethodAnalysisContext( + type, + method.Name, + appContext.SystemTypes.SystemObjectType, + method.Attributes, + Enumerable.Repeat(appContext.SystemTypes.SystemObjectType, parameterInfoArray.Length).ToArray(), + parameterNames, + parameterAttributes); + if (method is not ConstructorInfo) + { + foreach (var genericParameter in method.GetGenericArguments()) + { + var genericParameterContext = new GenericParameterTypeAnalysisContext(genericParameter.Name, genericParameter.GenericParameterPosition, Il2CppTypeEnum.IL2CPP_TYPE_MVAR, genericParameter.GenericParameterAttributes, methodContext); + methodContext.GenericParameters.Add(genericParameterContext); + } + } + type.Methods.Add(methodContext); + + methodMap.Add(method, methodContext); + + var resolver = new SystemTypeResolver(methodContext); + + var returnType = method switch + { + MethodInfo methodInfo => resolver.ResolveOrThrow(methodInfo.ReturnType), + ConstructorInfo => appContext.SystemTypes.SystemVoidType, + _ => throw new NotSupportedException($"Unsupported method type: {method.GetType()}") + }; + methodContext.SetDefaultReturnType(returnType); + for (var i = 0; i < parameterInfoArray.Length; i++) + { + var parameterType = resolver.ResolveOrThrow(parameterInfoArray[i].ParameterType); + var parameterContext = (InjectedParameterAnalysisContext)methodContext.Parameters[i]; + parameterContext.SetDefaultParameterType(parameterType); + } + } + + foreach (var property in sourceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) + { + if (property.DeclaringType != sourceType) + continue; + + var getMethod = methodMap.TryGetValue(property.GetMethod); + var setMethod = methodMap.TryGetValue(property.SetMethod); + if (getMethod == null && setMethod == null) + continue; + + var resolver = new SystemTypeResolver(type); + + var propertyType = resolver.ResolveOrThrow(property.PropertyType); + type.InjectPropertyContext( + property.Name, + propertyType, + getMethod, + setMethod, + property.Attributes); + } + + foreach (var eventInfo in sourceType.GetEvents(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) + { + if (eventInfo.DeclaringType != sourceType) + continue; + + var addMethod = methodMap.TryGetValue(eventInfo.AddMethod); + var removeMethod = methodMap.TryGetValue(eventInfo.RemoveMethod); + var raiseMethod = methodMap.TryGetValue(eventInfo.RaiseMethod); + if (addMethod == null && removeMethod == null && raiseMethod == null) + continue; + + var resolver = new SystemTypeResolver(type); + var eventType = resolver.ResolveOrThrow(eventInfo.EventHandlerType!); + type.InjectEventContext( + eventInfo.Name, + eventType, + addMethod, + removeMethod, + raiseMethod, + eventInfo.Attributes); + } + } + + public void SetDefaultBaseType(TypeAnalysisContext? baseType) + { + GetDefaultBaseType(type) = baseType; + } + } + + static IEnumerable GetPublicAndProtectedMethods(Type type) + { + var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + foreach (var constructor in constructors) + { + if (constructor.IsPublic || constructor.IsFamily || constructor.IsFamilyOrAssembly) + { + yield return constructor; + } + } + var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + foreach (var method in methods) + { + if (method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly) + { + yield return method; + } + } + } + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name = $"<{nameof(InjectedTypeAnalysisContext.DefaultBaseType)}>k__BackingField")] + private static extern ref TypeAnalysisContext? GetDefaultBaseType(InjectedTypeAnalysisContext context); +} diff --git a/Il2CppInterop.Generator/Extensions/InstructionListExtensions.cs b/Il2CppInterop.Generator/Extensions/InstructionListExtensions.cs new file mode 100644 index 00000000..de6f668b --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/InstructionListExtensions.cs @@ -0,0 +1,18 @@ +using Il2CppInterop.Generator.Operands; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class InstructionListExtensions +{ + public static Instruction Add(this List instructions, CilOpCode opCode) + { + return instructions.Add(opCode, null); + } + + public static Instruction Add(this List instructions, CilOpCode opCode, object? operand) + { + var instruction = new Instruction(opCode, operand); + instructions.Add(instruction); + return instruction; + } +} diff --git a/Il2CppInterop.Generator/Extensions/LocalVariableListExtensions.cs b/Il2CppInterop.Generator/Extensions/LocalVariableListExtensions.cs new file mode 100644 index 00000000..68be8ada --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/LocalVariableListExtensions.cs @@ -0,0 +1,14 @@ +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class LocalVariableListExtensions +{ + public static LocalVariable AddNew(this List localVariables, TypeAnalysisContext variableType) + { + var localVariable = new LocalVariable(variableType); + localVariables.Add(localVariable); + return localVariable; + } +} diff --git a/Il2CppInterop.Generator/Extensions/MethodAnalysisContextExtensions.cs b/Il2CppInterop.Generator/Extensions/MethodAnalysisContextExtensions.cs new file mode 100644 index 00000000..1cbd217a --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/MethodAnalysisContextExtensions.cs @@ -0,0 +1,108 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class MethodAnalysisContextExtensions +{ + extension(MethodAnalysisContext method) + { + [MaybeNull] + public FieldAnalysisContext MethodInfoField + { + get => method.GetExtraData("MethodInfoField"); + set => method.PutExtraData("MethodInfoField", value); + } + + [MaybeNull] + public FieldAnalysisContext ICallDelegateField + { + get => method.GetExtraData("ICallDelegateField"); + set => method.PutExtraData("ICallDelegateField", value); + } + + public MethodAnalysisContext MostUserFriendlyOverload + { + get => method.GetExtraData("MostUserFriendlyOverload") ?? method; + set => method.PutExtraData("MostUserFriendlyOverload", value); + } + + /// + /// The unsafe invoke method for this method. + /// + [MaybeNull] + public MethodAnalysisContext UnsafeInvokeMethod + { + get => method.GetExtraData("UnsafeInvokeMethod"); + set => method.PutExtraData("UnsafeInvokeMethod", value); + } + + /// + /// The unsafe implementation method for this method. + /// + [MaybeNull] + public MethodAnalysisContext UnsafeImplementationMethod + { + get => method.GetExtraData("UnsafeImplementationMethod"); + set => method.PutExtraData("UnsafeImplementationMethod", value); + } + + /// + /// The interface method that should be used instead when emitting calls to this method during unstripping. + /// + [MaybeNull] + public MethodAnalysisContext InterfaceRedirectMethod + { + get => method.GetExtraData("InterfaceRedirectMethod"); + set => method.PutExtraData("InterfaceRedirectMethod", value); + } + + [MaybeNull] + public int InitializationClassIndex + { + get => method.GetExtraStruct("InitializationClassIndex", -1); + set => method.PutExtraStruct("InitializationClassIndex", value); + } + + public bool IsInstanceConstructor => method.Name == ".ctor"; + public bool IsStaticConstructor => method.Name == ".cctor"; + public bool IsConstructor => method.IsInstanceConstructor || method.IsStaticConstructor; + public bool IsPublic => (method.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public; + public bool IsSpecialName => (method.Attributes & MethodAttributes.SpecialName) != default; + + public bool ImplementsAnInterfaceMethod => method.Overrides.Count > 0; + + public ushort Slot => method.Definition?.slot ?? ushort.MaxValue; + + public FieldAnalysisContext GetInstantiatedMethodInfoField() + { + var methodInfoField = method.MethodInfoField; + Debug.Assert(methodInfoField is not null); + Debug.Assert(method.DeclaringType is not null); + + IReadOnlyList methodInfoGenericArguments = [.. method.DeclaringType.GenericParameters, .. method.GenericParameters]; + if (methodInfoGenericArguments.Count == 0) + { + return methodInfoField; + } + else + { + return methodInfoField.MakeConcreteGeneric(methodInfoGenericArguments); + } + } + + public ConcreteGenericMethodAnalysisContext MakeConcreteGeneric(IEnumerable typeArguments, IEnumerable methodArguments) + { + return new ConcreteGenericMethodAnalysisContext(method, typeArguments, methodArguments); + } + + public MethodAnalysisContext MaybeMakeConcreteGeneric(IReadOnlyCollection typeArguments, IReadOnlyCollection methodArguments) + { + if (typeArguments.Count == 0 && methodArguments.Count == 0) + return method; + return method.MakeConcreteGeneric(typeArguments, methodArguments); + } + } +} diff --git a/Il2CppInterop.Generator/Extensions/ParameterDefinitionEx.cs b/Il2CppInterop.Generator/Extensions/ParameterDefinitionEx.cs deleted file mode 100644 index a32f1719..00000000 --- a/Il2CppInterop.Generator/Extensions/ParameterDefinitionEx.cs +++ /dev/null @@ -1,13 +0,0 @@ -using AsmResolver.DotNet.Collections; -using AsmResolver.DotNet.Signatures; - -namespace Il2CppInterop.Generator.Extensions; - -internal static class ParameterDefinitionEx -{ - public static bool IsParamsArray(this Parameter self) - { - return self.ParameterType is SzArrayTypeSignature && (self.Definition?.CustomAttributes.Any(attribute => - attribute.Constructor?.DeclaringType?.FullName == typeof(ParamArrayAttribute).FullName) ?? false); - } -} diff --git a/Il2CppInterop.Generator/Extensions/PropertyAnalysisContextExtensions.cs b/Il2CppInterop.Generator/Extensions/PropertyAnalysisContextExtensions.cs new file mode 100644 index 00000000..6b5ee065 --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/PropertyAnalysisContextExtensions.cs @@ -0,0 +1,17 @@ +using System.Diagnostics.CodeAnalysis; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class PropertyAnalysisContextExtensions +{ + extension(PropertyAnalysisContext property) + { + [MaybeNull] + public FieldAnalysisContext OriginalField + { + get => property.GetExtraData("OriginalField"); + set => property.PutExtraData("OriginalField", value); + } + } +} diff --git a/Il2CppInterop.Generator/Extensions/StringEx.cs b/Il2CppInterop.Generator/Extensions/StringEx.cs deleted file mode 100644 index 39769153..00000000 --- a/Il2CppInterop.Generator/Extensions/StringEx.cs +++ /dev/null @@ -1,221 +0,0 @@ -using System.Buffers; -using System.Diagnostics.CodeAnalysis; -using System.Text; -using AsmResolver; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; - -namespace Il2CppInterop.Generator.Extensions; - -public static class StringEx -{ - public static bool NameShouldBePrefixed(this string str, GeneratorOptions options) - { - if (options.Il2CppPrefixMode == GeneratorOptions.PrefixMode.OptIn) - { - foreach (var prefix in options.NamespacesAndAssembliesToPrefix) - if (str.StartsWith(prefix, StringComparison.Ordinal)) - return true; - return false; - } - else - { - foreach (var prefix in options.NamespacesAndAssembliesToNotPrefix) - if (str.StartsWith(prefix, StringComparison.Ordinal)) - return false; - - return true; - } - } - - public static bool NameShouldBePrefixed(this Utf8String? str, GeneratorOptions options) - { - return NameShouldBePrefixed(str?.Value ?? "", options); - } - - public static string UnSystemify(this string str, GeneratorOptions options) - { - const string Il2CppPrefix = "Il2Cpp"; - return str.NameShouldBePrefixed(options) ? Il2CppPrefix + str : str; - } - - public static string UnSystemify(this Utf8String? str, GeneratorOptions options) - { - return UnSystemify(str?.Value ?? "", options); - } - - public static string MakeValidInSource(this string str) - { - if (string.IsNullOrEmpty(str)) - return ""; - - char[]? chars = null; - for (var i = 0; i < str.Length; i++) - { - var it = str[i]; - if (IsValidInSource(it)) - continue; - - chars ??= str.ToCharArray(); - chars[i] = '_'; - } - - var result = chars is null ? str : new string(chars); - return char.IsDigit(result[0]) ? "_" + result : result; - } - - private static bool IsValidInSource(char c) => char.IsDigit(c) || (c is >= 'a' and <= 'z') || (c is >= 'A' and <= 'Z') || c == '_' || c == '`'; - - public static Utf8String MakeValidInSource(this Utf8String? str) - { - if (Utf8String.IsNullOrEmpty(str)) - return Utf8String.Empty; - - ReadOnlySpan data = str.GetBytesUnsafe(); - - var length = data.Length; - byte[]? rentedArray = null; - Span rentedArraySpan = default; - - if (char.IsDigit((char)data[0])) - { - length++; - rentedArray = ArrayPool.Shared.Rent(length); - rentedArray[0] = (byte)'_'; - rentedArraySpan = rentedArray.AsSpan(1); - data.CopyTo(rentedArraySpan); - } - - for (var i = 0; i < data.Length; i++) - { - if (IsValidInSource((char)data[i])) - continue; - - if (rentedArray is null) - { - rentedArray = ArrayPool.Shared.Rent(length); - rentedArraySpan = rentedArray.AsSpan(); - data.CopyTo(rentedArraySpan); - } - rentedArraySpan[i] = (byte)'_'; - } - - if (rentedArray is not null) - { - var result = new Utf8String(rentedArray, 0, length); - ArrayPool.Shared.Return(rentedArray); - return result; - } - else - { - return str; - } - } - - public static bool IsInvalidInSource([NotNullWhen(true)] this string? str) - { - if (str is null) - return false; - - for (var i = 0; i < str.Length; i++) - { - var it = str[i]; - if (!char.IsDigit(it) && !((it >= 'a' && it <= 'z') || (it >= 'A' && it <= 'Z')) && it != '_' && - it != '`') return true; - } - - return false; - } - - public static bool IsInvalidInSource([NotNullWhen(true)] this Utf8String? str) - { - return IsInvalidInSource(str?.Value); - } - - public static bool IsObfuscated([NotNullWhen(true)] this string? str, GeneratorOptions options) - { - if (str is null) - return false; - if (options.ObfuscatedNamesRegex != null) - return options.ObfuscatedNamesRegex.IsMatch(str); - - foreach (var it in str) - if (!char.IsDigit(it) && !((it >= 'a' && it <= 'z') || (it >= 'A' && it <= 'Z')) && it != '_' && - it != '`' && it != '.' && it != '<' && it != '>') - return true; - - return false; - } - - public static bool IsObfuscated(this Utf8String? str, GeneratorOptions options) - { - return IsObfuscated(str?.Value ?? "", options); - } - - public static ulong StableHash(this string str) - { - ulong hash = 0; - for (var i = 0; i < str.Length; i++) - hash = hash * 37 + str[i]; - - return hash; - } - - public static ulong StableHash(this Utf8String? str) - { - return StableHash(str?.Value ?? ""); - } - - public static bool StartsWith([NotNullWhen(true)] this Utf8String? str, string value) - { - return str is not null && str.Value.StartsWith(value, StringComparison.Ordinal); - } - - /// - /// Construct an unmangled name for a type signature. - /// - /// - /// The declaring type to use for resolving generic type parameter names. - /// The declaring method to use for resolving generic method parameter names. - /// - public static string GetUnmangledName(this TypeSignature typeRef, TypeDefinition? declaringType = null, MethodDefinition? declaringMethod = null) - { - var builder = new StringBuilder(); - if (typeRef is GenericInstanceTypeSignature genericInstance) - { - builder.Append(genericInstance.GenericType.ToTypeSignature().GetUnmangledName(declaringType, declaringMethod)); - foreach (var genericArgument in genericInstance.TypeArguments) - { - builder.Append("_"); - builder.Append(genericArgument.GetUnmangledName(declaringType, declaringMethod)); - } - } - else if (typeRef is ByReferenceTypeSignature byRef) - { - builder.Append("byref_"); - builder.Append(byRef.BaseType.GetUnmangledName(declaringType, declaringMethod)); - } - else if (typeRef is PointerTypeSignature pointer) - { - builder.Append("ptr_"); - builder.Append(pointer.BaseType.GetUnmangledName(declaringType, declaringMethod)); - } - else if (typeRef is GenericParameterSignature genericParameter) - { - if (genericParameter.ParameterType == GenericParameterType.Type) - builder.Append(declaringType!.GenericParameters[genericParameter.Index].Name.MakeValidInSource()); - else - builder.Append(declaringMethod!.GenericParameters[genericParameter.Index].Name.MakeValidInSource()); - } - else - { - if (typeRef.Namespace == "Il2CppInterop.Runtime" && (typeRef.Name?.StartsWith("Il2Cpp") ?? false) && - typeRef.Name.Contains("Array")) - builder.Append("ArrayOf"); - else - builder.Append(typeRef.Name?.Replace('`', '_')); - } - - return builder.ToString(); - } -} diff --git a/Il2CppInterop.Generator/Extensions/StringExtensions.cs b/Il2CppInterop.Generator/Extensions/StringExtensions.cs new file mode 100644 index 00000000..7a923b59 --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/StringExtensions.cs @@ -0,0 +1,24 @@ +using System.Buffers; + +namespace Il2CppInterop.Generator.Extensions; + +public static class StringExtensions +{ + public static string MakeValidCSharpName(this string name) + { + ArgumentException.ThrowIfNullOrEmpty(name); + var array = ArrayPool.Shared.Rent(name.Length + 1); + array[0] = '_'; + name.AsSpan().CopyTo(array.AsSpan(1)); + for (var i = name.Length; i > 0; i--) + { + if (!char.IsLetterOrDigit(array[i])) + { + array[i] = '_'; + } + } + var result = char.IsDigit(array[1]) ? new string(array.AsSpan(0, name.Length + 1)) : new string(array.AsSpan(1, name.Length)); + ArrayPool.Shared.Return(array); + return result; + } +} diff --git a/Il2CppInterop.Generator/Extensions/TypeAnalysisContextExtensions.cs b/Il2CppInterop.Generator/Extensions/TypeAnalysisContextExtensions.cs new file mode 100644 index 00000000..9b81c6d2 --- /dev/null +++ b/Il2CppInterop.Generator/Extensions/TypeAnalysisContextExtensions.cs @@ -0,0 +1,291 @@ +using System.Diagnostics.CodeAnalysis; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; + +namespace Il2CppInterop.Generator.Extensions; + +internal static class TypeAnalysisContextExtensions +{ + extension(TypeAnalysisContext type) + { + public bool IsModuleType => type.DeclaringType is null && string.IsNullOrEmpty(type.DefaultNamespace) && type.DefaultName == ""; + public bool IsPrivateImplementationDetailsType => type.DeclaringType is null && string.IsNullOrEmpty(type.DefaultNamespace) && type.DefaultName == ""; + public bool HasGenericParameters => type.GenericParameters.Count > 0; + public bool IsIl2CppPrimitive + { + get + { + if (type is ReferencedTypeAnalysisContext) + return false; + + if (type.Namespace != "Il2CppSystem") + return false; + + if (type.DeclaringAssembly.Name != "Il2Cppmscorlib") + return false; + + return type.Name is + "Boolean" or + "Byte" or + "SByte" or + "Int16" or + "UInt16" or + "Int32" or + "UInt32" or + "Int64" or + "UInt64" or + "Single" or + "Double" or + "Char" or + "IntPtr" or + "UIntPtr" or + "Void"; + } + } + + /// + /// The fields, methods, properties, and events of this type. + /// + public IEnumerable Members + { + get + { + return ((IEnumerable)type.Fields).Concat(type.Methods).Concat(type.Properties).Concat(type.Events); + } + } + + public string CSharpName + { + get + { + if (GenericTypeName.TryMatch(type.Name, out var result, out _)) + { + return result; + } + return type.Name; + } + } + + [MaybeNull] + public Type SourceType + { + get => type.GetExtraData("SourceType"); + set => type.PutExtraData("SourceType", value); + } + + [MaybeNull] + public MethodAnalysisContext PointerConstructor + { + get => type.GetExtraData("PointerConstructor"); + set => type.PutExtraData("PointerConstructor", value); + } + + [MaybeNull] + public TypeAnalysisContext InitializationType + { + get => type.GetExtraData("InitializationType"); + set => type.PutExtraData("InitializationType", value); + } + + [MaybeNull] + public FieldAnalysisContext SizeStorage + { + get => type.GetExtraData("SizeStorage"); + set => type.PutExtraData("SizeStorage", value); + } + + [MaybeNull] + public TypeAnalysisContext EnumIl2CppUnderlyingType + { + get => type.GetExtraData("EnumIl2CppUnderlyingType"); + set => type.PutExtraData("EnumIl2CppUnderlyingType", value); + } + + [MaybeNull] + public TypeAnalysisContext EnumMonoUnderlyingType + { + get => type.GetExtraData("EnumMonoUnderlyingType"); + set => type.PutExtraData("EnumMonoUnderlyingType", value); + } + + [MaybeNull] + public InjectedTypeAnalysisContext SystemExceptionType + { + get => type.GetExtraData("SystemExceptionType"); + set => type.PutExtraData("SystemExceptionType", value); + } + + [MaybeNull] + public List StaticConstructorInstructions + { + get => type.GetExtraData>("StaticConstructorInstructions"); + set => type.PutExtraData("StaticConstructorInstructions", value); + } + + public KnownTypeCode KnownType + { + get => type.GetExtraStruct("KnownType", KnownTypeCode.None); + set => type.PutExtraStruct("KnownType", value); + } + + public List GetOrCreateStaticConstructorInstructions() + { + var instructions = type.StaticConstructorInstructions; + if (instructions is null) + { + instructions = []; + type.StaticConstructorInstructions = instructions; + } + return instructions; + } + + public MethodAnalysisContext? TryGetMethodByName(string name) + { + for (var i = type.Methods.Count - 1; i >= 0; i--) + { + var method = type.Methods[i]; + if (method.Name == name) + { + return method; + } + } + return null; + } + + public MethodAnalysisContext GetMethodByName(string name) + { + return type.TryGetMethodByName(name) ?? throw new Exception($"Method {name} not found in type {type.Name}"); + } + + public FieldAnalysisContext GetFieldByName(string? name) + { + return type.TryGetFieldByName(name) ?? throw new Exception($"Field {name} not found in type {type.Name}"); + } + + public FieldAnalysisContext? TryGetFieldByName(string? name) + { + for (var i = type.Fields.Count - 1; i >= 0; i--) + { + var field = type.Fields[i]; + if (field.Name == name) + { + return field; + } + } + return null; + } + + public bool TryGetFieldByName(string? name, [NotNullWhen(true)] out FieldAnalysisContext? field) + { + field = type.TryGetFieldByName(name); + return field is not null; + } + + public PropertyAnalysisContext GetPropertyByName(string? name) + { + return type.TryGetPropertyByName(name) ?? throw new Exception($"Property {name} not found in type {type.Name}"); + } + + public PropertyAnalysisContext? TryGetPropertyByName(string? name) + { + for (var i = type.Properties.Count - 1; i >= 0; i--) + { + var property = type.Properties[i]; + if (property.Name == name) + { + return property; + } + } + return null; + } + + public bool TryGetPropertyByName(string? name, [NotNullWhen(true)] out PropertyAnalysisContext? property) + { + property = type.TryGetPropertyByName(name); + return property is not null; + } + + public bool TryGetMethodInSlot(int slot, [NotNullWhen(true)] out MethodAnalysisContext? method) + { + if (type is GenericInstanceTypeAnalysisContext genericInstanceType) + { + var genericMethod = genericInstanceType.GenericType.Methods.FirstOrDefault(m => m.Slot == slot); + if (genericMethod is not null) + { + method = new ConcreteGenericMethodAnalysisContext(genericMethod, genericInstanceType.GenericArguments, []); + return true; + } + } + else + { + var baseMethod = type.Methods.FirstOrDefault(m => m.Slot == slot); + if (baseMethod is not null) + { + method = baseMethod; + return true; + } + } + + method = null; + return false; + } + + public MethodAnalysisContext GetImplicitConversionFrom(TypeAnalysisContext sourceType) + { + return GetConversion("op_Implicit", type, sourceType, type.SelfInstantiateIfGeneric()); + } + + public MethodAnalysisContext GetImplicitConversionTo(TypeAnalysisContext targetType) + { + return GetConversion("op_Implicit", type, type.SelfInstantiateIfGeneric(), targetType); + } + + public MethodAnalysisContext GetExplicitConversionFrom(TypeAnalysisContext sourceType) + { + return GetConversion("op_Explicit", type, sourceType, type.SelfInstantiateIfGeneric()); + } + + public MethodAnalysisContext GetExplicitConversionTo(TypeAnalysisContext targetType) + { + return GetConversion("op_Explicit", type, type.SelfInstantiateIfGeneric(), targetType); + } + + public TypeAnalysisContext MaybeMakeGenericInstanceType(IReadOnlyCollection genericArguments) + { + if (type.GenericParameters.Count == 0) + { + return type; + } + else + { + return type.MakeGenericInstanceType(genericArguments); + } + } + + public TypeAnalysisContext SelfInstantiateIfGeneric() => type.MaybeMakeGenericInstanceType(type.GenericParameters); + + public bool ImplementsInterface(TypeAnalysisContext interfaceType) + { + foreach (var implementedInterface in type.InterfaceContexts) + { + if (TypeAnalysisContextEqualityComparer.Instance.Equals(implementedInterface, interfaceType)) + { + return true; + } + } + return false; + } + } + + private static MethodAnalysisContext GetConversion([ConstantExpected] string name, TypeAnalysisContext declaringType, TypeAnalysisContext sourceType, TypeAnalysisContext targetType) + { + return declaringType.Methods.First(m => + { + return m.Name == name + && m.IsStatic + && m.Parameters.Count == 1 + && TypeAnalysisContextEqualityComparer.Instance.Equals(m.ReturnType, targetType) + && TypeAnalysisContextEqualityComparer.Instance.Equals(m.Parameters[0].ParameterType, sourceType); + }); + } +} diff --git a/Il2CppInterop.Generator/Extensions/TypeReferenceEx.cs b/Il2CppInterop.Generator/Extensions/TypeReferenceEx.cs deleted file mode 100644 index 1ce59c58..00000000 --- a/Il2CppInterop.Generator/Extensions/TypeReferenceEx.cs +++ /dev/null @@ -1,58 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; - -namespace Il2CppInterop.Generator.Extensions; - -public static class TypeReferenceEx -{ - public static bool UnmangledNamesMatch(this TypeSignature typeRefA, TypeSignature typeRefB) - { - var aIsDefOrRef = typeRefA.GetType() == typeof(TypeDefOrRefSignature); - var bIsDefOrRef = typeRefB.GetType() == typeof(TypeDefOrRefSignature); - if (!(aIsDefOrRef && bIsDefOrRef) && typeRefA.GetType() != typeRefB.GetType()) - return false; - - switch (typeRefA) - { - case PointerTypeSignature pointer: - return pointer.BaseType.UnmangledNamesMatch(((PointerTypeSignature)typeRefB).BaseType); - case ByReferenceTypeSignature byRef: - return byRef.BaseType.UnmangledNamesMatch(((ByReferenceTypeSignature)typeRefB).BaseType); - case ArrayBaseTypeSignature array: - return array.BaseType.UnmangledNamesMatch(((ArrayBaseTypeSignature)typeRefB).BaseType); - case GenericInstanceTypeSignature genericInstance: - { - var elementA = genericInstance.GenericType.ToTypeSignature(); - var genericInstanceB = (GenericInstanceTypeSignature)typeRefB; - var elementB = genericInstanceB.GenericType.ToTypeSignature(); - if (!elementA.UnmangledNamesMatch(elementB)) - return false; - if (genericInstance.TypeArguments.Count != genericInstanceB.TypeArguments.Count) - return false; - - for (var i = 0; i < genericInstance.TypeArguments.Count; i++) - if (!genericInstance.TypeArguments[i].UnmangledNamesMatch(genericInstanceB.TypeArguments[i])) - return false; - - return true; - } - default: - return typeRefA.Name == typeRefB.Name; - } - } - - public static string? GetNamespacePrefix(this ITypeDefOrRef type) - { - if (type.DeclaringType is not null) - return $"{GetNamespacePrefix(type.DeclaringType)}.{type.DeclaringType.Name}"; - - return type.Namespace; - } - - // A temporary fix for AsmResolver returning IsValueType true for System.Enum - // See https://github.com/BepInEx/Il2CppInterop/issues/211 for the discussion - public static bool IsValueType(this ITypeDescriptor type) - { - return type.IsValueType && type.FullName != "System.Enum"; - } -} diff --git a/Il2CppInterop.Generator/Extensions/WriterEx.cs b/Il2CppInterop.Generator/Extensions/WriterEx.cs deleted file mode 100644 index 1c31abda..00000000 --- a/Il2CppInterop.Generator/Extensions/WriterEx.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Il2CppInterop.Generator.Extensions; - -public static class WriterEx -{ - [ThreadStatic] private static byte[]? ourBuffer; - - public static unsafe void Write(this BinaryWriter writer, T value) where T : unmanaged - { - var structSize = Marshal.SizeOf(); - - if (ourBuffer == null || ourBuffer.Length < structSize) - ourBuffer = new byte[structSize]; - - fixed (byte* bytes = ourBuffer) - { - *(T*)bytes = value; - } - - writer.Write(ourBuffer, 0, structSize); - } -} diff --git a/Il2CppInterop.Generator/FieldAccessorProcessingLayer.cs b/Il2CppInterop.Generator/FieldAccessorProcessingLayer.cs new file mode 100644 index 00000000..b804e75d --- /dev/null +++ b/Il2CppInterop.Generator/FieldAccessorProcessingLayer.cs @@ -0,0 +1,298 @@ +using System.Diagnostics; +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Core.Model.CustomAttributes; +using Il2CppInterop.Common.Attributes; +using Il2CppInterop.Generator.Operands; +using Il2CppInterop.Runtime.InteropTypes; + +namespace Il2CppInterop.Generator; + +public class FieldAccessorProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Field Accessor Processor"; + public override string Id => "field_accessor_processor"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var fieldAccessHelper = appContext.ResolveTypeOrThrow(typeof(FieldAccess)); + var getStaticFieldValue = fieldAccessHelper.GetMethodByName(nameof(FieldAccess.GetStaticFieldValue)); + var setStaticFieldValue = fieldAccessHelper.GetMethodByName(nameof(FieldAccess.SetStaticFieldValue)); + var getInstanceFieldValue = fieldAccessHelper.GetMethodByName(nameof(FieldAccess.GetInstanceFieldValue)); + var setInstanceFieldValue_Wbarrior = fieldAccessHelper.GetMethodByName(nameof(FieldAccess.SetInstanceFieldValue_Wbarrior)); + var setInstanceFieldValue_Pointer = fieldAccessHelper.GetMethodByName(nameof(FieldAccess.SetInstanceFieldValue_Pointer)); + + var setInstanceFieldValue = appContext.Binary.GetExportedFunctions().Any(pair => pair.Key == "il2cpp_gc_wbarrier_set_field") + ? setInstanceFieldValue_Wbarrior + : setInstanceFieldValue_Pointer; + + var il2CppFieldAttribute = appContext.ResolveTypeOrThrow(typeof(Il2CppFieldAttribute)); + var il2CppFieldAttributeConstructor = il2CppFieldAttribute.GetMethodByName(".ctor"); + var il2CppFieldAttributeIndex = il2CppFieldAttribute.GetPropertyByName(nameof(Il2CppFieldAttribute.Index)); + + var il2CppMemberAttribute = appContext.ResolveTypeOrThrow(typeof(Il2CppMemberAttribute)); + var il2CppMemberAttributeName = il2CppMemberAttribute.GetPropertyByName(nameof(Il2CppMemberAttribute.Name)); + + var byReference = appContext.ResolveTypeOrThrow(typeof(ByReference<>)); + var byReference_Constructor = byReference.GetMethodByName(".ctor"); + var byReferenceStatic = appContext.ResolveTypeOrThrow(typeof(ByReference)); + var byReferenceStatic_GetReferenceAtOffset = byReferenceStatic.GetMethodByName(nameof(ByReference.GetReferenceAtOffset)); + + var il2CppSystemObject = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Object"); + var il2CppSystemObject_get_Pointer = il2CppSystemObject.GetMethodByName($"get_{nameof(Object.Pointer)}"); + + var voidPointer = appContext.SystemTypes.SystemVoidType.MakePointerType(); + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + + var instantiatedType = type.SelfInstantiateIfGeneric(); + var isValueType = type.IsValueType; + HashSet existingNames = [type.CSharpName, .. type.Members.Select(m => m.Name)]; + + for (var i = type.Fields.Count - 1; i >= 0; i--) + { + var field = type.Fields[i]; + if (field.ConstantValue is not null) + { + Debug.Assert(field.IsStatic); + continue; // Skip fields with constant values, as they are not suitable for property conversion. + } + + if (field.IsInjected) + continue; + + if (!field.IsStatic && !type.IsIl2CppPrimitive) + { + var name = GetNonConflictingName($"GetFieldAddress_{field.Name}", existingNames); + var parameterType = isValueType ? byReference.MakeGenericInstanceType([instantiatedType]) : instantiatedType; + var getFieldAddress = new InjectedMethodAnalysisContext( + type, + name, + byReference.MakeGenericInstanceType([field.FieldType]), + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + [parameterType]) + { + IsInjected = true, + }; + type.Methods.Add(getFieldAddress); + + if (isValueType) + { + getFieldAddress.PutExtraData(new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg, getFieldAddress.Parameters[0]), + new Instruction(CilOpCodes.Ldsfld, field.GetInstantiatedOffsetStorage()), + new Instruction(CilOpCodes.Call, byReferenceStatic_GetReferenceAtOffset.MakeGenericInstanceMethod(instantiatedType, field.FieldType)), + new Instruction(CilOpCodes.Ret) + ], + }); + } + else + { + getFieldAddress.PutExtraData(new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg, getFieldAddress.Parameters[0]), + new Instruction(CilOpCodes.Callvirt, il2CppSystemObject_get_Pointer), + new Instruction(CilOpCodes.Ldsfld, field.GetInstantiatedOffsetStorage()), + new Instruction(CilOpCodes.Conv_I), + new Instruction(CilOpCodes.Add), + new Instruction(CilOpCodes.Newobj, byReference_Constructor.MakeConcreteGeneric([field.FieldType], [])), + new Instruction(CilOpCodes.Ret) + ], + }); + } + + field.FieldAddressAccessor = getFieldAddress; + } + + // Check for ByReference<> or Pointer<> in value types because they can break the runtime. + // https://github.com/dotnet/runtime/issues/121556 + var isPointerOrByRefInValueType = isValueType && !field.IsStatic && field.FieldType is GenericInstanceTypeAnalysisContext + { + GenericType.Namespace: "Il2CppInterop.Runtime.InteropTypes", + GenericType.Name: $"{nameof(ByReference<>)}`1" or $"{nameof(Pointer<>)}`1" + }; + + if (isValueType && !field.IsStatic && !isPointerOrByRefInValueType) + { + // Instance fields in value types do not get converted into properties. + + var attribute = new AnalyzedCustomAttribute(il2CppFieldAttributeConstructor); + if (field.Name != field.DefaultName) + { + var parameter = new CustomAttributePrimitiveParameter(field.DefaultName, attribute, CustomAttributeParameterKind.Property, attribute.Properties.Count); + attribute.Properties.Add(new CustomAttributeProperty(il2CppMemberAttributeName, parameter)); + } + var index = field.InitializationClassIndex; + if (index >= 0) + { + var parameter = new CustomAttributePrimitiveParameter(index, attribute, CustomAttributeParameterKind.Property, attribute.Properties.Count); + attribute.Properties.Add(new CustomAttributeProperty(il2CppFieldAttributeIndex, parameter)); + } + field.CustomAttributes ??= new(1); + field.CustomAttributes.Add(attribute); + + continue; + } + + var methodAttributes = field.IsStatic + ? MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Static + : MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName; + + var getMethod = new InjectedMethodAnalysisContext( + field.DeclaringType, + "get_" + field.Name, + field.FieldType, + methodAttributes, + []) + { + IsInjected = true, + }; + field.DeclaringType.Methods.Add(getMethod); + + // get accessor body + { + var instructions = new List(); + if (field.IsStatic) + { + instructions.Add(CilOpCodes.Ldsfld, field.GetInstantiatedFieldInfoAddressStorage()); + instructions.Add(CilOpCodes.Call, getStaticFieldValue.MakeGenericInstanceMethod([field.FieldType])); + instructions.Add(CilOpCodes.Ret); + } + else if (isPointerOrByRefInValueType) + { + var fieldType = (GenericInstanceTypeAnalysisContext)field.FieldType; + var genericArgument = fieldType.GenericArguments[0]; + var uninstantiatedConversion = fieldType.GenericType.GetExplicitConversionFrom(voidPointer); + instructions.Add(CilOpCodes.Ldarg, This.Instance); + instructions.Add(CilOpCodes.Ldfld, field.SelfInstantiateIfNecessary()); + instructions.Add(CilOpCodes.Call, uninstantiatedConversion.MakeConcreteGenericMethod(fieldType.GenericArguments, [])); + instructions.Add(CilOpCodes.Ret); + } + else + { + instructions.Add(CilOpCodes.Ldarg, This.Instance); + instructions.Add(CilOpCodes.Ldsfld, field.GetInstantiatedOffsetStorage()); + instructions.Add(CilOpCodes.Call, getInstanceFieldValue.MakeGenericInstanceMethod([field.FieldType])); + instructions.Add(CilOpCodes.Ret); + } + + getMethod.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + }); + } + + var setMethod = new InjectedMethodAnalysisContext( + field.DeclaringType, + "set_" + field.Name, + field.AppContext.SystemTypes.SystemVoidType, + methodAttributes, + [field.FieldType]) + { + IsInjected = true, + }; + field.DeclaringType.Methods.Add(setMethod); + + // set accessor body + { + var instructions = new List(); + if (field.IsStatic) + { + instructions.Add(CilOpCodes.Ldsfld, field.GetInstantiatedFieldInfoAddressStorage()); + instructions.Add(CilOpCodes.Ldarg, setMethod.Parameters[0]); + instructions.Add(CilOpCodes.Call, setStaticFieldValue.MakeGenericInstanceMethod([field.FieldType])); + instructions.Add(CilOpCodes.Ret); + } + else if (isPointerOrByRefInValueType) + { + var fieldType = (GenericInstanceTypeAnalysisContext)field.FieldType; + var genericArgument = fieldType.GenericArguments[0]; + var uninstantiatedConversion = fieldType.GenericType.GetExplicitConversionTo(voidPointer); + instructions.Add(CilOpCodes.Ldarg, This.Instance); + instructions.Add(CilOpCodes.Ldarg, setMethod.Parameters[0]); + instructions.Add(CilOpCodes.Call, uninstantiatedConversion.MakeConcreteGenericMethod(fieldType.GenericArguments, [])); + instructions.Add(CilOpCodes.Stfld, field.SelfInstantiateIfNecessary()); + instructions.Add(CilOpCodes.Ret); + } + else + { + instructions.Add(CilOpCodes.Ldarg, This.Instance); + instructions.Add(CilOpCodes.Ldsfld, field.GetInstantiatedOffsetStorage()); + instructions.Add(CilOpCodes.Ldarg, setMethod.Parameters[0]); + instructions.Add(CilOpCodes.Call, setInstanceFieldValue.MakeGenericInstanceMethod([field.FieldType])); + instructions.Add(CilOpCodes.Ret); + } + + setMethod.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + }); + } + + var property = new InjectedPropertyAnalysisContext( + field.Name, + field.FieldType, + getMethod, + setMethod, + PropertyAttributes.None, + field.DeclaringType) + { + IsInjected = true, + OriginalField = field, + }; + field.PropertyAccessor = property; + field.DeclaringType.Properties.Add(property); + + // Il2CppFieldAttribute + { + var attribute = new AnalyzedCustomAttribute(il2CppFieldAttributeConstructor); + if (property.Name != field.DefaultName) + { + var parameter = new CustomAttributePrimitiveParameter(field.DefaultName, attribute, CustomAttributeParameterKind.Property, attribute.Properties.Count); + attribute.Properties.Add(new CustomAttributeProperty(il2CppMemberAttributeName, parameter)); + } + var index = field.InitializationClassIndex; + if (index >= 0) + { + var parameter = new CustomAttributePrimitiveParameter(index, attribute, CustomAttributeParameterKind.Property, attribute.Properties.Count); + attribute.Properties.Add(new CustomAttributeProperty(il2CppFieldAttributeIndex, parameter)); + } + property.CustomAttributes = [attribute]; + } + + if (isPointerOrByRefInValueType) + { + field.OverrideFieldType = voidPointer; + field.OverrideName = GetNonConflictingName(field.Name + "_BackingField", existingNames); + field.Visibility = FieldAttributes.Private; + } + else + { + field.DeclaringType.Fields.RemoveAt(i); + } + } + } + } + } + + private static string GetNonConflictingName(string baseName, HashSet existingNames) + { + var name = baseName; + while (existingNames.Contains(name)) + { + name = $"{baseName}_"; + } + return name; + } +} diff --git a/Il2CppInterop.Generator/GeneratorOptions.cs b/Il2CppInterop.Generator/GeneratorOptions.cs deleted file mode 100644 index 59fe941e..00000000 --- a/Il2CppInterop.Generator/GeneratorOptions.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.IO.Compression; -using System.Text; -using System.Text.RegularExpressions; -using AsmResolver.DotNet; - -namespace Il2CppInterop.Generator; - -public class GeneratorOptions -{ - public List? Source { get; set; } - public string? OutputDir { get; set; } - - public string? UnityBaseLibsDir { get; set; } - public List AdditionalAssembliesBlacklist { get; } = new(); - public int TypeDeobfuscationCharsPerUniquifier { get; set; } = 2; - public int TypeDeobfuscationMaxUniquifiers { get; set; } = 10; - public string? GameAssemblyPath { get; set; } - public bool Verbose { get; set; } - public bool NoXrefCache { get; set; } - public Regex? ObfuscatedNamesRegex { get; set; } - public Dictionary RenameMap { get; } = new(); - public bool PassthroughNames { get; set; } - public bool Parallel { get; set; } = true; - - public PrefixMode Il2CppPrefixMode { get; set; } = PrefixMode.OptIn; - public HashSet NamespacesAndAssembliesToPrefix { get; } = - new() { "System", "mscorlib", "Microsoft", "Mono", "I18N" }; - public HashSet NamespacesAndAssembliesToNotPrefix { get; } = - new() { "Assembly-CSharp", "Unity" }; - - public List DeobfuscationGenerationAssemblies { get; } = new(); - public string? DeobfuscationNewAssembliesPath { get; set; } - - /// - /// Reads a rename map from the specified name into the specified instance of options - /// - public void ReadRenameMap(string fileName) - { - using var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); - ReadRenameMap(fileStream, fileName.EndsWith(".gz")); - } - - /// - /// Reads a rename map from the specified name into the specified instance of options. - /// The stream is not closed by this method. - /// - public void ReadRenameMap(Stream fileStream, bool isGzip) - { - if (isGzip) - { - using var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress, true); - ReadRenameMap(gzipStream, false); - return; - } - - using var reader = new StreamReader(fileStream, Encoding.UTF8, false, 65536, true); - while (!reader.EndOfStream) - { - var line = reader.ReadLine(); - if (string.IsNullOrEmpty(line) || line.StartsWith("#")) continue; - var split = line.Split(';'); - if (split.Length < 2) continue; - RenameMap[split[0]] = split[1]; - } - } - - public enum PrefixMode - { - /// - /// Only specified namespaces and assemblies will be renamed. - /// - OptIn, - /// - /// Only specified namespaces and assemblies will not be renamed. - /// - OptOut - } -} diff --git a/Il2CppInterop.Generator/GenericTypeName.cs b/Il2CppInterop.Generator/GenericTypeName.cs new file mode 100644 index 00000000..f64177f7 --- /dev/null +++ b/Il2CppInterop.Generator/GenericTypeName.cs @@ -0,0 +1,27 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +namespace Il2CppInterop.Generator; + +internal static partial class GenericTypeName +{ + public static bool TryMatch(string genericName, [NotNullWhen(true)] out string? typeName, [NotNullWhen(true)] out string? genericCount) + { + var match = GenericTypeRegex.Match(genericName); + if (match.Success) + { + typeName = match.Groups[1].Value; + genericCount = match.Groups[2].Value; + return true; + } + else + { + typeName = null; + genericCount = null; + return false; + } + } + + [GeneratedRegex(@"^(.+)`(\d+)$")] + private static partial Regex GenericTypeRegex { get; } +} diff --git a/Il2CppInterop.Generator/ICallProcessingLayer.cs b/Il2CppInterop.Generator/ICallProcessingLayer.cs new file mode 100644 index 00000000..775d86c1 --- /dev/null +++ b/Il2CppInterop.Generator/ICallProcessingLayer.cs @@ -0,0 +1,44 @@ +using System.Diagnostics; +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator; + +public class ICallProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "ICall Processor"; + public override string Id => "icall_processor"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + + foreach (var method in type.Methods) + { + if (!method.IsUnstripped || !method.DefaultImplAttributes.HasFlag(MethodImplAttributes.InternalCall)) + continue; + + Debug.Assert(type.InitializationType is not null); + Debug.Assert(!method.HasExtraData()); + Debug.Assert(!method.HasExtraData()); + Debug.Assert(!method.HasExtraData()); + Debug.Assert(method.GenericParameters.Count == 0 && type.GenericParameters.Count == 0, "Internal calls cannot be generic."); + + } + } + } + } + + private static string GetICallSignature(MethodAnalysisContext method) + { + return $"{method.DeclaringType!.DefaultFullName}::{method.DefaultName}"; + } +} diff --git a/Il2CppInterop.Generator/Il2CppGame.cs b/Il2CppInterop.Generator/Il2CppGame.cs new file mode 100644 index 00000000..8cd61519 --- /dev/null +++ b/Il2CppInterop.Generator/Il2CppGame.cs @@ -0,0 +1,85 @@ +using Cpp2IL.Core; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.InstructionSets; +using Cpp2IL.Core.Logging; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Plugin.StrippedCodeRegSupport; +using LibCpp2IL; + +namespace Il2CppInterop.Generator; + +public static class Il2CppGame +{ + static Il2CppGame() + { + Logger.InfoLog += Console.WriteLine; + Logger.WarningLog += Console.WriteLine; + Logger.ErrorLog += Console.WriteLine; + Logger.VerboseLog += Console.WriteLine; + + InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.X86_32); + InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.X86_64); + InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.WASM); + InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.ARM_V7); + InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.ARM_V8); + + LibCpp2IlBinaryRegistry.RegisterBuiltInBinarySupport(); + + new StrippedCodeRegSupportPlugin().OnLoad(); + } + + public static void Process(string gameExePath, string outputFolder, Cpp2IlOutputFormat outputFormat, List processingLayers, KeyValuePair[] extraData) + { + Process(gameExePath, processingLayers, extraData); + + outputFormat.DoOutput(GetCurrentAppContext(), outputFolder); + } + + public static void Process(string gameExePath, List processingLayers, KeyValuePair[] extraData) + { + var gameExeName = Path.GetFileNameWithoutExtension(gameExePath); + + var gameDirectory = Path.GetDirectoryName(gameExePath)!; + + var GameDataPath = Path.Join(gameDirectory, $"{gameExeName}_Data"); + + var GameAssemblyPath = GetGameAssemblyPath(gameDirectory); + + var MetaDataPath = Path.Join(GameDataPath, "il2cpp_data", "Metadata", "global-metadata.dat"); + + var UnityVersion = Cpp2IlApi.DetermineUnityVersion(null, GameDataPath); + + Cpp2IlApi.InitializeLibCpp2Il(GameAssemblyPath, MetaDataPath, UnityVersion, false); + + foreach ((var key, var value) in extraData) + { + Cpp2IlApi.CurrentAppContext.PutExtraData(key, value); + } + + foreach (var cpp2IlProcessingLayer in processingLayers) + { + cpp2IlProcessingLayer.PreProcess(GetCurrentAppContext(), processingLayers); + } + + foreach (var cpp2IlProcessingLayer in processingLayers) + { + cpp2IlProcessingLayer.Process(GetCurrentAppContext()); + } + } + + private static string GetGameAssemblyPath(string gameDirectory) + { + foreach (var fileName in (ReadOnlySpan)["GameAssembly.dll", "GameAssembly.dylib", "GameAssembly.so"]) + { + var path = Path.Join(gameDirectory, fileName); + if (File.Exists(path)) + return path; + } + throw new FileNotFoundException("Could not find GameAssembly binary in game directory."); + } + + private static ApplicationAnalysisContext GetCurrentAppContext() + { + return Cpp2IlApi.CurrentAppContext ?? throw new NullReferenceException(); + } +} diff --git a/Il2CppInterop.Generator/Il2CppInterop.Generator.csproj b/Il2CppInterop.Generator/Il2CppInterop.Generator.csproj index bab17953..e9573e45 100644 --- a/Il2CppInterop.Generator/Il2CppInterop.Generator.csproj +++ b/Il2CppInterop.Generator/Il2CppInterop.Generator.csproj @@ -1,23 +1,19 @@ - + Il2CppInterop.Generator knah, BepInEx et al. Library for generating assemblies and metadata for calling Il2Cpp functions from .NET - netstandard2.1;net472 - Il2CppInterop.Generator + net10.0 Debug;Release AnyCPU - latest + preview enable - - - MonoModBackports - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -25,5 +21,6 @@ + diff --git a/Il2CppInterop.Generator/Il2CppInteropGenerator.cs b/Il2CppInterop.Generator/Il2CppInteropGenerator.cs deleted file mode 100644 index a1a1aca5..00000000 --- a/Il2CppInterop.Generator/Il2CppInteropGenerator.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Il2CppInterop.Common; -using Il2CppInterop.Common.Host; -using Il2CppInterop.Common.XrefScans; -using Il2CppInterop.Generator.Runners; -using Il2CppInterop.Generator.XrefScans; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator; - -public sealed class Il2CppInteropGenerator : BaseHost -{ - private GeneratorOptions Options { get; init; } - - private readonly List _runners = new(); - - private Il2CppInteropGenerator(GeneratorOptions options) - { - Options = options; - } - - public static Il2CppInteropGenerator Create(GeneratorOptions options) - { - var generator = new Il2CppInteropGenerator(options); - generator.AddXrefScanner(); - return generator; - } - - public override void Start() - { - base.Start(); - - foreach (var runner in _runners) - { - Logger.Instance.LogTrace("Running {RunnerName}", runner.GetType().Name); - runner.Run(Options); - } - } - - public override void Dispose() - { - foreach (var runner in _runners) - runner.Dispose(); - _runners.Clear(); - base.Dispose(); - } - - public void Run() - { - Start(); - Dispose(); - } - - internal Il2CppInteropGenerator AddRunner() where T : IRunner, new() - { - _runners.Add(new T()); - return this; - } -} diff --git a/Il2CppInterop.Generator/Il2CppRenamingProcessingLayer.cs b/Il2CppInterop.Generator/Il2CppRenamingProcessingLayer.cs new file mode 100644 index 00000000..ca808274 --- /dev/null +++ b/Il2CppInterop.Generator/Il2CppRenamingProcessingLayer.cs @@ -0,0 +1,76 @@ +using System.Runtime.CompilerServices; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Logging; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator; + +public class Il2CppRenamingProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Il2Cpp Name Changes"; + + public override string Id => "il2cpprenamer"; + + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + Logger.InfoNewline("Renaming assemblies and types to Il2Cpp", nameof(Il2CppRenamingProcessingLayer)); + + var assemblyCount = appContext.Assemblies.Count; + for (var i = 0; i < assemblyCount; i++) + { + var assembly = appContext.Assemblies[i]; + + if (!IsUnity(assembly.Name) && !IsAssemblyCSharp(assembly.Name)) + { + assembly.OverrideName = "Il2Cpp" + assembly.Name; + } + + foreach (var type in assembly.TopLevelTypes) + { + if (!IsUnity(type.Namespace)) + { + type.OverrideNamespace = "Il2Cpp" + type.Namespace; + } + } + + ResetTypesByName(assembly); + + progressCallback?.Invoke(i, assemblyCount); + } + + ResetAssembliesByName(appContext); + } + + private static bool IsUnity(string name) + { + return name.StartsWith("Unity", StringComparison.Ordinal); + } + + private static bool IsAssemblyCSharp(string name) + { + return name.StartsWith("Assembly-CSharp", StringComparison.Ordinal); + } + + private static void ResetAssembliesByName(ApplicationAnalysisContext appContext) + { + var dictionary = appContext.AssembliesByName; + dictionary.Clear(); + foreach (var assembly in appContext.Assemblies) + { + dictionary[assembly.Name] = assembly; + } + } + + private static void ResetTypesByName(AssemblyAnalysisContext assembly) + { + var dictionary = GetTypesByName(assembly); + dictionary.Clear(); + foreach (var type in assembly.Types) + { + dictionary[type.FullName] = type; + } + } + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "TypesByName")] + private static extern ref Dictionary GetTypesByName(AssemblyAnalysisContext assembly); +} diff --git a/Il2CppInterop.Generator/Il2CppTypeConstraintProcessingLayer.cs b/Il2CppInterop.Generator/Il2CppTypeConstraintProcessingLayer.cs new file mode 100644 index 00000000..884700cc --- /dev/null +++ b/Il2CppInterop.Generator/Il2CppTypeConstraintProcessingLayer.cs @@ -0,0 +1,46 @@ +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Common; + +namespace Il2CppInterop.Generator; + +public class Il2CppTypeConstraintProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Il2CppType Constraint Processor"; + public override string Id => "il2cpptype_constraint_processor"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var iil2CppTypeGeneric = appContext.ResolveTypeOrThrow(typeof(IIl2CppType<>)); + var iobject = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.IObject"); + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + + foreach (var genericParameter in type.GenericParameters) + { + genericParameter.ConstraintTypes.Add(iil2CppTypeGeneric.MakeGenericInstanceType([genericParameter])); + genericParameter.ConstraintTypes.Add(iobject); + } + + foreach (var method in type.Methods) + { + if (method.IsInjected) + continue; + + foreach (var genericParameter in method.GenericParameters) + { + genericParameter.ConstraintTypes.Add(iil2CppTypeGeneric.MakeGenericInstanceType([genericParameter])); + genericParameter.ConstraintTypes.Add(iobject); + } + } + } + } + } +} diff --git a/Il2CppInterop.Generator/IndexerAttributeInjectionProcessingLayer.cs b/Il2CppInterop.Generator/IndexerAttributeInjectionProcessingLayer.cs new file mode 100644 index 00000000..144edb2c --- /dev/null +++ b/Il2CppInterop.Generator/IndexerAttributeInjectionProcessingLayer.cs @@ -0,0 +1,43 @@ +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Core.Model.CustomAttributes; + +namespace Il2CppInterop.Generator; + +public class IndexerAttributeInjectionProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Id => "indexer_attribute_injection"; + public override string Name => "Indexer Attribute Injection"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var defaultMemberAttribute = appContext.Mscorlib.GetTypeByFullNameOrThrow("System.Reflection.DefaultMemberAttribute"); + var defaultMemberAttributeConstructor = defaultMemberAttribute.Methods.First(m => m.IsInstanceConstructor && m.Parameters.Count == 1 && m.Parameters[0].ParameterType == appContext.SystemTypes.SystemStringType); + + HashSet indexerNames = []; + foreach (var type in appContext.AllTypes) + { + indexerNames.Clear(); + indexerNames.AddRange(type.Properties.Where(IsIndexerProperty).Select(p => p.Name)); + if (indexerNames.Count != 1) + continue; + + type.CustomAttributes ??= new(1); + + var customAttribute = new AnalyzedCustomAttribute(defaultMemberAttributeConstructor); + customAttribute.ConstructorParameters.Add(new CustomAttributePrimitiveParameter(indexerNames.First(), customAttribute, CustomAttributeParameterKind.ConstructorParam, 0)); + type.CustomAttributes.Add(customAttribute); + } + } + + private static bool IsIndexerProperty(PropertyAnalysisContext property) + { + if (property is { Getter.Parameters.Count: > 0, Getter.Overrides.Count: 0 }) + { + return property.Setter is null || property.Setter.Parameters.Count == property.Getter.Parameters.Count + 1; + } + else + { + return property is { Setter.Parameters.Count: > 1, Setter.Overrides.Count: 0 }; + } + } +} diff --git a/Il2CppInterop.Generator/InitializationClassProcessingLayer.cs b/Il2CppInterop.Generator/InitializationClassProcessingLayer.cs new file mode 100644 index 00000000..9d643eae --- /dev/null +++ b/Il2CppInterop.Generator/InitializationClassProcessingLayer.cs @@ -0,0 +1,656 @@ +using System.Buffers.Binary; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Logging; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Core.Model.CustomAttributes; +using Cpp2IL.Core.Utils; +using Il2CppInterop.Common.Attributes; +using Il2CppInterop.Generator.Operands; +using Il2CppInterop.Generator.Visitors; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.Injection; +using Il2CppInterop.Runtime.InteropTypes; +using Il2CppInterop.Runtime.Runtime; + +namespace Il2CppInterop.Generator; + +public class InitializationClassProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Id => "initialization_class_injector"; + public override string Name => "Inject initialization class into the Cpp2IL context system"; + + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var runClassConstructor = appContext.Mscorlib.GetTypeByFullNameOrThrow("System.Runtime.CompilerServices.RuntimeHelpers") + .Methods.First(m => m.Name == nameof(RuntimeHelpers.RunClassConstructor) && m.Parameters[0].ParameterType != appContext.SystemTypes.SystemIntPtrType); + var il2CppClassPointerStore = appContext.ResolveTypeOrThrow(typeof(Il2CppClassPointerStore<>)); + var classPointerField = il2CppClassPointerStore.GetFieldByName(nameof(Il2CppClassPointerStore<>.NativeClassPointer)); + + var il2CppStaticClass = appContext.ResolveTypeOrThrow(typeof(IL2CPP)); + var getIl2CppNestedType = il2CppStaticClass.GetMethodByName(nameof(IL2CPP.GetIl2CppNestedType)); + var getIl2CppClass = il2CppStaticClass.GetMethodByName(nameof(IL2CPP.GetIl2CppClass)); + var getIl2CppGenericInstanceType = il2CppStaticClass.GetMethodByName(nameof(IL2CPP.GetIl2CppGenericInstanceType)); + var il2CppRuntimeClassInit = il2CppStaticClass.GetMethodByName(nameof(IL2CPP.Il2CppRuntimeClassInit)); + var getIl2CppField = il2CppStaticClass.GetMethodByName(nameof(IL2CPP.GetIl2CppField)); + var getIl2CppFieldOffset = il2CppStaticClass.GetMethodByName(nameof(IL2CPP.GetIl2CppFieldOffset)); + var getIl2CppMethod = il2CppStaticClass.GetMethodByName(nameof(IL2CPP.GetIl2CppMethod)); + var getIl2CppMethodByToken = il2CppStaticClass.GetMethodByName(nameof(IL2CPP.GetIl2CppMethodByToken)); + var getIl2CppGenericInstanceMethod = il2CppStaticClass.GetMethodByName(nameof(IL2CPP.GetIl2CppGenericInstanceMethod)); + var getIl2CppValueSize = il2CppStaticClass.GetMethodByName(nameof(IL2CPP.GetIl2cppValueSize)); + var resolveICall = il2CppStaticClass.GetMethodByName(nameof(IL2CPP.ResolveICall)); + + var typeInjector = appContext.ResolveTypeOrThrow(typeof(TypeInjector)); + var registerTypeInIl2Cpp = typeInjector.Methods.Single(m => + { + return m.Name == nameof(TypeInjector.RegisterTypeInIl2Cpp) && m.Parameters.Count is 0 && m.GenericParameters.Count == 1; + }); + + var multicastDelegateType = appContext.Mscorlib.GetTypeByFullNameOrThrow("System.MulticastDelegate"); + var asyncCallbackType = appContext.Mscorlib.GetTypeByFullNameOrThrow("System.AsyncCallback"); + var iasyncResultType = appContext.Mscorlib.GetTypeByFullNameOrThrow("System.IAsyncResult"); + + var byReference = appContext.ResolveTypeOrThrow(typeof(ByReference<>)); + var byReference_CopyFrom = byReference.GetMethodByName(nameof(ByReference<>.CopyFrom)); + var byReference_CopyTo = byReference.GetMethodByName(nameof(ByReference<>.CopyTo)); + var byReference_Constructor = byReference.GetMethodByName(".ctor"); + + var il2CppTypeHelper_SizeOf = appContext.ResolveTypeOrThrow(typeof(Il2CppType)).GetMethodByName(nameof(Il2CppType.SizeOf)); + + var il2CppObjectPool = appContext.ResolveTypeOrThrow(typeof(Il2CppObjectPool)); + var il2CppObjectPool_RegisterInitializer = il2CppObjectPool.GetMethodByName(nameof(Il2CppObjectPool.RegisterInitializer)); + var il2CppObjectPool_ValueTypeInitializer = il2CppObjectPool.GetMethodByName(nameof(Il2CppObjectPool.ValueTypeInitializer)); + + var funcTypeInstantiated = (GenericInstanceTypeAnalysisContext)il2CppObjectPool_RegisterInitializer.Parameters[1].ParameterType; + var funcType = funcTypeInstantiated.GenericType; + var funcConstructor = funcType.Methods.Single(m => m.IsInstanceConstructor && m.Parameters.Count == 2); + var funcConstructorInstantiated = funcConstructor.MakeConcreteGeneric(funcTypeInstantiated.GenericArguments, []); + + var il2CppTypeAttribute = appContext.ResolveTypeOrThrow(typeof(Il2CppTypeAttribute)); + var il2CppTypeAttributeConstructor = il2CppTypeAttribute.GetMethodByName(".ctor"); + + var tokenLessMethodCount = 0; + + // 2 pointers + var headerSize = (object)(appContext.Binary.is32Bit ? 8 : 16); + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + for (var i = 0; i < assembly.Types.Count; i++) + { + var type = assembly.Types[i]; + + if (type.IsInjected) + continue; + + var initializationType = assembly.InjectType( + "Il2CppInterop.Generated", + $"Il2CppInternals_{HashString(type.FullName):x16}", + appContext.SystemTypes.SystemObjectType, + TypeAttributes.NotPublic | TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.Class); + initializationType.IsInjected = true; + initializationType.CopyGenericParameters(type, true, true); + + AddInstructionsToStaticConstructor(type, initializationType, runClassConstructor); + + // Il2CppTypeAttribute + { + var attribute = new AnalyzedCustomAttribute(il2CppTypeAttributeConstructor); + attribute.ConstructorParameters.Add(new CustomAttributeTypeParameter(initializationType, attribute, CustomAttributeParameterKind.ConstructorParam, 0)); + + type.CustomAttributes ??= new(1); + type.CustomAttributes.Add(attribute); + } + + // Initialization static constructor + { + var staticConstructor = new InjectedMethodAnalysisContext( + initializationType, + ".cctor", + type.AppContext.SystemTypes.SystemVoidType, + MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, + []); + initializationType.Methods.Add(staticConstructor); + + var instructions = new List(); + var localVariables = new List(); + + var typeToInitialize = initializationType.GenericParameters.Count == 0 + ? type + : type.MakeGenericInstanceType(initializationType.GenericParameters); + + var concreteClassPointerField = new ConcreteGenericFieldAnalysisContext(classPointerField, il2CppClassPointerStore.MakeGenericInstanceType([typeToInitialize])); + if (type.IsUnstripped) + { + instructions.Add(new Instruction(CilOpCodes.Call, registerTypeInIl2Cpp.MakeGenericInstanceMethod(typeToInitialize))); + } + else + { + if (typeToInitialize.DeclaringType is not null) + { + // Ensure declaring type is initialized first + instructions.Add(new Instruction(CilOpCodes.Ldtoken, typeToInitialize.DeclaringType)); + instructions.Add(new Instruction(CilOpCodes.Call, runClassConstructor)); + + // Il2CppClassPointerStore.NativeClassPointer = IL2CPP.GetIl2CppNestedType(Il2CppClassPointerStore.NativeClassPointer, "NestedClass"); + var declaringTypeClassPointerField = new ConcreteGenericFieldAnalysisContext(classPointerField, il2CppClassPointerStore.MakeGenericInstanceType([typeToInitialize.DeclaringType])); + instructions.Add(new Instruction(CilOpCodes.Ldsfld, declaringTypeClassPointerField)); + instructions.Add(new Instruction(CilOpCodes.Ldstr, type.DefaultName));// typeToInitialize can have the wrong DefaultName + instructions.Add(new Instruction(CilOpCodes.Call, getIl2CppNestedType)); + } + else + { + // Il2CppClassPointerStore.NativeClassPointer = IL2CPP.GetIl2CppClass("Assembly-CSharp.dll", "", "Class"); + var assemblyName = assembly.DefaultName; + if (!assemblyName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && assemblyName is not "__Generated") + { + assemblyName += ".dll"; + } + instructions.Add(new Instruction(CilOpCodes.Ldstr, assemblyName)); + instructions.Add(new Instruction(CilOpCodes.Ldstr, type.DefaultNamespace)); + instructions.Add(new Instruction(CilOpCodes.Ldstr, type.DefaultName)); + instructions.Add(new Instruction(CilOpCodes.Call, getIl2CppClass)); + } + if (type.GenericParameters.Count > 0) + { + instructions.Add(new Instruction(CilOpCodes.Ldc_I4, type.GenericParameters.Count)); + instructions.Add(new Instruction(CilOpCodes.Newarr, appContext.SystemTypes.SystemIntPtrType)); + for (var j = 0; j < type.GenericParameters.Count; j++) + { + instructions.Add(new Instruction(CilOpCodes.Dup)); + instructions.Add(new Instruction(CilOpCodes.Ldc_I4, j)); + var genericParameter = initializationType.GenericParameters[j]; + var classPointerForGenericParameter = new ConcreteGenericFieldAnalysisContext(classPointerField, il2CppClassPointerStore.MakeGenericInstanceType([genericParameter])); + instructions.Add(new Instruction(CilOpCodes.Ldsfld, classPointerForGenericParameter)); + instructions.Add(new Instruction(CilOpCodes.Stelem_I)); + } + instructions.Add(new Instruction(CilOpCodes.Call, getIl2CppGenericInstanceType)); + } + instructions.Add(new Instruction(CilOpCodes.Stsfld, concreteClassPointerField)); + + // IL2CPP.il2cpp_runtime_class_init(Il2CppClassPointerStore.NativeClassPointer); + instructions.Add(new Instruction(CilOpCodes.Ldsfld, concreteClassPointerField)); + instructions.Add(new Instruction(CilOpCodes.Call, il2CppRuntimeClassInit)); + } + + // Size = IL2CPP.il2cpp_class_value_size(Il2CppClassPointerStore.NativeClassPointer, ref align); + if (type.IsValueType) + { + var sizeStore = initializationType.InjectFieldContext( + "Size", + appContext.SystemTypes.SystemInt32Type, + FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.InitOnly); + type.SizeStorage = sizeStore; + + FieldAnalysisContext instantiatedSizeStore = initializationType.GenericParameters.Count > 0 + ? new ConcreteGenericFieldAnalysisContext(sizeStore, initializationType.MakeGenericInstanceType(initializationType.GenericParameters)) + : sizeStore; + + instructions.Add(new Instruction(CilOpCodes.Ldsfld, concreteClassPointerField)); + instructions.Add(new Instruction(CilOpCodes.Call, getIl2CppValueSize)); + instructions.Add(new Instruction(CilOpCodes.Stsfld, instantiatedSizeStore)); + } + + // FieldOffset_0 = (int)IL2CPP.il2cpp_field_get_offset(IL2CPP.GetIl2CppField(Il2CppClassPointerStore.NativeClassPointer, "field_name")); + for (var index = 0; index < type.Fields.Count; index++) + { + var field = type.Fields[index]; + + if (field.IsInjected) + continue; + + if (field.IsUnstripped && !type.IsUnstripped) + continue; + + field.InitializationClassIndex = index; + + var infoStore = initializationType.InjectFieldContext( + $"FieldInfoPtr_{index}", + appContext.SystemTypes.SystemIntPtrType, + FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.InitOnly); + field.FieldInfoAddressStorage = infoStore; + + FieldAnalysisContext instantiatedInfoStore = initializationType.GenericParameters.Count > 0 + ? new ConcreteGenericFieldAnalysisContext(infoStore, initializationType.MakeGenericInstanceType(initializationType.GenericParameters)) + : infoStore; + + var offsetStore = initializationType.InjectFieldContext( + $"FieldOffset_{index}", + appContext.SystemTypes.SystemInt32Type, + FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.InitOnly); + field.OffsetStorage = offsetStore; + + FieldAnalysisContext instantiatedOffsetStore = initializationType.GenericParameters.Count > 0 + ? new ConcreteGenericFieldAnalysisContext(offsetStore, initializationType.MakeGenericInstanceType(initializationType.GenericParameters)) + : offsetStore; + + instructions.Add(new Instruction(CilOpCodes.Ldsfld, concreteClassPointerField)); + instructions.Add(new Instruction(CilOpCodes.Ldstr, field.DefaultName)); + instructions.Add(new Instruction(CilOpCodes.Call, getIl2CppField)); + instructions.Add(new Instruction(CilOpCodes.Dup)); + instructions.Add(new Instruction(CilOpCodes.Stsfld, instantiatedInfoStore)); + instructions.Add(new Instruction(CilOpCodes.Call, getIl2CppFieldOffset)); + instructions.Add(new Instruction(CilOpCodes.Conv_I4)); + if (type.IsValueType) + { + // il2cpp_field_get_offset returns offset including the object header + // For value types, we need to subtract the header size to get the offset of the field within the struct + instructions.Add(new Instruction(CilOpCodes.Ldc_I4, headerSize)); + instructions.Add(new Instruction(CilOpCodes.Sub)); + } + instructions.Add(new Instruction(CilOpCodes.Stsfld, instantiatedOffsetStore)); + } + + // MethodInfoPtr_0 + for (var index = 0; index < type.Methods.Count; index++) + { + var method = type.Methods[index]; + + if (method.IsUnstripped || method.IsInjected) + continue; + + method.InitializationClassIndex = index; + + var methodInfoStore = initializationType.InjectFieldContext( + $"MethodInfoPtr_{index}", + appContext.SystemTypes.SystemIntPtrType, + FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.InitOnly); + method.MethodInfoField = methodInfoStore; + + FieldAnalysisContext concreteMethodInfoStore = initializationType.GenericParameters.Count > 0 + ? new ConcreteGenericFieldAnalysisContext(methodInfoStore, initializationType.MakeGenericInstanceType(initializationType.GenericParameters)) + : methodInfoStore; + + if (method.Token == 0) + { + tokenLessMethodCount++; + + instructions.Add(new Instruction(CilOpCodes.Ldsfld, concreteClassPointerField)); + instructions.Add(new Instruction(method.GenericParameters.Count == 0 ? CilOpCodes.Ldc_I4_0 : CilOpCodes.Ldc_I4_1)); + instructions.Add(new Instruction(CilOpCodes.Ldstr, method.DefaultName)); + instructions.Add(new Instruction(CilOpCodes.Ldstr, method.DefaultReturnType.DefaultFullName)); + instructions.Add(new Instruction(CilOpCodes.Ldc_I4, method.Parameters.Count)); + instructions.Add(new Instruction(CilOpCodes.Newarr, method.AppContext.SystemTypes.SystemStringType)); + + for (var parameterIndex = 0; i < method.Parameters.Count; i++) + { + instructions.Add(new Instruction(CilOpCodes.Dup)); + instructions.Add(new Instruction(CilOpCodes.Ldc_I4, parameterIndex)); + instructions.Add(new Instruction(CilOpCodes.Ldstr, method.Parameters[i].DefaultParameterType.DefaultFullName)); + instructions.Add(new Instruction(CilOpCodes.Stelem_Ref)); + } + + instructions.Add(new Instruction(CilOpCodes.Call, getIl2CppMethod)); + } + else + { + instructions.Add(new Instruction(CilOpCodes.Ldsfld, concreteClassPointerField)); + instructions.Add(new Instruction(CilOpCodes.Ldc_I4, unchecked((int)method.Token))); + instructions.Add(new Instruction(CilOpCodes.Call, getIl2CppMethodByToken)); + } + instructions.Add(new Instruction(CilOpCodes.Stsfld, concreteMethodInfoStore)); + + if (method.GenericParameters.Count > 0) + { + var methodInfoPtrGenericClass = initializationType.InjectNestedType( + $"MethodInfoPtrGeneric_{index}", + appContext.SystemTypes.SystemObjectType, + TypeAttributes.NestedAssembly | TypeAttributes.Abstract | TypeAttributes.Sealed); + methodInfoPtrGenericClass.IsInjected = true; + methodInfoPtrGenericClass.CopyGenericParameters(initializationType, false, true); + methodInfoPtrGenericClass.CopyGenericParameters(method, false, true); + methodInfoPtrGenericClass.GenericParameters.CopyConstraintsFrom([.. initializationType.GenericParameters, .. method.GenericParameters]); + + var methodInfoPtrGenericField = methodInfoPtrGenericClass.InjectFieldContext( + "Pointer", + appContext.SystemTypes.SystemIntPtrType, + FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.InitOnly); + method.MethodInfoField = methodInfoPtrGenericField; // A generic method's real MethodInfoField is the generically instantiated one. + + var methodInfoPtrGenericStaticConstructor = new InjectedMethodAnalysisContext( + methodInfoPtrGenericClass, + ".cctor", + appContext.SystemTypes.SystemVoidType, + MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, + []); + methodInfoPtrGenericClass.Methods.Add(methodInfoPtrGenericStaticConstructor); + + FieldAnalysisContext concreteMethodInfoStore2 = initializationType.GenericParameters.Count > 0 + ? new ConcreteGenericFieldAnalysisContext(methodInfoStore, initializationType.MakeGenericInstanceType(methodInfoPtrGenericClass.GenericParameters.Take(initializationType.GenericParameters.Count))) + : methodInfoStore; + + var instructions2 = new List(); + instructions2.Add(new Instruction(CilOpCodes.Ldsfld, concreteMethodInfoStore2)); + instructions2.Add(new Instruction(CilOpCodes.Ldsfld, concreteClassPointerField)); + instructions2.Add(new Instruction(CilOpCodes.Ldc_I4, method.GenericParameters.Count)); + instructions2.Add(new Instruction(CilOpCodes.Newarr, appContext.SystemTypes.SystemIntPtrType)); + for (var j = 0; j < method.GenericParameters.Count; j++) + { + instructions2.Add(new Instruction(CilOpCodes.Dup)); + instructions2.Add(new Instruction(CilOpCodes.Ldc_I4, j)); + var genericParameter = methodInfoPtrGenericClass.GenericParameters[j + initializationType.GenericParameters.Count]; + var classPointerForGenericParameter = new ConcreteGenericFieldAnalysisContext(classPointerField, il2CppClassPointerStore.MakeGenericInstanceType([genericParameter])); + instructions2.Add(new Instruction(CilOpCodes.Ldsfld, classPointerForGenericParameter)); + instructions2.Add(new Instruction(CilOpCodes.Stelem_I)); + } + instructions2.Add(new Instruction(CilOpCodes.Call, getIl2CppGenericInstanceMethod)); + instructions2.Add(new Instruction(CilOpCodes.Stsfld, new ConcreteGenericFieldAnalysisContext(methodInfoPtrGenericField, methodInfoPtrGenericClass.MakeGenericInstanceType(methodInfoPtrGenericClass.GenericParameters)))); + instructions2.Add(new Instruction(CilOpCodes.Ret)); + + methodInfoPtrGenericStaticConstructor.PutExtraData(new NativeMethodBody() + { + Instructions = instructions2, + }); + } + } + + // Internal call methods + for (var index = 0; index < type.Methods.Count; index++) + { + var method = type.Methods[index]; + + if (!method.IsUnstripped || !method.DefaultImplAttributes.HasFlag(MethodImplAttributes.InternalCall)) + continue; + + Debug.Assert(!method.HasExtraData()); + Debug.Assert(!method.HasExtraData()); + Debug.Assert(!method.HasExtraData()); + Debug.Assert(method.GenericParameters.Count == 0 && type.GenericParameters.Count == 0, "Internal calls cannot be generic."); + + // ICall_Delegate_Type_0 + TypeAnalysisContext delegateType; + { + delegateType = initializationType.InjectNestedType( + $"ICall_Delegate_Type_{index}", + multicastDelegateType); + + var returnType = method.ReturnType; + IEnumerable parameterTypes; + IEnumerable parameterNames; + // Wrap parameters in ByReference<> to align with NativeMethodBodyProcessingLayer + if (method.IsStatic) + { + parameterTypes = method.Parameters.Select(p => byReference.MakeGenericInstanceType([p.ParameterType])); + parameterNames = Enumerable.Range(0, method.Parameters.Count).Select(i => $"param_{i}"); + } + else + { + var thisParameterType = type.IsValueType ? byReference.MakeGenericInstanceType([type]) : type; + parameterTypes = method.Parameters.Select(p => byReference.MakeGenericInstanceType([p.ParameterType])).Prepend(thisParameterType); + parameterNames = Enumerable.Range(0, method.Parameters.Count).Select(i => $"param_{i}").Prepend("this"); + } + + // Constructor + { + delegateType.Methods.Add(new InjectedMethodAnalysisContext( + delegateType, + ".ctor", + appContext.SystemTypes.SystemVoidType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, + [appContext.SystemTypes.SystemObjectType, appContext.SystemTypes.SystemIntPtrType], + defaultImplAttributes: MethodImplAttributes.Runtime)); + } + + // Invoke + { + delegateType.Methods.Add(new InjectedMethodAnalysisContext( + delegateType, + "Invoke", + returnType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + parameterTypes.ToArray(), + parameterNames.ToArray(), + defaultImplAttributes: MethodImplAttributes.Runtime)); + } + + // BeginInvoke + { + delegateType.Methods.Add(new InjectedMethodAnalysisContext( + delegateType, + "BeginInvoke", + iasyncResultType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + parameterTypes.Append(asyncCallbackType).Append(appContext.SystemTypes.SystemObjectType).ToArray(), + parameterNames.Append("callback").Append("object").ToArray(), + defaultImplAttributes: MethodImplAttributes.Runtime)); + } + + // EndInvoke + { + delegateType.Methods.Add(new InjectedMethodAnalysisContext( + delegateType, + "EndInvoke", + returnType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + [iasyncResultType], + ["result"], + defaultImplAttributes: MethodImplAttributes.Runtime)); + } + } + + // ICall_Delegate_Field_0 + FieldAnalysisContext delegateField; + { + delegateField = initializationType.InjectFieldContext( + $"ICall_Delegate_Field_{index}", + delegateType, + FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.InitOnly); + + method.ICallDelegateField = delegateField; + } + + // Static constructor instructions + { + instructions.Add(new Instruction(CilOpCodes.Ldstr, $"{type.DefaultFullName}::{method.DefaultName}")); + instructions.Add(new Instruction(CilOpCodes.Call, new ConcreteGenericMethodAnalysisContext(resolveICall, [], [delegateType]))); + instructions.Add(new Instruction(CilOpCodes.Stsfld, delegateField)); + } + } + + // Il2CppObjectPool.RegisterInitializer + { + if (type.IsAbstract || type.IsInterface) + { + // Cannot register initializers for abstract types or interfaces + } + else if (type.IsValueType) + { + instructions.Add(CilOpCodes.Ldsfld, concreteClassPointerField); + instructions.Add(CilOpCodes.Ldnull); + instructions.Add(CilOpCodes.Ldftn, il2CppObjectPool_ValueTypeInitializer.MakeGenericInstanceMethod(typeToInitialize)); + instructions.Add(CilOpCodes.Newobj, funcConstructorInstantiated); + instructions.Add(CilOpCodes.Call, il2CppObjectPool_RegisterInitializer); + } + else + { + var creationMethod = new InjectedMethodAnalysisContext( + initializationType, + "Create", + funcTypeInstantiated.GenericArguments[1], + MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig, + [funcTypeInstantiated.GenericArguments[0]]); + initializationType.Methods.Add(creationMethod); + + var pointerConstructor = type.PointerConstructor; + Debug.Assert(pointerConstructor is not null); + + creationMethod.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Newobj, pointerConstructor.MaybeMakeConcreteGeneric(initializationType.GenericParameters, [])), + new Instruction(CilOpCodes.Ret), + ] + }); + + instructions.Add(CilOpCodes.Ldsfld, concreteClassPointerField); + instructions.Add(CilOpCodes.Ldnull); + instructions.Add(CilOpCodes.Ldftn, creationMethod.MaybeMakeConcreteGeneric(initializationType.GenericParameters, [])); + instructions.Add(CilOpCodes.Newobj, funcConstructorInstantiated); + instructions.Add(CilOpCodes.Call, il2CppObjectPool_RegisterInitializer); + } + } + + instructions.Add(new Instruction(CilOpCodes.Ret)); + + staticConstructor.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + LocalVariables = localVariables.Count > 0 ? localVariables : [], + }); + } + } + } + + Logger.Info($"Tokenless method count: {tokenLessMethodCount}", nameof(InitializationClassProcessingLayer)); + + // Il2CppInterop.Initialization.dll + { + var injectedAssembly = appContext.InjectAssembly("Il2CppInterop.Initialization"); + injectedAssembly.IsInjected = true; + + var initializationType = injectedAssembly.InjectType( + "Il2CppInterop.Initialization", + "Il2CppInitialization", + appContext.SystemTypes.SystemObjectType, + TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.Class); + + var initializeMethod = initializationType.InjectMethodContext( + "Initialize", + appContext.SystemTypes.SystemVoidType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + []); + + var instructions = new List(); + + var typeConverter = TypeConversionVisitor.Create(appContext); + + var processedTypes = new HashSet(TypeAnalysisContextEqualityComparer.Instance); + // Ensure all generic instantiations are initialized + foreach (var il2CppType in appContext.Binary.AllTypes) + { + var typeContext = injectedAssembly.ResolveIl2CppType(il2CppType); + + if (typeContext is not ReferencedTypeAnalysisContext && typeContext.GenericParameters.Count > 0) + continue; // Skip open generics + + if (InvalidTypeChecker.ContainsInvalidType(typeContext)) + continue; + + typeContext = typeConverter.Replace(typeContext); + + // Must happen after type conversion + if (InvalidConstraintChecker.ContainsInvalidConstraint(typeContext)) + continue; + + if (!processedTypes.Add(typeContext)) + continue; + + // Ensure the type is initialized + instructions.Add(new Instruction(CilOpCodes.Ldtoken, typeContext)); + instructions.Add(new Instruction(CilOpCodes.Call, runClassConstructor)); + } + + instructions.Add(new Instruction(CilOpCodes.Ret)); + + initializeMethod.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + }); + } + } + + private static void AddInstructionsToStaticConstructor(TypeAnalysisContext type, InjectedTypeAnalysisContext initializationType, MethodAnalysisContext runClassConstructor) + { + var instructions = type.GetOrCreateStaticConstructorInstructions(); + + var typeToInitialize = type.GenericParameters.Count == 0 ? initializationType : (TypeAnalysisContext)initializationType.MakeGenericInstanceType(type.GenericParameters); + + instructions.Add(new Instruction(CilOpCodes.Ldtoken, typeToInitialize)); + instructions.Add(new Instruction(CilOpCodes.Call, runClassConstructor)); + } + + private static ulong HashString(ReadOnlySpan chars) + { + var bytes = MemoryMarshal.AsBytes(chars); + Span hash = stackalloc byte[MD5.HashSizeInBytes]; + MD5.HashData(bytes, hash); + return BinaryPrimitives.ReadUInt64LittleEndian(hash); + } + + private sealed class InvalidTypeChecker : BooleanOrTypeVisitor + { + public static InvalidTypeChecker Instance { get; } = new InvalidTypeChecker(); + + public static bool ContainsInvalidType(TypeAnalysisContext type) + { + return Instance.Visit(type); + } + + public override bool Visit(BoxedTypeAnalysisContext type) => true; + public override bool Visit(ByRefTypeAnalysisContext type) => true; + public override bool Visit(GenericParameterTypeAnalysisContext type) => true; + public override bool Visit(PinnedTypeAnalysisContext type) => true; + public override bool Visit(SentinelTypeAnalysisContext type) => true; + } + + /// + /// Unity uses System.Object as a type argument when it deduplicates generic instantiations. + /// However, that might violate the constraints, so we need to detect those cases and skip generating initialization code for them. + /// + /// + /// This only checks for IObject because Object should have already been replaced.
+ /// + /// The fact that we need to check for these invalid constraints could indicate a major issue in the generated code. Either:
+ /// * The other instantiations don't exist in the Il2Cpp runtime (calling GetType() returns a type with object as the type argument). + /// This would mean the types are unusable unless we remove the constraints.
+ /// * The other instantiations do exist in the Il2Cpp runtime, and we need to use Il2Cpp reflection to construct the .NET Core type. + /// That is how System.__Canon works in .NET Core. + ///
+ private sealed class InvalidConstraintChecker : BooleanOrTypeVisitor + { + public static InvalidConstraintChecker Instance { get; } = new InvalidConstraintChecker(); + public static bool ContainsInvalidConstraint(TypeAnalysisContext type) + { + return Instance.Visit(type); + } + + public override bool Visit(GenericInstanceTypeAnalysisContext type) + { + for (var i = 0; i < type.GenericArguments.Count; i++) + { + if (!IsObject(type.GenericArguments[i])) + continue; + + foreach (var constraint in type.GenericType.GenericParameters[i].ConstraintTypes) + { + if (IsObject(constraint)) + continue; + if (!IsInjectedOrReference(constraint)) + return true; + } + } + return base.Visit(type); + } + + private static bool IsInjectedOrReference(TypeAnalysisContext type) + { + type = (type as GenericInstanceTypeAnalysisContext)?.GenericType ?? type; + + return type.IsInjected || type.DeclaringAssembly.IsReferenceAssembly; + } + + private static bool IsObject(TypeAnalysisContext type) + { + return type.KnownType is KnownTypeCode.Il2CppSystem_IObject; + } + } +} diff --git a/Il2CppInterop.Generator/InterfaceOverrideProcessingLayer.cs b/Il2CppInterop.Generator/InterfaceOverrideProcessingLayer.cs new file mode 100644 index 00000000..d8594d68 --- /dev/null +++ b/Il2CppInterop.Generator/InterfaceOverrideProcessingLayer.cs @@ -0,0 +1,168 @@ +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Core.Utils; +using Il2CppInterop.Generator.Operands; +using LibCpp2IL; + +namespace Il2CppInterop.Generator; + +public class InterfaceOverrideProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Interface Override Renaming"; + public override string Id => "interfaceoverrides"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + HashSet<(MethodAnalysisContext ImplementingMethod, MethodAnalysisContext InterfaceMethod)> set = new(MethodAnalysisContextEqualityComparer.Instance); + List<(TypeAnalysisContext Type, MethodAnalysisContext ImplementingMethod, MethodAnalysisContext InterfaceMethod)> list = new(); + foreach (var type in appContext.AllTypes) + { + if (type.IsInterface) + { + continue; + } + + var definition = type.Definition; + if (definition == null) + continue; + var vtable = definition.VTable; + + for (var index = 0; index < vtable.Length; index++) + { + var vtableEntry = vtable[index]; + var interfaceMethod = GetImplementedMethod(type, definition, index); + if (interfaceMethod == null) + continue; + + var implementingMethod = vtableEntry?.Type switch + { + MetadataUsageType.MethodDef => appContext.ResolveContextForMethod(vtableEntry.AsMethod()), + MetadataUsageType.MethodRef => appContext.ResolveContextForMethod(vtableEntry.AsGenericMethodRef()), + _ => null + }; + if (implementingMethod == null) + continue; + + if (implementingMethod.DeclaringType is null or { IsInterface: false }) + continue; + + if (implementingMethod.Name == interfaceMethod.Name) + continue; + + if (interfaceMethod is not ConcreteGenericMethodAnalysisContext) + { + var baseImplementingMethod = (implementingMethod as ConcreteGenericMethodAnalysisContext)?.BaseMethodContext ?? implementingMethod; + set.Add((baseImplementingMethod, interfaceMethod)); + } + else if (implementingMethod is not ConcreteGenericMethodAnalysisContext) + { + set.Add((implementingMethod, interfaceMethod)); + } + else + { + // Complex case - both are concrete generic methods. + list.Add((type, implementingMethod, interfaceMethod)); + } + } + } + + foreach ((var implementingMethod, var interfaceMethod) in set) + { + if (!implementingMethod.Overrides.Contains(interfaceMethod, MethodAnalysisContextEqualityComparer.Instance)) + { + implementingMethod.Overrides.Add(interfaceMethod); + } + } + + foreach (var (type, implementingMethod, interfaceMethod) in list) + { + var methodName = $"{interfaceMethod.DeclaringType?.FullName}.{interfaceMethod.Name}"; + var newMethod = new InjectedMethodAnalysisContext( + type, + methodName, + implementingMethod.ReturnType, + implementingMethod.Attributes | MethodAttributes.NewSlot, + implementingMethod.Parameters.Select(p => p.ParameterType).ToArray(), + implementingMethod.Parameters.Select(p => p.Name).ToArray(), + implementingMethod.Parameters.Select(p => p.Attributes).ToArray(), + implementingMethod.ImplAttributes) + { + IsInjected = true + }; + type.Methods.Add(newMethod); + + newMethod.Overrides.Add(interfaceMethod); + + var instructions = new List + { + { CilOpCodes.Ldarg, This.Instance } + }; + foreach (var param in newMethod.Parameters) + { + instructions.Add(CilOpCodes.Ldarg, param); + } + instructions.Add(CilOpCodes.Call, implementingMethod); + instructions.Add(CilOpCodes.Ret); + + newMethod.PutExtraData(new NativeMethodBody() { Instructions = instructions }); + } + + foreach ((_, var implementingMethod, _) in list) + { + implementingMethod.OverrideAttributes = implementingMethod.Attributes & ~(MethodAttributes.Final | MethodAttributes.Virtual); + implementingMethod.Visibility = MethodAttributes.Public; + } + } + + private static MethodAnalysisContext? GetImplementedMethod(TypeAnalysisContext type, LibCpp2IL.Metadata.Il2CppTypeDefinition definition, int index) + { + foreach (var interfaceOffset in definition.InterfaceOffsets) + { + if (index >= interfaceOffset.offset) + { + var interfaceTypeContext = interfaceOffset.Type.ToContext(type.DeclaringAssembly); + if (interfaceTypeContext != null && interfaceTypeContext.TryGetMethodInSlot(index - interfaceOffset.offset, out var method)) + { + return method; + } + } + } + return null; + } + + private sealed class MethodAnalysisContextEqualityComparer : IEqualityComparer, IEqualityComparer<(MethodAnalysisContext, MethodAnalysisContext)> + { + public static MethodAnalysisContextEqualityComparer Instance { get; } = new(); + public bool Equals(MethodAnalysisContext? x, MethodAnalysisContext? y) + { + if (ReferenceEquals(x, y)) + return true; + if (x == null || y == null) + return false; + + if (x is ConcreteGenericMethodAnalysisContext xConcrete && y is ConcreteGenericMethodAnalysisContext yConcrete) + { + return Equals(xConcrete.BaseMethodContext, yConcrete.BaseMethodContext) + && xConcrete.TypeGenericParameters.SequenceEqual(yConcrete.TypeGenericParameters, TypeAnalysisContextEqualityComparer.Instance) + && xConcrete.MethodGenericParameters.SequenceEqual(yConcrete.MethodGenericParameters, TypeAnalysisContextEqualityComparer.Instance); + } + + return false; // Non-generic methods have unique instances. + } + + public bool Equals((MethodAnalysisContext, MethodAnalysisContext) x, (MethodAnalysisContext, MethodAnalysisContext) y) + { + return Equals(x.Item1, y.Item1) && Equals(x.Item2, y.Item2); + } + + public int GetHashCode(MethodAnalysisContext obj) + { + return (obj as ConcreteGenericMethodAnalysisContext)?.BaseMethodContext.GetHashCode() ?? obj.GetHashCode(); + } + + public int GetHashCode((MethodAnalysisContext, MethodAnalysisContext) obj) + { + return HashCode.Combine(GetHashCode(obj.Item1), GetHashCode(obj.Item2)); + } + } +} diff --git a/Il2CppInterop.Generator/InvalidFieldRemovalProcessingLayer.cs b/Il2CppInterop.Generator/InvalidFieldRemovalProcessingLayer.cs new file mode 100644 index 00000000..e90573c8 --- /dev/null +++ b/Il2CppInterop.Generator/InvalidFieldRemovalProcessingLayer.cs @@ -0,0 +1,51 @@ +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator; + +public class InvalidFieldRemovalProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Invalid Field Removal"; + public override string Id => "invalid_field_removal"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + // C# allows developers to define explicit layout structs with reference type fields, but these are invalid in .NET when the field overlaps with another field. + // This processing layer handles the most common case of this - when there are multiple fields at the same offset, and at least one of them is a reference type. + // https://github.com/rlabrecque/Steamworks.NET/blob/55a9fc8eb4df4dd4cfc11b23003a5ea987317cae/CodeGen/templates/custom_types/SteamDatagramTickets/SteamDatagramRelayAuthTicket.cs#L98-L110 + + Dictionary offsetsToFieldCount = new(); + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + foreach (var type in assembly.Types) + { + if (type.IsInjected || !type.IsValueType || (type.Attributes & TypeAttributes.ExplicitLayout) == 0) + continue; + + offsetsToFieldCount.Clear(); + foreach (var field in type.Fields) + { + if (field.IsStatic) + continue; + var offset = field.Offset; + offsetsToFieldCount[offset] = offsetsToFieldCount.GetValueOrDefault(offset, 0) + 1; + } + + for (var i = type.Fields.Count - 1; i >= 0; i--) + { + var field = type.Fields[i]; + if (field.IsStatic) + continue; + + var offset = field.Offset; + if (offsetsToFieldCount[offset] > 1 && field.FieldType is { IsValueType: false } and not PointerTypeAnalysisContext) + { + type.Fields.RemoveAt(i); + } + } + } + } + } +} diff --git a/Il2CppInterop.Generator/InvisibleInterfaceProcessingLayer.cs b/Il2CppInterop.Generator/InvisibleInterfaceProcessingLayer.cs new file mode 100644 index 00000000..b07d86b0 --- /dev/null +++ b/Il2CppInterop.Generator/InvisibleInterfaceProcessingLayer.cs @@ -0,0 +1,40 @@ +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator; + +public class InvisibleInterfaceProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Invisible Interface Processor"; + public override string Id => "invisible_interface_processor"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + + foreach (var method in type.Methods) + { + foreach (var overriddenMethod in method.Overrides) + { + if (overriddenMethod.DeclaringType is not { IsInterface: true } @interface) + { + continue; + } + + if (!type.ImplementsInterface(@interface)) + { + type.InterfaceContexts.Add(@interface); + } + } + } + } + } + } +} diff --git a/Il2CppInterop.Generator/KnownTypeAssignmentProcessingLayer.cs b/Il2CppInterop.Generator/KnownTypeAssignmentProcessingLayer.cs new file mode 100644 index 00000000..622caf92 --- /dev/null +++ b/Il2CppInterop.Generator/KnownTypeAssignmentProcessingLayer.cs @@ -0,0 +1,28 @@ +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator; + +public sealed class KnownTypeAssignmentProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Known Type Assignment"; + public override string Id => "known_type_assignment"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + foreach (var knownType in Enum.GetValues()) + { + if (knownType is KnownTypeCode.None or KnownTypeCode.Il2CppSystem_IObject or KnownTypeCode.Il2CppSystem_IValueType or KnownTypeCode.Il2CppSystem_IEnum) + continue; + + var typeName = knownType.ToString().Replace('_', '.'); + if (typeName.StartsWith("Il2Cpp", StringComparison.Ordinal)) + { + appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow(typeName).KnownType = knownType; + } + else + { + appContext.Mscorlib.GetTypeByFullNameOrThrow(typeName).KnownType = knownType; + } + } + } +} diff --git a/Il2CppInterop.Generator/KnownTypeCode.cs b/Il2CppInterop.Generator/KnownTypeCode.cs new file mode 100644 index 00000000..4402ec36 --- /dev/null +++ b/Il2CppInterop.Generator/KnownTypeCode.cs @@ -0,0 +1,50 @@ +namespace Il2CppInterop.Generator; + +public enum KnownTypeCode +{ + None, + System_Object, + System_ValueType, + System_Enum, + System_Attribute, + System_String, + System_Boolean, + System_Byte, + System_SByte, + System_Int16, + System_UInt16, + System_Int32, + System_UInt32, + System_Int64, + System_UInt64, + System_Single, + System_Double, + System_Char, + System_IntPtr, + System_UIntPtr, + System_Void, + System_Array, + Il2CppSystem_Object, + Il2CppSystem_ValueType, + Il2CppSystem_Enum, + Il2CppSystem_Attribute, + Il2CppSystem_String, + Il2CppSystem_Boolean, + Il2CppSystem_Byte, + Il2CppSystem_SByte, + Il2CppSystem_Int16, + Il2CppSystem_UInt16, + Il2CppSystem_Int32, + Il2CppSystem_UInt32, + Il2CppSystem_Int64, + Il2CppSystem_UInt64, + Il2CppSystem_Single, + Il2CppSystem_Double, + Il2CppSystem_Char, + Il2CppSystem_IntPtr, + Il2CppSystem_UIntPtr, + Il2CppSystem_Void, + Il2CppSystem_IObject, + Il2CppSystem_IValueType, + Il2CppSystem_IEnum, +} diff --git a/Il2CppInterop.Generator/MarshallingProcessingLayer.cs b/Il2CppInterop.Generator/MarshallingProcessingLayer.cs new file mode 100644 index 00000000..7c6de3cb --- /dev/null +++ b/Il2CppInterop.Generator/MarshallingProcessingLayer.cs @@ -0,0 +1,596 @@ +using System.Diagnostics; +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Common; +using Il2CppInterop.Generator.Operands; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.InteropTypes; + +namespace Il2CppInterop.Generator; + +public class MarshallingProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Marshalling Processor"; + public override string Id => "marshalling_processor"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var iil2CppType = appContext.ResolveTypeOrThrow(typeof(IIl2CppType)); + var iil2CppType_get_ObjectClass = iil2CppType.GetMethodByName($"get_{nameof(IIl2CppType.ObjectClass)}"); + + var iil2CppTypeGeneric = appContext.ResolveTypeOrThrow(typeof(IIl2CppType<>)); + var iil2CppTypeGeneric_get_Size = iil2CppTypeGeneric.GetMethodByName($"get_{nameof(IIl2CppType<>.Size)}"); + var iil2CppTypeGeneric_get_AssemblyName = iil2CppTypeGeneric.GetMethodByName($"get_{nameof(IIl2CppType<>.AssemblyName)}"); + var iil2CppTypeGeneric_get_Namespace = iil2CppTypeGeneric.GetMethodByName($"get_{nameof(IIl2CppType<>.Namespace)}"); + var iil2CppTypeGeneric_get_Name = iil2CppTypeGeneric.GetMethodByName($"get_{nameof(IIl2CppType<>.Name)}"); + var iil2CppTypeGeneric_ReadFromSpan = iil2CppTypeGeneric.GetMethodByName(nameof(IIl2CppType<>.ReadFromSpan)); + var iil2CppTypeGeneric_WriteToSpan = iil2CppTypeGeneric.GetMethodByName(nameof(IIl2CppType<>.WriteToSpan)); + + var il2CppClassPointerStore = appContext.ResolveTypeOrThrow(typeof(Il2CppClassPointerStore<>)); + var il2CppClassPointerStore_NativeClassPointer = il2CppClassPointerStore.GetFieldByName(nameof(Il2CppClassPointerStore<>.NativeClassPointer)); + + var il2CppTypeHelper = appContext.ResolveTypeOrThrow(typeof(Il2CppType)); + var il2CppTypeHelper_ReadReference = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.ReadReference)); + var il2CppTypeHelper_WriteReference = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.WriteReference)); + var il2CppTypeHelper_ReadFromSpanAtOffset = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.ReadFromSpanAtOffset)); + var il2CppTypeHelper_WriteToSpanAtOffset = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.WriteToSpanAtOffset)); + var il2CppTypeHelper_ReadFromSpanBlittable = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.ReadFromSpanBlittable)); + var il2CppTypeHelper_WriteToSpanBlittable = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.WriteToSpanBlittable)); + var il2CppTypeHelper_WriteToSpan = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.WriteToSpan)); + + var intPtr_get_Size = appContext.SystemTypes.SystemIntPtrType.GetMethodByName($"get_{nameof(IntPtr.Size)}"); + + var il2CppSystemIObject = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.IObject"); + var il2CppSystemIValueType = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.IValueType"); + var il2CppSystemIEnum = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.IEnum"); + + var il2CppSystemValueType = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.ValueType"); + + // IValueType.Size + MethodAnalysisContext il2cppSystemIValueType_get_Size; + { + il2cppSystemIValueType_get_Size = new InjectedMethodAnalysisContext( + il2CppSystemIValueType, + $"get_{nameof(Il2CppSystem.IValueType.Size)}", + appContext.SystemTypes.SystemInt32Type, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Abstract | MethodAttributes.Virtual | MethodAttributes.SpecialName | MethodAttributes.NewSlot, + []) + { + IsInjected = true, + }; + il2CppSystemIValueType.Methods.Add(il2cppSystemIValueType_get_Size); + + var property = new InjectedPropertyAnalysisContext( + nameof(Il2CppSystem.IValueType.Size), + il2cppSystemIValueType_get_Size.ReturnType, + il2cppSystemIValueType_get_Size, + null, + PropertyAttributes.None, + il2CppSystemIValueType) + { + IsInjected = true, + }; + il2CppSystemIValueType.Properties.Add(property); + } + + // IValueType.WriteToSpan(Span) + MethodAnalysisContext il2cppSystemIValueType_WriteToSpan; + { + var spanOfByteType = il2CppTypeHelper_WriteToSpan.Parameters[^1].ParameterType; + il2cppSystemIValueType_WriteToSpan = new InjectedMethodAnalysisContext( + il2CppSystemIValueType, + nameof(Il2CppSystem.IValueType.WriteToSpan), + appContext.SystemTypes.SystemVoidType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Abstract | MethodAttributes.Virtual | MethodAttributes.NewSlot, + [spanOfByteType]) + { + IsInjected = true, + }; + il2CppSystemIValueType.Methods.Add(il2cppSystemIValueType_WriteToSpan); + } + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInjected && type != il2CppSystemIObject && type != il2CppSystemIValueType && type != il2CppSystemIEnum) + continue; + + type.InterfaceContexts.Add(iil2CppType); + + var instantiatedType = type.SelfInstantiateIfGeneric(); + var instantiatedIl2CppTypeGeneric = iil2CppTypeGeneric.MakeGenericInstanceType([instantiatedType]); + type.InterfaceContexts.Add(instantiatedIl2CppTypeGeneric); + + TypeAnalysisContext nameReferenceType; + TypeAnalysisContext classReferenceType; + if (type == il2CppSystemIObject) + { + nameReferenceType = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Object"); + classReferenceType = nameReferenceType; + } + else if (type == il2CppSystemIValueType) + { + nameReferenceType = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.ValueType"); + classReferenceType = nameReferenceType; + } + else if (type == il2CppSystemIEnum) + { + nameReferenceType = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Enum"); + classReferenceType = nameReferenceType; + } + else + { + nameReferenceType = type; + classReferenceType = instantiatedType; + } + + // ObjectClass + { + var methodName = $"{iil2CppType.FullName}.get_{nameof(IIl2CppType.ObjectClass)}"; + var method = new InjectedMethodAnalysisContext( + type, + methodName, + iil2CppType_get_ObjectClass.ReturnType, + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName | (type.IsInterface ? MethodAttributes.ReuseSlot : MethodAttributes.NewSlot), + []) + { + IsInjected = true, + }; + type.Methods.Add(method); + method.Overrides.Add(iil2CppType_get_ObjectClass); + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldsfld, new ConcreteGenericFieldAnalysisContext(il2CppClassPointerStore_NativeClassPointer, il2CppClassPointerStore.MakeGenericInstanceType([classReferenceType]))), + new Instruction(CilOpCodes.Ret), + ], + }); + + var propertyName = $"{iil2CppType.FullName}.{nameof(IIl2CppType.ObjectClass)}"; + var property = new InjectedPropertyAnalysisContext( + propertyName, + method.ReturnType, + method, + null, + PropertyAttributes.None, + type) + { + IsInjected = true, + }; + type.Properties.Add(property); + } + + // Size + { + var instantiatedSizeStorage = type.SizeStorage is null + ? null + : type.GenericParameters.Count > 0 + ? type.SizeStorage!.MakeConcreteGeneric(type.GenericParameters) + : type.SizeStorage; + + // IIl2CppType.Size + { + var instantiated_iil2CppTypeGeneric_get_Size = new ConcreteGenericMethodAnalysisContext(iil2CppTypeGeneric_get_Size, [instantiatedType], []); + var methodName = $"{instantiatedIl2CppTypeGeneric.FullName}.get_{nameof(IIl2CppType<>.Size)}"; + var method = new InjectedMethodAnalysisContext( + type, + methodName, + instantiated_iil2CppTypeGeneric_get_Size.ReturnType, + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName, + []) + { + IsInjected = true, + }; + type.Methods.Add(method); + method.Overrides.Add(instantiated_iil2CppTypeGeneric_get_Size); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + instantiatedSizeStorage is not null ? new Instruction(CilOpCodes.Ldsfld, instantiatedSizeStorage) : new Instruction(CilOpCodes.Call, intPtr_get_Size), + new Instruction(CilOpCodes.Ret), + ], + }); + + var propertyName = $"{instantiatedIl2CppTypeGeneric.FullName}.{nameof(IIl2CppType<>.Size)}"; + var property = new InjectedPropertyAnalysisContext( + propertyName, + method.ReturnType, + method, + null, + PropertyAttributes.None, + type) + { + IsInjected = true, + }; + type.Properties.Add(property); + } + + // IValueType.Size + if (type.IsValueType || type == il2CppSystemValueType) + { + var methodName = $"{il2CppSystemIValueType.FullName}.get_{nameof(Il2CppSystem.IValueType.Size)}"; + var method = new InjectedMethodAnalysisContext( + type, + methodName, + il2cppSystemIValueType_get_Size.ReturnType, + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName | MethodAttributes.NewSlot, + []) + { + IsInjected = true, + }; + type.Methods.Add(method); + method.Overrides.Add(il2cppSystemIValueType_get_Size); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + instantiatedSizeStorage is not null ? new Instruction(CilOpCodes.Ldsfld, instantiatedSizeStorage) : new Instruction(CilOpCodes.Call, intPtr_get_Size), + new Instruction(CilOpCodes.Ret), + ], + }); + + var propertyName = $"{il2CppSystemIValueType.FullName}.{nameof(Il2CppSystem.IValueType.Size)}"; + var property = new InjectedPropertyAnalysisContext( + propertyName, + method.ReturnType, + method, + null, + PropertyAttributes.None, + type) + { + IsInjected = true, + }; + type.Properties.Add(property); + } + } + + // AssemblyName + if (assembly.Name != assembly.DefaultName) + { + var instantiated_iil2CppTypeGeneric_get_AssemblyName = new ConcreteGenericMethodAnalysisContext(iil2CppTypeGeneric_get_AssemblyName, [instantiatedType], []); + var methodName = $"{instantiatedIl2CppTypeGeneric.FullName}.get_{nameof(IIl2CppType<>.AssemblyName)}"; + var method = new InjectedMethodAnalysisContext( + type, + methodName, + instantiated_iil2CppTypeGeneric_get_AssemblyName.ReturnType, + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName, + []) + { + IsInjected = true, + }; + type.Methods.Add(method); + method.Overrides.Add(instantiated_iil2CppTypeGeneric_get_AssemblyName); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldstr, assembly.DefaultName), + new Instruction(CilOpCodes.Ret), + ], + }); + + var propertyName = $"{instantiatedIl2CppTypeGeneric.FullName}.{nameof(IIl2CppType<>.AssemblyName)}"; + var property = new InjectedPropertyAnalysisContext( + propertyName, + method.ReturnType, + method, + null, + PropertyAttributes.None, + type) + { + IsInjected = true, + }; + type.Properties.Add(property); + } + + // Namespace + if (type.Namespace != nameReferenceType.DefaultNamespace) + { + var instantiated_iil2CppTypeGeneric_get_Namespace = new ConcreteGenericMethodAnalysisContext(iil2CppTypeGeneric_get_Namespace, [instantiatedType], []); + var methodName = $"{instantiatedIl2CppTypeGeneric.FullName}.get_{nameof(IIl2CppType<>.Namespace)}"; + var method = new InjectedMethodAnalysisContext( + type, + methodName, + instantiated_iil2CppTypeGeneric_get_Namespace.ReturnType, + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName, + []) + { + IsInjected = true, + }; + type.Methods.Add(method); + method.Overrides.Add(instantiated_iil2CppTypeGeneric_get_Namespace); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldstr, nameReferenceType.DefaultNamespace), + new Instruction(CilOpCodes.Ret), + ], + }); + + var propertyName = $"{instantiatedIl2CppTypeGeneric.FullName}.{nameof(IIl2CppType<>.Namespace)}"; + var property = new InjectedPropertyAnalysisContext( + propertyName, + method.ReturnType, + method, + null, + PropertyAttributes.None, + type) + { + IsInjected = true, + }; + type.Properties.Add(property); + } + + // Name + if (type.Name != nameReferenceType.DefaultName) + { + var instantiated_iil2CppTypeGeneric_get_Name = new ConcreteGenericMethodAnalysisContext(iil2CppTypeGeneric_get_Name, [instantiatedType], []); + var methodName = $"{instantiatedIl2CppTypeGeneric.FullName}.get_{nameof(IIl2CppType<>.Name)}"; + var method = new InjectedMethodAnalysisContext( + type, + methodName, + instantiated_iil2CppTypeGeneric_get_Name.ReturnType, + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName, + []) + { + IsInjected = true, + }; + type.Methods.Add(method); + method.Overrides.Add(instantiated_iil2CppTypeGeneric_get_Name); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldstr, nameReferenceType.DefaultName), + new Instruction(CilOpCodes.Ret), + ], + }); + + var propertyName = $"{instantiatedIl2CppTypeGeneric.FullName}.{nameof(IIl2CppType<>.Name)}"; + var property = new InjectedPropertyAnalysisContext( + propertyName, + method.ReturnType, + method, + null, + PropertyAttributes.None, + type) + { + IsInjected = true, + }; + type.Properties.Add(property); + } + + var instanceFieldCount = type.Fields.Count(f => !f.IsStatic); + + // ReadFromSpan + { + var instantiated_iil2CppTypeGeneric_ReadFromSpan = new ConcreteGenericMethodAnalysisContext(iil2CppTypeGeneric_ReadFromSpan, [instantiatedType], []); + var methodName = $"{instantiatedIl2CppTypeGeneric.FullName}.{nameof(IIl2CppType<>.ReadFromSpan)}"; + var method = new InjectedMethodAnalysisContext( + type, + methodName, + instantiated_iil2CppTypeGeneric_ReadFromSpan.ReturnType, + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, + [instantiated_iil2CppTypeGeneric_ReadFromSpan.Parameters[0].ParameterType]) + { + IsInjected = true, + }; + type.Methods.Add(method); + method.Overrides.Add(instantiated_iil2CppTypeGeneric_ReadFromSpan); + + if (!type.IsValueType) + { + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Call, il2CppTypeHelper_ReadReference.MakeGenericInstanceMethod(instantiatedType)), + new Instruction(CilOpCodes.Ret), + ], + }); + } + else if (instanceFieldCount == 0) + { + LocalVariable local = new(instantiatedType); + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldloca, local), + new Instruction(CilOpCodes.Initobj, instantiatedType), + new Instruction(CilOpCodes.Ldloc, local), + new Instruction(CilOpCodes.Ret), + ], + LocalVariables = [local], + }); + } + else if (type.IsIl2CppPrimitive) + { + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Call, il2CppTypeHelper_ReadFromSpanBlittable.MakeGenericInstanceMethod(instantiatedType)), + new Instruction(CilOpCodes.Ret), + ], + }); + } + else + { + LocalVariable local = new(instantiatedType); + List instructions = new(2 + instanceFieldCount * 5 + 2) + { + new Instruction(CilOpCodes.Ldloca, local), + new Instruction(CilOpCodes.Initobj, instantiatedType) + }; + + foreach (var field in type.Fields) + { + if (field.IsStatic) + continue; + + Debug.Assert(!field.IsInjected); + Debug.Assert(field.OffsetStorage is not null); + + var instantiatedField = field.SelfInstantiateIfNecessary(); + + instructions.Add(new Instruction(CilOpCodes.Ldloca, local)); + instructions.Add(new Instruction(CilOpCodes.Ldarg_0)); + instructions.Add(new Instruction(CilOpCodes.Ldsfld, field.OffsetStorage!.MaybeMakeConcreteGeneric(type.GenericParameters))); + instructions.Add(new Instruction(CilOpCodes.Call, il2CppTypeHelper_ReadFromSpanAtOffset.MakeGenericInstanceMethod(instantiatedField.FieldType))); + instructions.Add(new Instruction(CilOpCodes.Stfld, instantiatedField)); + } + + instructions.Add(new Instruction(CilOpCodes.Ldloc, local)); + instructions.Add(new Instruction(CilOpCodes.Ret)); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + LocalVariables = [local], + }); + } + } + + // WriteToSpan + { + var instantiated_iil2CppTypeGeneric_WriteToSpan = new ConcreteGenericMethodAnalysisContext(iil2CppTypeGeneric_WriteToSpan, [instantiatedType], []); + var methodName = $"{instantiatedIl2CppTypeGeneric.FullName}.{nameof(IIl2CppType<>.WriteToSpan)}"; + var method = new InjectedMethodAnalysisContext( + type, + methodName, + instantiated_iil2CppTypeGeneric_WriteToSpan.ReturnType, + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, + instantiated_iil2CppTypeGeneric_WriteToSpan.Parameters.Select(p => p.ParameterType).ToArray()) + { + IsInjected = true, + }; + type.Methods.Add(method); + method.Overrides.Add(instantiated_iil2CppTypeGeneric_WriteToSpan); + + if (!type.IsValueType) + { + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Ldarg_1), + new Instruction(CilOpCodes.Call, il2CppTypeHelper_WriteReference.MakeGenericInstanceMethod(instantiatedType)), + new Instruction(CilOpCodes.Ret), + ], + }); + } + else if (instanceFieldCount == 0) + { + // Struct with no instance fields - nothing to do. + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ret), + ], + }); + } + else if (type.IsIl2CppPrimitive) + { + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Ldarg_1), + new Instruction(CilOpCodes.Call, il2CppTypeHelper_WriteToSpanBlittable.MakeGenericInstanceMethod(instantiatedType)), + new Instruction(CilOpCodes.Ret), + ], + }); + } + else + { + List instructions = new(instanceFieldCount * 5 + 1); + foreach (var field in type.Fields) + { + if (field.IsStatic) + continue; + + Debug.Assert(!field.IsInjected); + Debug.Assert(field.OffsetStorage is not null); + + var instantiatedField = field.SelfInstantiateIfNecessary(); + + instructions.Add(new Instruction(CilOpCodes.Ldarga, method.Parameters[0])); + instructions.Add(new Instruction(CilOpCodes.Ldfld, instantiatedField)); + instructions.Add(new Instruction(CilOpCodes.Ldarg_1)); + instructions.Add(new Instruction(CilOpCodes.Ldsfld, field.OffsetStorage!.MaybeMakeConcreteGeneric(type.GenericParameters))); + instructions.Add(new Instruction(CilOpCodes.Call, il2CppTypeHelper_WriteToSpanAtOffset.MakeGenericInstanceMethod(instantiatedField.FieldType))); + } + instructions.Add(new Instruction(CilOpCodes.Ret)); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + }); + } + } + // IValueType.WriteToSpan + if (type.IsValueType || type == il2CppSystemValueType) + { + var methodName = $"{il2CppSystemIValueType.FullName}.{nameof(Il2CppSystem.IValueType.WriteToSpan)}"; + var method = new InjectedMethodAnalysisContext( + type, + methodName, + il2cppSystemIValueType_WriteToSpan.ReturnType, + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot, + il2cppSystemIValueType_WriteToSpan.Parameters.Select(p => p.ParameterType).ToArray()) + { + IsInjected = true, + }; + type.Methods.Add(method); + method.Overrides.Add(il2cppSystemIValueType_WriteToSpan); + + if (type.IsValueType) + { + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Ldobj, instantiatedType), + new Instruction(CilOpCodes.Ldarg_1), + new Instruction(CilOpCodes.Call, il2CppTypeHelper_WriteToSpan.MakeGenericInstanceMethod(instantiatedType)), + new Instruction(CilOpCodes.Ret), + ], + }); + } + else + { + method.PutExtraData(new NativeMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Ldarg_1), + new Instruction(CilOpCodes.Call, il2CppTypeHelper_WriteReference.MakeGenericInstanceMethod(instantiatedType)), + new Instruction(CilOpCodes.Ret), + ], + }); + } + } + } + } + } +} diff --git a/Il2CppInterop.Generator/MemberAttributeProcessingLayer.cs b/Il2CppInterop.Generator/MemberAttributeProcessingLayer.cs new file mode 100644 index 00000000..28523625 --- /dev/null +++ b/Il2CppInterop.Generator/MemberAttributeProcessingLayer.cs @@ -0,0 +1,78 @@ +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Core.Model.CustomAttributes; +using Il2CppInterop.Common.Attributes; + +namespace Il2CppInterop.Generator; + +public class MemberAttributeProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Member Attribute Processor"; + + public override string Id => "member_attribute_processor"; + + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + // This layer is responsible for adding Il2CppMethodAttribute and Il2CppPropertyAttribute to methods and properties. + // Fields are handled in FieldAccessorProcessingLayer + // Events are handled in EventProcessingLayer + + var il2CppMethodAttribute = appContext.ResolveTypeOrThrow(typeof(Il2CppMethodAttribute)); + var il2CppMethodAttributeConstructor = il2CppMethodAttribute.GetMethodByName(".ctor"); + var il2CppMethodAttributeIndex = il2CppMethodAttribute.GetPropertyByName(nameof(Il2CppMethodAttribute.Index)); + + var il2CppPropertyAttribute = appContext.ResolveTypeOrThrow(typeof(Il2CppPropertyAttribute)); + var il2CppPropertyAttributeConstructor = il2CppPropertyAttribute.GetMethodByName(".ctor"); + + var il2CppMemberAttribute = appContext.ResolveTypeOrThrow(typeof(Il2CppMemberAttribute)); + var il2CppMemberAttributeName = il2CppMemberAttribute.GetPropertyByName(nameof(Il2CppMemberAttribute.Name)); + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + + foreach (var method in type.Methods) + { + if (method.IsInjected) + continue; + + var attribute = new AnalyzedCustomAttribute(il2CppMethodAttributeConstructor); + if (method.Name != method.DefaultName) + { + var parameter = new CustomAttributePrimitiveParameter(method.DefaultName, attribute, CustomAttributeParameterKind.Property, attribute.Properties.Count); + attribute.Properties.Add(new CustomAttributeProperty(il2CppMemberAttributeName, parameter)); + } + var index = method.InitializationClassIndex; + if (index >= 0) + { + var parameter = new CustomAttributePrimitiveParameter(index, attribute, CustomAttributeParameterKind.Property, attribute.Properties.Count); + attribute.Properties.Add(new CustomAttributeProperty(il2CppMethodAttributeIndex, parameter)); + } + method.CustomAttributes ??= new(1); + method.CustomAttributes.Add(attribute); + } + + foreach (var property in type.Properties) + { + if (property.IsInjected) + continue; + + var attribute = new AnalyzedCustomAttribute(il2CppPropertyAttributeConstructor); + if (property.Name != property.DefaultName) + { + var parameter = new CustomAttributePrimitiveParameter(property.DefaultName, attribute, CustomAttributeParameterKind.Property, 0); + attribute.Properties.Add(new CustomAttributeProperty(il2CppMemberAttributeName, parameter)); + } + property.CustomAttributes ??= new(1); + property.CustomAttributes.Add(attribute); + } + } + } + } +} diff --git a/Il2CppInterop.Generator/MetadataAccess/AssemblyMetadataAccess.cs b/Il2CppInterop.Generator/MetadataAccess/AssemblyMetadataAccess.cs deleted file mode 100644 index e0cbd108..00000000 --- a/Il2CppInterop.Generator/MetadataAccess/AssemblyMetadataAccess.cs +++ /dev/null @@ -1,77 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; - -namespace Il2CppInterop.Generator.MetadataAccess; - -public class AssemblyMetadataAccess : IIl2CppMetadataAccess -{ - private readonly Il2CppAssemblyResolver myAssemblyResolver = new(); - private readonly List myAssemblies = new(); - private readonly Dictionary myAssembliesByName = new(); - private readonly Dictionary<(string AssemblyName, string TypeName), TypeDefinition> myTypesByName = new(); - - public AssemblyMetadataAccess(IEnumerable assemblyPaths) - { - Load(assemblyPaths.Select(AssemblyDefinition.FromFile)); - } - - public AssemblyMetadataAccess(IEnumerable assemblies) - { - // Note: At the moment this assumes that passed assemblies have their own assembly resolver set up - // If this is not true, this can cause issues with reference resolving - Load(assemblies); - } - - public void Dispose() - { - myAssemblyResolver.ClearCache(); - myAssemblies.Clear(); - myAssembliesByName.Clear(); - } - - public AssemblyDefinition? GetAssemblyBySimpleName(string name) - { - return myAssembliesByName.TryGetValue(name, out var result) ? result : null; - } - - public TypeDefinition? GetTypeByName(string assemblyName, string typeName) - { - return myTypesByName.TryGetValue((assemblyName, typeName), out var result) ? result : null; - } - - public IList Assemblies => myAssemblies; - - public IList? GetKnownInstantiationsFor(TypeDefinition genericDeclaration) - { - return null; - } - - public string? GetStringStoredAtAddress(long offsetInMemory) - { - return null; - } - - public MemberReference? GetMethodRefStoredAt(long offsetInMemory) - { - return null; - } - - private void Load(IEnumerable assemblies) - { - foreach (var sourceAssembly in assemblies) - { - myAssemblies.Add(sourceAssembly); - myAssembliesByName[sourceAssembly.Name!] = sourceAssembly; - sourceAssembly.ManifestModule!.MetadataResolver = new DefaultMetadataResolver(myAssemblyResolver); - myAssemblyResolver.AddToCache(sourceAssembly); - } - - foreach (var sourceAssembly in myAssemblies) - { - var sourceAssemblyName = sourceAssembly.Name!; - foreach (var type in sourceAssembly.ManifestModule!.TopLevelTypes) - // todo: nested types? - myTypesByName[(sourceAssemblyName, type.FullName)] = type; - } - } -} diff --git a/Il2CppInterop.Generator/MetadataAccess/IIl2CppMetadataAccess.cs b/Il2CppInterop.Generator/MetadataAccess/IIl2CppMetadataAccess.cs deleted file mode 100644 index f4f09d26..00000000 --- a/Il2CppInterop.Generator/MetadataAccess/IIl2CppMetadataAccess.cs +++ /dev/null @@ -1,11 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; - -namespace Il2CppInterop.Generator.MetadataAccess; - -public interface IIl2CppMetadataAccess : IMetadataAccess -{ - IList? GetKnownInstantiationsFor(TypeDefinition genericDeclaration); - string? GetStringStoredAtAddress(long offsetInMemory); - MemberReference? GetMethodRefStoredAt(long offsetInMemory); -} diff --git a/Il2CppInterop.Generator/MetadataAccess/IMetadataAccess.cs b/Il2CppInterop.Generator/MetadataAccess/IMetadataAccess.cs deleted file mode 100644 index 63f0b585..00000000 --- a/Il2CppInterop.Generator/MetadataAccess/IMetadataAccess.cs +++ /dev/null @@ -1,11 +0,0 @@ -using AsmResolver.DotNet; - -namespace Il2CppInterop.Generator.MetadataAccess; - -public interface IMetadataAccess : IDisposable -{ - IList Assemblies { get; } - - AssemblyDefinition? GetAssemblyBySimpleName(string name); - TypeDefinition? GetTypeByName(string assemblyName, string typeName); -} diff --git a/Il2CppInterop.Generator/MetadataAccess/Il2CppAssemblyResolver.cs b/Il2CppInterop.Generator/MetadataAccess/Il2CppAssemblyResolver.cs deleted file mode 100644 index 8874998a..00000000 --- a/Il2CppInterop.Generator/MetadataAccess/Il2CppAssemblyResolver.cs +++ /dev/null @@ -1,18 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.IO; - -namespace Il2CppInterop.Generator.MetadataAccess; - -internal sealed class Il2CppAssemblyResolver : AssemblyResolverBase -{ - public Il2CppAssemblyResolver() : base(new ByteArrayFileService()) - { - } - - protected override string? ProbeRuntimeDirectories(AssemblyDescriptor assembly) => null; - - public void AddToCache(AssemblyDefinition assembly) - { - AddToCache(assembly, assembly); - } -} diff --git a/Il2CppInterop.Generator/MetadataAccess/NullMetadataAccess.cs b/Il2CppInterop.Generator/MetadataAccess/NullMetadataAccess.cs deleted file mode 100644 index dc1e4682..00000000 --- a/Il2CppInterop.Generator/MetadataAccess/NullMetadataAccess.cs +++ /dev/null @@ -1,40 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; - -namespace Il2CppInterop.Generator.MetadataAccess; - -public class NullMetadataAccess : IMetadataAccess -{ - public static readonly NullMetadataAccess Instance = new(); - - public void Dispose() - { - } - - public IList Assemblies => Array.Empty(); - - public AssemblyDefinition? GetAssemblyBySimpleName(string name) - { - return null; - } - - public TypeDefinition? GetTypeByName(string assemblyName, string typeName) - { - return null; - } - - public IList? GetKnownInstantiationsFor(TypeReference genericDeclaration) - { - return null; - } - - public string? GetStringStoredAtAddress(long offsetInMemory) - { - return null; - } - - public MemberReference? GetMethodRefStoredAt(long offsetInMemory) - { - return null; - } -} diff --git a/Il2CppInterop.Generator/MethodBodyBase.cs b/Il2CppInterop.Generator/MethodBodyBase.cs new file mode 100644 index 00000000..3810e437 --- /dev/null +++ b/Il2CppInterop.Generator/MethodBodyBase.cs @@ -0,0 +1,492 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using AsmResolver.DotNet; +using AsmResolver.DotNet.Code.Cil; +using AssetRipper.CIL; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Core.Utils.AsmResolver; +using Il2CppInterop.Generator.Operands; +using Il2CppInterop.Generator.StackTypes; +using Il2CppInterop.Generator.Visitors; +using Il2CppInterop.Runtime.InteropTypes; +using Il2CppInterop.Runtime.InteropTypes.Arrays; + +namespace Il2CppInterop.Generator; + +public abstract class MethodBodyBase +{ + public required IReadOnlyList Instructions { get; init; } + public IReadOnlyList LocalVariables { get; init; } = []; + public IReadOnlyList ExceptionHandlers { get; init; } = []; + + public void FillMethodBody(MethodDefinition method) + { + if (!method.IsManagedMethodWithBody()) + { + return; + } + + var body = new CilMethodBody() + { + InitializeLocals = LocalVariables.Count > 0 + }; + method.CilMethodBody = body; + var module = method.DeclaringModule!; + var instructions = body.Instructions; + + var labels = new Dictionary(Instructions.Count + 1); + for (var i = Instructions.Count - 1; i >= 0; i--) + { + labels.Add(Instructions[i], new CilInstructionLabel()); + } + labels.Add(EndLabel.Instance, instructions.EndLabel); + + foreach (var exceptionHandler in ExceptionHandlers) + { + var handler = new CilExceptionHandler + { + HandlerType = exceptionHandler.HandlerType, + TryStart = labels.GetValue(exceptionHandler.TryStart), + TryEnd = labels.GetValue(exceptionHandler.TryEnd), + HandlerStart = labels.GetValue(exceptionHandler.HandlerStart), + HandlerEnd = labels.GetValue(exceptionHandler.HandlerEnd) ?? instructions.EndLabel, + FilterStart = labels.GetValue(exceptionHandler.FilterStart), + ExceptionType = exceptionHandler.ExceptionType?.ToTypeSignature(module).ToTypeDefOrRef(), + }; + body.ExceptionHandlers.Add(handler); + } + + Dictionary localVariableMap = new(LocalVariables.Count); + for (var i = LocalVariables.Count - 1; i >= 0; i--) + { + var localVariable = LocalVariables[i]; + var localVariableType = localVariable.Type.ToTypeSignature(module); + var value = new CilLocalVariable(localVariableType); + body.LocalVariables.Add(value); + localVariableMap.Add(localVariable, value); + } + + for (var i = 0; i < Instructions.Count; i++) + { + var instruction = Instructions[i]; + var operand = instruction.Operand switch + { + This => method.Parameters.ThisParameter ?? throw new NullReferenceException("This parameter should not be null."), + LocalVariable localVariable => localVariableMap[localVariable], + TypeAnalysisContext type => type.ToTypeSignature(module).ToTypeDefOrRef(), + MethodAnalysisContext methodOperand => methodOperand.ToMethodDescriptor(module), + FieldAnalysisContext field => field.ToFieldDescriptor(module), + ParameterAnalysisContext parameter => method.Parameters[parameter.ParameterIndex], + MultiDimensionalArrayMethod arrayMethod => arrayMethod.ToMethodDescriptor(module), + ILabel label => labels[label], + IReadOnlyList labelArray => labelArray.Select(labels.GetValue).ToArray(), + _ => instruction.Operand, + }; + var cilInstruction = new CilInstruction(instruction.Code, operand); + instructions.Add(cilInstruction); + ((CilInstructionLabel)labels[instruction]).Instruction = cilInstruction; + } + + instructions.OptimizeMacros(); + } + + internal void AnalyzeControlFlow(out Dictionary> instructionPredecessors, out Dictionary> instructionSuccessors) + { + instructionPredecessors = new Dictionary>(Instructions.Count); + instructionSuccessors = new Dictionary>(Instructions.Count); + foreach (var instruction in Instructions) + { + instructionPredecessors[instruction] = []; + instructionSuccessors[instruction] = []; + } + for (var i = 0; i < Instructions.Count; i++) + { + var instruction = Instructions[i]; + if (instruction.Code.FlowControl is CilFlowControl.Return or CilFlowControl.Throw) + { + // No successors. + } + else if (instruction.Operand is ILabel label) + { + Debug.Assert(instruction.Code.FlowControl is CilFlowControl.Branch or CilFlowControl.ConditionalBranch); + Instruction? targetInstruction = label as Instruction; + if (targetInstruction is not null) + { + instructionSuccessors[instruction].Add(targetInstruction); + instructionPredecessors[targetInstruction].Add(instruction); + } + + if (instruction.Code.Code is CilCode.Br or CilCode.Leave) + { + // Unconditional branches do not have fallthrough. + } + else if (i + 1 >= Instructions.Count) + { + // No fallthrough, this is the last instruction. + } + else if (Instructions[i + 1] == targetInstruction) + { + // Do nothing, already added above. + } + else + { + var nextInstruction = Instructions[i + 1]; + instructionSuccessors[instruction].Add(nextInstruction); + instructionPredecessors[nextInstruction].Add(instruction); + } + } + else if (instruction.Operand is IReadOnlyList labels) + { + Debug.Assert(instruction.Code.FlowControl is CilFlowControl.ConditionalBranch); + HashSet targetInstructions = new(); + foreach (var label1 in labels) + { + if (label1 is Instruction targetInstruction && targetInstructions.Add(targetInstruction)) + { + instructionSuccessors[instruction].Add(targetInstruction); + instructionPredecessors[targetInstruction].Add(instruction); + } + } + if (i + 1 < Instructions.Count) + { + var nextInstruction = Instructions[i + 1]; + if (!targetInstructions.Contains(nextInstruction)) + { + instructionSuccessors[instruction].Add(nextInstruction); + instructionPredecessors[nextInstruction].Add(instruction); + } + } + } + else + { + Debug.Assert(instruction.Code.FlowControl is CilFlowControl.Break or CilFlowControl.Call or CilFlowControl.Meta or CilFlowControl.Next); + if (i + 1 < Instructions.Count) + { + var nextInstruction = Instructions[i + 1]; + instructionSuccessors[instruction].Add(nextInstruction); + instructionPredecessors[nextInstruction].Add(instruction); + } + } + } + } + + internal Dictionary AnalyzeStackTypes(MethodAnalysisContext owner, TypeReplacementVisitor? visitor = null, bool il2CppTypes = false) + { + AnalyzeControlFlow(out var instructionPredecessors, out var instructionSuccessors); + + Dictionary result = new(Instructions.Count); + + if (Instructions.Count == 0) + { + return result; + } + + visitor ??= TypeReplacementVisitor.Null; + + result[Instructions[0]] = []; + + foreach (var exceptionHandler in ExceptionHandlers) + { + if (exceptionHandler.HandlerType is CilExceptionHandlerType.Finally) + { + if (exceptionHandler.HandlerStart is Instruction instruction) + { + Debug.Assert(instructionPredecessors[instruction].Count == 0); + result.Add(instruction, []); + } + } + else + { + StackType exceptionType = exceptionHandler.ExceptionType is null ? UnknownStackType.Instance : GetExactType(exceptionHandler.ExceptionType, visitor); + + if (exceptionHandler.HandlerStart is Instruction handlerInstruction) + { + Debug.Assert(instructionPredecessors[handlerInstruction].Count == 0); + result.Add(handlerInstruction, [exceptionType]); + } + + if (exceptionHandler.FilterStart is Instruction filterInstruction) + { + Debug.Assert(instructionPredecessors[filterInstruction].Count == 0); + result.Add(filterInstruction, [exceptionType]); + } + } + } + + bool changed; + do + { + changed = false; + + foreach (var instruction in Instructions) + { + if (!result.TryGetValue(instruction, out var stackInitial)) + { + continue; + } + + var popCount = instruction.GetPopCount(owner); + if (popCount is -1) + { + // -1 is special case for "pop all" + popCount = stackInitial.Length; + } + var stackAfterPop = stackInitial.AsSpan(0, stackInitial.Length - popCount); + + var poppedTypes = stackInitial.AsSpan(stackAfterPop.Length, stackInitial.Length - stackAfterPop.Length); + + var pushCount = instruction.GetPushCount(owner); + var stackAfterPushSize = stackAfterPop.Length + pushCount; + Span stackAfterPush = stackAfterPushSize is 0 ? [] : new StackType[stackAfterPushSize]; + stackAfterPop.CopyTo(stackAfterPush); + + // Set pushed types + if (pushCount is 1) + { + stackAfterPush[^1] = instruction.Code.Code switch + { + CilCode.Add or CilCode.Sub or CilCode.Mul or CilCode.Div or CilCode.Rem + or CilCode.Add_Ovf or CilCode.Add_Ovf_Un or CilCode.Sub_Ovf or CilCode.Sub_Ovf_Un + or CilCode.Mul_Ovf or CilCode.Mul_Ovf_Un or CilCode.Div_Un or CilCode.Rem_Un + or CilCode.And or CilCode.Or or CilCode.Xor => StackType.MergeForMathOperation(poppedTypes[0], poppedTypes[1]), + CilCode.Neg or CilCode.Not => poppedTypes[0], + CilCode.Shl or CilCode.Shr or CilCode.Shr_Un => poppedTypes[0], + CilCode.Ldobj => GetExactType((TypeAnalysisContext)instruction.Operand!, visitor), + CilCode.Castclass or CilCode.Isinst => GetExactType((TypeAnalysisContext)instruction.Operand!, visitor), + CilCode.Ldstr => GetExactType(il2CppTypes ? owner.AppContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.String") : owner.AppContext.SystemTypes.SystemStringType, visitor), + CilCode.Ldlen => IntegerStackType32.Instance, + CilCode.Ceq or CilCode.Cgt or CilCode.Cgt_Un or CilCode.Clt or CilCode.Clt_Un => IntegerStackType32.Instance, + CilCode.Sizeof => IntegerStackType32.Instance, + CilCode.Ldind_I + or CilCode.Ldelem_I + or CilCode.Conv_I or CilCode.Conv_U + or CilCode.Conv_Ovf_I or CilCode.Conv_Ovf_U + or CilCode.Conv_Ovf_I_Un or CilCode.Conv_Ovf_U_Un => IntegerStackTypeNative.Instance, + CilCode.Ldc_I4 or CilCode.Ldind_I1 or CilCode.Ldind_I2 or CilCode.Ldind_I4 + or CilCode.Ldind_U1 or CilCode.Ldind_U2 or CilCode.Ldind_U4 + or CilCode.Ldelem_I1 or CilCode.Ldelem_I2 or CilCode.Ldelem_I4 + or CilCode.Ldelem_U1 or CilCode.Ldelem_U2 or CilCode.Ldelem_U4 + or CilCode.Conv_I1 or CilCode.Conv_I2 or CilCode.Conv_I4 + or CilCode.Conv_U1 or CilCode.Conv_U2 or CilCode.Conv_U4 + or CilCode.Conv_Ovf_I1 or CilCode.Conv_Ovf_I2 or CilCode.Conv_Ovf_I4 + or CilCode.Conv_Ovf_I1_Un or CilCode.Conv_Ovf_I2_Un or CilCode.Conv_Ovf_I4_Un + or CilCode.Conv_Ovf_U1 or CilCode.Conv_Ovf_U2 or CilCode.Conv_Ovf_U4 + or CilCode.Conv_Ovf_U1_Un or CilCode.Conv_Ovf_U2_Un or CilCode.Conv_Ovf_U4_Un => IntegerStackType32.Instance, + CilCode.Ldc_I8 or CilCode.Ldind_I8 or CilCode.Ldelem_I8 + or CilCode.Conv_I8 or CilCode.Conv_U8 + or CilCode.Conv_Ovf_I8 or CilCode.Conv_Ovf_U8 + or CilCode.Conv_Ovf_I8_Un or CilCode.Conv_Ovf_U8_Un => IntegerStackType64.Instance, + CilCode.Ldc_R4 or CilCode.Ldind_R4 or CilCode.Ldelem_R4 or CilCode.Conv_R4 => SingleStackType.Instance, + CilCode.Ldc_R8 or CilCode.Ldind_R8 or CilCode.Ldelem_R8 or CilCode.Conv_R8 or CilCode.Conv_R_Un => DoubleStackType.Instance, + CilCode.Ldarg_0 or CilCode.Ldarg_1 or CilCode.Ldarg_2 or CilCode.Ldarg_3 => throw new InvalidOperationException("Ldarg_* should have been replaced with Ldarg."), + CilCode.Ldloc_0 or CilCode.Ldloc_1 or CilCode.Ldloc_2 or CilCode.Ldloc_3 => throw new InvalidOperationException("Ldloc_* should have been replaced with Ldloc."), + CilCode.Ldarg or CilCode.Ldarg_S => instruction.Operand switch + { + ParameterAnalysisContext parameter => GetExactType(visitor.Replace(parameter.ParameterType), visitor), + This => owner.DeclaringType!.IsValueType + ? GetExactType(visitor.Replace(owner.DeclaringType).MakeByReferenceType(), visitor) + : GetExactType(owner.DeclaringType, visitor), + _ => throw new InvalidOperationException("Ldarg operand should be ParameterAnalysisContext or This"), + }, + CilCode.Ldloc or CilCode.Ldloc_S => instruction.Operand switch + { + LocalVariable localVariable => GetExactType(localVariable.Type, visitor), + _ => throw new InvalidOperationException("Ldloc operand should be LocalVariable"), + }, + CilCode.Ldarga or CilCode.Ldarga_S => instruction.Operand switch + { + ParameterAnalysisContext parameter => GetExactType(parameter.ParameterType.MakeByReferenceType(), visitor), + This => throw new NotSupportedException("Ldarga on 'this' is not supported."), + _ => throw new InvalidOperationException("Ldarg operand should be ParameterAnalysisContext or This"), + }, + CilCode.Ldloca or CilCode.Ldloca_S => instruction.Operand switch + { + LocalVariable localVariable => GetExactType(localVariable.Type.MakeByReferenceType(), visitor), + _ => throw new InvalidOperationException("Ldloc operand should be LocalVariable"), + }, + CilCode.Newobj => instruction.Operand switch + { + MethodAnalysisContext methodOperand => GetExactType(methodOperand.DeclaringType!, visitor), + MultiDimensionalArrayMethod arrayMethod => GetExactType(arrayMethod.ArrayType, visitor), + _ => throw new InvalidOperationException("Newobj operand should be MethodAnalysisContext or MultiDimensionalArrayMethod"), + }, + CilCode.Call or CilCode.Callvirt => instruction.Operand switch + { + MethodAnalysisContext methodOperand => GetExactType(methodOperand.ReturnType, visitor), + MultiDimensionalArrayMethod arrayMethod => arrayMethod.MethodType switch + { + MultiDimensionalArrayMethodType.Get => GetExactType(arrayMethod.ArrayType.ElementType, visitor), + MultiDimensionalArrayMethodType.Address => GetExactType(arrayMethod.ArrayType.ElementType.MakeByReferenceType(), visitor), + _ => throw new NotSupportedException($"Method type {arrayMethod.MethodType} does not return a value."), + }, + _ => throw new InvalidOperationException("Call/Callvirt operand should be MethodAnalysisContext or MultiDimensionalArrayMethod"), + }, + CilCode.Ldfld or CilCode.Ldsfld => GetExactType(((FieldAnalysisContext)instruction.Operand!).FieldType, visitor), + CilCode.Ldflda or CilCode.Ldsflda => GetExactType(((FieldAnalysisContext)instruction.Operand!).FieldType.MakeByReferenceType(), visitor), + CilCode.Ldind_Ref => GetStackType_Ldind_Ref(stackInitial, visitor), + CilCode.Ldelem_Ref => GetStackType_Ldelem_Ref(stackInitial, visitor), + CilCode.Ldelema => GetExactType(((TypeAnalysisContext)instruction.Operand!).MakeByReferenceType(), visitor), + CilCode.Ldelem => GetExactType((TypeAnalysisContext)instruction.Operand!, visitor), + CilCode.Box => GetExactType((TypeAnalysisContext)instruction.Operand!, visitor), + CilCode.Unbox or CilCode.Unbox_Any => GetExactType((TypeAnalysisContext)instruction.Operand!, visitor), + CilCode.Newarr => GetExactType(((TypeAnalysisContext)instruction.Operand!).MakeSzArrayType(), visitor), + CilCode.Ldnull => UnknownStackType.Instance, + CilCode.Ldftn or CilCode.Ldvirtftn => IntegerStackTypeNative.Instance, + CilCode.Localloc => IntegerStackTypeNative.Instance, + CilCode.Ldtoken => UnknownStackType.Instance,// Todo + CilCode.Mkrefany => UnknownStackType.Instance,// Todo + CilCode.Refanytype => UnknownStackType.Instance,// Todo + CilCode.Refanyval => UnknownStackType.Instance,// Todo + CilCode.Arglist => UnknownStackType.Instance,// Todo + _ => UnknownStackType.Instance, + }; + } + else if (pushCount is 2) + { + Debug.Assert(instruction.Code == CilOpCodes.Dup); + stackAfterPush[^2] = stackAfterPush[^1] = poppedTypes[0]; + } + else + { + Debug.Assert(pushCount is 0); + } + + foreach (var successor in instructionSuccessors[instruction]) + { + if (result.TryGetValue(successor, out var existingStack)) + { + if (existingStack.Length != stackAfterPush.Length) + { + throw new InvalidOperationException($"Inconsistent stack heights at instruction {successor}: existing {existingStack.Length}, new {stackAfterPush.Length}"); + } + + for (var i = 0; i < existingStack.Length; i++) + { + var merged = StackType.Merge(existingStack[i], stackAfterPush[i]); + if (!EqualityComparer.Default.Equals(merged, existingStack[i])) + { + existingStack[i] = merged; + changed = true; + } + } + } + else + { + result[successor] = stackAfterPush.ToArray(); + changed = true; + } + } + } + } while (changed); + + Debug.Assert(result.Count == Instructions.Count); + + return result; + } + + private static StackType GetStackType_Ldind_Ref(StackType[] stackInitial, TypeReplacementVisitor visitor) + { + return stackInitial[^1] is ExactStackType exactType && TryGetByReferenceElementType(exactType.Type, out var elementType) + ? GetExactType(elementType!, visitor) + : UnknownStackType.Instance; + } + + private static StackType GetStackType_Ldelem_Ref(StackType[] stackInitial, TypeReplacementVisitor visitor) + { + return stackInitial[^2] is ExactStackType exactType && TryGetArrayElementType(exactType.Type, out var elementType) + ? GetExactType(elementType!, visitor) + : UnknownStackType.Instance; + } + + private static StackType GetExactType(TypeAnalysisContext type, TypeReplacementVisitor visitor) + { + var replacedType = visitor.Replace(type); + if (IsPointerType(replacedType)) + { + return IntegerStackTypeNative.Instance; + } + if (IsEnum(replacedType, out var enumElementType)) + { + return GetExactType(enumElementType, visitor); + } + return replacedType.KnownType switch + { + KnownTypeCode.Il2CppSystem_Boolean => IntegerStackType32.Instance, + KnownTypeCode.Il2CppSystem_Char => IntegerStackType32.Instance, + KnownTypeCode.Il2CppSystem_Byte => IntegerStackType32.Instance, + KnownTypeCode.Il2CppSystem_SByte => IntegerStackType32.Instance, + KnownTypeCode.Il2CppSystem_Int16 => IntegerStackType32.Instance, + KnownTypeCode.Il2CppSystem_UInt16 => IntegerStackType32.Instance, + KnownTypeCode.Il2CppSystem_Int32 => IntegerStackType32.Instance, + KnownTypeCode.Il2CppSystem_UInt32 => IntegerStackType32.Instance, + KnownTypeCode.Il2CppSystem_Int64 => IntegerStackType64.Instance, + KnownTypeCode.Il2CppSystem_UInt64 => IntegerStackType64.Instance, + KnownTypeCode.Il2CppSystem_IntPtr => IntegerStackTypeNative.Instance, + KnownTypeCode.Il2CppSystem_UIntPtr => IntegerStackTypeNative.Instance, + KnownTypeCode.Il2CppSystem_Single => SingleStackType.Instance, + KnownTypeCode.Il2CppSystem_Double => DoubleStackType.Instance, + _ => new ExactStackType(replacedType), + }; + } + + private static bool IsPointerType(TypeAnalysisContext type) + { + if (type is PointerTypeAnalysisContext) + return true; + + return type is GenericInstanceTypeAnalysisContext { GenericType: { Name: $"{nameof(Pointer<>)}`1" } genericType } && genericType.Namespace == typeof(Pointer<>).Namespace; + } + + protected static bool IsByReferenceType(TypeAnalysisContext type) + { + return TryGetByReferenceElementType(type, out _); + } + + protected static bool TryGetByReferenceElementType(TypeAnalysisContext type, [NotNullWhen(true)] out TypeAnalysisContext? elementType) + { + if (type is ByRefTypeAnalysisContext byRefType) + { + elementType = byRefType.ElementType; + return true; + } + if (type is GenericInstanceTypeAnalysisContext { GenericType: { Name: $"{nameof(ByReference<>)}`1" } genericType } && genericType.Namespace == typeof(ByReference<>).Namespace) + { + elementType = ((GenericInstanceTypeAnalysisContext)type).GenericArguments[0]; + return true; + } + elementType = null; + return false; + } + + private static bool TryGetArrayElementType(TypeAnalysisContext type, [NotNullWhen(true)] out TypeAnalysisContext? elementType) + { + if (type is ArrayTypeAnalysisContext arrayType) + { + elementType = arrayType.ElementType; + return true; + } + if (type is SzArrayTypeAnalysisContext szArrayType) + { + elementType = szArrayType.ElementType; + return true; + } + if (type is GenericInstanceTypeAnalysisContext { GenericType: { Name: $"{nameof(Il2CppArrayRank1<>)}`1" } genericType } && genericType.Namespace == typeof(Il2CppArrayRank1<>).Namespace) + { + elementType = ((GenericInstanceTypeAnalysisContext)type).GenericArguments[0]; + return true; + } + elementType = null; + return false; + } + + private static bool IsEnum(TypeAnalysisContext type, [NotNullWhen(true)] out TypeAnalysisContext? elementType) + { + if (type is { DefaultBaseType.KnownType: KnownTypeCode.Il2CppSystem_Enum or KnownTypeCode.System_Enum }) + { + elementType = type.Fields.FirstOrDefault(f => f.Name == "value__")?.FieldType; + return elementType is not null; + } + elementType = null; + return false; + } +} diff --git a/Il2CppInterop.Generator/MethodBodyTranslationProcessingLayer.cs b/Il2CppInterop.Generator/MethodBodyTranslationProcessingLayer.cs new file mode 100644 index 00000000..d80c3983 --- /dev/null +++ b/Il2CppInterop.Generator/MethodBodyTranslationProcessingLayer.cs @@ -0,0 +1,33 @@ +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Logging; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator; + +public class MethodBodyTranslationProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Id => "method_body_translation"; + public override string Name => "Method Body Translation"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var successfulCount = 0; + var totalCount = 0; + foreach (var method in appContext.AllTypes.SelectMany(t => t.Methods)) + { + var successful = TranslatedMethodBody.TryTranslateOriginalMethodBody(method); + if (successful) + { + successfulCount++; + } + if (method.HasExtraData()) + { + totalCount++; + } + } + + // Report how many method bodies were successfully translated. + // Note: this total count is less than the count of methods with original bodies that were unstripped earlier. + // This is because static constructors were removed. + Logger.InfoNewline($"Translated the original method body for {successfulCount}/{totalCount} attempts.", nameof(MethodBodyTranslationProcessingLayer)); + } +} diff --git a/Il2CppInterop.Generator/MethodInvokerProcessingLayer.cs b/Il2CppInterop.Generator/MethodInvokerProcessingLayer.cs new file mode 100644 index 00000000..30439f24 --- /dev/null +++ b/Il2CppInterop.Generator/MethodInvokerProcessingLayer.cs @@ -0,0 +1,392 @@ +using System.Diagnostics; +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; +using Il2CppInterop.Generator.Visitors; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.InteropTypes; + +namespace Il2CppInterop.Generator; + +// Todo: add attributes making these not show up in IntelliSense +public class MethodInvokerProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Method Invoker Processor"; + public override string Id => "method_invoker_processor"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var byReference = appContext.ResolveTypeOrThrow(typeof(ByReference<>)); + var byReference_GetValue = byReference.GetMethodByName(nameof(ByReference<>.GetValue)); + var byReference_SetValue = byReference.GetMethodByName(nameof(ByReference<>.SetValue)); + var byReference_CopyFrom = byReference.GetMethodByName(nameof(ByReference<>.CopyFrom)); + var byReference_CopyTo = byReference.GetMethodByName(nameof(ByReference<>.CopyTo)); + var byReference_Clear = byReference.GetMethodByName(nameof(ByReference<>.Clear)); + var byReference_ToPointer = byReference.GetMethodByName(nameof(ByReference<>.ToPointer)); + var byReference_Constructor = byReference.GetMethodByName(".ctor"); + + var il2CppTypeHelper = appContext.ResolveTypeOrThrow(typeof(Il2CppType)); + var il2CppTypeHelper_SizeOf = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.SizeOf)); + + var il2CppStaticClass = appContext.ResolveTypeOrThrow(typeof(IL2CPP)); + var newObjectPointer = il2CppStaticClass.GetMethodByName(nameof(IL2CPP.NewObjectPointer)); + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + + var instantiatedType = type.SelfInstantiateIfGeneric(); + + HashSet existingNames = [type.CSharpName, .. type.Members.Select(m => m.Name)]; + + for (var methodIndex = 0; methodIndex < type.Methods.Count; methodIndex++) + { + var method = type.Methods[methodIndex]; + if (method.IsInjected) + continue; + if (method.IsAbstract) + continue; + + var redirectedType = instantiatedType.KnownType switch + { + _ when method.IsInstanceConstructor => instantiatedType, + KnownTypeCode.Il2CppSystem_Object => appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.IObject"), + KnownTypeCode.Il2CppSystem_Enum => appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.IEnum"), + KnownTypeCode.Il2CppSystem_ValueType => appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.IValueType"), + _ => instantiatedType, + }; + + InjectedMethodAnalysisContext implementationMethod; + { + var name = GetNonConflictingName(method.IsInstanceConstructor ? "UnsafeConstructor" : $"UnsafeImplementation_{method.Name.Replace('.', '_')}", existingNames); + + implementationMethod = new InjectedMethodAnalysisContext( + type, + name, + type, // Placeholder return type + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, + []) + { + IsInjected = true, + }; + type.Methods.Add(implementationMethod); + + method.UnsafeImplementationMethod = implementationMethod; + + implementationMethod.CopyGenericParameters(method, true, true); + + var visitor = TypeReplacementVisitor.CreateForMethodCopying(method, implementationMethod); + + implementationMethod.SetDefaultReturnType(visitor.Replace(method.ReturnType)); + + if (!method.IsStatic) + { + var newParameter = new InjectedParameterAnalysisContext( + null, + byReference.MakeGenericInstanceType([redirectedType]), + ParameterAttributes.None, + 0, + implementationMethod); + implementationMethod.Parameters.Add(newParameter); + } + + foreach (var originalParameter in method.Parameters) + { + var newParameterType = byReference.MakeGenericInstanceType([visitor.Replace(originalParameter.ParameterType)]); + + var newParameter = new InjectedParameterAnalysisContext( + originalParameter.Name, + newParameterType, + originalParameter.Attributes, + implementationMethod.Parameters.Count, + implementationMethod); + implementationMethod.Parameters.Add(newParameter); + } + } + + InjectedMethodAnalysisContext? valueTypeHelper = null; + InjectedMethodAnalysisContext? referenceTypeHelper = null; + if (method.IsStatic) + { + // No helper needed + } + else + { + var name = method.IsInstanceConstructor + ? GetNonConflictingName("UnsafeConstruct", existingNames) + : GetNonConflictingName($"UnsafeInvoke_{method.Name.MakeValidCSharpName()}", existingNames); + + var helper = new InjectedMethodAnalysisContext( + type, + name, + type, // Placeholder return type + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + []) + { + IsInjected = true, + }; + type.Methods.Add(helper); + + method.UnsafeInvokeMethod = helper; + + helper.CopyGenericParameters(method, true, true); + + var visitor = TypeReplacementVisitor.CreateForMethodCopying(method, helper); + + helper.SetDefaultReturnType(visitor.Replace(method.ReturnType)); + + // "this" parameter + if (type.IsValueType) + { + var newParameter = new InjectedParameterAnalysisContext( + null, + byReference.MakeGenericInstanceType([redirectedType]), + ParameterAttributes.None, + 0, + helper); + helper.Parameters.Add(newParameter); + } + else + { + var newParameter = new InjectedParameterAnalysisContext( + null, + redirectedType, + ParameterAttributes.None, + 0, + helper); + helper.Parameters.Add(newParameter); + } + + foreach (var originalParameter in method.Parameters) + { + var newParameterType = visitor.Replace(originalParameter.ParameterType); + + var newParameter = new InjectedParameterAnalysisContext( + originalParameter.Name, + newParameterType, + originalParameter.Attributes, + helper.Parameters.Count, + helper); + helper.Parameters.Add(newParameter); + } + + List instructions = new(); + + var thisIsLocal = !type.IsValueType; + var localVariablesOffset = thisIsLocal ? 0 : 1; + var localVariablesCount = thisIsLocal ? method.Parameters.Count + 1 : method.Parameters.Count; + LocalVariable[] localVariables = new LocalVariable[localVariablesCount]; + + for (var i = localVariablesOffset; i < helper.Parameters.Count; i++) + { + var parameter = helper.Parameters[i]; + var parameterLocal = new LocalVariable(byReference.MakeGenericInstanceType([parameter.ParameterType])); + localVariables[i - localVariablesOffset] = parameterLocal; + + instructions.Add(CilOpCodes.Call, il2CppTypeHelper_SizeOf.MakeGenericInstanceMethod(parameter.ParameterType)); + instructions.Add(CilOpCodes.Conv_U); + instructions.Add(CilOpCodes.Localloc); + instructions.Add(CilOpCodes.Newobj, byReference_Constructor.MakeConcreteGeneric([parameter.ParameterType], [])); + instructions.Add(CilOpCodes.Stloc, parameterLocal); + + instructions.Add(CilOpCodes.Ldloca, parameterLocal); + instructions.Add(CilOpCodes.Ldarg, parameter); + instructions.Add(CilOpCodes.Call, byReference_SetValue.MakeConcreteGeneric([parameter.ParameterType], [])); + } + + if (!thisIsLocal) + { + instructions.Add(CilOpCodes.Ldarg, helper.Parameters[0]); + } + + foreach (var parameterLocal in localVariables) + { + instructions.Add(CilOpCodes.Ldloc, parameterLocal); + } + + instructions.Add(CilOpCodes.Call, implementationMethod.MaybeMakeConcreteGeneric(type.GenericParameters, helper.GenericParameters)); + + instructions.Add(CilOpCodes.Ret); + + helper.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + LocalVariables = localVariables, + }); + + if (type.IsValueType) + { + valueTypeHelper = helper; + } + else + { + referenceTypeHelper = helper; + } + } + + // Method body + Debug.Assert(!method.HasExtraData()); + if (valueTypeHelper is not null) + { + Debug.Assert(!method.IsStatic); + Debug.Assert(type.IsValueType); + + List instructions = new(); + + LocalVariable instanceLocal = new(byReference.MakeGenericInstanceType([instantiatedType])); + + instructions.Add(CilOpCodes.Call, il2CppTypeHelper_SizeOf.MakeGenericInstanceMethod(instantiatedType)); + instructions.Add(CilOpCodes.Conv_U); + instructions.Add(CilOpCodes.Localloc); + instructions.Add(CilOpCodes.Newobj, new ConcreteGenericMethodAnalysisContext(byReference_Constructor, [instantiatedType], [])); + instructions.Add(CilOpCodes.Stloc, instanceLocal); + + instructions.Add(CilOpCodes.Ldloca, instanceLocal); + instructions.Add(CilOpCodes.Ldarg, This.Instance); + instructions.Add(CilOpCodes.Call, byReference_CopyFrom.MakeConcreteGeneric([instantiatedType], [])); + + instructions.Add(CilOpCodes.Ldloc, instanceLocal); + + foreach (var parameter in method.Parameters) + { + instructions.Add(CilOpCodes.Ldarg, parameter); + } + + instructions.Add(CilOpCodes.Call, valueTypeHelper.MaybeMakeConcreteGeneric(type.GenericParameters, method.GenericParameters)); + + instructions.Add(CilOpCodes.Ldloca, instanceLocal); + instructions.Add(CilOpCodes.Ldarg, This.Instance); + instructions.Add(CilOpCodes.Call, byReference_CopyTo.MakeConcreteGeneric([instantiatedType], [])); + + instructions.Add(CilOpCodes.Ret); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + LocalVariables = [instanceLocal], + }); + } + else if (referenceTypeHelper is not null) + { + Debug.Assert(!method.IsStatic); + Debug.Assert(!type.IsValueType); + + List instructions = []; + + if (method.IsInstanceConstructor) + { + Debug.Assert(type.PointerConstructor is not null); + instructions.Add(CilOpCodes.Ldarg, This.Instance); + instructions.Add(CilOpCodes.Call, newObjectPointer.MakeGenericInstanceMethod(instantiatedType)); + instructions.Add(CilOpCodes.Call, type.PointerConstructor!.MaybeMakeConcreteGeneric(type.GenericParameters, [])); + } + + instructions.Add(CilOpCodes.Ldarg, This.Instance); + foreach (var parameter in method.Parameters) + { + instructions.Add(CilOpCodes.Ldarg, parameter); + } + instructions.Add(CilOpCodes.Call, referenceTypeHelper.MaybeMakeConcreteGeneric(type.GenericParameters, method.GenericParameters)); + instructions.Add(CilOpCodes.Ret); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + LocalVariables = [], + }); + } + else + { + Debug.Assert(method.IsStatic); + + List instructions = new(); + + LocalVariable[] parameterLocals = new LocalVariable[method.Parameters.Count]; + + for (var i = 0; i < method.Parameters.Count; i++) + { + var parameter = method.Parameters[i]; + var parameterLocal = new LocalVariable(byReference.MakeGenericInstanceType([parameter.ParameterType])); + parameterLocals[i] = parameterLocal; + + instructions.Add(CilOpCodes.Call, il2CppTypeHelper_SizeOf.MakeGenericInstanceMethod(parameter.ParameterType)); + instructions.Add(CilOpCodes.Conv_U); + instructions.Add(CilOpCodes.Localloc); + instructions.Add(CilOpCodes.Newobj, byReference_Constructor.MakeConcreteGeneric([parameter.ParameterType], [])); + instructions.Add(CilOpCodes.Stloc, parameterLocal); + + instructions.Add(CilOpCodes.Ldloca, parameterLocal); + instructions.Add(CilOpCodes.Ldarg, parameter); + instructions.Add(CilOpCodes.Call, byReference_SetValue.MakeConcreteGeneric([parameter.ParameterType], [])); + } + + foreach (var parameterLocal in parameterLocals) + { + instructions.Add(CilOpCodes.Ldloc, parameterLocal); + } + + instructions.Add(CilOpCodes.Call, implementationMethod.MaybeMakeConcreteGeneric(type.GenericParameters, method.GenericParameters)); + + instructions.Add(CilOpCodes.Ret); + + method.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + LocalVariables = parameterLocals, + }); + } + + // Interface redirect body + if (method.InterfaceRedirectMethod is not null) + { + Debug.Assert(method.GenericParameters.Count == 0); + Debug.Assert(method.DeclaringType is { GenericParameters.Count: 0 }); + + var methodBody = method.GetExtraData(); + Debug.Assert(methodBody is { ExceptionHandlers.Count: 0 }); + + var localVariables = new LocalVariable[methodBody.LocalVariables.Count]; + var operandRedirects = new Dictionary(methodBody.LocalVariables.Count); + for (var i = 0; i < localVariables.Length; i++) + { + var originalLocal = methodBody.LocalVariables[i]; + var newLocal = new LocalVariable(originalLocal.Type); + localVariables[i] = newLocal; + operandRedirects.Add(originalLocal, newLocal); + } + + method.InterfaceRedirectMethod!.PutExtraData(new NativeMethodBody() + { + Instructions = methodBody.Instructions.Select(i => + { + return new Instruction(i.Code, i.Operand is not null && operandRedirects.TryGetValue(i.Operand, out var redirectedOperand) ? redirectedOperand : i.Operand); + }).ToArray(), + LocalVariables = localVariables, + }); + + if (method.UnsafeInvokeMethod is not null) + { + method.InterfaceRedirectMethod!.UnsafeInvokeMethod = method.UnsafeInvokeMethod!; + } + } + } + } + } + } + + private static string GetNonConflictingName(string baseName, HashSet existingNames) + { + var name = baseName; + while (existingNames.Contains(name)) + { + name = $"{baseName}_"; + } + return name; + } +} diff --git a/Il2CppInterop.Generator/MonoIl2CppConversion.cs b/Il2CppInterop.Generator/MonoIl2CppConversion.cs new file mode 100644 index 00000000..6f56483d --- /dev/null +++ b/Il2CppInterop.Generator/MonoIl2CppConversion.cs @@ -0,0 +1,138 @@ +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator; +using Il2CppInterop.Generator.Operands; +using Il2CppInterop.Runtime.InteropTypes; + +namespace Il2CppInterop.Generator; + +internal static class MonoIl2CppConversion +{ + /// + /// Does not convert strings, which are treated as normal Il2Cpp objects. + /// + public static bool AddIl2CppToMonoConversion(List instructions, TypeAnalysisContext il2CppType) + { + // Depending on the type of the local variable, we may need to convert it. + // If the local variable is Pointer, we need to convert it to T*. + // If the local variable is an Il2Cpp primitive (like Il2CppSystem.Int32), we need to convert it to the corresponding C# type. + // If the local variable is an Il2Cpp enum, we need to convert it to the underlying C# primitive type. + + if (IsIl2CppPrimitiveValueType(il2CppType)) + { + var monoType = il2CppType.AppContext.Mscorlib.GetTypeByFullNameOrThrow($"System.{il2CppType.Name}"); + var conversionMethod = il2CppType.GetImplicitConversionTo(monoType); + instructions.Add(new Instruction(CilOpCodes.Call, conversionMethod)); + return true; + } + else if (il2CppType is GenericInstanceTypeAnalysisContext { GenericArguments.Count: 1, GenericType.DeclaringType: null } genericInstanceType) + { + if (IsPointerOrByReference(genericInstanceType)) + { + var elementType = genericInstanceType.GenericArguments[0]; + var conversionMethod = genericInstanceType.GenericType.Methods.First(m => m.Name == "op_Explicit" && m.ReturnType is PointerTypeAnalysisContext); + instructions.Add(new Instruction(CilOpCodes.Call, new ConcreteGenericMethodAnalysisContext(conversionMethod, [elementType], []))); + return true; + } + } + else if (il2CppType.EnumMonoUnderlyingType is { } underlyingType) + { + var conversionMethod = il2CppType.GetExplicitConversionTo(underlyingType); + instructions.Add(new Instruction(CilOpCodes.Call, conversionMethod)); + return true; + } + return false; + } + + /// + /// Does not convert strings, which are treated as normal Il2Cpp objects. + /// + public static bool AddMonoToIl2CppConversion(List instructions, TypeAnalysisContext il2CppType) + { + if (IsIl2CppPrimitiveValueType(il2CppType)) + { + var monoType = il2CppType.AppContext.Mscorlib.GetTypeByFullNameOrThrow($"System.{il2CppType.Name}"); + var conversionMethod = il2CppType.GetImplicitConversionFrom(monoType); + instructions.Add(new Instruction(CilOpCodes.Call, conversionMethod)); + return true; + } + else if (il2CppType is GenericInstanceTypeAnalysisContext { GenericArguments.Count: 1, GenericType.DeclaringType: null } genericInstanceType) + { + if (IsPointerOrByReference(genericInstanceType)) + { + var elementType = genericInstanceType.GenericArguments[0]; + var conversionMethod = genericInstanceType.GenericType.Methods.First(m => m.Name == "op_Explicit" && m.Parameters.Count == 1 && m.Parameters[0].ParameterType is PointerTypeAnalysisContext); + instructions.Add(new Instruction(CilOpCodes.Call, new ConcreteGenericMethodAnalysisContext(conversionMethod, [elementType], []))); + return true; + } + } + else if (il2CppType.EnumMonoUnderlyingType is { } underlyingType) + { + var conversionMethod = il2CppType.GetExplicitConversionFrom(underlyingType); + instructions.Add(new Instruction(CilOpCodes.Call, conversionMethod)); + return true; + } + return false; + } + + private static bool IsPointer(GenericInstanceTypeAnalysisContext genericInstanceType) + { + return genericInstanceType.GenericType.Name == $"{nameof(Pointer<>)}`1" && genericInstanceType.GenericType.Namespace == typeof(Pointer<>).Namespace; + } + + private static bool IsByReference(GenericInstanceTypeAnalysisContext genericInstanceType) + { + return genericInstanceType.GenericType.Name == $"{nameof(ByReference<>)}`1" && genericInstanceType.GenericType.Namespace == typeof(ByReference<>).Namespace; + } + + private static bool IsPointerOrByReference(GenericInstanceTypeAnalysisContext genericInstanceType) + { + return IsPointer(genericInstanceType) || IsByReference(genericInstanceType); + } + + public static void AddIl2CppToMonoStringConversion(List instructions, ApplicationAnalysisContext appContext) + { + var il2CppSystemString = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.String"); + var monoSystemString = appContext.SystemTypes.SystemStringType; + var conversionMethod = il2CppSystemString.GetImplicitConversionTo(monoSystemString); + instructions.Add(new Instruction(CilOpCodes.Call, conversionMethod)); + } + + public static void AddMonoToIl2CppStringConversion(List instructions, ApplicationAnalysisContext appContext) + { + var il2CppSystemString = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.String"); + var monoSystemString = appContext.SystemTypes.SystemStringType; + var conversionMethod = il2CppSystemString.GetImplicitConversionFrom(monoSystemString); + instructions.Add(new Instruction(CilOpCodes.Call, conversionMethod)); + } + + private static bool IsIl2CppPrimitiveValueType(TypeAnalysisContext type) + { + if (type is ReferencedTypeAnalysisContext) + return false; + + if (type.DeclaringType is not null) + return false; + + if (type.DeclaringAssembly != type.AppContext.Il2CppMscorlib) + return false; + + if (type.Namespace != "Il2CppSystem") + return false; + + return type.Name + is "Byte" + or "SByte" + or "Int16" + or "UInt16" + or "Int32" + or "UInt32" + or "IntPtr" + or "UIntPtr" + or "Int64" + or "UInt64" + or "Single" + or "Double" + or "Boolean" + or "Char"; + } +} diff --git a/Il2CppInterop.Generator/MscorlibAssemblyInjectionProcessingLayer.cs b/Il2CppInterop.Generator/MscorlibAssemblyInjectionProcessingLayer.cs new file mode 100644 index 00000000..6057d98b --- /dev/null +++ b/Il2CppInterop.Generator/MscorlibAssemblyInjectionProcessingLayer.cs @@ -0,0 +1,54 @@ +using AsmResolver.DotNet; +using Cpp2IL.Core.Logging; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator; + +public class MscorlibAssemblyInjectionProcessingLayer : UnstripBaseProcessingLayer +{ + public override string Name => "Inject a new mscorlib into the Cpp2IL context system"; + + public override string Id => "mscorlib_injector"; + + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var assemblyList = appContext.GetExtraData>(AssembliesKey); + if (assemblyList is null) + { + var directoryPath = appContext.GetExtraData(DirectoryKey); + if (directoryPath is null) + { + Logger.WarnNewline($"No assemblies specified - processor will not run. You need to provide the {DirectoryKey}, either by programmatically adding it as extra data in the app context, or by specifying it in the --processor-config command line option.", nameof(UnstripProcessingLayer)); + return; + } + + assemblyList = Directory.GetFiles(directoryPath, "*.dll", SearchOption.AllDirectories) + .Where(x => x.Contains("mscorlib", StringComparison.OrdinalIgnoreCase)) + .Select(path => AssemblyDefinition.FromFile(path)) + .ToList(); + } + + var mscorlib = assemblyList.FirstOrDefault(x => x.Name == "mscorlib"); + + if (mscorlib is null) + { + Logger.WarnNewline("mscorlib not provided - processor will not run.", nameof(UnstripProcessingLayer)); + return; + } + + if (appContext.AssembliesByName.ContainsKey("mscorlib")) + { + Logger.WarnNewline("mscorlib already injected - processor will not run.", nameof(MscorlibAssemblyInjectionProcessingLayer)); + return; + } + + Logger.InfoNewline($"Injecting new mscorlib...", nameof(MscorlibAssemblyInjectionProcessingLayer)); + + InjectAssemblies(appContext, [mscorlib], false, true); + + // Need to reset the system types context to use the new corlib + appContext.SystemTypes = new SystemTypesContext(appContext); + + appContext.AssembliesByName["mscorlib"].IsReferenceAssembly = true; + } +} diff --git a/Il2CppInterop.Generator/NativeMethodBody.cs b/Il2CppInterop.Generator/NativeMethodBody.cs new file mode 100644 index 00000000..a76d6332 --- /dev/null +++ b/Il2CppInterop.Generator/NativeMethodBody.cs @@ -0,0 +1,6 @@ +namespace Il2CppInterop.Generator; + +public class NativeMethodBody : MethodBodyBase +{ + +} diff --git a/Il2CppInterop.Generator/NativeMethodBodyProcessingLayer.cs b/Il2CppInterop.Generator/NativeMethodBodyProcessingLayer.cs new file mode 100644 index 00000000..c20d0053 --- /dev/null +++ b/Il2CppInterop.Generator/NativeMethodBodyProcessingLayer.cs @@ -0,0 +1,183 @@ +using System.Diagnostics; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; +using Il2CppInterop.Runtime.InteropTypes; + +namespace Il2CppInterop.Generator; + +public class NativeMethodBodyProcessingLayer : Cpp2IlProcessingLayer +{ + private const int ParameterOffset = 2; + public override string Name => "Native Method Body Processor"; + public override string Id => "native_method_body_processor"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var runtimeInvokeHelper = appContext.ResolveTypeOrThrow(typeof(RuntimeInvoke)); + var invokeAction = runtimeInvokeHelper.GetMethodByName(nameof(RuntimeInvoke.InvokeAction)); + var invokeFunction = runtimeInvokeHelper.GetMethodByName(nameof(RuntimeInvoke.InvokeFunction)); + var getPointerForThis = runtimeInvokeHelper.GetMethodByName(nameof(RuntimeInvoke.GetPointerForThis)); + var getPointerForParameter = runtimeInvokeHelper.GetMethodByName(nameof(RuntimeInvoke.GetPointerForParameter)); + + var intptrPointerType = appContext.SystemTypes.SystemIntPtrType.MakePointerType(); + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + foreach (var method in type.Methods) + { + if (method.IsInjected) + continue; + if (method.IsAbstract) + continue; + + var implementation = method.UnsafeImplementationMethod; + Debug.Assert(implementation is not null); + Debug.Assert(implementation.IsStatic); + + var hasThis = !method.IsStatic; + + Debug.Assert(implementation.Parameters.Count == method.Parameters.Count + (hasThis ? 1 : 0)); + + if (implementation.HasExtraData()) + continue; // Already has a translated body, skip. + + Debug.Assert(!implementation.HasExtraData()); + + if (method.IsUnstripped) + { + var icallDelegateField = method.ICallDelegateField; + if (icallDelegateField != null) + { + Debug.Assert(method.DeclaringType?.GenericParameters.Count == 0 && method.GenericParameters.Count == 0); + List instructions2 = new(1 + implementation.Parameters.Count + 2); + instructions2.Add(CilOpCodes.Ldsfld, icallDelegateField); + foreach (var parameter in implementation.Parameters) + { + instructions2.Add(CilOpCodes.Ldarg, parameter); + } + instructions2.Add(CilOpCodes.Callvirt, icallDelegateField.FieldType.GetMethodByName("Invoke")); + instructions2.Add(CilOpCodes.Ret); + implementation.PutExtraData(new NativeMethodBody() + { + Instructions = instructions2, + LocalVariables = [], + }); + } + continue; + } + + var argumentCount = method.Parameters.Count; + + List instructions = new(); + + LocalVariable? argumentsArrayLocal = null; + if (argumentCount > 0) + { + argumentsArrayLocal = new LocalVariable(intptrPointerType); + + instructions.Add(CilOpCodes.Ldc_I4, argumentCount); + instructions.Add(CilOpCodes.Conv_U); + instructions.Add(CilOpCodes.Sizeof, appContext.SystemTypes.SystemIntPtrType); + instructions.Add(CilOpCodes.Mul_Ovf_Un); + instructions.Add(CilOpCodes.Localloc); + + var startIndex = hasThis ? 1 : 0; + for (var i = 0; i < argumentCount; i++) + { + var parameter = implementation.Parameters[startIndex + i]; + var dataType = ((GenericInstanceTypeAnalysisContext)parameter.ParameterType).GenericArguments[0]; + instructions.Add(CilOpCodes.Dup); + AddOffsetForPointerIndex(instructions, i, appContext); + instructions.Add(CilOpCodes.Ldarg, parameter); + instructions.Add(CilOpCodes.Call, getPointerForParameter.MakeGenericInstanceMethod(dataType)); + instructions.Add(CilOpCodes.Stind_I); + } + instructions.Add(CilOpCodes.Stloc, argumentsArrayLocal); + } + + // Method info + { + var methodInfoField = method.MethodInfoField; + Debug.Assert(methodInfoField is not null); + Debug.Assert(implementation.DeclaringType is not null); + + IReadOnlyList methodInfoGenericArguments = [.. implementation.DeclaringType.GenericParameters, .. implementation.GenericParameters]; + + instructions.Add(CilOpCodes.Ldsfld, methodInfoField.MaybeMakeConcreteGeneric(methodInfoGenericArguments)); + } + + // Object pointer + if (hasThis) + { + var parameter = implementation.Parameters[0]; + var dataType = ((GenericInstanceTypeAnalysisContext)parameter.ParameterType).GenericArguments[0]; + + instructions.Add(CilOpCodes.Ldarg, parameter); + instructions.Add(CilOpCodes.Call, getPointerForThis.MakeGenericInstanceMethod(dataType)); + } + else + { + instructions.Add(CilOpCodes.Ldc_I4_0); + instructions.Add(CilOpCodes.Conv_I); + } + + // Arguments array + if (argumentsArrayLocal is not null) + { + instructions.Add(CilOpCodes.Ldloc, argumentsArrayLocal); + } + else + { + instructions.Add(CilOpCodes.Ldc_I4_0); + instructions.Add(CilOpCodes.Conv_U); + } + + if (implementation.IsVoid) + { + instructions.Add(CilOpCodes.Call, invokeAction); + } + else + { + instructions.Add(CilOpCodes.Call, invokeFunction.MakeGenericInstanceMethod(implementation.ReturnType)); + } + + instructions.Add(CilOpCodes.Ret); + + implementation.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + LocalVariables = argumentsArrayLocal is not null ? [argumentsArrayLocal] : [], + }); + } + } + } + } + + private static void AddOffsetForPointerIndex(List instructions, int index, ApplicationAnalysisContext appContext) + { + if (index != 0) + { + if (index > 1) + { + instructions.Add(CilOpCodes.Ldc_I4, index); + instructions.Add(CilOpCodes.Conv_I); + } + + instructions.Add(CilOpCodes.Sizeof, appContext.SystemTypes.SystemIntPtrType); + + if (index > 1) + { + instructions.Add(CilOpCodes.Mul); + } + + instructions.Add(CilOpCodes.Add); + } + } +} diff --git a/Il2CppInterop.Generator/ObjectInterfaceProcessingLayer.cs b/Il2CppInterop.Generator/ObjectInterfaceProcessingLayer.cs new file mode 100644 index 00000000..01bb1a3e --- /dev/null +++ b/Il2CppInterop.Generator/ObjectInterfaceProcessingLayer.cs @@ -0,0 +1,180 @@ +using System.Diagnostics; +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; + +namespace Il2CppInterop.Generator; + +public class ObjectInterfaceProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Object Interface Processor"; + public override string Id => "object_interface_processor"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + // Find references + var il2CppSystemObject = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Object"); + var il2CppSystemValueType = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.ValueType"); + var il2CppSystemEnum = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Enum"); + + // Inject interfaces + var il2CppSystemIObject = InjectInterface(appContext, "IObject"); + var il2CppSystemIValueType = InjectInterface(appContext, "IValueType"); + var il2CppSystemIEnum = InjectInterface(appContext, "IEnum"); + + // Set KnownType + il2CppSystemIObject.KnownType = KnownTypeCode.Il2CppSystem_IObject; + il2CppSystemIValueType.KnownType = KnownTypeCode.Il2CppSystem_IValueType; + il2CppSystemIEnum.KnownType = KnownTypeCode.Il2CppSystem_IEnum; + + // Il2CppSystem.IValueType : Il2CppSystem.IObject + il2CppSystemIValueType.InterfaceContexts.Add(il2CppSystemIObject); + + // Il2CppSystem.IEnum : Il2CppSystem.IObject, Il2CppSystem.IValueType /* and the other interfaces that Il2CppSystem.Enum implements */ + il2CppSystemIEnum.InterfaceContexts.Add(il2CppSystemIObject); + il2CppSystemIEnum.InterfaceContexts.Add(il2CppSystemIValueType); + foreach (var interfaceContext in il2CppSystemEnum.InterfaceContexts) + { + il2CppSystemIEnum.InterfaceContexts.Add(interfaceContext); + } + + // Il2CppSystem.ValueType : Il2CppSystem.IValueType + il2CppSystemValueType.InterfaceContexts.Add(il2CppSystemIValueType); + + // Il2CppSystem.Enum : Il2CppSystem.IEnum + il2CppSystemEnum.InterfaceContexts.Add(il2CppSystemIEnum); + + // Add methods to interfaces + foreach (var method in il2CppSystemObject.Methods) + { + if (!method.IsConstructor && !method.IsStatic && !method.IsInjected) + { + CreateInterfaceMethod(method, il2CppSystemObject, il2CppSystemIObject); + } + } + foreach (var method in il2CppSystemValueType.Methods) + { + if (!method.IsConstructor && !method.IsStatic && !method.IsInjected) + { + CreateInterfaceMethod(method, il2CppSystemValueType, il2CppSystemIValueType); + } + } + foreach (var method in il2CppSystemEnum.Methods) + { + if (!method.IsConstructor && !method.IsStatic && !method.IsInjected) + { + CreateInterfaceMethod(method, il2CppSystemEnum, il2CppSystemIEnum); + } + } + + // Add interfaces to types + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + + type.InterfaceContexts.Add(il2CppSystemIObject); + + if (type.DefaultBaseType == il2CppSystemValueType) + { + type.InterfaceContexts.Add(il2CppSystemIValueType); + } + else if (type.DefaultBaseType == il2CppSystemEnum) + { + type.InterfaceContexts.Add(il2CppSystemIValueType); + type.InterfaceContexts.Add(il2CppSystemIEnum); + } + } + } + } + + private static InjectedTypeAnalysisContext InjectInterface(ApplicationAnalysisContext appContext, string name) + { + var result = appContext.Il2CppMscorlib.InjectType("Il2CppSystem", name, null, TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract); + result.IsInjected = true; + return result; + } + + private static void CreateInterfaceMethod(MethodAnalysisContext classMethod, TypeAnalysisContext classType, TypeAnalysisContext interfaceType) + { + Debug.Assert(classMethod.DeclaringType == classType); + Debug.Assert(classMethod.GenericParameters.Count == 0); + Debug.Assert(classType.GenericParameters.Count == 0); + Debug.Assert(interfaceType.IsInjected); + + var @explicitlyImplements = classMethod.DefaultName.Contains('.'); + + var interfaceMethod = new InjectedMethodAnalysisContext( + interfaceType, + classMethod.DefaultName, + classMethod.ReturnType, + explicitlyImplements + ? MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.Final + : MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot, + classMethod.Parameters.Select(p => p.ParameterType).ToArray(), + classMethod.Parameters.Select(p => p.Name).ToArray(), + classMethod.Parameters.Select(p => p.Attributes).ToArray()) + { + OverrideName = classMethod.OverrideName, + }; + interfaceType.Methods.Add(interfaceMethod); + + foreach (var @override in classMethod.Overrides) + { + if (@override.DeclaringType is not { IsInterface: true }) + { + continue; + } + if (@explicitlyImplements) + { + interfaceMethod.Overrides.Add(@override); + @explicitlyImplements = true; + continue; + } + + var overrideImplementation = new InjectedMethodAnalysisContext( + interfaceType, + $"{@override.DeclaringType!.FullName}.{interfaceMethod.Name}", + classMethod.ReturnType, + MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.Final, + classMethod.Parameters.Select(p => p.ParameterType).ToArray(), + classMethod.Parameters.Select(p => p.Name).ToArray(), + classMethod.Parameters.Select(p => p.Attributes).ToArray()) + { + OverrideName = classMethod.OverrideName, + IsInjected = true, + }; + interfaceType.Methods.Add(overrideImplementation); + overrideImplementation.Overrides.Add(@override); + + List instructions = + [ + new Instruction(CilOpCodes.Ldarg_0), + ..Enumerable.Range(0, classMethod.Parameters.Count).Select(i => new Instruction + { + Code = CilOpCodes.Ldarg, + Operand = overrideImplementation.Parameters[i], + }), + new Instruction(CilOpCodes.Callvirt, interfaceMethod), + new Instruction(CilOpCodes.Ret), + ]; + overrideImplementation.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + }); + } + + classMethod.InterfaceRedirectMethod = interfaceMethod; + if (!classMethod.IsVirtual) + { + // If the class method isn't virtual, we want to make sure it can't be overridden in subclasses. + classMethod.Attributes |= MethodAttributes.Final | MethodAttributes.NewSlot; + } + classMethod.Attributes |= MethodAttributes.Virtual | MethodAttributes.HideBySig; + } +} diff --git a/Il2CppInterop.Generator/ObjectInternalsProcessingLayer.cs b/Il2CppInterop.Generator/ObjectInternalsProcessingLayer.cs new file mode 100644 index 00000000..c9a91557 --- /dev/null +++ b/Il2CppInterop.Generator/ObjectInternalsProcessingLayer.cs @@ -0,0 +1,197 @@ +using System.Reflection; +using AsmResolver.DotNet.Code.Cil; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.Runtime; + +namespace Il2CppInterop.Generator; + +public class ObjectInternalsProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Object Internals Processor"; + public override string Id => "object_internals_processor"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var il2CppSystemObject = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Object"); + + var il2CppStaticClass = appContext.ResolveTypeOrThrow(typeof(IL2CPP)); + var generationInternalsType = appContext.ResolveTypeOrThrow(typeof(GenerationInternals)); + + var il2CppObjectPool = appContext.ResolveTypeOrThrow(typeof(Il2CppObjectPool)); + + var pooledPointerField = new InjectedFieldAnalysisContext( + "_pooledPointer", + appContext.SystemTypes.SystemIntPtrType, + FieldAttributes.Private, + il2CppSystemObject) + { + IsInjected = true, + }; + il2CppSystemObject.Fields.Add(pooledPointerField); + + var gcHandleField = new InjectedFieldAnalysisContext( + "_gcHandle", + appContext.SystemTypes.SystemIntPtrType, + FieldAttributes.Private, + il2CppSystemObject) + { + IsInjected = true, + }; + il2CppSystemObject.Fields.Add(gcHandleField); + + // Constructor + { + var constructor = il2CppSystemObject.PointerConstructor!; + var baseConstructor = appContext.SystemTypes.SystemObjectType.Methods.Single(m => m.IsInstanceConstructor); + var objectPointerType = constructor.Parameters[0].ParameterType; + var objectPointerToSystemIntPtr = objectPointerType.GetExplicitConversionTo(appContext.SystemTypes.SystemIntPtrType); + + constructor.PutExtraData(new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Call, baseConstructor), + + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Ldarg_1), + new Instruction(CilOpCodes.Call, objectPointerToSystemIntPtr), + new Instruction(CilOpCodes.Stfld, pooledPointerField), + + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Ldarg_1), + new Instruction(CilOpCodes.Call, objectPointerToSystemIntPtr), + new Instruction(CilOpCodes.Ldc_I4_0), // false for "pinned" + new Instruction(CilOpCodes.Call, il2CppStaticClass.GetMethodByName(nameof(IL2CPP.il2cpp_gchandle_new))), + new Instruction(CilOpCodes.Stfld, gcHandleField), + + new Instruction(CilOpCodes.Ret), + ] + }); + } + + // Pointer property + { + var getMethod = new InjectedMethodAnalysisContext( + il2CppSystemObject, + $"get_{nameof(Object.Pointer)}", + appContext.SystemTypes.SystemIntPtrType, + MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.SpecialName, + []) + { + IsInjected = true, + }; + il2CppSystemObject.Methods.Add(getMethod); + + var property = new InjectedPropertyAnalysisContext( + nameof(Object.Pointer), + appContext.SystemTypes.SystemIntPtrType, + getMethod, + null, + PropertyAttributes.None, + il2CppSystemObject) + { + IsInjected = true, + }; + il2CppSystemObject.Properties.Add(property); + + getMethod.PutExtraData(new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Ldfld, gcHandleField), + new Instruction(CilOpCodes.Call, generationInternalsType.GetMethodByName(nameof(GenerationInternals.Il2CppGCHandleGetTargetOrThrow))), + new Instruction(CilOpCodes.Ret), + ] + }); + } + + // WasCollected property + { + var getMethod = new InjectedMethodAnalysisContext( + il2CppSystemObject, + $"get_{nameof(Object.WasCollected)}", + appContext.SystemTypes.SystemBooleanType, + MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.SpecialName, + []) + { + IsInjected = true, + }; + + il2CppSystemObject.Methods.Add(getMethod); + var property = new InjectedPropertyAnalysisContext( + nameof(Object.WasCollected), + appContext.SystemTypes.SystemBooleanType, + getMethod, + null, + PropertyAttributes.None, + il2CppSystemObject) + { + IsInjected = true, + }; + il2CppSystemObject.Properties.Add(property); + + getMethod.PutExtraData(new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Ldfld, gcHandleField), + new Instruction(CilOpCodes.Call, generationInternalsType.GetMethodByName(nameof(GenerationInternals.Il2CppGCHandleGetTargetWasCollected))), + new Instruction(CilOpCodes.Ret), + ] + }); + } + + // Finalizer + { + var baseMethod = appContext.SystemTypes.SystemObjectType.GetMethodByName("Finalize"); + var method = new InjectedMethodAnalysisContext( + il2CppSystemObject, + "Finalize", + appContext.SystemTypes.SystemVoidType, + MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.Virtual, + []) + { + IsInjected = true, + }; + il2CppSystemObject.Methods.Add(method); + + method.Overrides.Add(baseMethod); // Currently doesn't work because Cpp2IL excludes overrides from non-interface methods + + var instructions = new List(); + + var returnInstruction = new Instruction(CilOpCodes.Ret); + + var tryStart = instructions.Add(CilOpCodes.Ldarg_0); + instructions.Add(CilOpCodes.Ldfld, gcHandleField); + instructions.Add(CilOpCodes.Call, il2CppStaticClass.GetMethodByName(nameof(IL2CPP.il2cpp_gchandle_free))); + + instructions.Add(CilOpCodes.Ldarg_0); + instructions.Add(CilOpCodes.Ldfld, pooledPointerField); + instructions.Add(CilOpCodes.Call, il2CppObjectPool.GetMethodByName(nameof(Il2CppObjectPool.Remove))); + instructions.Add(CilOpCodes.Leave, returnInstruction); + + var handlerStart = instructions.Add(CilOpCodes.Ldarg_0); + instructions.Add(CilOpCodes.Call, baseMethod); + instructions.Add(CilOpCodes.Endfinally); + + instructions.Add(returnInstruction); + + var exceptionHandler = new ExceptionHandler() + { + HandlerType = CilExceptionHandlerType.Finally, + TryStart = tryStart, + TryEnd = handlerStart, + HandlerStart = handlerStart, + HandlerEnd = returnInstruction, + }; + + method.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + ExceptionHandlers = [exceptionHandler], + }); + } + } +} diff --git a/Il2CppInterop.Generator/ObjectOverridesProcessingLayer.cs b/Il2CppInterop.Generator/ObjectOverridesProcessingLayer.cs new file mode 100644 index 00000000..5bdedff4 --- /dev/null +++ b/Il2CppInterop.Generator/ObjectOverridesProcessingLayer.cs @@ -0,0 +1,124 @@ +using System.Diagnostics; +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; + +namespace Il2CppInterop.Generator; + +public class ObjectOverridesProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Object Overrides Processor"; + public override string Id => "object_overrides_processor"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var systemObject = appContext.SystemTypes.SystemObjectType; + var il2CppSystemIObject = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.IObject"); + + ImplementVirtualMethodsOnIl2CppSystemObject(appContext, systemObject, il2CppSystemIObject); + ImplementVirtualMethodsOnStructsAndEnums(appContext, systemObject, il2CppSystemIObject); + } + + private static void ImplementVirtualMethodsOnIl2CppSystemObject(ApplicationAnalysisContext appContext, TypeAnalysisContext systemObject, TypeAnalysisContext il2CppSystemIObject) + { + var il2CppSystemObject = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Object"); + + (MethodAnalysisContext, MethodAnalysisContext)[] virtualMethods = systemObject.Methods + .Where(m => m.IsVirtual && m.DefaultName is not "Finalize") + .Select(m => + ( + m, + il2CppSystemObject.Methods.Single(i => i.DefaultName == m.DefaultName && !i.IsStatic) + )) + .ToArray(); + + ImplementVirtualMethods(systemObject, il2CppSystemIObject, virtualMethods, il2CppSystemObject, false); + } + + private static void ImplementVirtualMethodsOnStructsAndEnums(ApplicationAnalysisContext appContext, TypeAnalysisContext systemObject, TypeAnalysisContext il2CppSystemIObject) + { + var il2CppSystemValueType = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.ValueType"); + var il2CppSystemEnum = appContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Enum"); + + (MethodAnalysisContext, MethodAnalysisContext)[] virtualMethods = systemObject.Methods + .Where(m => m.IsVirtual && m.DefaultName is not "Finalize") + .Select(m => + ( + m, + il2CppSystemIObject.Methods.Single(i => i.DefaultName == m.DefaultName) + )) + .ToArray(); + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.DefaultBaseType != il2CppSystemEnum && type.DefaultBaseType != il2CppSystemValueType) + continue; + + if (type == il2CppSystemEnum) + continue; + + if (type.IsInjected) + continue; + ImplementVirtualMethods(systemObject, il2CppSystemIObject, virtualMethods, type, true); + } + } + } + + private static void ImplementVirtualMethods(TypeAnalysisContext systemObject, TypeAnalysisContext il2CppSystemIObject, (MethodAnalysisContext, MethodAnalysisContext)[] virtualMethods, TypeAnalysisContext type, bool isValueType) + { + foreach ((var systemMethod, var il2CppMethod) in virtualMethods) + { + // Overrides on value types cannot be sealed + var attributes = isValueType + ? MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig + : MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.HideBySig; + var newMethod = new InjectedMethodAnalysisContext( + type, + systemMethod.DefaultName, + systemMethod.ReturnType, + attributes, + systemMethod.Parameters.Select(p => p.ParameterType).ToArray(), + systemMethod.Parameters.Select(p => p.Name).ToArray(), + systemMethod.Parameters.Select(p => p.Attributes).ToArray()) + { + IsInjected = true, + }; + type.Methods.Add(newMethod); + + List instructions = + [ + new Instruction(CilOpCodes.Ldarg_0), + ]; + if (isValueType) + { + // Value types need to be boxed + var instantiatedType = type.SelfInstantiateIfGeneric(); + instructions.Add(CilOpCodes.Ldobj, instantiatedType); + instructions.Add(CilOpCodes.Box, instantiatedType); + } + for (var i = 0; i < newMethod.Parameters.Count; i++) + { + var parameter = newMethod.Parameters[i]; + instructions.Add(CilOpCodes.Ldarg, parameter); + if (parameter.ParameterType == systemObject) + { + Debug.Assert(il2CppMethod.Parameters[i].ParameterType == il2CppSystemIObject); + instructions.Add(CilOpCodes.Isinst, il2CppSystemIObject); + } + } + instructions.Add(CilOpCodes.Callvirt, il2CppMethod); + instructions.Add(CilOpCodes.Call, il2CppMethod.ReturnType.GetImplicitConversionTo(newMethod.ReturnType)); + instructions.Add(CilOpCodes.Ret); + + newMethod.PutExtraData(new NativeMethodBody() + { + Instructions = instructions + }); + } + } +} diff --git a/Il2CppInterop.Generator/Operands/EndLabel.cs b/Il2CppInterop.Generator/Operands/EndLabel.cs new file mode 100644 index 00000000..bfcf3f5c --- /dev/null +++ b/Il2CppInterop.Generator/Operands/EndLabel.cs @@ -0,0 +1,9 @@ +namespace Il2CppInterop.Generator.Operands; + +public sealed class EndLabel : ILabel +{ + public static readonly EndLabel Instance = new(); + private EndLabel() + { + } +} diff --git a/Il2CppInterop.Generator/Operands/ExceptionHandler.cs b/Il2CppInterop.Generator/Operands/ExceptionHandler.cs new file mode 100644 index 00000000..0dfcc9f4 --- /dev/null +++ b/Il2CppInterop.Generator/Operands/ExceptionHandler.cs @@ -0,0 +1,15 @@ +using AsmResolver.DotNet.Code.Cil; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.Operands; + +public sealed record class ExceptionHandler +{ + public CilExceptionHandlerType HandlerType { get; set; } + public ILabel? TryStart { get; set; } + public ILabel? TryEnd { get; set; } + public ILabel? HandlerStart { get; set; } + public ILabel? HandlerEnd { get; set; } + public ILabel? FilterStart { get; set; } + public TypeAnalysisContext? ExceptionType { get; set; } +} diff --git a/Il2CppInterop.Generator/Operands/ILabel.cs b/Il2CppInterop.Generator/Operands/ILabel.cs new file mode 100644 index 00000000..74699d15 --- /dev/null +++ b/Il2CppInterop.Generator/Operands/ILabel.cs @@ -0,0 +1,5 @@ +namespace Il2CppInterop.Generator.Operands; + +public interface ILabel +{ +} diff --git a/Il2CppInterop.Generator/Operands/Instruction.cs b/Il2CppInterop.Generator/Operands/Instruction.cs new file mode 100644 index 00000000..1bfeb5e0 --- /dev/null +++ b/Il2CppInterop.Generator/Operands/Instruction.cs @@ -0,0 +1,102 @@ +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.Operands; + +public sealed class Instruction : ILabel +{ + public CilOpCode Code { get; set; } + public object? Operand { get; set; } + + public Instruction() + { + } + + public Instruction(CilOpCode code) + { + Code = code; + } + + public Instruction(CilOpCode code, object? operand) + { + Code = code; + Operand = operand; + } + + public override string ToString() => $"{Code} {Operand}"; + + public int GetPopCount(MethodAnalysisContext owner) + { + return Code.StackBehaviourPop switch + { + CilStackBehaviour.Pop0 => 0, + CilStackBehaviour.Pop1 or CilStackBehaviour.PopI or CilStackBehaviour.PopRef => 1, + CilStackBehaviour.Pop1_Pop1 or CilStackBehaviour.PopI_Pop1 or CilStackBehaviour.PopI_PopI or CilStackBehaviour.PopI_PopI8 or CilStackBehaviour.PopI_PopR4 or CilStackBehaviour.PopI_PopR8 or CilStackBehaviour.PopRef_Pop1 or CilStackBehaviour.PopRef_PopI => 2, + CilStackBehaviour.PopRef_PopI_Pop1 or CilStackBehaviour.PopI_PopI_PopI or CilStackBehaviour.PopRef_PopI_PopI or CilStackBehaviour.PopRef_PopI_PopI8 or CilStackBehaviour.PopRef_PopI_PopR4 or CilStackBehaviour.PopRef_PopI_PopR8 or CilStackBehaviour.PopRef_PopI_PopRef => 3, + CilStackBehaviour.VarPop => Code.Code switch + { + CilCode.Ret => IsVoid(owner.ReturnType) ? 0 : 1, + CilCode.Call or CilCode.Callvirt => Operand switch + { + MethodAnalysisContext method => method.Parameters.Count + (method.IsStatic ? 0 : 1), + MultiDimensionalArrayMethod multiArrayMethod => multiArrayMethod.MethodType switch + { + MultiDimensionalArrayMethodType.Get => multiArrayMethod.Rank, + MultiDimensionalArrayMethodType.Set => multiArrayMethod.Rank + 1, + MultiDimensionalArrayMethodType.Address => multiArrayMethod.Rank, + _ => throw new NotImplementedException($"Unhandled multidimensional array method type for {Code.Code}"), + } + 1, + _ => throw new NotImplementedException($"Unhandled operand type for {Code.Code}"), + }, + CilCode.Newobj => Operand switch + { + MethodAnalysisContext method => method.Parameters.Count, + MultiDimensionalArrayMethod multiArrayMethod => multiArrayMethod.MethodType switch + { + MultiDimensionalArrayMethodType.Constructor => multiArrayMethod.Rank, + _ => throw new NotImplementedException($"Unhandled multidimensional array method type for {Code.Code}"), + }, + _ => throw new NotImplementedException($"Unhandled operand type for {Code.Code}"), + }, + _ => throw new NotImplementedException($"Unhandled var pop count for {Code.Code}"), + }, + CilStackBehaviour.PopAll => -1, // Special case for 'pop all' + _ => throw new NotImplementedException($"Unhandled pop count for {Code.StackBehaviourPop}"), + }; + } + + public int GetPushCount(MethodAnalysisContext owner) + { + return Code.StackBehaviourPush switch + { + CilStackBehaviour.Push0 => 0, + CilStackBehaviour.Push1 or CilStackBehaviour.PushI or CilStackBehaviour.PushI8 or CilStackBehaviour.PushR4 or CilStackBehaviour.PushR8 or CilStackBehaviour.PushRef => 1, + CilStackBehaviour.Push1_Push1 => 2, + CilStackBehaviour.VarPush => Code.Code switch + { + CilCode.Call or CilCode.Callvirt => Operand switch + { + MethodAnalysisContext method => IsVoid(method.ReturnType) ? 0 : 1, + MultiDimensionalArrayMethod multiArrayMethod => multiArrayMethod.MethodType switch + { + MultiDimensionalArrayMethodType.Get or MultiDimensionalArrayMethodType.Address => 1, + MultiDimensionalArrayMethodType.Set or MultiDimensionalArrayMethodType.Constructor => 0, + _ => throw new NotImplementedException($"Unhandled multidimensional array method type for {Code.Code}"), + }, + _ => throw new NotImplementedException($"Unhandled operand type for {Code.Code}"), + }, + _ => throw new NotImplementedException($"Unhandled var push count for {Code.Code}"), + }, + _ => throw new NotImplementedException($"Unhandled push count for {Code.StackBehaviourPush}"), + }; + } + + private static bool IsVoid(TypeAnalysisContext type) + { + if (type.Name is not "Void") + { + return false; + } + + return type == type.AppContext.SystemTypes.SystemVoidType || type == type.AppContext.Il2CppMscorlib.GetTypeByFullName("Il2CppSystem.Void"); + } +} diff --git a/Il2CppInterop.Generator/Operands/LocalVariable.cs b/Il2CppInterop.Generator/Operands/LocalVariable.cs new file mode 100644 index 00000000..4e13a78d --- /dev/null +++ b/Il2CppInterop.Generator/Operands/LocalVariable.cs @@ -0,0 +1,19 @@ +using System.Diagnostics.CodeAnalysis; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.Operands; + +public sealed class LocalVariable +{ + public required TypeAnalysisContext Type { get; init; } + + public LocalVariable() + { + } + + [SetsRequiredMembers] + public LocalVariable(TypeAnalysisContext type) + { + Type = type; + } +} diff --git a/Il2CppInterop.Generator/Operands/MultiDimensionalArrayMethod.cs b/Il2CppInterop.Generator/Operands/MultiDimensionalArrayMethod.cs new file mode 100644 index 00000000..2a075c7d --- /dev/null +++ b/Il2CppInterop.Generator/Operands/MultiDimensionalArrayMethod.cs @@ -0,0 +1,35 @@ +using AsmResolver.DotNet; +using AsmResolver.DotNet.Signatures; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Core.Utils.AsmResolver; + +namespace Il2CppInterop.Generator.Operands; + +public sealed record class MultiDimensionalArrayMethod(ArrayTypeAnalysisContext ArrayType, MultiDimensionalArrayMethodType MethodType) +{ + public int Rank => ArrayType.Rank; + + public IMethodDescriptor ToMethodDescriptor(ModuleDefinition module) + { + var arrayTypeSignature = ArrayType.ToTypeSignature(module); + var elementTypeSignature = arrayTypeSignature.BaseType; + var indexParameters = Enumerable.Repeat(module.CorLibTypeFactory.Int32, ArrayType.Rank); + var methodSignature = MethodType switch + { + MultiDimensionalArrayMethodType.Get => MethodSignature.CreateInstance(elementTypeSignature, indexParameters), + MultiDimensionalArrayMethodType.Set => MethodSignature.CreateInstance(module.CorLibTypeFactory.Void, indexParameters.Append(elementTypeSignature)), + MultiDimensionalArrayMethodType.Constructor => MethodSignature.CreateInstance(module.CorLibTypeFactory.Void, indexParameters), + MultiDimensionalArrayMethodType.Address => MethodSignature.CreateInstance(elementTypeSignature.MakeByReferenceType(), indexParameters), + _ => throw new InvalidOperationException($"Unknown {nameof(MultiDimensionalArrayMethodType)}: {MethodType}"), + }; + var methodName = MethodType switch + { + MultiDimensionalArrayMethodType.Get => "Get", + MultiDimensionalArrayMethodType.Set => "Set", + MultiDimensionalArrayMethodType.Constructor => ".ctor", + MultiDimensionalArrayMethodType.Address => "Address", + _ => null, + }; + return new MemberReference(arrayTypeSignature.ToTypeDefOrRef(), methodName, methodSignature); + } +} diff --git a/Il2CppInterop.Generator/Operands/MultiDimensionalArrayMethodType.cs b/Il2CppInterop.Generator/Operands/MultiDimensionalArrayMethodType.cs new file mode 100644 index 00000000..6d3c47da --- /dev/null +++ b/Il2CppInterop.Generator/Operands/MultiDimensionalArrayMethodType.cs @@ -0,0 +1,21 @@ +namespace Il2CppInterop.Generator.Operands; + +public enum MultiDimensionalArrayMethodType +{ + /// + /// call instance bool bool[0..., 0...]::Get(int32, int32) + /// + Get, + /// + /// call instance void bool[0..., 0...]::Set(int32, int32, bool) + /// + Set, + /// + /// newobj instance void bool[0..., 0...]::.ctor(int32, int32) + /// + Constructor, + /// + /// call instance bool& bool[0..., 0...]::Address(int32, int32) + /// + Address, +} diff --git a/Il2CppInterop.Generator/Operands/This.cs b/Il2CppInterop.Generator/Operands/This.cs new file mode 100644 index 00000000..1aced9d7 --- /dev/null +++ b/Il2CppInterop.Generator/Operands/This.cs @@ -0,0 +1,9 @@ +namespace Il2CppInterop.Generator.Operands; + +public sealed record class This +{ + public static This Instance { get; } = new This(); + private This() + { + } +} diff --git a/Il2CppInterop.Generator/OriginalMethodBody.cs b/Il2CppInterop.Generator/OriginalMethodBody.cs new file mode 100644 index 00000000..cdce37bc --- /dev/null +++ b/Il2CppInterop.Generator/OriginalMethodBody.cs @@ -0,0 +1,157 @@ +using System.Diagnostics.CodeAnalysis; +using AsmResolver.DotNet; +using AsmResolver.DotNet.Code.Cil; +using AsmResolver.DotNet.Collections; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; + +namespace Il2CppInterop.Generator; + +public class OriginalMethodBody : MethodBodyBase +{ + public static bool MaybeStoreOriginalMethodBody(MethodDefinition originalMethod, MethodAnalysisContext methodContext, RuntimeContext runtimeContext) + { + if (TryResolveOriginalMethodBody(originalMethod, methodContext, runtimeContext, out var originalMethodBody)) + { + methodContext.PutExtraData(originalMethodBody); + return true; + } + else + { + return false; + } + } + + private static bool TryResolveOriginalMethodBody(MethodDefinition method, MethodAnalysisContext methodContext, RuntimeContext runtimeContext, [NotNullWhen(true)] out OriginalMethodBody? originalMethodBody) + { + var body = method.CilMethodBody; + if (body is null) + { + originalMethodBody = null; + return false; + } + + var resolver = new ContextResolver(methodContext, runtimeContext); + + var originalInstructions = body.Instructions; + originalInstructions.ExpandMacrosBackport(); + + var newInstructions = new Instruction[originalInstructions.Count]; + for (var i = 0; i < newInstructions.Length; i++) + { + newInstructions[i] = new Instruction + { + Code = originalInstructions[i].OpCode, + }; + } + + var newLocalVariables = new LocalVariable[body.LocalVariables.Count]; + for (var i = 0; i < newLocalVariables.Length; i++) + { + var localVariable = body.LocalVariables[i]; + var localVariableType = resolver.Resolve(localVariable.VariableType); + if (localVariableType is null) + { + originalMethodBody = default; + return false; + } + newLocalVariables[i] = new LocalVariable + { + Type = localVariableType, + }; + } + + var newExceptionHandlers = new ExceptionHandler[body.ExceptionHandlers.Count]; + for (var i = 0; i < newExceptionHandlers.Length; i++) + { + var exceptionHandler = body.ExceptionHandlers[i]; + var handlerType = exceptionHandler.HandlerType; + + var tryStart = ResolveLabel(newInstructions, originalInstructions, exceptionHandler.TryStart); + var tryEnd = ResolveLabel(newInstructions, originalInstructions, exceptionHandler.TryEnd); + var handlerStart = ResolveLabel(newInstructions, originalInstructions, exceptionHandler.HandlerStart); + var handlerEnd = ResolveLabel(newInstructions, originalInstructions, exceptionHandler.HandlerEnd); + var filterStart = ResolveLabel(newInstructions, originalInstructions, exceptionHandler.FilterStart); + TypeAnalysisContext? exceptionType; + if (exceptionHandler.ExceptionType is null) + { + exceptionType = null; + } + else + { + exceptionType = resolver.Resolve(exceptionHandler.ExceptionType); + if (exceptionType is null) + { + originalMethodBody = default; + return false; + } + } + newExceptionHandlers[i] = new ExceptionHandler + { + HandlerType = handlerType, + TryStart = tryStart, + TryEnd = tryEnd, + HandlerStart = handlerStart, + HandlerEnd = handlerEnd, + FilterStart = filterStart, + ExceptionType = exceptionType, + }; + } + + for (var i = 0; i < originalInstructions.Count; i++) + { + var instruction = originalInstructions[i]; + var operand = instruction.Operand; + var resolved = operand switch + { + null or byte or sbyte or short or ushort or int or uint or long or ulong or float or double or bool or char => operand, + string @string => @string, + AsmResolver.Utf8String utf8String => utf8String.ToString(), + CilLocalVariable localVariable => newLocalVariables[localVariable.Index], + Parameter parameter => parameter == method.Parameters.ThisParameter ? This.Instance : methodContext.Parameters[parameter.Index], + ITypeDescriptor typeDescriptor => resolver.Resolve(typeDescriptor), + IFieldDescriptor { Signature: not null } fieldDescriptor => resolver.Resolve(fieldDescriptor), + IMethodDescriptor { Signature: not null } methodDescriptor => resolver.Resolve(methodDescriptor), + ICilLabel label => ResolveLabel(newInstructions, originalInstructions, label), + IReadOnlyList labels => ResolveOperand(labels, originalInstructions, newInstructions), + StandAloneSignature => null,// Not currently supported + _ => null, + }; + if (resolved is null && operand is not null) + { + originalMethodBody = default; + return false; + } + newInstructions[i].Operand = resolved; + } + + originalMethodBody = new OriginalMethodBody + { + Instructions = newInstructions, + LocalVariables = newLocalVariables, + ExceptionHandlers = newExceptionHandlers, + }; + return true; + } + + private static ILabel[] ResolveOperand(IReadOnlyList labels, CilInstructionCollection originalInstructions, Instruction[] newInstructions) + { + var resolved = new ILabel[labels.Count]; + for (var i = 0; i < labels.Count; i++) + { + resolved[i] = ResolveLabel(newInstructions, originalInstructions, labels[i]); + } + return resolved; + } + + [return: NotNullIfNotNull(nameof(label))] + private static ILabel? ResolveLabel(Instruction[] newInstructions, CilInstructionCollection originalInstructions, ICilLabel? label) => label switch + { + null => null, + CilInstructionLabel instructionLabel => instructionLabel.Instruction is not null + ? newInstructions[originalInstructions.IndexOf(instructionLabel.Instruction)] + : throw new ArgumentException("Instruction label must reference an instruction", nameof(label)), + _ when label.GetType().Name == "CilEndLabel" => EndLabel.Instance, + _ => throw new ArgumentException($"Label is an unsupported type: {label.GetType()}", nameof(label)), + }; +} diff --git a/Il2CppInterop.Generator/Passes/Pass05CreateRenameGroups.cs b/Il2CppInterop.Generator/Passes/Pass05CreateRenameGroups.cs deleted file mode 100644 index e48edbc3..00000000 --- a/Il2CppInterop.Generator/Passes/Pass05CreateRenameGroups.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System.Text; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass05CreateRenameGroups -{ - private static readonly string[] ClassAccessNames = - {"Private", "Public", "NPublic", "NPrivate", "NProtected", "NInternal", "NFamAndAssem", "NFamOrAssem"}; - - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - foreach (var originalType in assemblyContext.OriginalAssembly.ManifestModule!.TopLevelTypes) - ProcessType(context, originalType, false); - - var typesToRemove = context.RenameGroups.Where(it => it.Value.Count > 1).ToList(); - foreach (var keyValuePair in typesToRemove) - { - context.RenameGroups.Remove(keyValuePair.Key); - foreach (var typeDefinition in keyValuePair.Value) - context.RenamedTypes.Remove(typeDefinition); - } - - foreach (var contextRenamedType in context.RenamedTypes) - context.PreviousRenamedTypes[contextRenamedType.Key] = contextRenamedType.Value; - - foreach (var assemblyContext in context.Assemblies) - foreach (var originalType in assemblyContext.OriginalAssembly.ManifestModule!.TopLevelTypes) - ProcessType(context, originalType, true); - } - - private static void ProcessType(RewriteGlobalContext context, TypeDefinition originalType, - bool allowExtraHeuristics) - { - foreach (var nestedType in originalType.NestedTypes) - ProcessType(context, nestedType, allowExtraHeuristics); - - if (context.RenamedTypes.ContainsKey(originalType)) return; - - var unobfuscatedName = GetUnobfuscatedNameBase(context, originalType, allowExtraHeuristics); - if (unobfuscatedName == null) return; - - context.RenameGroups - .GetOrCreate( - ((object?)originalType.DeclaringType ?? originalType.Namespace, unobfuscatedName, - originalType.GenericParameters.Count), _ => new List()).Add(originalType); - context.RenamedTypes[originalType] = unobfuscatedName; - } - - private static string? GetUnobfuscatedNameBase(RewriteGlobalContext context, TypeDefinition typeDefinition, - bool allowExtraHeuristics) - { - var options = context.Options; - if (options.PassthroughNames || !typeDefinition.Name.IsObfuscated(context.Options)) return null; - - var inheritanceDepth = 0; - var firstUnobfuscatedType = typeDefinition.BaseType; - while (firstUnobfuscatedType != null && firstUnobfuscatedType.Name.IsObfuscated(context.Options)) - { - firstUnobfuscatedType = firstUnobfuscatedType.Resolve()?.BaseType?.Resolve(); - inheritanceDepth++; - } - - var unobfuscatedInterfacesList = typeDefinition.Interfaces.Select(it => it.Interface!) - .Where(it => !it!.Name.IsObfuscated(context.Options)); - var accessName = ClassAccessNames[(int)(typeDefinition.Attributes & TypeAttributes.VisibilityMask)]; - - var classifier = typeDefinition.IsInterface ? "Interface" : typeDefinition.IsValueType() ? "Struct" : "Class"; - var compilerGenertaedString = typeDefinition.Name.StartsWith("<") ? "CompilerGenerated" : ""; - var abstractString = typeDefinition.IsAbstract ? "Abstract" : ""; - var sealedString = typeDefinition.IsSealed ? "Sealed" : ""; - var specialNameString = typeDefinition.IsSpecialName ? "SpecialName" : ""; - - var nameBuilder = new StringBuilder(); - nameBuilder.Append(firstUnobfuscatedType?.ToTypeSignature().GenericNameToStrings(context)?.ConcatAll() ?? classifier); - if (inheritanceDepth > 0) - nameBuilder.Append(inheritanceDepth); - nameBuilder.Append(compilerGenertaedString); - nameBuilder.Append(accessName); - nameBuilder.Append(abstractString); - nameBuilder.Append(sealedString); - nameBuilder.Append(specialNameString); - foreach (var interfaceRef in unobfuscatedInterfacesList) - nameBuilder.Append(interfaceRef.ToTypeSignature().GenericNameToStrings(context).ConcatAll()); - - var uniqContext = new UniquificationContext(options); - foreach (var fieldDef in typeDefinition.Fields) - { - if (!typeDefinition.IsEnum) - uniqContext.Push(fieldDef.Signature!.FieldType.GenericNameToStrings(context)); - - uniqContext.Push(fieldDef.Name!); - - if (uniqContext.CheckFull()) break; - } - - if (typeDefinition.IsEnum) - uniqContext.Push(typeDefinition.Fields.Count + "v"); - - foreach (var propertyDef in typeDefinition.Properties) - { - uniqContext.Push(propertyDef.Signature!.ReturnType.GenericNameToStrings(context)); - uniqContext.Push(propertyDef.Name!); - - if (uniqContext.CheckFull()) break; - } - - if (firstUnobfuscatedType?.Name == "MulticastDelegate") - { - var invokeMethod = typeDefinition.Methods.SingleOrDefault(it => it.Name == "Invoke"); - if (invokeMethod != null) - { - uniqContext.Push(invokeMethod.Signature!.ReturnType.GenericNameToStrings(context)); - - foreach (var parameterDef in invokeMethod.Parameters) - { - uniqContext.Push(parameterDef.ParameterType.GenericNameToStrings(context)); - if (uniqContext.CheckFull()) break; - } - } - } - - if (typeDefinition.IsInterface || - allowExtraHeuristics) // method order on non-interface types appears to be unstable - foreach (var methodDefinition in typeDefinition.Methods) - { - uniqContext.Push(methodDefinition.Name!); - uniqContext.Push(methodDefinition.Signature!.ReturnType.GenericNameToStrings(context)); - - foreach (var parameter in methodDefinition.Parameters) - { - uniqContext.Push(parameter.Name); - uniqContext.Push(parameter.ParameterType.GenericNameToStrings(context)); - - if (uniqContext.CheckFull()) break; - } - - if (uniqContext.CheckFull()) break; - } - - nameBuilder.Append(uniqContext.GetTop()); - - return nameBuilder.ToString(); - } - - private static string ConcatAll(this List strings) - { - return string.Concat(strings); - } - - private static string NameOrRename(this TypeSignature typeRef, RewriteGlobalContext context) - { - var resolved = typeRef.Resolve(); - if (resolved != null && context.PreviousRenamedTypes.TryGetValue(resolved, out var rename)) - return (rename.StableHash() % (ulong)Math.Pow(10, context.Options.TypeDeobfuscationCharsPerUniquifier)) - .ToString(); - - return typeRef.Name!; - } - - private static List GenericNameToStrings(this TypeSignature typeRef, RewriteGlobalContext context) - { - if (typeRef is SzArrayTypeSignature szArrayType) - return szArrayType.BaseType.GenericNameToStrings(context); - - if (typeRef is ArrayTypeSignature arrayType) - return arrayType.BaseType.GenericNameToStrings(context); - - if (typeRef is GenericInstanceTypeSignature genericInstance) - { - var baseTypeName = genericInstance.GenericType.ToTypeSignature().NameOrRename(context); - var indexOfBacktick = baseTypeName.IndexOf('`'); - if (indexOfBacktick >= 0) - baseTypeName = baseTypeName.Substring(0, indexOfBacktick); - - var entries = new List(); - - entries.Add(baseTypeName); - entries.Add(genericInstance.TypeArguments.Count.ToString()); - foreach (var genericArgument in genericInstance.TypeArguments) - entries.AddRange(genericArgument.GenericNameToStrings(context)); - return entries; - } - - if (typeRef.NameOrRename(context).IsObfuscated(context.Options)) - return new List { "Obf" }; - - return new List { typeRef.NameOrRename(context) }; - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass10CreateTypedefs.cs b/Il2CppInterop.Generator/Passes/Pass10CreateTypedefs.cs deleted file mode 100644 index f3c277d0..00000000 --- a/Il2CppInterop.Generator/Passes/Pass10CreateTypedefs.cs +++ /dev/null @@ -1,118 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Common; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass10CreateTypedefs -{ - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - foreach (var type in assemblyContext.OriginalAssembly.ManifestModule!.TopLevelTypes) - if (!IsCpp2ILInjectedType(type) && type.Name != "") - ProcessType(type, assemblyContext, null); - - static bool IsCpp2ILInjectedType(TypeDefinition type) => type.Namespace?.Value.StartsWith("Cpp2ILInjected", StringComparison.Ordinal) ?? false; - } - - private static void ProcessType(TypeDefinition type, AssemblyRewriteContext assemblyContext, - TypeDefinition? parentType) - { - var convertedTypeName = GetConvertedTypeName(assemblyContext.GlobalContext, type, parentType); - var newType = - new TypeDefinition( - convertedTypeName.Namespace ?? GetNamespace(type, assemblyContext), - convertedTypeName.Name, AdjustAttributes(type.Attributes)); - newType.IsSequentialLayout = false; - - if (type.IsSealed && type.IsAbstract) // is static - newType.IsSealed = newType.IsAbstract = true; - - if (parentType == null) - { - assemblyContext.NewAssembly.ManifestModule!.TopLevelTypes.Add(newType); - } - else - { - parentType.NestedTypes.Add(newType); - } - - foreach (var typeNestedType in type.NestedTypes) - ProcessType(typeNestedType, assemblyContext, newType); - - assemblyContext.RegisterTypeRewrite(new TypeRewriteContext(assemblyContext, type, newType)); - - static string? GetNamespace(TypeDefinition type, AssemblyRewriteContext assemblyContext) - { - if (type.Name?.Value is "" || type.DeclaringType is not null) - return type.Namespace; - else - return type.Namespace.UnSystemify(assemblyContext.GlobalContext.Options); - } - } - - internal static (string? Namespace, string Name) GetConvertedTypeName( - RewriteGlobalContext assemblyContextGlobalContext, TypeDefinition type, TypeDefinition? enclosingType) - { - if (assemblyContextGlobalContext.Options.PassthroughNames) - return (null, type.Name!); - - if (type.Name.IsObfuscated(assemblyContextGlobalContext.Options)) - { - var newNameBase = assemblyContextGlobalContext.RenamedTypes[type]; - var genericParametersCount = type.GenericParameters.Count; - var renameGroup = - assemblyContextGlobalContext.RenameGroups[ - ((object?)type.DeclaringType ?? type.Namespace, newNameBase, genericParametersCount)]; - var genericSuffix = genericParametersCount == 0 ? "" : "`" + genericParametersCount; - var convertedTypeName = newNameBase + - (renameGroup.Count == 1 ? "Unique" : renameGroup.IndexOf(type).ToString()) + - genericSuffix; - - var fullName = enclosingType == null - ? type.Namespace - : enclosingType.GetNamespacePrefix() + "." + enclosingType.Name; - - if (assemblyContextGlobalContext.Options.RenameMap.TryGetValue(fullName + "." + convertedTypeName, - out var newName)) - { - if (type.DeclaringModule!.TopLevelTypes.Any(t => t.FullName == newName)) - { - Logger.Instance.LogWarning("[Rename map issue] {NewName} already exists in {ModuleName} (mapped from {MappedNamespace}.{MappedType})", - newName, type.DeclaringModule.Name, fullName, convertedTypeName); - newName += "_Duplicate"; - } - - var lastDotPosition = newName.LastIndexOf("."); - if (lastDotPosition >= 0) - { - var ns = newName.Substring(0, lastDotPosition); - var name = newName.Substring(lastDotPosition + 1); - return (ns, name); - } - - convertedTypeName = newName; - } - - return (null, convertedTypeName); - } - - return (null, type.Name.MakeValidInSource()); - } - - private static TypeAttributes AdjustAttributes(TypeAttributes typeAttributes) - { - typeAttributes |= TypeAttributes.BeforeFieldInit; - typeAttributes &= ~(TypeAttributes.Abstract | TypeAttributes.Interface); - - var visibility = typeAttributes & TypeAttributes.VisibilityMask; - if (visibility == 0 || visibility == TypeAttributes.Public) - return typeAttributes | TypeAttributes.Public; - - return (typeAttributes & ~TypeAttributes.VisibilityMask) | TypeAttributes.NestedPublic; - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass11ComputeTypeSpecifics.cs b/Il2CppInterop.Generator/Passes/Pass11ComputeTypeSpecifics.cs deleted file mode 100644 index b50cd2e8..00000000 --- a/Il2CppInterop.Generator/Passes/Pass11ComputeTypeSpecifics.cs +++ /dev/null @@ -1,53 +0,0 @@ -using AsmResolver.DotNet.Signatures; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass11ComputeTypeSpecifics -{ - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - foreach (var typeContext in assemblyContext.Types) - ComputeSpecifics(typeContext); - } - - private static void ComputeSpecifics(TypeRewriteContext typeContext) - { - if (typeContext.ComputedTypeSpecifics != TypeRewriteContext.TypeSpecifics.NotComputed) return; - typeContext.ComputedTypeSpecifics = TypeRewriteContext.TypeSpecifics.Computing; - - foreach (var originalField in typeContext.OriginalType.Fields) - { - // Sometimes il2cpp metadata has invalid field offsets for some reason (https://github.com/SamboyCoding/Cpp2IL/issues/167) - if (originalField.ExtractFieldOffset() >= 0x8000000) - { - typeContext.ComputedTypeSpecifics = TypeRewriteContext.TypeSpecifics.NonBlittableStruct; - return; - } - - if (originalField.IsStatic) continue; - - var fieldType = originalField.Signature!.FieldType; - if (fieldType.IsPrimitive() || fieldType is PointerTypeSignature) - continue; - if (fieldType.FullName == "System.String" || fieldType.FullName == "System.Object" - || fieldType is ArrayBaseTypeSignature or ByReferenceTypeSignature or GenericParameterSignature or GenericInstanceTypeSignature) - { - typeContext.ComputedTypeSpecifics = TypeRewriteContext.TypeSpecifics.NonBlittableStruct; - return; - } - - var fieldTypeContext = typeContext.AssemblyContext.GlobalContext.GetNewTypeForOriginal(fieldType.Resolve()!); - ComputeSpecifics(fieldTypeContext); - if (fieldTypeContext.ComputedTypeSpecifics != TypeRewriteContext.TypeSpecifics.BlittableStruct) - { - typeContext.ComputedTypeSpecifics = TypeRewriteContext.TypeSpecifics.NonBlittableStruct; - return; - } - } - - typeContext.ComputedTypeSpecifics = TypeRewriteContext.TypeSpecifics.BlittableStruct; - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass12FillTypedefs.cs b/Il2CppInterop.Generator/Passes/Pass12FillTypedefs.cs deleted file mode 100644 index 39b9f5b1..00000000 --- a/Il2CppInterop.Generator/Passes/Pass12FillTypedefs.cs +++ /dev/null @@ -1,35 +0,0 @@ -using AsmResolver.DotNet; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass12FillTypedefs -{ - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - foreach (var typeContext in assemblyContext.Types) - { - foreach (var originalParameter in typeContext.OriginalType.GenericParameters) - { - var newParameter = new GenericParameter(originalParameter.Name.MakeValidInSource(), - originalParameter.Attributes.StripValueTypeConstraint()); - typeContext.NewType.GenericParameters.Add(newParameter); - } - - if (typeContext.OriginalType.IsEnum) - typeContext.NewType.BaseType = assemblyContext.Imports.Module.Enum().ToTypeDefOrRef(); - else if (typeContext.ComputedTypeSpecifics == TypeRewriteContext.TypeSpecifics.BlittableStruct) - typeContext.NewType.BaseType = assemblyContext.Imports.Module.ValueType().ToTypeDefOrRef(); - } - - // Second pass is explicitly done after first to account for rewriting of generic base types - value-typeness is important there - foreach (var assemblyContext in context.Assemblies) - foreach (var typeContext in assemblyContext.Types) - if (!typeContext.OriginalType.IsEnum && typeContext.ComputedTypeSpecifics != - TypeRewriteContext.TypeSpecifics.BlittableStruct) - typeContext.NewType.BaseType = assemblyContext.RewriteTypeRef(typeContext.OriginalType.BaseType!); - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass13FillGenericConstraints.cs b/Il2CppInterop.Generator/Passes/Pass13FillGenericConstraints.cs deleted file mode 100644 index 58451af2..00000000 --- a/Il2CppInterop.Generator/Passes/Pass13FillGenericConstraints.cs +++ /dev/null @@ -1,40 +0,0 @@ -using AsmResolver.DotNet; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass13FillGenericConstraints -{ - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - { - foreach (var typeContext in assemblyContext.Types) - { - for (var i = 0; i < typeContext.OriginalType.GenericParameters.Count; i++) - { - var originalParameter = typeContext.OriginalType.GenericParameters[i]; - var newParameter = typeContext.NewType.GenericParameters[i]; - foreach (var originalConstraint in originalParameter.Constraints) - { - if (originalConstraint.IsSystemValueType() || originalConstraint.IsInterface()) - continue; - - if (originalConstraint.IsSystemEnum()) - { - newParameter.Constraints.Add(new GenericParameterConstraint( - typeContext.AssemblyContext.Imports.Module.Enum().ToTypeDefOrRef())); - continue; - } - - newParameter.Constraints.Add( - new GenericParameterConstraint( - assemblyContext.RewriteTypeRef(originalConstraint.Constraint!))); - } - } - } - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass15GenerateMemberContexts.cs b/Il2CppInterop.Generator/Passes/Pass15GenerateMemberContexts.cs deleted file mode 100644 index 49fcb6ff..00000000 --- a/Il2CppInterop.Generator/Passes/Pass15GenerateMemberContexts.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Il2CppInterop.Generator.Contexts; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass15GenerateMemberContexts -{ - public static bool HasObfuscatedMethods; - - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - foreach (var typeContext in assemblyContext.Types) - typeContext.AddMembers(); - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass16ScanMethodRefs.cs b/Il2CppInterop.Generator/Passes/Pass16ScanMethodRefs.cs deleted file mode 100644 index f16a3541..00000000 --- a/Il2CppInterop.Generator/Passes/Pass16ScanMethodRefs.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System.Collections.Concurrent; -using System.IO.MemoryMappedFiles; -using System.Text; -using Il2CppInterop.Common.XrefScans; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass16ScanMethodRefs -{ - internal static HashSet NonDeadMethods = new(); - internal static IDictionary> MapOfCallers = new Dictionary>(); - - public static void DoPass(RewriteGlobalContext context, GeneratorOptions options) - { - if (string.IsNullOrEmpty(options.GameAssemblyPath)) - { - Pass15GenerateMemberContexts.HasObfuscatedMethods = false; - return; - } - - using var mappedFile = MemoryMappedFile.CreateFromFile(options.GameAssemblyPath, FileMode.Open, null, 0, - MemoryMappedFileAccess.Read); - using var accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); - - nint gameAssemblyPtr; - - unsafe - { - byte* fileStartPtr = null; - accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref fileStartPtr); - gameAssemblyPtr = (nint)fileStartPtr; - } - - context.HasGcWbarrierFieldWrite = - FindByteSequence(gameAssemblyPtr, accessor.Capacity, "il2cpp_gc_wbarrier_set_field"); - - if (!Pass15GenerateMemberContexts.HasObfuscatedMethods) return; - - var methodToCallersMap = new ConcurrentDictionary>(); - var methodToCalleesMap = new ConcurrentDictionary>(); - - context.MethodStartAddresses.Sort(); - - // Scan xrefs - context.Assemblies.SelectMany(it => it.Types).SelectMany(it => it.Methods).AsParallel().ForAll( - originalTypeMethod => - { - var address = originalTypeMethod.FileOffset; - if (address == 0) return; - - if (!options.NoXrefCache) - { - var pair = XrefScanMetadataGenerationUtil.FindMetadataInitForMethod(originalTypeMethod, gameAssemblyPtr); - originalTypeMethod.MetadataInitFlagRva = pair.FlagRva; - originalTypeMethod.MetadataInitTokenRva = pair.TokenRva; - } - - var nextMethodStart = context.MethodStartAddresses.BinarySearch(address + 1); - if (nextMethodStart < 0) nextMethodStart = ~nextMethodStart; - var length = nextMethodStart >= context.MethodStartAddresses.Count - ? 1024 * 1024 - : context.MethodStartAddresses[nextMethodStart] - address; - foreach (var callTargetGlobal in XrefScanner.XrefScanImpl( - XrefScanner.DecoderForAddress(IntPtr.Add(gameAssemblyPtr, (int)address), (int)length), - true)) - { - var callTarget = callTargetGlobal.RelativeToBase(gameAssemblyPtr + (nint)originalTypeMethod.FileOffset - (nint)originalTypeMethod.Rva); - if (callTarget.Type == XrefType.Method) - { - var targetRelative = callTarget.Pointer; - methodToCallersMap.GetOrAdd(targetRelative, _ => new List()).AddLocked( - new XrefInstance(XrefType.Method, (nint)originalTypeMethod.Rva, callTarget.FoundAt)); - methodToCalleesMap.GetOrAdd(originalTypeMethod.Rva, _ => new List()) - .AddLocked(targetRelative); - } - - if (!options.NoXrefCache) - originalTypeMethod.XrefScanResults.Add(callTarget); - } - }); - - MapOfCallers = methodToCallersMap; - - void MarkMethodAlive(long address) - { - if (!NonDeadMethods.Add(address)) return; - if (!methodToCalleesMap.TryGetValue(address, out var calleeList)) return; - - foreach (var callee in calleeList) - MarkMethodAlive(callee); - } - - // Now decided which of them are possible dead code - foreach (var assemblyRewriteContext in context.Assemblies) - foreach (var typeRewriteContext in assemblyRewriteContext.Types) - foreach (var methodRewriteContext in typeRewriteContext.Methods) - { - if (methodRewriteContext.FileOffset == 0) continue; - - var originalMethod = methodRewriteContext.OriginalMethod; - if (!originalMethod.Name.IsObfuscated(options) || originalMethod.IsVirtual) - MarkMethodAlive(methodRewriteContext.Rva); - } - } - - private static unsafe bool FindByteSequence(nint basePtr, long length, string str) - { - var bytes = (byte*)basePtr; - var sequence = Encoding.UTF8.GetBytes(str); - for (var i = 0L; i < length; i++) - { - for (var j = 0; j < sequence.Length; j++) - if (bytes[i + j] != sequence[j]) - goto next; - - return true; - - next:; - } - - return false; - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass18FinalizeMethodContexts.cs b/Il2CppInterop.Generator/Passes/Pass18FinalizeMethodContexts.cs deleted file mode 100644 index 319ba1d1..00000000 --- a/Il2CppInterop.Generator/Passes/Pass18FinalizeMethodContexts.cs +++ /dev/null @@ -1,67 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using Il2CppInterop.Common; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Utils; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass18FinalizeMethodContexts -{ - public static int TotalPotentiallyDeadMethods; - - public static void DoPass(RewriteGlobalContext context) - { - var pdmNested0Caller = 0; - var pdmNestedNZCaller = 0; - var pdmTop0Caller = 0; - var pdmTopNZCaller = 0; - - foreach (var assemblyContext in context.Assemblies) - { - foreach (var typeContext in assemblyContext.Types) - { - foreach (var methodContext in typeContext.Methods) - { - methodContext.CtorPhase2(); - - if (Pass15GenerateMemberContexts.HasObfuscatedMethods) - { - var callerCount = 0; - if (Pass16ScanMethodRefs.MapOfCallers.TryGetValue(methodContext.Rva, out var callers)) - callerCount = callers.Count; - - methodContext.NewMethod.CustomAttributes.Add( - new CustomAttribute((ICustomAttributeType)assemblyContext.Imports.CallerCountAttributector.Value, new CustomAttributeSignature(new CustomAttributeArgument(assemblyContext.Imports.Module.Int(), callerCount)))); - - if (!Pass15GenerateMemberContexts.HasObfuscatedMethods) continue; - if (methodContext.UnmangledName?.Contains("_PDM_") is not true) continue; - TotalPotentiallyDeadMethods++; - - var hasZeroCallers = callerCount == 0; - if (methodContext.DeclaringType.OriginalType.IsNested) - { - if (hasZeroCallers) - pdmNested0Caller++; - else - pdmNestedNZCaller++; - } - else - { - if (hasZeroCallers) - pdmTop0Caller++; - else - pdmTopNZCaller++; - } - } - } - } - } - - if (Pass15GenerateMemberContexts.HasObfuscatedMethods) - { - Logger.Instance.LogTrace("Dead method statistics: 0t={Top0Caller} mt={TopNZCaller} 0n={Nested0Caller} mn={NestedNZCaller}", pdmTop0Caller, pdmTopNZCaller, pdmNested0Caller, pdmNestedNZCaller); - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass19CopyMethodParameters.cs b/Il2CppInterop.Generator/Passes/Pass19CopyMethodParameters.cs deleted file mode 100644 index 709dd202..00000000 --- a/Il2CppInterop.Generator/Passes/Pass19CopyMethodParameters.cs +++ /dev/null @@ -1,49 +0,0 @@ -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass19CopyMethodParameters -{ - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - { - foreach (var typeContext in assemblyContext.Types) - { - foreach (var methodRewriteContext in typeContext.Methods) - { - var originalMethod = methodRewriteContext.OriginalMethod; - var newMethod = methodRewriteContext.NewMethod; - - foreach (var originalMethodParameter in originalMethod.Parameters) - { - var newName = originalMethodParameter.Name.IsObfuscated(context.Options) - ? $"param_{originalMethodParameter.Sequence}" - : originalMethodParameter.Name; - - var newParameter = newMethod.AddParameter( - assemblyContext.RewriteTypeRef(originalMethodParameter.ParameterType), - newName, - originalMethodParameter.GetOrCreateDefinition().Attributes & ~ParameterAttributes.HasFieldMarshal); - - if (originalMethodParameter.IsParamsArray()) - { - newParameter.Definition!.Constant = null; - newParameter.Definition.IsOptional = true; - } - else - { - newParameter.Definition!.Constant = originalMethodParameter.Definition!.Constant; - } - } - - var paramsMethod = context.CreateParamsMethod(originalMethod, newMethod, assemblyContext.Imports, - type => assemblyContext.RewriteTypeRef(type)); - if (paramsMethod != null) typeContext.NewType.Methods.Add(paramsMethod); - } - } - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass20GenerateStaticConstructors.cs b/Il2CppInterop.Generator/Passes/Pass20GenerateStaticConstructors.cs deleted file mode 100644 index f71c60ae..00000000 --- a/Il2CppInterop.Generator/Passes/Pass20GenerateStaticConstructors.cs +++ /dev/null @@ -1,180 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using Il2CppInterop.Common; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass20GenerateStaticConstructors -{ - private static int ourTokenlessMethods; - - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - foreach (var typeContext in assemblyContext.Types) - GenerateStaticProxy(assemblyContext, typeContext); - - Logger.Instance.LogTrace("Tokenless method count: {TokenlessMethodCount}", ourTokenlessMethods); - } - - private static void GenerateStaticProxy(AssemblyRewriteContext assemblyContext, TypeRewriteContext typeContext) - { - var oldType = typeContext.OriginalType; - var newType = typeContext.NewType; - if (newType.IsEnum) return; - - var staticCtorMethod = newType.GetOrCreateStaticConstructor(); - - var ctorBuilder = staticCtorMethod.CilMethodBody!.Instructions; - ctorBuilder.Clear(); - - if (newType.IsNested) - { - ctorBuilder.Add(OpCodes.Ldsfld, - assemblyContext.GlobalContext.GetNewTypeForOriginal(oldType.DeclaringType!).ClassPointerFieldRef); - ctorBuilder.Add(OpCodes.Ldstr, oldType.Name ?? ""); - ctorBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_GetIl2CppNestedType.Value); - } - else - { - ctorBuilder.Add(OpCodes.Ldstr, oldType.DeclaringModule?.Name ?? ""); - ctorBuilder.Add(OpCodes.Ldstr, oldType.Namespace ?? ""); - ctorBuilder.Add(OpCodes.Ldstr, oldType.Name ?? ""); - ctorBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_GetIl2CppClass.Value); - } - - if (oldType.HasGenericParameters()) - { - var il2CppTypeTypeRewriteContext = assemblyContext.GlobalContext.GetAssemblyByName("mscorlib") - .GetTypeByName("System.Type"); - var il2CppSystemTypeRef = newType.DeclaringModule!.DefaultImporter.ImportType(il2CppTypeTypeRewriteContext.NewType); - - var il2CppTypeHandleTypeRewriteContext = assemblyContext.GlobalContext.GetAssemblyByName("mscorlib") - .GetTypeByName("System.RuntimeTypeHandle"); - var il2CppSystemTypeHandleRef = newType.DeclaringModule.DefaultImporter.ImportType(il2CppTypeHandleTypeRewriteContext.NewType); - - ctorBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_il2cpp_class_get_type.Value); - ctorBuilder.Add(OpCodes.Call, - new MemberReference(il2CppSystemTypeRef, "internal_from_handle", MethodSignature.CreateStatic(il2CppSystemTypeRef.ToTypeSignature(), assemblyContext.Imports.Module.IntPtr()))); - - ctorBuilder.Add(OpCodes.Ldc_I4, oldType.GenericParameters.Count); - - ctorBuilder.Add(OpCodes.Newarr, il2CppSystemTypeRef); - - for (var i = 0; i < oldType.GenericParameters.Count; i++) - { - ctorBuilder.Add(OpCodes.Dup); - ctorBuilder.Add(OpCodes.Ldc_I4, i); - - var param = oldType.GenericParameters[i]; - var storeRef = assemblyContext.Imports.Il2CppClassPointerStore - .MakeGenericInstanceType(new GenericParameterSignature(GenericParameterType.Type, param.Number)); - var fieldRef = new MemberReference(storeRef.ToTypeDefOrRef(), "NativeClassPtr", new FieldSignature(assemblyContext.Imports.Module.IntPtr())); - ctorBuilder.Add(OpCodes.Ldsfld, fieldRef); - - ctorBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_il2cpp_class_get_type.Value); - - ctorBuilder.Add(OpCodes.Call, - new MemberReference(il2CppSystemTypeRef, "internal_from_handle", MethodSignature.CreateStatic(il2CppSystemTypeRef.ToTypeSignature(), assemblyContext.Imports.Module.IntPtr()))); - ctorBuilder.Add(OpCodes.Stelem_Ref); - } - - var il2CppTypeArray = assemblyContext.Imports.Il2CppReferenceArray.MakeGenericInstanceType(il2CppSystemTypeRef.ToTypeSignature()); - ctorBuilder.Add(OpCodes.Newobj, - new MemberReference(il2CppTypeArray.ToTypeDefOrRef(), ".ctor", MethodSignature.CreateInstance(assemblyContext.Imports.Module.Void(), new GenericParameterSignature(GenericParameterType.Type, 0).MakeSzArrayType()))); - ctorBuilder.Add(OpCodes.Call, - ReferenceCreator.CreateInstanceMethodReference(nameof(Type.MakeGenericType), il2CppSystemTypeRef.ToTypeSignature(), il2CppSystemTypeRef, il2CppTypeArray)); - - ctorBuilder.Add(OpCodes.Call, - ReferenceCreator.CreateInstanceMethodReference(typeof(Type).GetProperty(nameof(Type.TypeHandle))!.GetMethod!.Name, - il2CppSystemTypeHandleRef.ToTypeSignature(), il2CppSystemTypeRef)); - ctorBuilder.Add(OpCodes.Ldfld, - ReferenceCreator.CreateFieldReference("value", assemblyContext.Imports.Module.IntPtr(), il2CppSystemTypeHandleRef)); - - ctorBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_il2cpp_class_from_type.Value); - } - - ctorBuilder.Add(OpCodes.Stsfld, typeContext.ClassPointerFieldRef); - - if (oldType.IsBeforeFieldInit) - { - ctorBuilder.Add(OpCodes.Ldsfld, typeContext.ClassPointerFieldRef); - ctorBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_il2cpp_runtime_class_init.Value); - } - - if (oldType.IsEnum) - { - ctorBuilder.Add(OpCodes.Ret); - return; - } - - foreach (var field in typeContext.Fields) - { - ctorBuilder.Add(OpCodes.Ldsfld, typeContext.ClassPointerFieldRef); - ctorBuilder.Add(OpCodes.Ldstr, field.OriginalField.Name!); - ctorBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_GetIl2CppField.Value); - ctorBuilder.Add(OpCodes.Stsfld, field.PointerField); - } - - foreach (var method in typeContext.Methods) - { - ctorBuilder.Add(OpCodes.Ldsfld, typeContext.ClassPointerFieldRef); - - var token = method.OriginalMethod.ExtractToken(); - if (token == 0) - { - ourTokenlessMethods++; - - ctorBuilder.Add( - method.OriginalMethod.GenericParameters.Count > 0 ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - ctorBuilder.Add(OpCodes.Ldstr, method.OriginalMethod.Name!); - ctorBuilder.EmitLoadTypeNameString(assemblyContext.Imports, method.OriginalMethod, - method.OriginalMethod.Signature!.ReturnType, method.NewMethod.Signature!.ReturnType); - ctorBuilder.Add(OpCodes.Ldc_I4, method.OriginalMethod.Parameters.Count); - ctorBuilder.Add(OpCodes.Newarr, assemblyContext.Imports.Module.String().ToTypeDefOrRef()); - - for (var i = 0; i < method.OriginalMethod.Parameters.Count; i++) - { - ctorBuilder.Add(OpCodes.Dup); - ctorBuilder.Add(OpCodes.Ldc_I4, i); - ctorBuilder.EmitLoadTypeNameString(assemblyContext.Imports, method.OriginalMethod, - method.OriginalMethod.Parameters[i].ParameterType, - method.NewMethod.Parameters[i].ParameterType); - ctorBuilder.Add(OpCodes.Stelem_Ref); - } - - ctorBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_GetIl2CppMethod.Value); - } - else - { - ctorBuilder.Add(OpCodes.Ldc_I4, (int)token); - ctorBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_GetIl2CppMethodByToken.Value); - } - - ctorBuilder.Add(OpCodes.Stsfld, method.NonGenericMethodInfoPointerField); - } - - ctorBuilder.Add(OpCodes.Ret); - } - - private static void EmitLoadTypeNameString(this ILProcessor ctorBuilder, RuntimeAssemblyReferences imports, - MethodDefinition originalMethod, TypeSignature originalTypeReference, TypeSignature newTypeReference) - { - if (originalMethod.HasGenericParameters() || originalTypeReference.FullName == "System.Void") - { - ctorBuilder.Add(OpCodes.Ldstr, originalTypeReference.FullName); - } - else - { - ctorBuilder.Add(newTypeReference is ByReferenceTypeSignature ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - ctorBuilder.Add(OpCodes.Call, - imports.Module.DefaultImporter.ImportMethod( - imports.IL2CPP_RenderTypeName.Value - .MakeGenericInstanceMethod(newTypeReference is ByReferenceTypeSignature ? newTypeReference.GetElementType() : newTypeReference))); - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass21GenerateValueTypeFields.cs b/Il2CppInterop.Generator/Passes/Pass21GenerateValueTypeFields.cs deleted file mode 100644 index c17c9ab9..00000000 --- a/Il2CppInterop.Generator/Passes/Pass21GenerateValueTypeFields.cs +++ /dev/null @@ -1,63 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass21GenerateValueTypeFields -{ - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - { - var il2CppTypeTypeRewriteContext = assemblyContext.GlobalContext.GetAssemblyByName("mscorlib") - .GetTypeByName("System.Object"); - var il2CppSystemTypeRef = - assemblyContext.NewAssembly.ManifestModule!.DefaultImporter.ImportType(il2CppTypeTypeRewriteContext.NewType).ToTypeSignature(); - - foreach (var typeContext in assemblyContext.Types) - { - if (typeContext.ComputedTypeSpecifics != TypeRewriteContext.TypeSpecifics.BlittableStruct || - typeContext.OriginalType.IsEnum) continue; - - try - { - var newType = typeContext.NewType; - newType.Attributes = (newType.Attributes & ~TypeAttributes.LayoutMask) | - TypeAttributes.ExplicitLayout; - - ILGeneratorEx.GenerateBoxMethod(assemblyContext.Imports, newType, typeContext.ClassPointerFieldRef, - il2CppSystemTypeRef); - - foreach (var fieldContext in typeContext.Fields) - { - var field = fieldContext.OriginalField; - if (field.IsStatic) continue; - - var newField = new FieldDefinition(fieldContext.UnmangledName, field.Attributes.ForcePublic(), - !field.Signature!.FieldType.IsValueType() - ? assemblyContext.Imports.Module.IntPtr() - : assemblyContext.RewriteTypeRef(field.Signature.FieldType)); - - newField.FieldOffset = field.ExtractFieldOffset(); - - // Special case: bools in Il2Cpp are bytes - if (newField.Signature!.FieldType.FullName == "System.Boolean") - newField.MarshalDescriptor = new SimpleMarshalDescriptor(NativeType.U1); - - newType.Fields.Add(newField); - } - } - catch (Exception ex) - { - throw new Exception( - $"Failed to generate value type fields for type {typeContext.OriginalType.FullName} in assembly {typeContext.AssemblyContext.OriginalAssembly.Name}", - ex); - } - } - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass22GenerateEnums.cs b/Il2CppInterop.Generator/Passes/Pass22GenerateEnums.cs deleted file mode 100644 index 7d103866..00000000 --- a/Il2CppInterop.Generator/Passes/Pass22GenerateEnums.cs +++ /dev/null @@ -1,59 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass22GenerateEnums -{ - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - foreach (var typeContext in assemblyContext.Types) - { - if (!typeContext.OriginalType.IsEnum) continue; - - var type = typeContext.OriginalType; - var newType = typeContext.NewType; - - if (type.Namespace.NameShouldBePrefixed(context.Options)) - { - newType.CustomAttributes.Add(new CustomAttribute( - (ICustomAttributeType)assemblyContext.Imports.OriginalNameAttributector.Value, - new CustomAttributeSignature( - new CustomAttributeArgument(assemblyContext.Imports.Module.String(), type.DeclaringModule?.Name ?? ""), - new CustomAttributeArgument(assemblyContext.Imports.Module.String(), type.Namespace ?? ""), - new CustomAttributeArgument(assemblyContext.Imports.Module.String(), type.Name ?? "")))); - } - - if (type.CustomAttributes.Any(it => it.Constructor?.DeclaringType?.FullName == "System.FlagsAttribute")) - newType.CustomAttributes.Add(new CustomAttribute(assemblyContext.Imports.Module.FlagsAttributeCtor())); - - foreach (var fieldDefinition in type.Fields) - { - var fieldName = fieldDefinition.Name!; - if (!context.Options.PassthroughNames && fieldName.IsObfuscated(context.Options)) - fieldName = GetUnmangledName(fieldDefinition); - - if (context.Options.RenameMap.TryGetValue( - typeContext.NewType.GetNamespacePrefix() + "." + typeContext.NewType.Name + "::" + fieldName, - out var newName)) - fieldName = newName; - - var newDef = new FieldDefinition(fieldName, fieldDefinition.Attributes | FieldAttributes.HasDefault, - assemblyContext.RewriteTypeRef(fieldDefinition.Signature!.FieldType)); - newType.Fields.Add(newDef); - - newDef.Constant = fieldDefinition.Constant; - } - } - } - - public static string GetUnmangledName(FieldDefinition field) - { - return "EnumValue" + field.Constant; - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass23GeneratePointerConstructors.cs b/Il2CppInterop.Generator/Passes/Pass23GeneratePointerConstructors.cs deleted file mode 100644 index a71dba02..00000000 --- a/Il2CppInterop.Generator/Passes/Pass23GeneratePointerConstructors.cs +++ /dev/null @@ -1,39 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass23GeneratePointerConstructors -{ - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - foreach (var typeContext in assemblyContext.Types) - { - if (typeContext.ComputedTypeSpecifics == TypeRewriteContext.TypeSpecifics.BlittableStruct || - typeContext.OriginalType.IsEnum) continue; - - var newType = typeContext.NewType; - - var nativeCtor = new MethodDefinition(".ctor", - MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RuntimeSpecialName | - MethodAttributes.HideBySig, MethodSignature.CreateInstance(assemblyContext.Imports.Module.Void())); - - nativeCtor.AddParameter(assemblyContext.Imports.Module.IntPtr(), "pointer"); - - nativeCtor.CilMethodBody = new(); - var ctorBody = nativeCtor.CilMethodBody.Instructions; - newType.Methods.Add(nativeCtor); - - ctorBody.Add(OpCodes.Ldarg_0); - ctorBody.Add(OpCodes.Ldarg_1); - ctorBody.Add(OpCodes.Call, - new MemberReference(newType.BaseType, ".ctor", MethodSignature.CreateInstance(assemblyContext.Imports.Module.Void(), assemblyContext.Imports.Module.IntPtr()))); - ctorBody.Add(OpCodes.Ret); - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass25GenerateNonBlittableValueTypeDefaultCtors.cs b/Il2CppInterop.Generator/Passes/Pass25GenerateNonBlittableValueTypeDefaultCtors.cs deleted file mode 100644 index 1dbbf53e..00000000 --- a/Il2CppInterop.Generator/Passes/Pass25GenerateNonBlittableValueTypeDefaultCtors.cs +++ /dev/null @@ -1,38 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass25GenerateNonBlittableValueTypeDefaultCtors -{ - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - foreach (var typeContext in assemblyContext.Types) - { - if (typeContext.ComputedTypeSpecifics != - TypeRewriteContext.TypeSpecifics.NonBlittableStruct) continue; - - var emptyCtor = new MethodDefinition(".ctor", - MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RuntimeSpecialName | - MethodAttributes.HideBySig, MethodSignature.CreateInstance(assemblyContext.Imports.Module.Void())); - - typeContext.NewType.Methods.Add(emptyCtor); - - emptyCtor.CilMethodBody = new(); - - // NOTE(Kas): This used to stackalloc data of the valuetype's size and box it into an object - // but it seems like it caused issues on certain games. If more issues arise - revert this. - var bodyBuilder = emptyCtor.CilMethodBody.Instructions; - bodyBuilder.Add(OpCodes.Ldarg_0); - bodyBuilder.Add(OpCodes.Ldsfld, typeContext.ClassPointerFieldRef); - bodyBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_il2cpp_object_new.Value); - bodyBuilder.Add(OpCodes.Call, - new MemberReference(typeContext.NewType.BaseType, ".ctor", MethodSignature.CreateInstance(assemblyContext.Imports.Module.Void(), assemblyContext.Imports.Module.IntPtr()))); - bodyBuilder.Add(OpCodes.Ret); - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass30GenerateGenericMethodStoreConstructors.cs b/Il2CppInterop.Generator/Passes/Pass30GenerateGenericMethodStoreConstructors.cs deleted file mode 100644 index 57a589b7..00000000 --- a/Il2CppInterop.Generator/Passes/Pass30GenerateGenericMethodStoreConstructors.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.Reflection; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass30GenerateGenericMethodStoreConstructors -{ - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - { - foreach (var typeContext in assemblyContext.Types) - { - foreach (var methodContext in typeContext.Methods) - { - var oldMethod = methodContext.OriginalMethod; - - var storeType = methodContext.GenericInstantiationsStore; - if (storeType != null) - { - var cctor = storeType.GetOrCreateStaticConstructor(); - - var ctorBuilder = cctor.CilMethodBody!.Instructions; - ctorBuilder.Clear(); - - var il2CppTypeTypeRewriteContext = assemblyContext.GlobalContext - .GetAssemblyByName("mscorlib").GetTypeByName("System.Type"); - var il2CppSystemTypeRef = - assemblyContext.NewAssembly.ManifestModule!.DefaultImporter.ImportType(il2CppTypeTypeRewriteContext.NewType); - - var il2CppMethodInfoTypeRewriteContext = assemblyContext.GlobalContext - .GetAssemblyByName("mscorlib").GetTypeByName("System.Reflection.MethodInfo"); - var il2CppSystemReflectionMethodInfoRef = - assemblyContext.NewAssembly.ManifestModule.DefaultImporter.ImportType(il2CppMethodInfoTypeRewriteContext.NewType); - - ctorBuilder.Add(OpCodes.Ldsfld, methodContext.NonGenericMethodInfoPointerField); - ctorBuilder.Add(OpCodes.Ldsfld, typeContext.ClassPointerFieldRef); - ctorBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_il2cpp_method_get_object.Value); - ctorBuilder.Add(OpCodes.Newobj, - new MemberReference(il2CppSystemReflectionMethodInfoRef, ".ctor", - MethodSignature.CreateInstance(assemblyContext.Imports.Module.Void(), assemblyContext.Imports.Module.IntPtr()))); - - ctorBuilder.Add(OpCodes.Ldc_I4, oldMethod.GenericParameters.Count); - - ctorBuilder.Add(OpCodes.Newarr, il2CppSystemTypeRef); - - for (var i = 0; i < oldMethod.GenericParameters.Count; i++) - { - ctorBuilder.Add(OpCodes.Dup); - ctorBuilder.Add(OpCodes.Ldc_I4, i); - - var param = storeType.GenericParameters[i]; - var storeRef = assemblyContext.Imports.Il2CppClassPointerStore.MakeGenericInstanceType(new GenericParameterSignature(GenericParameterType.Type, param.Number)); - var fieldRef = new MemberReference( - storeRef.ToTypeDefOrRef(), - "NativeClassPtr", - new FieldSignature(assemblyContext.Imports.Module.IntPtr())); - ctorBuilder.Add(OpCodes.Ldsfld, fieldRef); - - ctorBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_il2cpp_class_get_type.Value); - - ctorBuilder.Add(OpCodes.Call, - new MemberReference(il2CppSystemTypeRef, "internal_from_handle", - MethodSignature.CreateStatic(il2CppSystemTypeRef.ToTypeSignature(), assemblyContext.Imports.Module.IntPtr()))); - ctorBuilder.Add(OpCodes.Stelem_Ref); - } - - var il2CppTypeArray = assemblyContext.Imports.Il2CppReferenceArray.MakeGenericInstanceType(il2CppSystemTypeRef.ToTypeSignature()); - ctorBuilder.Add(OpCodes.Newobj, - ReferenceCreator.CreateInstanceMethodReference(".ctor", assemblyContext.Imports.Module.Void(), il2CppTypeArray.ToTypeDefOrRef(), new GenericParameterSignature(GenericParameterType.Type, 0).MakeSzArrayType())); - ctorBuilder.Add(OpCodes.Call, - ReferenceCreator.CreateInstanceMethodReference(nameof(MethodInfo.MakeGenericMethod), il2CppSystemReflectionMethodInfoRef.ToTypeSignature(), - il2CppSystemReflectionMethodInfoRef, il2CppTypeArray)); - ctorBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_Il2CppObjectBaseToPtrNotNull.Value); - - ctorBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_il2cpp_method_get_from_reflection.Value); - ctorBuilder.Add(OpCodes.Stsfld, - ReferenceCreator.CreateFieldReference("Pointer", assemblyContext.Imports.Module.IntPtr(), - methodContext.GenericInstantiationsStoreSelfSubstRef)); - - ctorBuilder.Add(OpCodes.Ret); - } - } - } - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass40GenerateFieldAccessors.cs b/Il2CppInterop.Generator/Passes/Pass40GenerateFieldAccessors.cs deleted file mode 100644 index b100ebc0..00000000 --- a/Il2CppInterop.Generator/Passes/Pass40GenerateFieldAccessors.cs +++ /dev/null @@ -1,38 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass40GenerateFieldAccessors -{ - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - { - foreach (var typeContext in assemblyContext.Types) - { - foreach (var fieldContext in typeContext.Fields) - { - if (typeContext.ComputedTypeSpecifics == TypeRewriteContext.TypeSpecifics.BlittableStruct && - !fieldContext.OriginalField.IsStatic) continue; - - var field = fieldContext.OriginalField; - var unmangleFieldName = fieldContext.UnmangledName; - - var propertyType = assemblyContext.RewriteTypeRef(fieldContext.OriginalField.Signature!.FieldType); - var signature = field.IsStatic - ? PropertySignature.CreateStatic(propertyType) - : PropertySignature.CreateInstance(propertyType); - var property = new PropertyDefinition(unmangleFieldName, PropertyAttributes.None, signature); - typeContext.NewType.Properties.Add(property); - - FieldAccessorGenerator.MakeGetter(field, fieldContext, property, assemblyContext.Imports); - FieldAccessorGenerator.MakeSetter(field, fieldContext, property, assemblyContext.Imports); - } - } - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass50GenerateMethods.cs b/Il2CppInterop.Generator/Passes/Pass50GenerateMethods.cs deleted file mode 100644 index eff085a7..00000000 --- a/Il2CppInterop.Generator/Passes/Pass50GenerateMethods.cs +++ /dev/null @@ -1,259 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Code.Cil; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Cil; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass50GenerateMethods -{ - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - { - foreach (var typeContext in assemblyContext.Types) - { - foreach (var methodRewriteContext in typeContext.Methods) - { - var originalMethod = methodRewriteContext.OriginalMethod; - var newMethod = methodRewriteContext.NewMethod; - var imports = assemblyContext.Imports; - - var bodyBuilder = newMethod.CilMethodBody!.Instructions; - var exceptionLocal = new CilLocalVariable(imports.Module.IntPtr()); - var argArray = new CilLocalVariable(imports.Module.IntPtr().MakePointerType()); - var resultVar = new CilLocalVariable(imports.Module.IntPtr()); - var valueTypeLocal = new CilLocalVariable(newMethod.Signature!.ReturnType); - newMethod.CilMethodBody.LocalVariables.Add(exceptionLocal); - newMethod.CilMethodBody.LocalVariables.Add(argArray); - newMethod.CilMethodBody.LocalVariables.Add(resultVar); - - if (valueTypeLocal.VariableType.FullName != "System.Void") - newMethod.CilMethodBody.LocalVariables.Add(valueTypeLocal); - - // Pre-initialize any present params - // TODO: This doesn't account for params T[] (i.e. generic element type) yet; may emit incorrect IL - // TODO: Do we really need a loop here? C# allows only one params array. - // On the other hand, CreateParamsMethod accommodates multiple ParamArrayAttribute as well - CilInstructionLabel? nextInstruction = null; - for (var paramIndex = 0; paramIndex < originalMethod.Parameters.Count; paramIndex++) - { - var newParameter = newMethod.Parameters[paramIndex]; - var originalParameter = originalMethod.Parameters[paramIndex]; - if (!originalParameter.IsParamsArray()) - continue; - - var originalElementType = ((ArrayBaseTypeSignature)originalParameter.ParameterType).BaseType; - - if (nextInstruction != null) - nextInstruction.Instruction = bodyBuilder.Add(OpCodes.Nop); - nextInstruction = new(); - - bodyBuilder.Add(OpCodes.Ldarg, newParameter); - bodyBuilder.Add(OpCodes.Brtrue, nextInstruction); - - bodyBuilder.Add(OpCodes.Ldc_I4_0); - bodyBuilder.Add(OpCodes.Conv_I8); - bodyBuilder.Add(OpCodes.Newobj, imports.Module.DefaultImporter.ImportMethod(originalElementType.FullName switch - { - "System.String" => imports.Il2CppStringArrayctor_size.Value, - _ when originalElementType.IsValueType() => imports.Il2CppStructArrayctor_size.Get(((GenericInstanceTypeSignature)newParameter.ParameterType).TypeArguments[0]), - _ => imports.Il2CppRefrenceArrayctor_size.Get(((GenericInstanceTypeSignature)newParameter.ParameterType).TypeArguments[0]) - })); - bodyBuilder.Add(OpCodes.Starg, newParameter); - } - - if (nextInstruction != null) - nextInstruction.Instruction = bodyBuilder.Add(OpCodes.Nop); - - if (typeContext.ComputedTypeSpecifics != TypeRewriteContext.TypeSpecifics.BlittableStruct) - { - if (originalMethod.IsConstructor) - { - bodyBuilder.Add(OpCodes.Ldarg_0); - bodyBuilder.Add(OpCodes.Ldsfld, typeContext.ClassPointerFieldRef); - bodyBuilder.Add(OpCodes.Call, imports.IL2CPP_il2cpp_object_new.Value); - bodyBuilder.Add(OpCodes.Call, - ReferenceCreator.CreateInstanceMethodReference(".ctor", imports.Module.Void(), typeContext.SelfSubstitutedRef, imports.Module.IntPtr())); - } - else if (!originalMethod.IsStatic) - { - bodyBuilder.Add(OpCodes.Ldarg_0); - bodyBuilder.Add(OpCodes.Call, imports.IL2CPP_Il2CppObjectBaseToPtrNotNull.Value); - bodyBuilder.Add(OpCodes.Pop); - } - } - - if (originalMethod.Parameters.Count == 0) - { - bodyBuilder.Add(OpCodes.Ldc_I4_0); - bodyBuilder.Add(OpCodes.Conv_U); - } - else - { - bodyBuilder.Add(OpCodes.Ldc_I4, originalMethod.Parameters.Count); - bodyBuilder.Add(OpCodes.Conv_U); - bodyBuilder.Add(OpCodes.Sizeof, imports.Module.IntPtr().ToTypeDefOrRef()); - bodyBuilder.Add(OpCodes.Mul_Ovf_Un); - bodyBuilder.Add(OpCodes.Localloc); - } - - bodyBuilder.Add(OpCodes.Stloc, argArray); - - var argOffset = originalMethod.IsStatic ? 0 : 1; - - var byRefParams = new List<(int, CilLocalVariable)>(); - - for (var i = 0; i < newMethod.Parameters.Count; i++) - { - bodyBuilder.Add(OpCodes.Ldloc, argArray); - if (i > 0) - { - bodyBuilder.Add(OpCodes.Ldc_I4, i); - bodyBuilder.Add(OpCodes.Conv_U); - bodyBuilder.Add(OpCodes.Sizeof, imports.Module.IntPtr().ToTypeDefOrRef()); - bodyBuilder.Add(OpCodes.Mul_Ovf_Un); - bodyBuilder.Add(OpCodes.Add); - } - - var newParam = newMethod.Parameters[i]; - // NOTE(Kas): out parameters of value type are passed directly as a pointer to the il2cpp method - // since we don't need to perform any additional copies - if (newParam.Definition!.IsOut && !newParam.ParameterType.GetElementType().IsValueType()) - { - var elementType = newParam.ParameterType.GetElementType(); - - // Storage for the output Il2CppObjectBase pointer, it's - // unused if there's a generic value type parameter - var outVar = new CilLocalVariable(imports.Module.IntPtr()); - bodyBuilder.Owner.LocalVariables.Add(outVar); - - if (elementType is GenericParameterSignature) - { - bodyBuilder.Add(OpCodes.Ldtoken, elementType.ToTypeDefOrRef()); - bodyBuilder.Add(OpCodes.Call, imports.Module.TypeGetTypeFromHandle()); - bodyBuilder.Add(OpCodes.Callvirt, imports.Module.TypeGetIsValueType()); - - var valueTypeBlock = new CilInstructionLabel(); - var continueBlock = new CilInstructionLabel(); - - bodyBuilder.Add(OpCodes.Brtrue, valueTypeBlock); - - // The generic parameter is an Il2CppObjectBase => set the output storage to a nullptr - bodyBuilder.Add(OpCodes.Ldc_I4, 0); - bodyBuilder.Add(OpCodes.Stloc, outVar); - bodyBuilder.Add(OpCodes.Ldloca, outVar); - bodyBuilder.Add(OpCodes.Conv_I); - - bodyBuilder.Add(OpCodes.Br_S, continueBlock); - - // Instruction block that handles generic value types, we only need to return a reference - // to the output argument since it is already allocated for us - valueTypeBlock.Instruction = bodyBuilder.Add(OpCodes.Nop); - bodyBuilder.AddLoadArgument(argOffset + i); - - continueBlock.Instruction = bodyBuilder.Add(OpCodes.Nop); - } - else - { - bodyBuilder.Add(OpCodes.Ldc_I4, 0); - bodyBuilder.Add(OpCodes.Stloc, outVar); - bodyBuilder.Add(OpCodes.Ldloca, outVar); - bodyBuilder.Add(OpCodes.Conv_I); - } - byRefParams.Add((i, outVar)); - } - else - { - bodyBuilder.EmitObjectToPointer(originalMethod.Parameters[i].ParameterType, newParam.ParameterType, - methodRewriteContext.DeclaringType, argOffset + i, false, true, true, false, out var refVar); - if (refVar != null) - byRefParams.Add((i, refVar)); - } - bodyBuilder.Add(OpCodes.Stind_I); - - } - - if (!originalMethod.DeclaringType!.IsSealed && !originalMethod.IsFinal && - ((originalMethod.IsVirtual && !originalMethod.DeclaringType.IsValueType()) || originalMethod.IsAbstract)) - { - bodyBuilder.Add(OpCodes.Ldarg_0); - bodyBuilder.Add(OpCodes.Call, imports.IL2CPP_Il2CppObjectBaseToPtr.Value); - if (methodRewriteContext.GenericInstantiationsStoreSelfSubstRef != null) - bodyBuilder.Add(OpCodes.Ldsfld, - ReferenceCreator.CreateFieldReference("Pointer", imports.Module.IntPtr(), - methodRewriteContext.GenericInstantiationsStoreSelfSubstMethodRef)); - else - bodyBuilder.Add(OpCodes.Ldsfld, methodRewriteContext.NonGenericMethodInfoPointerField); - bodyBuilder.Add(OpCodes.Call, imports.IL2CPP_il2cpp_object_get_virtual_method.Value); - } - else if (methodRewriteContext.GenericInstantiationsStoreSelfSubstRef != null) - { - bodyBuilder.Add(OpCodes.Ldsfld, - ReferenceCreator.CreateFieldReference("Pointer", imports.Module.IntPtr(), - methodRewriteContext.GenericInstantiationsStoreSelfSubstMethodRef)); - } - else - { - bodyBuilder.Add(OpCodes.Ldsfld, methodRewriteContext.NonGenericMethodInfoPointerField); - } - - if (originalMethod.IsStatic) - bodyBuilder.Add(OpCodes.Ldc_I4_0); - else - bodyBuilder.EmitObjectToPointer(originalMethod.DeclaringType.ToTypeSignature(), newMethod.DeclaringType!.ToTypeSignature(), typeContext, 0, - true, false, true, true, out _); - - bodyBuilder.Add(OpCodes.Ldloc, argArray); - bodyBuilder.Add(OpCodes.Ldloca, exceptionLocal); - bodyBuilder.Add(OpCodes.Call, imports.IL2CPP_il2cpp_runtime_invoke.Value); - bodyBuilder.Add(OpCodes.Stloc, resultVar); - - bodyBuilder.Add(OpCodes.Ldloc, exceptionLocal); - bodyBuilder.Add(OpCodes.Call, imports.Il2CppException_RaiseExceptionIfNecessary.Value); - - foreach (var byRefParam in byRefParams) - { - var paramIndex = byRefParam.Item1; - var paramVariable = byRefParam.Item2; - var methodParam = newMethod.Parameters[paramIndex]; - - if (methodParam.Definition!.IsOut && methodParam.ParameterType.GetElementType() is GenericParameterSignature) - { - bodyBuilder.Add(OpCodes.Ldtoken, methodParam.ParameterType.GetElementType().ToTypeDefOrRef()); - bodyBuilder.Add(OpCodes.Call, imports.Module.TypeGetTypeFromHandle()); - bodyBuilder.Add(OpCodes.Callvirt, imports.Module.TypeGetIsValueType()); - - var continueBlock = new CilInstructionLabel(); - - bodyBuilder.Add(OpCodes.Brtrue, continueBlock); - - // The generic parameter is an Il2CppObjectBase => update the reference appropriately - bodyBuilder.EmitUpdateRef(newMethod.Parameters[paramIndex], paramIndex + argOffset, paramVariable, - imports); - - bodyBuilder.Add(OpCodes.Br_S, continueBlock); - - // There is no need to handle generic value types, they are already passed by reference - - continueBlock.Instruction = bodyBuilder.Add(OpCodes.Nop); - } - else - { - bodyBuilder.EmitUpdateRef(newMethod.Parameters[paramIndex], paramIndex + argOffset, paramVariable, - imports); - } - } - - bodyBuilder.EmitPointerToObject(originalMethod.Signature!.ReturnType, newMethod.Signature.ReturnType, typeContext, - resultVar, false, true); - - bodyBuilder.Add(OpCodes.Ret); - } - } - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass60AddImplicitConversions.cs b/Il2CppInterop.Generator/Passes/Pass60AddImplicitConversions.cs deleted file mode 100644 index 500fbc21..00000000 --- a/Il2CppInterop.Generator/Passes/Pass60AddImplicitConversions.cs +++ /dev/null @@ -1,215 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Code.Cil; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Cil; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass60AddImplicitConversions -{ - private const MethodAttributes OperatorAttributes = MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; - - public static void DoPass(RewriteGlobalContext context) - { - var assemblyContext = context.GetAssemblyByName("mscorlib"); - var typeContext = assemblyContext.GetTypeByName("System.String"); - var objectTypeContext = assemblyContext.GetTypeByName("System.Object"); - - var methodFromMonoString = new MethodDefinition("op_Implicit", OperatorAttributes, - MethodSignature.CreateStatic(typeContext.NewType.ToTypeSignature(), assemblyContext.Imports.Module.String())); - typeContext.NewType.Methods.Add(methodFromMonoString); - methodFromMonoString.CilMethodBody = new CilMethodBody(); - var fromBuilder = methodFromMonoString.CilMethodBody.Instructions; - - var createIl2CppStringNop = new CilInstructionLabel(); - - fromBuilder.Add(OpCodes.Ldarg_0); - fromBuilder.Add(OpCodes.Dup); - fromBuilder.Add(OpCodes.Brtrue_S, createIl2CppStringNop); - fromBuilder.Add(OpCodes.Ret); - - createIl2CppStringNop.Instruction = fromBuilder.Add(OpCodes.Nop); - - fromBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_ManagedStringToIl2Cpp.Value); - fromBuilder.Add(OpCodes.Newobj, - ReferenceCreator.CreateInstanceMethodReference(".ctor", assemblyContext.Imports.Module.Void(), typeContext.NewType, assemblyContext.Imports.Module.IntPtr())); - fromBuilder.Add(OpCodes.Ret); - - var methodToObject = new MethodDefinition("op_Implicit", OperatorAttributes, MethodSignature.CreateStatic(objectTypeContext.NewType.ToTypeSignature())); - methodToObject.AddParameter(assemblyContext.Imports.Module.String()); - objectTypeContext.NewType.Methods.Add(methodToObject); - methodToObject.CilMethodBody = new CilMethodBody(); - var toObjectBuilder = methodToObject.CilMethodBody.Instructions; - toObjectBuilder.Add(OpCodes.Ldarg_0); - toObjectBuilder.Add(OpCodes.Call, methodFromMonoString); - toObjectBuilder.Add(OpCodes.Ret); - - var methodToMonoString = new MethodDefinition("op_Implicit", OperatorAttributes, MethodSignature.CreateStatic(assemblyContext.Imports.Module.String())); - methodToMonoString.AddParameter(typeContext.NewType.ToTypeSignature()); - typeContext.NewType.Methods.Add(methodToMonoString); - methodToMonoString.CilMethodBody = new CilMethodBody(); - var toBuilder = methodToMonoString.CilMethodBody.Instructions; - - var createStringNop = new CilInstructionLabel(); - - toBuilder.Add(OpCodes.Ldarg_0); - toBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_Il2CppObjectBaseToPtr.Value); - toBuilder.Add(OpCodes.Dup); - toBuilder.Add(OpCodes.Brtrue_S, createStringNop); - toBuilder.Add(OpCodes.Pop); - toBuilder.Add(OpCodes.Ldnull); - toBuilder.Add(OpCodes.Ret); - - createStringNop.Instruction = toBuilder.Add(OpCodes.Nop); - toBuilder.Add(OpCodes.Call, assemblyContext.Imports.IL2CPP_Il2CppStringToManaged.Value); - toBuilder.Add(OpCodes.Ret); - - AddDelegateConversions(context); - - TypeSignature[] primitiveTypes = - [ - assemblyContext.Imports.Module.SByte(), - assemblyContext.Imports.Module.Byte(), - - assemblyContext.Imports.Module.Short(), - assemblyContext.Imports.Module.UShort(), - - assemblyContext.Imports.Module.Int(), - assemblyContext.Imports.Module.UInt(), - - assemblyContext.Imports.Module.Long(), - assemblyContext.Imports.Module.ULong(), - - assemblyContext.Imports.Module.Float(), - assemblyContext.Imports.Module.Double(), - - assemblyContext.Imports.Module.Char(), - assemblyContext.Imports.Module.Bool(), - ]; - - foreach (var systemType in primitiveTypes) - { - var il2CppSystemType = assemblyContext.GetTypeByName(systemType.FullName).NewType; - - var method = new MethodDefinition("op_Implicit", OperatorAttributes, MethodSignature.CreateStatic(objectTypeContext.NewType.ToTypeSignature())); - method.AddParameter(systemType, "value"); - - method.CilMethodBody = new CilMethodBody(); - var il = method.CilMethodBody.Instructions; - - var structLocal = new CilLocalVariable(il2CppSystemType.ToTypeSignature()); - method.CilMethodBody.LocalVariables.Add(structLocal); - - il.Add(OpCodes.Ldloca, structLocal); - il.Add(OpCodes.Initobj, il2CppSystemType); - - il.Add(OpCodes.Ldloca, structLocal); - il.Add(OpCodes.Ldarg_0); - il.Add(OpCodes.Stfld, il2CppSystemType.Fields.Single(f => f.Name == "m_value")); - - il.Add(OpCodes.Ldloca_S, structLocal); - il.Add(OpCodes.Call, il2CppSystemType.Methods.Single(m => m.Name == "BoxIl2CppObject")); - il.Add(OpCodes.Ret); - - objectTypeContext.NewType.Methods.Add(method); - } - } - - private static void AddDelegateConversions(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - { - foreach (var typeContext in assemblyContext.Types) - { - if (typeContext.OriginalType.BaseType?.FullName != "System.MulticastDelegate") - continue; - - var invokeMethod = typeContext.NewType.Methods.Single(it => it.Name == "Invoke"); - if (invokeMethod.Parameters.Count > 8) - continue; // mscorlib only contains delegates of up to 8 parameters - - // Don't generate implicit conversions for pointers and byrefs, as they can't be specified in generics - if (invokeMethod.Parameters.Any(it => it.ParameterType is PointerTypeSignature or ByReferenceTypeSignature)) - continue; - - var implicitMethod = new MethodDefinition("op_Implicit", OperatorAttributes, MethodSignature.CreateStatic(typeContext.SelfSubstitutedRef.ToTypeSignature())); - typeContext.NewType.Methods.Add(implicitMethod); - implicitMethod.CilMethodBody = new CilMethodBody(); - - var hasReturn = invokeMethod.Signature!.ReturnType.FullName != "System.Void"; - var hasParameters = invokeMethod.Parameters.Count > 0; - - TypeSignature monoDelegateType; - if (!hasReturn && !hasParameters) - monoDelegateType = typeContext.NewType.DeclaringModule!.Action(); - else if (!hasReturn) - monoDelegateType = typeContext.NewType.DeclaringModule!.Action(invokeMethod.Parameters.Count); - else - monoDelegateType = typeContext.NewType.DeclaringModule!.Func(invokeMethod.Parameters.Count); - - GenericInstanceTypeSignature? genericInstanceType = null; - if (hasParameters) - { - genericInstanceType = new GenericInstanceTypeSignature(monoDelegateType.ToTypeDefOrRef(), false); - foreach (var t in invokeMethod.Parameters) - genericInstanceType.TypeArguments.Add(t.ParameterType); - } - - if (hasReturn) - { - genericInstanceType ??= new GenericInstanceTypeSignature(monoDelegateType.ToTypeDefOrRef(), false); - genericInstanceType.TypeArguments.Add(invokeMethod.Signature.ReturnType); - } - - implicitMethod.AddParameter(genericInstanceType != null - ? typeContext.NewType.DeclaringModule!.DefaultImporter.ImportTypeSignature(genericInstanceType) - : monoDelegateType); - - var bodyBuilder = implicitMethod.CilMethodBody.Instructions; - - bodyBuilder.Add(OpCodes.Ldarg_0); - var delegateSupportTypeRef = typeContext.AssemblyContext.Imports.DelegateSupport; - var genericConvertSignature = MethodSignature.CreateStatic(new GenericParameterSignature(GenericParameterType.Method, 0), 1, assemblyContext.Imports.Module.Delegate()); - var genericConvertRef = new MemberReference(delegateSupportTypeRef.ToTypeDefOrRef(), "ConvertDelegate", genericConvertSignature); - var convertMethodRef = genericConvertRef.MakeGenericInstanceMethod(typeContext.SelfSubstitutedRef.ToTypeSignature()); - bodyBuilder.Add(OpCodes.Call, typeContext.NewType.DeclaringModule!.DefaultImporter.ImportMethod(convertMethodRef)); - bodyBuilder.Add(OpCodes.Ret); - - // public static T operator+(T lhs, T rhs) => Il2CppSystem.Delegate.Combine(lhs, rhs).Cast(); - var addMethod = new MethodDefinition("op_Addition", OperatorAttributes, MethodSignature.CreateStatic(typeContext.SelfSubstitutedRef.ToTypeSignature())); - typeContext.NewType.Methods.Add(addMethod); - addMethod.AddParameter(typeContext.SelfSubstitutedRef.ToTypeSignature()); - addMethod.AddParameter(typeContext.SelfSubstitutedRef.ToTypeSignature()); - addMethod.CilMethodBody = new CilMethodBody(); - var addBody = addMethod.CilMethodBody.Instructions; - addBody.Add(OpCodes.Ldarg_0); - addBody.Add(OpCodes.Ldarg_1); - addBody.Add(OpCodes.Call, assemblyContext.Imports.Il2CppSystemDelegateCombine.Value); - addBody.Add(OpCodes.Call, - assemblyContext.Imports.Module.DefaultImporter.ImportMethod(assemblyContext.Imports.Il2CppObjectBase_Cast.Value.MakeGenericInstanceMethod(typeContext.SelfSubstitutedRef.ToTypeSignature()))); - addBody.Add(OpCodes.Ret); - - // public static T operator-(T lhs, T rhs) => Il2CppSystem.Delegate.Remove(lhs, rhs)?.Cast(); - var subtractMethod = new MethodDefinition("op_Subtraction", OperatorAttributes, MethodSignature.CreateStatic(typeContext.SelfSubstitutedRef.ToTypeSignature())); - typeContext.NewType.Methods.Add(subtractMethod); - subtractMethod.AddParameter(typeContext.SelfSubstitutedRef.ToTypeSignature()); - subtractMethod.AddParameter(typeContext.SelfSubstitutedRef.ToTypeSignature()); - subtractMethod.CilMethodBody = new CilMethodBody(); - var subtractBody = subtractMethod.CilMethodBody.Instructions; - subtractBody.Add(OpCodes.Ldarg_0); - subtractBody.Add(OpCodes.Ldarg_1); - subtractBody.Add(OpCodes.Call, assemblyContext.Imports.Il2CppSystemDelegateRemove.Value); - subtractBody.Add(OpCodes.Dup); - var ret = new CilInstructionLabel(); - subtractBody.Add(OpCodes.Brfalse_S, ret); - subtractBody.Add(OpCodes.Call, - assemblyContext.Imports.Module.DefaultImporter.ImportMethod(assemblyContext.Imports.Il2CppObjectBase_Cast.Value.MakeGenericInstanceMethod(typeContext.SelfSubstitutedRef.ToTypeSignature()))); - ret.Instruction = subtractBody.Add(OpCodes.Ret); - } - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass61ImplementAwaiters.cs b/Il2CppInterop.Generator/Passes/Pass61ImplementAwaiters.cs deleted file mode 100644 index 9ed14fa0..00000000 --- a/Il2CppInterop.Generator/Passes/Pass61ImplementAwaiters.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Runtime.CompilerServices; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Cloning; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Cil; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Common; -using Il2CppInterop.Generator.Contexts; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass61ImplementAwaiters -{ - public static void DoPass(RewriteGlobalContext context) - { - var corlib = context.CorLib; - - var actionUntyped = corlib.GetTypeByName("System.Action"); - - var actionConversion = actionUntyped.NewType.Methods.Single(m => m.Name == "op_Implicit"); - - foreach (var assemblyContext in context.Assemblies) - { - // Use Lazy as a lazy way to not actually import the references until they're needed - - Lazy actionUntypedRef = new(() => assemblyContext.NewAssembly.ManifestModule!.DefaultImporter.ImportType(actionConversion.Parameters[0].ParameterType.ToTypeDefOrRef())!); - Lazy actionConversionRef = new(() => assemblyContext.NewAssembly.ManifestModule!.DefaultImporter.ImportMethod(actionConversion)); - Lazy notifyCompletionRef = new(() => assemblyContext.NewAssembly.ManifestModule!.DefaultImporter.ImportType(typeof(INotifyCompletion))); - var voidRef = assemblyContext.NewAssembly.ManifestModule!.CorLibTypeFactory.Void; - - foreach (var typeContext in assemblyContext.Types) - { - // Odds are a majority of types won't implement any interfaces. Skip them to save time. - if (typeContext.OriginalType.IsInterface || typeContext.OriginalType.Interfaces.Count == 0) - continue; - - var iNotifyCompletion = typeof(INotifyCompletion); - var interfaceImplementation = typeContext.OriginalType.Interfaces.SingleOrDefault(interfaceImpl => interfaceImpl.Interface?.Namespace == iNotifyCompletion.Namespace && interfaceImpl.Interface?.Name == iNotifyCompletion.Name); - if (interfaceImplementation is null) - continue; - - var allOnCompleted = typeContext.Methods.Where(m => m.OriginalMethod.Name == nameof(INotifyCompletion.OnCompleted)).Select(mc => mc.NewMethod).ToArray(); - - // Conversion spits out an Il2CppSystem.Action, so look for methods that take that (and only that) in & return void, so the stack is balanced - // And use SignatureComparer because otherwise equality checks would fail due to the TypeSignatures being different references - var interopOnCompleted = allOnCompleted.FirstOrDefault(m => !m.IsStatic && m.Parameters.Count == 1 && m.Signature is not null && SignatureComparer.Default.Equals(m.Signature.ReturnType, voidRef) && SignatureComparer.Default.Equals(m.Signature.ParameterTypes[0], actionConversion.Signature?.ReturnType)); - - if (interopOnCompleted is null) - { - var typeName = typeContext.OriginalType.FullName; - var foundMethodCount = allOnCompleted.Length; - Logger.Instance.LogInformation("Type {typeName} was found to implement INotifyCompletion, but no suitable method was found. {foundMethodCount} method(s) were found with the required name.", typeName, foundMethodCount); - continue; - } - - var onCompletedAttr = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; - var sig = MethodSignature.CreateInstance(voidRef, [actionUntypedRef.Value.ToTypeSignature()]); - - var proxyOnCompleted = new MethodDefinition(nameof(INotifyCompletion.OnCompleted), onCompletedAttr, sig); - var parameter = proxyOnCompleted.Parameters[0].GetOrCreateDefinition(); - parameter.Name = "continuation"; - - var body = proxyOnCompleted.CilMethodBody ??= new(); - - typeContext.NewType.Interfaces.Add(new(notifyCompletionRef.Value)); - typeContext.NewType.Methods.Add(proxyOnCompleted); - - var instructions = body.Instructions; - instructions.Add(CilOpCodes.Ldarg_0); // load "this" - instructions.Add(CilOpCodes.Ldarg_1); // not static, so ldarg1 loads "continuation" - instructions.Add(CilOpCodes.Call, actionConversionRef.Value); - - // The titular jump to the interop method -- it's gotta reference the method on the right type, so we need to handle generic parameters - // Without this, awaiters declared in generic types like UniTask.Awaiter would effectively try to cast themselves to their untyped versions (UniTask<>.Awaiter in this case, which isn't a thing) - var genericParameterCount = typeContext.NewType.GenericParameters.Count; - if (genericParameterCount > 0) - { - var typeArguments = Enumerable.Range(0, genericParameterCount).Select(i => new GenericParameterSignature(GenericParameterType.Type, i)).ToArray(); - var interopOnCompleteGeneric = typeContext.NewType.MakeGenericInstanceType(typeArguments) - .ToTypeDefOrRef() - .CreateMemberReference(interopOnCompleted.Name, interopOnCompleted.Signature); - instructions.Add(CilOpCodes.Call, interopOnCompleteGeneric); - } - else - { - instructions.Add(CilOpCodes.Call, interopOnCompleted); - } - - instructions.Add(CilOpCodes.Ret); - } - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass70GenerateProperties.cs b/Il2CppInterop.Generator/Passes/Pass70GenerateProperties.cs deleted file mode 100644 index e80e7911..00000000 --- a/Il2CppInterop.Generator/Passes/Pass70GenerateProperties.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System.Reflection; -using AsmResolver; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass70GenerateProperties -{ - public static void DoPass(RewriteGlobalContext context) - { - foreach (var assemblyContext in context.Assemblies) - foreach (var typeContext in assemblyContext.Types) - { - var type = typeContext.OriginalType; - var propertyCountsByName = new Dictionary(); - - foreach (var oldProperty in type.Properties) - { - FixPropertyDefinitionParameters(oldProperty); - - var unmangledPropertyName = UnmanglePropertyName(assemblyContext, oldProperty, typeContext.NewType, - propertyCountsByName); - - var propertyType = assemblyContext.RewriteTypeRef(oldProperty.Signature!.ReturnType); - var signature = oldProperty.Signature.HasThis - ? PropertySignature.CreateInstance(propertyType) - : PropertySignature.CreateStatic(propertyType); - foreach (var oldParameter in oldProperty.Signature.ParameterTypes) - signature.ParameterTypes.Add(assemblyContext.RewriteTypeRef(oldParameter)); - - var property = new PropertyDefinition(unmangledPropertyName, oldProperty.Attributes, signature); - - typeContext.NewType.Properties.Add(property); - - property.SetSemanticMethods( - oldProperty.GetMethod is null ? null : typeContext.GetMethodByOldMethod(oldProperty.GetMethod).NewMethod, - oldProperty.SetMethod is null ? null : typeContext.GetMethodByOldMethod(oldProperty.SetMethod).NewMethod); - } - - string? defaultMemberName = null; - if (type.CustomAttributes.FirstOrDefault(IsDefaultMemberAttributeFake) != null) - { - defaultMemberName = "Item"; - } - else - { - var realDefaultMemberAttribute = type.CustomAttributes.FirstOrDefault(IsDefaultMemberAttributeReal); - if (realDefaultMemberAttribute != null) - defaultMemberName = realDefaultMemberAttribute.Signature?.FixedArguments[0].Element?.ToString() ?? "Item"; - } - - if (defaultMemberName != null) - typeContext.NewType.CustomAttributes.Add(new CustomAttribute( - ReferenceCreator.CreateInstanceMethodReference(".ctor", assemblyContext.Imports.Module.Void(), - assemblyContext.Imports.Module.DefaultMemberAttribute().ToTypeDefOrRef(), assemblyContext.Imports.Module.String()), - new CustomAttributeSignature(new CustomAttributeArgument(assemblyContext.Imports.Module.String(), defaultMemberName)))); - } - - static bool IsDefaultMemberAttributeFake(CustomAttribute attribute) - { - return attribute.AttributeType()?.Name == "AttributeAttribute" && attribute.Signature!.NamedArguments.Any(it => - { - // Name support is for backwards compatibility. - return (it.MemberName == "Type" && it.Argument.Element is ITypeDescriptor { Namespace: "System.Reflection", Name: nameof(DefaultMemberAttribute) }) - || (it.MemberName == "Name" && it.Argument.GetElementAsString() == nameof(DefaultMemberAttribute)); - }); - } - - static bool IsDefaultMemberAttributeReal(CustomAttribute attribute) - { - return attribute.AttributeType() is { Namespace.Value: "System.Reflection", Name.Value: nameof(DefaultMemberAttribute) }; - } - } - - private static string UnmanglePropertyName(AssemblyRewriteContext assemblyContext, PropertyDefinition prop, - ITypeDefOrRef declaringType, Dictionary countsByBaseName) - { - if (assemblyContext.GlobalContext.Options.PassthroughNames || - !prop.Name.IsObfuscated(assemblyContext.GlobalContext.Options)) return prop.Name!; - - var baseName = "prop_" + assemblyContext.RewriteTypeRef(prop.Signature!.ReturnType).GetUnmangledName(prop.DeclaringType); - - countsByBaseName.TryGetValue(baseName, out var index); - countsByBaseName[baseName] = index + 1; - - var unmanglePropertyName = baseName + "_" + index; - - if (assemblyContext.GlobalContext.Options.RenameMap.TryGetValue( - declaringType.GetNamespacePrefix() + "." + declaringType.Name + "::" + unmanglePropertyName, out var newNameByType)) - { - unmanglePropertyName = newNameByType; - } - else if (assemblyContext.GlobalContext.Options.RenameMap.TryGetValue( - declaringType.GetNamespacePrefix() + "::" + unmanglePropertyName, out var newName)) - { - unmanglePropertyName = newName; - } - - - return unmanglePropertyName; - } - - private static void FixPropertyDefinitionParameters(PropertyDefinition property) - { - // See: https://github.com/SamboyCoding/Cpp2IL/issues/249 - - if (property.Signature is null or { ParameterTypes.Count: > 0 }) - return; - - var getMethod = property.GetMethod; - if (getMethod?.Signature is not null) - { - if (getMethod.Signature.ParameterTypes.Count > 0) - { - foreach (var parameter in getMethod.Signature.ParameterTypes) - { - property.Signature.ParameterTypes.Add(parameter); - } - } - - return; - } - - var setMethod = property.SetMethod; - if (setMethod?.Signature is not null) - { - if (setMethod.Signature.ParameterTypes.Count > 1) - { - foreach (var parameter in setMethod.Signature.ParameterTypes.Take(setMethod.Signature.ParameterTypes.Count - 1)) - { - property.Signature.ParameterTypes.Add(parameter); - } - } - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass79UnstripTypes.cs b/Il2CppInterop.Generator/Passes/Pass79UnstripTypes.cs deleted file mode 100644 index 739d58a9..00000000 --- a/Il2CppInterop.Generator/Passes/Pass79UnstripTypes.cs +++ /dev/null @@ -1,149 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Common; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass79UnstripTypes -{ - public static void DoPass(RewriteGlobalContext context) - { - var typesUnstripped = 0; - - foreach (var unityAssembly in context.UnityAssemblies.Assemblies) - { - var processedAssembly = context.TryGetAssemblyByName(unityAssembly.Name); - if (processedAssembly == null) - { - var newAssembly = new AssemblyDefinition(unityAssembly.Name, unityAssembly.Version); - newAssembly.Modules.Add(new ModuleDefinition(unityAssembly.ManifestModule!.Name)); - var newContext = new AssemblyRewriteContext(context, unityAssembly, - newAssembly); - context.AddAssemblyContext(unityAssembly.Name!, newContext); - processedAssembly = newContext; - } - - var imports = processedAssembly.Imports; - - foreach (var unityType in unityAssembly.ManifestModule!.TopLevelTypes) - ProcessType(processedAssembly, unityType, null, imports, ref typesUnstripped); - } - - Logger.Instance.LogTrace("Unstripped {UnstrippedTypeCount} types", typesUnstripped); - } - - private static void ProcessType(AssemblyRewriteContext processedAssembly, TypeDefinition unityType, - TypeDefinition? enclosingNewType, RuntimeAssemblyReferences imports, ref int typesUnstripped) - { - if (unityType.Name == "") - return; - - // Don't unstrip delegates, the il2cpp runtime methods are stripped and we cannot recover them - if (unityType.BaseType != null && unityType.BaseType.FullName == "System.MulticastDelegate") - return; - var newModule = processedAssembly.NewAssembly.ManifestModule!; - var processedType = enclosingNewType == null - ? processedAssembly.TryGetTypeByName(unityType.FullName)?.NewType - : enclosingNewType.NestedTypes.SingleOrDefault(it => it.Name == unityType.Name); - if (unityType.IsEnum) - { - if (processedType != null) return; - - typesUnstripped++; - var clonedType = CloneEnum(unityType, imports); - if (enclosingNewType == null) - { - newModule.TopLevelTypes.Add(clonedType); - } - else - { - enclosingNewType.NestedTypes.Add(clonedType); - } - - processedAssembly.RegisterTypeRewrite(new TypeRewriteContext(processedAssembly, null, clonedType)); - - return; - } - - if (processedType == null && !unityType.IsEnum && !HasNonBlittableFields(unityType) && - !unityType.HasGenericParameters()) // restore all types even if it would be not entirely correct - { - typesUnstripped++; - var clonedType = new TypeDefinition(unityType.Namespace, unityType.Name, ForcePublic(unityType.Attributes), unityType.BaseType == null ? null : newModule.DefaultImporter.ImportType(unityType.BaseType)); - if (enclosingNewType == null) - { - newModule.TopLevelTypes.Add(clonedType); - } - else - { - enclosingNewType.NestedTypes.Add(clonedType); - } - - // Unity assemblies sometimes have struct layouts on classes. - // This gets overlooked on mono but not on coreclr. - if (!clonedType.IsValueType() && (clonedType.IsExplicitLayout || clonedType.IsSequentialLayout)) - { - clonedType.IsExplicitLayout = clonedType.IsSequentialLayout = false; - clonedType.IsAutoLayout = true; - } - - processedAssembly.RegisterTypeRewrite(new TypeRewriteContext(processedAssembly, null, clonedType)); - processedType = clonedType; - } - - foreach (var nestedUnityType in unityType.NestedTypes) - ProcessType(processedAssembly, nestedUnityType, processedType, imports, ref typesUnstripped); - } - - private static TypeDefinition CloneEnum(TypeDefinition sourceEnum, RuntimeAssemblyReferences imports) - { - var newType = new TypeDefinition(sourceEnum.Namespace, sourceEnum.Name, ForcePublic(sourceEnum.Attributes), - imports.Module.Enum().ToTypeDefOrRef()); - foreach (var sourceEnumField in sourceEnum.Fields) - { - TypeSignature fieldType = sourceEnumField.Name == "value__" - ? imports.Module.ImportCorlibReference(sourceEnumField.Signature!.FieldType.FullName) - : newType.ToTypeSignature(); - var newField = new FieldDefinition(sourceEnumField.Name, sourceEnumField.Attributes, new FieldSignature(fieldType)); - newField.Constant = sourceEnumField.Constant; - newType.Fields.Add(newField); - } - - return newType; - } - - private static bool HasNonBlittableFields(TypeDefinition type) - { - if (!type.IsValueType()) return false; - - var typeSignature = type.ToTypeSignature(); - foreach (var fieldDefinition in type.Fields) - { - if (fieldDefinition.IsStatic || SignatureComparer.Default.Equals(fieldDefinition.Signature?.FieldType, typeSignature)) - continue; - - if (!fieldDefinition.Signature!.FieldType.IsValueType()) - return true; - - if (fieldDefinition.Signature.FieldType.Namespace?.StartsWith("System") ?? false && - HasNonBlittableFields(fieldDefinition.Signature.FieldType.Resolve())) - return true; - } - - return false; - } - - private static TypeAttributes ForcePublic(TypeAttributes typeAttributes) - { - var visibility = typeAttributes & TypeAttributes.VisibilityMask; - if (visibility == 0 || visibility == TypeAttributes.Public) - return typeAttributes | TypeAttributes.Public; - - return (typeAttributes & ~TypeAttributes.VisibilityMask) | TypeAttributes.NestedPublic; - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass80UnstripFields.cs b/Il2CppInterop.Generator/Passes/Pass80UnstripFields.cs deleted file mode 100644 index 63bc0a9b..00000000 --- a/Il2CppInterop.Generator/Passes/Pass80UnstripFields.cs +++ /dev/null @@ -1,65 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Common; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass80UnstripFields -{ - public static void DoPass(RewriteGlobalContext context) - { - var fieldsUnstripped = 0; - var fieldsIgnored = 0; - - foreach (var unityAssembly in context.UnityAssemblies.Assemblies) - { - var processedAssembly = context.TryGetAssemblyByName(unityAssembly.Name); - if (processedAssembly == null) continue; - var imports = processedAssembly.Imports; - - foreach (var unityType in unityAssembly.ManifestModule!.TopLevelTypes) - { - var processedType = processedAssembly.TryGetTypeByName(unityType.FullName); - if (processedType == null) continue; - - if (unityType.IsEnum) - continue;// Enum fields do not get stripped. - - foreach (var unityField in unityType.Fields) - { - if (unityField.IsStatic && !unityField.HasConstant()) - continue;// Non-constant static fields might require initialization, which we can't do. - if (unityField.IsInstance() && (processedType.OriginalType is not null || processedType.NewType.IsReferenceType())) - continue;// Instance fields are only supported on newly created value types. - - var processedField = processedType.TryGetFieldByUnityAssemblyField(unityField); - if (processedField != null) continue; - - var fieldType = - Pass80UnstripMethods.ResolveTypeInNewAssemblies(context, unityField.Signature!.FieldType, imports); - if (fieldType == null) - { - Logger.Instance.LogTrace("Field {UnityField} on type {UnityType} has unsupported type {UnityFieldType}, the type will be unusable", unityField.ToString(), unityType.FullName, unityField.Signature.FieldType.ToString()); - fieldsIgnored++; - continue; - } - - var newField = new FieldDefinition(unityField.Name!, - (unityField.Attributes & ~FieldAttributes.FieldAccessMask) | FieldAttributes.Public, fieldType); - - if (unityField.HasConstant()) newField.Constant = unityField.Constant; - - processedType.NewType.Fields.Add(newField); - - fieldsUnstripped++; - } - } - } - - Logger.Instance.LogInformation("Restored {FieldsUnstripped} fields", fieldsUnstripped); - Logger.Instance.LogInformation("Failed to restore {FieldsIgnored} fields", fieldsIgnored); - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs b/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs deleted file mode 100644 index 04a285bf..00000000 --- a/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs +++ /dev/null @@ -1,291 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Common; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass80UnstripMethods -{ - public static void DoPass(RewriteGlobalContext context) - { - var methodsUnstripped = 0; - var methodsIgnored = 0; - - foreach (var unityAssembly in context.UnityAssemblies.Assemblies) - { - var processedAssembly = context.TryGetAssemblyByName(unityAssembly.Name); - if (processedAssembly == null) continue; - var imports = processedAssembly.Imports; - - foreach (var unityType in unityAssembly.ManifestModule!.TopLevelTypes) - { - var processedType = processedAssembly.TryGetTypeByName(unityType.FullName); - if (processedType == null) continue; - - foreach (var unityMethod in unityType.Methods) - { - var isICall = (unityMethod.ImplAttributes & MethodImplAttributes.InternalCall) != 0; - if (unityMethod.IsConstructor) continue; - if (unityMethod.IsAbstract) continue; - if (!unityMethod.HasMethodBody && !isICall) continue; // CoreCLR chokes on no-body methods - - var processedMethod = processedType.TryGetMethodByUnityAssemblyMethod(unityMethod); - if (processedMethod != null) continue; - - var returnType = ResolveTypeInNewAssemblies(context, unityMethod.Signature!.ReturnType, imports); - if (returnType == null) - { - Logger.Instance.LogTrace("Method {UnityMethod} has unsupported return type {UnityMethodReturnType}", unityMethod.ToString(), unityMethod.Signature.ReturnType.ToString()); - methodsIgnored++; - continue; - } - - var newAttributes = (unityMethod.Attributes & ~MethodAttributes.MemberAccessMask) | MethodAttributes.Public; - var newMethod = new MethodDefinition(unityMethod.Name, - newAttributes, - MethodSignatureCreator.CreateMethodSignature(newAttributes, returnType, unityMethod.Signature.GenericParameterCount)); - newMethod.CilMethodBody = new(); - var hadBadParameter = false; - foreach (var unityMethodParameter in unityMethod.Parameters) - { - var convertedType = - ResolveTypeInNewAssemblies(context, unityMethodParameter.ParameterType, imports); - if (convertedType == null) - { - hadBadParameter = true; - Logger.Instance.LogTrace("Method {UnityMethod} has unsupported parameter type {UnityMethodParameter}", unityMethod.ToString(), unityMethodParameter.ToString()); - break; - } - - newMethod.AddParameter(convertedType, unityMethodParameter.Name, unityMethodParameter.Definition?.Attributes ?? default); - } - - if (hadBadParameter) - { - methodsIgnored++; - continue; - } - - foreach (var unityMethodGenericParameter in unityMethod.GenericParameters) - { - var newParameter = new GenericParameter(unityMethodGenericParameter.Name.MakeValidInSource()); - newParameter.Attributes = unityMethodGenericParameter.Attributes; - foreach (var genericParameterConstraint in unityMethodGenericParameter.Constraints) - { - if (genericParameterConstraint.IsSystemValueType() || genericParameterConstraint.IsInterface()) - continue; - - if (genericParameterConstraint.IsSystemEnum()) - { - newParameter.Constraints.Add(new GenericParameterConstraint(imports.Module.Enum().ToTypeDefOrRef())); - continue; - } - - var newType = ResolveTypeInNewAssemblies(context, genericParameterConstraint.Constraint?.ToTypeSignature(), - imports); - if (newType != null) - newParameter.Constraints.Add(new GenericParameterConstraint(newType.ToTypeDefOrRef())); - } - - newMethod.GenericParameters.Add(newParameter); - } - - if (isICall) - { - var delegateType = - UnstripGenerator.CreateDelegateTypeForICallMethod(unityMethod, newMethod, imports); - processedType.NewType.NestedTypes.Add(delegateType); - - processedType.NewType.Methods.Add(newMethod); - - var delegateField = UnstripGenerator.GenerateStaticCtorSuffix(processedType.NewType, - delegateType, unityMethod, imports); - UnstripGenerator.GenerateInvokerMethodBody(newMethod, delegateField, delegateType, - processedType, imports); - } - else - { - Pass81FillUnstrippedMethodBodies.PushMethod(unityMethod, newMethod, processedType, imports); - processedType.NewType.Methods.Add(newMethod); - } - - if (unityMethod.IsGetMethod) - { - var property = GetOrCreateProperty(unityMethod, newMethod); - property.GetMethod = newMethod; - } - else if (unityMethod.IsSetMethod) - { - var property = GetOrCreateProperty(unityMethod, newMethod); - property.SetMethod = newMethod; - } - - var paramsMethod = context.CreateParamsMethod(unityMethod, newMethod, imports, - type => ResolveTypeInNewAssemblies(context, type, imports)); - if (paramsMethod != null) processedType.NewType.Methods.Add(paramsMethod); - - methodsUnstripped++; - } - } - } - - Logger.Instance.LogInformation("Restored {UnstrippedMethods} methods", methodsUnstripped); - Logger.Instance.LogInformation("Failed to restore {IgnoredMethods} methods", methodsIgnored); - } - - private static PropertyDefinition GetOrCreateProperty(MethodDefinition unityMethod, MethodDefinition newMethod) - { - var unityProperty = - unityMethod.DeclaringType!.Properties.Single( - it => it.SetMethod == unityMethod || it.GetMethod == unityMethod); - var newProperty = newMethod.DeclaringType?.Properties.SingleOrDefault(it => - it.Name == unityProperty.Name && it.Signature!.ParameterTypes.Count == unityProperty.Signature!.ParameterTypes.Count && - it.Signature.ParameterTypes.SequenceEqual(unityProperty.Signature.ParameterTypes, SignatureComparer.Default)); - if (newProperty == null) - { - TypeSignature propertyType; - IEnumerable parameterTypes; - if (unityMethod.IsGetMethod) - { - propertyType = newMethod.Signature!.ReturnType; - parameterTypes = newMethod.Signature.ParameterTypes; - } - else - { - propertyType = newMethod.Signature!.ParameterTypes.Last(); - parameterTypes = newMethod.Signature.ParameterTypes.Take(newMethod.Signature.ParameterTypes.Count - 1); - } - - var propertySignature = unityProperty.Signature!.HasThis - ? PropertySignature.CreateInstance(propertyType, parameterTypes) - : PropertySignature.CreateStatic(propertyType, parameterTypes); - newProperty = new PropertyDefinition(unityProperty.Name, unityProperty.Attributes, propertySignature); - newMethod.DeclaringType!.Properties.Add(newProperty); - } - - return newProperty; - } - - internal static TypeSignature? ResolveTypeInNewAssemblies(RewriteGlobalContext context, TypeSignature? unityType, - RuntimeAssemblyReferences imports, bool useSystemCorlibPrimitives = true) - { - var resolved = ResolveTypeInNewAssembliesRaw(context, unityType, imports, useSystemCorlibPrimitives); - return resolved != null ? imports.Module.DefaultImporter.ImportTypeSignature(resolved) : null; - } - - internal static TypeSignature? ResolveTypeInNewAssembliesRaw(RewriteGlobalContext context, TypeSignature? unityType, - RuntimeAssemblyReferences imports, bool useSystemCorlibPrimitives = true) - { - if (unityType is null) - return null; - - if (unityType is GenericParameterSignature genericParameterSignature) - return new GenericParameterSignature(imports.Module, genericParameterSignature.ParameterType, genericParameterSignature.Index); - - if (unityType is ByReferenceTypeSignature) - { - var resolvedElementType = ResolveTypeInNewAssemblies(context, unityType.GetElementType(), imports); - return resolvedElementType?.MakeByReferenceType(); - } - - if (unityType is ArrayBaseTypeSignature arrayType) - { - if (arrayType.Rank != 1) return null; - var resolvedElementType = ResolveTypeInNewAssemblies(context, unityType.GetElementType(), imports); - if (resolvedElementType == null) return null; - if (resolvedElementType.FullName == "System.String") - return imports.Il2CppStringArray; - var genericBase = resolvedElementType switch - { - GenericParameterSignature => imports.Il2CppArrayBase, - { IsValueType: true } => imports.Il2CppStructArray, - _ => imports.Il2CppReferenceArray - }; - return new GenericInstanceTypeSignature(genericBase.ToTypeDefOrRef(), false, resolvedElementType); - } - - if (unityType is PointerTypeSignature) - { - var resolvedElementType = ResolveTypeInNewAssemblies(context, unityType.GetElementType(), imports); - return resolvedElementType?.MakePointerType(); - } - - if (unityType is PinnedTypeSignature) - { - var resolvedElementType = ResolveTypeInNewAssemblies(context, unityType.GetElementType(), imports); - return resolvedElementType?.MakePinnedType(); - } - - if (unityType is CustomModifierTypeSignature customModifier) - { - var resolvedElementType = ResolveTypeInNewAssemblies(context, customModifier.BaseType, imports); - var resolvedModifierType = ResolveTypeInNewAssemblies(context, customModifier.ModifierType.ToTypeSignature(), imports); - return resolvedElementType is not null && resolvedModifierType is not null - ? new CustomModifierTypeSignature(resolvedModifierType.ToTypeDefOrRef(), customModifier.IsRequired, resolvedElementType) - : null; - } - - if (unityType is GenericInstanceTypeSignature genericInstance) - { - var baseRef = ResolveTypeInNewAssembliesRaw(context, genericInstance.GenericType.ToTypeSignature(), imports); - if (baseRef == null) return null; - var newInstance = new GenericInstanceTypeSignature(baseRef.ToTypeDefOrRef(), baseRef.IsValueType()); - foreach (var unityGenericArgument in genericInstance.TypeArguments) - { - var resolvedArgument = ResolveTypeInNewAssemblies(context, unityGenericArgument, imports); - if (resolvedArgument == null) return null; - newInstance.TypeArguments.Add(resolvedArgument); - } - - return newInstance; - } - - if (unityType is BoxedTypeSignature) - return null; // Boxed types are not yet supported - - if (unityType is FunctionPointerTypeSignature) - return null; // Function pointers are not yet supported - - if (unityType is SentinelTypeSignature) - return unityType; // SentinelTypeSignature has no state and be reused. - - if (unityType.DeclaringType != null) - { - var enclosingResolvedType = ResolveTypeInNewAssembliesRaw(context, unityType.DeclaringType.ToTypeSignature(), imports); - if (enclosingResolvedType == null) return null; - var resolvedNestedType = enclosingResolvedType.Resolve()!.NestedTypes - .FirstOrDefault(it => it.Name == unityType.Name); - - return resolvedNestedType?.ToTypeSignature(); - } - - var targetAssemblyName = unityType.Scope!.Name!; - if (targetAssemblyName.EndsWith(".dll")) - targetAssemblyName = targetAssemblyName.Substring(0, targetAssemblyName.Length - 4); - - if (useSystemCorlibPrimitives && (unityType.IsPrimitive() || unityType.ElementType is ElementType.String or ElementType.Void)) - return imports.Module.CorLibTypeFactory.FromElementType(unityType.ElementType); - - if (targetAssemblyName == "UnityEngine") - foreach (var assemblyRewriteContext in context.Assemblies) - { - if (!assemblyRewriteContext.NewAssembly.Name.StartsWith("UnityEngine")) - continue; - - var newTypeInAnyUnityAssembly = - assemblyRewriteContext.TryGetTypeByName(unityType.FullName)?.NewType; - if (newTypeInAnyUnityAssembly != null) - return newTypeInAnyUnityAssembly.ToTypeSignature(); - } - - var targetAssembly = context.TryGetAssemblyByName(targetAssemblyName); - var newType = targetAssembly?.TryGetTypeByName(unityType.FullName)?.NewType.ToTypeSignature(); - - return newType; - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass81FillUnstrippedMethodBodies.cs b/Il2CppInterop.Generator/Passes/Pass81FillUnstrippedMethodBodies.cs deleted file mode 100644 index 705e419a..00000000 --- a/Il2CppInterop.Generator/Passes/Pass81FillUnstrippedMethodBodies.cs +++ /dev/null @@ -1,47 +0,0 @@ -using AsmResolver.DotNet; -using Il2CppInterop.Common; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Utils; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass81FillUnstrippedMethodBodies -{ - private static readonly - List<(MethodDefinition unityMethod, MethodDefinition newMethod, TypeRewriteContext processedType, - RuntimeAssemblyReferences imports)> StuffToProcess = - new(); - - public static void DoPass(RewriteGlobalContext context) - { - var methodsSucceeded = 0; - var methodsFailed = 0; - - foreach (var (unityMethod, newMethod, processedType, imports) in StuffToProcess) - { - var success = UnstripTranslator.TranslateMethod(unityMethod, newMethod, processedType, imports); - if (success == false) - { - methodsFailed++; - UnstripTranslator.ReplaceBodyWithException(newMethod, imports); - } - else - { - methodsSucceeded++; - } - } - - StuffToProcess.Clear(); - StuffToProcess.Capacity = 0; - - Logger.Instance.LogInformation("IL unstrip statistics: {MethodsSucceeded} successful, {MethodsFailed} failed", methodsSucceeded, - methodsFailed); - } - - public static void PushMethod(MethodDefinition unityMethod, MethodDefinition newMethod, - TypeRewriteContext processedType, RuntimeAssemblyReferences imports) - { - StuffToProcess.Add((unityMethod, newMethod, processedType, imports)); - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass89GenerateForwarders.cs b/Il2CppInterop.Generator/Passes/Pass89GenerateForwarders.cs deleted file mode 100644 index 70b77aee..00000000 --- a/Il2CppInterop.Generator/Passes/Pass89GenerateForwarders.cs +++ /dev/null @@ -1,60 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Common; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass89GenerateForwarders -{ - public static void DoPass(RewriteGlobalContext context) - { - var targetAssembly = context.TryGetAssemblyByName("UnityEngine"); - if (targetAssembly == null) - { - Logger.Instance.LogInformation("No UnityEngine.dll, will not generate forwarders"); - return; - } - - var targetModule = targetAssembly.NewAssembly.ManifestModule; - - foreach (var assemblyRewriteContext in context.Assemblies) - { - if (!assemblyRewriteContext.NewAssembly.Name.StartsWith("UnityEngine.")) continue; - foreach (var mainModuleType in assemblyRewriteContext.NewAssembly.ManifestModule!.TopLevelTypes) - { - if (mainModuleType.Name == "") - continue; - - var exportedType = new ExportedType(null, mainModuleType.Namespace, mainModuleType.Name) - { - Attributes = TypeAttributes.Forwarder - }; - targetModule!.ExportedTypes.Add(exportedType); - - AddNestedTypes(mainModuleType, exportedType, targetModule); - } - } - } - - private static void AddNestedTypes(TypeDefinition mainModuleType, ExportedType importedType, - ModuleDefinition targetModule) - { - foreach (var nested in mainModuleType.NestedTypes) - { - if ((nested.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.NestedPublic) continue; - - var nestedImport = targetModule.DefaultImporter.ImportType(nested); - var nestedExport = new ExportedType(importedType, nestedImport.Namespace, nestedImport.Name) - { - Attributes = TypeAttributes.Forwarder - }; - - targetModule.ExportedTypes.Add(nestedExport); - - AddNestedTypes(nested, nestedExport, targetModule); - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass89GenerateMethodXrefCache.cs b/Il2CppInterop.Generator/Passes/Pass89GenerateMethodXrefCache.cs deleted file mode 100644 index 4ee5a9ab..00000000 --- a/Il2CppInterop.Generator/Passes/Pass89GenerateMethodXrefCache.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System.Text; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using Il2CppInterop.Common.Attributes; -using Il2CppInterop.Common.Maps; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass89GenerateMethodXrefCache -{ - public static void DoPass(RewriteGlobalContext context, GeneratorOptions options) - { - var data = new List(); - var existingAttributesPerAddress = new Dictionary(); - - if (options.NoXrefCache) - goto skipDataGather; - - foreach (var assemblyRewriteContext in context.Assemblies) - { - if (options.AdditionalAssembliesBlacklist.Contains(assemblyRewriteContext.NewAssembly.Name!)) - continue; - - var imports = assemblyRewriteContext.Imports; - - foreach (var typeRewriteContext in assemblyRewriteContext.Types) - foreach (var methodRewriteContext in typeRewriteContext.Methods) - { - var address = methodRewriteContext.Rva; - - if (existingAttributesPerAddress.TryGetValue(address, out var attribute)) - { - methodRewriteContext.NewMethod.CustomAttributes.Add( - new CustomAttribute((ICustomAttributeType?)imports.CachedScanResultsAttributector.Value, new CustomAttributeSignature([], - [ - new CustomAttributeNamedArgument( - CustomAttributeArgumentMemberType.Field, - nameof(CachedScanResultsAttribute.RefRangeStart), - imports.Module.Int(), - new CustomAttributeArgument(imports.Module.Int(), attribute.RefRangeStart)), - new CustomAttributeNamedArgument( - CustomAttributeArgumentMemberType.Field, - nameof(CachedScanResultsAttribute.RefRangeEnd), - imports.Module.Int(), - new CustomAttributeArgument(imports.Module.Int(), attribute.RefRangeEnd)), - new CustomAttributeNamedArgument( - CustomAttributeArgumentMemberType.Field, - nameof(CachedScanResultsAttribute.XrefRangeStart), - imports.Module.Int(), - new CustomAttributeArgument(imports.Module.Int(), attribute.RefRangeStart)), - new CustomAttributeNamedArgument( - CustomAttributeArgumentMemberType.Field, - nameof(CachedScanResultsAttribute.XrefRangeEnd), - imports.Module.Int(), - new CustomAttributeArgument(imports.Module.Int(), attribute.RefRangeEnd)), - new CustomAttributeNamedArgument( - CustomAttributeArgumentMemberType.Field, - nameof(CachedScanResultsAttribute.MetadataInitTokenRva), - imports.Module.Long(), - new CustomAttributeArgument(imports.Module.Long(), attribute.MetadataInitTokenRva)), - new CustomAttributeNamedArgument( - CustomAttributeArgumentMemberType.Field, - nameof(CachedScanResultsAttribute.MetadataInitFlagRva), - imports.Module.Long(), - new CustomAttributeArgument(imports.Module.Long(), attribute.MetadataInitFlagRva)) - ]))); - continue; - } - - var xrefStart = data.Count; - - foreach (var xrefScanResult in methodRewriteContext.XrefScanResults) - data.Add(MethodXrefScanCache.MethodData.FromXrefInstance(xrefScanResult)); - - var xrefEnd = data.Count; - - var refStart = 0; - var refEnd = 0; - - if (address != 0) - if (Pass16ScanMethodRefs.MapOfCallers.TryGetValue(address, out var callerMap)) - { - refStart = data.Count; - foreach (var xrefInstance in callerMap) - data.Add(MethodXrefScanCache.MethodData.FromXrefInstance(xrefInstance)); - - refEnd = data.Count; - } - - if (xrefEnd != xrefStart || refStart != refEnd) - { - methodRewriteContext.NewMethod.CustomAttributes.Add( - new CustomAttribute((ICustomAttributeType?)imports.CachedScanResultsAttributector.Value, new CustomAttributeSignature([], - [ - new CustomAttributeNamedArgument( - CustomAttributeArgumentMemberType.Field, - nameof(CachedScanResultsAttribute.RefRangeStart), - imports.Module.Int(), - new CustomAttributeArgument(imports.Module.Int(), refStart)), - new CustomAttributeNamedArgument( - CustomAttributeArgumentMemberType.Field, - nameof(CachedScanResultsAttribute.RefRangeEnd), - imports.Module.Int(), - new CustomAttributeArgument(imports.Module.Int(), refEnd)), - new CustomAttributeNamedArgument( - CustomAttributeArgumentMemberType.Field, - nameof(CachedScanResultsAttribute.XrefRangeStart), - imports.Module.Int(), - new CustomAttributeArgument(imports.Module.Int(), xrefStart)), - new CustomAttributeNamedArgument( - CustomAttributeArgumentMemberType.Field, - nameof(CachedScanResultsAttribute.XrefRangeEnd), - imports.Module.Int(), - new CustomAttributeArgument(imports.Module.Int(), xrefEnd)), - new CustomAttributeNamedArgument( - CustomAttributeArgumentMemberType.Field, - nameof(CachedScanResultsAttribute.MetadataInitTokenRva), - imports.Module.Long(), - new CustomAttributeArgument(imports.Module.Long(), methodRewriteContext.MetadataInitTokenRva)), - new CustomAttributeNamedArgument( - CustomAttributeArgumentMemberType.Field, - nameof(CachedScanResultsAttribute.MetadataInitFlagRva), - imports.Module.Long(), - new CustomAttributeArgument(imports.Module.Long(), methodRewriteContext.MetadataInitFlagRva)) - ]))); - - existingAttributesPerAddress[address] = new CachedScanResultsAttribute - { - RefRangeStart = refStart, - RefRangeEnd = refEnd, - XrefRangeStart = xrefStart, - XrefRangeEnd = xrefEnd, - MetadataInitFlagRva = methodRewriteContext.MetadataInitFlagRva, - MetadataInitTokenRva = methodRewriteContext.MetadataInitTokenRva - }; - } - } - } - - skipDataGather: - - var header = new MethodXrefScanCache.FileHeader - { - Magic = MethodXrefScanCache.Magic, - Version = MethodXrefScanCache.Version, - InitMethodMetadataRva = XrefScanMetadataGenerationUtil.MetadataInitForMethodRva - }; - - using var writer = - new BinaryWriter( - new FileStream(Path.Combine(options.OutputDir, MethodXrefScanCache.FileName), FileMode.Create, - FileAccess.Write), Encoding.UTF8, false); - writer.Write(header); - - foreach (var valueTuple in data) - writer.Write(valueTuple); - - if (options.Verbose) - { - using var plainTextWriter = - new StreamWriter(Path.Combine(options.OutputDir, MethodXrefScanCache.FileName + ".txt")); - for (var i = 0; i < data.Count; i++) - plainTextWriter.WriteLine($"{i}\t{data[i].Type}\t{data[i].Address}\t{data[i].FoundAt}"); - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass90WriteToDisk.cs b/Il2CppInterop.Generator/Passes/Pass90WriteToDisk.cs deleted file mode 100644 index 8bb6ea15..00000000 --- a/Il2CppInterop.Generator/Passes/Pass90WriteToDisk.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System.Runtime.Versioning; -using AsmResolver; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Builder; -using AsmResolver.DotNet.Signatures; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass90WriteToDisk -{ - public static void DoPass(RewriteGlobalContext context, GeneratorOptions options) - { - var targetAttributeConstructor = typeof(TargetFrameworkAttribute).GetConstructor([typeof(string)]); - - foreach (var asmContext in context.Assemblies) - { - var module = asmContext.NewAssembly.ManifestModule!; - - // Add TargetFrameworkAttribute to the assembly. - { - var importedConstructor = (ICustomAttributeType)module.DefaultImporter.ImportMethod(targetAttributeConstructor); - - CustomAttribute targetFrameworkAttribute = new(importedConstructor, new()); - - CustomAttributeArgument fixedArgument = new(module.CorLibTypeFactory.String, module.OriginalTargetRuntime.ToString()); - targetFrameworkAttribute.Signature!.FixedArguments.Add(fixedArgument); - - CustomAttributeNamedArgument namedArgument = new( - CustomAttributeArgumentMemberType.Property, - nameof(TargetFrameworkAttribute.FrameworkDisplayName), - module.CorLibTypeFactory.String, - new(module.CorLibTypeFactory.String, CorlibReferences.TargetFrameworkName)); - targetFrameworkAttribute.Signature.NamedArguments.Add(namedArgument); - - asmContext.NewAssembly.CustomAttributes.Add(targetFrameworkAttribute); - } - - // Rewrite corlib references. - foreach (var reference in module.AssemblyReferences) - { - // System.Private.CoreLib needs rewriting because references can get created during the rewrite process. - // mscorlib and netstandard are included for completeness. - if (reference.Name?.Value is "System.Private.CoreLib" or "mscorlib" or "netstandard") - { - CorlibReferences.RewriteCorlibReference(reference); - continue; - } - } - - // Optimize macros in all methods and assign tokens. - foreach (var type in module.GetAllTypes()) - { - foreach (var method in type.Methods) - { - method.CilMethodBody?.Instructions.OptimizeMacros(); - module.TokenAllocator.AssignNextAvailableToken(method); - } - } - } - - var assembliesToProcess = context.Assemblies - .Where(it => !options.AdditionalAssembliesBlacklist.Contains(it.NewAssembly.Name!)); - - void Processor(AssemblyRewriteContext assemblyContext) - { - assemblyContext.NewAssembly.Write( - Path.Combine(options.OutputDir ?? ".", $"{assemblyContext.NewAssembly.Name}.dll"), new ManagedPEImageBuilder(ThrowErrorListener.Instance)); - } - - if (options.Parallel) - { - Parallel.ForEach(assembliesToProcess, Processor); - } - else - { - foreach (var assemblyRewriteContext in assembliesToProcess) - { - Processor(assemblyRewriteContext); - } - } - } -} diff --git a/Il2CppInterop.Generator/Passes/Pass91GenerateMethodPointerMap.cs b/Il2CppInterop.Generator/Passes/Pass91GenerateMethodPointerMap.cs deleted file mode 100644 index 7d501591..00000000 --- a/Il2CppInterop.Generator/Passes/Pass91GenerateMethodPointerMap.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Reflection; -using System.Text; -using Il2CppInterop.Common.Maps; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; - -namespace Il2CppInterop.Generator.Passes; - -public static class Pass91GenerateMethodPointerMap -{ - public static void DoPass(RewriteGlobalContext context, GeneratorOptions options) - { - var data = new List<(long, int, int)>(); - var assemblyList = new List(); - - foreach (var assemblyRewriteContext in context.Assemblies) - { - if (options.AdditionalAssembliesBlacklist.Contains(assemblyRewriteContext.NewAssembly.Name!)) - continue; - - assemblyList.Add(assemblyRewriteContext.NewAssembly.FullName); - - foreach (var typeRewriteContext in assemblyRewriteContext.Types) - foreach (var methodRewriteContext in typeRewriteContext.Methods) - { - var address = methodRewriteContext.Rva; - - if (address != 0) - data.Add((address, methodRewriteContext.NewMethod.MetadataToken.ToInt32(), assemblyList.Count - 1)); - } - } - - data.Sort((a, b) => a.Item1.CompareTo(b.Item1)); - - var header = new MethodAddressToTokenMapFileHeader - { - Magic = MethodAddressToTokenMapBase.Magic, - Version = MethodAddressToTokenMapBase.Version, - NumMethods = data.Count, - NumAssemblies = assemblyList.Count - }; - - using var writer = - new BinaryWriter( - new FileStream( - Path.Combine(options.OutputDir, MethodAddressToTokenMapBase.FileName), - FileMode.Create, FileAccess.Write), Encoding.UTF8, false); - writer.Write(header); - - foreach (var s in assemblyList) - writer.Write(s); - - header.DataOffset = (int)writer.BaseStream.Position; - - foreach (var valueTuple in data) - writer.Write(valueTuple.Item1); - - foreach (var valueTuple in data) - { - writer.Write(valueTuple.Item2); - writer.Write(valueTuple.Item3); - } - - writer.BaseStream.Position = 0; - writer.Write(header); - - if (options.Verbose) - { - using var plainTextWriter = new StreamWriter(Path.Combine(options.OutputDir, - MethodAddressToTokenMapBase.FileName + ".txt")); - for (var i = 0; i < data.Count; i++) - plainTextWriter.WriteLine($"{i}\t{data[i].Item1}\t{data[i].Item2}\t{data[i].Item3}"); - } - } -} diff --git a/Il2CppInterop.Generator/PointerConstructorProcessingLayer.cs b/Il2CppInterop.Generator/PointerConstructorProcessingLayer.cs new file mode 100644 index 00000000..d72f8c61 --- /dev/null +++ b/Il2CppInterop.Generator/PointerConstructorProcessingLayer.cs @@ -0,0 +1,97 @@ +using System.Diagnostics; +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Common; +using Il2CppInterop.Generator.Operands; + +namespace Il2CppInterop.Generator; + +public class PointerConstructorProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Pointer Constructor Processor"; + public override string Id => "pointer_constructor_processor"; + + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var objectPointerType = appContext.ResolveTypeOrThrow(typeof(ObjectPointer)); + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInjected || type.IsInterface) + continue; + + Debug.Assert(!type.IsStatic, "Static types should have been marked as instance types by now."); + + if (type.IsValueType) + continue; + + // Constructors for formerly static types should be private + var attributes = type.DefaultAttributes.HasFlag(TypeAttributes.Abstract) && type.DefaultAttributes.HasFlag(TypeAttributes.Sealed) + ? MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName + : MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; + + var constructor = new InjectedMethodAnalysisContext( + type, + ".ctor", + appContext.SystemTypes.SystemVoidType, + attributes, + [objectPointerType]) + { + IsInjected = true, + }; + type.Methods.Add(constructor); + + type.PointerConstructor = constructor; + } + } + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + var constructor = type.PointerConstructor; + if (constructor is null) + continue; + + var baseType = type.BaseType; + if (baseType is null) + continue; + + MethodAnalysisContext? baseConstructor; + if (baseType is GenericInstanceTypeAnalysisContext genericInstanceType) + { + var m = genericInstanceType.GenericType.PointerConstructor; + if (m is null) + continue; + baseConstructor = new ConcreteGenericMethodAnalysisContext(m, genericInstanceType.GenericArguments, []); + } + else + { + baseConstructor = baseType.PointerConstructor; + if (baseConstructor is null) + continue; + } + + var methodBody = new NativeMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Ldarg_1), + new Instruction(CilOpCodes.Call, baseConstructor), + new Instruction(CilOpCodes.Ret) + ] + }; + constructor.PutExtraData(methodBody); + } + } + } +} diff --git a/Il2CppInterop.Generator/PrimitiveImplicitConversionProcessingLayer.cs b/Il2CppInterop.Generator/PrimitiveImplicitConversionProcessingLayer.cs new file mode 100644 index 00000000..21f068fd --- /dev/null +++ b/Il2CppInterop.Generator/PrimitiveImplicitConversionProcessingLayer.cs @@ -0,0 +1,155 @@ +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Common; +using Il2CppInterop.Generator.Operands; +using Il2CppInterop.Runtime; + +namespace Il2CppInterop.Generator; + +public class PrimitiveImplicitConversionProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Id => "primitive_implicit_conversions"; + public override string Name => "Primitive Implicit Conversions"; + + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var il2CppMscorlib = appContext.Il2CppMscorlib; + var mscorlib = appContext.Mscorlib; + + // Il2CppSystem.String + { + var il2CppType = il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.String"); + var monoType = mscorlib.GetTypeByFullNameOrThrow("System.String"); + + var objectPointerType = appContext.ResolveTypeOrThrow(typeof(ObjectPointer)); + var objectPointerConversionFromIntPtr = objectPointerType.GetExplicitConversionFrom(appContext.SystemTypes.SystemIntPtrType); + + var generationInternalsType = appContext.ResolveTypeOrThrow(typeof(GenerationInternals)); + var managedStringToIl2Cpp = generationInternalsType.Methods.First(m => m.Name == nameof(GenerationInternals.ManagedStringToIl2Cpp)); + var il2CppStringToManaged = generationInternalsType.Methods.First(m => m.Name == nameof(GenerationInternals.Il2CppStringToManaged)); + + // Il2Cpp -> Mono + { + var implicitConversion = new InjectedMethodAnalysisContext( + il2CppType, + "op_Implicit", + monoType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Static, + [il2CppType]); + implicitConversion.IsInjected = true; + il2CppType.Methods.Add(implicitConversion); + + implicitConversion.PutExtraData(new TranslatedMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Call, il2CppStringToManaged), // Returns null if the input is null + new Instruction(CilOpCodes.Ret), + ] + }); + } + + // Mono -> Il2Cpp + { + var implicitConversion = new InjectedMethodAnalysisContext( + il2CppType, + "op_Implicit", + il2CppType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Static, + [monoType]); + implicitConversion.IsInjected = true; + il2CppType.Methods.Add(implicitConversion); + + implicitConversion.PutExtraData(new TranslatedMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg_0), + new Instruction(CilOpCodes.Call, managedStringToIl2Cpp), // Returns null if the input is null + new Instruction(CilOpCodes.Ret), + ] + }); + } + } + + ReadOnlySpan<(string, string)> numericPairs = + [ + ("Il2CppSystem.SByte", "System.SByte"), + ("Il2CppSystem.Byte", "System.Byte"), + ("Il2CppSystem.Int16", "System.Int16"), + ("Il2CppSystem.UInt16", "System.UInt16"), + ("Il2CppSystem.Int32", "System.Int32"), + ("Il2CppSystem.UInt32", "System.UInt32"), + ("Il2CppSystem.Int64", "System.Int64"), + ("Il2CppSystem.UInt64", "System.UInt64"), + ("Il2CppSystem.Single", "System.Single"), + ("Il2CppSystem.Double", "System.Double"), + ("Il2CppSystem.Char", "System.Char"), + ("Il2CppSystem.Boolean", "System.Boolean"), + ("Il2CppSystem.IntPtr", "System.IntPtr"), + ("Il2CppSystem.UIntPtr", "System.UIntPtr"), + ]; + + foreach (var (il2CppTypeName, monoTypeName) in numericPairs) + { + var il2CppType = il2CppMscorlib.GetTypeByFullNameOrThrow(il2CppTypeName); + var monoType = mscorlib.GetTypeByFullNameOrThrow(monoTypeName); + var field = il2CppType.Fields.Single(f => !f.IsStatic); + field.OverrideFieldType = monoType; + + if (monoTypeName is "System.Boolean") + { + // Not sure this does anything meaningful + + // Ensure boolean size is 1 byte + il2CppType.Definition!.RawSizes.native_size = 1; + + // The fact that we have to change the layout here might indicate an issue in Cpp2IL. + // It only emits sizes for structs with explicit layout, but not for sequential layout. + il2CppType.Attributes = (il2CppType.Attributes & ~TypeAttributes.LayoutMask) | TypeAttributes.ExplicitLayout; + } + + // Il2Cpp -> Mono + { + var implicitConversion = new InjectedMethodAnalysisContext( + il2CppType, + "op_Implicit", + monoType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Static, + [il2CppType]); + implicitConversion.IsInjected = true; + implicitConversion.PutExtraData(new TranslatedMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarg, implicitConversion.Parameters[0]), + new Instruction(CilOpCodes.Ldfld, field), + new Instruction(CilOpCodes.Ret), + ] + }); + il2CppType.Methods.Add(implicitConversion); + } + + // Mono -> Il2Cpp + { + var implicitConversion = new InjectedMethodAnalysisContext( + il2CppType, + "op_Implicit", + il2CppType, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Static, + [monoType]); + implicitConversion.IsInjected = true; + implicitConversion.PutExtraData(new TranslatedMethodBody() + { + Instructions = [ + new Instruction(CilOpCodes.Ldarga, implicitConversion.Parameters[0]), + new Instruction(CilOpCodes.Conv_U), + new Instruction(CilOpCodes.Ldobj, il2CppType), + new Instruction(CilOpCodes.Ret), + ] + }); + il2CppType.Methods.Add(implicitConversion); + } + } + } + +} diff --git a/Il2CppInterop.Generator/PublicizerProcessingLayer.cs b/Il2CppInterop.Generator/PublicizerProcessingLayer.cs new file mode 100644 index 00000000..c5d5d6e0 --- /dev/null +++ b/Il2CppInterop.Generator/PublicizerProcessingLayer.cs @@ -0,0 +1,57 @@ +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator; + +public class PublicizerProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Id => "publicizer"; + public override string Name => "Publicizer"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + { + continue; + } + + foreach (var type in assembly.Types) + { + if (type.IsInjected) + { + continue; + } + + type.Visibility = type.DeclaringType is null ? TypeAttributes.Public : TypeAttributes.NestedPublic; + + foreach (var field in type.Fields) + { + if (!field.IsInjected) + { + field.Visibility = FieldAttributes.Public; + } + } + + foreach (var method in type.Methods) + { + if (method.Visibility != MethodAttributes.Public) + { + if (method.IsStaticConstructor || method.IsInjected) + { + continue; + } + + if (method.ImplementsAnInterfaceMethod) + { + continue; + } + + method.Visibility = MethodAttributes.Public; + } + } + } + } + } +} diff --git a/Il2CppInterop.Generator/ReferenceAssemblyInjectionProcessingLayer.cs b/Il2CppInterop.Generator/ReferenceAssemblyInjectionProcessingLayer.cs new file mode 100644 index 00000000..9c1bf4a7 --- /dev/null +++ b/Il2CppInterop.Generator/ReferenceAssemblyInjectionProcessingLayer.cs @@ -0,0 +1,87 @@ +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Common; +using Il2CppInterop.Common.Attributes; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.Exceptions; +using Il2CppInterop.Runtime.Injection; +using Il2CppInterop.Runtime.InteropTypes; +using Il2CppInterop.Runtime.InteropTypes.Arrays; +using Il2CppInterop.Runtime.Runtime; + +namespace Il2CppInterop.Generator; + +public class ReferenceAssemblyInjectionProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Id => "reference_assembly_injector"; + public override string Name => "Inject required references into the Cpp2IL context system"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + Type[] il2CppInteropCommonTypes = + [ + typeof(ObjectPointer), + + typeof(Il2CppMemberAttribute), + typeof(Il2CppMethodAttribute), + typeof(Il2CppFieldAttribute), + typeof(Il2CppPropertyAttribute), + typeof(Il2CppEventAttribute), + typeof(Il2CppTypeAttribute), + + typeof(IIl2CppType), + typeof(IIl2CppType<>), + ]; + InjectTypes(appContext, typeof(ObjectPointer).Assembly, il2CppInteropCommonTypes); + + Type[] il2CppInteropRuntimeTypes = + [ + typeof(Il2CppArrayBase), + typeof(Il2CppArrayBase<>), + typeof(Il2CppArrayRank1<>), + typeof(Il2CppArrayRank2<>), + typeof(Il2CppArrayRank3<>), + + typeof(Runtime.Exceptions.Il2CppException), + typeof(IL2CPP), + typeof(Il2CppClassPointerStore<>), + typeof(Il2CppObjectPool), + typeof(TypeInjector), + typeof(DelegateSupport), + typeof(Pointer<>), + typeof(ByReference<>), + typeof(ByReference), + typeof(FieldAccess), + typeof(IIl2CppException), + typeof(RuntimeInvoke), + typeof(Il2CppType), + typeof(GenerationInternals), + ]; + InjectTypes(appContext, typeof(Il2CppArrayBase).Assembly, il2CppInteropRuntimeTypes); + } + + /// + /// Injects the given assembly and some of its types into the . + /// + /// The + /// The assembly + /// The types to be injected from . Must be in order of inheritance + private static void InjectTypes(ApplicationAnalysisContext appContext, Assembly assembly, Type[] types) + { + var il2CppInteropRuntime = appContext.InjectAssembly(assembly); + + il2CppInteropRuntime.IsReferenceAssembly = true; + + var typeContextArray = new InjectedTypeAnalysisContext[types.Length]; + + for (var i = 0; i < types.Length; i++) + { + typeContextArray[i] = il2CppInteropRuntime.InjectType(types[i]); + } + + for (var index = 0; index < types.Length; index++) + { + typeContextArray[index].InjectContentFromSourceType(); + } + } +} diff --git a/Il2CppInterop.Generator/ReferenceReplacementProcessingLayer.cs b/Il2CppInterop.Generator/ReferenceReplacementProcessingLayer.cs new file mode 100644 index 00000000..1196fd0e --- /dev/null +++ b/Il2CppInterop.Generator/ReferenceReplacementProcessingLayer.cs @@ -0,0 +1,113 @@ +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Visitors; + +namespace Il2CppInterop.Generator; + +public class ReferenceReplacementProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Id => "reference_replacement"; + public override string Name => "Reference Replacement"; + + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var il2CppMscorlib = appContext.AssembliesByName["Il2Cppmscorlib"]; + var mscorlib = appContext.AssembliesByName["mscorlib"]; + + var monoSystemObject = mscorlib.GetTypeByFullNameOrThrow("System.Object"); + var monoSystemValueType = mscorlib.GetTypeByFullNameOrThrow("System.ValueType"); + var monoSystemVoid = mscorlib.GetTypeByFullNameOrThrow("System.Void"); + + var il2CppSystemObject = il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Object"); + var il2CppSystemVoid = il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Void"); + var il2CppSystemEnum = il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Enum"); + var il2CppSystemValueType = il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.ValueType"); + + var visitor = TypeConversionVisitor.Create(appContext); + + il2CppSystemObject.OverrideBaseType = monoSystemObject; + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + + foreach (var type in assembly.Types) + { + if (type.IsInterface) + { + type.OverrideBaseType = null; + } + else if (type.IsStatic) + { + type.OverrideBaseType = monoSystemObject; + } + else if (type == il2CppSystemObject || type == il2CppSystemEnum || type == il2CppSystemValueType) + { + } + else if (type.BaseType is null || type.BaseType == il2CppSystemObject) + { + type.OverrideBaseType = il2CppSystemObject; + } + else if (type.BaseType == il2CppSystemValueType) + { + type.OverrideBaseType = monoSystemValueType; + } + else if (type.BaseType == il2CppSystemEnum) + { + type.OverrideBaseType = monoSystemValueType; + } + else + { + type.OverrideBaseType = visitor.Replace(type.BaseType); + } + + visitor.Modify(type.InterfaceContexts); + foreach (var genericParameter in type.GenericParameters) + { + visitor.Modify(genericParameter.ConstraintTypes); + } + + foreach (var field in type.Fields) + { + field.OverrideFieldType = visitor.Replace(field.FieldType); + } + + foreach (var method in type.Methods) + { + if (method.ReturnType == il2CppSystemVoid) + { + // Special case for void return type. + method.OverrideReturnType = monoSystemVoid; + } + else + { + method.OverrideReturnType = visitor.Replace(method.ReturnType); + } + foreach (var parameter in method.Parameters) + { + parameter.OverrideParameterType = visitor.Replace(parameter.ParameterType); + } + foreach (var genericParameter in method.GenericParameters) + { + visitor.Modify(genericParameter.ConstraintTypes); + } + for (var i = 0; i < method.Overrides.Count; i++) + { + method.Overrides[i] = visitor.Replace(method.Overrides[i]); + } + } + + foreach (var property in type.Properties) + { + property.OverridePropertyType = visitor.Replace(property.PropertyType); + } + + foreach (var @event in type.Events) + { + @event.OverrideEventType = visitor.Replace(@event.EventType); + } + } + } + } +} diff --git a/Il2CppInterop.Generator/Runners/DeobfuscationAnalyzer.cs b/Il2CppInterop.Generator/Runners/DeobfuscationAnalyzer.cs deleted file mode 100644 index 2c5b465c..00000000 --- a/Il2CppInterop.Generator/Runners/DeobfuscationAnalyzer.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.MetadataAccess; -using Il2CppInterop.Generator.Passes; -using Il2CppInterop.Generator.Utils; - -namespace Il2CppInterop.Generator.Runners; - -public static class DeobfuscationAnalyzer -{ - public static Il2CppInteropGenerator AddDeobfuscationAnalyzer(this Il2CppInteropGenerator gen) - { - return gen.AddRunner(); - } -} - -internal class DeobfuscationAnalyzerRunner : IRunner -{ - public void Dispose() { } - - public void Run(GeneratorOptions options) - { - RewriteGlobalContext rewriteContext; - IIl2CppMetadataAccess inputAssemblies; - using (new TimingCookie("Reading assemblies")) - { - inputAssemblies = new AssemblyMetadataAccess(options.Source ?? throw new ArgumentException("Source assemblies must be provided.", nameof(options))); - } - - using (new TimingCookie("Creating assembly contexts")) - { - rewriteContext = new RewriteGlobalContext(options, inputAssemblies, NullMetadataAccess.Instance); - } - - for (var chars = 1; chars <= 3; chars++) - for (var uniq = 3; uniq <= 15; uniq++) - { - options.TypeDeobfuscationCharsPerUniquifier = chars; - options.TypeDeobfuscationMaxUniquifiers = uniq; - - rewriteContext.RenamedTypes.Clear(); - rewriteContext.RenameGroups.Clear(); - - Pass05CreateRenameGroups.DoPass(rewriteContext); - - var uniqueTypes = rewriteContext.RenameGroups.Values.Count(it => it.Count == 1); - var nonUniqueTypes = rewriteContext.RenameGroups.Values.Count(it => it.Count > 1); - - // Ensure the output is written to stdout - Console.WriteLine($"Chars=\t{chars}\tMaxU=\t{uniq}\tUniq=\t{uniqueTypes}\tNonUniq=\t{nonUniqueTypes}"); - } - } -} diff --git a/Il2CppInterop.Generator/Runners/DeobfuscationMapGenerator.cs b/Il2CppInterop.Generator/Runners/DeobfuscationMapGenerator.cs deleted file mode 100644 index e6a5e28b..00000000 --- a/Il2CppInterop.Generator/Runners/DeobfuscationMapGenerator.cs +++ /dev/null @@ -1,391 +0,0 @@ -using System.IO.Compression; -using System.Text; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Common; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Il2CppInterop.Generator.MetadataAccess; -using Il2CppInterop.Generator.Passes; -using Il2CppInterop.Generator.Utils; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator.Runners; - -public static class DeobfuscationMapGenerator -{ - public static Il2CppInteropGenerator AddDeobfuscationMapGenerator(this Il2CppInteropGenerator gen) - { - return gen.AddRunner(); - } -} - -internal class DeobfuscationMapGeneratorRunner : IRunner -{ - public void Run(GeneratorOptions options) - { - if (options.Source == null || !options.Source.Any()) - { - Console.WriteLine("No input specified; use -h for help"); - return; - } - - if (string.IsNullOrEmpty(options.OutputDir)) - { - Console.WriteLine("No target dir specified; use -h for help"); - return; - } - - if (string.IsNullOrEmpty(options.DeobfuscationNewAssembliesPath)) - { - Console.WriteLine("No obfuscated assembly path specified; use -h for help"); - return; - } - - if (!Directory.Exists(options.OutputDir)) - Directory.CreateDirectory(options.OutputDir); - - RewriteGlobalContext rewriteContext; - IIl2CppMetadataAccess inputAssemblies; - using (new TimingCookie("Reading assemblies")) - { - inputAssemblies = - new AssemblyMetadataAccess(Directory.EnumerateFiles(options.DeobfuscationNewAssembliesPath, "*.dll")); - } - - using (new TimingCookie("Creating rewrite assemblies")) - { - rewriteContext = new RewriteGlobalContext(options, inputAssemblies, NullMetadataAccess.Instance); - } - - using (new TimingCookie("Computing renames")) - { - Pass05CreateRenameGroups.DoPass(rewriteContext); - } - - using (new TimingCookie("Creating typedefs")) - { - Pass10CreateTypedefs.DoPass(rewriteContext); - } - - using (new TimingCookie("Computing struct blittability")) - { - Pass11ComputeTypeSpecifics.DoPass(rewriteContext); - } - - using (new TimingCookie("Filling typedefs")) - { - Pass12FillTypedefs.DoPass(rewriteContext); - } - - using (new TimingCookie("Filling generic constraints")) - { - Pass13FillGenericConstraints.DoPass(rewriteContext); - } - - using (new TimingCookie("Creating members")) - { - Pass15GenerateMemberContexts.DoPass(rewriteContext); - } - - - RewriteGlobalContext cleanContext; - IIl2CppMetadataAccess cleanAssemblies; - using (new TimingCookie("Reading clean assemblies")) - { - cleanAssemblies = new AssemblyMetadataAccess(options.Source); - } - - using (new TimingCookie("Creating clean rewrite assemblies")) - { - cleanContext = new RewriteGlobalContext(options, cleanAssemblies, NullMetadataAccess.Instance); - } - - using (new TimingCookie("Computing clean assembly renames")) - { - Pass05CreateRenameGroups.DoPass(cleanContext); - } - - using (new TimingCookie("Creating clean assembly typedefs")) - { - Pass10CreateTypedefs.DoPass(cleanContext); - } - - - var usedNames = new Dictionary(); - - using var fileOutput = new FileStream(options.OutputDir + Path.DirectorySeparatorChar + "RenameMap.csv.gz", - FileMode.Create, FileAccess.Write); - using var gzipStream = new GZipStream(fileOutput, CompressionLevel.Optimal, true); - using var writer = new StreamWriter(gzipStream, Encoding.UTF8, 65536, true); - - void DoEnum(TypeRewriteContext obfuscatedType, TypeRewriteContext cleanType) - { - foreach (var originalTypeField in obfuscatedType.OriginalType.Fields) - { - if (!originalTypeField.Name.IsObfuscated(obfuscatedType.AssemblyContext.GlobalContext.Options)) - continue; - var matchedField = - cleanType.OriginalType.Fields[obfuscatedType.OriginalType.Fields.IndexOf(originalTypeField)]; - - writer.WriteLine(obfuscatedType.NewType.GetNamespacePrefix() + "." + obfuscatedType.NewType.Name + - "::" + Pass22GenerateEnums.GetUnmangledName(originalTypeField) + ";" + - matchedField.Name + ";0"); - } - } - - foreach (var assemblyContext in rewriteContext.Assemblies) - { - if (options.DeobfuscationGenerationAssemblies.Count > 0 && - !options.DeobfuscationGenerationAssemblies.Contains(assemblyContext.NewAssembly.Name!)) - continue; - - var cleanAssembly = cleanContext.GetAssemblyByName(assemblyContext.OriginalAssembly.Name!); - - void DoType(TypeRewriteContext typeContext, TypeRewriteContext? enclosingType) - { - if (!typeContext.OriginalNameWasObfuscated) return; - - var cleanType = FindBestMatchType(typeContext, cleanAssembly, enclosingType); - if (cleanType.Item1 == null) return; - - if (!usedNames.TryGetValue(cleanType.Item1.NewType, out var existing) || - existing.Item2 < cleanType.Item2) - usedNames[cleanType.Item1.NewType] = ( - typeContext.NewType.GetNamespacePrefix() + "." + typeContext.NewType.Name, cleanType.Item2, - typeContext.OriginalType.Namespace != cleanType.Item1.OriginalType.Namespace); - else - return; - - if (typeContext.OriginalType.IsEnum) - DoEnum(typeContext, cleanType.Item1); - - foreach (var originalTypeNestedType in typeContext.OriginalType.NestedTypes) - DoType(typeContext.AssemblyContext.GetContextForOriginalType(originalTypeNestedType), - cleanType.Item1); - } - - foreach (var typeContext in assemblyContext.Types) - { - if (typeContext.NewType.DeclaringType != null) continue; - - DoType(typeContext, null); - } - } - - - foreach (var keyValuePair in usedNames) - writer.WriteLine(keyValuePair.Value.Item1 + ";" + - (keyValuePair.Value.ForceNs ? keyValuePair.Key.Namespace + "." : "") + - keyValuePair.Key.Name + ";" + keyValuePair.Value.Item2); - - Logger.Instance.LogInformation("Done!"); - - rewriteContext.Dispose(); - } - - private static (TypeRewriteContext?, int) FindBestMatchType(TypeRewriteContext obfType, - AssemblyRewriteContext cleanAssembly, TypeRewriteContext? enclosingCleanType) - { - var inheritanceDepthOfOriginal = 0; - var currentBase = obfType.OriginalType.BaseType; - while (true) - { - if (currentBase == null) break; - var currentBaseContext = - obfType.AssemblyContext.GlobalContext.TryGetNewTypeForOriginal(currentBase.Resolve()!); - if (currentBaseContext == null || !currentBaseContext.OriginalNameWasObfuscated) break; - - inheritanceDepthOfOriginal++; - currentBase = currentBaseContext.OriginalType.BaseType; - } - - var bestPenalty = int.MinValue; - TypeRewriteContext? bestMatch = null; - - var source = - enclosingCleanType?.OriginalType.NestedTypes.Select(it => - cleanAssembly.GlobalContext.GetNewTypeForOriginal(it)) ?? - cleanAssembly.Types.Where(it => it.NewType.DeclaringType == null); - - foreach (var candidateCleanType in source) - { - if (obfType.OriginalType.HasMethods() != candidateCleanType.OriginalType.HasMethods()) - continue; - - if (obfType.OriginalType.HasFields() != candidateCleanType.OriginalType.HasFields()) - continue; - - if (obfType.OriginalType.IsEnum) - if (obfType.OriginalType.Fields.Count != candidateCleanType.OriginalType.Fields.Count) - continue; - - var currentPenalty = 0; - - var tryBase = candidateCleanType.OriginalType.BaseType; - var actualBaseDepth = 0; - while (tryBase != null) - { - if (tryBase?.Name == currentBase?.Name && tryBase?.Namespace == currentBase?.Namespace) - break; - - tryBase = tryBase?.Resolve()?.BaseType; - actualBaseDepth++; - } - - if (tryBase == null && currentBase != null) - continue; - - var baseDepthDifference = Math.Abs(actualBaseDepth - inheritanceDepthOfOriginal); - if (baseDepthDifference > 1) continue; // heuristic optimization - currentPenalty -= baseDepthDifference * 50; - - currentPenalty -= - Math.Abs(candidateCleanType.OriginalType.Fields.Count - obfType.OriginalType.Fields.Count) * 5; - - currentPenalty -= Math.Abs(obfType.OriginalType.NestedTypes.Count - - candidateCleanType.OriginalType.NestedTypes.Count) * 10; - - currentPenalty -= - Math.Abs(obfType.OriginalType.Properties.Count - candidateCleanType.OriginalType.Properties.Count) * 5; - - currentPenalty -= - Math.Abs(obfType.OriginalType.Interfaces.Count - candidateCleanType.OriginalType.Interfaces.Count) * 35; - - var options = obfType.AssemblyContext.GlobalContext.Options; - - foreach (var obfuscatedField in obfType.OriginalType.Fields) - { - if (obfuscatedField.Name.IsObfuscated(options)) - { - var bestFieldScore = candidateCleanType.OriginalType.Fields.Max(it => - TypeMatchWeight(obfuscatedField.Signature!.FieldType, it.Signature!.FieldType, options)); - currentPenalty += bestFieldScore * (bestFieldScore < 0 ? 10 : 2); - continue; - } - - if (candidateCleanType.OriginalType.Fields.Any(it => it.Name == obfuscatedField.Name)) - currentPenalty += 10; - } - - foreach (var obfuscatedMethod in obfType.OriginalType.Methods) - { - if (obfuscatedMethod.IsConstructor) continue; - - if (obfuscatedMethod.Name.IsObfuscated(options)) - { - var bestMethodScore = candidateCleanType.OriginalType.Methods.Max(it => - MethodSignatureMatchWeight(obfuscatedMethod, it, options)); - currentPenalty += bestMethodScore * (bestMethodScore < 0 ? 10 : 1); - - continue; - } - - if (candidateCleanType.OriginalType.Methods.Any(it => it.Name == obfuscatedMethod.Name)) - currentPenalty += (obfuscatedMethod.Name?.Length ?? 0) / 10 * 5 + 1; - } - - if (currentPenalty == bestPenalty) - { - bestMatch = null; - } - else if (currentPenalty > bestPenalty) - { - bestPenalty = currentPenalty; - bestMatch = candidateCleanType; - } - } - - // if (bestPenalty < -100) - // bestMatch = null; - - return (bestMatch, bestPenalty); - } - - private static int TypeMatchWeight(TypeSignature a, TypeSignature b, GeneratorOptions options) - { - if (a.GetType() != b.GetType()) - return -1; - - var runningSum = 0; - - void Accumulate(int i) - { - if (i < 0 || runningSum < 0) - runningSum = -1; - else - runningSum += i; - } - - switch (a) - { - case ArrayBaseTypeSignature arr: - if (b is not ArrayBaseTypeSignature brr) - return -1; - return TypeMatchWeight(arr.BaseType, brr.BaseType, options) * 5; - case ByReferenceTypeSignature abr: - if (b is not ByReferenceTypeSignature bbr) - return -1; - return TypeMatchWeight(abr.BaseType, bbr.BaseType, options) * 5; - case GenericInstanceTypeSignature agi: - if (b is not GenericInstanceTypeSignature bgi) - return -1; - if (agi.TypeArguments.Count != bgi.TypeArguments.Count) return -1; - Accumulate(TypeMatchWeight(agi.GenericType.ToTypeSignature(), bgi.GenericType.ToTypeSignature(), options)); - for (var i = 0; i < agi.TypeArguments.Count; i++) - Accumulate(TypeMatchWeight(agi.TypeArguments[i], bgi.TypeArguments[i], options)); - return runningSum * 5; - case GenericParameterSignature: - if (b is not GenericParameterSignature) - return -1; - return 5; - default: - if (a.IsNested()) - { - if (!b.IsNested()) - return -1; - - if (a.Name.IsObfuscated(options)) - return 0; - - var declMatch = TypeMatchWeight(a.DeclaringType!.ToTypeSignature(), b.DeclaringType!.ToTypeSignature(), options); - if (declMatch == -1 || a.Name != b.Name) - return -1; - - return 1; - } - - if (a.Name.IsObfuscated(options)) - return 0; - return a.Name == b.Name && a.Namespace == b.Namespace ? 1 : -1; - } - } - - private static int MethodSignatureMatchWeight(MethodDefinition a, MethodDefinition b, GeneratorOptions options) - { - if (a.Parameters.Count != b.Parameters.Count || a.IsStatic != b.IsStatic || - (a.Attributes & MethodAttributes.MemberAccessMask) != - (b.Attributes & MethodAttributes.MemberAccessMask)) - return -1; - - var runningSum = TypeMatchWeight(a.Signature!.ReturnType, b.Signature!.ReturnType, options); - if (runningSum == -1) - return -1; - - void Accumulate(int i) - { - if (i < 0 || runningSum < 0) - runningSum = -1; - else - runningSum += i; - } - - for (var i = 0; i < a.Parameters.Count; i++) - Accumulate(TypeMatchWeight(a.Parameters[i].ParameterType, b.Parameters[i].ParameterType, options)); - - return runningSum * (a.Parameters.Count + 1); - } - - public void Dispose() { } -} diff --git a/Il2CppInterop.Generator/Runners/IRunner.cs b/Il2CppInterop.Generator/Runners/IRunner.cs deleted file mode 100644 index ad697781..00000000 --- a/Il2CppInterop.Generator/Runners/IRunner.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Il2CppInterop.Generator.Runners; - -internal interface IRunner : IDisposable -{ - void Run(GeneratorOptions options); -} diff --git a/Il2CppInterop.Generator/Runners/InteropAssemblyGenerator.cs b/Il2CppInterop.Generator/Runners/InteropAssemblyGenerator.cs deleted file mode 100644 index 72d46862..00000000 --- a/Il2CppInterop.Generator/Runners/InteropAssemblyGenerator.cs +++ /dev/null @@ -1,222 +0,0 @@ -using Il2CppInterop.Common; -using Il2CppInterop.Common.XrefScans; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.MetadataAccess; -using Il2CppInterop.Generator.Passes; -using Il2CppInterop.Generator.Utils; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator.Runners; - -public static class InteropAssemblyGenerator -{ - public static Il2CppInteropGenerator AddInteropAssemblyGenerator(this Il2CppInteropGenerator gen) - { - return gen.AddRunner(); - } -} - -internal class InteropAssemblyGeneratorRunner : IRunner -{ - public void Run(GeneratorOptions options) - { - if (options.Source == null || !options.Source.Any()) - { - Console.WriteLine("No input specified; use -h for help"); - return; - } - - if (string.IsNullOrEmpty(options.OutputDir)) - { - Console.WriteLine("No target dir specified; use -h for help"); - return; - } - - if (!Directory.Exists(options.OutputDir)) - Directory.CreateDirectory(options.OutputDir); - - RewriteGlobalContext rewriteContext; - IIl2CppMetadataAccess gameAssemblies; - IMetadataAccess unityAssemblies; - - using (new TimingCookie("Reading assemblies")) - { - gameAssemblies = new AssemblyMetadataAccess(options.Source); - } - - if (!string.IsNullOrEmpty(options.UnityBaseLibsDir)) - using (new TimingCookie("Reading unity assemblies")) - { - unityAssemblies = new AssemblyMetadataAccess(Directory.EnumerateFiles(options.UnityBaseLibsDir, "*.dll")); - } - else - unityAssemblies = NullMetadataAccess.Instance; - - using (new TimingCookie("Creating rewrite assemblies")) - { - rewriteContext = new RewriteGlobalContext(options, gameAssemblies, unityAssemblies); - } - - using (new TimingCookie("Computing renames")) - { - Pass05CreateRenameGroups.DoPass(rewriteContext); - } - - using (new TimingCookie("Creating typedefs")) - { - Pass10CreateTypedefs.DoPass(rewriteContext); - } - - using (new TimingCookie("Computing struct blittability")) - { - Pass11ComputeTypeSpecifics.DoPass(rewriteContext); - } - - using (new TimingCookie("Filling typedefs")) - { - Pass12FillTypedefs.DoPass(rewriteContext); - } - - using (new TimingCookie("Filling generic constraints")) - { - Pass13FillGenericConstraints.DoPass(rewriteContext); - } - - using (new TimingCookie("Creating members")) - { - Pass15GenerateMemberContexts.DoPass(rewriteContext); - } - - using (new TimingCookie("Scanning method cross-references")) - { - Pass16ScanMethodRefs.DoPass(rewriteContext, options); - } - - using (new TimingCookie("Finalizing method declarations")) - { - Pass18FinalizeMethodContexts.DoPass(rewriteContext); - } - - Logger.Instance.LogInformation("{DeadMethodsCount} total potentially dead methods", Pass18FinalizeMethodContexts.TotalPotentiallyDeadMethods); - - using (new TimingCookie("Filling method parameters")) - { - Pass19CopyMethodParameters.DoPass(rewriteContext); - } - - using (new TimingCookie("Creating static constructors")) - { - Pass20GenerateStaticConstructors.DoPass(rewriteContext); - } - - using (new TimingCookie("Creating value type fields")) - { - Pass21GenerateValueTypeFields.DoPass(rewriteContext); - } - - using (new TimingCookie("Creating enums")) - { - Pass22GenerateEnums.DoPass(rewriteContext); - } - - using (new TimingCookie("Creating IntPtr constructors")) - { - Pass23GeneratePointerConstructors.DoPass(rewriteContext); - } - - using (new TimingCookie("Creating non-blittable struct constructors")) - { - Pass25GenerateNonBlittableValueTypeDefaultCtors.DoPass(rewriteContext); - } - - using (new TimingCookie("Creating generic method static constructors")) - { - Pass30GenerateGenericMethodStoreConstructors.DoPass(rewriteContext); - } - - using (new TimingCookie("Creating field accessors")) - { - Pass40GenerateFieldAccessors.DoPass(rewriteContext); - } - - using (new TimingCookie("Filling methods")) - { - Pass50GenerateMethods.DoPass(rewriteContext); - } - - using (new TimingCookie("Generating implicit conversions")) - { - Pass60AddImplicitConversions.DoPass(rewriteContext); - } - - using (new TimingCookie("Implementing awaiters")) - { - Pass61ImplementAwaiters.DoPass(rewriteContext); - } - - using (new TimingCookie("Creating properties")) - { - Pass70GenerateProperties.DoPass(rewriteContext); - } - - if (options.UnityBaseLibsDir != null) - { - using (new TimingCookie("Unstripping types")) - { - Pass79UnstripTypes.DoPass(rewriteContext); - } - - using (new TimingCookie("Unstripping fields")) - { - Pass80UnstripFields.DoPass(rewriteContext); - } - - using (new TimingCookie("Unstripping methods")) - { - Pass80UnstripMethods.DoPass(rewriteContext); - } - - using (new TimingCookie("Unstripping method bodies")) - { - Pass81FillUnstrippedMethodBodies.DoPass(rewriteContext); - } - } - else - { - Logger.Instance.LogWarning("Not performing unstripping as unity libs are not specified"); - } - - // Breaks .net runtime - //using (new TimingCookie("Generating forwarded types")) - //{ - // Pass89GenerateForwarders.DoPass(rewriteContext); - //} - - using (new TimingCookie("Writing xref cache")) - { - Pass89GenerateMethodXrefCache.DoPass(rewriteContext, options); - } - - using (new TimingCookie("Writing assemblies")) - { - Pass90WriteToDisk.DoPass(rewriteContext, options); - } - - using (new TimingCookie("Writing method pointer map")) - { - Pass91GenerateMethodPointerMap.DoPass(rewriteContext, options); - } - - using (new TimingCookie("Clearing static data")) - { - Pass16ScanMethodRefs.MapOfCallers = new Dictionary>(); - Pass16ScanMethodRefs.NonDeadMethods = []; - } - - Logger.Instance.LogInformation("Done!"); - - rewriteContext.Dispose(); - } - - public void Dispose() { } -} diff --git a/Il2CppInterop.Generator/StackTypes/DoubleStackType.cs b/Il2CppInterop.Generator/StackTypes/DoubleStackType.cs new file mode 100644 index 00000000..1db5959e --- /dev/null +++ b/Il2CppInterop.Generator/StackTypes/DoubleStackType.cs @@ -0,0 +1,10 @@ +namespace Il2CppInterop.Generator.StackTypes; + +public sealed record class DoubleStackType : StackType +{ + public static DoubleStackType Instance { get; } = new(); + private DoubleStackType() + { + } + public override string ToString() => "double"; +} diff --git a/Il2CppInterop.Generator/StackTypes/ExactStackType.cs b/Il2CppInterop.Generator/StackTypes/ExactStackType.cs new file mode 100644 index 00000000..4ff1bad4 --- /dev/null +++ b/Il2CppInterop.Generator/StackTypes/ExactStackType.cs @@ -0,0 +1,18 @@ +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.StackTypes; + +public sealed record class ExactStackType(TypeAnalysisContext Type) : StackType +{ + public bool Equals(ExactStackType? other) + { + return TypeAnalysisContextEqualityComparer.Instance.Equals(Type, other?.Type); + } + + public override int GetHashCode() + { + return TypeAnalysisContextEqualityComparer.Instance.GetHashCode(Type); + } + + public override string ToString() => Type.FullName; +} diff --git a/Il2CppInterop.Generator/StackTypes/IncompatibleStackType.cs b/Il2CppInterop.Generator/StackTypes/IncompatibleStackType.cs new file mode 100644 index 00000000..f0e40e81 --- /dev/null +++ b/Il2CppInterop.Generator/StackTypes/IncompatibleStackType.cs @@ -0,0 +1,11 @@ +namespace Il2CppInterop.Generator.StackTypes; + +public sealed record class IncompatibleStackType : StackType +{ + public static IncompatibleStackType Instance { get; } = new(); + private IncompatibleStackType() + { + } + + public override string ToString() => "incompatible"; +} diff --git a/Il2CppInterop.Generator/StackTypes/IntegerStackType.cs b/Il2CppInterop.Generator/StackTypes/IntegerStackType.cs new file mode 100644 index 00000000..2c04cf73 --- /dev/null +++ b/Il2CppInterop.Generator/StackTypes/IntegerStackType.cs @@ -0,0 +1,5 @@ +namespace Il2CppInterop.Generator.StackTypes; + +public abstract record class IntegerStackType : StackType +{ +} diff --git a/Il2CppInterop.Generator/StackTypes/IntegerStackType32.cs b/Il2CppInterop.Generator/StackTypes/IntegerStackType32.cs new file mode 100644 index 00000000..41723be8 --- /dev/null +++ b/Il2CppInterop.Generator/StackTypes/IntegerStackType32.cs @@ -0,0 +1,11 @@ +namespace Il2CppInterop.Generator.StackTypes; + +public sealed record class IntegerStackType32 : IntegerStackType +{ + public static IntegerStackType32 Instance { get; } = new(); + private IntegerStackType32() + { + } + + public override string ToString() => "int"; +} diff --git a/Il2CppInterop.Generator/StackTypes/IntegerStackType64.cs b/Il2CppInterop.Generator/StackTypes/IntegerStackType64.cs new file mode 100644 index 00000000..54a8f17c --- /dev/null +++ b/Il2CppInterop.Generator/StackTypes/IntegerStackType64.cs @@ -0,0 +1,11 @@ +namespace Il2CppInterop.Generator.StackTypes; + +public sealed record class IntegerStackType64 : IntegerStackType +{ + public static IntegerStackType64 Instance { get; } = new(); + private IntegerStackType64() + { + } + + public override string ToString() => "long"; +} diff --git a/Il2CppInterop.Generator/StackTypes/IntegerStackTypeNative.cs b/Il2CppInterop.Generator/StackTypes/IntegerStackTypeNative.cs new file mode 100644 index 00000000..53bcb28c --- /dev/null +++ b/Il2CppInterop.Generator/StackTypes/IntegerStackTypeNative.cs @@ -0,0 +1,11 @@ +namespace Il2CppInterop.Generator.StackTypes; + +public sealed record class IntegerStackTypeNative : IntegerStackType +{ + public static IntegerStackTypeNative Instance { get; } = new(); + private IntegerStackTypeNative() + { + } + + public override string ToString() => "nint"; +} diff --git a/Il2CppInterop.Generator/StackTypes/SingleStackType.cs b/Il2CppInterop.Generator/StackTypes/SingleStackType.cs new file mode 100644 index 00000000..05bd90c6 --- /dev/null +++ b/Il2CppInterop.Generator/StackTypes/SingleStackType.cs @@ -0,0 +1,10 @@ +namespace Il2CppInterop.Generator.StackTypes; + +public sealed record class SingleStackType : StackType +{ + public static SingleStackType Instance { get; } = new(); + private SingleStackType() + { + } + public override string ToString() => "float"; +} diff --git a/Il2CppInterop.Generator/StackTypes/StackType.cs b/Il2CppInterop.Generator/StackTypes/StackType.cs new file mode 100644 index 00000000..5db46122 --- /dev/null +++ b/Il2CppInterop.Generator/StackTypes/StackType.cs @@ -0,0 +1,48 @@ +namespace Il2CppInterop.Generator.StackTypes; + +public abstract record class StackType +{ + public static StackType Merge(StackType a, StackType b) + { + if (a is IncompatibleStackType || b is IncompatibleStackType) + { + return IncompatibleStackType.Instance; + } + if (a is UnknownStackType) + { + return b; + } + if (b is UnknownStackType) + { + return a; + } + if (EqualityComparer.Default.Equals(a, b)) + { + return a; + } + + // Could be improved, but good enough for now. + return IncompatibleStackType.Instance; + } + + public static StackType MergeForMathOperation(StackType a, StackType b) + { + if (a is IntegerStackType64 && b is IntegerStackType) + { + return IntegerStackType64.Instance; + } + if (b is IntegerStackType64 && a is IntegerStackType) + { + return IntegerStackType64.Instance; + } + if (a is IntegerStackTypeNative && b is IntegerStackType32) + { + return IntegerStackTypeNative.Instance; + } + if (b is IntegerStackTypeNative && a is IntegerStackType32) + { + return IntegerStackTypeNative.Instance; + } + return Merge(a, b); + } +} diff --git a/Il2CppInterop.Generator/StackTypes/UnknownStackType.cs b/Il2CppInterop.Generator/StackTypes/UnknownStackType.cs new file mode 100644 index 00000000..a6e6e3f0 --- /dev/null +++ b/Il2CppInterop.Generator/StackTypes/UnknownStackType.cs @@ -0,0 +1,11 @@ +namespace Il2CppInterop.Generator.StackTypes; + +public sealed record class UnknownStackType : StackType +{ + public static UnknownStackType Instance { get; } = new(); + private UnknownStackType() + { + } + + public override string ToString() => "unknown"; +} diff --git a/Il2CppInterop.Generator/StaticConstructorProcessingLayer.cs b/Il2CppInterop.Generator/StaticConstructorProcessingLayer.cs new file mode 100644 index 00000000..dae14370 --- /dev/null +++ b/Il2CppInterop.Generator/StaticConstructorProcessingLayer.cs @@ -0,0 +1,50 @@ +using System.Diagnostics; +using System.Reflection; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; + +namespace Il2CppInterop.Generator; + +public class StaticConstructorProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "Static Constructor Processor"; + public override string Id => "static_constructor_processor"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + foreach (var type in appContext.AllTypes) + { + var instructions = type.StaticConstructorInstructions; + if (instructions is null or { Count: 0 }) + continue; + + for (var i = type.Methods.Count - 1; i >= 0; i--) + { + if (type.Methods[i].IsStaticConstructor) + { + Debug.Fail($"Type {type.FullName} already has a static constructor defined. It should have been renamed in an earlier processing layer."); + } + } + + // Add a final instruction to return from the static constructor. + instructions.Add(new Instruction(CilOpCodes.Ret)); + + var staticConstructor = new InjectedMethodAnalysisContext( + type, + ".cctor", + type.AppContext.SystemTypes.SystemVoidType, + MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, + []) + { + IsInjected = true + }; + type.Methods.Add(staticConstructor); + + var nativeMethodBody = new NativeMethodBody() + { + Instructions = instructions, + }; + staticConstructor.PutExtraData(nativeMethodBody); + } + } +} diff --git a/Il2CppInterop.Generator/SystemTypeResolver.cs b/Il2CppInterop.Generator/SystemTypeResolver.cs new file mode 100644 index 00000000..50929e85 --- /dev/null +++ b/Il2CppInterop.Generator/SystemTypeResolver.cs @@ -0,0 +1,97 @@ +using System.Diagnostics; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator; + +public readonly struct SystemTypeResolver +{ + private readonly AssemblyAnalysisContext referencedFrom; + private readonly TypeAnalysisContext? referencingType; + private readonly MethodAnalysisContext? referencingMethod; + + public SystemTypeResolver(AssemblyAnalysisContext referencedFrom) + { + this.referencedFrom = referencedFrom; + } + + public SystemTypeResolver(TypeAnalysisContext referencingType) + { + if (referencingType is ReferencedTypeAnalysisContext) + throw new ArgumentException("Must be a simple type", nameof(referencingType)); + referencedFrom = referencingType.DeclaringAssembly; + this.referencingType = referencingType; + } + + public SystemTypeResolver(MethodAnalysisContext referencingMethod) + { + if (referencingMethod is ConcreteGenericMethodAnalysisContext) + throw new ArgumentException("Must be a simple method", nameof(referencingMethod)); + referencedFrom = referencingMethod.CustomAttributeAssembly; + referencingType = referencingMethod.DeclaringType; + this.referencingMethod = referencingMethod; + } + public TypeAnalysisContext ResolveOrThrow(Type? type, bool allowGenericInstance = true) + { + return Resolve(type, allowGenericInstance) ?? throw new($"Unable to resolve type {type?.FullName}"); + } + + public TypeAnalysisContext? Resolve(Type? type) => Resolve(type, true); + public TypeAnalysisContext? Resolve(Type? type, bool allowGenericInstance) + { + if (type is null) + return null; + + if (type.IsSZArray) + return Resolve(type.GetElementType())?.MakeSzArrayType(); + + if (type.IsByRef) + return Resolve(type.GetElementType())?.MakeByReferenceType(); + + if (type.IsPointer) + return Resolve(type.GetElementType())?.MakePointerType(); + + if (type.IsArray) + return Resolve(type.GetElementType())?.MakeArrayType(type.GetArrayRank()); + + if (type.IsGenericParameter) + { + if (type.IsGenericTypeParameter) + { + return TryGetGenericParameter(referencingType?.GenericParameters, type.GenericParameterPosition); + } + else + { + Debug.Assert(type.IsGenericMethodParameter); + return TryGetGenericParameter(referencingMethod?.GenericParameters, type.GenericParameterPosition); + } + } + + if (type.IsGenericType && allowGenericInstance) + { + var genericArguments = type.GetGenericArguments().Select(Resolve).ToArray(); + if (genericArguments.Any(x => x is null)) + return null; + + var genericType = type.GetGenericTypeDefinition(); + return Resolve(genericType, false)?.MakeGenericInstanceType(genericArguments!); + } + + if (type.IsFunctionPointer) + throw new NotSupportedException($"Function pointers are not supported: {type.Name}"); + + // Custom modifiers might be possible to support, but probably not necessary + + var assemblyName = type.Assembly.GetName().Name!; + if (assemblyName == "System.Private.CoreLib") + assemblyName = "mscorlib"; + var assembly = referencedFrom.AppContext.GetAssemblyByName(assemblyName); + return assembly?.GetTypeByFullName(type.FullName!); + } + + private static GenericParameterTypeAnalysisContext? TryGetGenericParameter(List? genericParameters, int index) + { + if (genericParameters is null || index < 0 || index >= genericParameters.Count) + return null; + return genericParameters[index]; + } +} diff --git a/Il2CppInterop.Generator/TranslatedMethodBody.cs b/Il2CppInterop.Generator/TranslatedMethodBody.cs new file mode 100644 index 00000000..f1a4819f --- /dev/null +++ b/Il2CppInterop.Generator/TranslatedMethodBody.cs @@ -0,0 +1,1101 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; +using Il2CppInterop.Generator.StackTypes; +using Il2CppInterop.Generator.Visitors; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.Exceptions; +using Il2CppInterop.Runtime.InteropTypes; +using Il2CppInterop.Runtime.InteropTypes.Arrays; + +namespace Il2CppInterop.Generator; + +public class TranslatedMethodBody : MethodBodyBase +{ + // Notes: + // + // There are subtle flaws in this implementation, in regards to exception handling. + // Certain instructions (eg `castclass` and `callvirt`) can throw exceptions. + // These exceptions will have System types, not Il2Cpp types. + // + // + + public static bool TryTranslateOriginalMethodBody(MethodAnalysisContext methodContext) + { + if (!methodContext.TryGetExtraData(out OriginalMethodBody? originalMethodBody)) + { + return false; + } + + var appContext = methodContext.AppContext; + + var byReference = appContext.ResolveTypeOrThrow(typeof(ByReference<>)); + var byReference_Constructor = byReference.GetMethodByName(".ctor"); + var byReference_ToPointer = byReference.GetMethodByName(nameof(ByReference<>.ToPointer)); + var byReference_GetValue = byReference.GetMethodByName(nameof(ByReference<>.GetValue)); + + var byReferenceStatic = appContext.ResolveTypeOrThrow(typeof(ByReference)); + var byReferenceStatic_GetValue = byReferenceStatic.GetMethodByName(nameof(ByReference.GetValue)); + var byReferenceStatic_SetValue1 = byReferenceStatic.GetMethodByName(nameof(ByReference.SetValue1)); + var byReferenceStatic_SetValue2 = byReferenceStatic.GetMethodByName(nameof(ByReference.SetValue2)); + + var il2CppTypeHelper = appContext.ResolveTypeOrThrow(typeof(Il2CppType)); + var il2CppTypeHelper_SizeOf = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.SizeOf)); + + Debug.Assert(methodContext.UnsafeImplementationMethod is not null); + var implementationMethod = methodContext.UnsafeImplementationMethod!; + + TypeReplacementVisitor replacementVisitor = TypeReplacementVisitor.Combine(TypeConversionVisitor.Create(methodContext.AppContext), TypeReplacementVisitor.CreateForMethodCopying(methodContext, implementationMethod)); + + var dictionary = originalMethodBody.AnalyzeStackTypes(methodContext, replacementVisitor, true); + + var initializeInstructions = new List(); + var localVariableList = new List(originalMethodBody.LocalVariables.Count); + var localVariableDictionary = new Dictionary(originalMethodBody.LocalVariables.Count); + foreach (var originalLocalVariable in originalMethodBody.LocalVariables) + { + var transferredType = replacementVisitor.Replace(originalLocalVariable.Type); // Convert to Il2Cpp types and swap out generic parameters for the correct ones + var localType = byReference.MakeGenericInstanceType([transferredType]); // All locals are byref + var translatedLocalVariable = new LocalVariable + { + Type = localType, + }; + localVariableList.Add(translatedLocalVariable); + localVariableDictionary.Add(originalLocalVariable, translatedLocalVariable); + + initializeInstructions.Add(CilOpCodes.Call, il2CppTypeHelper_SizeOf.MakeGenericInstanceMethod(transferredType)); + initializeInstructions.Add(CilOpCodes.Conv_U); + initializeInstructions.Add(CilOpCodes.Localloc); + initializeInstructions.Add(CilOpCodes.Newobj, byReference_Constructor.MakeConcreteGeneric([transferredType], [])); + initializeInstructions.Add(CilOpCodes.Stloc, translatedLocalVariable); + } + + var instructionDictionary = new Dictionary(originalMethodBody.Instructions.Count); + foreach (var originalInstruction in originalMethodBody.Instructions) + { + instructionDictionary[originalInstruction] = new Instruction(); + } + + var handlerStarts = originalMethodBody.ExceptionHandlers.Select(eh => eh.HandlerStart).OfType().ToHashSet(); + + var translatedInstructions = new List(originalMethodBody.Instructions.Count); + foreach (var originalInstruction in originalMethodBody.Instructions) + { + var translatedInstruction = instructionDictionary[originalInstruction]; + + if (handlerStarts.Contains(originalInstruction)) + { + // Ensure that the start of the exception handler is a nop, + // so that we can insert any necessary instructions at the beginning of the handler. + translatedInstruction.Code = CilOpCodes.Nop; + translatedInstructions.Add(translatedInstruction); + translatedInstruction = new Instruction(); + } + + translatedInstructions.Add(translatedInstruction); + + var originalCode = originalInstruction.Code; + var originalOperand = originalInstruction.Operand; + + if (originalOperand is null) + { + switch (originalCode.Code) + { + case CilCode.Arglist: + return false; + + case CilCode.Ldlen: + // This is Il2CppArrayBase.Length + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext.ResolveTypeOrThrow(typeof(Il2CppArrayBase)).GetMethodByName($"get_{nameof(Il2CppArrayBase.Length)}"); + } + break; + + case CilCode.Ldelem_I1: + // This is Il2CppArrayBase.LoadElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.LoadElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemSByteType]); + } + break; + + case CilCode.Ldelem_I2: + // This is Il2CppArrayBase.LoadElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.LoadElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemInt16Type]); + } + break; + + case CilCode.Ldelem_I4: + // This is Il2CppArrayBase.LoadElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.LoadElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemInt32Type]); + } + break; + + case CilCode.Ldelem_I8: + // This is Il2CppArrayBase.LoadElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.LoadElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemInt64Type]); + } + break; + + case CilCode.Ldelem_U1: + // This is Il2CppArrayBase.LoadElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.LoadElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemByteType]); + } + break; + + case CilCode.Ldelem_U2: + // This is Il2CppArrayBase.LoadElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.LoadElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemUInt16Type]); + } + break; + + case CilCode.Ldelem_U4: + // This is Il2CppArrayBase.LoadElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.LoadElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemUInt32Type]); + } + break; + + case CilCode.Ldelem_I: + // This is Il2CppArrayBase.LoadElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.LoadElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemIntPtrType]); + } + break; + + case CilCode.Ldelem_R4: + // This is Il2CppArrayBase.LoadElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.LoadElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemSingleType]); + } + break; + + case CilCode.Ldelem_R8: + // This is Il2CppArrayBase.LoadElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.LoadElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemDoubleType]); + } + break; + + case CilCode.Stelem_I1: + // This is Il2CppArrayBase.StoreElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.StoreElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemSByteType]); + } + break; + + case CilCode.Stelem_I2: + // This is Il2CppArrayBase.StoreElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.StoreElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemInt16Type]); + } + break; + + case CilCode.Stelem_I4: + // This is Il2CppArrayBase.StoreElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.StoreElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemInt32Type]); + } + break; + + case CilCode.Stelem_I8: + // This is Il2CppArrayBase.StoreElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.StoreElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemInt64Type]); + } + break; + + case CilCode.Stelem_I: + // This is Il2CppArrayBase.StoreElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.StoreElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemIntPtrType]); + } + break; + + case CilCode.Stelem_R4: + // This is Il2CppArrayBase.StoreElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.StoreElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemSingleType]); + } + break; + + case CilCode.Stelem_R8: + // This is Il2CppArrayBase.StoreElementUnsafe + { + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayBase)) + .GetMethodByName(nameof(Il2CppArrayBase.StoreElementUnsafe)) + .MakeGenericInstanceMethod([methodContext.AppContext.SystemTypes.SystemDoubleType]); + } + break; + + case CilCode.Ldelem_Ref: + // This is Il2CppReferenceArray.get_Item but the T is not known because the operand is null. + return false; + + case CilCode.Stelem_Ref: + // This is Il2CppReferenceArray.set_Item but the T is not known because the operand is null. + return false; + + case >= CilCode.Ldind_I1 and < CilCode.Ldind_Ref: + // This is for by ref and pointers + goto default; + + case CilCode.Ldind_Ref: + // This is for by ref and pointers + return false; + + case CilCode.Stind_Ref: + // This is for by ref and pointers + return false; + + case > CilCode.Stind_Ref and <= CilCode.Stind_R8: + // This is for by ref and pointers + goto default; + + case CilCode.Refanytype: + // Not implemented yet + return false; + + case CilCode.Throw: + { + translatedInstruction.Code = CilOpCodes.Call; + translatedInstruction.Operand = methodContext.AppContext.ResolveTypeOrThrow(typeof(Il2CppException)).GetMethodByName(nameof(Il2CppException.FromNativeObject)); + translatedInstructions.Add(new Instruction(originalCode)); + } + break; + + case CilCode.Ret: + if (MonoIl2CppConversion.AddMonoToIl2CppConversion(translatedInstructions, methodContext.ReturnType)) + { + translatedInstruction.Code = CilOpCodes.Nop; + + translatedInstructions.Add(new Instruction(originalCode)); + } + else + { + translatedInstruction.Code = originalCode; + } + break; + + case CilCode.Volatile: + // This op code can be ignored. + translatedInstruction.Code = CilOpCodes.Nop; + break; + + default: + // nop, ldnull, ldarg_0, mul, add, etc. + translatedInstruction.Code = originalCode; + break; + } + } + else if (originalOperand is byte or sbyte or short or ushort or int or uint or long or ulong or float or double or bool or char) + { + if (originalCode == CilOpCodes.Unaligned) + { + // This op code can be ignored. + translatedInstruction.Code = CilOpCodes.Nop; + } + else + { + translatedInstruction.Code = originalCode; + translatedInstruction.Operand = originalOperand; + } + } + else if (originalOperand is string) + { + Debug.Assert(originalCode == CilOpCodes.Ldstr); + translatedInstruction.Code = originalCode; + translatedInstruction.Operand = originalOperand; + + MonoIl2CppConversion.AddMonoToIl2CppStringConversion(translatedInstructions, methodContext.AppContext); + } + else if (originalOperand is IReadOnlyList labels) + { + Debug.Assert(originalCode == CilOpCodes.Switch); + translatedInstruction.Code = originalCode; + translatedInstruction.Operand = ResolveLabels(labels, instructionDictionary); + } + else if (originalOperand is ILabel label) + { + translatedInstruction.Code = originalCode; + translatedInstruction.Operand = ResolveLabel(label, instructionDictionary); + } + else if (originalOperand is This) + { + Debug.Assert(originalCode == CilOpCodes.Ldarg); + + var newParameter = implementationMethod.Parameters[0]; + + var parameterType = (GenericInstanceTypeAnalysisContext)newParameter.ParameterType; + var dataType = parameterType.GenericArguments[0]; + + if (dataType.IsValueType) + { + translatedInstruction.Code = CilOpCodes.Ldarg; + translatedInstruction.Operand = newParameter; + + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, parameterType); + } + else + { + translatedInstruction.Code = CilOpCodes.Ldarga; + translatedInstruction.Operand = newParameter; + + translatedInstructions.Add(CilOpCodes.Call, byReference_GetValue.MakeConcreteGeneric(parameterType.GenericArguments, [])); + + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, dataType); + } + } + else if (originalOperand is ParameterAnalysisContext parameter) + { + var parameterOffset = implementationMethod.Parameters.Count - methodContext.Parameters.Count; + var newParameter = implementationMethod.Parameters[parameterOffset + parameter.ParameterIndex]; + + var parameterType = (GenericInstanceTypeAnalysisContext)newParameter.ParameterType; + var dataType = parameterType.GenericArguments[0]; + + if (originalCode == CilOpCodes.Ldarg) + { + translatedInstruction.Code = CilOpCodes.Ldarga; + translatedInstruction.Operand = newParameter; + + translatedInstructions.Add(CilOpCodes.Call, byReference_GetValue.MakeConcreteGeneric(parameterType.GenericArguments, [])); + + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, dataType); + } + else if (originalCode == CilOpCodes.Starg) + { + if (MonoIl2CppConversion.AddMonoToIl2CppConversion(translatedInstructions, dataType)) + { + translatedInstruction.Code = CilOpCodes.Nop; + + translatedInstructions.Add(CilOpCodes.Ldarg, newParameter); + } + else + { + translatedInstruction.Code = CilOpCodes.Ldarg; + translatedInstruction.Operand = newParameter; + } + translatedInstructions.Add(CilOpCodes.Call, byReferenceStatic_SetValue2.MakeGenericInstanceMethod(parameterType.GenericArguments)); + } + else if (originalCode == CilOpCodes.Ldarga) + { + translatedInstruction.Code = CilOpCodes.Ldarg; + translatedInstruction.Operand = newParameter; + + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, parameterType); + } + else + { + Debug.Fail($"Unexpected CIL code: {originalCode}"); + return false; + } + } + else if (originalOperand is LocalVariable localVariable) + { + var translatedLocalVariable = localVariableDictionary[localVariable]; + + var localType = (GenericInstanceTypeAnalysisContext)translatedLocalVariable.Type; + var dataType = localType.GenericArguments[0]; + + if (originalCode == CilOpCodes.Ldloc) + { + translatedInstruction.Code = CilOpCodes.Ldloca; + translatedInstruction.Operand = translatedLocalVariable; + + translatedInstructions.Add(CilOpCodes.Call, byReference_GetValue.MakeConcreteGeneric(localType.GenericArguments, [])); + + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, dataType); + } + else if (originalCode == CilOpCodes.Stloc) + { + if (MonoIl2CppConversion.AddMonoToIl2CppConversion(translatedInstructions, dataType)) + { + translatedInstruction.Code = CilOpCodes.Nop; + + translatedInstructions.Add(CilOpCodes.Ldloc, translatedLocalVariable); + } + else + { + translatedInstruction.Code = CilOpCodes.Ldloc; + translatedInstruction.Operand = translatedLocalVariable; + } + translatedInstructions.Add(CilOpCodes.Call, byReferenceStatic_SetValue2.MakeGenericInstanceMethod(localType.GenericArguments)); + } + else if (originalCode == CilOpCodes.Ldloca) + { + translatedInstruction.Code = CilOpCodes.Ldloc; + translatedInstruction.Operand = translatedLocalVariable; + + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, localType); + } + else + { + Debug.Fail($"Unexpected CIL code: {originalCode}"); + return false; + } + } + else if (originalOperand is TypeAnalysisContext type) + { + var translatedType = replacementVisitor.Replace(type); + + switch (originalCode.Code) + { + case CilCode.Castclass or CilCode.Isinst: + { + translatedInstruction.Code = originalCode; + translatedInstruction.Operand = translatedType; + } + break; + case CilCode.Constrained: + return false; + case CilCode.Initobj: + { + // References on the stack are always void*. + translatedInstruction.Code = CilOpCodes.Call; + translatedInstruction.Operand = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.InitializeObject)).MakeGenericInstanceMethod(translatedType); + } + break; + case CilCode.Cpobj: + { + // References on the stack are always void*. + translatedInstruction.Code = CilOpCodes.Call; + translatedInstruction.Operand = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.CopyObject)).MakeGenericInstanceMethod(translatedType); + } + break; + case CilCode.Ldobj: + { + // References on the stack are always void*. + translatedInstruction.Code = CilOpCodes.Call; + translatedInstruction.Operand = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.ReadFromPointer)).MakeGenericInstanceMethod(translatedType); + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, translatedType); + } + break; + case CilCode.Stobj: + { + var storeMethod = il2CppTypeHelper.GetMethodByName(nameof(Il2CppType.StoreObject)).MakeGenericInstanceMethod(translatedType); + if (MonoIl2CppConversion.AddMonoToIl2CppConversion(translatedInstructions, translatedType)) + { + translatedInstruction.Code = CilOpCodes.Nop; + + translatedInstructions.Add(CilOpCodes.Call, storeMethod); + } + else + { + translatedInstruction.Code = CilOpCodes.Call; + translatedInstruction.Operand = storeMethod; + } + } + break; + case CilCode.Sizeof: + { + translatedInstruction.Code = CilOpCodes.Call; + translatedInstruction.Operand = il2CppTypeHelper_SizeOf.MakeGenericInstanceMethod(translatedType); + } + break; + case CilCode.Box: + if (MonoIl2CppConversion.AddMonoToIl2CppConversion(translatedInstructions, translatedType)) + { + translatedInstruction.Code = CilOpCodes.Nop; + + translatedInstructions.Add(new Instruction(originalCode, translatedType)); + } + else + { + translatedInstruction.Code = originalCode; + translatedInstruction.Operand = translatedType; + } + break; + case CilCode.Unbox: + { + translatedInstruction.Code = originalCode; + translatedInstruction.Operand = translatedType; + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, translatedType); + } + break; + case CilCode.Unbox_Any: + { + translatedInstruction.Code = originalCode; + translatedInstruction.Operand = translatedType; + } + break; + case CilCode.Ldtoken: + { + translatedInstruction.Code = originalCode; + translatedInstruction.Operand = translatedType; + + translatedInstructions.Add(new Instruction(CilOpCodes.Call, methodContext.AppContext.SystemTypes.SystemTypeType.GetMethodByName(nameof(Type.GetTypeFromHandle)))); + + var managedTypeToIl2CppTypeMethod = methodContext.AppContext + .ResolveTypeOrThrow(typeof(GenerationInternals)) + .Methods.First(m => m.Name == nameof(GenerationInternals.ManagedTypeToIl2CppType) && m.Parameters.Count == 1); + + translatedInstructions.Add(new Instruction(CilOpCodes.Call, managedTypeToIl2CppTypeMethod)); + + var getTypeHandleMethod = methodContext.AppContext.Il2CppMscorlib.GetTypeByFullNameOrThrow("Il2CppSystem.Type").GetMethodByName("get_TypeHandle"); + + translatedInstructions.Add(new Instruction(CilOpCodes.Callvirt, getTypeHandleMethod)); + } + break; + case CilCode.Newarr: + { + translatedInstruction.Code = CilOpCodes.Newobj; + translatedInstruction.Operand = appContext.ResolveTypeOrThrow(typeof(Il2CppArrayRank1<>)) + .Methods + .Single(m => m.IsInstanceConstructor && m.Parameters.Count == 1 && m.Parameters[0].ParameterType == appContext.SystemTypes.SystemInt32Type) + .MakeConcreteGeneric([translatedType], []); + } + break; + case CilCode.Ldelem: + { + var genericMethod = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayRank1<>)) + .GetMethodByName("get_Item"); + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = new ConcreteGenericMethodAnalysisContext(genericMethod, [translatedType], []); + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, translatedType); + } + break; + case CilCode.Stelem: + { + translatedInstruction.Code = CilOpCodes.Nop; + + var genericMethod = methodContext.AppContext + .ResolveTypeOrThrow(typeof(Il2CppArrayRank1<>)) + .GetMethodByName("set_Item"); + var concreteGenericMethod = new ConcreteGenericMethodAnalysisContext(genericMethod, [translatedType], []); + MonoIl2CppConversion.AddMonoToIl2CppConversion(translatedInstructions, translatedType); + + translatedInstructions.Add(new Instruction(CilOpCodes.Callvirt, concreteGenericMethod)); + } + break; + case CilCode.Ldelema: + { + var getElementAddress = appContext.ResolveTypeOrThrow(typeof(Il2CppArrayRank1<>)) + .GetMethodByName(nameof(Il2CppArrayRank1<>.GetElementAddress)) + .MakeConcreteGeneric([translatedType], []); + translatedInstruction.Code = CilOpCodes.Callvirt; + translatedInstruction.Operand = getElementAddress; + + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, getElementAddress.ReturnType); + } + break; + case CilCode.Mkrefany or CilCode.Refanyval: + // Not implemented yet + return false; + default: + Debug.Fail($"Unexpected CIL code: {originalCode}"); + return false; + } + } + else if (originalOperand is MethodAnalysisContext method) + { + MethodAnalysisContext baseMethod; + TypeAnalysisContext[] typeGenericArguments; + TypeAnalysisContext[] methodGenericArguments; + if (method is ConcreteGenericMethodAnalysisContext concreteGeneric) + { + baseMethod = concreteGeneric.BaseMethodContext; + if (concreteGeneric.TypeGenericParameters.Count > 0) + { + typeGenericArguments = new TypeAnalysisContext[concreteGeneric.TypeGenericParameters.Count]; + for (var i = 0; i < concreteGeneric.TypeGenericParameters.Count; i++) + { + typeGenericArguments[i] = replacementVisitor.Replace(concreteGeneric.TypeGenericParameters[i]); + } + } + else + { + typeGenericArguments = []; + } + if (concreteGeneric.MethodGenericParameters.Count > 0) + { + methodGenericArguments = new TypeAnalysisContext[concreteGeneric.MethodGenericParameters.Count]; + for (var i = 0; i < concreteGeneric.MethodGenericParameters.Count; i++) + { + methodGenericArguments[i] = replacementVisitor.Replace(concreteGeneric.MethodGenericParameters[i]); + } + } + else + { + methodGenericArguments = []; + } + } + else + { + baseMethod = method; + typeGenericArguments = []; + methodGenericArguments = []; + } + if (originalCode.Code is CilCode.Callvirt) + { + while (baseMethod.InterfaceRedirectMethod is not null) + { + baseMethod = baseMethod.InterfaceRedirectMethod!; + } + } + if (originalCode.Code is CilCode.Call && baseMethod.InterfaceRedirectMethod is not null) + { + // This is unsupported + return false; + } + if (originalCode.Code is CilCode.Call && baseMethod.UnsafeInvokeMethod is not null) + { + translatedInstruction.Code = CilOpCodes.Nop; + + var targetMethod = baseMethod.UnsafeInvokeMethod?.MaybeMakeConcreteGeneric(typeGenericArguments, methodGenericArguments); + + Debug.Assert(targetMethod is not null); + Debug.Assert(targetMethod.IsStatic); + Debug.Assert(method.Parameters.Count == targetMethod.Parameters.Count - 1); + + var temporaryVariables = new LocalVariable[targetMethod.Parameters.Count]; + for (var i = targetMethod.Parameters.Count - 1; i >= 0; i--) // Order matters + { + var methodParameter = targetMethod.Parameters[i]; + MonoIl2CppConversion.AddMonoToIl2CppConversion(translatedInstructions, methodParameter.ParameterType); + var temporaryVariable = new LocalVariable + { + Type = methodParameter.ParameterType, + }; + translatedInstructions.Add(CilOpCodes.Stloc, temporaryVariable); + + temporaryVariables[i] = temporaryVariable; + localVariableList.Add(temporaryVariable); + } + + foreach (var temporaryVariable in temporaryVariables) + { + translatedInstructions.Add(CilOpCodes.Ldloc, temporaryVariable); + } + + translatedInstructions.Add(originalCode, targetMethod); + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, targetMethod.ReturnType); + } + else if (originalCode.Code is CilCode.Call or CilCode.Callvirt or CilCode.Newobj) + { + translatedInstruction.Code = CilOpCodes.Nop; + + var targetMethod = baseMethod.MaybeMakeConcreteGeneric(typeGenericArguments, methodGenericArguments); + + var temporaryVariables = new LocalVariable[targetMethod.Parameters.Count]; + for (var i = targetMethod.Parameters.Count - 1; i >= 0; i--) // Order matters + { + var methodParameter = targetMethod.Parameters[i]; + MonoIl2CppConversion.AddMonoToIl2CppConversion(translatedInstructions, methodParameter.ParameterType); + var temporaryVariable = new LocalVariable + { + Type = methodParameter.ParameterType, + }; + translatedInstructions.Add(CilOpCodes.Stloc, temporaryVariable); + + temporaryVariables[i] = temporaryVariable; + localVariableList.Add(temporaryVariable); + } + + foreach (var temporaryVariable in temporaryVariables) + { + translatedInstructions.Add(CilOpCodes.Ldloc, temporaryVariable); + } + + translatedInstructions.Add(originalCode, targetMethod); + + var returnType = originalCode == CilOpCodes.Newobj ? targetMethod.DeclaringType! : targetMethod.ReturnType; + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, returnType); + } + else if (originalCode == CilOpCodes.Ldtoken) + { + // Not sure this can happen in normal CIL code, but we check for it just in case. + return false; + } + else if (originalCode == CilOpCodes.Ldftn || originalCode == CilOpCodes.Ldvirtftn) + { + return false; + } + else if (originalCode == CilOpCodes.Jmp) + { + // This shouldn't happen in normal CIL code, but we check for it just in case. + return false; + } + else + { + Debug.Fail($"Unexpected CIL code: {originalCode}"); + return false; + } + } + else if (originalOperand is FieldAnalysisContext field) + { + FieldAnalysisContext baseField; + TypeAnalysisContext[] declaringTypeArguments; + if (field is ConcreteGenericFieldAnalysisContext concreteGenericField) + { + baseField = concreteGenericField.BaseFieldContext; + declaringTypeArguments = replacementVisitor.Replace(((GenericInstanceTypeAnalysisContext)field.DeclaringType).GenericArguments).ToArray(); + } + else + { + baseField = field; + declaringTypeArguments = []; + } + + if (originalCode == CilOpCodes.Ldfld || originalCode == CilOpCodes.Ldsfld) + { + // Load field value + + // ldfld can accept either a ref or a value, so we use stack analysis to determine. + bool operandIsReferenceType; + if (originalCode == CilOpCodes.Ldfld && baseField is { DeclaringType.IsValueType: true }) + { + var stack = dictionary[originalInstruction]; + var operandStackType = stack[^1]; + if (operandStackType is ExactStackType { Type: { } objectType }) + { + operandIsReferenceType = IsByReferenceType(objectType); + } + else if (operandStackType is IntegerStackTypeNative) + { + operandIsReferenceType = true; + } + else + { + return false; + } + } + else + { + operandIsReferenceType = true; + } + + var translatedField = baseField.MaybeMakeConcreteGeneric(declaringTypeArguments); + var fieldType = translatedField.FieldType; + + if (baseField.PropertyAccessor is not null) + { + var accessorMethod = baseField.PropertyAccessor!.Getter!.MaybeMakeConcreteGeneric(declaringTypeArguments, []); + fieldType = accessorMethod.ReturnType; + if (operandIsReferenceType) + { + translatedInstruction.Code = originalCode == CilOpCodes.Ldfld && baseField is { DeclaringType.IsValueType: false } ? CilOpCodes.Callvirt : CilOpCodes.Call; + translatedInstruction.Operand = accessorMethod; + } + else + { + Debug.Assert(accessorMethod.DeclaringType is not null); + LocalVariable temporaryLocal = new(accessorMethod.DeclaringType); + localVariableList.Add(temporaryLocal); + + translatedInstruction.Code = CilOpCodes.Stloc; + translatedInstruction.Operand = temporaryLocal; + + translatedInstructions.Add(CilOpCodes.Ldloca, temporaryLocal); + translatedInstructions.Add(CilOpCodes.Call, accessorMethod); + } + } + else if (baseField.DeclaringType.IsIl2CppPrimitive) + { + Debug.Assert(!baseField.IsStatic, "There should be no static fields."); + Debug.Assert(fieldType.DeclaringAssembly == appContext.Mscorlib); + + if (!operandIsReferenceType) + { + return false; + } + + translatedInstruction.Code = CilOpCodes.Ldobj; + translatedInstruction.Operand = fieldType; + } + else if (baseField.DeclaringType.Fields.Contains(baseField)) + { + Debug.Assert(!baseField.IsStatic, "There should be no static fields."); + Debug.Assert(baseField is { DeclaringType.IsValueType: true }, "Only value types should have instance fields."); + Debug.Assert(baseField.FieldAddressAccessor is not null); + + if (operandIsReferenceType) + { + translatedInstruction.Code = CilOpCodes.Nop; + MonoIl2CppConversion.AddMonoToIl2CppConversion(translatedInstructions, byReference.MakeGenericInstanceType([translatedField.DeclaringType])); + translatedInstructions.Add(CilOpCodes.Call, baseField.FieldAddressAccessor!.MaybeMakeConcreteGeneric(declaringTypeArguments, [])); + translatedInstructions.Add(CilOpCodes.Call, byReferenceStatic_GetValue.MakeGenericInstanceMethod(fieldType)); + } + else + { + translatedInstruction.Code = originalCode; + translatedInstruction.Operand = translatedField; + } + } + else + { + // This should not occur. + return false; + } + + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, fieldType); + } + else if (originalCode == CilOpCodes.Stfld || originalCode == CilOpCodes.Stsfld) + { + // Store field value + + translatedInstruction.Code = CilOpCodes.Nop; + + var translatedField = baseField.MaybeMakeConcreteGeneric(declaringTypeArguments); + var fieldType = translatedField.FieldType; + + var accessorMethod = baseField.PropertyAccessor?.Setter!.MaybeMakeConcreteGeneric(declaringTypeArguments, []); + + if (accessorMethod is not null) + { + fieldType = accessorMethod.Parameters[^1].ParameterType; + } + + MonoIl2CppConversion.AddMonoToIl2CppConversion(translatedInstructions, fieldType); + + if (accessorMethod is not null) + { + translatedInstructions.Add(new Instruction(originalCode == CilOpCodes.Stfld && baseField is { DeclaringType.IsValueType: false } ? CilOpCodes.Callvirt : CilOpCodes.Call, accessorMethod)); + } + else if (baseField.DeclaringType.IsIl2CppPrimitive) + { + Debug.Assert(!baseField.IsStatic, "There should be no static fields."); + Debug.Assert(baseField.FieldType.DeclaringAssembly == appContext.Mscorlib); + + // This should not occur because the primitive fields are readonly. + return false; + } + else if (baseField.DeclaringType.Fields.Contains(baseField)) + { + Debug.Assert(!baseField.IsStatic, "There should be no static fields."); + Debug.Assert(baseField is { DeclaringType.IsValueType: true }, "Only value types should have instance fields."); + Debug.Assert(baseField.FieldAddressAccessor is not null); + + var fieldAddressAccessor = baseField.FieldAddressAccessor!.MaybeMakeConcreteGeneric(declaringTypeArguments, []); + Debug.Assert(TypeAnalysisContextEqualityComparer.Instance.Equals(fieldType, ((GenericInstanceTypeAnalysisContext)fieldAddressAccessor.ReturnType).GenericArguments[0])); + + LocalVariable temporaryLocal = new(fieldType); + localVariableList.Add(temporaryLocal); + + translatedInstructions.Add(CilOpCodes.Stloc, temporaryLocal); + MonoIl2CppConversion.AddMonoToIl2CppConversion(translatedInstructions, byReference.MakeGenericInstanceType([translatedField.DeclaringType])); + translatedInstructions.Add(CilOpCodes.Call, fieldAddressAccessor); + translatedInstructions.Add(CilOpCodes.Ldloc, temporaryLocal); + translatedInstructions.Add(CilOpCodes.Call, byReferenceStatic_SetValue1.MakeGenericInstanceMethod(fieldType)); + } + else + { + // This should not occur. + return false; + } + } + else if (originalCode == CilOpCodes.Ldflda) + { + // Load field address + Debug.Assert(!baseField.IsStatic); + Debug.Assert(baseField.FieldAddressAccessor is not null); + + var translatedField = baseField.MaybeMakeConcreteGeneric(declaringTypeArguments); + var fieldAddressAccessor = baseField.FieldAddressAccessor!.MaybeMakeConcreteGeneric(declaringTypeArguments, []); + + translatedInstruction.Code = CilOpCodes.Nop; + if (baseField.DeclaringType is { IsValueType: true }) + { + MonoIl2CppConversion.AddMonoToIl2CppConversion(translatedInstructions, byReference.MakeGenericInstanceType([translatedField.DeclaringType])); + } + translatedInstructions.Add(CilOpCodes.Call, fieldAddressAccessor); + MonoIl2CppConversion.AddIl2CppToMonoConversion(translatedInstructions, fieldAddressAccessor.ReturnType); + } + else if (originalCode == CilOpCodes.Ldsflda) + { + // Load static field address + // Not implemented yet + return false; + } + else if (originalCode == CilOpCodes.Ldtoken) + { + // This can happen in array initializers that have constant data. + return false; + } + else + { + Debug.Fail($"Unexpected CIL code: {originalCode}"); + return false; + } + } + else if (originalOperand is MultiDimensionalArrayMethod) + { + // Not implemented yet + return false; + } + else + { + return false; + } + } + + var exceptionHandlers = new ExceptionHandler[originalMethodBody.ExceptionHandlers.Count]; + for (var i = 0; i < exceptionHandlers.Length; i++) + { + var originalExceptionHandler = originalMethodBody.ExceptionHandlers[i]; + + var handlerStart = ResolveLabel(originalExceptionHandler.HandlerStart, instructionDictionary); + + TypeAnalysisContext? exceptionType; + if (originalExceptionHandler.ExceptionType is null) + { + exceptionType = null; + } + else if (IsIl2CppPrimitive(originalExceptionHandler.ExceptionType, "Object")) + { + exceptionType = methodContext.AppContext.SystemTypes.SystemObjectType; + } + else + { + if (originalExceptionHandler.ExceptionType is GenericInstanceTypeAnalysisContext genericInstance) + { + exceptionType = genericInstance.GenericType.SystemExceptionType!.MakeGenericInstanceType(replacementVisitor.Replace(genericInstance.GenericArguments)); + } + else + { + exceptionType = originalExceptionHandler.ExceptionType.SystemExceptionType; + Debug.Assert(exceptionType is not null); + } + + // The system exception wrapper contains a reference to the underlying Il2Cpp object. + // We need to load the object reference after entering the exception handler. + + Debug.Assert(handlerStart is Instruction { Code.Code: CilCode.Nop }); + var handlerStartIndex = translatedInstructions.IndexOf((Instruction)handlerStart); + + var loadObjectInstruction = new Instruction(CilOpCodes.Ldfld, methodContext.AppContext.ResolveTypeOrThrow(typeof(Il2CppException)).GetFieldByName(nameof(Il2CppException.Il2cppObject))); + var castInstruction = new Instruction(CilOpCodes.Castclass, originalExceptionHandler.ExceptionType); + translatedInstructions.Insert(handlerStartIndex + 1, loadObjectInstruction); + translatedInstructions.Insert(handlerStartIndex + 2, castInstruction); + } + + var translatedExceptionHandler = new ExceptionHandler + { + HandlerType = originalExceptionHandler.HandlerType, + TryStart = ResolveLabel(originalExceptionHandler.TryStart, instructionDictionary), + TryEnd = ResolveLabel(originalExceptionHandler.TryEnd, instructionDictionary), + HandlerStart = handlerStart, + HandlerEnd = ResolveLabel(originalExceptionHandler.HandlerEnd, instructionDictionary), + FilterStart = ResolveLabel(originalExceptionHandler.FilterStart, instructionDictionary), + ExceptionType = exceptionType, + }; + exceptionHandlers[i] = translatedExceptionHandler; + } + + translatedInstructions.InsertRange(0, initializeInstructions); + + implementationMethod.PutExtraData(new TranslatedMethodBody + { + Instructions = translatedInstructions, + LocalVariables = localVariableList, + ExceptionHandlers = exceptionHandlers, + }); + return true; + + static ILabel[] ResolveLabels(IReadOnlyList labels, Dictionary instructionDictionary) + { + var resolvedLabels = new ILabel[labels.Count]; + for (var i = 0; i < labels.Count; i++) + { + resolvedLabels[i] = ResolveLabel(labels[i], instructionDictionary); + } + return resolvedLabels; + } + + [return: NotNullIfNotNull(nameof(label))] + static ILabel? ResolveLabel(ILabel? label, Dictionary instructionDictionary) + { + if (label is null or EndLabel) + return label; + + return instructionDictionary[(Instruction)label]; + } + } + + private static bool IsIl2CppPrimitive(TypeAnalysisContext type, string name) + { + if (type is ReferencedTypeAnalysisContext) + return false; + + if (type.DeclaringType is not null) + return false; + + if (type.DeclaringAssembly != type.AppContext.Il2CppMscorlib) + return false; + + return type.Namespace == "Il2CppSystem" && type.Name == name; + } +} diff --git a/Il2CppInterop.Generator/TypeAnalysisContextEqualityComparer.cs b/Il2CppInterop.Generator/TypeAnalysisContextEqualityComparer.cs new file mode 100644 index 00000000..9231a67b --- /dev/null +++ b/Il2CppInterop.Generator/TypeAnalysisContextEqualityComparer.cs @@ -0,0 +1,407 @@ +using AsmResolver.DotNet; +using AsmResolver.DotNet.Signatures; +using Cpp2IL.Core.Model.Contexts; +using LibCpp2IL.BinaryStructures; + +namespace Il2CppInterop.Generator; + +public class TypeAnalysisContextEqualityComparer : + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer +{ + public static TypeAnalysisContextEqualityComparer Instance { get; } = new(); + + public bool Equals(TypeAnalysisContext? x, TypeAnalysisContext? y) + { + if (ReferenceEquals(x, y)) + return true; + if (x is null || y is null) + return false; + if (x.Type != y.Type) + return false; + + return x.Type switch + { + Il2CppTypeEnum.IL2CPP_TYPE_ARRAY => Equals(x as ArrayTypeAnalysisContext, y as ArrayTypeAnalysisContext), + Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY => Equals(x as SzArrayTypeAnalysisContext, y as SzArrayTypeAnalysisContext), + Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST => Equals(x as GenericInstanceTypeAnalysisContext, y as GenericInstanceTypeAnalysisContext), + Il2CppTypeEnum.IL2CPP_TYPE_VAR or Il2CppTypeEnum.IL2CPP_TYPE_MVAR => Equals(x as GenericParameterTypeAnalysisContext, y as GenericParameterTypeAnalysisContext), + Il2CppTypeEnum.IL2CPP_TYPE_SENTINEL => Equals(x as SentinelTypeAnalysisContext, y as SentinelTypeAnalysisContext), + Il2CppTypeEnum.IL2CPP_TYPE_PINNED => Equals(x as PinnedTypeAnalysisContext, y as PinnedTypeAnalysisContext), + Il2CppTypeEnum.IL2CPP_TYPE_PTR => Equals(x as PointerTypeAnalysisContext, y as PointerTypeAnalysisContext), + Il2CppTypeEnum.IL2CPP_TYPE_BYREF => Equals(x as ByRefTypeAnalysisContext, y as ByRefTypeAnalysisContext), + Il2CppTypeEnum.IL2CPP_TYPE_BOXED => Equals(x as BoxedTypeAnalysisContext, y as BoxedTypeAnalysisContext), + Il2CppTypeEnum.IL2CPP_TYPE_CMOD_OPT or Il2CppTypeEnum.IL2CPP_TYPE_CMOD_REQD => Equals(x as CustomModifierTypeAnalysisContext, y as CustomModifierTypeAnalysisContext), + Il2CppTypeEnum.IL2CPP_TYPE_FNPTR => false,// Function pointers are not part of the Cpp2IL context system + _ => false,// Type definitions have unique instances + }; + } + public bool Equals(GenericInstanceTypeAnalysisContext? x, GenericInstanceTypeAnalysisContext? y) + { + if (ReferenceEquals(x, y)) + return true; + if (x is null || y is null) + return false; + + return Equals(x.GenericType, y.GenericType) && x.GenericArguments.SequenceEqual(y.GenericArguments, this); + } + public bool Equals(GenericParameterTypeAnalysisContext? x, GenericParameterTypeAnalysisContext? y) + { + return ReferenceEquals(x, y); // Generic parameters have unique instances + } + public bool Equals(SentinelTypeAnalysisContext? x, SentinelTypeAnalysisContext? y) + { + if (x is null || y is null) + return ReferenceEquals(x, y); + + return true; // Sentinel types are always equal + } + public bool Equals(CustomModifierTypeAnalysisContext? x, CustomModifierTypeAnalysisContext? y) + { + if (ReferenceEquals(x, y)) + return true; + if (x is null || y is null) + return false; + + return Equals(x.ElementType, y.ElementType) && Equals(x.ModifierType, y.ModifierType); + } + public bool Equals(PointerTypeAnalysisContext? x, PointerTypeAnalysisContext? y) + { + if (ReferenceEquals(x, y)) + return true; + if (x is null || y is null) + return false; + + return Equals(x.ElementType, y.ElementType); + } + public bool Equals(ByRefTypeAnalysisContext? x, ByRefTypeAnalysisContext? y) + { + if (ReferenceEquals(x, y)) + return true; + if (x is null || y is null) + return false; + + return Equals(x.ElementType, y.ElementType); + } + public bool Equals(BoxedTypeAnalysisContext? x, BoxedTypeAnalysisContext? y) + { + if (ReferenceEquals(x, y)) + return true; + if (x is null || y is null) + return false; + + return Equals(x.ElementType, y.ElementType); + } + public bool Equals(SzArrayTypeAnalysisContext? x, SzArrayTypeAnalysisContext? y) + { + if (ReferenceEquals(x, y)) + return true; + if (x is null || y is null) + return false; + + return Equals(x.ElementType, y.ElementType); + } + public bool Equals(ArrayTypeAnalysisContext? x, ArrayTypeAnalysisContext? y) + { + if (ReferenceEquals(x, y)) + return true; + if (x is null || y is null) + return false; + + return Equals(x.ElementType, y.ElementType) && x.Rank == y.Rank; + } + public bool Equals(PinnedTypeAnalysisContext? x, PinnedTypeAnalysisContext? y) + { + if (ReferenceEquals(x, y)) + return true; + if (x is null || y is null) + return false; + + return Equals(x.ElementType, y.ElementType); + } + + public int GetHashCode(TypeAnalysisContext type) => type.Type switch + { + Il2CppTypeEnum.IL2CPP_TYPE_ARRAY => GetHashCode((ArrayTypeAnalysisContext)type), + Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY => GetHashCode((SzArrayTypeAnalysisContext)type), + Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST => GetHashCode((GenericInstanceTypeAnalysisContext)type), + Il2CppTypeEnum.IL2CPP_TYPE_VAR or Il2CppTypeEnum.IL2CPP_TYPE_MVAR => GetHashCode((GenericParameterTypeAnalysisContext)type), + Il2CppTypeEnum.IL2CPP_TYPE_SENTINEL => GetHashCode((SentinelTypeAnalysisContext)type), + Il2CppTypeEnum.IL2CPP_TYPE_PINNED => GetHashCode((PinnedTypeAnalysisContext)type), + Il2CppTypeEnum.IL2CPP_TYPE_PTR => GetHashCode((PointerTypeAnalysisContext)type), + Il2CppTypeEnum.IL2CPP_TYPE_BYREF => GetHashCode((ByRefTypeAnalysisContext)type), + Il2CppTypeEnum.IL2CPP_TYPE_BOXED => GetHashCode((BoxedTypeAnalysisContext)type), + Il2CppTypeEnum.IL2CPP_TYPE_CMOD_OPT or Il2CppTypeEnum.IL2CPP_TYPE_CMOD_REQD => GetHashCode((CustomModifierTypeAnalysisContext)type), + Il2CppTypeEnum.IL2CPP_TYPE_FNPTR => 0, // Function pointers are not part of the Cpp2IL context system + _ => HashCode.Combine(type.Type, type), // Type definitions have unique instances + }; + + public int GetHashCode(GenericInstanceTypeAnalysisContext type) + { + HashCode hash = new HashCode(); + hash.Add(type.Type); + hash.Add(type.GenericType, this); + foreach (var genericArgument in type.GenericArguments) + { + hash.Add(genericArgument, this); + } + return hash.ToHashCode(); + } + + public int GetHashCode(GenericParameterTypeAnalysisContext type) + { + return HashCode.Combine(type.Type, type); // Generic parameters have unique instances, so we can use the instance itself as the hash code. + } + + public int GetHashCode(SentinelTypeAnalysisContext type) + { + return HashCode.Combine(type.Type); + } + + public int GetHashCode(CustomModifierTypeAnalysisContext type) + { + HashCode hash = new HashCode(); + hash.Add(type.Type); + hash.Add(type.ElementType, this); + hash.Add(type.ModifierType, this); + return hash.ToHashCode(); + } + + public int GetHashCode(PointerTypeAnalysisContext type) + { + HashCode hash = new HashCode(); + hash.Add(type.Type); + hash.Add(type.ElementType, this); + return hash.ToHashCode(); + } + + public int GetHashCode(ByRefTypeAnalysisContext type) + { + HashCode hash = new HashCode(); + hash.Add(type.Type); + hash.Add(type.ElementType, this); + return hash.ToHashCode(); + } + + public int GetHashCode(BoxedTypeAnalysisContext type) + { + HashCode hash = new HashCode(); + hash.Add(type.Type); + hash.Add(type.ElementType, this); + return hash.ToHashCode(); + } + + public int GetHashCode(SzArrayTypeAnalysisContext type) + { + HashCode hash = new HashCode(); + hash.Add(type.Type); + hash.Add(type.ElementType, this); + return hash.ToHashCode(); + } + + public int GetHashCode(ArrayTypeAnalysisContext type) + { + HashCode hash = new HashCode(); + hash.Add(type.Type); + hash.Add(type.ElementType, this); + hash.Add(type.Rank); + return hash.ToHashCode(); + } + + public int GetHashCode(PinnedTypeAnalysisContext type) + { + HashCode hash = new HashCode(); + hash.Add(type.Type); + hash.Add(type.ElementType, this); + return hash.ToHashCode(); + } + + public bool Equals(TypeAnalysisContext? x, TypeSignature? y) + { + if (x is null) + return y is null; + if (y is null) + return false; + + return x.Type switch + { + Il2CppTypeEnum.IL2CPP_TYPE_ARRAY => Equals(x as ArrayTypeAnalysisContext, y as ArrayTypeSignature), + Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY => Equals(x as SzArrayTypeAnalysisContext, y as SzArrayTypeSignature), + Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST => Equals(x as GenericInstanceTypeAnalysisContext, y as GenericInstanceTypeSignature), + Il2CppTypeEnum.IL2CPP_TYPE_VAR or Il2CppTypeEnum.IL2CPP_TYPE_MVAR => Equals(x as GenericParameterTypeAnalysisContext, y as GenericParameterSignature), + Il2CppTypeEnum.IL2CPP_TYPE_SENTINEL => Equals(x as SentinelTypeAnalysisContext, y as SentinelTypeSignature), + Il2CppTypeEnum.IL2CPP_TYPE_PINNED => Equals(x as PinnedTypeAnalysisContext, y as PinnedTypeSignature), + Il2CppTypeEnum.IL2CPP_TYPE_PTR => Equals(x as PointerTypeAnalysisContext, y as PointerTypeSignature), + Il2CppTypeEnum.IL2CPP_TYPE_BYREF => Equals(x as ByRefTypeAnalysisContext, y as ByReferenceTypeSignature), + Il2CppTypeEnum.IL2CPP_TYPE_BOXED => Equals(x as BoxedTypeAnalysisContext, y as BoxedTypeSignature), + Il2CppTypeEnum.IL2CPP_TYPE_CMOD_OPT or Il2CppTypeEnum.IL2CPP_TYPE_CMOD_REQD => Equals(x as CustomModifierTypeAnalysisContext, y as CustomModifierTypeSignature), + _ => SimpleTypeEquals(x, y), + }; + + bool SimpleTypeEquals(TypeAnalysisContext x, TypeSignature y) + { + if (x.Name != y.Name || x.Namespace != y.Namespace) + return false; + + if (x.DeclaringType is not null) + return Equals(x.DeclaringType, y.DeclaringType); + else if (y.DeclaringType is not null) + return false; + + return true; + } + } + + public bool Equals(TypeAnalysisContext? x, ITypeDescriptor? y) + { + return y switch + { + null => x is null, + TypeSignature typeSignature => Equals(x, typeSignature), + ITypeDefOrRef typeDefOrRef => Equals(x, typeDefOrRef), + _ => false, + }; + } + + public bool Equals(TypeAnalysisContext? x, ITypeDefOrRef? y) + { + if (y is TypeSpecification typeSpecification) + return Equals(x, typeSpecification.Signature); + if (x is null) + return y is null; + if (y is null) + return false; + if (x is ReferencedTypeAnalysisContext) + return false; + + return SimpleTypeEquals(x, y); + + bool SimpleTypeEquals(TypeAnalysisContext x, ITypeDefOrRef y) + { + if (x.Name != y.Name || x.Namespace != y.Namespace) + return false; + + if (x.DeclaringType is not null) + return Equals(x.DeclaringType, y.DeclaringType); + else if (y.DeclaringType is not null) + return false; + + return true; + } + } + + public bool Equals(GenericInstanceTypeAnalysisContext? x, GenericInstanceTypeSignature? y) + { + if (x is null) + return y is null; + if (y is null) + return false; + if (!Equals(x.GenericType, y.GenericType)) + return false; + if (x.GenericArguments.Count != y.TypeArguments.Count) + return false; + for (var i = 0; i < x.GenericArguments.Count; i++) + { + if (!Equals(x.GenericArguments[i], y.TypeArguments[i])) + return false; + } + return true; + } + + public bool Equals(GenericParameterTypeAnalysisContext? x, GenericParameterSignature? y) + { + if (x is null) + return y is null; + if (y is null) + return false; + return x.Index == y.Index && Equals(x.Type, y.ParameterType); + } + + private static bool Equals(Il2CppTypeEnum x, GenericParameterType y) => (x, y) switch + { + (Il2CppTypeEnum.IL2CPP_TYPE_VAR, GenericParameterType.Type) => true, + (Il2CppTypeEnum.IL2CPP_TYPE_MVAR, GenericParameterType.Method) => true, + _ => false, + }; + + public bool Equals(SentinelTypeAnalysisContext? x, SentinelTypeSignature? y) + { + if (x is null) + return y is null; + if (y is null) + return false; + return true; + } + + public bool Equals(CustomModifierTypeAnalysisContext? x, CustomModifierTypeSignature? y) + { + if (x is null) + return y is null; + if (y is null) + return false; + return (x.Required == y.IsRequired) && Equals(x.ElementType, y.BaseType) && Equals(x.ModifierType, y.ModifierType); + } + + public bool Equals(PointerTypeAnalysisContext? x, PointerTypeSignature? y) + { + if (x is null) + return y is null; + if (y is null) + return false; + return Equals(x.ElementType, y.BaseType); + } + + public bool Equals(ByRefTypeAnalysisContext? x, ByReferenceTypeSignature? y) + { + if (x is null) + return y is null; + if (y is null) + return false; + return Equals(x.ElementType, y.BaseType); + } + + public bool Equals(BoxedTypeAnalysisContext? x, BoxedTypeSignature? y) + { + if (x is null) + return y is null; + if (y is null) + return false; + return Equals(x.ElementType, y.BaseType); + } + + public bool Equals(SzArrayTypeAnalysisContext? x, SzArrayTypeSignature? y) + { + if (x is null) + return y is null; + if (y is null) + return false; + return Equals(x.ElementType, y.BaseType); + } + + public bool Equals(ArrayTypeAnalysisContext? x, ArrayTypeSignature? y) + { + if (x is null) + return y is null; + if (y is null) + return false; + return x.Rank == y.Rank && Equals(x.ElementType, y.BaseType); + } + + public bool Equals(PinnedTypeAnalysisContext? x, PinnedTypeSignature? y) + { + if (x is null) + return y is null; + if (y is null) + return false; + return Equals(x.ElementType, y.BaseType); + } +} diff --git a/Il2CppInterop.Generator/UnstripBaseProcessingLayer.cs b/Il2CppInterop.Generator/UnstripBaseProcessingLayer.cs new file mode 100644 index 00000000..786d2e5a --- /dev/null +++ b/Il2CppInterop.Generator/UnstripBaseProcessingLayer.cs @@ -0,0 +1,733 @@ +using System.Diagnostics.CodeAnalysis; +using AsmResolver.DotNet; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Logging; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Core.Model.CustomAttributes; +using Il2CppInterop.Generator.Operands; +using LibCpp2IL.BinaryStructures; + +namespace Il2CppInterop.Generator; + +public abstract class UnstripBaseProcessingLayer : Cpp2IlProcessingLayer +{ + public const string AssembliesKey = "unstrip-assemblies"; + public const string DirectoryKey = "unstrip-directory"; + + protected static void InjectAssemblies(ApplicationAnalysisContext appContext, IReadOnlyList assemblyList, bool storeMethodBodies, bool allowGenericTypes) + { + // Inject assemblies + var assemblyDictionary = new Dictionary(assemblyList.Count); + RuntimeContext? runtimeContext = null; + foreach (var assembly in assemblyList) + { + if (IsEmpty(assembly.ManifestModule!)) + continue; // Skip empty assemblies + + var assemblyRuntimeContext = assembly.RuntimeContext ?? throw new InvalidOperationException("Assembly is missing runtime context"); + if (runtimeContext is null) + { + runtimeContext = assemblyRuntimeContext; + } + else if (runtimeContext != assemblyRuntimeContext) + { + throw new InvalidOperationException("All assemblies must have the same runtime context"); + } + + if (!appContext.AssembliesByName.TryGetValue(assembly.Name!, out var assemblyContext)) + { + assemblyContext = appContext.InjectAssembly(assembly.Name!, assembly.Version, (uint)assembly.HashAlgorithm, (uint)assembly.Attributes, assembly.Culture, null, assembly.PublicKey); + assemblyContext.IsUnstripped = true; + } + assemblyDictionary.Add(assembly.ManifestModule!, assemblyContext); + } + + if (runtimeContext is null) + { + return; // No assemblies were injected, so we can skip the rest of the process + } + + // Inject types + var injectedTypes = new List<(TypeDefinition, InjectedTypeAnalysisContext)>(); + var existingTypes = new List<(TypeDefinition, TypeAnalysisContext)>(); + if (allowGenericTypes) + { + foreach ((var module, var assemblyContext) in assemblyDictionary) + { + foreach (var type in module.TopLevelTypes) + { + var typeContext = assemblyContext.GetTypeByFullName(type.FullName); + if (typeContext is null) + { + typeContext = assemblyContext.InjectType((string?)type.Namespace ?? "", (string?)type.Name ?? "", null, (System.Reflection.TypeAttributes)type.Attributes); + foreach (var genericParameter in type.GenericParameters) + { + var genericParameterContext = new GenericParameterTypeAnalysisContext( + genericParameter.Name!, + genericParameter.Number, + Il2CppTypeEnum.IL2CPP_TYPE_VAR, + (System.Reflection.GenericParameterAttributes)genericParameter.Attributes, + typeContext); + typeContext.GenericParameters.Add(genericParameterContext); + } + injectedTypes.Add((type, (InjectedTypeAnalysisContext)typeContext)); + typeContext.IsUnstripped = true; + } + else + { + existingTypes.Add((type, typeContext)); + } + InjectNestedTypes(type, typeContext, injectedTypes, existingTypes); + } + } + } + else + { + var nonExistingTypes = new Queue(); + var existingTypesDictionary = new Dictionary(runtimeContext.SignatureComparer); + foreach ((var module, var assemblyContext) in assemblyDictionary) + { + foreach (var type in module.TopLevelTypes) + { + var typeContext = assemblyContext.GetTypeByFullName(type.FullName); + if (typeContext is null) + { + nonExistingTypes.Enqueue(type); + //AddNestedTypes(type, nonExistingTypes); + } + else + { + existingTypesDictionary.Add(type, typeContext); + //AddNestedTypes(type, typeContext, nonExistingTypes, existingTypesDictionary); + } + } + } + + var invalidTypes = new HashSet(runtimeContext.SignatureComparer); + var injectedTypesDictionary = new Dictionary(runtimeContext.SignatureComparer); + while (nonExistingTypes.TryDequeue(out var nonExistingType)) + { + if (nonExistingType.GenericParameters.Count > 0) + { + invalidTypes.Add(nonExistingType); + continue; + } + if (ContainsAnyAbstractGenericMethods(nonExistingType, runtimeContext)) + { + invalidTypes.Add(nonExistingType); + continue; + } + + var baseType = nonExistingType.BaseType; + if (baseType is not null) + { + var baseTypeResolved = baseType.TryResolve(runtimeContext); + if (baseTypeResolved is null) + { + invalidTypes.Add(nonExistingType); + continue; + } + if (baseTypeResolved.GenericParameters.Count > 0) + { + invalidTypes.Add(nonExistingType); + continue; + } + if (baseTypeResolved.DeclaringType is not null) + { + // We don't currently support nested types + invalidTypes.Add(nonExistingType); + continue; + } + if (invalidTypes.Contains(baseTypeResolved)) + { + invalidTypes.Add(nonExistingType); + continue; + } + if (ContainsAnyAbstractGenericMethods(baseTypeResolved, runtimeContext)) + { + invalidTypes.Add(nonExistingType); + continue; + } + if (!existingTypesDictionary.ContainsKey(baseTypeResolved) && !injectedTypesDictionary.ContainsKey(baseTypeResolved)) + { + nonExistingTypes.Enqueue(nonExistingType); + continue; + } + } + + var shouldSkipToNextType = false; + foreach (var interfaceType in nonExistingType.Interfaces.Select(i => i.Interface)) + { + var interfaceTypeResolved = interfaceType?.TryResolve(runtimeContext); + if (interfaceTypeResolved is null) + { + invalidTypes.Add(nonExistingType); + shouldSkipToNextType = true; + break; + } + if (interfaceTypeResolved.GenericParameters.Count > 0) + { + invalidTypes.Add(nonExistingType); + shouldSkipToNextType = true; + break; + } + if (interfaceTypeResolved.DeclaringType is not null) + { + // We don't currently support nested types + invalidTypes.Add(nonExistingType); + shouldSkipToNextType = true; + break; + } + if (invalidTypes.Contains(interfaceTypeResolved)) + { + invalidTypes.Add(nonExistingType); + shouldSkipToNextType = true; + break; + } + if (ContainsAnyAbstractGenericMethods(interfaceTypeResolved, runtimeContext)) + { + invalidTypes.Add(nonExistingType); + shouldSkipToNextType = true; + break; + } + if (!existingTypesDictionary.ContainsKey(interfaceTypeResolved) && !injectedTypesDictionary.ContainsKey(interfaceTypeResolved)) + { + nonExistingTypes.Enqueue(nonExistingType); + shouldSkipToNextType = true; + break; + } + } + if (shouldSkipToNextType) + { + continue; + } + + if (nonExistingType.DeclaringType is null) + { + var typeContext = assemblyDictionary[nonExistingType.DeclaringModule!].InjectType((string?)nonExistingType.Namespace ?? "", (string?)nonExistingType.Name ?? "", null, (System.Reflection.TypeAttributes)nonExistingType.Attributes); + injectedTypesDictionary.Add(nonExistingType, typeContext); + typeContext.IsUnstripped = true; + } + else if (invalidTypes.Contains(nonExistingType.DeclaringType)) + { + invalidTypes.Add(nonExistingType); + } + else if (nonExistingType.DeclaringType.GenericParameters.Count > 0) + { + invalidTypes.Add(nonExistingType); + } + else if (existingTypesDictionary.TryGetValue(nonExistingType.DeclaringType, out var declaringTypeContext) || injectedTypesDictionary.TryGetValue(nonExistingType.DeclaringType, out declaringTypeContext)) + { + var typeContext = declaringTypeContext.InjectNestedType((string?)nonExistingType.Name ?? "", null, (System.Reflection.TypeAttributes)nonExistingType.Attributes); + injectedTypesDictionary.Add(nonExistingType, typeContext); + typeContext.IsUnstripped = true; + } + else + { + nonExistingTypes.Enqueue(nonExistingType); + } + } + + injectedTypes.Capacity = injectedTypesDictionary.Count; + existingTypes.Capacity = existingTypesDictionary.Count; + injectedTypes.AddRange(injectedTypesDictionary.Select(kv => (kv.Key, (InjectedTypeAnalysisContext)kv.Value))); + existingTypes.AddRange(existingTypesDictionary.Select(kv => (kv.Key, kv.Value))); + } + + // Set up type hierarchy + foreach (var (type, typeContext) in injectedTypes) + { + var resolver = new ContextResolver(typeContext, runtimeContext); + if (type.BaseType is not null) + { + if (resolver.TryResolve(type.BaseType, out var baseTypeContext)) + { + typeContext.SetDefaultBaseType(baseTypeContext); + } + else + { + throw new InvalidOperationException($"Unable to resolve base type {type.BaseType} for type {type.FullName}"); + } + } + + foreach (var interfaceType in type.Interfaces.Select(t => t.Interface)) + { + if (resolver.TryResolve(interfaceType, out var interfaceTypeContext)) + { + typeContext.InterfaceContexts.Add(interfaceTypeContext); + } + else + { + throw new InvalidOperationException($"Unable to resolve interface type {interfaceType} for type {type.FullName}"); + } + } + + for (var i = 0; i < type.GenericParameters.Count; i++) + { + var genericParameter = type.GenericParameters[i]; + var genericParameterContext = typeContext.GenericParameters[i]; + foreach (var constraint in genericParameter.Constraints) + { + if (resolver.TryResolve(constraint.Constraint, out var constraintType)) + { + genericParameterContext.ConstraintTypes.Add(constraintType); + } + else + { + throw new InvalidOperationException($"Unable to resolve generic parameter constraint {constraint.Constraint} for type {type.FullName}"); + } + } + } + } + + // Inject members + List<(InjectedMethodAnalysisContext, IMethodDefOrRef)> methodsNeedingOverrides = new(); + List<(MethodAnalysisContext, MethodDefinition)> methodsNeedingBodies = new(); + foreach (var (type, typeContext) in injectedTypes) + { + CopyCustomAttributes(type, typeContext, typeContext.DeclaringAssembly, runtimeContext); + + for (var i = 0; i < type.GenericParameters.Count; i++) + { + CopyCustomAttributes(type.GenericParameters[i], typeContext.GenericParameters[i], typeContext.DeclaringAssembly, runtimeContext); + } + + foreach (var field in type.Fields) + { + TryInjectField(field, typeContext, runtimeContext); + } + + Dictionary methodLookup = new(); + foreach (var method in type.Methods) + { + if (method.IsPInvokeImpl) + { + // No p/invoke methods + continue; + } + + if (TryInjectMethod(method, typeContext, runtimeContext, out var methodContext)) + { + methodLookup.Add(method, methodContext); + methodsNeedingBodies.Add((methodContext, method)); + } + } + + foreach (var property in type.Properties) + { + TryInjectProperty(property, typeContext, runtimeContext, methodLookup); + } + + foreach (var @event in type.Events) + { + TryInjectEvent(@event, typeContext, runtimeContext, methodLookup); + } + + foreach (var implementation in type.MethodImplementations) + { + if (implementation.Body is not MethodDefinition method || implementation.Declaration is null || !methodLookup.TryGetValue(method, out var methodContext)) + continue; + + methodsNeedingOverrides.Add(((InjectedMethodAnalysisContext)methodContext, implementation.Declaration)); + } + } + foreach (var (type, typeContext) in existingTypes) + { + // We shouldn't inject static fields. + // Even though it's not like instance fields where state gets changed, + // we can't guarantee that the static fields will be initialized properly. + // However, we can still inject constant fields. + foreach (var field in type.Fields) + { + if (!field.IsStatic) + continue; // Instance fields cannot be unstripped + + if (typeContext.Fields.Any(f => f.Name == field.Name)) + continue; // Already present + + if (field.Constant?.InterpretData() is null) + continue; // Skip fields without a constant value + + TryInjectField(field, typeContext, runtimeContext); + } + + Dictionary methodLookup = new(); + foreach (var method in type.Methods) + { + var existingMethodContext = new ContextResolver(typeContext, runtimeContext).ResolveInType(method); + if (existingMethodContext is not null) + { + methodLookup.Add(method, existingMethodContext); + if (method.GenericParameters.Count > 0 || type.GenericParameters.Count > 0) + { + // We inject the method if it's generic because we want to support generic instantiations + // that were not present in the original game. We abstain from non-generic methods because + // of performance, memory usage, output size, and risk of breaking the game. + // + // Counterargument: injecting as much as possible allows for more transpilers to be written. + + methodsNeedingBodies.Add((existingMethodContext, method)); + } + continue; + } + + if (method.IsPInvokeImpl) + { + // No p/invoke methods + continue; + } + + if (!TryInjectMethod(method, typeContext, runtimeContext, out var methodContext)) + continue; + + methodLookup.Add(method, methodContext); + if (method.IsAbstract) + { + methodContext.Attributes &= ~System.Reflection.MethodAttributes.Abstract; // We convert abstract methods to virtual methods + + methodContext.PutExtraData(new OriginalMethodBody() + { + Instructions = + [ + new Instruction(CilOpCodes.Ldnull), + new Instruction(CilOpCodes.Throw), + ] + }); + } + else + { + methodsNeedingBodies.Add((methodContext, method)); + } + + foreach (var implementation in type.MethodImplementations) + { + if (implementation.Body != method || implementation.Declaration is null) + continue; + methodsNeedingOverrides.Add((methodContext, implementation.Declaration)); + } + } + + foreach (var property in type.Properties) + { + if (typeContext.Properties.Any(p => p.Name == property.Name)) + continue; // Already present + + TryInjectProperty(property, typeContext, runtimeContext, methodLookup); + } + + foreach (var @event in type.Events) + { + if (typeContext.Events.Any(e => e.Name == @event.Name)) + continue; // Already present + + TryInjectEvent(@event, typeContext, runtimeContext, methodLookup); + } + } + + // Assign method overrides + foreach (var (methodContext, declaration) in methodsNeedingOverrides) + { + var declarationContext = new ContextResolver(methodContext.DeclaringType!, runtimeContext).Resolve(declaration); + if (declarationContext is not null) + { + methodContext.Overrides.Add((MethodAnalysisContext)declarationContext); + } + } + + // Assign method bodies + if (storeMethodBodies) + { + var successfulCount = 0; + foreach (var (methodContext, methodDefinition) in methodsNeedingBodies) + { + var successful = OriginalMethodBody.MaybeStoreOriginalMethodBody(methodDefinition, methodContext, runtimeContext); + if (successful) + { + successfulCount++; + } + } + + // Report how many method bodies were successfully stored. + Logger.InfoNewline($"Recovered the original method body for {successfulCount}/{methodsNeedingBodies.Count} attempts.", nameof(UnstripBaseProcessingLayer)); + } + } + + private static bool ContainsAnyAbstractGenericMethods(TypeDefinition type, RuntimeContext runtimeContext) + { + while (type.IsAbstract) + { + if (type.Methods.Any(m => m.IsAbstract && m.GenericParameters.Count > 0)) + return true; + if (type.BaseType is null) + break; + var baseTypeResolved = type.BaseType.TryResolve(runtimeContext); + if (baseTypeResolved is null) + return true; // If we can't resolve the base type, we have to assume it contains abstract generic methods + type = baseTypeResolved; + } + return false; + } + + private static void AddNestedTypes(TypeDefinition declaringType, TypeAnalysisContext declaringTypeContext, Queue nonExistingTypes, Dictionary existingTypes) + { + foreach (var nestedType in declaringType.NestedTypes) + { + var nestedTypeContext = declaringTypeContext.NestedTypes.FirstOrDefault(t => t.Name == nestedType.Name); + if (nestedTypeContext is null) + { + nonExistingTypes.Enqueue(nestedType); + AddNestedTypes(nestedType, nonExistingTypes); + } + else + { + existingTypes.Add(nestedType, nestedTypeContext); + AddNestedTypes(nestedType, nestedTypeContext, nonExistingTypes, existingTypes); + } + } + } + + private static void AddNestedTypes(TypeDefinition declaringType, Queue nonExistingTypes) + { + foreach (var nestedType in declaringType.NestedTypes) + { + nonExistingTypes.Enqueue(nestedType); + AddNestedTypes(nestedType, nonExistingTypes); + } + } + + private static void InjectNestedTypes(TypeDefinition declaringType, TypeAnalysisContext declaringTypeContext, List<(TypeDefinition, InjectedTypeAnalysisContext)> injectedTypes, List<(TypeDefinition, TypeAnalysisContext)> existingTypes) + { + foreach (var nestedType in declaringType.NestedTypes) + { + var nestedTypeContext = declaringTypeContext.NestedTypes.FirstOrDefault(t => t.Name == nestedType.Name); + if (nestedTypeContext is null) + { + nestedTypeContext = declaringTypeContext.InjectNestedType((string?)nestedType.Name ?? "", null, (System.Reflection.TypeAttributes)nestedType.Attributes); + foreach (var genericParameter in nestedType.GenericParameters) + { + var genericParameterContext = new GenericParameterTypeAnalysisContext( + genericParameter.Name!, + genericParameter.Number, + Il2CppTypeEnum.IL2CPP_TYPE_VAR, + (System.Reflection.GenericParameterAttributes)genericParameter.Attributes, + nestedTypeContext); + nestedTypeContext.GenericParameters.Add(genericParameterContext); + } + injectedTypes.Add((nestedType, (InjectedTypeAnalysisContext)nestedTypeContext)); + + nestedTypeContext.IsUnstripped = true; + } + else + { + existingTypes.Add((nestedType, nestedTypeContext)); + } + InjectNestedTypes(nestedType, nestedTypeContext, injectedTypes, existingTypes); + } + } + + private static bool TryInjectField(FieldDefinition field, TypeAnalysisContext typeContext, RuntimeContext runtimeContext) + { + if (field.Name is not null && new ContextResolver(typeContext, runtimeContext).TryResolve(field.Signature?.FieldType, out var fieldTypeContext)) + { + var fieldContext = new InjectedFieldAnalysisContext( + field.Name!, + fieldTypeContext, + (System.Reflection.FieldAttributes)field.Attributes, + typeContext); + typeContext.Fields.Add(fieldContext); + + fieldContext.OverrideConstantValue = field.Constant?.InterpretData(); + + CopyCustomAttributes(field, fieldContext, typeContext.DeclaringAssembly, runtimeContext); + + fieldContext.IsUnstripped = true; + + return true; + } + + return false; + } + + private static bool TryInjectMethod(MethodDefinition method, TypeAnalysisContext typeContext, RuntimeContext runtimeContext, [NotNullWhen(true)] out InjectedMethodAnalysisContext? methodContext) + { + // Due to an unfortunate reality of resolving types, we need to create a method and add it to our target type before we can resolve its signature. + // This is because the method signature can reference the method's generic parameters, so we need to create the method first. + methodContext = new InjectedMethodAnalysisContext( + typeContext, + method.Name!, + typeContext.AppContext.SystemTypes.SystemObjectType, + (System.Reflection.MethodAttributes)method.Attributes, + Enumerable.Repeat(typeContext.AppContext.SystemTypes.SystemObjectType, method.Parameters.Count).ToArray(), + method.Parameters.Select(p => p.Name).ToArray(), + method.Parameters.Select(p => (System.Reflection.ParameterAttributes)p.GetOrCreateDefinition().Attributes).ToArray(), + (System.Reflection.MethodImplAttributes)method.ImplAttributes); + + foreach (var genericParameter in method.GenericParameters) + { + var genericParameterContext = new GenericParameterTypeAnalysisContext(genericParameter.Name!, genericParameter.Number, Il2CppTypeEnum.IL2CPP_TYPE_MVAR, (System.Reflection.GenericParameterAttributes)genericParameter.Attributes, methodContext); + methodContext.GenericParameters.Add(genericParameterContext); + } + + typeContext.Methods.Add(methodContext); + + var methodResolver = new ContextResolver(methodContext, runtimeContext); + + if (!methodResolver.TryResolve(method.Signature?.ReturnType, out var returnTypeContext)) + { + typeContext.Methods.Remove(methodContext); + return false; + } + else + { + methodContext.SetDefaultReturnType(returnTypeContext); + } + + if (!methodResolver.TryResolve(method.Parameters.Select(p => p.ParameterType), out var parameterTypeContexts)) + { + typeContext.Methods.Remove(methodContext); + return false; + } + else + { + for (var i = 0; i < parameterTypeContexts.Count; i++) + { + var parameter = (InjectedParameterAnalysisContext)methodContext.Parameters[i]; + parameter.SetDefaultParameterType(parameterTypeContexts[i]); + } + } + + if (!TryAddGenericConstraints(method, methodContext, runtimeContext)) + { + typeContext.Methods.Remove(methodContext); + return false; + } + + CopyCustomAttributes(method, methodContext, typeContext.DeclaringAssembly, runtimeContext); + for (var i = 0; i < method.Parameters.Count; i++) + { + CopyCustomAttributes(method.Parameters[i].GetOrCreateDefinition(), methodContext.Parameters[i], typeContext.DeclaringAssembly, runtimeContext); + } + for (var i = 0; i < method.GenericParameters.Count; i++) + { + CopyCustomAttributes(method.GenericParameters[i], methodContext.GenericParameters[i], typeContext.DeclaringAssembly, runtimeContext); + } + + methodContext.IsUnstripped = true; + + return true; + + static bool TryAddGenericConstraints(MethodDefinition method, InjectedMethodAnalysisContext methodContext, RuntimeContext runtimeContext) + { + var methodResolver = new ContextResolver(methodContext, runtimeContext); + var anyInvalidConstraints = false; + for (var i = 0; i < method.GenericParameters.Count; i++) + { + var genericParameter = method.GenericParameters[i]; + var genericParameterContext = methodContext.GenericParameters[i]; + foreach (var constraint in genericParameter.Constraints) + { + if (methodResolver.TryResolve(constraint.Constraint, out var constraintType)) + { + genericParameterContext.ConstraintTypes.Add(constraintType); + } + else + { + anyInvalidConstraints = true; + break; + } + } + if (anyInvalidConstraints) + break; + } + + return !anyInvalidConstraints; + } + } + + private static bool TryInjectProperty(PropertyDefinition property, TypeAnalysisContext typeContext, RuntimeContext runtimeContext, Dictionary methodLookup) + { + var resolver = new ContextResolver(typeContext, runtimeContext); + + if (!resolver.TryResolve(property.Signature?.ReturnType, out var propertyTypeContext)) + return false; + + var getMethodContext = methodLookup.TryGetValue(property.GetMethod); + if (getMethodContext is null && property.GetMethod is not null) + return false; + + var setMethodContext = methodLookup.TryGetValue(property.SetMethod); + if (setMethodContext is null && property.SetMethod is not null) + return false; + + var propertyContext = new InjectedPropertyAnalysisContext( + property.Name!, + propertyTypeContext, + getMethodContext, + setMethodContext, + (System.Reflection.PropertyAttributes)property.Attributes, + typeContext); + typeContext.Properties.Add(propertyContext); + + CopyCustomAttributes(property, propertyContext, typeContext.DeclaringAssembly, runtimeContext); + + propertyContext.IsUnstripped = true; + + return true; + } + + private static bool TryInjectEvent(EventDefinition @event, TypeAnalysisContext typeContext, RuntimeContext runtimeContext, Dictionary methodLookup) + { + var resolver = new ContextResolver(typeContext, runtimeContext); + + if (!resolver.TryResolve(@event.EventType, out var eventTypeContext)) + return false; + + var addMethodContext = methodLookup.TryGetValue(@event.AddMethod); + if (addMethodContext is null && @event.AddMethod is not null) + return false; + + var removeMethodContext = methodLookup.TryGetValue(@event.RemoveMethod); + if (removeMethodContext is null && @event.RemoveMethod is not null) + return false; + + var fireMethodContext = methodLookup.TryGetValue(@event.FireMethod); + if (fireMethodContext is null && @event.FireMethod is not null) + return false; + + var eventContext = new InjectedEventAnalysisContext( + @event.Name!, + eventTypeContext, + addMethodContext, + removeMethodContext, + fireMethodContext, + (System.Reflection.EventAttributes)@event.Attributes, + typeContext); + typeContext.Events.Add(eventContext); + + CopyCustomAttributes(@event, eventContext, typeContext.DeclaringAssembly, runtimeContext); + + eventContext.IsUnstripped = true; + + return true; + } + + private static void CopyCustomAttributes(IHasCustomAttribute source, HasCustomAttributes destination, AssemblyAnalysisContext assembly, RuntimeContext runtimeContext) + { + foreach (var customAttribute in source.CustomAttributes) + { + if (customAttribute.Constructor is null or { Signature: null or { ParameterTypes.Count: > 0 } }) + continue; // Skip custom attributes with parameters or an invalid constructor + + if (!new ContextResolver(assembly, runtimeContext).TryResolve(customAttribute.Constructor, out var constructorContext)) + continue; // Skip custom attributes with an invalid constructor + + destination.CustomAttributes ??= []; + destination.CustomAttributes.Add(new AnalyzedCustomAttribute((MethodAnalysisContext)constructorContext)); + } + } + + private static bool IsEmpty(ModuleDefinition module) + { + return module.TopLevelTypes.Count == 0 || (module.TopLevelTypes.Count == 1 && module.TopLevelTypes[0].IsModuleType); + } +} diff --git a/Il2CppInterop.Generator/UnstripProcessingLayer.cs b/Il2CppInterop.Generator/UnstripProcessingLayer.cs new file mode 100644 index 00000000..f8636f45 --- /dev/null +++ b/Il2CppInterop.Generator/UnstripProcessingLayer.cs @@ -0,0 +1,46 @@ +using AsmResolver.DotNet; +using Cpp2IL.Core.Logging; +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator; + +public class UnstripProcessingLayer : UnstripBaseProcessingLayer +{ + public override string Name => "Unstrip external assemblies into the Cpp2IL context system"; + + public override string Id => "unstrip"; + + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + var assemblyList = appContext.GetExtraData>(AssembliesKey); + if (assemblyList is null) + { + var directoryPath = appContext.GetExtraData(DirectoryKey); + if (directoryPath is null) + { + Logger.WarnNewline($"No assemblies specified - processor will not run. You need to provide the {DirectoryKey}, either by programmatically adding it as extra data in the app context, or by specifying it in the --processor-config command line option.", nameof(UnstripProcessingLayer)); + return; + } + + RuntimeContext runtimeContext = new(DotNetRuntimeInfo.NetFramework(4, 7), null, KnownCorLibs.MsCorLib_v4_0_0_0, null, Directory.GetDirectories(directoryPath, "*", SearchOption.AllDirectories).Append(directoryPath)); + assemblyList = Directory.GetFiles(directoryPath, "*.dll", SearchOption.AllDirectories) + .Select(path => AssemblyDefinition.FromFile(path, createRuntimeContext: false)) + .ToList(); + + foreach (var assembly in assemblyList) + { + runtimeContext.AddAssembly(assembly); + } + } + + if (assemblyList.Count == 0) + { + Logger.WarnNewline("No assemblies provided - processor will not run.", nameof(UnstripProcessingLayer)); + return; + } + + Logger.InfoNewline($"Unstripping {assemblyList.Count} assemblies...", nameof(UnstripProcessingLayer)); + + InjectAssemblies(appContext, assemblyList, true, false); + } +} diff --git a/Il2CppInterop.Generator/UserFriendlyOverloadProcessingLayer.cs b/Il2CppInterop.Generator/UserFriendlyOverloadProcessingLayer.cs new file mode 100644 index 00000000..1346354e --- /dev/null +++ b/Il2CppInterop.Generator/UserFriendlyOverloadProcessingLayer.cs @@ -0,0 +1,138 @@ +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator.Operands; +using Il2CppInterop.Generator.Visitors; +using Il2CppInterop.Runtime.InteropTypes.Arrays; +using CilOpCodes = AsmResolver.PE.DotNet.Cil.CilOpCodes; + +namespace Il2CppInterop.Generator; + +public sealed class UserFriendlyOverloadProcessingLayer : Cpp2IlProcessingLayer +{ + public override string Name => "User-Friendly Overloads"; + public override string Id => "user_friendly_overloads"; + public override void Process(ApplicationAnalysisContext appContext, Action? progressCallback = null) + { + const string ArrayNamespace = "Il2CppInterop.Runtime.InteropTypes.Arrays"; + const string ArrayClassName = nameof(Il2CppArrayRank1<>) + "`1"; + + var il2CppArrayBase = appContext.ResolveTypeOrThrow(typeof(Il2CppArrayRank1<>)); + var il2CppArrayBase_ToManagedArray = il2CppArrayBase.Methods.Single(m => m.Name == "op_Explicit" && m.ReturnType is SzArrayTypeAnalysisContext); + var il2CppArrayBase_FromManagedArray = il2CppArrayBase.Methods.Single(m => m.Name == "op_Explicit" && m.Parameters.Count == 1 && m.Parameters[0].ParameterType is SzArrayTypeAnalysisContext); + + foreach (var assembly in appContext.Assemblies) + { + if (assembly.IsReferenceAssembly || assembly.IsInjected) + continue; + foreach (var type in assembly.Types) + { + if (type.IsInjected) + continue; + + if (type.IsInterface) + { + continue; // We don't add method overloads to interfaces + } + + // for instead of foreach because we might be modifying the collection + for (var methodIndex = 0; methodIndex < type.Methods.Count; methodIndex++) + { + var method = type.Methods[methodIndex]; + if (method.IsInjected || !method.IsPublic || method.IsSpecialName) + continue; + + method = method.MostUserFriendlyOverload; + + var anyPossibleConversions = method.Parameters.Any(p => + { + // Convert Il2CppArrayBase to T[] + if (p.ParameterType is GenericInstanceTypeAnalysisContext { GenericType: { Namespace: ArrayNamespace, Name: ArrayClassName } }) + return true; + + // Convert Il2Cpp delegate type to System delegate type + // Not implemented yet + + // Convert ref Il2CppSystem.Int32 to ref int + // Not implemented yet + + // Convert Il2Cpp primitive to System primitive + // Not implemented yet + + return false; + }); + if (!anyPossibleConversions) + continue; + + var newMethod = new InjectedMethodAnalysisContext(type, method.Name, appContext.SystemTypes.SystemVoidType, method.Attributes, []) + { + IsInjected = true, + }; + type.Methods.Add(newMethod); + + type.Methods[methodIndex].MostUserFriendlyOverload = newMethod; + + newMethod.CopyGenericParameters(method, true); + + var visitor = TypeReplacementVisitor.CreateForMethodCopying(method, newMethod); + + TypeAnalysisContext[] parameterTypes = new TypeAnalysisContext[method.Parameters.Count]; + MethodAnalysisContext?[] conversionMethods = new MethodAnalysisContext?[method.Parameters.Count]; + + for (var i = 0; i < method.Parameters.Count; i++) + { + var parameter = method.Parameters[i]; + + // Convert Il2CppArrayBase to T[] + if (parameter.ParameterType is GenericInstanceTypeAnalysisContext { GenericType: { Namespace: ArrayNamespace, Name: ArrayClassName }, GenericArguments: [var elementType] }) + { + parameterTypes[i] = visitor.Replace(elementType).MakeSzArrayType(); + conversionMethods[i] = il2CppArrayBase_FromManagedArray.MakeConcreteGeneric([elementType], []); + continue; + } + + // No conversion + { + parameterTypes[i] = visitor.Replace(parameter.ParameterType); + } + } + + newMethod.SetDefaultReturnType(visitor.Replace(method.ReturnType)); + + for (var i = 0; i < method.Parameters.Count; i++) + { + var parameter = method.Parameters[i]; + + var newParameter = new InjectedParameterAnalysisContext(parameter.Name, parameterTypes[i], parameter.Attributes, parameter.ParameterIndex, newMethod); + newMethod.Parameters.Add(newParameter); + } + + List instructions = new(); + + if (!newMethod.IsStatic) + { + instructions.Add(new Instruction(CilOpCodes.Ldarg, This.Instance)); + } + + for (var i = 0; i < newMethod.Parameters.Count; i++) + { + instructions.Add(new Instruction(CilOpCodes.Ldarg, newMethod.Parameters[i])); + var conversionMethod = conversionMethods[i]; + if (conversionMethod is not null) + { + instructions.Add(new Instruction(CilOpCodes.Call, conversionMethod)); + } + } + + instructions.Add(new Instruction(newMethod.IsStatic || type.IsValueType ? CilOpCodes.Call : CilOpCodes.Callvirt, method.MaybeMakeConcreteGeneric(type.GenericParameters, newMethod.GenericParameters))); + + instructions.Add(new Instruction(CilOpCodes.Ret)); + + newMethod.PutExtraData(new NativeMethodBody() + { + Instructions = instructions, + }); + } + } + } + } +} diff --git a/Il2CppInterop.Generator/Usings.cs b/Il2CppInterop.Generator/Usings.cs index 5bf58772..55cb8484 100644 --- a/Il2CppInterop.Generator/Usings.cs +++ b/Il2CppInterop.Generator/Usings.cs @@ -1,3 +1,3 @@ -global using ILProcessor = AsmResolver.DotNet.Code.Cil.CilInstructionCollection; -global using OpCode = AsmResolver.PE.DotNet.Cil.CilOpCode; -global using OpCodes = AsmResolver.PE.DotNet.Cil.CilOpCodes; +global using AsmResolver.PE.DotNet.Cil; +global using Il2CppInterop.Generator.Extensions; +global using Object = Il2CppSystem.Object; diff --git a/Il2CppInterop.Generator/Utils/CorlibReferences.cs b/Il2CppInterop.Generator/Utils/CorlibReferences.cs deleted file mode 100644 index cf341c04..00000000 --- a/Il2CppInterop.Generator/Utils/CorlibReferences.cs +++ /dev/null @@ -1,262 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; - -namespace Il2CppInterop.Generator.Utils; - -internal static class CorlibReferences -{ - /// - /// This is used in the TargetFrameworkAttribute. - /// - public static string TargetFrameworkName => ".NET 6.0"; - public static AssemblyReference TargetCorlib => KnownCorLibs.SystemRuntime_v6_0_0_0; - - public static void RewriteCorlibReference(AssemblyReference assemblyNameReference) - { - CopyValues(assemblyNameReference, TargetCorlib); - } - - private static void CopyValues(AssemblyReference target, AssemblyReference source) - { - target.Attributes = source.Attributes; - target.Culture = source.Culture; - target.DisableJitCompileOptimizer = source.DisableJitCompileOptimizer; - target.EnableJitCompileTracking = source.EnableJitCompileTracking; - target.HashValue = source.HashValue?.ToArray(); - target.HasPublicKey = source.HasPublicKey; - target.IsRetargetable = source.IsRetargetable; - target.IsWindowsRuntime = source.IsWindowsRuntime; - target.Name = source.Name; - target.PublicKeyOrToken = source.PublicKeyOrToken?.ToArray(); - target.Version = source.Version; - } - - public static TypeSignature ImportCorlibReference(this ModuleDefinition module, string fullName) - { - return module.DefaultImporter.ImportTypeSignature(typeof(string).Assembly.GetType(fullName)); - } - - public static TypeSignature Void(this ModuleDefinition module) - { - return module.CorLibTypeFactory.Void; - } - - public static TypeSignature Bool(this ModuleDefinition module) - { - return module.CorLibTypeFactory.Boolean; - } - - public static TypeSignature IntPtr(this ModuleDefinition module) - { - return module.CorLibTypeFactory.IntPtr; - } - - public static TypeSignature String(this ModuleDefinition module) - { - return module.CorLibTypeFactory.String; - } - - public static TypeSignature SByte(this ModuleDefinition module) - { - return module.CorLibTypeFactory.SByte; - } - - public static TypeSignature Byte(this ModuleDefinition module) - { - return module.CorLibTypeFactory.Byte; - } - - public static TypeSignature Short(this ModuleDefinition module) - { - return module.CorLibTypeFactory.Int16; - } - - public static TypeSignature Int(this ModuleDefinition module) - { - return module.CorLibTypeFactory.Int32; - } - - public static TypeSignature Long(this ModuleDefinition module) - { - return module.CorLibTypeFactory.Int64; - } - - public static TypeSignature UShort(this ModuleDefinition module) - { - return module.CorLibTypeFactory.UInt16; - } - - public static TypeSignature UInt(this ModuleDefinition module) - { - return module.CorLibTypeFactory.UInt32; - } - - public static TypeSignature ULong(this ModuleDefinition module) - { - return module.CorLibTypeFactory.UInt64; - } - - public static TypeSignature Float(this ModuleDefinition module) - { - return module.CorLibTypeFactory.Single; - } - - public static TypeSignature Double(this ModuleDefinition module) - { - return module.CorLibTypeFactory.Double; - } - - public static TypeSignature Char(this ModuleDefinition module) - { - return module.CorLibTypeFactory.Char; - } - - public static TypeSignature Type(this ModuleDefinition module) - { - return module.DefaultImporter.ImportTypeSignature(typeof(Type)); - } - - public static TypeSignature Object(this ModuleDefinition module) - { - return module.CorLibTypeFactory.Object; - } - - public static TypeSignature Enum(this ModuleDefinition module) - { - return module.DefaultImporter.ImportTypeSignature(typeof(Enum)); - } - - public static TypeSignature ValueType(this ModuleDefinition module) - { - return module.DefaultImporter.ImportTypeSignature(typeof(ValueType)); - } - - public static TypeSignature Delegate(this ModuleDefinition module) - { - return module.DefaultImporter.ImportTypeSignature(typeof(Delegate)); - } - - public static TypeSignature MulticastDelegate(this ModuleDefinition module) - { - return module.DefaultImporter.ImportTypeSignature(typeof(MulticastDelegate)); - } - - public static TypeSignature DefaultMemberAttribute(this ModuleDefinition module) - { - return module.DefaultImporter.ImportTypeSignature(typeof(DefaultMemberAttribute)); - } - - public static TypeSignature NotSupportedException(this ModuleDefinition module) - { - return module.DefaultImporter.ImportTypeSignature(typeof(NotSupportedException)); - } - - public static TypeSignature FlagsAttribute(this ModuleDefinition module) - { - return module.DefaultImporter.ImportTypeSignature(typeof(FlagsAttribute)); - } - - public static TypeSignature ObsoleteAttribute(this ModuleDefinition module) - { - return module.DefaultImporter.ImportTypeSignature(typeof(ObsoleteAttribute)); - } - - public static TypeSignature Attribute(this ModuleDefinition module) - { - return module.DefaultImporter.ImportTypeSignature(typeof(Attribute)); - } - - public static TypeSignature RuntimeTypeHandle(this ModuleDefinition module) - { - return module.DefaultImporter.ImportTypeSignature(typeof(RuntimeTypeHandle)); - } - - public static TypeSignature ExtensionAttribute(this ModuleDefinition module) - { - return module.DefaultImporter.ImportTypeSignature(typeof(ExtensionAttribute)); - } - - public static TypeSignature ParamArrayAttribute(this ModuleDefinition module) - { - return module.DefaultImporter.ImportTypeSignature(typeof(ParamArrayAttribute)); - } - - public static TypeSignature Action(this ModuleDefinition module, int n = 0) - { - return n switch - { - 0 => module.ImportCorlibReference("System.Action"), - 1 => module.ImportCorlibReference("System.Action`1"), - _ => module.ImportCorlibReference($"System.Action`{n}") - }; - } - - public static TypeSignature Func(this ModuleDefinition module, int n = 0) - { - return n switch - { - 0 => module.ImportCorlibReference("System.Func`1"), - 1 => module.ImportCorlibReference("System.Func`2"), - _ => module.ImportCorlibReference($"System.Func`{n + 1}") - }; - } - - public static MemberReference TypeGetTypeFromHandle(this ModuleDefinition module) - { - var type = module.Type(); - MethodSignature signature = MethodSignature.CreateStatic(type, module.RuntimeTypeHandle()); - return new MemberReference(type.ToTypeDefOrRef(), nameof(System.Type.GetTypeFromHandle), signature); - } - - public static MemberReference TypeGetIsValueType(this ModuleDefinition module) - { - var type = module.Type(); - return new MemberReference(type.ToTypeDefOrRef(), "get_IsValueType", MethodSignature.CreateInstance(module.Bool())); - } - - public static MemberReference TypeGetFullName(this ModuleDefinition module) - { - var type = module.Type(); - return new MemberReference(type.ToTypeDefOrRef(), "get_FullName", MethodSignature.CreateInstance(module.String())); - } - - public static MemberReference StringEquals(this ModuleDefinition module) - { - var @string = module.String(); - MethodSignature signature = MethodSignature.CreateStatic(module.Bool(), @string, @string); - return new MemberReference(@string.ToTypeDefOrRef(), nameof(string.Equals), signature); - } - - public static MemberReference ExtensionAttributeCtor(this ModuleDefinition module) - { - return MakeConstructorReference(module, module.ExtensionAttribute().ToTypeDefOrRef()); - } - - public static MemberReference ParamArrayAttributeCtor(this ModuleDefinition module) - { - return MakeConstructorReference(module, module.ParamArrayAttribute().ToTypeDefOrRef()); - } - - public static MemberReference FlagsAttributeCtor(this ModuleDefinition module) - { - return MakeConstructorReference(module, module.FlagsAttribute().ToTypeDefOrRef()); - } - - public static MemberReference NotSupportedExceptionCtor(this ModuleDefinition module) - { - return MakeConstructorReference(module, module.NotSupportedException().ToTypeDefOrRef(), module.String()); - } - - public static MemberReference ObsoleteAttributeCtor(this ModuleDefinition module) - { - return MakeConstructorReference(module, module.ObsoleteAttribute().ToTypeDefOrRef(), module.String()); - } - - private static MemberReference MakeConstructorReference(ModuleDefinition module, ITypeDefOrRef type, params TypeSignature[] parameters) - { - MethodSignature signature = MethodSignature.CreateInstance(module.Void(), parameters); - return new MemberReference(type, ".ctor", signature); - } -} diff --git a/Il2CppInterop.Generator/Utils/FieldAccessorGenerator.cs b/Il2CppInterop.Generator/Utils/FieldAccessorGenerator.cs deleted file mode 100644 index 7a2ffa27..00000000 --- a/Il2CppInterop.Generator/Utils/FieldAccessorGenerator.cs +++ /dev/null @@ -1,130 +0,0 @@ -using AsmResolver.DotNet; -using AsmResolver.DotNet.Code.Cil; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; - -namespace Il2CppInterop.Generator.Utils; - -internal static class FieldAccessorGenerator -{ - public static void MakeGetter(FieldDefinition field, FieldRewriteContext fieldContext, PropertyDefinition property, - RuntimeAssemblyReferences imports) - { - var attributes = Field2MethodAttrs(field.Attributes) | MethodAttributes.SpecialName | MethodAttributes.HideBySig; - var getter = new MethodDefinition("get_" + property.Name, - attributes, - MethodSignatureCreator.CreateMethodSignature(attributes, property.Signature!.ReturnType, 0)); - - getter.CilMethodBody = new(); - var getterBody = getter.CilMethodBody.Instructions; - property.DeclaringType!.Methods.Add(getter); - - CilLocalVariable local0; - if (field.IsStatic) - { - local0 = new CilLocalVariable(property.Signature.ReturnType.IsValueType() - ? property.Signature.ReturnType - : imports.Module.IntPtr()); - getter.CilMethodBody.LocalVariables.Add(local0); - - var localIsPointer = false; - if (field.Signature!.FieldType.IsValueType() && !property.Signature.ReturnType.IsValueType()) - { - var pointerStore = imports.Il2CppClassPointerStore.MakeGenericInstanceType(property.Signature.ReturnType).ToTypeDefOrRef(); - var pointerStoreType = property.DeclaringType.DeclaringModule!.DefaultImporter.ImportType(pointerStore); - getterBody.Add(OpCodes.Ldsfld, - new MemberReference(pointerStoreType, "NativeClassPtr", new FieldSignature(imports.Module.IntPtr()))); - getterBody.Add(OpCodes.Ldc_I4, 0); - getterBody.Add(OpCodes.Conv_U); - getterBody.Add(OpCodes.Call, imports.IL2CPP_il2cpp_class_value_size.Value); - getterBody.Add(OpCodes.Conv_U); - getterBody.Add(OpCodes.Localloc); - getterBody.Add(OpCodes.Stloc, local0); - localIsPointer = true; - } - - getterBody.Add(OpCodes.Ldsfld, fieldContext.PointerField); - if (localIsPointer) - getterBody.Add(OpCodes.Ldloc, local0); - else - getterBody.Add(OpCodes.Ldloca_S, local0); - getterBody.Add(OpCodes.Conv_U); - getterBody.Add(OpCodes.Call, imports.IL2CPP_il2cpp_field_static_get_value.Value); - - if (property.Signature.ReturnType.IsValueType()) - { - getterBody.Add(OpCodes.Ldloc, local0); - getterBody.Add(OpCodes.Ret); - - property.GetMethod = getter; - return; - } - } - else - { - local0 = new CilLocalVariable(imports.Module.IntPtr()); - getter.CilMethodBody.LocalVariables.Add(local0); - - getterBody.EmitObjectToPointer(fieldContext.DeclaringType.OriginalType.ToTypeSignature(), fieldContext.DeclaringType.NewType.ToTypeSignature(), - fieldContext.DeclaringType, 0, false, false, false, false, out _); - getterBody.Add(OpCodes.Ldsfld, fieldContext.PointerField); - getterBody.Add(OpCodes.Call, imports.IL2CPP_il2cpp_field_get_offset.Value); - getterBody.Add(OpCodes.Add); - - getterBody.Add(OpCodes.Stloc_0); - } - - getterBody.EmitPointerToObject(fieldContext.OriginalField.Signature!.FieldType, property.Signature.ReturnType, - fieldContext.DeclaringType, local0, !field.IsStatic, false); - - if (property.Signature.ReturnType.IsPointerLike()) - getterBody.Add(OpCodes.Ldind_I); - - getterBody.Add(OpCodes.Ret); - - property.GetMethod = getter; - } - - public static void MakeSetter(FieldDefinition field, FieldRewriteContext fieldContext, PropertyDefinition property, - RuntimeAssemblyReferences imports) - { - var attributes = Field2MethodAttrs(field.Attributes) | MethodAttributes.SpecialName | MethodAttributes.HideBySig; - var setter = new MethodDefinition("set_" + property.Name, - attributes, - MethodSignatureCreator.CreateMethodSignature(attributes, imports.Module.Void(), 0, property.Signature!.ReturnType)); - property.DeclaringType!.Methods.Add(setter); - setter.CilMethodBody = new(); - var setterBody = setter.CilMethodBody.Instructions; - - if (field.IsStatic) - { - setterBody.Add(OpCodes.Ldsfld, fieldContext.PointerField); - setterBody.EmitObjectToPointer(field.Signature!.FieldType, property.Signature.ReturnType, fieldContext.DeclaringType, 0, false, - true, true, true, out _); - setterBody.Add(OpCodes.Call, imports.IL2CPP_il2cpp_field_static_set_value.Value); - } - else - { - setterBody.EmitObjectToPointer(fieldContext.DeclaringType.OriginalType.ToTypeSignature(), fieldContext.DeclaringType.NewType.ToTypeSignature(), - fieldContext.DeclaringType, 0, false, false, false, false, out _); - setterBody.Add(OpCodes.Dup); - setterBody.Add(OpCodes.Ldsfld, fieldContext.PointerField); - setterBody.Add(OpCodes.Call, imports.IL2CPP_il2cpp_field_get_offset.Value); - setterBody.Add(OpCodes.Add); - setterBody.EmitObjectStore(field.Signature!.FieldType, property.Signature.ReturnType, fieldContext.DeclaringType, 1); - } - - setterBody.Add(OpCodes.Ret); - - property.SetMethod = setter; - } - - private static MethodAttributes Field2MethodAttrs(FieldAttributes fieldAttributes) - { - if ((fieldAttributes & FieldAttributes.Static) != 0) - return MethodAttributes.Public | MethodAttributes.Static; - return MethodAttributes.Public; - } -} diff --git a/Il2CppInterop.Generator/Utils/Memoize.cs b/Il2CppInterop.Generator/Utils/Memoize.cs deleted file mode 100644 index bbab4b7b..00000000 --- a/Il2CppInterop.Generator/Utils/Memoize.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Il2CppInterop.Generator.Utils; - -public class Memoize where TParam : notnull -{ - private readonly Dictionary _cache = new(); - private readonly Func _func; - - public Memoize(Func func) => _func = func; - - public TResult Get(TParam param) - { - if (_cache.TryGetValue(param.GetHashCode(), out var result)) - { - return result; - } - - result = _func(param); - _cache.Add(param.GetHashCode(), result); - return result; - } -} diff --git a/Il2CppInterop.Generator/Utils/MethodSignatureCreator.cs b/Il2CppInterop.Generator/Utils/MethodSignatureCreator.cs deleted file mode 100644 index 023ca43d..00000000 --- a/Il2CppInterop.Generator/Utils/MethodSignatureCreator.cs +++ /dev/null @@ -1,17 +0,0 @@ -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; - -namespace Il2CppInterop.Generator.Utils; - -internal static class MethodSignatureCreator -{ - public static MethodSignature CreateMethodSignature(MethodAttributes attributes, TypeSignature returnType, int genericParameterCount, params TypeSignature[] parameterTypes) - { - return CreateMethodSignature((attributes & MethodAttributes.Static) != 0, returnType, genericParameterCount, parameterTypes); - } - - public static MethodSignature CreateMethodSignature(bool isStatic, TypeSignature returnType, int genericParameterCount, params TypeSignature[] parameterTypes) - { - return isStatic ? MethodSignature.CreateStatic(returnType, genericParameterCount, parameterTypes) : MethodSignature.CreateInstance(returnType, genericParameterCount, parameterTypes); - } -} diff --git a/Il2CppInterop.Generator/Utils/ReferenceCreator.cs b/Il2CppInterop.Generator/Utils/ReferenceCreator.cs deleted file mode 100644 index b9272be6..00000000 --- a/Il2CppInterop.Generator/Utils/ReferenceCreator.cs +++ /dev/null @@ -1,22 +0,0 @@ -using AsmResolver; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; - -namespace Il2CppInterop.Generator.Utils; -internal static class ReferenceCreator -{ - public static MemberReference CreateFieldReference(Utf8String? name, TypeSignature fieldType, IMemberRefParent? parent) - { - return new MemberReference(parent, name, new FieldSignature(fieldType)); - } - - public static MemberReference CreateInstanceMethodReference(Utf8String? name, TypeSignature returnType, IMemberRefParent? parent, params TypeSignature[] parameterTypes) - { - return new MemberReference(parent, name, MethodSignature.CreateInstance(returnType, parameterTypes)); - } - - public static MemberReference CreateStaticMethodReference(Utf8String? name, TypeSignature returnType, IMemberRefParent? parent, params TypeSignature[] parameterTypes) - { - return new MemberReference(parent, name, MethodSignature.CreateStatic(returnType, parameterTypes)); - } -} diff --git a/Il2CppInterop.Generator/Utils/RuntimeAssemblyReferences.cs b/Il2CppInterop.Generator/Utils/RuntimeAssemblyReferences.cs deleted file mode 100644 index fbdfc204..00000000 --- a/Il2CppInterop.Generator/Utils/RuntimeAssemblyReferences.cs +++ /dev/null @@ -1,516 +0,0 @@ -// ReSharper disable InconsistentNaming - -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using Il2CppInterop.Common.Attributes; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; - -namespace Il2CppInterop.Generator.Utils; - -public class RuntimeAssemblyReferences -{ - private readonly Dictionary allTypes = new(); - private readonly RewriteGlobalContext globalCtx; - - public RuntimeAssemblyReferences(ModuleDefinition module, RewriteGlobalContext globalContext) - { - Module = module; - globalCtx = globalContext; - InitTypeRefs(); - InitMethodRefs(); - } - - public ModuleDefinition Module { get; } -#nullable disable - public Memoize Il2CppRefrenceArrayctor { get; private set; } - public Lazy Il2CppStringArrayctor { get; private set; } - public Memoize Il2CppStructArrayctor { get; private set; } - public Memoize Il2CppRefrenceArrayctor_size { get; private set; } - public Lazy Il2CppStringArrayctor_size { get; private set; } - public Memoize Il2CppStructArrayctor_size { get; private set; } - public Lazy Il2CppArrayBase_get_Length { get; private set; } - public Memoize Il2CppArrayBase_get_Item { get; private set; } - public Memoize Il2CppArrayBase_set_Item { get; private set; } - public Lazy IL2CPP_Il2CppObjectBaseToPtr { get; private set; } - public Lazy IL2CPP_Il2CppObjectBaseToPtrNotNull { get; private set; } - public Lazy IL2CPP_Il2CppStringToManaged { get; private set; } - public Lazy IL2CPP_ManagedStringToIl2Cpp { get; private set; } - public Lazy Il2CppObjectBase_Cast { get; private set; } - public Lazy Il2CppObjectBase_TryCast { get; private set; } - public Lazy Il2CppObjectPool_Get { get; private set; } - public Lazy IL2CPP_ResolveICall { get; private set; } - public Lazy IL2CPP_il2cpp_gc_wbarrier_set_field { get; private set; } - public Lazy IL2CPP_FieldWriteWbarrierStub { get; private set; } - public Lazy IL2CPP_il2cpp_field_get_offset { get; private set; } - public Lazy IL2CPP_il2cpp_field_static_get_value { get; private set; } - public Lazy IL2CPP_il2cpp_field_static_set_value { get; private set; } - public Lazy IL2CPP_il2cpp_runtime_invoke { get; private set; } - public Lazy IL2CPP_il2cpp_runtime_class_init { get; private set; } - public Lazy IL2CPP_il2cpp_object_unbox { get; private set; } - public Lazy IL2CPP_il2cpp_value_box { get; private set; } - public Lazy IL2CPP_il2cpp_class_value_size { get; private set; } - public Lazy IL2CPP_il2cpp_object_get_class { get; private set; } - public Lazy IL2CPP_il2cpp_class_is_valuetype { get; private set; } - public Lazy Il2CppException_RaiseExceptionIfNecessary { get; private set; } - public Lazy IL2CPP_il2cpp_object_get_virtual_method { get; private set; } - public Lazy IL2CPP_GetIl2CppField { get; private set; } - public Lazy IL2CPP_GetIl2CppNestedType { get; private set; } - public Lazy IL2CPP_GetIl2CppClass { get; private set; } - public Lazy IL2CPP_GetIl2CppMethod { get; private set; } - public Lazy IL2CPP_GetIl2CppMethodByToken { get; private set; } - public Lazy IL2CPP_il2cpp_class_get_type { get; private set; } - public Lazy IL2CPP_il2cpp_class_from_type { get; private set; } - public Lazy IL2CPP_il2cpp_object_new { get; private set; } - public Lazy IL2CPP_il2cpp_method_get_from_reflection { get; private set; } - public Lazy IL2CPP_il2cpp_method_get_object { get; private set; } - public Lazy IL2CPP_PointerToValueGeneric { get; private set; } - public Lazy IL2CPP_RenderTypeName { get; private set; } - public Lazy OriginalNameAttributector { get; private set; } - public Lazy ObfuscatedNameAttributector { get; private set; } - public Lazy CallerCountAttributector { get; private set; } - public Lazy CachedScanResultsAttributector { get; private set; } - public Lazy Il2CppSystemDelegateCombine { get; private set; } - public Lazy Il2CppSystemDelegateRemove { get; private set; } - public Lazy Il2CppSystemRuntimeTypeHandleGetRuntimeTypeHandle { get; private set; } - - public IMethodDescriptor WriteFieldWBarrier => globalCtx.HasGcWbarrierFieldWrite - ? IL2CPP_il2cpp_gc_wbarrier_set_field.Value - : IL2CPP_FieldWriteWbarrierStub.Value; - - public TypeSignature Il2CppObjectBase { get; private set; } - public TypeSignature Il2CppObjectPool { get; private set; } - public TypeSignature Il2CppStringArray { get; private set; } - public TypeSignature Il2CppArrayBase { get; private set; } - public TypeSignature Il2CppStructArray { get; private set; } - public TypeSignature Il2CppReferenceArray { get; private set; } - public TypeSignature Il2CppClassPointerStore { get; private set; } - public TypeSignature Il2Cpp { get; set; } - public TypeSignature RuntimeReflectionHelper { get; private set; } - public TypeSignature DelegateSupport { get; private set; } - public TypeSignature Il2CppException { get; private set; } -#nullable enable - private TypeSignature ResolveType(string typeName) - { - return allTypes[typeName]; - } - - private void InitTypeRefs() - { - allTypes["System.Void"] = Module.DefaultImporter.ImportTypeSignature(typeof(void)); - allTypes["System.String[]"] = Module.DefaultImporter.ImportTypeSignature(typeof(string[])); - allTypes["System.IntPtr"] = Module.DefaultImporter.ImportTypeSignature(typeof(IntPtr)); - allTypes["System.String"] = Module.DefaultImporter.ImportTypeSignature(typeof(string)); - allTypes["System.UInt32"] = Module.DefaultImporter.ImportTypeSignature(typeof(uint)); - allTypes["System.Void*"] = Module.DefaultImporter.ImportTypeSignature(typeof(void*)); - allTypes["System.Void**"] = Module.DefaultImporter.ImportTypeSignature(typeof(void**)); - allTypes["System.IntPtr&"] = Module.DefaultImporter.ImportTypeSignature(typeof(IntPtr).MakeByRefType()); - allTypes["System.Int32"] = Module.DefaultImporter.ImportTypeSignature(typeof(int)); - allTypes["System.UInt32&"] = Module.DefaultImporter.ImportTypeSignature(typeof(uint).MakeByRefType()); - allTypes["System.Boolean"] = Module.DefaultImporter.ImportTypeSignature(typeof(bool)); - allTypes["System.Int64"] = Module.DefaultImporter.ImportTypeSignature(typeof(long)); - - var assemblyRef = new AssemblyReference("Il2CppInterop.Runtime", new Version(0, 0, 0, 0)); - Module.AssemblyReferences.Add(assemblyRef); - - Il2CppObjectBase = - new TypeReference(Module, assemblyRef, "Il2CppInterop.Runtime.InteropTypes", "Il2CppObjectBase").ToTypeSignature(); - - Il2CppObjectPool = new TypeReference(Module, assemblyRef, "Il2CppInterop.Runtime.Runtime", "Il2CppObjectPool").ToTypeSignature(); - - Il2CppStringArray = new TypeReference(Module, assemblyRef, "Il2CppInterop.Runtime.InteropTypes.Arrays", "Il2CppStringArray").ToTypeSignature(); - - Il2CppArrayBase = new TypeReference(Module, assemblyRef, "Il2CppInterop.Runtime.InteropTypes.Arrays", "Il2CppArrayBase`1").ToTypeSignature(); - - var nonGenericIl2CppArrayBase = new TypeReference(Module, assemblyRef, "Il2CppInterop.Runtime.InteropTypes.Arrays", "Il2CppArrayBase").ToTypeSignature(); - - var genericIl2CppArrayBase = new TypeReference(Module, assemblyRef, "Il2CppInterop.Runtime.InteropTypes.Arrays", "Il2CppArrayBase`1").ToTypeSignature(); - - Il2CppStructArray = new TypeReference(Module, assemblyRef, "Il2CppInterop.Runtime.InteropTypes.Arrays", "Il2CppStructArray`1").ToTypeSignature(); - - Il2CppReferenceArray = new TypeReference(Module, assemblyRef, "Il2CppInterop.Runtime.InteropTypes.Arrays", "Il2CppReferenceArray`1").ToTypeSignature(); - - Il2CppClassPointerStore = new TypeReference(Module, assemblyRef, "Il2CppInterop.Runtime", "Il2CppClassPointerStore`1").ToTypeSignature(); - - Il2Cpp = new TypeReference(Module, assemblyRef, "Il2CppInterop.Runtime", "IL2CPP").ToTypeSignature(); - - RuntimeReflectionHelper = - new TypeReference(Module, assemblyRef, "Il2CppInterop.Runtime", "RuntimeReflectionHelper").ToTypeSignature(); - - DelegateSupport = new TypeReference(Module, assemblyRef, "Il2CppInterop.Runtime", "DelegateSupport").ToTypeSignature(); - - Il2CppException = new TypeReference(Module, assemblyRef, "Il2CppInterop.Runtime", "Il2CppException").ToTypeSignature(); - - allTypes["Il2CppInterop.Runtime.InteropTypes.Il2CppObjectBase"] = Il2CppObjectBase; - allTypes["Il2CppInterop.Runtime.Runtime.Il2CppObjectPool"] = Il2CppObjectPool; - allTypes["Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppArrayBase"] = nonGenericIl2CppArrayBase; - allTypes["Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppArrayBase"] = genericIl2CppArrayBase; - allTypes["Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStringArray"] = Il2CppStringArray; - allTypes["Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppReferenceArray"] = Il2CppReferenceArray; - allTypes["Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStructArray"] = Il2CppStructArray; - allTypes["Il2CppInterop.Runtime.Il2CppException"] = Il2CppException; - allTypes["Il2CppInterop.Runtime.IL2CPP"] = Il2Cpp; - } - - private void InitMethodRefs() - { - Il2CppRefrenceArrayctor = new((param) => - { - var owner = ResolveType("Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppReferenceArray"); - var gp = new GenericParameterSignature(GenericParameterType.Type, 0); - var giOwner = owner.MakeGenericInstanceType(param).ToTypeDefOrRef(); - return ReferenceCreator.CreateInstanceMethodReference(".ctor", ResolveType("System.Void"), - giOwner, gp.MakeSzArrayType()); - }); - - Il2CppStringArrayctor = new Lazy(() => - { - return ReferenceCreator.CreateInstanceMethodReference(".ctor", ResolveType("System.Void"), - ResolveType("Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStringArray").ToTypeDefOrRef(), ResolveType("System.String[]")); - }); - - Il2CppStructArrayctor = new((param) => - { - var owner = ResolveType("Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStructArray"); - var gp = new GenericParameterSignature(GenericParameterType.Type, 0); - var giOwner = new GenericInstanceTypeSignature(owner.ToTypeDefOrRef(), false); - giOwner.TypeArguments.Add(param); - var mr = ReferenceCreator.CreateInstanceMethodReference(".ctor", ResolveType("System.Void"), - giOwner.ToTypeDefOrRef()); - var paramType = gp.MakeSzArrayType(); - ((MethodSignature)mr.Signature!).ParameterTypes.Add(paramType); - return mr; - }); - - Il2CppRefrenceArrayctor_size = new((param) => - { - var owner = ResolveType("Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppReferenceArray"); - var giOwner = new GenericInstanceTypeSignature(owner.ToTypeDefOrRef(), false); - giOwner.TypeArguments.Add(param); - var mr = ReferenceCreator.CreateInstanceMethodReference(".ctor", ResolveType("System.Void"), - giOwner.ToTypeDefOrRef(), ResolveType("System.Int64")); - return mr; - }); - - Il2CppStringArrayctor_size = new Lazy(() => - { - var mr = ReferenceCreator.CreateInstanceMethodReference(".ctor", ResolveType("System.Void"), - ResolveType("Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStringArray").ToTypeDefOrRef(), ResolveType("System.Int64")); - return mr; - }); - - Il2CppStructArrayctor_size = new((param) => - { - var owner = ResolveType("Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStructArray"); - var giOwner = owner.MakeGenericInstanceType(param).ToTypeDefOrRef(); - var mr = ReferenceCreator.CreateInstanceMethodReference(".ctor", ResolveType("System.Void"), - giOwner, ResolveType("System.Int64")); - return mr; - }); - - Il2CppArrayBase_get_Length = new Lazy(() => - { - var owner = ResolveType("Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppArrayBase"); - var mr = ReferenceCreator.CreateInstanceMethodReference("get_Length", ResolveType("System.Int32"), - owner.ToTypeDefOrRef()); - return mr; - }); - - Il2CppArrayBase_get_Item = new((param) => - { - var owner = ResolveType("Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppArrayBase"); - var giOwner = owner.MakeGenericInstanceType(param).ToTypeDefOrRef(); - var mr = ReferenceCreator.CreateInstanceMethodReference("get_Item", new GenericParameterSignature(Module, GenericParameterType.Type, 0), - giOwner, ResolveType("System.Int32")); - return mr; - }); - - Il2CppArrayBase_set_Item = new((param) => - { - var owner = ResolveType("Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppArrayBase"); - var giOwner = owner.MakeGenericInstanceType(param).ToTypeDefOrRef(); - var mr = ReferenceCreator.CreateInstanceMethodReference("set_Item", Module.Void(), - giOwner, ResolveType("System.Int32"), new GenericParameterSignature(Module, GenericParameterType.Type, 0)); - return mr; - }); - - IL2CPP_Il2CppObjectBaseToPtr = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("Il2CppObjectBaseToPtr", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("Il2CppInterop.Runtime.InteropTypes.Il2CppObjectBase")); - return mr; - }); - - IL2CPP_Il2CppObjectBaseToPtrNotNull = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("Il2CppObjectBaseToPtrNotNull", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), - ResolveType("Il2CppInterop.Runtime.InteropTypes.Il2CppObjectBase")); - return mr; - }); - - IL2CPP_Il2CppStringToManaged = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("Il2CppStringToManaged", ResolveType("System.String"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_ManagedStringToIl2Cpp = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("ManagedStringToIl2Cpp", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.String")); - return mr; - }); - - Il2CppObjectBase_Cast = new Lazy(() => - { - var gp0 = new GenericParameterSignature(GenericParameterType.Method, 0); - var signature = MethodSignature.CreateInstance(gp0, 1); - var mr = new MemberReference(ResolveType("Il2CppInterop.Runtime.InteropTypes.Il2CppObjectBase").ToTypeDefOrRef(), "Cast", signature); - return mr; - }); - - Il2CppObjectBase_TryCast = new Lazy(() => - { - var gp0 = new GenericParameterSignature(GenericParameterType.Method, 0); - var signature = MethodSignature.CreateInstance(gp0, 1); - var mr = new MemberReference(ResolveType("Il2CppInterop.Runtime.InteropTypes.Il2CppObjectBase").ToTypeDefOrRef(), "TryCast", signature); - return mr; - }); - - Il2CppObjectPool_Get = new Lazy(() => - { - var gp0 = new GenericParameterSignature(GenericParameterType.Method, 0); - var signature = MethodSignature.CreateStatic(gp0, 1, ResolveType("System.IntPtr")); - var mr = new MemberReference(ResolveType("Il2CppInterop.Runtime.Runtime.Il2CppObjectPool").ToTypeDefOrRef(), "Get", signature); - return mr; - }); - - IL2CPP_ResolveICall = new Lazy(() => - { - var gp0 = new GenericParameterSignature(GenericParameterType.Method, 0); - var signature = MethodSignature.CreateStatic(gp0, 1, ResolveType("System.String")); - var mr = new MemberReference(ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), "ResolveICall", signature); - return mr; - }); - - IL2CPP_il2cpp_gc_wbarrier_set_field = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_gc_wbarrier_set_field", ResolveType("System.Void"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr"), ResolveType("System.IntPtr"), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_FieldWriteWbarrierStub = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("FieldWriteWbarrierStub", ResolveType("System.Void"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr"), ResolveType("System.IntPtr"), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_il2cpp_field_get_offset = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_field_get_offset", ResolveType("System.UInt32"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_il2cpp_field_static_get_value = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_field_static_get_value", ResolveType("System.Void"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr"), ResolveType("System.Void*")); - return mr; - }); - - IL2CPP_il2cpp_field_static_set_value = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_field_static_set_value", ResolveType("System.Void"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr"), ResolveType("System.Void*")); - return mr; - }); - - IL2CPP_il2cpp_runtime_invoke = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_runtime_invoke", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr"), ResolveType("System.IntPtr"), ResolveType("System.Void**"), ResolveType("System.IntPtr&")); - return mr; - }); - - IL2CPP_il2cpp_runtime_class_init = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_runtime_class_init", ResolveType("System.Void"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_il2cpp_object_unbox = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_object_unbox", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_il2cpp_value_box = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_value_box", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr"), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_il2cpp_class_value_size = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_class_value_size", ResolveType("System.Int32"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr"), ResolveType("System.UInt32&")); - return mr; - }); - - IL2CPP_il2cpp_object_get_class = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_object_get_class", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_il2cpp_class_is_valuetype = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_class_is_valuetype", ResolveType("System.Boolean"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr")); - return mr; - }); - - Il2CppException_RaiseExceptionIfNecessary = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("RaiseExceptionIfNecessary", ResolveType("System.Void"), - ResolveType("Il2CppInterop.Runtime.Il2CppException").ToTypeDefOrRef(), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_il2cpp_object_get_virtual_method = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_object_get_virtual_method", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr"), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_GetIl2CppField = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("GetIl2CppField", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr"), ResolveType("System.String")); - return mr; - }); - - IL2CPP_GetIl2CppNestedType = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("GetIl2CppNestedType", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr"), ResolveType("System.String")); - return mr; - }); - - IL2CPP_GetIl2CppClass = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("GetIl2CppClass", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.String"), ResolveType("System.String"), ResolveType("System.String")); - return mr; - }); - - IL2CPP_GetIl2CppMethod = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("GetIl2CppMethod", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), - ResolveType("System.IntPtr"), ResolveType("System.Boolean"), ResolveType("System.String"), ResolveType("System.String"), ResolveType("System.String[]")); - return mr; - }); - - IL2CPP_GetIl2CppMethodByToken = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("GetIl2CppMethodByToken", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr"), ResolveType("System.Int32")); - return mr; - }); - - IL2CPP_il2cpp_class_get_type = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_class_get_type", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_il2cpp_class_from_type = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_class_from_type", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_il2cpp_object_new = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_object_new", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_il2cpp_method_get_from_reflection = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_method_get_from_reflection", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_il2cpp_method_get_object = new Lazy(() => - { - var mr = ReferenceCreator.CreateStaticMethodReference("il2cpp_method_get_object", ResolveType("System.IntPtr"), - ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), ResolveType("System.IntPtr"), ResolveType("System.IntPtr")); - return mr; - }); - - IL2CPP_PointerToValueGeneric = new Lazy(() => - { - var gp0 = new GenericParameterSignature(GenericParameterType.Method, 0); - var signature = MethodSignature.CreateStatic(gp0, 1, ResolveType("System.IntPtr"), ResolveType("System.Boolean"), ResolveType("System.Boolean")); - var mr = new MemberReference(ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), "PointerToValueGeneric", signature); - return mr; - }); - - IL2CPP_RenderTypeName = new Lazy(() => - { - var gp0 = new GenericParameterSignature(GenericParameterType.Method, 0); - var signature = MethodSignature.CreateStatic(ResolveType("System.String"), 1, ResolveType("System.Boolean")); - var mr = new MemberReference(ResolveType("Il2CppInterop.Runtime.IL2CPP").ToTypeDefOrRef(), "RenderTypeName", signature); - return mr; - }); - - OriginalNameAttributector = new Lazy(() => ReferenceCreator.CreateInstanceMethodReference(".ctor", - Module.Void(), - Module.DefaultImporter.ImportType(typeof(OriginalNameAttribute)), Module.String(), Module.String(), Module.String())); - - ObfuscatedNameAttributector = new Lazy(() => ReferenceCreator.CreateInstanceMethodReference(".ctor", - Module.Void(), - Module.DefaultImporter.ImportType(typeof(ObfuscatedNameAttribute)), Module.String())); - - CallerCountAttributector = new Lazy(() => - ReferenceCreator.CreateInstanceMethodReference(".ctor", Module.Void(), Module.DefaultImporter.ImportType(typeof(CallerCountAttribute)), Module.Int())); - - CachedScanResultsAttributector = new Lazy(() => - ReferenceCreator.CreateInstanceMethodReference(".ctor", Module.Void(), - Module.DefaultImporter.ImportType(typeof(CachedScanResultsAttribute)))); - - Il2CppSystemDelegateCombine = new Lazy(() => - Module.DefaultImporter.ImportMethod(globalCtx.GetAssemblyByName("mscorlib").NewAssembly.ManifestModule! - .GetType("Il2CppSystem.Delegate").Methods.Single(m => m.Name == "Combine" && m.Parameters.Count == 2))); - - Il2CppSystemDelegateRemove = new Lazy(() => - Module.DefaultImporter.ImportMethod(globalCtx.GetAssemblyByName("mscorlib").NewAssembly.ManifestModule! - .GetType("Il2CppSystem.Delegate").Methods.Single(m => m.Name == "Remove"))); - - Il2CppSystemRuntimeTypeHandleGetRuntimeTypeHandle = new Lazy(() => - { - var declaringTypeRef = RuntimeReflectionHelper; - var returnTypeRef = Module.DefaultImporter.ImportType(globalCtx.GetAssemblyByName("mscorlib").NewAssembly.ManifestModule! - .GetType("Il2CppSystem.RuntimeTypeHandle")); - var signature = MethodSignature.CreateStatic(returnTypeRef.ToTypeSignature(), 1); - var methodReference = new MemberReference(declaringTypeRef.ToTypeDefOrRef(), "GetRuntimeTypeHandle", signature); - return Module.DefaultImporter.ImportMethod(methodReference); - }); - } -} diff --git a/Il2CppInterop.Generator/Utils/TimingCookie.cs b/Il2CppInterop.Generator/Utils/TimingCookie.cs deleted file mode 100644 index 9eed28bc..00000000 --- a/Il2CppInterop.Generator/Utils/TimingCookie.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Diagnostics; -using Il2CppInterop.Common; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator.Utils; - -internal readonly struct TimingCookie : IDisposable -{ - private readonly Stopwatch myStopwatch; - - public TimingCookie(string message) - { - Logger.Instance.LogInformation("{Message}...", message); - myStopwatch = Stopwatch.StartNew(); - } - - public void Dispose() - { - Logger.Instance.LogInformation("Done in {Elapsed}", myStopwatch.Elapsed); - } -} diff --git a/Il2CppInterop.Generator/Utils/UniquificationContext.cs b/Il2CppInterop.Generator/Utils/UniquificationContext.cs deleted file mode 100644 index 662654ef..00000000 --- a/Il2CppInterop.Generator/Utils/UniquificationContext.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Il2CppInterop.Generator.Extensions; - -namespace Il2CppInterop.Generator.Utils; - -public class UniquificationContext -{ - private readonly GeneratorOptions myGeneratorOptions; - private readonly SortedSet<(string, float)> myPrefixes = new(new Item2Comparer()); - private readonly Dictionary myUniquifiersCount = new(); - - public UniquificationContext(GeneratorOptions generatorOptions) - { - myGeneratorOptions = generatorOptions; - } - - public bool CheckFull() - { - return myUniquifiersCount.Count >= myGeneratorOptions.TypeDeobfuscationMaxUniquifiers; - } - - public void Push(string str, bool noSubstring = false) - { - if (str.IsInvalidInSource()) return; - - var stringPrefix = noSubstring - ? str - : SubstringBounded(str, 0, myGeneratorOptions.TypeDeobfuscationCharsPerUniquifier); - var currentCount = myUniquifiersCount[stringPrefix] = myUniquifiersCount.GetOrCreate(stringPrefix, _ => 0) + 1; - myPrefixes.Add((stringPrefix, myUniquifiersCount.Count + currentCount * 2 + myPrefixes.Count / 100f)); - } - - public void Push(List strings, bool noSubstring = false) - { - foreach (var str in strings) - Push(str, noSubstring); - } - - public string GetTop() - { - return string.Join("", - myPrefixes.Take(myGeneratorOptions.TypeDeobfuscationMaxUniquifiers).Select(it => it.Item1)); - } - - private static string SubstringBounded(string str, int startIndex, int length) - { - length = Math.Min(length, str.Length); - return str.Substring(startIndex, length); - } - - private class Item2Comparer : IComparer<(string, float)> - { - public int Compare((string, float) x, (string, float) y) - { - return x.Item2.CompareTo(y.Item2); - } - } -} diff --git a/Il2CppInterop.Generator/Utils/UnstripGenerator.cs b/Il2CppInterop.Generator/Utils/UnstripGenerator.cs deleted file mode 100644 index 2b7fb54d..00000000 --- a/Il2CppInterop.Generator/Utils/UnstripGenerator.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System.Text; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Code.Cil; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Common; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Generator.Utils; - -public static class UnstripGenerator -{ - public static TypeDefinition CreateDelegateTypeForICallMethod(MethodDefinition unityMethod, - MethodDefinition convertedMethod, RuntimeAssemblyReferences imports) - { - var delegateType = new TypeDefinition("", unityMethod.Name + "Delegate", - TypeAttributes.Sealed | TypeAttributes.NestedPrivate, imports.Module.MulticastDelegate().ToTypeDefOrRef()); - - var constructor = new MethodDefinition(".ctor", - MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RuntimeSpecialName | - MethodAttributes.Public, MethodSignature.CreateInstance(imports.Module.Void(), imports.Module.Object(), imports.Module.IntPtr())); - constructor.ImplAttributes = MethodImplAttributes.CodeTypeMask; - delegateType.Methods.Add(constructor); - - var invokeMethod = new MethodDefinition("Invoke", - MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Public, - MethodSignature.CreateInstance(imports.Module.Void())); // todo - invokeMethod.ImplAttributes = MethodImplAttributes.CodeTypeMask; - delegateType.Methods.Add(invokeMethod); - - invokeMethod.Signature!.ReturnType = convertedMethod.Signature!.ReturnType.IsValueType() - ? convertedMethod.Signature.ReturnType - : imports.Module.IntPtr(); - if (!convertedMethod.IsStatic) - invokeMethod.AddParameter(imports.Module.IntPtr(), "@this"); - foreach (var convertedParameter in convertedMethod.Parameters) - invokeMethod.AddParameter( - convertedParameter.ParameterType.IsValueType() - ? convertedParameter.ParameterType - : imports.Module.IntPtr(), - convertedParameter.Name, - convertedParameter.Definition!.Attributes & ~ParameterAttributes.Optional); - - return delegateType; - } - - public static void GenerateInvokerMethodBody(MethodDefinition newMethod, FieldDefinition delegateField, - TypeDefinition delegateType, TypeRewriteContext enclosingType, RuntimeAssemblyReferences imports) - { - var body = newMethod.CilMethodBody!.Instructions; - - body.Add(OpCodes.Ldsfld, delegateField); - if (!newMethod.IsStatic) - { - body.Add(OpCodes.Ldarg_0); - body.Add(OpCodes.Call, imports.IL2CPP_Il2CppObjectBaseToPtrNotNull.Value); - } - - var argOffset = newMethod.IsStatic ? 0 : 1; - - for (var i = 0; i < newMethod.Parameters.Count; i++) - { - var param = newMethod.Parameters[i]; - var paramType = param.ParameterType; - if (paramType.IsValueType() || (paramType is ByReferenceTypeSignature && paramType.GetElementType().IsValueType())) - { - body.AddLoadArgument(i + argOffset); - } - else - { - body.EmitObjectToPointer(param.ParameterType, param.ParameterType, enclosingType, i + argOffset, false, - true, true, true, out var refVar); - if (refVar != null) - { - Logger.Instance.LogTrace("Method {NewMethod} has a reference-typed ref parameter, this will be ignored", - newMethod.ToString()); - } - } - } - - body.Add(OpCodes.Call, delegateType.Methods.Single(it => it.Name == "Invoke")); - if (!newMethod.Signature!.ReturnType.IsValueTypeLike()) - { - var pointerVar = new CilLocalVariable(imports.Module.IntPtr()); - newMethod.CilMethodBody.LocalVariables.Add(pointerVar); - body.Add(OpCodes.Stloc, pointerVar); - body.EmitPointerToObject(newMethod.Signature.ReturnType, newMethod.Signature.ReturnType, enclosingType, pointerVar, false, - false); - } - - body.Add(OpCodes.Ret); - } - - public static FieldDefinition GenerateStaticCtorSuffix(TypeDefinition enclosingType, TypeDefinition delegateType, - MethodDefinition unityMethod, RuntimeAssemblyReferences imports) - { - var delegateField = new FieldDefinition(delegateType.Name + "Field", - FieldAttributes.Static | FieldAttributes.Private | FieldAttributes.InitOnly, new FieldSignature(delegateType.ToTypeSignature())); - enclosingType.Fields.Add(delegateField); - - var staticCtor = enclosingType.GetOrCreateStaticConstructor(); - - var bodyProcessor = staticCtor.CilMethodBody!.Instructions; - - bodyProcessor.Remove(staticCtor.CilMethodBody.Instructions.Last()); // remove ret - - bodyProcessor.Add(OpCodes.Ldstr, GetICallSignature(unityMethod)); - - var methodRef = imports.IL2CPP_ResolveICall.Value.MakeGenericInstanceMethod(delegateType.ToTypeSignature()); - bodyProcessor.Add(OpCodes.Call, enclosingType.DeclaringModule!.DefaultImporter.ImportMethod(methodRef)); - bodyProcessor.Add(OpCodes.Stsfld, delegateField); - - bodyProcessor.Add(OpCodes.Ret); // restore ret - - return delegateField; - } - - private static string GetICallSignature(MethodDefinition unityMethod) - { - var builder = new StringBuilder(); - builder.Append(unityMethod.DeclaringType!.FullName); - builder.Append("::"); - builder.Append(unityMethod.Name); - - return builder.ToString(); - } -} diff --git a/Il2CppInterop.Generator/Utils/UnstripTranslator.cs b/Il2CppInterop.Generator/Utils/UnstripTranslator.cs deleted file mode 100644 index a65934b5..00000000 --- a/Il2CppInterop.Generator/Utils/UnstripTranslator.cs +++ /dev/null @@ -1,500 +0,0 @@ -using System.Diagnostics; -using AsmResolver; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Code.Cil; -using AsmResolver.DotNet.Collections; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Cil; -using AsmResolver.PE.DotNet.Metadata.Tables; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Passes; - -namespace Il2CppInterop.Generator.Utils; - -public static class UnstripTranslator -{ - public static bool TranslateMethod(MethodDefinition original, MethodDefinition target, - TypeRewriteContext typeRewriteContext, RuntimeAssemblyReferences imports) - { - if (original.CilMethodBody is null) - return true; - - target.CilMethodBody = new(); - - var globalContext = typeRewriteContext.AssemblyContext.GlobalContext; - Dictionary localVariableMap = new(); - foreach (var variableDefinition in original.CilMethodBody.LocalVariables) - { - var variableType = - Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, variableDefinition.VariableType, - imports); - if (variableType == null) - return false; - var newVariableDefinition = new CilLocalVariable(variableType); - target.CilMethodBody.LocalVariables.Add(newVariableDefinition); - localVariableMap.Add(variableDefinition, newVariableDefinition); - } - - // We expand macros because our instructions are not mapped one-to-one, - // so specialized instructions like Br_S need to be expanded to Br for safety. - // In pass 90, we optimize all macros, so we won't need to worry about that here. - original.CilMethodBody.Instructions.ExpandMacros(); - - List> labelMap = new(); - Dictionary instructionMap = new(); - - var targetBuilder = target.CilMethodBody.Instructions; - foreach (var bodyInstruction in original.CilMethodBody.Instructions) - { - if (bodyInstruction.Operand is null) - { - CilInstruction newInstruction; - switch (bodyInstruction.OpCode.Code) - { - case CilCode.Ldlen: - //This is Il2CppArrayBase.Length - newInstruction = targetBuilder.Add(OpCodes.Callvirt, - imports.Module.DefaultImporter.ImportMethod(imports.Il2CppArrayBase_get_Length.Value)); - break; - - case CilCode.Ldelem_Ref: - //This is Il2CppReferenceArray.get_Item but the T is not known because the operand is null. - return false; - - case CilCode.Stelem_Ref: - //This is Il2CppReferenceArray.set_Item but the T is not known because the operand is null. - return false; - - case CilCode.Ldelem_I1: - case CilCode.Ldelem_I2: - case CilCode.Ldelem_I4: - case CilCode.Ldelem_U4: - //This is Il2CppArrayBase.get_Item but the T could be either the cooresponding primitive or an enum. - return false; - - case CilCode.Ldelem_U1: - //This is Il2CppArrayBase.get_Item but the T could be either byte, bool, or an enum. - return false; - - case CilCode.Ldelem_U2: - //This is Il2CppArrayBase.get_Item but the T could be either ushort, char, or an enum. - return false; - - case CilCode.Ldelem_I8: - //This is Il2CppArrayBase.get_Item but the T could be either signed, unsigned, or an enum. - return false; - - case CilCode.Ldelem_I: - //This is Il2CppArrayBase.get_Item but the T could be either signed, unsigned, or a pointer. - return false; - - case CilCode.Ldelem_R4: - { - var getMethod = imports.Il2CppArrayBase_get_Item.Get(imports.Module.CorLibTypeFactory.Single); - newInstruction = targetBuilder.Add(OpCodes.Callvirt, imports.Module.DefaultImporter.ImportMethod(getMethod)); - } - break; - - case CilCode.Ldelem_R8: - { - var getMethod = imports.Il2CppArrayBase_get_Item.Get(imports.Module.CorLibTypeFactory.Double); - newInstruction = targetBuilder.Add(OpCodes.Callvirt, imports.Module.DefaultImporter.ImportMethod(getMethod)); - } - break; - - case >= CilCode.Stelem_I and <= CilCode.Stelem_I8: - //This is Il2CppStructArray.set_Item - return false; - - case CilCode.Stelem_R4: - { - var setMethod = imports.Il2CppArrayBase_set_Item.Get(imports.Module.CorLibTypeFactory.Single); - newInstruction = targetBuilder.Add(OpCodes.Callvirt, imports.Module.DefaultImporter.ImportMethod(setMethod)); - } - break; - - case CilCode.Stelem_R8: - { - var setMethod = imports.Il2CppArrayBase_set_Item.Get(imports.Module.CorLibTypeFactory.Double); - newInstruction = targetBuilder.Add(OpCodes.Callvirt, imports.Module.DefaultImporter.ImportMethod(setMethod)); - } - break; - - case >= CilCode.Ldind_I1 and <= CilCode.Ldind_Ref: - //This is for by ref parameters - goto default; - - case >= CilCode.Stind_Ref and <= CilCode.Stind_R8: - //This is for by ref parameters - goto default; - - default: - //Noop, ldnull, ldarg_0, mul, add, etc. - newInstruction = targetBuilder.Add(bodyInstruction.OpCode); - break; - } - - instructionMap.Add(bodyInstruction, newInstruction); - } - else if (bodyInstruction.OpCode.OperandType == CilOperandType.InlineField) - { - // This code doesn't handle fields in the corlib types well. - // Static fields are fine, but references to instance fields can't be redirected. - - var fieldArg = (IFieldDescriptor)bodyInstruction.Operand; - var useSystemCorlibType = fieldArg.Signature?.HasThis ?? true; - var fieldDeclarer = - Pass80UnstripMethods.ResolveTypeInNewAssembliesRaw(globalContext, fieldArg.DeclaringType!.ToTypeSignature(), imports, useSystemCorlibType); - if (fieldDeclarer == null) - return false; - var fieldDeclarerDefinition = fieldDeclarer.Resolve(); - if (fieldDeclarerDefinition == null) - return false; - - var fieldDeclarerContext = globalContext.GetContextForNewType(fieldDeclarerDefinition); - var propertyName = fieldDeclarerContext.Fields.SingleOrDefault(it => it.OriginalField.Name == fieldArg.Name)?.UnmangledName; - - var newField = fieldDeclarerDefinition.Fields.SingleOrDefault(it => it.Name == fieldArg.Name) - ?? fieldDeclarerDefinition.Fields.SingleOrDefault(it => it.Name == propertyName); - if (newField != null) - { - var newInstruction = targetBuilder.Add(bodyInstruction.OpCode, imports.Module.DefaultImporter.ImportField(newField)); - instructionMap.Add(bodyInstruction, newInstruction); - } - else - { - if (propertyName == null) - { - return false; - } - else if (bodyInstruction.OpCode == OpCodes.Ldfld || bodyInstruction.OpCode == OpCodes.Ldsfld) - { - var getterMethod = fieldDeclarerDefinition.Properties - .SingleOrDefault(it => it.Name == propertyName)?.GetMethod; - if (getterMethod == null) - return false; - - var newInstruction = targetBuilder.Add(OpCodes.Call, imports.Module.DefaultImporter.ImportMethod(getterMethod)); - instructionMap.Add(bodyInstruction, newInstruction); - } - else if (bodyInstruction.OpCode == OpCodes.Stfld || bodyInstruction.OpCode == OpCodes.Stsfld) - { - var setterMethod = fieldDeclarerDefinition.Properties - .SingleOrDefault(it => it.Name == propertyName)?.SetMethod; - if (setterMethod == null) - return false; - - var newInstruction = targetBuilder.Add(OpCodes.Call, imports.Module.DefaultImporter.ImportMethod(setterMethod)); - instructionMap.Add(bodyInstruction, newInstruction); - } - else - { - //Ldflda, Ldsflda - return false; - } - } - } - else if (bodyInstruction.OpCode.OperandType == CilOperandType.InlineMethod) - { - // This code doesn't handle methods in the corlib types well. - // Static methods are fine, but references to instance methods can't be redirected. - - var methodArg = (IMethodDescriptor)bodyInstruction.Operand; - var useSystemCorlibType = methodArg.Signature?.HasThis ?? true; - var methodDeclarer = - Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, methodArg.DeclaringType?.ToTypeSignature(), imports, useSystemCorlibType); - if (methodDeclarer == null) - return false; - - var newReturnType = - Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, methodArg.Signature?.ReturnType, imports); - if (newReturnType == null) - return false; - - var newMethodSignature = methodArg.Signature!.HasThis - ? MethodSignature.CreateInstance(newReturnType, methodArg.Signature.GenericParameterCount) - : MethodSignature.CreateStatic(newReturnType, methodArg.Signature.GenericParameterCount); - foreach (var methodArgParameter in methodArg.Signature.ParameterTypes) - { - var newParamType = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, - methodArgParameter, imports); - if (newParamType == null) - return false; - - newMethodSignature.ParameterTypes.Add(newParamType); - } - - var memberReference = new MemberReference(methodDeclarer.ToTypeDefOrRef(), methodArg.Name, newMethodSignature); - - IMethodDescriptor newMethod; - if (methodArg is MethodSpecification genericMethod) - { - if (genericMethod.Signature is null) - return false; - - TypeSignature[] typeArguments = new TypeSignature[genericMethod.Signature.TypeArguments.Count]; - for (var i = 0; i < genericMethod.Signature.TypeArguments.Count; i++) - { - var newTypeArgument = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, genericMethod.Signature.TypeArguments[i], imports); - if (newTypeArgument == null) - return false; - - typeArguments[i] = newTypeArgument; - } - - newMethod = memberReference.MakeGenericInstanceMethod(typeArguments); - } - else - { - newMethod = memberReference; - } - - var newInstruction = targetBuilder.Add(bodyInstruction.OpCode, imports.Module.DefaultImporter.ImportMethod(newMethod)); - instructionMap.Add(bodyInstruction, newInstruction); - } - else if (bodyInstruction.OpCode.OperandType == CilOperandType.InlineType) - { - var targetType = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, ((ITypeDefOrRef)bodyInstruction.Operand).ToTypeSignature(), imports); - if (targetType == null) - return false; - - if ((bodyInstruction.OpCode == OpCodes.Castclass && !targetType.IsValueType) || - (bodyInstruction.OpCode == OpCodes.Unbox_Any && targetType is GenericParameterSignature)) - { - // Compilers use unbox.any for casting to generic parameter types. - // Castclass is only used for reference types. - // Both can be translated to Il2CppObjectBase.Cast(). - var newInstruction = targetBuilder.Add(OpCodes.Call, - imports.Module.DefaultImporter.ImportMethod(imports.Il2CppObjectBase_Cast.Value.MakeGenericInstanceMethod(targetType))); - instructionMap.Add(bodyInstruction, newInstruction); - } - else if (bodyInstruction.OpCode == OpCodes.Isinst && !targetType.IsValueType) - { - var newInstruction = targetBuilder.Add(OpCodes.Call, - imports.Module.DefaultImporter.ImportMethod(imports.Il2CppObjectBase_TryCast.Value.MakeGenericInstanceMethod(targetType))); - instructionMap.Add(bodyInstruction, newInstruction); - } - else if (bodyInstruction.OpCode == OpCodes.Newarr) - { - var newInstruction = targetBuilder.Add(OpCodes.Conv_I8); - - ITypeDefOrRef il2cppTypeArray; - if (targetType.IsValueType) - { - return false; - } - else if (targetType.FullName == "System.String") - { - il2cppTypeArray = imports.Il2CppStringArray.ToTypeDefOrRef(); - } - else - { - il2cppTypeArray = imports.Il2CppReferenceArray.MakeGenericInstanceType(targetType).ToTypeDefOrRef(); - } - targetBuilder.Add(OpCodes.Newobj, imports.Module.DefaultImporter.ImportMethod( - ReferenceCreator.CreateInstanceMethodReference(".ctor", imports.Module.Void(), il2cppTypeArray, imports.Module.Long()))); - instructionMap.Add(bodyInstruction, newInstruction); - } - else if (bodyInstruction.OpCode == OpCodes.Ldelema) - { - // Not implemented - return false; - } - else if (bodyInstruction.OpCode == OpCodes.Ldelem) - { - var getMethod = imports.Il2CppArrayBase_get_Item.Get(targetType); - var newInstruction = targetBuilder.Add(OpCodes.Callvirt, imports.Module.DefaultImporter.ImportMethod(getMethod)); - instructionMap.Add(bodyInstruction, newInstruction); - } - else if (bodyInstruction.OpCode == OpCodes.Stelem) - { - var setMethod = imports.Il2CppArrayBase_set_Item.Get(targetType); - var newInstruction = targetBuilder.Add(OpCodes.Callvirt, imports.Module.DefaultImporter.ImportMethod(setMethod)); - instructionMap.Add(bodyInstruction, newInstruction); - } - else - { - var newInstruction = targetBuilder.Add(bodyInstruction.OpCode, targetType.ToTypeDefOrRef()); - instructionMap.Add(bodyInstruction, newInstruction); - } - } - else if (bodyInstruction.OpCode.OperandType == CilOperandType.InlineSig) - { - // todo: rewrite sig if this ever happens in unity types - return false; - } - else if (bodyInstruction.OpCode.OperandType == CilOperandType.InlineTok) - { - Debug.Assert(bodyInstruction.OpCode.Code is CilCode.Ldtoken); - switch (bodyInstruction.Operand) - { - case ITypeDefOrRef typeDefOrRef: - { - var targetTok = Pass80UnstripMethods.ResolveTypeInNewAssemblies(globalContext, typeDefOrRef.ToTypeSignature(), imports); - if (targetTok == null) - return false; - - var newInstruction = targetBuilder.Add(OpCodes.Call, - imports.Module.DefaultImporter.ImportMethod(imports.Il2CppSystemRuntimeTypeHandleGetRuntimeTypeHandle.Value.MakeGenericInstanceMethod(targetTok))); - instructionMap.Add(bodyInstruction, newInstruction); - } - break; - default: - // Ldtoken is also used for members, which is not implemented. - return false; - } - } - else if (bodyInstruction.OpCode.OperandType is CilOperandType.InlineSwitch && bodyInstruction.Operand is IReadOnlyList labels) - { - List newLabels = new(labels.Count); - for (var i = 0; i < labels.Count; i++) - { - if (labels[i] is CilInstructionLabel oldLabel) - { - var newLabel = new CilInstructionLabel(); - labelMap.Add(new(oldLabel, newLabel)); - newLabels.Add(newLabel); - } - else - { - return false; - } - } - var newInstruction = targetBuilder.Add(bodyInstruction.OpCode, newLabels); - instructionMap.Add(bodyInstruction, newInstruction); - } - else if (bodyInstruction.Operand is string or Utf8String - || bodyInstruction.Operand.GetType().IsPrimitive) - { - var newInstruction = new CilInstruction(bodyInstruction.OpCode, bodyInstruction.Operand); - targetBuilder.Add(newInstruction); - instructionMap.Add(bodyInstruction, newInstruction); - } - else if (bodyInstruction.Operand is Parameter parameter) - { - var newInstruction = targetBuilder.Add(bodyInstruction.OpCode, target.Parameters.GetBySignatureIndex(parameter.MethodSignatureIndex)); - instructionMap.Add(bodyInstruction, newInstruction); - } - else if (bodyInstruction.Operand is CilLocalVariable localVariable) - { - var newInstruction = targetBuilder.Add(bodyInstruction.OpCode, localVariableMap[localVariable]); - instructionMap.Add(bodyInstruction, newInstruction); - } - else if (bodyInstruction.Operand is CilInstructionLabel label) - { - var newLabel = new CilInstructionLabel(); - labelMap.Add(new(label, newLabel)); - var newInstruction = targetBuilder.Add(bodyInstruction.OpCode, newLabel); - instructionMap.Add(bodyInstruction, newInstruction); - } - else - { - return false; - } - } - - foreach ((var oldLabel, var newLabel) in labelMap) - { - newLabel.Instruction = instructionMap[oldLabel.Instruction!]; - } - - // Copy exception handlers - foreach (var exceptionHandler in original.CilMethodBody.ExceptionHandlers) - { - var newExceptionHandler = new CilExceptionHandler - { - HandlerType = exceptionHandler.HandlerType - }; - - switch (exceptionHandler.TryStart) - { - case null: - break; - case CilInstructionLabel { Instruction: not null } tryStart: - newExceptionHandler.TryStart = new CilInstructionLabel(instructionMap[tryStart.Instruction]); - break; - default: - return false; - } - - switch (exceptionHandler.TryEnd) - { - case null: - break; - case CilInstructionLabel { Instruction: not null } tryEnd: - newExceptionHandler.TryEnd = new CilInstructionLabel(instructionMap[tryEnd.Instruction]); - break; - default: - return false; - } - - switch (exceptionHandler.HandlerStart) - { - case null: - break; - case CilInstructionLabel { Instruction: not null } handlerStart: - newExceptionHandler.HandlerStart = new CilInstructionLabel(instructionMap[handlerStart.Instruction]); - break; - default: - return false; - } - - switch (exceptionHandler.HandlerEnd) - { - case null: - break; - case CilInstructionLabel { Instruction: not null } handlerEnd: - newExceptionHandler.HandlerEnd = new CilInstructionLabel(instructionMap[handlerEnd.Instruction]); - break; - default: - return false; - } - - switch (exceptionHandler.FilterStart) - { - case null: - break; - case CilInstructionLabel { Instruction: not null } filterStart: - newExceptionHandler.FilterStart = new CilInstructionLabel(instructionMap[filterStart.Instruction]); - break; - default: - return false; - } - - switch (exceptionHandler.ExceptionType?.ToTypeSignature()) - { - case null: - break; - case CorLibTypeSignature { ElementType: ElementType.Object }: - newExceptionHandler.ExceptionType = imports.Module.CorLibTypeFactory.Object.ToTypeDefOrRef(); - break; - default: - // In the future, we will throw exact exceptions, but we don't right now, - // so attempting to catch a specific exception type will always fail. - return false; - } - - target.CilMethodBody.ExceptionHandlers.Add(newExceptionHandler); - } - - return true; - } - - public static void ReplaceBodyWithException(MethodDefinition newMethod, RuntimeAssemblyReferences imports) - { - newMethod.CilMethodBody = new(); - var processor = newMethod.CilMethodBody.Instructions; - - processor.Add(OpCodes.Ldstr, "Method unstripping failed"); - processor.Add(OpCodes.Newobj, imports.Module.NotSupportedExceptionCtor()); - processor.Add(OpCodes.Throw); - processor.Add(OpCodes.Ret); - } - - //Required for deconstruction on net472 - private static void Deconstruct(this KeyValuePair pair, out CilInstructionLabel key, out CilInstructionLabel value) - { - key = pair.Key; - value = pair.Value; - } -} diff --git a/Il2CppInterop.Generator/Utils/XrefScanMetadataGenerationUtil.cs b/Il2CppInterop.Generator/Utils/XrefScanMetadataGenerationUtil.cs deleted file mode 100644 index bb0355dc..00000000 --- a/Il2CppInterop.Generator/Utils/XrefScanMetadataGenerationUtil.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Il2CppInterop.Common.XrefScans; -using Il2CppInterop.Generator.Contexts; -using Il2CppInterop.Generator.Extensions; - -namespace Il2CppInterop.Generator.Utils; - -internal static class XrefScanMetadataGenerationUtil -{ - internal static long MetadataInitForMethodRva; - internal static IntPtr MetadataInitForMethodFileOffset; - - private static readonly (string Assembly, string Type, string Method)[] MetadataInitCandidates = - { - ("UnityEngine.CoreModule", "UnityEngine.Object", ".cctor"), - ("mscorlib", "System.Exception", "get_Message"), - ("mscorlib", "System.IntPtr", "Equals") - }; - - private static void FindMetadataInitForMethod(RewriteGlobalContext context, long gameAssemblyBase) - { - foreach (var metadataInitCandidate in MetadataInitCandidates) - { - var assembly = - context.Assemblies.FirstOrDefault(it => - it.OriginalAssembly.Name == metadataInitCandidate.Assembly); - var unityObjectCctor = assembly?.TryGetTypeByName(metadataInitCandidate.Type)?.OriginalType.Methods - .FirstOrDefault(it => it.Name == metadataInitCandidate.Method); - - if (unityObjectCctor == null) continue; - - MetadataInitForMethodFileOffset = - (IntPtr)(long)XrefScannerLowLevel - .JumpTargets((IntPtr)(gameAssemblyBase + unityObjectCctor.ExtractOffset())).First(); - MetadataInitForMethodRva = (long)MetadataInitForMethodFileOffset - gameAssemblyBase - - unityObjectCctor.ExtractOffset() + unityObjectCctor.ExtractRva(); - - return; - } - - throw new ApplicationException("Unable to find a method with metadata init reference"); - } - - internal static (long FlagRva, long TokenRva) FindMetadataInitForMethod(MethodRewriteContext method, - long gameAssemblyBase) - { - if (MetadataInitForMethodRva == 0) - FindMetadataInitForMethod(method.DeclaringType.AssemblyContext.GlobalContext, gameAssemblyBase); - - var codeStart = (IntPtr)(gameAssemblyBase + method.FileOffset); - var firstCall = XrefScannerLowLevel.JumpTargets(codeStart).FirstOrDefault(); - if (firstCall != MetadataInitForMethodFileOffset || firstCall == IntPtr.Zero) return (0, 0); - - var tokenPointer = - XrefScanUtilFinder.FindLastRcxReadAddressBeforeCallTo(codeStart, MetadataInitForMethodFileOffset); - var initFlagPointer = - XrefScanUtilFinder.FindByteWriteTargetRightAfterCallTo(codeStart, MetadataInitForMethodFileOffset); - - if (tokenPointer == IntPtr.Zero || initFlagPointer == IntPtr.Zero) return (0, 0); - - return ((long)initFlagPointer - gameAssemblyBase - method.FileOffset + method.Rva, - (long)tokenPointer - gameAssemblyBase - method.FileOffset + method.Rva); - } -} diff --git a/Il2CppInterop.Generator/Visitors/BooleanAndTypeVisitor.cs b/Il2CppInterop.Generator/Visitors/BooleanAndTypeVisitor.cs new file mode 100644 index 00000000..4adb4c09 --- /dev/null +++ b/Il2CppInterop.Generator/Visitors/BooleanAndTypeVisitor.cs @@ -0,0 +1,30 @@ +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.Visitors; + +public abstract class BooleanAndTypeVisitor : TypeVisitor +{ + public override bool Visit(ArrayTypeAnalysisContext type) => Visit(type.ElementType); + public override bool Visit(BoxedTypeAnalysisContext type) => Visit(type.ElementType); + public override bool Visit(ByRefTypeAnalysisContext type) => Visit(type.ElementType); + public override bool Visit(CustomModifierTypeAnalysisContext type) => Visit(type.ElementType) && Visit(type.ModifierType); + public override bool Visit(GenericInstanceTypeAnalysisContext type) + { + if (!Visit(type.GenericType)) + return false; + + foreach (var genericArgument in type.GenericArguments) + { + if (!Visit(genericArgument)) + return false; + } + + return true; + } + public override bool Visit(GenericParameterTypeAnalysisContext type) => true; + public override bool Visit(PinnedTypeAnalysisContext type) => Visit(type.ElementType); + public override bool Visit(PointerTypeAnalysisContext type) => Visit(type.ElementType); + public override bool Visit(SentinelTypeAnalysisContext type) => true; + public override bool Visit(SzArrayTypeAnalysisContext type) => Visit(type.ElementType); + protected override bool VisitSimpleType(TypeAnalysisContext type) => true; +} diff --git a/Il2CppInterop.Generator/Visitors/BooleanOrTypeVisitor.cs b/Il2CppInterop.Generator/Visitors/BooleanOrTypeVisitor.cs new file mode 100644 index 00000000..c8b303b1 --- /dev/null +++ b/Il2CppInterop.Generator/Visitors/BooleanOrTypeVisitor.cs @@ -0,0 +1,30 @@ +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.Visitors; + +public abstract class BooleanOrTypeVisitor : TypeVisitor +{ + public override bool Visit(ArrayTypeAnalysisContext type) => Visit(type.ElementType); + public override bool Visit(BoxedTypeAnalysisContext type) => Visit(type.ElementType); + public override bool Visit(ByRefTypeAnalysisContext type) => Visit(type.ElementType); + public override bool Visit(CustomModifierTypeAnalysisContext type) => Visit(type.ElementType) || Visit(type.ModifierType); + public override bool Visit(GenericInstanceTypeAnalysisContext type) + { + if (Visit(type.GenericType)) + return true; + + foreach (var genericArgument in type.GenericArguments) + { + if (Visit(genericArgument)) + return true; + } + + return false; + } + public override bool Visit(GenericParameterTypeAnalysisContext type) => false; + public override bool Visit(PinnedTypeAnalysisContext type) => Visit(type.ElementType); + public override bool Visit(PointerTypeAnalysisContext type) => Visit(type.ElementType); + public override bool Visit(SentinelTypeAnalysisContext type) => false; + public override bool Visit(SzArrayTypeAnalysisContext type) => Visit(type.ElementType); + protected override bool VisitSimpleType(TypeAnalysisContext type) => false; +} diff --git a/Il2CppInterop.Generator/Visitors/DefaultTypeVisitor.cs b/Il2CppInterop.Generator/Visitors/DefaultTypeVisitor.cs new file mode 100644 index 00000000..b993da71 --- /dev/null +++ b/Il2CppInterop.Generator/Visitors/DefaultTypeVisitor.cs @@ -0,0 +1,68 @@ +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.Visitors; + +public abstract class DefaultTypeVisitor : TypeVisitor +{ + public override T Visit(ArrayTypeAnalysisContext type) => CombineResults(type, Visit(type.ElementType)); + public override T Visit(BoxedTypeAnalysisContext type) => CombineResults(type, Visit(type.ElementType)); + public override T Visit(ByRefTypeAnalysisContext type) => CombineResults(type, Visit(type.ElementType)); + public override T Visit(CustomModifierTypeAnalysisContext type) => CombineResults(type, Visit(type.ElementType), Visit(type.ModifierType)); + public override T Visit(GenericInstanceTypeAnalysisContext type) + { + var genericTypeResult = Visit(type.GenericType); + var genericArgumentsResults = new T[type.GenericArguments.Count]; + for (var i = 0; i < type.GenericArguments.Count; i++) + { + genericArgumentsResults[i] = Visit(type.GenericArguments[i]); + } + return CombineResults(type, genericTypeResult, genericArgumentsResults); + } + /// + /// If not overridden, this will return . + /// + public override T Visit(GenericParameterTypeAnalysisContext type) => default!; + public override T Visit(PinnedTypeAnalysisContext type) => CombineResults(type, Visit(type.ElementType)); + public override T Visit(PointerTypeAnalysisContext type) => CombineResults(type, Visit(type.ElementType)); + /// + /// If not overridden, this will return . + /// + public override T Visit(SentinelTypeAnalysisContext type) => default!; + public override T Visit(SzArrayTypeAnalysisContext type) => CombineResults(type, Visit(type.ElementType)); + /// + /// If not overridden, this will return . + /// + protected override T VisitSimpleType(TypeAnalysisContext type) => default!; + /// + /// If not overridden, this will return the . + /// + protected virtual T CombineResults(ArrayTypeAnalysisContext type, T elementResult) => elementResult; + /// + /// If not overridden, this will return the . + /// + protected virtual T CombineResults(BoxedTypeAnalysisContext type, T elementResult) => elementResult; + /// + /// If not overridden, this will return the . + /// + protected virtual T CombineResults(ByRefTypeAnalysisContext type, T elementResult) => elementResult; + /// + /// If not overridden, this will return the . + /// + protected virtual T CombineResults(CustomModifierTypeAnalysisContext type, T elementResult, T modifierResult) => elementResult; + /// + /// If not overridden, this will return the . + /// + protected virtual T CombineResults(GenericInstanceTypeAnalysisContext type, T genericTypeResult, T[] genericArgumentsResults) => genericTypeResult; + /// + /// If not overridden, this will return the . + /// + protected virtual T CombineResults(PinnedTypeAnalysisContext type, T elementResult) => elementResult; + /// + /// If not overridden, this will return the . + /// + protected virtual T CombineResults(PointerTypeAnalysisContext type, T elementResult) => elementResult; + /// + /// If not overridden, this will return the . + /// + protected virtual T CombineResults(SzArrayTypeAnalysisContext type, T elementResult) => elementResult; +} diff --git a/Il2CppInterop.Generator/Visitors/TypeConversionVisitor.cs b/Il2CppInterop.Generator/Visitors/TypeConversionVisitor.cs new file mode 100644 index 00000000..905779ac --- /dev/null +++ b/Il2CppInterop.Generator/Visitors/TypeConversionVisitor.cs @@ -0,0 +1,76 @@ +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator; +using Il2CppInterop.Runtime.InteropTypes; +using Il2CppInterop.Runtime.InteropTypes.Arrays; + +namespace Il2CppInterop.Generator.Visitors; + +internal sealed class TypeConversionVisitor : TypeReplacementVisitor +{ + private TypeConversionVisitor(Dictionary replacements) : base(replacements) + { + } + + public required TypeAnalysisContext Pointer { get; init; } + public required TypeAnalysisContext ByRef { get; init; } + public required TypeAnalysisContext ArrayRank1 { get; init; } + public required TypeAnalysisContext ArrayRank2 { get; init; } + public required TypeAnalysisContext ArrayRank3 { get; init; } + + public static TypeConversionVisitor Create(ApplicationAnalysisContext appContext) + { + var il2CppMscorlib = appContext.AssembliesByName["Il2Cppmscorlib"]; + var mscorlib = appContext.AssembliesByName["mscorlib"]; + var il2CppInteropRuntime = appContext.AssembliesByName["Il2CppInterop.Runtime"]; + + var pointer = il2CppInteropRuntime.GetTypeByFullNameOrThrow(typeof(Pointer<>)); + var byRef = il2CppInteropRuntime.GetTypeByFullNameOrThrow(typeof(ByReference<>)); + var arrayRank1 = il2CppInteropRuntime.GetTypeByFullNameOrThrow(typeof(Il2CppArrayRank1<>)); + var arrayRank2 = il2CppInteropRuntime.GetTypeByFullNameOrThrow(typeof(Il2CppArrayRank2<>)); + var arrayRank3 = il2CppInteropRuntime.GetTypeByFullNameOrThrow(typeof(Il2CppArrayRank3<>)); + + (string, string)[] replacements = + [ + ("Il2CppSystem.Object", "Il2CppSystem.IObject"), + ("Il2CppSystem.Enum", "Il2CppSystem.IEnum"), + ("Il2CppSystem.ValueType", "Il2CppSystem.IValueType"), + ]; + + var replacementDictionary = replacements.ToDictionary(pair => il2CppMscorlib.GetTypeByFullNameOrThrow(pair.Item1), pair => il2CppMscorlib.GetTypeByFullNameOrThrow(pair.Item2), TypeAnalysisContextEqualityComparer.Instance); + + return new TypeConversionVisitor(replacementDictionary) + { + Pointer = pointer, + ByRef = byRef, + ArrayRank1 = arrayRank1, + ArrayRank2 = arrayRank2, + ArrayRank3 = arrayRank3, + }; + } + + protected override TypeAnalysisContext CombineResults(ArrayTypeAnalysisContext type, TypeAnalysisContext elementResult) + { + return type.Rank switch + { + 1 => ArrayRank1.MakeGenericInstanceType([elementResult]), + 2 => ArrayRank2.MakeGenericInstanceType([elementResult]), + 3 => ArrayRank3.MakeGenericInstanceType([elementResult]), + _ => throw new NotImplementedException($"Support for arrays with rank {type.Rank} has not been implemented."), + }; + } + + protected override TypeAnalysisContext CombineResults(SzArrayTypeAnalysisContext type, TypeAnalysisContext elementResult) + { + return ArrayRank1.MakeGenericInstanceType([elementResult]); + } + + protected override TypeAnalysisContext CombineResults(PointerTypeAnalysisContext type, TypeAnalysisContext elementResult) + { + return Pointer.MakeGenericInstanceType([elementResult]); + } + + protected override TypeAnalysisContext CombineResults(ByRefTypeAnalysisContext type, TypeAnalysisContext elementResult) + { + return ByRef.MakeGenericInstanceType([elementResult]); + } +} diff --git a/Il2CppInterop.Generator/Visitors/TypeReplacementVisitor.cs b/Il2CppInterop.Generator/Visitors/TypeReplacementVisitor.cs new file mode 100644 index 00000000..12af98c3 --- /dev/null +++ b/Il2CppInterop.Generator/Visitors/TypeReplacementVisitor.cs @@ -0,0 +1,194 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Cpp2IL.Core.Model.Contexts; +using Il2CppInterop.Generator; + +namespace Il2CppInterop.Generator.Visitors; + +internal class TypeReplacementVisitor(Dictionary replacements) : DefaultTypeVisitor +{ + private readonly Dictionary _replacements = replacements; + + public static TypeReplacementVisitor Null { get; } = new NullTypeReplacementVisitor(); + + public static TypeReplacementVisitor CreateForMethodCopying(MethodAnalysisContext source, MethodAnalysisContext destination) + { + Debug.Assert(source.GenericParameters.Count == destination.GenericParameters.Count); + Debug.Assert(source.DeclaringType == destination.DeclaringType); + if (source.GenericParameters.Count == 0) + return new([]); + + var replacements = new Dictionary(source.GenericParameters.Count); + for (var i = source.GenericParameters.Count - 1; i >= 0; i--) + { + replacements.Add(source.GenericParameters[i], destination.GenericParameters[i]); + } + + return new TypeReplacementVisitor(replacements); + } + + public static TypeReplacementVisitor Combine(TypeReplacementVisitor first, TypeReplacementVisitor second) + { + return new CombinedTypeReplacementVisitor(first, second); + } + + [return: NotNullIfNotNull(nameof(type))] + public TypeAnalysisContext? Replace(TypeAnalysisContext? type) + { + return type is null ? null : Visit(type); + } + + public IEnumerable Replace(IEnumerable types) + { + foreach (var type in types) + { + yield return Replace(type); + } + } + + public IReadOnlyList Replace(IReadOnlyList types) + { + if (types.Count == 0) + return []; + + var results = new TypeAnalysisContext[types.Count]; + for (var i = types.Count - 1; i >= 0; i--) + { + results[i] = Replace(types[i]); + } + + return results; + } + + public void Modify(List types) + { + for (var i = 0; i < types.Count; i++) + { + types[i] = Replace(types[i]); + } + } + + [return: NotNullIfNotNull(nameof(method))] + public MethodAnalysisContext? Replace(MethodAnalysisContext? method) + { + if (method is null) + return null; + + if (method is not ConcreteGenericMethodAnalysisContext concreteGenericMethod) + return method; + + var typeArguments = Replace(concreteGenericMethod.TypeGenericParameters); + var methodArguments = Replace(concreteGenericMethod.MethodGenericParameters); + + return new ConcreteGenericMethodAnalysisContext(concreteGenericMethod.BaseMethodContext, typeArguments, methodArguments); + } + + [return: NotNullIfNotNull(nameof(field))] + public FieldAnalysisContext? Replace(FieldAnalysisContext? field) + { + if (field is null) + return null; + + if (field is not ConcreteGenericFieldAnalysisContext concreteGenericField) + return field; + + var declaringType = (GenericInstanceTypeAnalysisContext)Replace(concreteGenericField.DeclaringType); + + return declaringType == concreteGenericField.DeclaringType ? field : new ConcreteGenericFieldAnalysisContext(concreteGenericField.BaseFieldContext, declaringType); + } + + protected override TypeAnalysisContext VisitSimpleType(TypeAnalysisContext type) + { + return _replacements.TryGetValue(type, out var replacement) ? replacement : type; + } + + protected override TypeAnalysisContext CombineResults(ArrayTypeAnalysisContext type, TypeAnalysisContext elementResult) + { + return type.ElementType == elementResult ? type : elementResult.MakeArrayType(type.Rank); + } + + protected override TypeAnalysisContext CombineResults(BoxedTypeAnalysisContext type, TypeAnalysisContext elementResult) + { + return type.ElementType == elementResult ? type : elementResult.MakeBoxedType(); + } + + protected override TypeAnalysisContext CombineResults(ByRefTypeAnalysisContext type, TypeAnalysisContext elementResult) + { + return type.ElementType == elementResult ? type : elementResult.MakeByReferenceType(); + } + + protected override TypeAnalysisContext CombineResults(CustomModifierTypeAnalysisContext type, TypeAnalysisContext elementResult, TypeAnalysisContext modifierResult) + { + if (type.ElementType == elementResult && type.ModifierType == modifierResult) + return type; + return elementResult.MakeCustomModifierType(modifierResult, type.Required); + } + + protected override TypeAnalysisContext CombineResults(GenericInstanceTypeAnalysisContext type, TypeAnalysisContext genericTypeResult, TypeAnalysisContext[] genericArgumentsResults) + { + if (type.GenericType == genericTypeResult && type.GenericArguments.SequenceEqual(genericArgumentsResults)) + return type; + return genericTypeResult.MakeGenericInstanceType(genericArgumentsResults!); + } + + public override TypeAnalysisContext Visit(GenericParameterTypeAnalysisContext type) + { + return _replacements.TryGetValue(type, out var replacement) ? replacement : type; + } + + protected override TypeAnalysisContext CombineResults(PinnedTypeAnalysisContext type, TypeAnalysisContext elementResult) + { + return type.ElementType == elementResult ? type : elementResult.MakePinnedType(); + } + + protected override TypeAnalysisContext CombineResults(PointerTypeAnalysisContext type, TypeAnalysisContext elementResult) + { + return type.ElementType == elementResult ? type : elementResult.MakePointerType(); + } + + public override TypeAnalysisContext Visit(SentinelTypeAnalysisContext type) + { + return _replacements.TryGetValue(type, out var replacement) ? replacement : type; + } + + protected override TypeAnalysisContext CombineResults(SzArrayTypeAnalysisContext type, TypeAnalysisContext elementResult) + { + return type.ElementType == elementResult ? type : elementResult.MakeSzArrayType(); + } + + private sealed class CombinedTypeReplacementVisitor(TypeReplacementVisitor first, TypeReplacementVisitor second) : TypeReplacementVisitor([]) + { + public override TypeAnalysisContext Visit(TypeAnalysisContext type) => second.Visit(first.Visit(type)); + protected override TypeAnalysisContext Visit(ReferencedTypeAnalysisContext type) => second.Visit(first.Visit(type)); + public override TypeAnalysisContext Visit(WrappedTypeAnalysisContext type) => second.Visit(first.Visit(type)); + public override TypeAnalysisContext Visit(ArrayTypeAnalysisContext type) => second.Visit(first.Visit(type)); + public override TypeAnalysisContext Visit(BoxedTypeAnalysisContext type) => second.Visit(first.Visit(type)); + public override TypeAnalysisContext Visit(ByRefTypeAnalysisContext type) => second.Visit(first.Visit(type)); + public override TypeAnalysisContext Visit(CustomModifierTypeAnalysisContext type) => second.Visit(first.Visit(type)); + public override TypeAnalysisContext Visit(GenericInstanceTypeAnalysisContext type) => second.Visit(first.Visit(type)); + public override TypeAnalysisContext Visit(GenericParameterTypeAnalysisContext type) => second.Visit(first.Visit(type)); + public override TypeAnalysisContext Visit(PinnedTypeAnalysisContext type) => second.Visit(first.Visit(type)); + public override TypeAnalysisContext Visit(PointerTypeAnalysisContext type) => second.Visit(first.Visit(type)); + public override TypeAnalysisContext Visit(SentinelTypeAnalysisContext type) => second.Visit(first.Visit(type)); + public override TypeAnalysisContext Visit(SzArrayTypeAnalysisContext type) => second.Visit(first.Visit(type)); + protected override TypeAnalysisContext VisitSimpleType(TypeAnalysisContext type) => second.Visit(first.Visit(type)); + } + + private sealed class NullTypeReplacementVisitor() : TypeReplacementVisitor([]) + { + public override TypeAnalysisContext Visit(TypeAnalysisContext type) => type; + protected override TypeAnalysisContext Visit(ReferencedTypeAnalysisContext type) => type; + public override TypeAnalysisContext Visit(WrappedTypeAnalysisContext type) => type; + public override TypeAnalysisContext Visit(ArrayTypeAnalysisContext type) => type; + public override TypeAnalysisContext Visit(BoxedTypeAnalysisContext type) => type; + public override TypeAnalysisContext Visit(ByRefTypeAnalysisContext type) => type; + public override TypeAnalysisContext Visit(CustomModifierTypeAnalysisContext type) => type; + public override TypeAnalysisContext Visit(GenericInstanceTypeAnalysisContext type) => type; + public override TypeAnalysisContext Visit(GenericParameterTypeAnalysisContext type) => type; + public override TypeAnalysisContext Visit(PinnedTypeAnalysisContext type) => type; + public override TypeAnalysisContext Visit(PointerTypeAnalysisContext type) => type; + public override TypeAnalysisContext Visit(SentinelTypeAnalysisContext type) => type; + public override TypeAnalysisContext Visit(SzArrayTypeAnalysisContext type) => type; + protected override TypeAnalysisContext VisitSimpleType(TypeAnalysisContext type) => type; + } +} diff --git a/Il2CppInterop.Generator/Visitors/TypeVisitor.cs b/Il2CppInterop.Generator/Visitors/TypeVisitor.cs new file mode 100644 index 00000000..f7ff65e8 --- /dev/null +++ b/Il2CppInterop.Generator/Visitors/TypeVisitor.cs @@ -0,0 +1,42 @@ +using Cpp2IL.Core.Model.Contexts; + +namespace Il2CppInterop.Generator.Visitors; + +public abstract class TypeVisitor +{ + public virtual T Visit(TypeAnalysisContext type) => type switch + { + ReferencedTypeAnalysisContext referencedType => Visit(referencedType), + _ => VisitSimpleType(type), + }; + protected virtual T Visit(ReferencedTypeAnalysisContext type) => type switch + { + WrappedTypeAnalysisContext wrappedType => Visit(wrappedType), + GenericInstanceTypeAnalysisContext genericInstanceType => Visit(genericInstanceType), + GenericParameterTypeAnalysisContext genericParameterType => Visit(genericParameterType), + SentinelTypeAnalysisContext sentinelType => Visit(sentinelType), + _ => throw new ArgumentOutOfRangeException(nameof(type), type, "Type is not supported"), + }; + public virtual T Visit(WrappedTypeAnalysisContext type) => type switch + { + ArrayTypeAnalysisContext arrayType => Visit(arrayType), + BoxedTypeAnalysisContext boxedType => Visit(boxedType), + ByRefTypeAnalysisContext byRefType => Visit(byRefType), + CustomModifierTypeAnalysisContext customModifierType => Visit(customModifierType), + PinnedTypeAnalysisContext pinnedType => Visit(pinnedType), + PointerTypeAnalysisContext pointerType => Visit(pointerType), + SzArrayTypeAnalysisContext szArrayType => Visit(szArrayType), + _ => throw new ArgumentOutOfRangeException(nameof(type), type, "Type is not supported"), + }; + public abstract T Visit(ArrayTypeAnalysisContext type); + public abstract T Visit(BoxedTypeAnalysisContext type); + public abstract T Visit(ByRefTypeAnalysisContext type); + public abstract T Visit(CustomModifierTypeAnalysisContext type); + public abstract T Visit(GenericInstanceTypeAnalysisContext type); + public abstract T Visit(GenericParameterTypeAnalysisContext type); + public abstract T Visit(PinnedTypeAnalysisContext type); + public abstract T Visit(PointerTypeAnalysisContext type); + public abstract T Visit(SentinelTypeAnalysisContext type); + public abstract T Visit(SzArrayTypeAnalysisContext type); + protected abstract T VisitSimpleType(TypeAnalysisContext type); +} diff --git a/Il2CppInterop.Generator/XrefScans/XrefScanImpl.cs b/Il2CppInterop.Generator/XrefScans/XrefScanImpl.cs deleted file mode 100644 index fbcb3aba..00000000 --- a/Il2CppInterop.Generator/XrefScans/XrefScanImpl.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Il2CppInterop.Common.XrefScans; - -namespace Il2CppInterop.Generator.XrefScans; - -internal class XrefScanImpl : IXrefScannerImpl -{ - public (XrefScanUtil.InitMetadataForMethod, IntPtr)? GetMetadataResolver() - { - return null; - } - - public bool XrefGlobalClassFilter(IntPtr movTarget) - { - return false; - } -} diff --git a/Il2CppInterop.HarmonySupport/Extensions.cs b/Il2CppInterop.HarmonySupport/Extensions.cs new file mode 100644 index 00000000..3b36149d --- /dev/null +++ b/Il2CppInterop.HarmonySupport/Extensions.cs @@ -0,0 +1,81 @@ +using System.Reflection; +using System.Reflection.Emit; + +namespace Il2CppInterop.HarmonySupport; + +internal static class Extensions +{ + public static void Emit(this ILGenerator il, OpCode opCode, MethodBase methodBase) + { + if (methodBase is MethodInfo methodInfo) + { + il.Emit(opCode, methodInfo); + } + else if (methodBase is ConstructorInfo constructorInfo) + { + il.Emit(opCode, constructorInfo); + } + else + { + throw new InvalidOperationException("Can't emit a call to a MethodBase that isn't either a MethodInfo or a ConstructorInfo"); + } + } + + public static void EmitLdarg(this ILGenerator il, int index) + { + switch (index) + { + case 0: + il.Emit(OpCodes.Ldarg_0); + break; + case 1: + il.Emit(OpCodes.Ldarg_1); + break; + case 2: + il.Emit(OpCodes.Ldarg_2); + break; + case 3: + il.Emit(OpCodes.Ldarg_3); + break; + default: + if (index <= byte.MaxValue) + { + il.Emit(OpCodes.Ldarg_S, (byte)index); + } + else + { + il.Emit(OpCodes.Ldarg, index); + } + break; + } + } + + public static void EmitStarg(this ILGenerator il, int index) + { + switch (index) + { + case 0: + il.Emit(OpCodes.Starg_S, (byte)0); + break; + case 1: + il.Emit(OpCodes.Starg_S, (byte)1); + break; + case 2: + il.Emit(OpCodes.Starg_S, (byte)2); + break; + case 3: + il.Emit(OpCodes.Starg_S, (byte)3); + break; + default: + if (index <= byte.MaxValue) + { + il.Emit(OpCodes.Starg_S, (byte)index); + } + else + { + il.Emit(OpCodes.Starg, index); + } + break; + } + } +} diff --git a/Il2CppInterop.HarmonySupport/HarmonyBackendComponent.cs b/Il2CppInterop.HarmonySupport/HarmonyBackendComponent.cs deleted file mode 100644 index 89661413..00000000 --- a/Il2CppInterop.HarmonySupport/HarmonyBackendComponent.cs +++ /dev/null @@ -1,39 +0,0 @@ -using HarmonyLib.Public.Patching; -using Il2CppInterop.Common.Host; -using Il2CppInterop.Runtime; -using Il2CppInterop.Runtime.Injection; - -namespace Il2CppInterop.HarmonySupport; - -public static class HarmonySupport -{ - public static T AddHarmonySupport(this T host) where T : BaseHost - { - host.AddComponent(new HarmonySupportComponent()); - return host; - } -} - -internal class HarmonySupportComponent : IHostComponent -{ - public void Dispose() => PatchManager.ResolvePatcher -= TryResolve; - - public void Start() => PatchManager.ResolvePatcher += TryResolve; - - private static void TryResolve(object sender, PatchManager.PatcherResolverEventArgs args) - { - var declaringType = args.Original.DeclaringType; - if (declaringType == null) return; - if (Il2CppType.From(declaringType, false) == null || - ClassInjector.IsManagedTypeInjected(declaringType)) - { - return; - } - - var backend = new Il2CppDetourMethodPatcher(args.Original); - if (backend.IsValid) - { - args.MethodPatcher = backend; - } - } -} diff --git a/Il2CppInterop.HarmonySupport/Il2CppDetourMethodPatcher.cs b/Il2CppInterop.HarmonySupport/Il2CppDetourMethodPatcher.cs deleted file mode 100644 index 0bd437ee..00000000 --- a/Il2CppInterop.HarmonySupport/Il2CppDetourMethodPatcher.cs +++ /dev/null @@ -1,482 +0,0 @@ -using System.Reflection; -using System.Reflection.Emit; -using System.Runtime.InteropServices; -using HarmonyLib; -using HarmonyLib.Public.Patching; -using Il2CppInterop.Common; -using Il2CppInterop.Runtime; -using Il2CppInterop.Runtime.Injection; -using Il2CppInterop.Runtime.InteropTypes; -using Il2CppInterop.Runtime.Runtime; -using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo; -using Il2CppInterop.Runtime.Startup; -using Microsoft.Extensions.Logging; -using MonoMod.Cil; -using MonoMod.RuntimeDetour; -using MonoMod.Utils; -using Detour = MonoMod.RuntimeDetour.Detour; -using IDetour = Il2CppInterop.Runtime.Injection.IDetour; -using ValueType = Il2CppSystem.ValueType; - -namespace Il2CppInterop.HarmonySupport; - -internal unsafe class Il2CppDetourMethodPatcher : MethodPatcher -{ - private static readonly MethodInfo IL2CPPToManagedStringMethodInfo - = AccessTools.Method(typeof(IL2CPP), - nameof(IL2CPP.Il2CppStringToManaged)); - - private static readonly MethodInfo ManagedToIL2CPPStringMethodInfo - = AccessTools.Method(typeof(IL2CPP), - nameof(IL2CPP.ManagedStringToIl2Cpp)); - - private static readonly MethodInfo ObjectBaseToPtrMethodInfo - = AccessTools.Method(typeof(IL2CPP), - nameof(IL2CPP.Il2CppObjectBaseToPtr)); - - private static readonly MethodInfo ObjectBaseToPtrNotNullMethodInfo - = AccessTools.Method(typeof(IL2CPP), - nameof(IL2CPP.Il2CppObjectBaseToPtrNotNull)); - - private static readonly MethodInfo ReportExceptionMethodInfo - = AccessTools.Method(typeof(Il2CppDetourMethodPatcher), nameof(ReportException)); - - // Map each value type to correctly sized store opcode to prevent memory overwrite - // Special case: bool is byte in Il2Cpp - private static readonly Dictionary StIndOpcodes = new() - { - [typeof(byte)] = OpCodes.Stind_I1, - [typeof(sbyte)] = OpCodes.Stind_I1, - [typeof(bool)] = OpCodes.Stind_I1, - [typeof(short)] = OpCodes.Stind_I2, - [typeof(ushort)] = OpCodes.Stind_I2, - [typeof(int)] = OpCodes.Stind_I4, - [typeof(uint)] = OpCodes.Stind_I4, - [typeof(long)] = OpCodes.Stind_I8, - [typeof(ulong)] = OpCodes.Stind_I8, - [typeof(float)] = OpCodes.Stind_R4, - [typeof(double)] = OpCodes.Stind_R8 - }; - - private static readonly List DelegateCache = new(); - private static readonly List DetourCache = new(); - - private INativeMethodInfoStruct modifiedNativeMethodInfo; - - private IDetour nativeDetour; - - private INativeMethodInfoStruct originalNativeMethodInfo; - - /// - /// Constructs a new instance of method patcher. - /// - /// - public Il2CppDetourMethodPatcher(MethodBase original) : base(original) => Init(); - - internal bool IsValid { get; private set; } - - private void Init() - { - try - { - var methodField = Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(Original); - - if (methodField == null) - { - var fieldInfoField = - Il2CppInteropUtils.GetIl2CppFieldInfoPointerFieldForGeneratedFieldAccessor(Original); - - if (fieldInfoField != null) - { - throw new - Exception($"Method {Original.FullDescription()} is a field accessor, it can't be patched."); - } - - // Generated method is probably unstripped, it can be safely handed to IL handler - return; - } - - // Get the native MethodInfo struct for the target method - originalNativeMethodInfo = - UnityVersionHandler.Wrap((Il2CppMethodInfo*)(IntPtr)methodField.GetValue(null)); - - // Create a modified native MethodInfo struct, that will point towards the trampoline - modifiedNativeMethodInfo = UnityVersionHandler.NewMethod(); - Buffer.MemoryCopy(originalNativeMethodInfo.Pointer.ToPointer(), - modifiedNativeMethodInfo.Pointer.ToPointer(), UnityVersionHandler.MethodSize(), - UnityVersionHandler.MethodSize()); - IsValid = true; - } - catch (Exception e) - { - Logger.Instance.LogWarning( - "Failed to init IL2CPP patch backend for {Original}, using normal patch handlers: {ErrorMessage}", - Original.FullDescription(), e.Message); - } - } - - /// - public override DynamicMethodDefinition PrepareOriginal() => null; - - /// - public override MethodBase DetourTo(MethodBase replacement) - { - // // Unpatch an existing detour if it exists - if (nativeDetour != null) - { - // Point back to the original method before we unpatch - modifiedNativeMethodInfo.MethodPointer = originalNativeMethodInfo.MethodPointer; - nativeDetour.Dispose(); - } - - // Generate a new DMD of the modified unhollowed method, and apply harmony patches to it - var copiedDmd = CopyOriginal(); - - HarmonyManipulator.Manipulate(copiedDmd.OriginalMethod, copiedDmd.OriginalMethod.GetPatchInfo(), - new ILContext(copiedDmd.Definition)); - - // Generate the MethodInfo instances - var managedHookedMethod = copiedDmd.Generate(); - var unmanagedTrampolineMethod = GenerateNativeToManagedTrampoline(managedHookedMethod).Generate(); - - // Apply a detour from the unmanaged implementation to the patched harmony method - var unmanagedDelegateType = DelegateTypeFactory.instance.CreateDelegateType(unmanagedTrampolineMethod, - CallingConvention.Cdecl); - - var unmanagedDelegate = unmanagedTrampolineMethod.CreateDelegate(unmanagedDelegateType); - DelegateCache.Add(unmanagedDelegate); - - nativeDetour = - Il2CppInteropRuntime.Instance.DetourProvider.Create(originalNativeMethodInfo.MethodPointer, unmanagedDelegate); - nativeDetour.Apply(); - modifiedNativeMethodInfo.MethodPointer = nativeDetour.OriginalTrampoline; - - var detour = new Detour(Original, managedHookedMethod); - detour.Apply(); - DetourCache.Add(detour); - - return managedHookedMethod; - } - - /// - public override DynamicMethodDefinition CopyOriginal() - { - var dmd = new DynamicMethodDefinition(Original); - dmd.Definition.Name = "UnhollowedWrapper_" + dmd.Definition.Name; - var cursor = new ILCursor(new ILContext(dmd.Definition)); - - - // Remove il2cpp_object_get_virtual_method - if (cursor.TryGotoNext(x => x.MatchLdarg(0), - x => x.MatchCall(typeof(IL2CPP), - nameof(IL2CPP.Il2CppObjectBaseToPtr)), - x => x.MatchLdsfld(out _), - x => x.MatchCall(typeof(IL2CPP), - nameof(IL2CPP.il2cpp_object_get_virtual_method)))) - { - cursor.RemoveRange(4); - } - else - { - cursor.Goto(0) - .GotoNext(x => - x.MatchLdsfld(Il2CppInteropUtils - .GetIl2CppMethodInfoPointerFieldForGeneratedMethod(Original))) - .Remove(); - } - - // Replace original IL2CPPMethodInfo pointer with a modified one that points to the trampoline - cursor - .Emit(Mono.Cecil.Cil.OpCodes.Ldc_I8, modifiedNativeMethodInfo.Pointer.ToInt64()) - .Emit(Mono.Cecil.Cil.OpCodes.Conv_I); - - return dmd; - } - - // Tries to guess whether a function needs a return buffer for the return struct, in all cases except win64 it's undefined behaviour - private static bool IsReturnBufferNeeded(int size) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170#return-values - return size != 1 && size != 4 && size != 8; - } - - if (Environment.Is64BitProcess) - { - // x64 gcc and clang seem to use a return buffer for everything above 16 bytes - return size > 16; - } - - // Looks like on x32 gcc and clang return buffer is always used - return true; - } - - private DynamicMethodDefinition GenerateNativeToManagedTrampoline(MethodInfo targetManagedMethodInfo) - { - // managedParams are the interop types used on the managed side - // unmanagedParams are IntPtr references that are used by IL2CPP compiled assembly - var paramStartIndex = 0; - - var managedReturnType = AccessTools.GetReturnedType(Original); - var unmanagedReturnType = managedReturnType.NativeType(); - - var returnSize = IntPtr.Size; - - var isReturnValueType = managedReturnType.IsSubclassOf(typeof(ValueType)); - if (isReturnValueType) - { - uint align = 0; - returnSize = IL2CPP.il2cpp_class_value_size(Il2CppClassPointerStore.GetNativeClassPointer(managedReturnType), ref align); - } - - var hasReturnBuffer = isReturnValueType && IsReturnBufferNeeded(returnSize); - if (hasReturnBuffer) - // C compilers seem to return large structs by allocating a return buffer on caller's side and passing it as the first parameter - // TODO: Handle ARM - // TODO: Check if this applies to values other than structs - { - unmanagedReturnType = typeof(IntPtr); - paramStartIndex++; - } - - if (!Original.IsStatic) - { - paramStartIndex++; - } - - var managedParams = Original.GetParameters().Select(x => x.ParameterType).ToArray(); - var unmanagedParams = - new Type[managedParams.Length + paramStartIndex + - 1]; // +1 for methodInfo at the end - - if (hasReturnBuffer) - // With GCC the return buffer seems to be the first param, same is likely with other compilers too - { - unmanagedParams[0] = typeof(IntPtr); - } - - if (!Original.IsStatic) - { - unmanagedParams[paramStartIndex - 1] = typeof(IntPtr); - } - - unmanagedParams[^1] = typeof(Il2CppMethodInfo*); - Array.Copy(managedParams.Select(TrampolineHelpers.NativeType).ToArray(), 0, - unmanagedParams, paramStartIndex, managedParams.Length); - - var dmd = new DynamicMethodDefinition("(il2cpp -> managed) " + Original.Name, - unmanagedReturnType, - unmanagedParams - ); - - var il = dmd.GetILGenerator(); - il.BeginExceptionBlock(); - - // Declare a list of variables to dereference back to the original pointers. - // This is required due to the needed interop type conversions, so we can't directly pass some addresses as byref types - var indirectVariables = new LocalBuilder[managedParams.Length]; - - if (!Original.IsStatic) - { - EmitConvertArgumentToManaged(il, paramStartIndex - 1, Original.DeclaringType, out _); - } - - for (var i = 0; i < managedParams.Length; ++i) - { - EmitConvertArgumentToManaged(il, i + paramStartIndex, managedParams[i], out indirectVariables[i]); - } - - // Run the managed method - il.Emit(OpCodes.Call, targetManagedMethodInfo); - - // Store the managed return type temporarily (if there was one) - LocalBuilder managedReturnVariable = null; - if (managedReturnType != typeof(void)) - { - managedReturnVariable = il.DeclareLocal(managedReturnType); - il.Emit(OpCodes.Stloc, managedReturnVariable); - } - - // Convert any managed byref values into their relevant IL2CPP types, and then store the values into their relevant dereferenced pointers - for (var i = 0; i < managedParams.Length; ++i) - { - if (indirectVariables[i] == null) - { - continue; - } - - il.Emit(OpCodes.Ldarg_S, i + paramStartIndex); - il.Emit(OpCodes.Ldloc, indirectVariables[i]); - var directType = managedParams[i].GetElementType(); - EmitConvertManagedTypeToIL2CPP(il, directType); - il.Emit(StIndOpcodes.TryGetValue(directType, out var stindOpCodde) ? stindOpCodde : OpCodes.Stind_I); - } - - // Handle any lingering exceptions - il.BeginCatchBlock(typeof(Exception)); - il.Emit(OpCodes.Call, ReportExceptionMethodInfo); - il.EndExceptionBlock(); - - // Convert the return value back to an IL2CPP friendly type (if there was a return value), and then return - if (managedReturnVariable != null) - { - if (hasReturnBuffer) - { - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldloc, managedReturnVariable); - il.Emit(OpCodes.Call, ObjectBaseToPtrNotNullMethodInfo); - EmitUnbox(il); - il.Emit(OpCodes.Ldc_I4, returnSize); - il.Emit(OpCodes.Cpblk); - - // Return the same pointer to the return buffer - il.Emit(OpCodes.Ldarg_0); - } - else - { - il.Emit(OpCodes.Ldloc, managedReturnVariable); - EmitConvertManagedTypeToIL2CPP(il, managedReturnType); - } - } - - il.Emit(OpCodes.Ret); - - return dmd; - } - - private static void EmitUnbox(ILGenerator il) - { - il.Emit(OpCodes.Ldc_I4_2); - il.Emit(OpCodes.Conv_I); - il.Emit(OpCodes.Sizeof, typeof(void*)); - il.Emit(OpCodes.Mul); - il.Emit(OpCodes.Add); - } - - private static void ReportException(Exception ex) => - Logger.Instance.LogError(ex, "During invoking native->managed trampoline"); - - private static void EmitConvertManagedTypeToIL2CPP(ILGenerator il, Type returnType) - { - if (returnType == typeof(string)) - { - il.Emit(OpCodes.Call, ManagedToIL2CPPStringMethodInfo); - } - else if (!returnType.IsValueType && returnType.IsSubclassOf(typeof(Il2CppObjectBase))) - { - il.Emit(OpCodes.Call, ObjectBaseToPtrMethodInfo); - } - } - - private static void EmitConvertArgumentToManaged(ILGenerator il, - int argIndex, - Type managedParamType, - out LocalBuilder variable) - { - variable = null; - - bool needsBoxing = managedParamType.IsSubclassOf(typeof(ValueType)); - - if (needsBoxing) - { - var classPtr = Il2CppClassPointerStore.GetNativeClassPointer(managedParamType); - - // il2cpp_value_box uses .NET boxing semantics which boxes Nullable as just T, - // losing the HasValue field. Manually box Nullable to preserve full data. - bool isNullable = managedParamType.IsGenericType && - managedParamType.GetGenericTypeDefinition().FullName == "Il2CppSystem.Nullable`1"; - - if (isNullable) - { - uint align = 0; - var valueSize = IL2CPP.il2cpp_class_value_size(classPtr, ref align); - - il.Emit(OpCodes.Ldc_I8, classPtr.ToInt64()); - il.Emit(OpCodes.Conv_I); - il.Emit(OpCodes.Call, AccessTools.Method(typeof(IL2CPP), nameof(IL2CPP.il2cpp_object_new))); - var objLocal = il.DeclareLocal(typeof(IntPtr)); - il.Emit(OpCodes.Stloc, objLocal); - il.Emit(Environment.Is64BitProcess ? OpCodes.Ldarg : OpCodes.Ldarga_S, argIndex); - il.Emit(OpCodes.Ldloc, objLocal); - il.Emit(OpCodes.Call, AccessTools.Method(typeof(IL2CPP), nameof(IL2CPP.il2cpp_object_unbox))); - il.Emit(OpCodes.Ldc_I4, (int)valueSize); - il.Emit(OpCodes.Call, AccessTools.Method(typeof(Il2CppDetourMethodPatcher), nameof(CopyMemory))); - il.Emit(OpCodes.Ldloc, objLocal); - } - else - { - // Box struct into object first before conversion - il.Emit(OpCodes.Ldc_I8, classPtr.ToInt64()); - il.Emit(OpCodes.Conv_I); - // On x64, struct is always a pointer but it is a non-pointer on x86 - // We don't handle byref structs on x86 yet but we're yet to encounter those - il.Emit(Environment.Is64BitProcess ? OpCodes.Ldarg : OpCodes.Ldarga_S, argIndex); - il.Emit(OpCodes.Call, - AccessTools.Method(typeof(IL2CPP), - nameof(IL2CPP.il2cpp_value_box))); - } - } - else - { - il.Emit(OpCodes.Ldarg_S, argIndex); - } - - if (managedParamType.IsValueType) // don't need to convert blittable types - { - return; - } - - void EmitCreateIl2CppObject(Type originalType) - { - var endLabel = il.DefineLabel(); - var notNullLabel = il.DefineLabel(); - - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Brtrue_S, notNullLabel); - - il.Emit(OpCodes.Pop); - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Br_S, endLabel); - - il.MarkLabel(notNullLabel); - il.Emit(OpCodes.Call, AccessTools.Method(typeof(Il2CppObjectPool), nameof(Il2CppObjectPool.Get)).MakeGenericMethod(originalType)); - - il.MarkLabel(endLabel); - } - - void HandleTypeConversion(Type originalType) - { - if (originalType == typeof(string)) - { - il.Emit(OpCodes.Call, IL2CPPToManagedStringMethodInfo); - } - else if (originalType.IsSubclassOf(typeof(Il2CppObjectBase))) - { - EmitCreateIl2CppObject(originalType); - } - } - - if (managedParamType.IsByRef) - { - // TODO: directType being ValueType is not handled yet (but it's not that common in games). Implement when needed. - var directType = managedParamType.GetElementType(); - - variable = il.DeclareLocal(directType); - - il.Emit(OpCodes.Ldind_I); - - HandleTypeConversion(directType); - - il.Emit(OpCodes.Stloc, variable); - il.Emit(OpCodes.Ldloca, variable); - } - else - { - HandleTypeConversion(managedParamType); - } - } - - private static void CopyMemory(IntPtr src, IntPtr dest, int size) => - Buffer.MemoryCopy(src.ToPointer(), dest.ToPointer(), size, size); -} diff --git a/Il2CppInterop.HarmonySupport/Il2CppInterop.HarmonySupport.csproj b/Il2CppInterop.HarmonySupport/Il2CppInterop.HarmonySupport.csproj index 6e8e09f1..f79e4079 100644 --- a/Il2CppInterop.HarmonySupport/Il2CppInterop.HarmonySupport.csproj +++ b/Il2CppInterop.HarmonySupport/Il2CppInterop.HarmonySupport.csproj @@ -2,23 +2,16 @@ Module to allow using Harmony with Il2CppInterop assemblies - net6.0 - Il2CppInterop.HarmonySupport + net10.0 + enable - + - - - - - - false - ..\Il2CppInterop.Runtime\Libs\Il2Cppmscorlib.dll - + diff --git a/Il2CppInterop.HarmonySupport/Il2CppInteropDetour.cs b/Il2CppInterop.HarmonySupport/Il2CppInteropDetour.cs new file mode 100644 index 00000000..dc7a5135 --- /dev/null +++ b/Il2CppInterop.HarmonySupport/Il2CppInteropDetour.cs @@ -0,0 +1,336 @@ +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using Il2CppInterop.Common; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.Injection; +using Il2CppInterop.Runtime.InteropTypes; +using Il2CppInterop.Runtime.Runtime; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo; +using Microsoft.Extensions.Logging; +using Mono.Cecil; +using MonoMod.Cil; +using MonoMod.Core; +using MonoMod.Utils; + +namespace Il2CppInterop.HarmonySupport; + +internal sealed class Il2CppInteropDetour : ICoreDetourWithClone +{ + private static readonly MethodInfo ReportExceptionMethodInfo + = typeof(Il2CppInteropDetour).GetMethod(nameof(ReportException), BindingFlags.NonPublic | BindingFlags.Static)!; + + internal readonly INativeMethodInfoStruct _nativeSourceClone; + private readonly Delegate _thunkDelegate; + + internal ICoreDetour? _detour; + internal ICoreNativeDetour? _nativeDetour; + + public MethodBase Source { get; } + public INativeMethodInfoStruct NativeSource { get; } + public MethodBase Target { get; } + + public bool IsApplied => _nativeDetour?.IsApplied ?? false; + + public DynamicMethodDefinition SourceMethodCloneIL { get; private set; } + public MethodInfo SourceMethodClone { get; private set; } + + public Il2CppInteropDetour(MethodBase source, INativeMethodInfoStruct nativeSource, MethodBase target) + { + Source = source; + NativeSource = nativeSource; + Target = target; + + _nativeSourceClone = UnityVersionHandler.NewMethod(); + unsafe + { + Buffer.MemoryCopy(NativeSource.MethodInfoPointer, _nativeSourceClone.MethodInfoPointer, UnityVersionHandler.MethodSize(), UnityVersionHandler.MethodSize()); + } + + SourceMethodCloneIL = CopyOriginal(); + SourceMethodClone = SourceMethodCloneIL.Generate(); + + try + { + var thunk = GenerateNativeToManagedThunk().Generate(); + var thunkDelegateType = DelegateSupport.GetOrCreateDelegateType(new DelegateSupport.MethodSignature((MethodInfo)source, !source.IsStatic), (MethodInfo)source); + _thunkDelegate = thunk.CreateDelegate(thunkDelegateType); + } + catch (Exception e) + { + Logger.Instance.LogError(e, "Exception during generating native to managed thunk"); + throw; + } + } + + private DynamicMethodDefinition CopyOriginal() + { + try + { + var dmd = new DynamicMethodDefinition(Source); + dmd.Definition.Name = "RuntimeModified_" + dmd.Definition.Name; + + var cursor = new ILCursor(new ILContext(dmd.Definition)); + + foreach (var instr in cursor.Instrs) + { + if (instr.OpCode != Mono.Cecil.Cil.OpCodes.Call || + instr.Operand is not MethodReference methodRef) + continue; + + var name = methodRef.Name; + + if (name.StartsWith("UnsafeInvoke_", StringComparison.Ordinal)) + { + instr.Operand = ProcessWrapper(methodRef, "UnsafeImplementation_"); + } + else if (name.StartsWith("UnsafeConstruct", StringComparison.Ordinal)) + { + instr.Operand = ProcessWrapper(methodRef, "UnsafeConstructor"); + } + else if (name.StartsWith("UnsafeImplementation_", StringComparison.Ordinal) || name.StartsWith("UnsafeConstructor", StringComparison.Ordinal)) + { + instr.Operand = ProcessPointerPatchedLeaf(methodRef); + } + } + + return dmd; + } + catch (Exception e) + { + Logger.Instance.LogError(e, "Exception during creating runtime modified original"); + throw; + } + } + + private MethodInfo ProcessWrapper(MethodReference outerRef, string innerPrefix) + { + var dmd = CreateWrappedDMD(outerRef); + var cursor = new ILCursor(new ILContext(dmd.Definition)); + + foreach (var instr in cursor.Instrs) + { + if (instr.OpCode == Mono.Cecil.Cil.OpCodes.Call && + instr.Operand is MethodReference inner && + inner.Name.StartsWith(innerPrefix, StringComparison.Ordinal)) + { + instr.Operand = ProcessPointerPatchedLeaf(inner); + } + } + + return dmd.Generate(); + } + private MethodInfo ProcessPointerPatchedLeaf(MethodReference leafRef) + { + var dmd = CreateWrappedDMD(leafRef); + var cursor = new ILCursor(new ILContext(dmd.Definition)); + + cursor.Goto(0) + .GotoNext(x => + x.MatchLdsfld(Il2CppInteropUtils + .GetIl2CppMethodInfoPointerFieldForGeneratedMethod(Source)!)) + .Remove(); + + var ptr = _nativeSourceClone.Pointer.ToInt64(); + + cursor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I8, ptr) + .Emit(Mono.Cecil.Cil.OpCodes.Conv_I); + + return dmd.Generate(); + } + + private DynamicMethodDefinition CreateWrappedDMD(MethodReference methodRef) + { + var dmd = new DynamicMethodDefinition(methodRef.ResolveReflection()); + dmd.Definition.Name = "RuntimeModified_" + dmd.Definition.Name; + return dmd; + } + + public void Apply() + { + if (NativeSource.MethodPointer == default) + { + Logger.Instance.LogWarning("Method pointer for {Source} was null", Source.Name); + return; + } + + // Unpatch an existing detour if it exists + if (_nativeDetour != null) + { + // Point back to the original method before we unpatch + _nativeSourceClone.MethodPointer = NativeSource.MethodPointer; + _nativeDetour.Dispose(); + } + + _detour = DetourFactory.Current.CreateDetour(Source, Target); + + _nativeDetour = DetourFactory.Current.CreateNativeDetour(NativeSource.MethodPointer, Marshal.GetFunctionPointerForDelegate(_thunkDelegate)); + if (!_nativeDetour.HasOrigEntrypoint) + { + throw new Exception("HasOrigEntrypoint has to be true"); + } + _nativeSourceClone.MethodPointer = _nativeDetour.OrigEntrypoint; + } + + public void Undo() + { + _detour?.Undo(); + _nativeDetour?.Undo(); + } + + public void Dispose() + { + _detour?.Dispose(); + _nativeDetour?.Dispose(); + + Marshal.FreeHGlobal(_nativeSourceClone.Pointer); + } + + // Tries to guess whether a function needs a return buffer for the return struct, in all cases except win64 it's undefined behaviour + private static bool IsReturnBufferNeeded(int size) + { + if (OperatingSystem.IsWindows()) + { + // https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170#return-values + return size is not 1 and not 4 and not 8; + } + + if (Environment.Is64BitProcess) + { + // x64 gcc and clang seem to use a return buffer for everything above 16 bytes + return size > 16; + } + + // Looks like on x32 gcc and clang return buffer is always used + return true; + } + + private static Type GetReturnType(MethodBase methodOrConstructor) + { + return methodOrConstructor is ConstructorInfo ? typeof(void) : ((MethodInfo)methodOrConstructor).ReturnType; + } + + private DynamicMethodDefinition GenerateNativeToManagedThunk() + { + // managedParams are the interop types used on the managed side + // unmanagedParams are IntPtr references that are used by IL2CPP compiled assembly + var paramStartIndex = 0; + + var managedReturnType = GetReturnType(Source); + var unmanagedReturnType = managedReturnType.NativeType(); + + var returnSize = IntPtr.Size; + + var isReturnValueType = managedReturnType.IsValueType; + if (isReturnValueType) + { + uint align = 0; + returnSize = IL2CPP.il2cpp_class_value_size(Il2CppClassPointerStore.GetNativeClassPointer(managedReturnType), ref align); + } + + var hasReturnBuffer = isReturnValueType && IsReturnBufferNeeded(returnSize); + if (hasReturnBuffer) + // C compilers seem to return large structs by allocating a return buffer on caller's side and passing it as the first parameter + // TODO: Handle ARM + // TODO: Check if this applies to values other than structs + { + unmanagedReturnType = typeof(IntPtr); + paramStartIndex++; + } + + if (!Source.IsStatic) + { + paramStartIndex++; + } + + var managedParams = Source.GetParameters().Select(x => x.ParameterType).ToArray(); + var unmanagedParams = + new Type[managedParams.Length + paramStartIndex + + 1]; // +1 for methodInfo at the end + + if (hasReturnBuffer) + // With GCC the return buffer seems to be the first param, same is likely with other compilers too + { + unmanagedParams[0] = typeof(IntPtr); + } + + if (!Source.IsStatic) + { + unmanagedParams[paramStartIndex - 1] = typeof(IntPtr); + } + + unmanagedParams[^1] = typeof(Il2CppMethodInfo*); + Array.Copy(managedParams.Select(TrampolineHelpers.NativeType).ToArray(), 0, + unmanagedParams, paramStartIndex, managedParams.Length); + + var dmd = new DynamicMethodDefinition("(il2cpp -> managed) " + Source.Name, + unmanagedReturnType, + unmanagedParams + ); + + var il = dmd.GetILGenerator(); + il.BeginExceptionBlock(); + + if (!Source.IsStatic) + { + EmitConvertArgumentToManaged(il, paramStartIndex - 1, Source.DeclaringType!); + } + + for (var i = 0; i < managedParams.Length; ++i) + { + EmitConvertArgumentToManaged(il, i + paramStartIndex, managedParams[i]); + } + + // Run the managed method + il.Emit(OpCodes.Call, Target); + + // Store the managed return type temporarily (if there was one) + LocalBuilder? managedReturnVariable = null; + if (managedReturnType != typeof(void)) + { + managedReturnVariable = il.DeclareLocal(managedReturnType); + il.Emit(OpCodes.Stloc, managedReturnVariable); + } + + // Handle any lingering exceptions + il.BeginCatchBlock(typeof(Exception)); + il.Emit(OpCodes.Call, ReportExceptionMethodInfo); + il.EndExceptionBlock(); + + // Convert the return value back to an IL2CPP friendly type (if there was a return value), and then return + if (managedReturnVariable != null) + { + if (hasReturnBuffer) + { + il.Emit(OpCodes.Ldloc, managedReturnVariable); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Call, typeof(Il2CppType).GetMethod(nameof(Il2CppType.WriteToPointer))!.MakeGenericMethod(managedReturnType)); + + // Return the same pointer to the return buffer + il.Emit(OpCodes.Ldarg_0); + } + else + { + var unmanagedReturnVariable = il.DeclareLocal(unmanagedReturnType); + il.Emit(OpCodes.Ldloc, managedReturnVariable); + il.Emit(OpCodes.Ldloca, unmanagedReturnVariable); + il.Emit(OpCodes.Call, typeof(Il2CppType).GetMethod(nameof(Il2CppType.WriteToPointer))!.MakeGenericMethod(managedReturnType)); + il.Emit(OpCodes.Ldloc, unmanagedReturnVariable); + } + } + il.Emit(OpCodes.Ret); + + return dmd; + } + + private static void ReportException(Exception ex) => + Logger.Instance.LogError(ex, "During invoking native->managed trampoline"); + + private static void EmitConvertArgumentToManaged(ILGenerator il, + int argIndex, + Type managedParamType) + { + il.Emit(OpCodes.Ldarga, argIndex); + il.Emit(OpCodes.Call, typeof(Il2CppType).GetMethod(nameof(Il2CppType.ReadFromPointer))!.MakeGenericMethod(managedParamType)); + } +} diff --git a/Il2CppInterop.HarmonySupport/Il2CppInteropDetourFactory.cs b/Il2CppInterop.HarmonySupport/Il2CppInteropDetourFactory.cs new file mode 100644 index 00000000..75263cb8 --- /dev/null +++ b/Il2CppInterop.HarmonySupport/Il2CppInteropDetourFactory.cs @@ -0,0 +1,79 @@ +using System.Diagnostics.CodeAnalysis; +using Il2CppInterop.Common; +using Il2CppInterop.Runtime.Injection; +using Il2CppInterop.Runtime.Runtime; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo; +using MonoMod.Core; + +namespace Il2CppInterop.HarmonySupport; + +public sealed class Il2CppInteropDetourFactory(IDetourFactory? fallback = null) : IDetourFactory +{ + private readonly IDetourFactory _fallback = fallback ?? DetourFactory.Current; + + public ICoreDetour CreateDetour(CreateDetourRequest request) + { + ArgumentNullException.ThrowIfNull(request.Source); + ArgumentNullException.ThrowIfNull(request.Target); + + if (TryCreateDetour(request, out var detour)) + { + return detour; + } + + return _fallback.CreateDetour(request); + } + + private static bool TryCreateDetour(CreateDetourRequest request, [NotNullWhen(true)] out Il2CppInteropDetour? detour) + { + var declaringType = request.Source.DeclaringType; + if (declaringType != null && TypeInjector.IsPreexistingType(declaringType)) + { + if (!request.CreateSourceCloneIfNotILClone) + { + throw new InvalidOperationException("IDetourFactory consumer has to support CreateSourceCloneIfNotILClone"); + } + + var methodField = Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(request.Source); + + if (methodField == null) + { + var fieldInfoField = Il2CppInteropUtils.GetIl2CppFieldInfoPointerFieldForGeneratedFieldAccessor(request.Source); + + if (fieldInfoField != null) + { + throw new Exception($"Method {request.Source} is a field accessor, it can't be patched."); + } + + // Generated method is probably unstripped, it can be safely handed to IL handler + detour = null; + return false; + } + + var nativeSourcePointer = methodField.GetValue(null) ?? throw new Exception(); + INativeMethodInfoStruct nativeSource; + unsafe + { + nativeSource = UnityVersionHandler.Wrap((Il2CppMethodInfo*)(IntPtr)nativeSourcePointer); + } + + detour = new Il2CppInteropDetour(request.Source, nativeSource, request.Target); + if (request.ApplyByDefault) + { + detour.Apply(); + } + + return true; + } + + detour = null; + return false; + } + + public ICoreNativeDetour CreateNativeDetour(CreateNativeDetourRequest request) + { + return _fallback.CreateNativeDetour(request); + } + + public bool SupportsNativeDetourOrigEntrypoint => true; +} diff --git a/Il2CppInterop.Runtime/Attributes/AlsoInitializeAttribute.cs b/Il2CppInterop.Runtime/Attributes/AlsoInitializeAttribute.cs deleted file mode 100644 index b2f1d4b8..00000000 --- a/Il2CppInterop.Runtime/Attributes/AlsoInitializeAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Il2CppInterop.Runtime.Attributes; - -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] -public class AlsoInitializeAttribute : Attribute -{ - public readonly Type LinkedType; - - public AlsoInitializeAttribute(Type linkedType) - { - LinkedType = linkedType; - } -} diff --git a/Il2CppInterop.Runtime/Attributes/ClassInjectionAssemblyTargetAttribute.cs b/Il2CppInterop.Runtime/Attributes/ClassInjectionAssemblyTargetAttribute.cs deleted file mode 100644 index 4916018c..00000000 --- a/Il2CppInterop.Runtime/Attributes/ClassInjectionAssemblyTargetAttribute.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Il2CppInterop.Runtime.Attributes; - -[AttributeUsage(AttributeTargets.Class)] -public class ClassInjectionAssemblyTargetAttribute : Attribute -{ - private readonly string[] assemblies; - - public ClassInjectionAssemblyTargetAttribute(string assembly) - { - if (string.IsNullOrWhiteSpace(assembly)) assemblies = new string[0]; - else assemblies = new[] { assembly }; - } - - public ClassInjectionAssemblyTargetAttribute(string[] assemblies) - { - if (assemblies is null) this.assemblies = new string[0]; - else this.assemblies = assemblies; - } - - internal IntPtr[] GetImagePointers() - { - var result = new List(); - foreach (var assembly in assemblies) - { - var intPtr = IL2CPP.GetIl2CppImage(assembly); - if (intPtr != IntPtr.Zero) result.Add(intPtr); - } - - return result.ToArray(); - } -} diff --git a/Il2CppInterop.Runtime/Attributes/HideFromIl2CppAttribute.cs b/Il2CppInterop.Runtime/Attributes/HideFromIl2CppAttribute.cs deleted file mode 100644 index 78af88c8..00000000 --- a/Il2CppInterop.Runtime/Attributes/HideFromIl2CppAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Il2CppInterop.Runtime.Attributes; - -/// -/// This attribute indicates that the target should not be exposed to IL2CPP in injected classes -/// -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property | - AttributeTargets.Event)] -public class HideFromIl2CppAttribute : Attribute -{ -} diff --git a/Il2CppInterop.Runtime/Attributes/Il2CppImplementsAttribute.cs b/Il2CppInterop.Runtime/Attributes/Il2CppImplementsAttribute.cs deleted file mode 100644 index 9a86fded..00000000 --- a/Il2CppInterop.Runtime/Attributes/Il2CppImplementsAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Il2CppInterop.Runtime.Attributes; - -[AttributeUsage(AttributeTargets.Class)] -public class Il2CppImplementsAttribute : Attribute -{ - public Il2CppImplementsAttribute(params Type[] interfaces) - { - Interfaces = interfaces; - } - - public Type[] Interfaces { get; } -} diff --git a/Il2CppInterop.Runtime/DelegateSupport.cs b/Il2CppInterop.Runtime/DelegateSupport.cs index 5299beff..4c689bcc 100644 --- a/Il2CppInterop.Runtime/DelegateSupport.cs +++ b/Il2CppInterop.Runtime/DelegateSupport.cs @@ -7,11 +7,9 @@ using System.Text; using Il2CppInterop.Common; using Il2CppInterop.Runtime.Injection; -using Il2CppInterop.Runtime.InteropTypes; +using Il2CppInterop.Runtime.InteropTypes.CoreLib; using Il2CppInterop.Runtime.Runtime; using Microsoft.Extensions.Logging; -using Object = Il2CppSystem.Object; -using ValueType = Il2CppSystem.ValueType; namespace Il2CppInterop.Runtime; @@ -29,13 +27,10 @@ public static class DelegateSupport internal static Type GetOrCreateDelegateType(MethodSignature signature, MethodInfo managedMethod) { - return ourDelegateTypes.GetOrAdd(signature, - (signature, managedMethodInner) => - CreateDelegateType(managedMethodInner, signature), - managedMethod); + return ourDelegateTypes.GetOrAdd(signature, CreateDelegateType, managedMethod); } - private static Type CreateDelegateType(MethodInfo managedMethodInner, MethodSignature signature) + private static Type CreateDelegateType(MethodSignature signature, MethodInfo managedMethodInner) { var typeName = "Il2CppToManagedDelegate_" + managedMethodInner.DeclaringType + "_" + signature.GetHashCode() + (signature.HasThis ? "HasThis" : "") + @@ -43,9 +38,6 @@ private static Type CreateDelegateType(MethodInfo managedMethodInner, MethodSign var newType = ModuleBuilder.DefineType(typeName, TypeAttributes.Sealed | TypeAttributes.Public, typeof(MulticastDelegate)); - newType.SetCustomAttribute(new CustomAttributeBuilder( - typeof(UnmanagedFunctionPointerAttribute).GetConstructor(new[] { typeof(CallingConvention) })!, - new object[] { CallingConvention.Cdecl })); var ctor = newType.DefineConstructor( MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | @@ -103,21 +95,16 @@ private static string ExtractSignature(MethodInfo methodInfo) return builder.ToString(); } - private static Delegate GetOrCreateNativeToManagedTrampoline(MethodSignature signature, - Il2CppSystem.Reflection.MethodInfo nativeMethod, MethodInfo managedMethod) + private static Delegate GetOrCreateNativeToManagedTrampoline(MethodSignature signature, MethodInfo managedMethod) { - return NativeToManagedTrampolines.GetOrAdd(managedMethod, - (_, tuple) => GenerateNativeToManagedTrampoline(tuple.nativeMethod, tuple.managedMethod, tuple.signature), - (nativeMethod, managedMethod, signature)); + return NativeToManagedTrampolines.GetOrAdd(managedMethod, GenerateNativeToManagedTrampoline, signature); } - private static Delegate GenerateNativeToManagedTrampoline(Il2CppSystem.Reflection.MethodInfo nativeMethod, - MethodInfo managedMethod, MethodSignature signature) + private static Delegate GenerateNativeToManagedTrampoline(MethodInfo managedMethod, MethodSignature signature) { var returnType = managedMethod.ReturnType.NativeType(); var managedParameters = managedMethod.GetParameters(); - var nativeParameters = nativeMethod.GetParameters(); var parameterTypes = new Type[managedParameters.Length + 1 + 1]; // thisptr for target, methodInfo last arg parameterTypes[0] = typeof(IntPtr); parameterTypes[managedParameters.Length + 1] = typeof(Il2CppMethodInfo*); @@ -132,72 +119,27 @@ private static Delegate GenerateNativeToManagedTrampoline(Il2CppSystem.Reflectio var tryLabel = bodyBuilder.BeginExceptionBlock(); bodyBuilder.Emit(OpCodes.Ldarg_0); - bodyBuilder.Emit(OpCodes.Call, - typeof(ClassInjectorBase).GetMethod(nameof(ClassInjectorBase.GetMonoObjectFromIl2CppPointer))!); + bodyBuilder.Emit(OpCodes.Call, typeof(Il2CppObjectPool).GetMethod(nameof(Il2CppObjectPool.Get))!); bodyBuilder.Emit(OpCodes.Castclass, typeof(Il2CppToMonoDelegateReference)); - bodyBuilder.Emit(OpCodes.Ldfld, - typeof(Il2CppToMonoDelegateReference).GetField(nameof(Il2CppToMonoDelegateReference.ReferencedDelegate))); + bodyBuilder.Emit(OpCodes.Callvirt, typeof(Il2CppToMonoDelegateReference).GetProperty(nameof(Il2CppToMonoDelegateReference.ReferencedDelegate))!.GetMethod!); for (var i = 0; i < managedParameters.Length; i++) { - var parameterType = managedParameters[i].ParameterType; - bodyBuilder.Emit(OpCodes.Ldarg, i + 1); - if (parameterType == typeof(string)) - { - bodyBuilder.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.Il2CppStringToManaged))!); - } - else if (!parameterType.IsValueType) - { - var labelNull = bodyBuilder.DefineLabel(); - var labelDone = bodyBuilder.DefineLabel(); - bodyBuilder.Emit(OpCodes.Brfalse, labelNull); - bodyBuilder.Emit(OpCodes.Ldarg, i + 1); - bodyBuilder.Emit(OpCodes.Newobj, parameterType.GetConstructor(new[] { typeof(IntPtr) })!); - bodyBuilder.Emit(OpCodes.Br, labelDone); - bodyBuilder.MarkLabel(labelNull); - bodyBuilder.Emit(OpCodes.Ldnull); - bodyBuilder.MarkLabel(labelDone); - } + bodyBuilder.Emit(OpCodes.Call, typeof(DelegateSupport).GetMethod(nameof(ConvertNativeArgument), BindingFlags.Static | BindingFlags.NonPublic)!.MakeGenericMethod(parameterTypes[i + 1], managedParameters[i].ParameterType)); } bodyBuilder.Emit(OpCodes.Call, managedMethod); - if (managedMethod.ReturnType == typeof(string)) - { - bodyBuilder.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.ManagedStringToIl2Cpp))!); - } - else if (!managedMethod.ReturnType.IsValueType) - { - var labelNull = bodyBuilder.DefineLabel(); - var labelDone = bodyBuilder.DefineLabel(); - bodyBuilder.Emit(OpCodes.Dup); - bodyBuilder.Emit(OpCodes.Brfalse, labelNull); - bodyBuilder.Emit(OpCodes.Call, - typeof(Il2CppObjectBase).GetProperty(nameof(Il2CppObjectBase.Pointer))!.GetMethod); - bodyBuilder.Emit(OpCodes.Br, labelDone); - bodyBuilder.MarkLabel(labelNull); - bodyBuilder.Emit(OpCodes.Pop); - bodyBuilder.Emit(OpCodes.Ldc_I4_0); - bodyBuilder.Emit(OpCodes.Conv_I); - bodyBuilder.MarkLabel(labelDone); - } - - LocalBuilder returnLocal = null; + LocalBuilder? returnLocal = null; if (returnType != typeof(void)) { returnLocal = bodyBuilder.DeclareLocal(returnType); + bodyBuilder.Emit(OpCodes.Call, typeof(DelegateSupport).GetMethod(nameof(ConvertReturnValue), BindingFlags.Static | BindingFlags.NonPublic)!.MakeGenericMethod(managedMethod.ReturnType, returnType)); bodyBuilder.Emit(OpCodes.Stloc, returnLocal); } - var exceptionLocal = bodyBuilder.DeclareLocal(typeof(Exception)); bodyBuilder.BeginCatchBlock(typeof(Exception)); - bodyBuilder.Emit(OpCodes.Stloc, exceptionLocal); - bodyBuilder.Emit(OpCodes.Ldstr, "Exception in IL2CPP-to-Managed trampoline, not passing it to il2cpp: "); - bodyBuilder.Emit(OpCodes.Ldloc, exceptionLocal); - bodyBuilder.Emit(OpCodes.Callvirt, typeof(object).GetMethod(nameof(ToString))!); - bodyBuilder.Emit(OpCodes.Call, - typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) })!); bodyBuilder.Emit(OpCodes.Call, typeof(DelegateSupport).GetMethod(nameof(LogError), BindingFlags.Static | BindingFlags.NonPublic)!); bodyBuilder.EndExceptionBlock(); @@ -209,12 +151,30 @@ private static Delegate GenerateNativeToManagedTrampoline(Il2CppSystem.Reflectio return trampoline.CreateDelegate(GetOrCreateDelegateType(signature, managedMethod)); } - private static void LogError(string message) + private static void LogError(Exception exception) { - Logger.Instance.LogError("{Message}", message); + Logger.Instance.LogError("Exception in IL2CPP-to-Managed trampoline, not passing it to il2cpp: {Exception}", exception); } - public static TIl2Cpp? ConvertDelegate(Delegate @delegate) where TIl2Cpp : Il2CppObjectBase + private static unsafe TManaged? ConvertNativeArgument(TNative value) + where TNative : unmanaged + where TManaged : IIl2CppType + { + var span = new ReadOnlySpan(&value, sizeof(TNative)); + return TManaged.ReadFromSpan(span); + } + + private static unsafe TNative ConvertReturnValue(TManaged? value) + where TNative : unmanaged + where TManaged : IIl2CppType + { + TNative result = default; + var span = new Span(&result, sizeof(TNative)); + TManaged.WriteToSpan(value, span); + return result; + } + + public static TIl2Cpp? ConvertDelegate(Delegate @delegate) where TIl2Cpp : Il2CppSystem.Delegate { if (@delegate == null) return null; @@ -230,19 +190,12 @@ private static void LogError(string message) if (parameterType.IsGenericParameter) throw new ArgumentException( $"Delegate has unsubstituted generic parameter ({parameterType}) which is not supported"); - - if (parameterType.BaseType == typeof(ValueType)) - throw new ArgumentException( - $"Delegate has parameter of type {parameterType} (non-blittable struct) which is not supported"); } - var classTypePtr = Il2CppClassPointerStore.GetNativeClassPointer(typeof(TIl2Cpp)); + var classTypePtr = Il2CppClassPointerStore.NativeClassPointer; if (classTypePtr == IntPtr.Zero) throw new ArgumentException($"Type {typeof(TIl2Cpp)} has uninitialized class pointer"); - if (Il2CppClassPointerStore.NativeClassPtr == IntPtr.Zero) - ClassInjector.RegisterTypeInIl2Cpp(); - var il2CppDelegateType = Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(classTypePtr)); var nativeDelegateInvokeMethod = il2CppDelegateType.GetMethod("Invoke"); @@ -256,31 +209,17 @@ private static void LogError(string message) var nativeType = nativeParameters[i].ParameterType; var managedType = parameterInfos[i].ParameterType; - if (nativeType.IsPrimitive || managedType.IsPrimitive) - { - if (nativeType.FullName != managedType.FullName) - throw new ArgumentException( - $"Parameter type mismatch at parameter {i}: {nativeType.FullName} != {managedType.FullName}"); - - continue; - } - - var classPointerFromManagedType = (IntPtr)typeof(Il2CppClassPointerStore<>).MakeGenericType(managedType) - .GetField(nameof(Il2CppClassPointerStore.NativeClassPtr)).GetValue(null); + var classPointerFromManagedType = Il2CppClassPointerStore.GetNativeClassPointer(managedType); var classPointerFromNativeType = IL2CPP.il2cpp_class_from_type(nativeType._impl.value); if (classPointerFromManagedType != classPointerFromNativeType) throw new ArgumentException( $"Parameter type at {i} has mismatched native type pointers; types: {nativeType.FullName} != {managedType.FullName}"); - - if (nativeType.IsByRef || managedType.IsByRef) - throw new ArgumentException($"Parameter at {i} is passed by reference, this is not supported"); } var signature = new MethodSignature(nativeDelegateInvokeMethod, true); - var managedTrampoline = - GetOrCreateNativeToManagedTrampoline(signature, nativeDelegateInvokeMethod, managedInvokeMethod); + var managedTrampoline = GetOrCreateNativeToManagedTrampoline(signature, managedInvokeMethod); var methodInfo = UnityVersionHandler.NewMethod(); methodInfo.MethodPointer = Marshal.GetFunctionPointerForDelegate(managedTrampoline); @@ -290,17 +229,7 @@ private static void LogError(string message) var delegateReference = new Il2CppToMonoDelegateReference(@delegate, methodInfo.Pointer); - Il2CppSystem.Delegate converted; - if (UnityVersionHandler.MustUseDelegateConstructor) - { - converted = ((TIl2Cpp)Activator.CreateInstance(typeof(TIl2Cpp), delegateReference.Cast(), - methodInfo.Pointer)).Cast(); - } - else - { - var nativeDelegatePtr = IL2CPP.il2cpp_object_new(classTypePtr); - converted = new Il2CppSystem.Delegate(nativeDelegatePtr); - } + TIl2Cpp converted = (TIl2Cpp)Activator.CreateInstance(typeof(TIl2Cpp), delegateReference, methodInfo.Pointer)!; converted.method_ptr = methodInfo.MethodPointer; converted.method_info = nativeDelegateInvokeMethod; // todo: is this truly a good hack? @@ -311,13 +240,13 @@ private static void LogError(string message) { // U2021.2.0+ hack in case the constructor did the wrong thing anyway converted.invoke_impl = converted.method_ptr; - converted.method_code = converted.m_target.Pointer; + converted.method_code = delegateReference.Pointer; } - return converted.Cast(); + return converted; } - internal class MethodSignature : IEquatable + internal sealed class MethodSignature : IEquatable { public readonly bool ConstructedFromNative; public readonly bool HasThis; @@ -348,7 +277,7 @@ public MethodSignature(MethodInfo methodInfo, bool hasThis) var hashCode = new HashCode(); hashCode.Add(methodInfo.ReturnType.NativeType()); - if (hasThis) hashCode.Add(methodInfo.DeclaringType.NativeType()); + if (hasThis) hashCode.Add(methodInfo.DeclaringType!.NativeType()); foreach (var parameterInfo in methodInfo.GetParameters()) { hashCode.Add(parameterInfo.ParameterType.NativeType()); @@ -362,55 +291,26 @@ public override int GetHashCode() return _hashCode; } - public bool Equals(MethodSignature other) + public bool Equals(MethodSignature? other) { - if (ReferenceEquals(null, other)) return false; + if (other is null) return false; if (ReferenceEquals(this, other)) return true; return _hashCode.GetHashCode() == other.GetHashCode(); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((MethodSignature)obj); + return Equals(obj as MethodSignature); } - public static bool operator ==(MethodSignature left, MethodSignature right) + public static bool operator ==(MethodSignature? left, MethodSignature? right) { return Equals(left, right); } - public static bool operator !=(MethodSignature left, MethodSignature right) + public static bool operator !=(MethodSignature? left, MethodSignature? right) { return !Equals(left, right); } } - - private class Il2CppToMonoDelegateReference : Object - { - public IntPtr MethodInfo; - public Delegate ReferencedDelegate; - - public Il2CppToMonoDelegateReference(IntPtr obj0) : base(obj0) - { - } - - public Il2CppToMonoDelegateReference(Delegate referencedDelegate, IntPtr methodInfo) : base( - ClassInjector.DerivedConstructorPointer()) - { - ClassInjector.DerivedConstructorBody(this); - - ReferencedDelegate = referencedDelegate; - MethodInfo = methodInfo; - } - - ~Il2CppToMonoDelegateReference() - { - Marshal.FreeHGlobal(MethodInfo); - MethodInfo = IntPtr.Zero; - ReferencedDelegate = null; - } - } } diff --git a/Il2CppInterop.Runtime/Exceptions/IIl2CppException.cs b/Il2CppInterop.Runtime/Exceptions/IIl2CppException.cs new file mode 100644 index 00000000..054c4a60 --- /dev/null +++ b/Il2CppInterop.Runtime/Exceptions/IIl2CppException.cs @@ -0,0 +1,6 @@ +namespace Il2CppInterop.Runtime.Exceptions; + +public interface IIl2CppException +{ + Il2CppException CreateSystemException(); +} diff --git a/Il2CppInterop.Runtime/Exceptions/Il2CppException.cs b/Il2CppInterop.Runtime/Exceptions/Il2CppException.cs new file mode 100644 index 00000000..cd605d72 --- /dev/null +++ b/Il2CppInterop.Runtime/Exceptions/Il2CppException.cs @@ -0,0 +1,64 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using Il2CppInterop.Runtime.Runtime; + +namespace Il2CppInterop.Runtime.Exceptions; + +public class Il2CppException : Exception +{ + [ThreadStatic] + private static byte[]? ourMessageBytes; + + public static Func? ParseMessageHook; + + public readonly Il2CppSystem.Exception Il2cppObject; + + public Il2CppException(Il2CppSystem.Exception il2cppObject) + { + Il2cppObject = il2cppObject; + } + + public override string Message => BuildMessage(Il2cppObject); + + private static unsafe string BuildMessage(Il2CppSystem.Exception il2cppException) + { + var exception = il2cppException.Pointer; + + if (ParseMessageHook != null) + return ParseMessageHook(exception); + + ourMessageBytes ??= new byte[65536]; + fixed (byte* message = ourMessageBytes) + { + IL2CPP.il2cpp_format_exception(exception, message, ourMessageBytes.Length); + } + + var builtMessage = Encoding.UTF8.GetString(ourMessageBytes, 0, Array.IndexOf(ourMessageBytes, (byte)0)); + return $""" + {builtMessage} + --- BEGIN IL2CPP STACK TRACE --- + {il2cppException.ToString(false, true)} + --- END IL2CPP STACK TRACE --- + + """; + } + + public static void RaiseExceptionIfNecessary(IntPtr returnedException) + { + if (returnedException == IntPtr.Zero) + return; + + var il2cppException = (IIl2CppException?)Il2CppObjectPool.Get(returnedException); + Debug.Assert(il2cppException is not null); + + throw il2cppException.CreateSystemException(); + } + + [return: NotNullIfNotNull(nameof(exception))] + public static Il2CppException? FromNativeObject(IIl2CppException? exception) + { + return exception?.CreateSystemException(); + } +} diff --git a/Il2CppInterop.Runtime/ObjectCollectedException.cs b/Il2CppInterop.Runtime/Exceptions/ObjectCollectedException.cs similarity index 77% rename from Il2CppInterop.Runtime/ObjectCollectedException.cs rename to Il2CppInterop.Runtime/Exceptions/ObjectCollectedException.cs index 9dbbc0e1..8ae419d9 100644 --- a/Il2CppInterop.Runtime/ObjectCollectedException.cs +++ b/Il2CppInterop.Runtime/Exceptions/ObjectCollectedException.cs @@ -1,6 +1,6 @@ using System; -namespace Il2CppInterop.Runtime; +namespace Il2CppInterop.Runtime.Exceptions; public class ObjectCollectedException : Exception { diff --git a/Il2CppInterop.Common/Extensions/AssemblyExtensions.cs b/Il2CppInterop.Runtime/Extensions/AssemblyExtensions.cs similarity index 60% rename from Il2CppInterop.Common/Extensions/AssemblyExtensions.cs rename to Il2CppInterop.Runtime/Extensions/AssemblyExtensions.cs index a783e6da..7b04d8da 100644 --- a/Il2CppInterop.Common/Extensions/AssemblyExtensions.cs +++ b/Il2CppInterop.Runtime/Extensions/AssemblyExtensions.cs @@ -1,6 +1,8 @@ -using System.Reflection; +using System; +using System.Linq; +using System.Reflection; -namespace Il2CppInterop.Common.Extensions; +namespace Il2CppInterop.Runtime.Extensions; internal static class AssemblyExtensions { @@ -12,7 +14,7 @@ public static Type[] GetTypesSafe(this Assembly assembly) } catch (ReflectionTypeLoadException ex) { - return ex.Types.Where(t => t != null).ToArray(); + return ex.Types.Where(t => t != null).ToArray()!; } } } diff --git a/Il2CppInterop.Runtime/GenerationInternals.cs b/Il2CppInterop.Runtime/GenerationInternals.cs new file mode 100644 index 00000000..dc56387c --- /dev/null +++ b/Il2CppInterop.Runtime/GenerationInternals.cs @@ -0,0 +1,68 @@ +using System; +using System.ComponentModel; +using Il2CppInterop.Common; +using Il2CppInterop.Runtime.Exceptions; + +namespace Il2CppInterop.Runtime; + +/// +/// Do not reference this class. Everything in it is an implementation detail of the generator. Breaking changes may occur at any time without warning. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static class GenerationInternals +{ + public static unsafe string? Il2CppStringToManaged(Il2CppSystem.String? il2CppString) + { + if (il2CppString == null) + return null; + + var il2CppStringPtr = il2CppString.Pointer; + + var length = IL2CPP.il2cpp_string_length(il2CppStringPtr); + var chars = IL2CPP.il2cpp_string_chars(il2CppStringPtr); + + return new string(chars, 0, length); + } + + public static unsafe Il2CppSystem.String? ManagedStringToIl2Cpp(string? str) + { + if (str == null) + return null; + + fixed (char* chars = str) + { + return new Il2CppSystem.String((ObjectPointer)IL2CPP.il2cpp_string_new_utf16(chars, str.Length)); + } + } + + public static Il2CppSystem.Type ManagedTypeToIl2CppType(Type type) + { + var classPointer = Il2CppClassPointerStore.GetNativeClassPointer(type); + if (classPointer == IntPtr.Zero) + { + throw new ArgumentException($"{type} does not have a corresponding IL2CPP class pointer"); + } + + var il2CppType = IL2CPP.il2cpp_class_get_type(classPointer); + if (il2CppType == IntPtr.Zero) + { + throw new ArgumentException($"{type} does not have a corresponding IL2CPP type pointer"); + } + + return Il2CppSystem.Type.internal_from_handle(il2CppType); + } + + public static nint Il2CppGCHandleGetTargetOrThrow(nint gchandle) + { + var obj = IL2CPP.il2cpp_gchandle_get_target(gchandle); + if (obj == nint.Zero) + throw new ObjectCollectedException("Object was garbage collected in IL2CPP domain"); + return obj; + } + + public static bool Il2CppGCHandleGetTargetWasCollected(nint gchandle) + { + var obj = IL2CPP.il2cpp_gchandle_get_target(gchandle); + return obj == nint.Zero; + } +} diff --git a/Il2CppInterop.Runtime/IL2CPP.cs b/Il2CppInterop.Runtime/IL2CPP.cs index 0cb5acac..e92a989f 100644 --- a/Il2CppInterop.Runtime/IL2CPP.cs +++ b/Il2CppInterop.Runtime/IL2CPP.cs @@ -5,95 +5,76 @@ using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Text; using System.Text.RegularExpressions; using Il2CppInterop.Common; -using Il2CppInterop.Common.Attributes; +using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; -using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppInterop.Runtime.Runtime; using Microsoft.Extensions.Logging; namespace Il2CppInterop.Runtime; -public static unsafe class IL2CPP +public static unsafe partial class IL2CPP { - private static readonly Dictionary ourImagesMap = new(); - - static IL2CPP() + public static void Il2CppRuntimeClassInit(nint @class) { - var domain = il2cpp_domain_get(); - if (domain == IntPtr.Zero) - { - Logger.Instance.LogError("No il2cpp domain found; sad!"); - return; - } - - uint assembliesCount = 0; - var assemblies = il2cpp_domain_get_assemblies(domain, ref assembliesCount); - for (var i = 0; i < assembliesCount; i++) - { - var image = il2cpp_assembly_get_image(assemblies[i]); - var name = il2cpp_image_get_name_(image)!; - ourImagesMap[name] = image; - } + if (@class != nint.Zero) + il2cpp_runtime_class_init(@class); } - internal static IntPtr GetIl2CppImage(string name) + public static nint GetIl2CppClass(string assemblyName, string namespaze, string className) { - if (ourImagesMap.ContainsKey(name)) return ourImagesMap[name]; - return IntPtr.Zero; + return il2cpp_class_from_name(AssemblyInjector.GetOrCreateImage(assemblyName).Pointer, namespaze, className); } - internal static IntPtr[] GetIl2CppImages() + public static nint GetIl2CppField(nint clazz, string fieldName) { - return ourImagesMap.Values.ToArray(); + if (clazz == nint.Zero) + return nint.Zero; + + var field = il2cpp_class_get_field_from_name(clazz, fieldName); + if (field == nint.Zero) + Logger.Instance.LogError( + "Field {FieldName} was not found on class {ClassName}", fieldName, il2cpp_class_get_name(clazz)); + return field; } - public static IntPtr GetIl2CppClass(string assemblyName, string namespaze, string className) + public static int GetIl2CppFieldOffset(nint field) { - if (!ourImagesMap.TryGetValue(assemblyName, out var image)) - { - Logger.Instance.LogError("Assembly {AssemblyName} is not registered in il2cpp", assemblyName); - return IntPtr.Zero; - } - - var clazz = il2cpp_class_from_name(image, namespaze, className); - return clazz; + if (field == nint.Zero) + return -1; + return (int)il2cpp_field_get_offset(field); } - public static IntPtr GetIl2CppField(IntPtr clazz, string fieldName) + public static int GetIl2cppValueSize(nint klass) { - if (clazz == IntPtr.Zero) return IntPtr.Zero; - - var field = il2cpp_class_get_field_from_name(clazz, fieldName); - if (field == IntPtr.Zero) - Logger.Instance.LogError( - "Field {FieldName} was not found on class {ClassName}", fieldName, il2cpp_class_get_name_(clazz)); - return field; + if (klass == nint.Zero) + return 0; + uint align = 0; + return il2cpp_class_value_size(klass, ref align); } - public static IntPtr GetIl2CppMethodByToken(IntPtr clazz, int token) + public static nint GetIl2CppMethodByToken(nint clazz, int token) { - if (clazz == IntPtr.Zero) + if (clazz == nint.Zero) return NativeStructUtils.GetMethodInfoForMissingMethod(token.ToString()); - var iter = IntPtr.Zero; - IntPtr method; - while ((method = il2cpp_class_get_methods(clazz, ref iter)) != IntPtr.Zero) + var iter = nint.Zero; + nint method; + while ((method = il2cpp_class_get_methods(clazz, ref iter)) != nint.Zero) if (il2cpp_method_get_token(method) == token) return method; - var className = il2cpp_class_get_name_(clazz); + var className = il2cpp_class_get_name(clazz); Logger.Instance.LogTrace("Unable to find method {ClassName}::{Token}", className, token); return NativeStructUtils.GetMethodInfoForMissingMethod(className + "::" + token); } - public static IntPtr GetIl2CppMethod(IntPtr clazz, bool isGeneric, string methodName, string returnTypeName, + public static nint GetIl2CppMethod(nint clazz, bool isGeneric, string methodName, string returnTypeName, params string[] argTypes) { - if (clazz == IntPtr.Zero) + if (clazz == nint.Zero) return NativeStructUtils.GetMethodInfoForMissingMethod(methodName + "(" + string.Join(", ", argTypes) + ")"); @@ -105,12 +86,12 @@ public static IntPtr GetIl2CppMethod(IntPtr clazz, bool isGeneric, string method } var methodsSeen = 0; - var lastMethod = IntPtr.Zero; - var iter = IntPtr.Zero; - IntPtr method; - while ((method = il2cpp_class_get_methods(clazz, ref iter)) != IntPtr.Zero) + var lastMethod = nint.Zero; + var iter = nint.Zero; + nint method; + while ((method = il2cpp_class_get_methods(clazz, ref iter)) != nint.Zero) { - if (il2cpp_method_get_name_(method) != methodName) + if (il2cpp_method_get_name(method) != methodName) continue; if (il2cpp_method_get_param_count(method) != argTypes.Length) @@ -120,7 +101,7 @@ public static IntPtr GetIl2CppMethod(IntPtr clazz, bool isGeneric, string method continue; var returnType = il2cpp_method_get_return_type(method); - var returnTypeNameActual = il2cpp_type_get_name_(returnType); + var returnTypeNameActual = il2cpp_type_get_name(returnType); if (returnTypeNameActual != returnTypeName) continue; @@ -131,7 +112,7 @@ public static IntPtr GetIl2CppMethod(IntPtr clazz, bool isGeneric, string method for (var i = 0; i < argTypes.Length; i++) { var paramType = il2cpp_method_get_param(method, (uint)i); - var typeName = il2cpp_type_get_name_(paramType); + var typeName = il2cpp_type_get_name(paramType); if (typeName != argTypes[i]) { badType = true; @@ -144,19 +125,19 @@ public static IntPtr GetIl2CppMethod(IntPtr clazz, bool isGeneric, string method return method; } - var className = il2cpp_class_get_name_(clazz); + var className = il2cpp_class_get_name(clazz); if (methodsSeen == 1) { Logger.Instance.LogTrace( "Method {ClassName}::{MethodName} was stubbed with a random matching method of the same name", className, methodName); Logger.Instance.LogTrace( - "Stubby return type/target: {LastMethod} / {ReturnTypeName}", il2cpp_type_get_name_(il2cpp_method_get_return_type(lastMethod)), returnTypeName); + "Stubby return type/target: {LastMethod} / {ReturnTypeName}", il2cpp_type_get_name(il2cpp_method_get_return_type(lastMethod)), returnTypeName); Logger.Instance.LogTrace("Stubby parameter types/targets follow:"); for (var i = 0; i < argTypes.Length; i++) { var paramType = il2cpp_method_get_param(lastMethod, (uint)i); - var typeName = il2cpp_type_get_name_(paramType); + var typeName = il2cpp_type_get_name(paramType); Logger.Instance.LogTrace(" {TypeName} / {ArgType}", typeName, argTypes[i]); } @@ -168,20 +149,20 @@ public static IntPtr GetIl2CppMethod(IntPtr clazz, bool isGeneric, string method foreach (var argType in argTypes) Logger.Instance.LogTrace(" {ArgType}", argType); Logger.Instance.LogTrace("Available methods of this name follow:"); - iter = IntPtr.Zero; - while ((method = il2cpp_class_get_methods(clazz, ref iter)) != IntPtr.Zero) + iter = nint.Zero; + while ((method = il2cpp_class_get_methods(clazz, ref iter)) != nint.Zero) { - if (il2cpp_method_get_name_(method) != methodName) + if (il2cpp_method_get_name(method) != methodName) continue; var nParams = il2cpp_method_get_param_count(method); Logger.Instance.LogTrace("Method starts"); Logger.Instance.LogTrace( - " return {MethodTypeName}", il2cpp_type_get_name_(il2cpp_method_get_return_type(method))); + " return {MethodTypeName}", il2cpp_type_get_name(il2cpp_method_get_return_type(method))); for (var i = 0; i < nParams; i++) { var paramType = il2cpp_method_get_param(method, (uint)i); - var typeName = il2cpp_type_get_name_(paramType); + var typeName = il2cpp_type_get_name(paramType); Logger.Instance.LogTrace(" {TypeName}", typeName); } @@ -192,75 +173,80 @@ public static IntPtr GetIl2CppMethod(IntPtr clazz, bool isGeneric, string method string.Join(", ", argTypes) + ")"); } - public static string? Il2CppStringToManaged(IntPtr il2CppString) + public static nint GetIl2CppGenericInstanceMethod(nint methodInfoPointer, nint declaringTypeClassPointer, params nint[] genericMethodArguments) { - if (il2CppString == IntPtr.Zero) return null; + // Ensure Il2CppSystem.RuntimeType is initialized before we call Il2CppSystem.Type.internal_from_handle + RuntimeHelpers.RunClassConstructor(typeof(Il2CppSystem.RuntimeType).TypeHandle); - var length = il2cpp_string_length(il2CppString); - var chars = il2cpp_string_chars(il2CppString); - - return new string(chars, 0, length); + var types = new Il2CppSystem.Type[genericMethodArguments.Length]; + for (var i = 0; i < genericMethodArguments.Length; i++) + { + types[i] = Il2CppSystem.Type.internal_from_handle(il2cpp_class_get_type(genericMethodArguments[i])); + } + var methodInfoObject = (Il2CppSystem.Reflection.MethodInfo)Il2CppObjectPool.Get(il2cpp_method_get_object(methodInfoPointer, declaringTypeClassPointer))!; + var methodInfoGeneric = methodInfoObject.MakeGenericMethod(types); + return il2cpp_method_get_from_reflection(methodInfoGeneric?.Pointer ?? throw new NullReferenceException()); } - public static IntPtr ManagedStringToIl2Cpp(string? str) + public static nint GetIl2CppGenericInstanceType(nint typeClassPointer, params nint[] genericTypeArguments) { - if (str == null) return IntPtr.Zero; + // Ensure Il2CppSystem.RuntimeType is initialized before we call Il2CppSystem.Type.internal_from_handle + RuntimeHelpers.RunClassConstructor(typeof(Il2CppSystem.RuntimeType).TypeHandle); - fixed (char* chars = str) + var types = new Il2CppSystem.Type[genericTypeArguments.Length]; + for (var i = 0; i < genericTypeArguments.Length; i++) { - return il2cpp_string_new_utf16(chars, str.Length); + types[i] = Il2CppSystem.Type.internal_from_handle(il2cpp_class_get_type(genericTypeArguments[i])); } + return il2cpp_class_from_type(Il2CppSystem.Type.internal_from_handle(il2cpp_class_get_type(typeClassPointer)).MakeGenericType(types).TypeHandle.value); } - public static IntPtr Il2CppObjectBaseToPtr(Il2CppObjectBase obj) - { - return obj?.Pointer ?? IntPtr.Zero; - } - - public static IntPtr Il2CppObjectBaseToPtrNotNull(Il2CppObjectBase obj) + public static ObjectPointer NewObjectPointer() { - return obj?.Pointer ?? throw new NullReferenceException(); + return (ObjectPointer)il2cpp_object_new(Il2CppClassPointerStore.NativeClassPointer); } - public static IntPtr GetIl2CppNestedType(IntPtr enclosingType, string nestedTypeName) + public static nint GetIl2CppNestedType(nint enclosingType, string nestedTypeName) { - if (enclosingType == IntPtr.Zero) return IntPtr.Zero; + if (enclosingType == nint.Zero) return nint.Zero; - var iter = IntPtr.Zero; - IntPtr nestedTypePtr; + var iter = nint.Zero; + nint nestedTypePtr; if (il2cpp_class_is_inflated(enclosingType)) { Logger.Instance.LogTrace("Original class was inflated, falling back to reflection"); - return RuntimeReflectionHelper.GetNestedTypeViaReflection(enclosingType, nestedTypeName); + return GetNestedTypeViaReflection(enclosingType, nestedTypeName); } - while ((nestedTypePtr = il2cpp_class_get_nested_types(enclosingType, ref iter)) != IntPtr.Zero) - if (il2cpp_class_get_name_(nestedTypePtr) == nestedTypeName) + while ((nestedTypePtr = il2cpp_class_get_nested_types(enclosingType, ref iter)) != nint.Zero) + if (il2cpp_class_get_name(nestedTypePtr) == nestedTypeName) return nestedTypePtr; Logger.Instance.LogError( - "Nested type {NestedTypeName} on {EnclosingTypeName} not found!", nestedTypeName, il2cpp_class_get_name_(enclosingType)); + "Nested type {NestedTypeName} on {EnclosingTypeName} not found!", nestedTypeName, il2cpp_class_get_name(enclosingType)); - return IntPtr.Zero; - } + return nint.Zero; - public static void ThrowIfNull(object arg) - { - if (arg == null) - throw new NullReferenceException(); + static IntPtr GetNestedTypeViaReflection(nint enclosingClass, string nestedTypeName) + { + var reflectionType = Il2CppSystem.Type.internal_from_handle(il2cpp_class_get_type(enclosingClass)); + var nestedType = reflectionType.GetNestedType(nestedTypeName, Il2CppSystem.Reflection.BindingFlags.Public | Il2CppSystem.Reflection.BindingFlags.NonPublic); + + return nestedType != null ? il2cpp_class_from_system_type(nestedType.Pointer) : IntPtr.Zero; + } } public static T ResolveICall(string signature) where T : Delegate { var icallPtr = il2cpp_resolve_icall(signature); - if (icallPtr == IntPtr.Zero) + if (icallPtr == nint.Zero) { Logger.Instance.LogTrace("ICall {Signature} not resolved", signature); return GenerateDelegateForMissingICall(signature); } - return Marshal.GetDelegateForFunctionPointer(icallPtr); + return GenerateDelegateForICall(icallPtr); } private static T GenerateDelegateForMissingICall(string signature) where T : Delegate @@ -272,776 +258,925 @@ private static T GenerateDelegateForMissingICall(string signature) where T : var bodyBuilder = trampoline.GetILGenerator(); bodyBuilder.Emit(OpCodes.Ldstr, $"ICall with signature {signature} was not resolved"); - bodyBuilder.Emit(OpCodes.Newobj, typeof(Exception).GetConstructor(new[] { typeof(string) })!); + bodyBuilder.Emit(OpCodes.Newobj, typeof(Exception).GetConstructor([typeof(string)])!); bodyBuilder.Emit(OpCodes.Throw); return (T)trampoline.CreateDelegate(typeof(T)); } - public static T? PointerToValueGeneric(IntPtr objectPointer, bool isFieldPointer, bool valueTypeWouldBeBoxed) + private static T GenerateDelegateForICall(nint icallPtr) where T : Delegate { - if (isFieldPointer) - { - if (il2cpp_class_is_valuetype(Il2CppClassPointerStore.NativeClassPtr)) - objectPointer = il2cpp_value_box(Il2CppClassPointerStore.NativeClassPtr, objectPointer); - else - objectPointer = *(IntPtr*)objectPointer; - } - - if (!valueTypeWouldBeBoxed && il2cpp_class_is_valuetype(Il2CppClassPointerStore.NativeClassPtr)) - objectPointer = il2cpp_value_box(Il2CppClassPointerStore.NativeClassPtr, objectPointer); + var invoke = typeof(T).GetMethod("Invoke")!; - if (typeof(T) == typeof(string)) - return (T)(object)Il2CppStringToManaged(objectPointer); + var trampoline = new DynamicMethod("(icall delegate) " + typeof(T).FullName, + invoke.ReturnType, invoke.GetParameters().Select(it => it.ParameterType).ToArray(), typeof(IL2CPP), true); + var bodyBuilder = trampoline.GetILGenerator(); - if (objectPointer == IntPtr.Zero) - return default; + var sizeOfMethod = typeof(Il2CppType).GetMethod(nameof(Il2CppType.SizeOf))!; + var parameters = invoke.GetParameters(); + var parameterTypes = new Type[parameters.Length]; + var locals = new LocalBuilder[parameters.Length]; + for (var i = 0; i < parameters.Length; i++) + { + var parameter = parameters[i]; + var parameterType = parameter.ParameterType; - if (typeof(T).IsValueType) - return Il2CppObjectBase.UnboxUnsafe(objectPointer); + // Parameter is a ByReference, and we need to get the underlying type T + var elementType = parameterType.GenericTypeArguments[0]; - return Il2CppObjectPool.Get(objectPointer); - } + var nativeStruct = TrampolineHelpers.NativeType(elementType); + parameterTypes[i] = nativeStruct; - public static string RenderTypeName(bool addRefMarker = false) - { - return RenderTypeName(typeof(T), addRefMarker); - } + var nativeLocal = bodyBuilder.DeclareLocal(nativeStruct); + locals[i] = nativeLocal; - public static string RenderTypeName(Type t, bool addRefMarker = false) - { - if (addRefMarker) return RenderTypeName(t) + "&"; - if (t.IsArray) return RenderTypeName(t.GetElementType()) + "[]"; - if (t.IsByRef) return RenderTypeName(t.GetElementType()) + "&"; - if (t.IsPointer) return RenderTypeName(t.GetElementType()) + "*"; - if (t.IsGenericParameter) return t.Name; + bodyBuilder.Emit(OpCodes.Ldarga, i); + bodyBuilder.Emit(OpCodes.Ldloca, nativeLocal); + bodyBuilder.Emit(OpCodes.Call, parameterType.GetMethod(nameof(ByReference<>.CopyToUnmanaged))!.MakeGenericMethod(nativeStruct)); + } - if (t.IsGenericType) + foreach (var local in locals) { - if (t.TypeHasIl2CppArrayBase()) - return RenderTypeName(t.GetGenericArguments()[0]) + "[]"; - - var builder = new StringBuilder(); - builder.Append(t.GetGenericTypeDefinition().FullNameObfuscated().TrimIl2CppPrefix()); - builder.Append('<'); - var genericArguments = t.GetGenericArguments(); - for (var i = 0; i < genericArguments.Length; i++) - { - if (i != 0) builder.Append(','); - builder.Append(RenderTypeName(genericArguments[i])); - } - - builder.Append('>'); - return builder.ToString(); + bodyBuilder.Emit(OpCodes.Ldloc, local); } - if (t == typeof(Il2CppStringArray)) - return "System.String[]"; + bodyBuilder.Emit(OpCodes.Ldc_I8, icallPtr); + bodyBuilder.Emit(OpCodes.Conv_I); - return t.FullNameObfuscated().TrimIl2CppPrefix(); - } + if (invoke.ReturnType == typeof(void)) + { + bodyBuilder.EmitCalli(OpCodes.Calli, CallingConventions.Standard, typeof(void), parameterTypes, null); + } + else + { + var returnType = invoke.ReturnType; + var nativeStruct = TrampolineHelpers.NativeType(returnType); - private static string FullNameObfuscated(this Type t) - { - var obfuscatedNameAnnotations = t.GetCustomAttribute(); - if (obfuscatedNameAnnotations == null) return t.FullName; - return obfuscatedNameAnnotations.ObfuscatedName; - } + bodyBuilder.EmitCalli(OpCodes.Calli, CallingConventions.Standard, nativeStruct, parameterTypes, null); - private static string TrimIl2CppPrefix(this string s) - { - return s.StartsWith("Il2Cpp") ? s.Substring("Il2Cpp".Length) : s; - } + var returnLocal = bodyBuilder.DeclareLocal(nativeStruct); + bodyBuilder.Emit(OpCodes.Stloc, returnLocal); - private static bool TypeHasIl2CppArrayBase(this Type type) - { - if (type == null) return false; - if (type.IsConstructedGenericType) type = type.GetGenericTypeDefinition(); - if (type == typeof(Il2CppArrayBase<>)) return true; - return TypeHasIl2CppArrayBase(type.BaseType); - } + bodyBuilder.Emit(OpCodes.Ldloca, returnLocal); + bodyBuilder.Emit(OpCodes.Call, typeof(Il2CppType).GetMethod(nameof(Il2CppType.ReadFromPointer))!.MakeGenericMethod(returnType)); + } + bodyBuilder.Emit(OpCodes.Ret); - // this is called if there's no actual il2cpp_gc_wbarrier_set_field() - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void FieldWriteWbarrierStub(IntPtr obj, IntPtr targetAddress, IntPtr value) - { - // ignore obj - *(IntPtr*)targetAddress = value; + return (T)trampoline.CreateDelegate(typeof(T)); } // IL2CPP Functions - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_init(IntPtr domain_name); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_init(nint domain_name); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_init_utf16(IntPtr domain_name); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_init_utf16(nint domain_name); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_shutdown(); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_shutdown(); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_set_config_dir(IntPtr config_path); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_set_config_dir(nint config_path); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_set_data_dir(IntPtr data_path); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_set_data_dir(nint data_path); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_set_temp_dir(IntPtr temp_path); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_set_temp_dir(nint temp_path); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_set_commandline_arguments(int argc, IntPtr argv, IntPtr basedir); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_set_commandline_arguments(int argc, nint argv, nint basedir); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_set_commandline_arguments_utf16(int argc, IntPtr argv, IntPtr basedir); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_set_commandline_arguments_utf16(int argc, nint argv, nint basedir); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_set_config_utf16(IntPtr executablePath); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_set_config_utf16(nint executablePath); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_set_config(IntPtr executablePath); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_set_config(nint executablePath); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_set_memory_callbacks(IntPtr callbacks); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_set_memory_callbacks(nint callbacks); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_get_corlib(); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_get_corlib(); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_add_internal_call(IntPtr name, IntPtr method); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_add_internal_call(nint name, nint method); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_resolve_icall([MarshalAs(UnmanagedType.LPStr)] string name); + [LibraryImport("GameAssembly", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(System.Runtime.InteropServices.Marshalling.AnsiStringMarshaller))] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_resolve_icall([MarshalAs(UnmanagedType.LPStr)] string name); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_alloc(uint size); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_alloc(uint size); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_free(IntPtr ptr); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_free(nint ptr); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_array_class_get(IntPtr element_class, uint rank); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_array_class_get(nint element_class, uint rank); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern uint il2cpp_array_length(IntPtr array); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint il2cpp_array_length(nint array); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern uint il2cpp_array_get_byte_length(IntPtr array); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint il2cpp_array_get_byte_length(nint array); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_array_new(IntPtr elementTypeInfo, ulong length); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_array_new(nint elementTypeInfo, ulong length); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_array_new_specific(IntPtr arrayTypeInfo, ulong length); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_array_new_specific(nint arrayTypeInfo, ulong length); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_array_new_full(IntPtr array_class, ref ulong lengths, ref ulong lower_bounds); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_array_new_full(nint array_class, ulong* lengths, ulong* lower_bounds); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_bounded_array_class_get(IntPtr element_class, uint rank, - [MarshalAs(UnmanagedType.I1)] bool bounded); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_bounded_array_class_get(nint element_class, uint rank, [MarshalAs(UnmanagedType.I1)] bool bounded); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern int il2cpp_array_element_size(IntPtr array_class); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int il2cpp_array_element_size(nint array_class); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_assembly_get_image(IntPtr assembly); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_assembly_get_image(nint assembly); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_enum_basetype(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_enum_basetype(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_class_is_generic(IntPtr klass); + public static partial bool il2cpp_class_is_generic(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_class_is_inflated(IntPtr klass); + public static partial bool il2cpp_class_is_inflated(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_class_is_assignable_from(IntPtr klass, IntPtr oklass); + public static partial bool il2cpp_class_is_assignable_from(nint klass, nint oklass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_class_is_subclass_of(IntPtr klass, IntPtr klassc, - [MarshalAs(UnmanagedType.I1)] bool check_interfaces); + public static partial bool il2cpp_class_is_subclass_of(nint klass, nint klassc, [MarshalAs(UnmanagedType.I1)] bool check_interfaces); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_class_has_parent(IntPtr klass, IntPtr klassc); - - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_from_il2cpp_type(IntPtr type); + public static partial bool il2cpp_class_has_parent(nint klass, nint klassc); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_from_name(IntPtr image, [MarshalAs(UnmanagedType.LPUTF8Str)] string namespaze, - [MarshalAs(UnmanagedType.LPUTF8Str)] string name); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_from_il2cpp_type(nint type); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_from_system_type(IntPtr type); + [LibraryImport("GameAssembly", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(System.Runtime.InteropServices.Marshalling.AnsiStringMarshaller))] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_from_name(nint image, [MarshalAs(UnmanagedType.LPUTF8Str)] string namespaze, [MarshalAs(UnmanagedType.LPUTF8Str)] string name); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_element_class(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_from_system_type(nint type); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_events(IntPtr klass, ref IntPtr iter); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_element_class(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_fields(IntPtr klass, ref IntPtr iter); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_events(nint klass, ref nint iter); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_nested_types(IntPtr klass, ref IntPtr iter); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_fields(nint klass, ref nint iter); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_interfaces(IntPtr klass, ref IntPtr iter); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_nested_types(nint klass, ref nint iter); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_properties(IntPtr klass, ref IntPtr iter); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_interfaces(nint klass, ref nint iter); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_property_from_name(IntPtr klass, IntPtr name); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_properties(nint klass, ref nint iter); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_field_from_name(IntPtr klass, - [MarshalAs(UnmanagedType.LPUTF8Str)] string name); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_property_from_name(nint klass, nint name); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_methods(IntPtr klass, ref IntPtr iter); + [LibraryImport("GameAssembly", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(System.Runtime.InteropServices.Marshalling.AnsiStringMarshaller))] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_field_from_name(nint klass, [MarshalAs(UnmanagedType.LPUTF8Str)] string name); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_method_from_name(IntPtr klass, - [MarshalAs(UnmanagedType.LPUTF8Str)] string name, int argsCount); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_methods(nint klass, ref nint iter); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern nint il2cpp_class_get_name(IntPtr klass); + [LibraryImport("GameAssembly", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(System.Runtime.InteropServices.Marshalling.AnsiStringMarshaller))] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_method_from_name(nint klass, [MarshalAs(UnmanagedType.LPUTF8Str)] string name, int argsCount); - public static string? il2cpp_class_get_name_(IntPtr klass) - => Marshal.PtrToStringUTF8(il2cpp_class_get_name(klass)); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalAs(UnmanagedType.LPUTF8Str)] + public static partial string il2cpp_class_get_name(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern nint il2cpp_class_get_namespace(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalAs(UnmanagedType.LPUTF8Str)] + public static partial string il2cpp_class_get_namespace(nint klass); - public static string? il2cpp_class_get_namespace_(IntPtr klass) - => Marshal.PtrToStringUTF8(il2cpp_class_get_namespace(klass)); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_parent(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_parent(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_declaring_type(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_declaring_type(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int il2cpp_class_instance_size(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern int il2cpp_class_instance_size(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint il2cpp_class_num_fields(nint enumKlass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern uint il2cpp_class_num_fields(IntPtr enumKlass); - - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_class_is_valuetype(IntPtr klass); + public static partial bool il2cpp_class_is_valuetype(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern int il2cpp_class_value_size(IntPtr klass, ref uint align); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int il2cpp_class_value_size(nint klass, ref uint align); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_class_is_blittable(IntPtr klass); + public static partial bool il2cpp_class_is_blittable(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern int il2cpp_class_get_flags(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int il2cpp_class_get_flags(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_class_is_abstract(IntPtr klass); + public static partial bool il2cpp_class_is_abstract(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_class_is_interface(IntPtr klass); + public static partial bool il2cpp_class_is_interface(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern int il2cpp_class_array_element_size(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int il2cpp_class_array_element_size(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_from_type(IntPtr type); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_from_type(nint type); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_type(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_type(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern uint il2cpp_class_get_type_token(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint il2cpp_class_get_type_token(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_class_has_attribute(IntPtr klass, IntPtr attr_class); + public static partial bool il2cpp_class_has_attribute(nint klass, nint attr_class); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_class_has_references(IntPtr klass); + public static partial bool il2cpp_class_has_references(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_class_is_enum(IntPtr klass); + public static partial bool il2cpp_class_is_enum(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_class_get_image(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_image(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern nint il2cpp_class_get_assemblyname(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_class_get_assemblyname(nint klass); - public static string? il2cpp_class_get_assemblyname_(IntPtr klass) + public static string? il2cpp_class_get_assemblyname_(nint klass) => Marshal.PtrToStringUTF8(il2cpp_class_get_assemblyname(klass)); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern int il2cpp_class_get_rank(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int il2cpp_class_get_rank(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern uint il2cpp_class_get_bitmap_size(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint il2cpp_class_get_bitmap_size(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_class_get_bitmap(IntPtr klass, ref uint bitmap); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_class_get_bitmap(nint klass, ref uint bitmap); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_stats_dump_to_file(IntPtr path); + public static partial bool il2cpp_stats_dump_to_file(nint path); + + //[LibraryImport("GameAssembly")] + //[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + //public static partial ulong il2cpp_stats_get_value(IL2CPP_Stat stat); - //[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - //public extern static ulong il2cpp_stats_get_value(IL2CPP_Stat stat); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_domain_get(); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_domain_get(); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_domain_assembly_open(IntPtr domain, IntPtr name); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_domain_assembly_open(nint domain, nint name); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr* il2cpp_domain_get_assemblies(IntPtr domain, ref uint size); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint* il2cpp_domain_get_assemblies(nint domain, ref uint size); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr - il2cpp_exception_from_name_msg(IntPtr image, IntPtr name_space, IntPtr name, IntPtr msg); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_raise_exception(nint ex); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_get_exception_argument_null(IntPtr arg); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_exception_from_name_msg(nint image, nint name_space, nint name, nint msg); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_format_exception(IntPtr ex, void* message, int message_size); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_get_exception_argument_null(nint arg); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_format_stack_trace(IntPtr ex, void* output, int output_size); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_format_exception(nint ex, void* message, int message_size); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_unhandled_exception(IntPtr ex); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_format_stack_trace(nint ex, void* output, int output_size); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern int il2cpp_field_get_flags(IntPtr field); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_unhandled_exception(nint ex); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern nint il2cpp_field_get_name(IntPtr field); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int il2cpp_field_get_flags(nint field); - public static string? il2cpp_field_get_name_(IntPtr field) - => Marshal.PtrToStringUTF8(il2cpp_field_get_name(field)); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalAs(UnmanagedType.LPUTF8Str)] + public static partial string il2cpp_field_get_name(nint field); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_field_get_parent(IntPtr field); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_field_get_parent(nint field); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern uint il2cpp_field_get_offset(IntPtr field); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint il2cpp_field_get_offset(nint field); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_field_get_type(IntPtr field); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_field_get_type(nint field); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_field_get_value(IntPtr obj, IntPtr field, void* value); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_field_get_value(nint obj, nint field, void* value); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_field_get_value_object(IntPtr field, IntPtr obj); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_field_get_value_object(nint field, nint obj); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_field_has_attribute(IntPtr field, IntPtr attr_class); + public static partial bool il2cpp_field_has_attribute(nint field, nint attr_class); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_field_set_value(IntPtr obj, IntPtr field, void* value); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_field_set_value(nint obj, nint field, void* value); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_field_static_get_value(IntPtr field, void* value); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_field_static_get_value(nint field, void* value); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_field_static_set_value(IntPtr field, void* value); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_field_static_set_value(nint field, void* value); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_field_set_value_object(IntPtr instance, IntPtr field, IntPtr value); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_field_set_value_object(nint instance, nint field, nint value); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_gc_collect(int maxGenerations); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_gc_collect(int maxGenerations); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern int il2cpp_gc_collect_a_little(); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int il2cpp_gc_collect_a_little(); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_gc_disable(); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_gc_disable(); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_gc_enable(); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_gc_enable(); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_gc_is_disabled(); + public static partial bool il2cpp_gc_is_disabled(); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern long il2cpp_gc_get_used_size(); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial long il2cpp_gc_get_used_size(); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern long il2cpp_gc_get_heap_size(); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial long il2cpp_gc_get_heap_size(); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_gc_wbarrier_set_field(IntPtr obj, IntPtr targetAddress, IntPtr gcObj); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_gc_wbarrier_set_field(nint obj, nint targetAddress, nint gcObj); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern nint il2cpp_gchandle_new(IntPtr obj, [MarshalAs(UnmanagedType.I1)] bool pinned); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_gchandle_new(nint obj, [MarshalAs(UnmanagedType.I1)] bool pinned); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern nint il2cpp_gchandle_new_weakref(IntPtr obj, - [MarshalAs(UnmanagedType.I1)] bool track_resurrection); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_gchandle_new_weakref(nint obj, [MarshalAs(UnmanagedType.I1)] bool track_resurrection); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_gchandle_get_target(nint gchandle); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_gchandle_get_target(nint gchandle); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_gchandle_free(nint gchandle); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_gchandle_free(nint gchandle); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_unity_liveness_calculation_begin(IntPtr filter, int max_object_count, - IntPtr callback, IntPtr userdata, IntPtr onWorldStarted, IntPtr onWorldStopped); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_unity_liveness_calculation_begin(nint filter, int max_object_count, nint callback, nint userdata, nint onWorldStarted, nint onWorldStopped); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_unity_liveness_calculation_end(IntPtr state); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_unity_liveness_calculation_end(nint state); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_unity_liveness_calculation_from_root(IntPtr root, IntPtr state); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_unity_liveness_calculation_from_root(nint root, nint state); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_unity_liveness_calculation_from_statics(IntPtr state); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_unity_liveness_calculation_from_statics(nint state); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_method_get_return_type(IntPtr method); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_method_get_return_type(nint method); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_method_get_declaring_type(IntPtr method); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_method_get_declaring_type(nint method); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern nint il2cpp_method_get_name(IntPtr method); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalAs(UnmanagedType.LPUTF8Str)] + public static partial string il2cpp_method_get_name(nint method); - public static string? il2cpp_method_get_name_(IntPtr method) - => Marshal.PtrToStringUTF8(il2cpp_method_get_name(method)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr il2cpp_method_get_from_reflection(IntPtr method) + public static nint il2cpp_method_get_from_reflection(nint method) { if (UnityVersionHandler.HasGetMethodFromReflection) return _il2cpp_method_get_from_reflection(method); Il2CppReflectionMethod* reflectionMethod = (Il2CppReflectionMethod*)method; - return (IntPtr)reflectionMethod->method; + return (nint)reflectionMethod->method; } - [DllImport("GameAssembly", EntryPoint = nameof(il2cpp_method_get_from_reflection), CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - private static extern IntPtr _il2cpp_method_get_from_reflection(IntPtr method); + [LibraryImport("GameAssembly", EntryPoint = "il2cpp_method_get_from_reflection")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + private static partial nint _il2cpp_method_get_from_reflection(nint method); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_method_get_object(IntPtr method, IntPtr refclass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_method_get_object(nint method, nint refclass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_method_is_generic(IntPtr method); + public static partial bool il2cpp_method_is_generic(nint method); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_method_is_inflated(IntPtr method); + public static partial bool il2cpp_method_is_inflated(nint method); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_method_is_instance(IntPtr method); + public static partial bool il2cpp_method_is_instance(nint method); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern uint il2cpp_method_get_param_count(IntPtr method); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint il2cpp_method_get_param_count(nint method); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_method_get_param(IntPtr method, uint index); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_method_get_param(nint method, uint index); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_method_get_class(IntPtr method); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_method_get_class(nint method); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_method_has_attribute(IntPtr method, IntPtr attr_class); - - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern uint il2cpp_method_get_flags(IntPtr method, ref uint iflags); + public static partial bool il2cpp_method_has_attribute(nint method, nint attr_class); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern uint il2cpp_method_get_token(IntPtr method); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint il2cpp_method_get_flags(nint method, ref uint iflags); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern nint il2cpp_method_get_param_name(IntPtr method, uint index); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint il2cpp_method_get_token(nint method); - public static string? il2cpp_method_get_param_name_(IntPtr method, uint index) - => Marshal.PtrToStringUTF8(il2cpp_method_get_param_name(method, index)); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalAs(UnmanagedType.LPUTF8Str)] + public static partial string il2cpp_method_get_param_name(nint method, uint index); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_profiler_install(IntPtr prof, IntPtr shutdown_callback); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_profiler_install(nint prof, nint shutdown_callback); - // [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - // public extern static void il2cpp_profiler_set_events(IL2CPP_ProfileFlags events); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_profiler_install_enter_leave(IntPtr enter, IntPtr fleave); + //[LibraryImport("GameAssembly")] + //[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + //public static partial void il2cpp_profiler_set_events(IL2CPP_ProfileFlags events); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_profiler_install_allocation(IntPtr callback); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_profiler_install_enter_leave(nint enter, nint fleave); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_profiler_install_gc(IntPtr callback, IntPtr heap_resize_callback); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_profiler_install_allocation(nint callback); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_profiler_install_fileio(IntPtr callback); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_profiler_install_gc(nint callback, nint heap_resize_callback); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_profiler_install_thread(IntPtr start, IntPtr end); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_profiler_install_fileio(nint callback); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern uint il2cpp_property_get_flags(IntPtr prop); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_profiler_install_thread(nint start, nint end); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_property_get_get_method(IntPtr prop); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint il2cpp_property_get_flags(nint prop); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_property_get_set_method(IntPtr prop); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_property_get_get_method(nint prop); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern nint il2cpp_property_get_name(IntPtr prop); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_property_get_set_method(nint prop); - public static string? il2cpp_property_get_name_(IntPtr prop) - => Marshal.PtrToStringUTF8(il2cpp_property_get_name(prop)); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalAs(UnmanagedType.LPUTF8Str)] + public static partial string il2cpp_property_get_name(nint prop); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_property_get_parent(IntPtr prop); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_property_get_parent(nint prop); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_object_get_class(IntPtr obj); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_object_get_class(nint obj); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern uint il2cpp_object_get_size(IntPtr obj); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint il2cpp_object_get_size(nint obj); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_object_get_virtual_method(IntPtr obj, IntPtr method); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_object_get_virtual_method(nint obj, nint method); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_object_new(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_object_new(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_object_unbox(IntPtr obj); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_object_unbox(nint obj); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_value_box(IntPtr klass, IntPtr data); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_value_box(nint klass, nint data); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_monitor_enter(IntPtr obj); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_monitor_enter(nint obj); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_monitor_try_enter(IntPtr obj, uint timeout); + public static partial bool il2cpp_monitor_try_enter(nint obj, uint timeout); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_monitor_exit(IntPtr obj); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_monitor_exit(nint obj); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_monitor_pulse(IntPtr obj); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_monitor_pulse(nint obj); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_monitor_pulse_all(IntPtr obj); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_monitor_pulse_all(nint obj); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_monitor_wait(IntPtr obj); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_monitor_wait(nint obj); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_monitor_try_wait(IntPtr obj, uint timeout); + public static partial bool il2cpp_monitor_try_wait(nint obj, uint timeout); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_runtime_invoke(IntPtr method, IntPtr obj, void** param, ref IntPtr exc); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_runtime_invoke(nint method, nint obj, void** param, ref nint exc); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] // param can be of Il2CppObject* - public static extern IntPtr il2cpp_runtime_invoke_convert_args(IntPtr method, IntPtr obj, void** param, - int paramCount, ref IntPtr exc); + public static partial nint il2cpp_runtime_invoke_convert_args(nint method, nint obj, void** param, int paramCount, ref nint exc); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_runtime_class_init(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_runtime_class_init(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_runtime_object_init(IntPtr obj); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_runtime_object_init(nint obj); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_runtime_object_init_exception(IntPtr obj, ref IntPtr exc); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_runtime_object_init_exception(nint obj, ref nint exc); - // [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - // public extern static void il2cpp_runtime_unhandled_exception_policy_set(IL2CPP_RuntimeUnhandledExceptionPolicy value); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern int il2cpp_string_length(IntPtr str); + //[LibraryImport("GameAssembly")] + //[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + //public static partial void il2cpp_runtime_unhandled_exception_policy_set(IL2CPP_RuntimeUnhandledExceptionPolicy value); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern char* il2cpp_string_chars(IntPtr str); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int il2cpp_string_length(nint str); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_string_new(string str); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial char* il2cpp_string_chars(nint str); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_string_new_len(string str, uint length); + [LibraryImport("GameAssembly", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(System.Runtime.InteropServices.Marshalling.AnsiStringMarshaller))] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_string_new(string str); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_string_new_utf16(char* text, int len); + [LibraryImport("GameAssembly", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(System.Runtime.InteropServices.Marshalling.AnsiStringMarshaller))] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_string_new_len(string str, uint length); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_string_new_wrapper(string str); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_string_new_utf16(char* text, int len); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_string_intern(string str); + [LibraryImport("GameAssembly", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(System.Runtime.InteropServices.Marshalling.AnsiStringMarshaller))] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_string_new_wrapper(string str); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_string_is_interned(string str); + [LibraryImport("GameAssembly", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(System.Runtime.InteropServices.Marshalling.AnsiStringMarshaller))] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_string_intern(string str); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_thread_current(); + [LibraryImport("GameAssembly", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(System.Runtime.InteropServices.Marshalling.AnsiStringMarshaller))] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_string_is_interned(string str); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_thread_attach(IntPtr domain); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_thread_current(); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_thread_detach(IntPtr thread); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_thread_attach(nint domain); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void** il2cpp_thread_get_all_attached_threads(ref uint size); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_thread_detach(nint thread); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void** il2cpp_thread_get_all_attached_threads(ref uint size); + + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_is_vm_thread(IntPtr thread); + public static partial bool il2cpp_is_vm_thread(nint thread); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_current_thread_walk_frame_stack(IntPtr func, IntPtr user_data); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_current_thread_walk_frame_stack(nint func, nint user_data); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_thread_walk_frame_stack(IntPtr thread, IntPtr func, IntPtr user_data); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_thread_walk_frame_stack(nint thread, nint func, nint user_data); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_current_thread_get_top_frame(IntPtr frame); + public static partial bool il2cpp_current_thread_get_top_frame(nint frame); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_thread_get_top_frame(IntPtr thread, IntPtr frame); + public static partial bool il2cpp_thread_get_top_frame(nint thread, nint frame); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_current_thread_get_frame_at(int offset, IntPtr frame); + public static partial bool il2cpp_current_thread_get_frame_at(int offset, nint frame); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_thread_get_frame_at(IntPtr thread, int offset, IntPtr frame); - - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern int il2cpp_current_thread_get_stack_depth(); + public static partial bool il2cpp_thread_get_frame_at(nint thread, int offset, nint frame); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern int il2cpp_thread_get_stack_depth(IntPtr thread); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int il2cpp_current_thread_get_stack_depth(); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_type_get_object(IntPtr type); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int il2cpp_thread_get_stack_depth(nint thread); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern int il2cpp_type_get_type(IntPtr type); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_type_get_object(nint type); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_type_get_class_or_element_class(IntPtr type); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int il2cpp_type_get_type(nint type); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern nint il2cpp_type_get_name(IntPtr type); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_type_get_class_or_element_class(nint type); - public static string? il2cpp_type_get_name_(IntPtr type) - => Marshal.PtrToStringUTF8(il2cpp_type_get_name(type)); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalAs(UnmanagedType.LPUTF8Str)] + public static partial string il2cpp_type_get_name(nint type); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_type_is_byref(IntPtr type); + public static partial bool il2cpp_type_is_byref(nint type); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern uint il2cpp_type_get_attrs(IntPtr type); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint il2cpp_type_get_attrs(nint type); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_type_equals(IntPtr type, IntPtr otherType); - - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_type_get_assembly_qualified_name(IntPtr type); - - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_image_get_assembly(IntPtr image); + public static partial bool il2cpp_type_equals(nint type, nint otherType); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern nint il2cpp_image_get_name(IntPtr image); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_type_get_assembly_qualified_name(nint type); - public static string? il2cpp_image_get_name_(IntPtr image) - => Marshal.PtrToStringUTF8(il2cpp_image_get_name(image)); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_image_get_assembly(nint image); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern nint il2cpp_image_get_filename(IntPtr image); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalAs(UnmanagedType.LPUTF8Str)] + public static partial string il2cpp_image_get_name(nint image); - public static string? il2cpp_image_get_filename_(IntPtr image) - => Marshal.PtrToStringUTF8(il2cpp_image_get_filename(image)); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + [return: MarshalAs(UnmanagedType.LPUTF8Str)] + public static partial string il2cpp_image_get_filename(nint image); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_image_get_entry_point(IntPtr image); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_image_get_entry_point(nint image); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern uint il2cpp_image_get_class_count(IntPtr image); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial uint il2cpp_image_get_class_count(nint image); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_image_get_class(IntPtr image, uint index); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_image_get_class(nint image, uint index); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_capture_memory_snapshot(); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_capture_memory_snapshot(); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_free_captured_memory_snapshot(IntPtr snapshot); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_free_captured_memory_snapshot(nint snapshot); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_set_find_plugin_callback(IntPtr method); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_set_find_plugin_callback(nint method); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_register_log_callback(IntPtr method); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_register_log_callback(nint method); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_debugger_set_agent_options(IntPtr options); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_debugger_set_agent_options(nint options); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_is_debugger_attached(); + public static partial bool il2cpp_is_debugger_attached(); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_unity_install_unitytls_interface(void* unitytlsInterfaceStruct); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_unity_install_unitytls_interface(void* unitytlsInterfaceStruct); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_custom_attrs_from_class(IntPtr klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_custom_attrs_from_class(nint klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_custom_attrs_from_method(IntPtr method); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_custom_attrs_from_method(nint method); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_custom_attrs_get_attr(IntPtr ainfo, IntPtr attr_klass); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_custom_attrs_get_attr(nint ainfo, nint attr_klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I1)] - public static extern bool il2cpp_custom_attrs_has_attr(IntPtr ainfo, IntPtr attr_klass); + public static partial bool il2cpp_custom_attrs_has_attr(nint ainfo, nint attr_klass); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_custom_attrs_construct(IntPtr cinfo); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial nint il2cpp_custom_attrs_construct(nint cinfo); - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern void il2cpp_custom_attrs_free(IntPtr ainfo); + [LibraryImport("GameAssembly")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial void il2cpp_custom_attrs_free(nint ainfo); } diff --git a/Il2CppInterop.Runtime/Il2CppClassPointerStore.cs b/Il2CppInterop.Runtime/Il2CppClassPointerStore.cs index b75da661..b27c6970 100644 --- a/Il2CppInterop.Runtime/Il2CppClassPointerStore.cs +++ b/Il2CppInterop.Runtime/Il2CppClassPointerStore.cs @@ -1,79 +1,51 @@ -using System; -using System.Linq; using System.Runtime.CompilerServices; -using Il2CppInterop.Common.Attributes; -using Il2CppInterop.Runtime.Attributes; -using String = Il2CppSystem.String; -using Void = Il2CppSystem.Void; +using Il2CppSystem; namespace Il2CppInterop.Runtime; public static class Il2CppClassPointerStore { - public static IntPtr GetNativeClassPointer(Type type) + public static nint GetNativeClassPointer(System.Type type) { - if (type == typeof(void)) return Il2CppClassPointerStore.NativeClassPtr; - if (type == typeof(String)) return Il2CppClassPointerStore.NativeClassPtr; - return (IntPtr)typeof(Il2CppClassPointerStore<>) + if (type == typeof(void)) + return Il2CppClassPointerStore.NativeClassPointer; + + return (nint)typeof(Il2CppClassPointerStore<>) .MakeGenericType(type) - .GetField(nameof(Il2CppClassPointerStore.NativeClassPtr)) - .GetValue(null); + .GetField(nameof(Il2CppClassPointerStore<>.NativeClassPointer))! + .GetValue(null)!; } - internal static void SetNativeClassPointer(Type type, IntPtr value) + internal static void SetNativeClassPointer(System.Type type, nint value) { typeof(Il2CppClassPointerStore<>) .MakeGenericType(type) - .GetField(nameof(Il2CppClassPointerStore.NativeClassPtr)) + .GetField(nameof(Il2CppClassPointerStore<>.NativeClassPointer))! .SetValue(null, value); } } public static class Il2CppClassPointerStore { - public static IntPtr NativeClassPtr; - public static Type CreatedTypeRedirect; + public static nint NativeClassPointer; static Il2CppClassPointerStore() { - var targetType = typeof(T); - if (!targetType.IsEnum) + if (typeof(T) == typeof(IObject)) { - RuntimeHelpers.RunClassConstructor(targetType.TypeHandle); + NativeClassPointer = Il2CppClassPointerStore.NativeClassPointer; } - else + else if (typeof(T) == typeof(IValueType)) { - var assemblyName = targetType.Module.Name; - var @namespace = targetType.Namespace ?? ""; - var name = targetType.Name; - foreach (var customAttribute in targetType.CustomAttributes) - { - if (customAttribute.AttributeType != typeof(OriginalNameAttribute)) continue; - assemblyName = (string)customAttribute.ConstructorArguments[0].Value; - @namespace = (string)customAttribute.ConstructorArguments[1].Value; - name = (string)customAttribute.ConstructorArguments[2].Value; - } - - if (targetType.IsNested) - NativeClassPtr = - IL2CPP.GetIl2CppNestedType(Il2CppClassPointerStore.GetNativeClassPointer(targetType.DeclaringType), - name); - else - NativeClassPtr = - IL2CPP.GetIl2CppClass(assemblyName, @namespace, name); + NativeClassPointer = Il2CppClassPointerStore.NativeClassPointer; } - - if (targetType.IsPrimitive || targetType == typeof(string)) - RuntimeHelpers.RunClassConstructor(AppDomain.CurrentDomain.GetAssemblies() - .Single(it => it.GetName().Name == "Il2Cppmscorlib").GetType("Il2Cpp" + targetType.FullName) - .TypeHandle); - - foreach (var customAttribute in targetType.CustomAttributes) + else if (typeof(T) == typeof(IEnum)) { - if (customAttribute.AttributeType != typeof(AlsoInitializeAttribute)) continue; - - var linkedType = (Type)customAttribute.ConstructorArguments[0].Value; - RuntimeHelpers.RunClassConstructor(linkedType.TypeHandle); + NativeClassPointer = Il2CppClassPointerStore.NativeClassPointer; + } + else + { + RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); } } } diff --git a/Il2CppInterop.Runtime/Il2CppException.cs b/Il2CppInterop.Runtime/Il2CppException.cs deleted file mode 100644 index 3411ae98..00000000 --- a/Il2CppInterop.Runtime/Il2CppException.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Text; - -namespace Il2CppInterop.Runtime; - -public class Il2CppException : Exception -{ - [ThreadStatic] private static byte[] ourMessageBytes; - - public static Func ParseMessageHook; - - public Il2CppException(IntPtr exception) : base(BuildMessage(exception)) - { - } - - private static unsafe string BuildMessage(IntPtr exception) - { - if (ParseMessageHook != null) return ParseMessageHook(exception); - ourMessageBytes ??= new byte[65536]; - fixed (byte* message = ourMessageBytes) - { - IL2CPP.il2cpp_format_exception(exception, message, ourMessageBytes.Length); - } - - var builtMessage = Encoding.UTF8.GetString(ourMessageBytes, 0, Array.IndexOf(ourMessageBytes, (byte)0)); - Il2CppSystem.Exception il2cppException = new(exception); - return builtMessage + "\n" + - "--- BEGIN IL2CPP STACK TRACE ---\n" + - $"{il2cppException.ToString(false, true)}\n" + - "--- END IL2CPP STACK TRACE ---\n"; - } - - public static void RaiseExceptionIfNecessary(IntPtr returnedException) - { - if (returnedException == IntPtr.Zero) return; - throw new Il2CppException(returnedException); - } -} diff --git a/Il2CppInterop.Runtime/Il2CppInterop.Runtime.csproj b/Il2CppInterop.Runtime/Il2CppInterop.Runtime.csproj index 6fda3574..68acc89a 100644 --- a/Il2CppInterop.Runtime/Il2CppInterop.Runtime.csproj +++ b/Il2CppInterop.Runtime/Il2CppInterop.Runtime.csproj @@ -4,13 +4,12 @@ Il2CppInterop.Runtime knah, BepInEx et al. Runtime tools for bridging .NET and Il2Cpp together - net6.0 + net10.0 true - Il2CppInterop.Runtime Debug;Release AnyCPU false - Latest + preview enable @@ -21,25 +20,14 @@ - - false - Libs\Il2Cppmscorlib.dll - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + diff --git a/Il2CppInterop.Runtime/Il2CppToMonoDelegateReference.cs b/Il2CppInterop.Runtime/Il2CppToMonoDelegateReference.cs new file mode 100644 index 00000000..47f58a93 --- /dev/null +++ b/Il2CppInterop.Runtime/Il2CppToMonoDelegateReference.cs @@ -0,0 +1,34 @@ +using System; +using System.Runtime.InteropServices; +using Il2CppInterop.Common; +using Il2CppInterop.Common.Attributes; +using Microsoft.Extensions.Logging; + +namespace Il2CppInterop.Runtime; + +[InjectedType(Assembly = "Assembly-CSharp.dll")] +internal sealed partial class Il2CppToMonoDelegateReference : Object +{ + [Il2CppField] + public partial Il2CppSystem.IntPtr MethodInfo { get; set; } + [ManagedField] + public partial Delegate ReferencedDelegate { get; set; } + + public Il2CppToMonoDelegateReference(Delegate referencedDelegate, IntPtr methodInfo) : this(IL2CPP.NewObjectPointer()) + { + ReferencedDelegate = referencedDelegate; + MethodInfo = methodInfo; + } + + [Il2CppFinalizer] + private void DisposeMethodInfo() + { + Marshal.FreeHGlobal(MethodInfo); + MethodInfo = IntPtr.Zero; + } + + partial void LogErrorIl2CppFinalize(Exception exception) + { + Logger.Instance.LogError($"Exception in {nameof(Il2CppToMonoDelegateReference)}.{nameof(Il2CppFinalize)}: {{Exception}}", exception); + } +} diff --git a/Il2CppInterop.Runtime/Il2CppType.cs b/Il2CppInterop.Runtime/Il2CppType.cs deleted file mode 100644 index 481b3a1b..00000000 --- a/Il2CppInterop.Runtime/Il2CppType.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Il2CppSystem; -using ArgumentException = System.ArgumentException; -using IntPtr = System.IntPtr; - -namespace Il2CppInterop.Runtime; - -public static class Il2CppType -{ - public static Type TypeFromPointer(IntPtr classPointer, string typeName = "") - { - return TypeFromPointerInternal(classPointer, typeName, true); - } - - private static Type? TypeFromPointerInternal(IntPtr classPointer, string typeName, bool throwOnFailure) - { - if (classPointer == IntPtr.Zero) - { - if (throwOnFailure) - throw new ArgumentException($"{typeName} does not have a corresponding IL2CPP class pointer"); - return null; - } - - var il2CppType = IL2CPP.il2cpp_class_get_type(classPointer); - if (il2CppType == IntPtr.Zero) - { - if (throwOnFailure) - throw new ArgumentException($"{typeName} does not have a corresponding IL2CPP type pointer"); - return null; - } - - return Type.internal_from_handle(il2CppType); - } - - public static Type From(System.Type type) - { - return From(type, true); - } - - public static Type From(System.Type type, bool throwOnFailure) - { - var pointer = Il2CppClassPointerStore.GetNativeClassPointer(type); - return TypeFromPointerInternal(pointer, type.Name, throwOnFailure); - } - - public static Type Of() - { - return Of(true); - } - - public static Type Of(bool throwOnFailure) - { - var classPointer = Il2CppClassPointerStore.NativeClassPtr; - return TypeFromPointerInternal(classPointer, typeof(T).Name, throwOnFailure); - } -} diff --git a/Il2CppInterop.Runtime/Il2CppTypePointerStore.cs b/Il2CppInterop.Runtime/Il2CppTypePointerStore.cs new file mode 100644 index 00000000..edfdacfb --- /dev/null +++ b/Il2CppInterop.Runtime/Il2CppTypePointerStore.cs @@ -0,0 +1,54 @@ +using Il2CppInterop.Runtime.InteropTypes; +using Il2CppInterop.Runtime.Runtime; + +namespace Il2CppInterop.Runtime; + +internal static class Il2CppTypePointerStore +{ + public static nint GetNativeTypePointer(System.Type type) + { + if (type == typeof(void)) + return Il2CppTypePointerStore.NativeTypePointer; + + return (nint)typeof(Il2CppTypePointerStore<>) + .MakeGenericType(type) + .GetProperty(nameof(Il2CppTypePointerStore<>.NativeTypePointer))! + .GetValue(null)!; + } +} + +internal static unsafe class Il2CppTypePointerStore +{ + public static nint NativeTypePointer + { + get + { + if (field == default) + { + var classPointer = Il2CppClassPointerStore.NativeClassPointer; + if (classPointer != default) + { + field = IL2CPP.il2cpp_class_get_type(classPointer); + } + else if (typeof(IByReference).IsAssignableFrom(typeof(T))) + { + // For ByReference, we can still get the type pointer even if T isn't an Il2Cpp type, as long as T is a valid ByReference element type + var elementType = typeof(T).GetGenericArguments()[0]; + var elementTypePointer = Il2CppTypePointerStore.GetNativeTypePointer(elementType); + if (elementTypePointer != default) + { + var elemType = UnityVersionHandler.Wrap((Il2CppTypeStruct*)elementTypePointer); + var refType = UnityVersionHandler.NewType(); + refType.Data = elemType.Data; + refType.Attrs = elemType.Attrs; + refType.Type = elemType.Type; + refType.ByRef = true; + refType.Pinned = elemType.Pinned; + field = refType.Pointer; + } + } + } + return field; + } + } +} diff --git a/Il2CppInterop.Runtime/Injection/AssemblyInjector.cs b/Il2CppInterop.Runtime/Injection/AssemblyInjector.cs new file mode 100644 index 00000000..e8720bf5 --- /dev/null +++ b/Il2CppInterop.Runtime/Injection/AssemblyInjector.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Il2CppInterop.Common; +using Il2CppInterop.Runtime.Runtime; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.Image; +using Microsoft.Extensions.Logging; + +namespace Il2CppInterop.Runtime.Injection; + +internal static unsafe class AssemblyInjector +{ + private static readonly Dictionary images = []; + + internal static INativeImageStruct GetOrCreateImage(string name) + { + lock (images) + { + if (images.Count == 0) + { + var domain = IL2CPP.il2cpp_domain_get(); + if (domain == nint.Zero) + { + Logger.Instance.LogError("No il2cpp domain found; sad!"); + } + else + { + uint assembliesCount = 0; + var assemblies = IL2CPP.il2cpp_domain_get_assemblies(domain, ref assembliesCount); + for (var i = 0; i < assembliesCount; i++) + { + var image = UnityVersionHandler.Wrap((Il2CppImage*)IL2CPP.il2cpp_assembly_get_image(assemblies[i])); + var imageName = Marshal.PtrToStringUTF8(image.Name)!; + images[imageName] = image; + } + } + } + if (!images.TryGetValue(name, out var result)) + { + var assembly = UnityVersionHandler.NewAssembly(); + assembly.Name.Name = Marshal.StringToCoTaskMemUTF8(name); + result = UnityVersionHandler.NewImage(); + result.Assembly = assembly.AssemblyPointer; + result.Dynamic = 1; + result.Name = assembly.Name.Name; + if (result.HasNameNoExt) + { + if (name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + { + result.NameNoExt = Marshal.StringToCoTaskMemUTF8(name.Substring(0, name.Length - 4)); + } + else + { + result.NameNoExt = assembly.Name.Name; + } + } + images[name] = result; + } + return result; + } + } +} diff --git a/Il2CppInterop.Runtime/Injection/ClassInjector.Debug.cs b/Il2CppInterop.Runtime/Injection/ClassInjector.Debug.cs deleted file mode 100644 index 47f67966..00000000 --- a/Il2CppInterop.Runtime/Injection/ClassInjector.Debug.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using Il2CppInterop.Common; -using Il2CppInterop.Runtime.Runtime; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Runtime.Injection; - -public unsafe partial class ClassInjector -{ - public static void Dump() - { - Dump((Il2CppClass*)Il2CppClassPointerStore.NativeClassPtr); - } - - private static string ToString(Il2CppClass* il2CppClass) - { - if (il2CppClass == default) return "null"; - var classStruct = UnityVersionHandler.Wrap(il2CppClass); - return $"{Marshal.PtrToStringUTF8(classStruct.Namespace)}.{Marshal.PtrToStringUTF8(classStruct.Name)}"; - } - - private static string ToString(Il2CppTypeStruct* il2CppType) - { - if (il2CppType == default) return "null"; - return IL2CPP.il2cpp_type_get_name_((IntPtr)il2CppType)!; - } - - public static void Dump(Il2CppClass* il2CppClass) - { - if (il2CppClass == default) throw new ArgumentNullException(nameof(il2CppClass)); - - InjectorHelpers.Setup(); - InjectorHelpers.ClassInit(il2CppClass); - - var classStruct = UnityVersionHandler.Wrap(il2CppClass); - - Logger.Instance.LogDebug("Dumping {Pointer:X}", classStruct.Pointer); - - Logger.Instance.LogDebug(" Namespace = {Namespace}", Marshal.PtrToStringUTF8(classStruct.Namespace)); - Logger.Instance.LogDebug(" Name = {Name}", Marshal.PtrToStringUTF8(classStruct.Name)); - - Logger.Instance.LogDebug(" Parent = {Parent}", ToString(classStruct.Parent)); - Logger.Instance.LogDebug(" InstanceSize = {InstanceSize}", classStruct.InstanceSize); - Logger.Instance.LogDebug(" NativeSize = {NativeSize}", classStruct.NativeSize); - Logger.Instance.LogDebug(" ActualSize = {ActualSize}", classStruct.ActualSize); - Logger.Instance.LogDebug(" Flags = {Flags}", classStruct.Flags); - Logger.Instance.LogDebug(" ValueType = {ValueType}", classStruct.ValueType); - Logger.Instance.LogDebug(" EnumType = {EnumType}", classStruct.EnumType); - Logger.Instance.LogDebug(" IsGeneric = {IsGeneric}", classStruct.IsGeneric); - Logger.Instance.LogDebug(" Initialized = {Initialized}", classStruct.Initialized); - Logger.Instance.LogDebug(" InitializedAndNoError = {InitializedAndNoError}", classStruct.InitializedAndNoError); - Logger.Instance.LogDebug(" SizeInited = {SizeInited}", classStruct.SizeInited); - Logger.Instance.LogDebug(" HasFinalize = {HasFinalize}", classStruct.HasFinalize); - Logger.Instance.LogDebug(" IsVtableInitialized = {IsVtableInitialized}", classStruct.IsVtableInitialized); - - var vtable = (VirtualInvokeData*)classStruct.VTable; - Logger.Instance.LogDebug(" VTable ({VtableCount}):", classStruct.VtableCount); - for (var i = 0; i < classStruct.VtableCount; i++) - { - var virtualInvokeData = vtable![i]; - var methodName = virtualInvokeData.method == default ? "" : Marshal.PtrToStringUTF8(UnityVersionHandler.Wrap(virtualInvokeData.method).Name); - - Logger.Instance.LogDebug(" [{I}] {MethodName} - {MethodPtr}", i, methodName, (virtualInvokeData.methodPtr == default ? "" : virtualInvokeData.methodPtr)); - } - - Logger.Instance.LogDebug(" Fields ({FieldCount}):", classStruct.FieldCount); - for (var i = 0; i < classStruct.FieldCount; i++) - { - var field = UnityVersionHandler.Wrap(classStruct.Fields + i * UnityVersionHandler.FieldInfoSize()); - - Logger.Instance.LogDebug($" [{i}] {ToString(field.Type)} {Marshal.PtrToStringUTF8(field.Name)} - {field.Offset}"); - } - - Logger.Instance.LogDebug(" Methods ({MethodCount}):", classStruct.MethodCount); - for (var i = 0; i < classStruct.MethodCount; i++) - { - var method = UnityVersionHandler.Wrap(classStruct.Methods[i]); - - Logger.Instance.LogDebug(" [{I}] {ReturnType} {Name}({ParametersCount}), {Flags}, {Slot}", i, ToString(method.ReturnType), Marshal.PtrToStringUTF8(method.Name), method.ParametersCount, method.Flags, method.Slot); - } - - Logger.Instance.LogDebug(" ImplementedInterfaces ({InterfaceCount}):", classStruct.InterfaceCount); - for (var i = 0; i < classStruct.InterfaceCount; i++) - { - var @interface = UnityVersionHandler.Wrap(classStruct.ImplementedInterfaces[i]); - - Logger.Instance.LogDebug(" [{I}] {Name}", i, Marshal.PtrToStringUTF8(@interface.Name)); - } - - Logger.Instance.LogDebug(" InterfaceOffsets ({InterfaceOffsetsCount}):", classStruct.InterfaceOffsetsCount); - for (var i = 0; i < classStruct.InterfaceOffsetsCount; i++) - { - var pair = classStruct.InterfaceOffsets[i]; - var @interface = UnityVersionHandler.Wrap(pair.interfaceType); - - Logger.Instance.LogDebug(" [{I}] {Offset} - {Name}", i, pair.offset, Marshal.PtrToStringUTF8(@interface.Name)); - } - - Logger.Instance.LogDebug(" TypeHierarchy ({TypeHierarchyDepth}):", classStruct.TypeHierarchyDepth); - for (var i = 0; i < classStruct.TypeHierarchyDepth; i++) - { - var @interface = UnityVersionHandler.Wrap(classStruct.TypeHierarchy[i]); - - Logger.Instance.LogDebug(" [{I}] {Name}", i, Marshal.PtrToStringUTF8(@interface.Name)); - } - } -} diff --git a/Il2CppInterop.Runtime/Injection/ClassInjector.cs b/Il2CppInterop.Runtime/Injection/ClassInjector.cs deleted file mode 100644 index b9fb6486..00000000 --- a/Il2CppInterop.Runtime/Injection/ClassInjector.cs +++ /dev/null @@ -1,1179 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; -using System.Text; -using Il2CppInterop.Common; -using Il2CppInterop.Runtime.Attributes; -using Il2CppInterop.Runtime.InteropTypes; -using Il2CppInterop.Runtime.InteropTypes.Arrays; -using Il2CppInterop.Runtime.InteropTypes.Fields; -using Il2CppInterop.Runtime.Runtime; -using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class; -using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo; -using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; -using Microsoft.Extensions.Logging; -using ValueType = Il2CppSystem.ValueType; -using Void = Il2CppSystem.Void; - -namespace Il2CppInterop.Runtime.Injection; - -public unsafe class Il2CppInterfaceCollection : List -{ - public Il2CppInterfaceCollection(IEnumerable interfaces) : base(interfaces) - { - } - - public Il2CppInterfaceCollection(IEnumerable interfaces) : base(ResolveNativeInterfaces(interfaces)) - { - } - - private static IEnumerable ResolveNativeInterfaces(IEnumerable interfaces) - { - return interfaces.Select(it => - { - var classPointer = Il2CppClassPointerStore.GetNativeClassPointer(it); - if (classPointer == IntPtr.Zero) - throw new ArgumentException( - $"Type {it} doesn't have an IL2CPP class pointer, which means it's not an IL2CPP interface"); - return UnityVersionHandler.Wrap((Il2CppClass*)classPointer); - }); - } - - public static implicit operator Il2CppInterfaceCollection(INativeClassStruct[] interfaces) - { - return new(interfaces); - } - - public static implicit operator Il2CppInterfaceCollection(Type[] interfaces) - { - return new(interfaces); - } -} - -public class RegisterTypeOptions -{ - public static readonly RegisterTypeOptions Default = new(); - - public bool LogSuccess { get; init; } = true; - public Func? InterfacesResolver { get; init; } = null; - public Il2CppInterfaceCollection? Interfaces { get; init; } = null; -} - -public static unsafe partial class ClassInjector -{ - /// type.FullName - private static readonly HashSet InjectedTypes = new(); - - /// (method) : (method_inst, method) - internal static readonly Dictionary)> - InflatedMethodFromContextDictionary = new(); - - private static readonly ConcurrentDictionary InvokerCache = new(); - - private static readonly ConcurrentDictionary<(Type type, FieldAttributes attrs), IntPtr> - _injectedFieldTypes = new(); - - private static readonly VoidCtorDelegate FinalizeDelegate = Finalize; - - public static void ProcessNewObject(Il2CppObjectBase obj) - { - var pointer = obj.Pointer; - var handle = GCHandle.Alloc(obj, GCHandleType.Normal); - AssignGcHandle(pointer, handle); - } - - public static IntPtr DerivedConstructorPointer() - { - return IL2CPP.il2cpp_object_new(Il2CppClassPointerStore - .NativeClassPtr); // todo: consider calling base constructor - } - - public static void DerivedConstructorBody(Il2CppObjectBase objectBase) - { - if (objectBase.isWrapped) - return; - var fields = objectBase.GetType() - .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly) - .Where(IsFieldEligible) - .ToArray(); - foreach (var field in fields) - field.SetValue(objectBase, field.FieldType.GetConstructor( - BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, - new[] { typeof(Il2CppObjectBase), typeof(string) }, Array.Empty()) - .Invoke(new object[] { objectBase, field.Name }) - ); - var ownGcHandle = GCHandle.Alloc(objectBase, GCHandleType.Normal); - AssignGcHandle(objectBase.Pointer, ownGcHandle); - } - - public static void AssignGcHandle(IntPtr pointer, GCHandle gcHandle) - { - var handleAsPointer = GCHandle.ToIntPtr(gcHandle); - if (pointer == IntPtr.Zero) throw new NullReferenceException(nameof(pointer)); - ClassInjectorBase.GetInjectedData(pointer)->managedGcHandle = GCHandle.ToIntPtr(gcHandle); - } - - - public static bool IsTypeRegisteredInIl2Cpp() where T : class - { - return IsTypeRegisteredInIl2Cpp(typeof(T)); - } - - public static bool IsTypeRegisteredInIl2Cpp(Type type) - { - var currentPointer = Il2CppClassPointerStore.GetNativeClassPointer(type); - if (currentPointer != IntPtr.Zero) - return true; - if (IsManagedTypeInjected(type)) return true; - - return false; - } - - internal static bool IsManagedTypeInjected(Type type) - { - lock (InjectedTypes) - { - if (InjectedTypes.Contains(type.FullName)) - return true; - } - - return false; - } - - public static void RegisterTypeInIl2Cpp() where T : class - { - RegisterTypeInIl2Cpp(typeof(T)); - } - - public static void RegisterTypeInIl2Cpp(Type type) - { - RegisterTypeInIl2Cpp(type, RegisterTypeOptions.Default); - } - - public static void RegisterTypeInIl2Cpp(RegisterTypeOptions options) where T : class - { - RegisterTypeInIl2Cpp(typeof(T), options); - } - - public static void RegisterTypeInIl2Cpp(Type type, RegisterTypeOptions options) - { - var interfaces = options.Interfaces; - if (interfaces == null) - { - var interfacesAttribute = type.GetCustomAttribute(); - interfaces = interfacesAttribute?.Interfaces ?? - options.InterfacesResolver?.Invoke(type) ?? Array.Empty(); - } - - if (type == null) - throw new ArgumentException("Type argument cannot be null"); - - if (type.IsGenericType || type.IsGenericTypeDefinition) - throw new ArgumentException($"Type {type} is generic and can't be used in il2cpp"); - - var currentPointer = Il2CppClassPointerStore.GetNativeClassPointer(type); - if (currentPointer != IntPtr.Zero) - return; //already registered in il2cpp - - var baseType = type.BaseType; - if (baseType == null) - throw new ArgumentException($"Class {type} does not inherit from a class registered in il2cpp"); - - var baseClassPointer = - UnityVersionHandler.Wrap((Il2CppClass*)Il2CppClassPointerStore.GetNativeClassPointer(baseType)); - if (baseClassPointer == null) - { - RegisterTypeInIl2Cpp(baseType, new RegisterTypeOptions { LogSuccess = options.LogSuccess }); - baseClassPointer = - UnityVersionHandler.Wrap((Il2CppClass*)Il2CppClassPointerStore.GetNativeClassPointer(baseType)); - } - - InjectorHelpers.Setup(); - - // Initialize the vtable of all base types (Class::Init is recursive internally) - InjectorHelpers.ClassInit(baseClassPointer.ClassPointer); - - if (baseClassPointer.ValueType || baseClassPointer.EnumType) - throw new ArgumentException($"Base class {baseType} is value type and can't be inherited from"); - - if (baseClassPointer.IsGeneric) - throw new ArgumentException($"Base class {baseType} is generic and can't be inherited from"); - - if ((baseClassPointer.Flags & Il2CppClassAttributes.TYPE_ATTRIBUTE_SEALED) != 0) - throw new ArgumentException($"Base class {baseType} is sealed and can't be inherited from"); - - if ((baseClassPointer.Flags & Il2CppClassAttributes.TYPE_ATTRIBUTE_INTERFACE) != 0) - throw new ArgumentException($"Base class {baseType} is an interface and can't be inherited from"); - - if (interfaces.Any(i => (i.Flags & Il2CppClassAttributes.TYPE_ATTRIBUTE_INTERFACE) == 0)) - throw new ArgumentException($"Some of the interfaces in {interfaces} are not interfaces"); - - lock (InjectedTypes) - { - if (!InjectedTypes.Add(type.FullName)) - throw new ArgumentException( - $"Type with FullName {type.FullName} is already injected. Don't inject the same type twice, or use a different namespace"); - } - - var interfaceFunctionCount = interfaces.Sum(i => i.MethodCount); - var classPointer = UnityVersionHandler.NewClass(baseClassPointer.VtableCount + interfaceFunctionCount); - - classPointer.Image = InjectorHelpers.InjectedImage.ImagePointer; - classPointer.Parent = baseClassPointer.ClassPointer; - classPointer.ElementClass = classPointer.Class = classPointer.CastClass = classPointer.ClassPointer; - classPointer.NativeSize = -1; - classPointer.ActualSize = classPointer.InstanceSize = baseClassPointer.InstanceSize; - - classPointer.Initialized = true; - classPointer.InitializedAndNoError = true; - classPointer.SizeInited = true; - classPointer.HasFinalize = true; - classPointer.IsVtableInitialized = true; - - classPointer.Name = Marshal.StringToCoTaskMemUTF8(type.Name); - classPointer.Namespace = Marshal.StringToCoTaskMemUTF8(type.Namespace ?? string.Empty); - - classPointer.ThisArg.Type = classPointer.ByValArg.Type = Il2CppTypeEnum.IL2CPP_TYPE_CLASS; - classPointer.ThisArg.ByRef = true; - - classPointer.Flags = baseClassPointer.Flags; // todo: adjust flags? - - if (!type.IsAbstract) classPointer.Flags &= ~Il2CppClassAttributes.TYPE_ATTRIBUTE_ABSTRACT; - - var fieldsToInject = type - .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly) - .Where(IsFieldEligible) - .ToArray(); - classPointer.FieldCount = (ushort)fieldsToInject.Length; - - var il2cppFields = - (Il2CppFieldInfo*)Marshal.AllocHGlobal(classPointer.FieldCount * UnityVersionHandler.FieldInfoSize()); - var fieldOffset = (int)classPointer.InstanceSize; - for (var i = 0; i < classPointer.FieldCount; i++) - { - var fieldInfo = UnityVersionHandler.Wrap(il2cppFields + i * UnityVersionHandler.FieldInfoSize()); - fieldInfo.Name = Marshal.StringToCoTaskMemUTF8(fieldsToInject[i].Name); - fieldInfo.Parent = classPointer.ClassPointer; - fieldInfo.Offset = fieldOffset; - - var fieldType = fieldsToInject[i].FieldType == typeof(Il2CppStringField) - ? typeof(string) - : fieldsToInject[i].FieldType.GenericTypeArguments[0]; - var fieldAttributes = fieldsToInject[i].Attributes; - var fieldInfoClass = Il2CppClassPointerStore.GetNativeClassPointer(fieldType); - if (!_injectedFieldTypes.TryGetValue((fieldType, fieldAttributes), out var fieldTypePtr)) - { - var classType = - UnityVersionHandler.Wrap((Il2CppTypeStruct*)IL2CPP.il2cpp_class_get_type(fieldInfoClass)); - - var duplicatedType = UnityVersionHandler.NewType(); - duplicatedType.Data = classType.Data; - duplicatedType.Attrs = (ushort)fieldAttributes; - duplicatedType.Type = classType.Type; - duplicatedType.ByRef = classType.ByRef; - duplicatedType.Pinned = classType.Pinned; - - _injectedFieldTypes[(fieldType, fieldAttributes)] = duplicatedType.Pointer; - fieldTypePtr = duplicatedType.Pointer; - } - - fieldInfo.Type = (Il2CppTypeStruct*)fieldTypePtr; - if (fieldInfoClass == IntPtr.Zero) - throw new Exception($"Type {fieldType} in {type}.{fieldsToInject[i].Name} doesn't exist in Il2Cpp"); - - if (IL2CPP.il2cpp_class_is_valuetype(fieldInfoClass)) - { - uint _align = 0; - var fieldSize = IL2CPP.il2cpp_class_value_size(fieldInfoClass, ref _align); - fieldOffset += fieldSize; - } - else - { - fieldOffset += sizeof(Il2CppObject*); - } - } - - classPointer.Fields = il2cppFields; - - classPointer.InstanceSize = (uint)(fieldOffset + sizeof(InjectedClassData)); - classPointer.ActualSize = classPointer.InstanceSize; - - var eligibleMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly).Where(IsMethodEligible).ToArray(); - var methodsOffset = type.IsAbstract ? 1 : 2; // 1 is the finalizer, 1 is empty ctor - var methodCount = methodsOffset + eligibleMethods.Length; - - classPointer.MethodCount = (ushort)methodCount; - var methodPointerArray = (Il2CppMethodInfo**)Marshal.AllocHGlobal(methodCount * IntPtr.Size); - classPointer.Methods = methodPointerArray; - - methodPointerArray[0] = ConvertStaticMethod(FinalizeDelegate, "Finalize", classPointer); - var finalizeMethod = UnityVersionHandler.Wrap(methodPointerArray[0]); - var fieldsToInitialize = type - .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Where(IsFieldEligible) - .ToArray(); - - if (!type.IsAbstract) methodPointerArray[1] = ConvertStaticMethod(CreateEmptyCtor(type, fieldsToInitialize), ".ctor", classPointer); - var infos = new Dictionary<(string, int, bool), int>(eligibleMethods.Length); - for (var i = 0; i < eligibleMethods.Length; i++) - { - var methodInfo = eligibleMethods[i]; - var methodInfoPointer = methodPointerArray[i + methodsOffset] = ConvertMethodInfo(methodInfo, classPointer); - if (methodInfo.IsGenericMethod && !methodInfo.IsAbstract) - InflatedMethodFromContextDictionary.Add((IntPtr)methodInfoPointer, (methodInfo, new Dictionary())); - infos[(methodInfo.Name, methodInfo.GetParameters().Length, methodInfo.IsGenericMethod)] = i + methodsOffset; - } - - var abstractMethods = eligibleMethods.Where(x => x.IsAbstract).ToArray(); - - var vTablePointer = (VirtualInvokeData*)classPointer.VTable; - var baseVTablePointer = (VirtualInvokeData*)baseClassPointer.VTable; - classPointer.VtableCount = (ushort)(baseClassPointer.VtableCount + interfaceFunctionCount + abstractMethods.Length); - - var extendsAbstract = baseClassPointer.Flags.HasFlag(Il2CppClassAttributes.TYPE_ATTRIBUTE_ABSTRACT); - var abstractBaseMethods = new List(); - - if (extendsAbstract) - { - static void FindAbstractMethods(List list, INativeClassStruct klass) - { - if (klass.Parent != default) FindAbstractMethods(list, UnityVersionHandler.Wrap(klass.Parent)); - - for (var i = 0; i < klass.MethodCount; i++) - { - var baseMethod = UnityVersionHandler.Wrap(klass.Methods[i]); - var name = Marshal.PtrToStringUTF8(baseMethod.Name)!; - - if (baseMethod.Flags.HasFlag(Il2CppMethodFlags.METHOD_ATTRIBUTE_ABSTRACT)) - { - list.Add(baseMethod); - } - else - { - var existing = list.SingleOrDefault(m => - { - if (Marshal.PtrToStringUTF8(m.Name) != name) return false; - if (m.ParametersCount != baseMethod.ParametersCount) return false; - if (GetIl2CppTypeFullName(m.ReturnType) != GetIl2CppTypeFullName(baseMethod.ReturnType)) return false; - - for (var i = 0; i < m.ParametersCount; i++) - { - var parameterInfo = UnityVersionHandler.Wrap(baseMethod.Parameters, i); - var otherParameterInfo = UnityVersionHandler.Wrap(m.Parameters, i); - - if (GetIl2CppTypeFullName(parameterInfo.ParameterType) != GetIl2CppTypeFullName(otherParameterInfo.ParameterType)) return false; - } - - return true; - }); - - if (existing != null) - { - list.Remove(existing); - } - } - } - } - - FindAbstractMethods(abstractBaseMethods, baseClassPointer); - } - - var abstractV = 0; - - INativeMethodInfoStruct HandleAbstractMethod(int position) - { - if (!extendsAbstract) throw new NullReferenceException("VTable method was null even though base type isn't abstract"); - - var nativeMethodInfoStruct = abstractBaseMethods[abstractV++]; - - vTablePointer[position].method = nativeMethodInfoStruct.MethodInfoPointer; - vTablePointer[position].methodPtr = nativeMethodInfoStruct.MethodPointer; - return nativeMethodInfoStruct; - } - - for (var i = 0; i < baseClassPointer.VtableCount; i++) - { - vTablePointer[i] = baseVTablePointer[i]; - - INativeMethodInfoStruct baseMethod; - - if (baseVTablePointer[i].method == default) - { - baseMethod = HandleAbstractMethod(i); - } - else - { - baseMethod = UnityVersionHandler.Wrap(vTablePointer[i].method); - } - - if (baseMethod.Name == IntPtr.Zero) - { - baseMethod = HandleAbstractMethod(i); - } - - var methodName = Marshal.PtrToStringUTF8(baseMethod.Name); - - if (methodName == "Finalize") // slot number is not static - { - vTablePointer[i].method = methodPointerArray[0]; - vTablePointer[i].methodPtr = finalizeMethod.MethodPointer; - continue; - } - - var parameters = new Type[baseMethod.ParametersCount]; - - for (var j = 0; j < baseMethod.ParametersCount; j++) - { - var parameterInfo = UnityVersionHandler.Wrap(baseMethod.Parameters, j); - var parameterType = SystemTypeFromIl2CppType(parameterInfo.ParameterType); - - parameters[j] = parameterType; - } - - var monoMethodImplementation = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly, parameters); - - if (monoMethodImplementation != null && monoMethodImplementation.IsAbstract) - { - continue; - } - - var methodPointerArrayIndex = Array.IndexOf(eligibleMethods, monoMethodImplementation); - if (methodPointerArrayIndex >= 0) - { - var method = UnityVersionHandler.Wrap(methodPointerArray[methodPointerArrayIndex + methodsOffset]); - vTablePointer[i].method = methodPointerArray[methodPointerArrayIndex + methodsOffset]; - vTablePointer[i].methodPtr = method.MethodPointer; - } - - if (vTablePointer[i].method == default || vTablePointer[i].methodPtr == IntPtr.Zero) - { - throw new Exception("No method found for vtable entry " + methodName); - } - } - - var offsets = new int[interfaces.Count]; - - var index = baseClassPointer.VtableCount; - for (var i = 0; i < interfaces.Count; i++) - { - offsets[i] = index; - for (var j = 0; j < interfaces[i].MethodCount; j++) - { - var vTableMethod = UnityVersionHandler.Wrap(interfaces[i].Methods[j]); - var methodName = Marshal.PtrToStringUTF8(vTableMethod.Name); - if (!infos.TryGetValue((methodName, vTableMethod.ParametersCount, vTableMethod.IsGeneric), - out var methodIndex)) - { - ++index; - continue; - } - - var method = methodPointerArray[methodIndex]; - vTablePointer[index].method = method; - vTablePointer[index].methodPtr = UnityVersionHandler.Wrap(method).MethodPointer; - ++index; - } - } - - var interfaceCount = baseClassPointer.InterfaceCount + interfaces.Count; - classPointer.InterfaceCount = (ushort)interfaceCount; - classPointer.ImplementedInterfaces = (Il2CppClass**)Marshal.AllocHGlobal(interfaceCount * IntPtr.Size); - for (var i = 0; i < baseClassPointer.InterfaceCount; i++) - classPointer.ImplementedInterfaces[i] = baseClassPointer.ImplementedInterfaces[i]; - for (int i = baseClassPointer.InterfaceCount; i < interfaceCount; i++) - classPointer.ImplementedInterfaces[i] = interfaces[i - baseClassPointer.InterfaceCount].ClassPointer; - - var interfaceOffsetsCount = baseClassPointer.InterfaceOffsetsCount + interfaces.Count; - classPointer.InterfaceOffsetsCount = (ushort)interfaceOffsetsCount; - classPointer.InterfaceOffsets = - (Il2CppRuntimeInterfaceOffsetPair*)Marshal.AllocHGlobal(interfaceOffsetsCount * - Marshal - .SizeOf()); - for (var i = 0; i < baseClassPointer.InterfaceOffsetsCount; i++) - classPointer.InterfaceOffsets[i] = baseClassPointer.InterfaceOffsets[i]; - for (int i = baseClassPointer.InterfaceOffsetsCount; i < interfaceOffsetsCount; i++) - classPointer.InterfaceOffsets[i] = new Il2CppRuntimeInterfaceOffsetPair - { - interfaceType = interfaces[i - baseClassPointer.InterfaceOffsetsCount].ClassPointer, - offset = offsets[i - baseClassPointer.InterfaceOffsetsCount] - }; - - for (var i = 0; i < abstractMethods.Length; i++) - { - vTablePointer[index++] = default; - } - - var TypeHierarchyDepth = 1 + baseClassPointer.TypeHierarchyDepth; - classPointer.TypeHierarchyDepth = (byte)TypeHierarchyDepth; - classPointer.TypeHierarchy = (Il2CppClass**)Marshal.AllocHGlobal(TypeHierarchyDepth * IntPtr.Size); - for (var i = 0; i < TypeHierarchyDepth; i++) - classPointer.TypeHierarchy[i] = baseClassPointer.TypeHierarchy[i]; - classPointer.TypeHierarchy[TypeHierarchyDepth - 1] = classPointer.ClassPointer; - - classPointer.ByValArg.Data = - classPointer.ThisArg.Data = (IntPtr)InjectorHelpers.CreateClassToken(classPointer.Pointer); - - RuntimeSpecificsStore.SetClassInfo(classPointer.Pointer, true); - Il2CppClassPointerStore.SetNativeClassPointer(type, classPointer.Pointer); - - InjectorHelpers.AddTypeToLookup(type, classPointer.Pointer); - - if (options.LogSuccess) - Logger.Instance.LogInformation("Registered mono type {Type} in il2cpp domain", type); - } - - private static bool IsTypeSupported(Type type) - { - if (type.IsValueType || - type == typeof(string) || - type.IsGenericParameter) return true; - if (type.IsByRef) return IsTypeSupported(type.GetElementType()); - - return typeof(Il2CppObjectBase).IsAssignableFrom(type); - } - - private static bool IsFieldEligible(FieldInfo field) - { - if (!field.FieldType.IsGenericType) return field.FieldType == typeof(Il2CppStringField); - var genericTypeDef = field.FieldType.GetGenericTypeDefinition(); - if (genericTypeDef != typeof(Il2CppReferenceField<>) && genericTypeDef != typeof(Il2CppValueField<>)) - return false; - - return IsTypeSupported(field.FieldType.GenericTypeArguments[0]); - } - - private static bool IsMethodEligible(MethodInfo method) - { - if (method.Name == "Finalize") return false; - if (method.IsStatic) return false; - if (method.CustomAttributes.Any(it => typeof(HideFromIl2CppAttribute).IsAssignableFrom(it.AttributeType))) - return false; - - if (method.DeclaringType != null) - { - if (method.DeclaringType.GetProperties(BindingFlags.Instance | BindingFlags.Public | - BindingFlags.NonPublic | BindingFlags.DeclaredOnly) - .Where(property => property.GetAccessors(true).Contains(method)) - .Any(property => - property.CustomAttributes.Any(it => - typeof(HideFromIl2CppAttribute).IsAssignableFrom(it.AttributeType))) - ) - return false; - - foreach (var eventInfo in method.DeclaringType.GetEvents(BindingFlags.Instance | BindingFlags.Public | - BindingFlags.NonPublic | - BindingFlags.DeclaredOnly)) - if ((eventInfo.GetAddMethod(true) == method || eventInfo.GetRemoveMethod(true) == method) && - eventInfo.GetCustomAttribute() != null) - return false; - } - - if (!IsTypeSupported(method.ReturnType)) - { - Logger.Instance.LogWarning( - "Method {Method} on type {DeclaringType} has unsupported return type {ReturnType}", method.ToString(), method.DeclaringType, method.ReturnType); - return false; - } - - foreach (var parameter in method.GetParameters()) - { - var parameterType = parameter.ParameterType; - if (!IsTypeSupported(parameterType)) - { - Logger.Instance.LogWarning( - "Method {Method} on type {DeclaringType} has unsupported parameter {Parameter} of type {ParameterType}", method.ToString(), method.DeclaringType, parameter, parameterType); - return false; - } - } - - return true; - } - - private static Il2CppMethodInfo* ConvertStaticMethod(VoidCtorDelegate voidCtor, string methodName, - INativeClassStruct declaringClass) - { - var converted = UnityVersionHandler.NewMethod(); - converted.Name = Marshal.StringToCoTaskMemUTF8(methodName); - converted.Class = declaringClass.ClassPointer; - - Delegate invoker; - if (UnityVersionHandler.IsMetadataV29OrHigher) - { - invoker = new InvokerDelegateMetadataV29(StaticVoidIntPtrInvoker_MetadataV29); - } - else - { - invoker = new InvokerDelegate(StaticVoidIntPtrInvoker); - } - - GCHandle.Alloc(invoker); - converted.InvokerMethod = Marshal.GetFunctionPointerForDelegate(invoker); - - converted.MethodPointer = Marshal.GetFunctionPointerForDelegate(voidCtor); - converted.Slot = ushort.MaxValue; - converted.ReturnType = - (Il2CppTypeStruct*)IL2CPP.il2cpp_class_get_type(Il2CppClassPointerStore.NativeClassPtr); - - converted.Flags = Il2CppMethodFlags.METHOD_ATTRIBUTE_PUBLIC | - Il2CppMethodFlags.METHOD_ATTRIBUTE_HIDE_BY_SIG | - Il2CppMethodFlags.METHOD_ATTRIBUTE_SPECIAL_NAME | - Il2CppMethodFlags.METHOD_ATTRIBUTE_RT_SPECIAL_NAME; - - return converted.MethodInfoPointer; - } - - internal static Il2CppMethodInfo* ConvertMethodInfo(MethodInfo monoMethod, INativeClassStruct declaringClass) - { - var converted = UnityVersionHandler.NewMethod(); - converted.Name = Marshal.StringToCoTaskMemUTF8(monoMethod.Name); - converted.Class = declaringClass.ClassPointer; - - var parameters = monoMethod.GetParameters(); - if (parameters.Length > 0) - { - converted.ParametersCount = (byte)parameters.Length; - var paramsArray = UnityVersionHandler.NewMethodParameterArray(parameters.Length); - converted.Parameters = paramsArray[0]; - for (var i = 0; i < parameters.Length; i++) - { - var parameterInfo = parameters[i]; - var param = UnityVersionHandler.Wrap(paramsArray[i]); - if (UnityVersionHandler.ParameterInfoHasNamePosToken()) - { - param.Name = Marshal.StringToCoTaskMemUTF8(parameterInfo.Name); - param.Position = i; - param.Token = 0; - } - - var parameterType = parameterInfo.ParameterType; - if (!parameterType.IsGenericParameter) - { - if (parameterType.IsByRef) - { - var elementType = parameterType.GetElementType(); - if (!elementType.IsGenericParameter) - { - var elemType = UnityVersionHandler.Wrap( - (Il2CppTypeStruct*)IL2CPP.il2cpp_class_get_type( - Il2CppClassPointerStore.GetNativeClassPointer(elementType))); - var refType = UnityVersionHandler.NewType(); - refType.Data = elemType.Data; - refType.Attrs = elemType.Attrs; - refType.Type = elemType.Type; - refType.ByRef = true; - refType.Pinned = elemType.Pinned; - param.ParameterType = refType.TypePointer; - } - else - { - var type = UnityVersionHandler.NewType(); - type.Type = Il2CppTypeEnum.IL2CPP_TYPE_MVAR; - type.ByRef = true; - param.ParameterType = type.TypePointer; - } - } - else - { - param.ParameterType = - (Il2CppTypeStruct*)IL2CPP.il2cpp_class_get_type( - Il2CppClassPointerStore.GetNativeClassPointer(parameterType)); - } - } - else - { - var type = UnityVersionHandler.NewType(); - type.Type = Il2CppTypeEnum.IL2CPP_TYPE_MVAR; - param.ParameterType = type.TypePointer; - } - } - } - - if (monoMethod.IsGenericMethod) - { - if (monoMethod.ContainsGenericParameters) - converted.IsGeneric = true; - else - converted.IsInflated = true; - } - - if (!monoMethod.ContainsGenericParameters && !monoMethod.IsAbstract) - { - converted.InvokerMethod = Marshal.GetFunctionPointerForDelegate(GetOrCreateInvoker(monoMethod)); - converted.MethodPointer = Marshal.GetFunctionPointerForDelegate(GetOrCreateTrampoline(monoMethod)); - converted.VirtualMethodPointer = converted.MethodPointer; - } - - converted.Slot = ushort.MaxValue; - - if (!monoMethod.ReturnType.IsGenericParameter) - { - converted.ReturnType = - (Il2CppTypeStruct*)IL2CPP.il2cpp_class_get_type( - Il2CppClassPointerStore.GetNativeClassPointer(monoMethod.ReturnType)); - } - else - { - var type = UnityVersionHandler.NewType(); - type.Type = Il2CppTypeEnum.IL2CPP_TYPE_MVAR; - converted.ReturnType = type.TypePointer; - } - - converted.Flags = Il2CppMethodFlags.METHOD_ATTRIBUTE_PUBLIC | - Il2CppMethodFlags.METHOD_ATTRIBUTE_HIDE_BY_SIG; - - if (monoMethod.IsAbstract) - { - converted.Flags |= Il2CppMethodFlags.METHOD_ATTRIBUTE_ABSTRACT; - } - - return converted.MethodInfoPointer; - } - - private static VoidCtorDelegate CreateEmptyCtor(Type targetType, FieldInfo[] fieldsToInitialize) - { - var method = new DynamicMethod("FromIl2CppCtorDelegate", MethodAttributes.Public | MethodAttributes.Static, - CallingConventions.Standard, typeof(void), new[] { typeof(IntPtr) }, targetType, true); - - var body = method.GetILGenerator(); - - var monoCtor = targetType.GetConstructor(new[] { typeof(IntPtr) }); - if (monoCtor != null) - { - body.Emit(OpCodes.Ldarg_0); - body.Emit(OpCodes.Newobj, monoCtor); - } - else - { - var local = body.DeclareLocal(targetType); - body.Emit(OpCodes.Ldtoken, targetType); - body.Emit(OpCodes.Call, - typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle), BindingFlags.Public | BindingFlags.Static)!); - body.Emit(OpCodes.Call, - typeof(FormatterServices).GetMethod(nameof(FormatterServices.GetUninitializedObject), - BindingFlags.Public | BindingFlags.Static)!); - body.Emit(OpCodes.Stloc, local); - body.Emit(OpCodes.Ldloc, local); - body.Emit(OpCodes.Ldarg_0); - body.Emit(OpCodes.Call, - typeof(Il2CppObjectBase).GetMethod(nameof(Il2CppObjectBase.CreateGCHandle), - BindingFlags.NonPublic | BindingFlags.Instance)!); - body.Emit(OpCodes.Ldloc, local); - body.Emit(OpCodes.Ldc_I4_1); - body.Emit(OpCodes.Stfld, - typeof(Il2CppObjectBase).GetField(nameof(Il2CppObjectBase.isWrapped), - BindingFlags.NonPublic | BindingFlags.Instance)!); - body.Emit(OpCodes.Ldloc, local); - body.Emit(OpCodes.Call, - targetType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, - Type.EmptyTypes, Array.Empty())!); - body.Emit(OpCodes.Ldloc, local); - } - - foreach (var field in fieldsToInitialize) - { - body.Emit(OpCodes.Dup); - body.Emit(OpCodes.Dup); - body.Emit(OpCodes.Ldstr, field.Name); - body.Emit(OpCodes.Newobj, field.FieldType.GetConstructor( - BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, - new[] { typeof(Il2CppObjectBase), typeof(string) }, Array.Empty()) - ); - body.Emit(OpCodes.Stfld, field); - } - - body.Emit(OpCodes.Call, typeof(ClassInjector).GetMethod(nameof(ProcessNewObject))!); - - body.Emit(OpCodes.Ret); - - var @delegate = (VoidCtorDelegate)method.CreateDelegate(typeof(VoidCtorDelegate)); - GCHandle.Alloc(@delegate); // pin it forever - return @delegate; - } - - public static void Finalize(IntPtr ptr) - { - var gcHandle = ClassInjectorBase.GetGcHandlePtrFromIl2CppObject(ptr); - GCHandle.FromIntPtr(gcHandle).Free(); - } - - private static Delegate GetOrCreateInvoker(MethodInfo monoMethod) - { - return InvokerCache.GetOrAdd(ExtractSignature(monoMethod), - static (_, monoMethodInner) => CreateInvoker(monoMethodInner), monoMethod); - } - - private static Delegate GetOrCreateTrampoline(MethodInfo monoMethod) - { - return CreateTrampoline(monoMethod); - } - - private static Delegate CreateInvoker(MethodInfo monoMethod) - { - DynamicMethod method; - if (UnityVersionHandler.IsMetadataV29OrHigher) - { - var parameterTypes = new[] { typeof(IntPtr), typeof(Il2CppMethodInfo*), typeof(IntPtr), typeof(IntPtr*), typeof(IntPtr*) }; - - method = new DynamicMethod("Invoker_" + ExtractSignature(monoMethod), - MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(void), - parameterTypes, monoMethod.DeclaringType, true); - } - else - { - var parameterTypes = new[] { typeof(IntPtr), typeof(Il2CppMethodInfo*), typeof(IntPtr), typeof(IntPtr*) }; - - method = new DynamicMethod("Invoker_" + ExtractSignature(monoMethod), - MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(IntPtr), - parameterTypes, monoMethod.DeclaringType, true); - } - - var body = method.GetILGenerator(); - - body.Emit(OpCodes.Ldarg_2); - for (var i = 0; i < monoMethod.GetParameters().Length; i++) - { - var parameterInfo = monoMethod.GetParameters()[i]; - body.Emit(OpCodes.Ldarg_3); - body.Emit(OpCodes.Ldc_I4, i * IntPtr.Size); - body.Emit(OpCodes.Add_Ovf_Un); - var nativeType = parameterInfo.ParameterType.NativeType(); - body.Emit(OpCodes.Ldobj, typeof(IntPtr)); - if (nativeType != typeof(IntPtr)) - body.Emit(OpCodes.Ldobj, nativeType); - } - - body.Emit(OpCodes.Ldarg_0); - body.EmitCalli(OpCodes.Calli, CallingConvention.Cdecl, monoMethod.ReturnType.NativeType(), - new[] { typeof(IntPtr) }.Concat(monoMethod.GetParameters().Select(it => it.ParameterType.NativeType())) - .ToArray()); - - if (UnityVersionHandler.IsMetadataV29OrHigher) - { - if (monoMethod.ReturnType != typeof(void)) - { - var returnValue = body.DeclareLocal(monoMethod.ReturnType.NativeType()); - body.Emit(OpCodes.Stloc, returnValue); - body.Emit(OpCodes.Ldarg_S, (byte)4); - body.Emit(OpCodes.Ldloc, returnValue); - body.Emit(OpCodes.Stobj, returnValue.LocalType); - } - } - else - { - if (monoMethod.ReturnType == typeof(void)) - { - body.Emit(OpCodes.Ldc_I4_0); - body.Emit(OpCodes.Conv_I); - } - else if (monoMethod.ReturnType.IsValueType) - { - var returnValue = body.DeclareLocal(monoMethod.ReturnType); - body.Emit(OpCodes.Stloc, returnValue); - var classField = typeof(Il2CppClassPointerStore<>).MakeGenericType(monoMethod.ReturnType) - .GetField(nameof(Il2CppClassPointerStore.NativeClassPtr)); - body.Emit(OpCodes.Ldsfld, classField); - body.Emit(OpCodes.Ldloca, returnValue); - body.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.il2cpp_value_box))!); - } - } - - body.Emit(OpCodes.Ret); - - GCHandle.Alloc(method); - - var @delegate = method.CreateDelegate(GetInvokerDelegateType()); - GCHandle.Alloc(@delegate); - return @delegate; - } - - private static Type GetInvokerDelegateType() - { - if (UnityVersionHandler.IsMetadataV29OrHigher) - { - return typeof(InvokerDelegateMetadataV29); - } - - return typeof(InvokerDelegate); - } - - private static IntPtr StaticVoidIntPtrInvoker(IntPtr methodPointer, Il2CppMethodInfo* methodInfo, IntPtr obj, - IntPtr* args) - { - Marshal.GetDelegateForFunctionPointer(methodPointer)(obj); - return IntPtr.Zero; - } - - private static void StaticVoidIntPtrInvoker_MetadataV29(IntPtr methodPointer, Il2CppMethodInfo* methodInfo, IntPtr obj, - IntPtr* args, IntPtr* returnValue) - { - Marshal.GetDelegateForFunctionPointer(methodPointer)(obj); - } - - private static Delegate CreateTrampoline(MethodInfo monoMethod) - { - var nativeParameterTypes = new[] { typeof(IntPtr) }.Concat(monoMethod.GetParameters() - .Select(it => it.ParameterType.NativeType()).Concat(new[] { typeof(Il2CppMethodInfo*) })).ToArray(); - - var managedParameters = new[] { monoMethod.DeclaringType } - .Concat(monoMethod.GetParameters().Select(it => it.ParameterType)).ToArray(); - - var method = new DynamicMethod( - "Trampoline_" + ExtractSignature(monoMethod) + monoMethod.DeclaringType + monoMethod.Name, - MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, - monoMethod.ReturnType.NativeType(), nativeParameterTypes, - monoMethod.DeclaringType, true); - - var signature = new DelegateSupport.MethodSignature(monoMethod, true); - var delegateType = DelegateSupport.GetOrCreateDelegateType(signature, monoMethod); - - var body = method.GetILGenerator(); - - body.BeginExceptionBlock(); - - body.Emit(OpCodes.Ldarg_0); - body.Emit(OpCodes.Call, - typeof(ClassInjectorBase).GetMethod(nameof(ClassInjectorBase.GetMonoObjectFromIl2CppPointer))!); - body.Emit(OpCodes.Castclass, monoMethod.DeclaringType); - - var indirectVariables = new LocalBuilder[managedParameters.Length]; - - for (var i = 1; i < managedParameters.Length; i++) - { - var parameter = managedParameters[i]; - if (parameter.IsSubclassOf(typeof(ValueType))) - { - body.Emit(OpCodes.Ldc_I8, Il2CppClassPointerStore.GetNativeClassPointer(parameter).ToInt64()); - body.Emit(OpCodes.Conv_I); - body.Emit(Environment.Is64BitProcess ? OpCodes.Ldarg : OpCodes.Ldarga_S, i); - body.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.il2cpp_value_box))); - } - else - { - body.Emit(OpCodes.Ldarg, i); - } - - if (parameter.IsValueType) continue; - - void HandleTypeConversion(Type type) - { - if (type == typeof(string)) - { - body.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.Il2CppStringToManaged))!); - } - else if (type.IsSubclassOf(typeof(Il2CppObjectBase))) - { - var labelNull = body.DefineLabel(); - var labelNotNull = body.DefineLabel(); - body.Emit(OpCodes.Dup); - body.Emit(OpCodes.Brfalse, labelNull); - // We need to directly resolve from all constructors because on mono GetConstructor can cause the following issue: - // `Missing field layout info for ...` - // This is caused by GetConstructor calling RuntimeTypeHandle.CanCastTo which can fail since right now unhollower emits ALL fields which appear to now work properly - body.Emit(OpCodes.Newobj, type.GetConstructors().FirstOrDefault(ci => - { - var ps = ci.GetParameters(); - return ps.Length == 1 && ps[0].ParameterType == typeof(IntPtr); - })!); - body.Emit(OpCodes.Br, labelNotNull); - body.MarkLabel(labelNull); - body.Emit(OpCodes.Pop); - body.Emit(OpCodes.Ldnull); - body.MarkLabel(labelNotNull); - } - } - - if (parameter.IsByRef) - { - var elemType = parameter.GetElementType(); - - indirectVariables[i] = body.DeclareLocal(elemType); - - body.Emit(OpCodes.Ldind_I); - HandleTypeConversion(elemType); - body.Emit(OpCodes.Stloc, indirectVariables[i]); - body.Emit(OpCodes.Ldloca, indirectVariables[i]); - } - else - { - HandleTypeConversion(parameter); - } - } - - body.Emit(OpCodes.Call, monoMethod); - LocalBuilder managedReturnVariable = null; - if (monoMethod.ReturnType != typeof(void)) - { - managedReturnVariable = body.DeclareLocal(monoMethod.ReturnType); - body.Emit(OpCodes.Stloc, managedReturnVariable); - } - - for (var i = 1; i < managedParameters.Length; i++) - { - var variable = indirectVariables[i]; - if (variable == null) - continue; - body.Emit(OpCodes.Ldarg_S, i); - body.Emit(OpCodes.Ldloc, variable); - var directType = managedParameters[i].GetElementType(); - if (directType == typeof(string)) - body.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.ManagedStringToIl2Cpp))!); - else if (!directType.IsValueType) - body.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.Il2CppObjectBaseToPtr))!); - body.Emit(InjectorHelpers.StIndOpcodes.TryGetValue(directType, out var stindOpCodde) - ? stindOpCodde - : OpCodes.Stind_I); - } - // body.Emit(OpCodes.Ret); // breaks coreclr - - var exceptionLocal = body.DeclareLocal(typeof(Exception)); - body.BeginCatchBlock(typeof(Exception)); - body.Emit(OpCodes.Stloc, exceptionLocal); - body.Emit(OpCodes.Ldstr, "Exception in IL2CPP-to-Managed trampoline, not passing it to il2cpp: "); - body.Emit(OpCodes.Ldloc, exceptionLocal); - body.Emit(OpCodes.Callvirt, typeof(object).GetMethod(nameof(ToString))!); - body.Emit(OpCodes.Call, - typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) })!); - body.Emit(OpCodes.Call, typeof(ClassInjector).GetMethod(nameof(LogError), BindingFlags.Static | BindingFlags.NonPublic)!); - - body.EndExceptionBlock(); - - if (managedReturnVariable != null) - { - body.Emit(OpCodes.Ldloc, managedReturnVariable); - if (monoMethod.ReturnType == typeof(string)) - body.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.ManagedStringToIl2Cpp))!); - else if (!monoMethod.ReturnType.IsValueType) - body.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.Il2CppObjectBaseToPtr))!); - } - - body.Emit(OpCodes.Ret); - - var @delegate = method.CreateDelegate(delegateType); - GCHandle.Alloc(@delegate); // pin it forever - return @delegate; - } - - private static void LogError(string message) - { - Logger.Instance.LogError("{Message}", message); - } - - private static string ExtractSignature(MethodInfo monoMethod) - { - var builder = new StringBuilder(); - builder.Append(monoMethod.ReturnType.NativeType().Name); - builder.Append(monoMethod.IsStatic ? "" : "This"); - foreach (var parameterInfo in monoMethod.GetParameters()) - builder.Append(parameterInfo.ParameterType.NativeType().Name); - return builder.ToString(); - } - - private static Type RewriteType(Type type) - { - if (type.IsByRef) - return RewriteType(type.GetElementType()).MakeByRefType(); - - if (type.IsValueType && !type.IsEnum) - return type; - - if (type == typeof(string)) - return type; - - if (type.IsArray) - { - var elementType = type.GetElementType(); - if (elementType!.FullName == "System.String") return typeof(Il2CppStringArray); - - var convertedElementType = RewriteType(elementType); - if (elementType.IsGenericParameter) return typeof(Il2CppArrayBase<>).MakeGenericType(convertedElementType); - - return (convertedElementType.IsValueType ? typeof(Il2CppStructArray<>) : typeof(Il2CppReferenceArray<>)) - .MakeGenericType(convertedElementType); - } - - if (type.FullName!.StartsWith("System")) - { - var fullName = $"Il2Cpp{type.FullName}"; - var resolvedType = Type.GetType($"{fullName}, Il2Cpp{type.Assembly.GetName().Name}", false); - if (resolvedType != null) - return resolvedType; - - return AppDomain.CurrentDomain.GetAssemblies() - .Select(a => a.GetType(fullName, false)) - .First(t => t != null); - } - - return type; - } - - private static string GetIl2CppTypeFullName(Il2CppTypeStruct* typePointer) - { - var klass = UnityVersionHandler.Wrap((Il2CppClass*)IL2CPP.il2cpp_class_from_type((IntPtr)typePointer)); - var assembly = UnityVersionHandler.Wrap(UnityVersionHandler.Wrap(klass.Image).Assembly); - var fullName = new StringBuilder(); - var names = new Stack(); - var declaringType = klass; - var outerType = klass; - do - { - names.Push(Marshal.PtrToStringUTF8(declaringType.Name) ?? ""); - outerType = declaringType; - } - while ((declaringType = UnityVersionHandler.Wrap(declaringType.DeclaringType)) != default); - var namespaceName = outerType.Namespace != IntPtr.Zero ? Marshal.PtrToStringUTF8(outerType.Namespace) ?? "" : ""; - - fullName.Append(namespaceName); - if (namespaceName.Length > 0) - fullName.Append('.'); - fullName.Append(string.Join("+", names)); - - var assemblyName = Marshal.PtrToStringUTF8(assembly.Name.Name); - if (assemblyName != "mscorlib") - { - fullName.Append(", "); - fullName.Append(assemblyName); - } - - return fullName.ToString(); - } - - internal static Type SystemTypeFromIl2CppType(Il2CppTypeStruct* typePointer) - { - var fullName = GetIl2CppTypeFullName(typePointer); - var type = Type.GetType(fullName) - ?? Type.GetType(fullName.Contains('.') ? "Il2Cpp" + fullName : "Il2Cpp." + fullName) - ?? throw new NullReferenceException($"Couldn't find System.Type for Il2Cpp type: {fullName}"); - - INativeTypeStruct wrappedType = UnityVersionHandler.Wrap(typePointer); - if (wrappedType.Type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) - { - Il2CppGenericClass* genericClass = (Il2CppGenericClass*)wrappedType.Data; - uint argc = genericClass->context.class_inst->type_argc; - Il2CppTypeStruct** argv = genericClass->context.class_inst->type_argv; - Type[] genericArguments = new Type[argc]; - - for (int i = 0; i < argc; i++) - { - genericArguments[i] = SystemTypeFromIl2CppType(argv[i]); - } - type = type.MakeGenericType(genericArguments); - } - if (wrappedType.ByRef) - type = type.MakeByRefType(); - return RewriteType(type); - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void InvokerDelegateMetadataV29(IntPtr methodPointer, Il2CppMethodInfo* methodInfo, IntPtr obj, IntPtr* args, IntPtr* returnValue); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate IntPtr InvokerDelegate(IntPtr methodPointer, Il2CppMethodInfo* methodInfo, IntPtr obj, IntPtr* args); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void VoidCtorDelegate(IntPtr objectPointer); -} diff --git a/Il2CppInterop.Runtime/Injection/DetourProvider.cs b/Il2CppInterop.Runtime/Injection/DetourProvider.cs index 24ce64ba..3b6b626a 100644 --- a/Il2CppInterop.Runtime/Injection/DetourProvider.cs +++ b/Il2CppInterop.Runtime/Injection/DetourProvider.cs @@ -3,28 +3,17 @@ namespace Il2CppInterop.Runtime.Injection; -public interface IDetour : IDisposable -{ - nint Target { get; } - nint Detour { get; } - nint OriginalTrampoline { get; } - - void Apply(); - T GenerateTrampoline() where T : Delegate; -} public interface IDetourProvider { - IDetour Create(nint original, TDelegate target) where TDelegate : Delegate; + IDisposable Create(nint original, TDelegate target, out TDelegate trampoline) where TDelegate : Delegate; } internal static class Detour { - public static IDetour Apply(nint original, T target, out T trampoline) where T : Delegate + public static IDisposable Apply(nint original, T target, out T trampoline) where T : Delegate { - var detour = Il2CppInteropRuntime.Instance.DetourProvider.Create(original, target); - trampoline = detour.GenerateTrampoline(); - detour.Apply(); - return detour; + return Il2CppInteropRuntime.Instance.DetourProvider.Create(original, target, out trampoline); + } } diff --git a/Il2CppInterop.Runtime/Injection/EnumInjector.cs b/Il2CppInterop.Runtime/Injection/EnumInjector.cs deleted file mode 100644 index 4dd71e17..00000000 --- a/Il2CppInterop.Runtime/Injection/EnumInjector.cs +++ /dev/null @@ -1,298 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.InteropServices; -using Il2CppInterop.Common; -using Il2CppInterop.Runtime.Runtime; -using Il2CppSystem; -using Microsoft.Extensions.Logging; -using ArgumentException = System.ArgumentException; -using Convert = System.Convert; -using Enum = System.Enum; -using IntPtr = System.IntPtr; -using Type = System.Type; - -namespace Il2CppInterop.Runtime.Injection; - -public static unsafe class EnumInjector -{ - // fieldInfo : defaultValueBlob - private static readonly ConcurrentDictionary s_DefaultValueOverrides = new(); - - private static readonly IntPtr value__Cached = Marshal.StringToCoTaskMemUTF8("value__"); - - internal static bool GetDefaultValueOverride(Il2CppFieldInfo* fieldInfo, out IntPtr defaultValueBlob) - { - return s_DefaultValueOverrides.TryGetValue((IntPtr)fieldInfo, out defaultValueBlob); - } - - public static void InjectEnumValues(Dictionary valuesToAdd) where TEnum : Enum - { - InjectEnumValues(typeof(TEnum), valuesToAdd); - } - - public static void InjectEnumValues(Type type, Dictionary valuesToAdd) - { - if (type == null) - throw new ArgumentException("Type argument cannot be null"); - if (!type.IsEnum) - throw new ArgumentException("Type argument needs to be an enum"); - - var enumPtr = Il2CppClassPointerStore.GetNativeClassPointer(type); - if (enumPtr == IntPtr.Zero) - throw new ArgumentException("Type needs to be an Il2Cpp enum"); - - InjectorHelpers.Setup(); - - InjectorHelpers.ClassInit((Il2CppClass*)enumPtr); - - var il2cppEnum = UnityVersionHandler.Wrap((Il2CppClass*)enumPtr); - var newFieldCount = il2cppEnum.FieldCount + valuesToAdd.Count; - var newFields = (Il2CppFieldInfo*)Marshal.AllocHGlobal(newFieldCount * UnityVersionHandler.FieldInfoSize()); - - int fieldIdx; - for (fieldIdx = 0; fieldIdx < il2cppEnum.FieldCount; ++fieldIdx) - { - var offset = fieldIdx * UnityVersionHandler.FieldInfoSize(); - var oldField = UnityVersionHandler.Wrap(il2cppEnum.Fields + offset); - var newField = UnityVersionHandler.Wrap(newFields + offset); - - newField.Name = oldField.Name; - newField.Type = oldField.Type; - newField.Parent = oldField.Parent; - newField.Offset = oldField.Offset; - - // Move the default value blob from the old field to the new one - if (s_DefaultValueOverrides.TryRemove((IntPtr)oldField.FieldInfoPointer, out var blob)) - s_DefaultValueOverrides[(IntPtr)newField.FieldInfoPointer] = blob; - } - - var enumElementType = UnityVersionHandler.Wrap(il2cppEnum.ElementClass).ByValArg; - - foreach (var newData in valuesToAdd) - { - var offset = fieldIdx * UnityVersionHandler.FieldInfoSize(); - var newField = UnityVersionHandler.Wrap(newFields + offset); - newField.Name = Marshal.StringToCoTaskMemUTF8(newData.Key); - newField.Type = enumElementType.TypePointer; - newField.Parent = il2cppEnum.ClassPointer; - newField.Offset = 0; - - CreateOrUpdateFieldDefaultValue(newField.FieldInfoPointer, enumElementType.TypePointer, newData.Value); - - ++fieldIdx; - } - - il2cppEnum.FieldCount = (ushort)newFieldCount; - il2cppEnum.Fields = newFields; - - var runtimeEnumType = Il2CppType.TypeFromPointer(enumPtr).TryCast(); - if (runtimeEnumType != null) - // The mono runtime caches the enum names and values the first time they are requested, so we reset this cache - runtimeEnumType.GenericCache = null; - } - - private static int GetEnumElementSize(Il2CppTypeEnum type) - { - return type switch - { - Il2CppTypeEnum.IL2CPP_TYPE_I1 => sizeof(sbyte), - Il2CppTypeEnum.IL2CPP_TYPE_U1 => sizeof(byte), - - Il2CppTypeEnum.IL2CPP_TYPE_CHAR => sizeof(char), - - Il2CppTypeEnum.IL2CPP_TYPE_I2 => sizeof(short), - Il2CppTypeEnum.IL2CPP_TYPE_U2 => sizeof(ushort), - - Il2CppTypeEnum.IL2CPP_TYPE_I4 => sizeof(int), - Il2CppTypeEnum.IL2CPP_TYPE_U4 => sizeof(uint), - - Il2CppTypeEnum.IL2CPP_TYPE_I8 => sizeof(long), - Il2CppTypeEnum.IL2CPP_TYPE_U8 => sizeof(ulong), - - _ => throw new ArgumentException($"The type provided {type} is invalid") - }; - } - - private static IntPtr AllocateNewDefaultValueBlob(Il2CppTypeEnum type) - { - var size = GetEnumElementSize(type); - var blob = Marshal.AllocHGlobal(size); - Logger.Instance.LogTrace("Allocated default value blob at 0x{Blob} of {Size} for {Type}", blob.ToInt64().ToString("X2"), size, type); - return blob; - } - - private static IntPtr CreateOrUpdateFieldDefaultValue(Il2CppFieldInfo* field, Il2CppTypeStruct* type, object value) - { - var typeEnum = UnityVersionHandler.Wrap(type).Type; - - if (!GetDefaultValueOverride(field, out var newBlob)) - { - newBlob = AllocateNewDefaultValueBlob(typeEnum); - s_DefaultValueOverrides[(IntPtr)field] = newBlob; - } - - SetFieldDefaultValue(newBlob, typeEnum, value); - return newBlob; - } - - private static void SetFieldDefaultValue(IntPtr blob, Il2CppTypeEnum type, object value) - { - var valueData = Convert.ToInt64(value); - switch (type) - { - case Il2CppTypeEnum.IL2CPP_TYPE_I1: - *(sbyte*)blob = (sbyte)valueData; - break; - case Il2CppTypeEnum.IL2CPP_TYPE_U1: - *(byte*)blob = (byte)valueData; - break; - - case Il2CppTypeEnum.IL2CPP_TYPE_CHAR: - *(char*)blob = (char)valueData; - break; - - case Il2CppTypeEnum.IL2CPP_TYPE_I2: - *(short*)blob = (short)valueData; - break; - case Il2CppTypeEnum.IL2CPP_TYPE_U2: - *(ushort*)blob = (ushort)valueData; - break; - - case Il2CppTypeEnum.IL2CPP_TYPE_I4: - *(int*)blob = (int)valueData; - break; - case Il2CppTypeEnum.IL2CPP_TYPE_U4: - *(uint*)blob = (uint)valueData; - break; - - case Il2CppTypeEnum.IL2CPP_TYPE_I8: - *(long*)blob = valueData; - break; - case Il2CppTypeEnum.IL2CPP_TYPE_U8: - *(ulong*)blob = (ulong)valueData; - break; - - default: throw new ArgumentException($"The type provided {type} is invalid"); - } - } - - public static void RegisterEnumInIl2Cpp(bool logSuccess = true) where TEnum : Enum - { - RegisterEnumInIl2Cpp(typeof(TEnum), logSuccess); - } - - public static void RegisterEnumInIl2Cpp(Type type, bool logSuccess = true) - { - if (type == null) - throw new ArgumentException("Type argument cannot be null"); - - if (!type.IsEnum) - throw new ArgumentException("Type argument needs to be an enum"); - - var enumPtr = Il2CppClassPointerStore.GetNativeClassPointer(type); - if (enumPtr != IntPtr.Zero) - return; - - InjectorHelpers.Setup(); - - var baseEnum = - UnityVersionHandler.Wrap((Il2CppClass*)Il2CppClassPointerStore.NativeClassPtr); - - InjectorHelpers.ClassInit(baseEnum.ClassPointer); - - var il2cppEnum = UnityVersionHandler.NewClass(baseEnum.VtableCount); - var elementClass = - UnityVersionHandler.Wrap( - (Il2CppClass*)Il2CppClassPointerStore.GetNativeClassPointer(Enum.GetUnderlyingType(type))); - - il2cppEnum.Image = InjectorHelpers.InjectedImage.ImagePointer; - il2cppEnum.Class = il2cppEnum.CastClass = il2cppEnum.ElementClass = elementClass.ClassPointer; - il2cppEnum.Parent = baseEnum.ClassPointer; - il2cppEnum.ActualSize = il2cppEnum.InstanceSize = - (uint)(baseEnum.InstanceSize + GetEnumElementSize(elementClass.ByValArg.Type)); - il2cppEnum.NativeSize = -1; - - il2cppEnum.ValueType = true; - il2cppEnum.EnumType = true; - il2cppEnum.Initialized = true; - il2cppEnum.InitializedAndNoError = true; - il2cppEnum.SizeInited = true; - il2cppEnum.HasFinalize = true; - il2cppEnum.IsVtableInitialized = true; - - il2cppEnum.Name = Marshal.StringToCoTaskMemUTF8(type.Name); - il2cppEnum.Namespace = Marshal.StringToCoTaskMemUTF8(type.Namespace ?? string.Empty); - - var token = InjectorHelpers.CreateClassToken(il2cppEnum.Pointer); - il2cppEnum.ThisArg.Data = il2cppEnum.ByValArg.Data = (IntPtr)token; - - // Has to be IL2CPP_TYPE_VALUETYPE because IL2CPP_TYPE_ENUM isn't used - il2cppEnum.ThisArg.Type = il2cppEnum.ByValArg.Type = Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE; - - il2cppEnum.Flags = (Il2CppClassAttributes)type.Attributes; - - il2cppEnum.VtableCount = baseEnum.VtableCount; - var vtable = (VirtualInvokeData*)il2cppEnum.VTable; - var baseVTable = (VirtualInvokeData*)baseEnum.VTable; - for (var i = 0; i < baseEnum.VtableCount; i++) - vtable[i] = baseVTable[i]; - - var enumValues = Enum.GetValues(type); - var enumNames = Enum.GetNames(type); - il2cppEnum.FieldCount = (ushort)(enumValues.Length + 1); // value__ - - var il2cppFields = - (Il2CppFieldInfo*)Marshal.AllocHGlobal(il2cppEnum.FieldCount * UnityVersionHandler.FieldInfoSize()); - var valueField = UnityVersionHandler.Wrap(il2cppFields); - valueField.Name = value__Cached; - valueField.Parent = il2cppEnum.ClassPointer; - valueField.Offset = (int)baseEnum.InstanceSize; - - var enumValueType = UnityVersionHandler.NewType(); - enumValueType.Data = elementClass.ThisArg.Data; - enumValueType.Attrs = (ushort)(FieldAttributes.Private | FieldAttributes.Family | FieldAttributes.SpecialName | - FieldAttributes.RTSpecialName); - enumValueType.Type = elementClass.ThisArg.Type; - enumValueType.ByRef = elementClass.ThisArg.ByRef; - enumValueType.Pinned = elementClass.ThisArg.Pinned; - - valueField.Type = enumValueType.TypePointer; - - var enumConstType = UnityVersionHandler.NewType(); - enumConstType.Data = il2cppEnum.ThisArg.Data; - enumConstType.Attrs = (ushort)(FieldAttributes.Private | FieldAttributes.Family | FieldAttributes.InitOnly | - FieldAttributes.Literal | FieldAttributes.HasDefault); - enumConstType.Type = Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE; - enumConstType.ByRef = false; - enumConstType.Pinned = false; - - for (var i = 1; i < il2cppEnum.FieldCount; i++) - { - var fieldValue = enumValues.GetValue(i - 1); - var il2cppField = UnityVersionHandler.Wrap(il2cppFields + i * UnityVersionHandler.FieldInfoSize()); - il2cppField.Name = Marshal.StringToCoTaskMemUTF8(enumNames[i - 1]); - il2cppField.Type = enumConstType.TypePointer; - il2cppField.Parent = il2cppEnum.ClassPointer; - il2cppField.Offset = 0; - - CreateOrUpdateFieldDefaultValue(il2cppField.FieldInfoPointer, elementClass.ThisArg.TypePointer, fieldValue); - } - - il2cppEnum.Fields = il2cppFields; - - il2cppEnum.TypeHierarchyDepth = (byte)(1 + baseEnum.TypeHierarchyDepth); - il2cppEnum.TypeHierarchy = (Il2CppClass**)Marshal.AllocHGlobal(il2cppEnum.TypeHierarchyDepth * sizeof(void*)); - for (var i = 0; i < il2cppEnum.TypeHierarchyDepth; i++) - il2cppEnum.TypeHierarchy[i] = baseEnum.TypeHierarchy[i]; - il2cppEnum.TypeHierarchy[il2cppEnum.TypeHierarchyDepth - 1] = il2cppEnum.ClassPointer; - - RuntimeSpecificsStore.SetClassInfo(il2cppEnum.Pointer, true); - Il2CppClassPointerStore.SetNativeClassPointer(type, il2cppEnum.Pointer); - - InjectorHelpers.AddTypeToLookup(type, il2cppEnum.Pointer); - - if (logSuccess) - Logger.Instance.LogInformation("Registered managed enum {Type} in il2cpp domain", type); - } -} diff --git a/Il2CppInterop.Runtime/Injection/Hooks/Class_FromIl2CppType_Hook.cs b/Il2CppInterop.Runtime/Injection/Hooks/Class_FromIl2CppType_Hook.cs index bac0354c..d8212e7a 100644 --- a/Il2CppInterop.Runtime/Injection/Hooks/Class_FromIl2CppType_Hook.cs +++ b/Il2CppInterop.Runtime/Injection/Hooks/Class_FromIl2CppType_Hook.cs @@ -27,7 +27,10 @@ internal readonly struct Il2CppType private Il2CppClass* Hook(Il2CppType* type, bool throwOnError) { - if ((nint)type->data < 0 && (type->type == Il2CppTypeEnum.IL2CPP_TYPE_CLASS || type->type == Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE)) + if (type == null) + return Original(type, throwOnError); + + if ((nint)type->data < 0 && (type->type is Il2CppTypeEnum.IL2CPP_TYPE_CLASS or Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE)) { InjectorHelpers.s_InjectedClasses.TryGetValue((nint)type->data, out var classPointer); return (Il2CppClass*)classPointer; diff --git a/Il2CppInterop.Runtime/Injection/Hooks/Class_GetFieldDefaultValue_Hook.cs b/Il2CppInterop.Runtime/Injection/Hooks/Class_GetFieldDefaultValue_Hook.cs index a0a42582..f9aacd72 100644 --- a/Il2CppInterop.Runtime/Injection/Hooks/Class_GetFieldDefaultValue_Hook.cs +++ b/Il2CppInterop.Runtime/Injection/Hooks/Class_GetFieldDefaultValue_Hook.cs @@ -2,8 +2,8 @@ using System.Linq; using System.Runtime.InteropServices; using Il2CppInterop.Common; -using Il2CppInterop.Common.Extensions; using Il2CppInterop.Common.XrefScans; +using Il2CppInterop.Runtime.Extensions; using Il2CppInterop.Runtime.Runtime; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class; using Il2CppInterop.Runtime.Runtime.VersionSpecific.FieldInfo; @@ -22,7 +22,7 @@ internal unsafe class Class_GetFieldDefaultValue_Hook : Hook - { - public override string TargetMethodName => "GenericMethod::GetMethod"; - public override MethodDelegate GetDetour() => Hook; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate Il2CppMethodInfo* MethodDelegate(Il2CppGenericMethod* gmethod, bool copyMethodPtr); - - private Il2CppMethodInfo* Hook(Il2CppGenericMethod* gmethod, bool copyMethodPtr) - { - if (gmethod == null || gmethod->methodDefinition == null) - return Original(gmethod, copyMethodPtr); - - if (ClassInjector.InflatedMethodFromContextDictionary.TryGetValue((IntPtr)gmethod->methodDefinition, out var methods)) - { - var instancePointer = gmethod->context.method_inst; - if (methods.Item2.TryGetValue((IntPtr)instancePointer, out var inflatedMethodPointer)) - return (Il2CppMethodInfo*)inflatedMethodPointer; - - var typeArguments = new Type[instancePointer->type_argc]; - for (var i = 0; i < instancePointer->type_argc; i++) - typeArguments[i] = ClassInjector.SystemTypeFromIl2CppType(instancePointer->type_argv[i]); - var inflatedMethod = methods.Item1.MakeGenericMethod(typeArguments); - Logger.Instance.LogTrace("Inflated method: {InflatedMethod}", inflatedMethod.Name); - inflatedMethodPointer = (IntPtr)ClassInjector.ConvertMethodInfo(inflatedMethod, - UnityVersionHandler.Wrap(UnityVersionHandler.Wrap(gmethod->methodDefinition).Class)); - methods.Item2.Add((IntPtr)instancePointer, inflatedMethodPointer); - - return (Il2CppMethodInfo*)inflatedMethodPointer; - } - - return Original(gmethod, copyMethodPtr); - } - - private static readonly MemoryUtils.SignatureDefinition[] s_Signatures = - { - // Unity 2021.2.5 (x64) - new MemoryUtils.SignatureDefinition - { - pattern = "\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x56\x57\x41\x54\x41\x56\x41\x57\x48\x81\xEC\xB0\x00", - mask = "xxxxxxxxxxxxxxxxxxxxxxx", - xref = false - } - }; - - // Compilers might change method location (Unity 2021.2+) - // We do a signature scan to find the correct method - - public override IntPtr FindTargetMethod() - { - var genericMethodGetMethod = s_Signatures - .Select(s => MemoryUtils.FindSignatureInModule(InjectorHelpers.Il2CppModule, s)) - .FirstOrDefault(p => p != 0); - - if (genericMethodGetMethod == 0) - { - var getVirtualMethodAPI = InjectorHelpers.GetIl2CppExport(nameof(IL2CPP.il2cpp_object_get_virtual_method)); - Logger.Instance.LogTrace("il2cpp_object_get_virtual_method: 0x{GetVirtualMethodApiAddress}", getVirtualMethodAPI.ToInt64().ToString("X2")); - - var getVirtualMethod = XrefScannerLowLevel.JumpTargets(getVirtualMethodAPI).Single(); - Logger.Instance.LogTrace("Object::GetVirtualMethod: 0x{GetVirtualMethodAddress}", getVirtualMethod.ToInt64().ToString("X2")); - - var getVirtualMethodXrefs = XrefScannerLowLevel.JumpTargets(getVirtualMethod).ToArray(); - - // If the game is built with IL2CPP Master setting, this will return 0 entries, so we do another xref scan with retn instructions ignored. - if (getVirtualMethodXrefs.Length == 0) - { - genericMethodGetMethod = XrefScannerLowLevel.JumpTargets(getVirtualMethod, true).Last(); - } - else - { - // U2021.2.0+, there's additional shim that takes 3 parameters - // On U2020.3.41+ there is also a shim, which gets inlined with one added in U2021.2.0+ in release builds - if (UnityVersionHandler.HasShimForGetMethod) - { - var shim = getVirtualMethodXrefs.Last(); - - var shimXrefs = XrefScannerLowLevel.JumpTargets(shim).ToArray(); - - // If the xref count is 1, it probably means the target is after ret - if (Il2CppInteropRuntime.Instance.UnityVersion.Major == 2020 && shimXrefs.Length == 1) - { - shimXrefs = XrefScannerLowLevel.JumpTargets(shim, true).ToArray(); - } - - genericMethodGetMethod = shimXrefs.Take(2).Last(); - } - else - { - genericMethodGetMethod = getVirtualMethodXrefs.Last(); - } - } - } - - return genericMethodGetMethod; - } - } -} diff --git a/Il2CppInterop.Runtime/Injection/Hooks/GenericMethod_GetMethod_Unity6_Hook.cs b/Il2CppInterop.Runtime/Injection/Hooks/GenericMethod_GetMethod_Unity6_Hook.cs deleted file mode 100644 index b1b58f72..00000000 --- a/Il2CppInterop.Runtime/Injection/Hooks/GenericMethod_GetMethod_Unity6_Hook.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Linq; -using System.Runtime.InteropServices; -using Il2CppInterop.Common; -using Il2CppInterop.Common.XrefScans; -using Il2CppInterop.Runtime.Runtime; -using Il2CppInterop.Runtime.Startup; -using Microsoft.Extensions.Logging; - -namespace Il2CppInterop.Runtime.Injection.Hooks -{ - /// Unity 6 (6000.x.x): the 1-param GetMethod(const Il2CppGenericMethod&) is inlined into - /// the 3-param GetMethod(const MethodInfo*, const Il2CppGenericInst*, const Il2CppGenericInst*). - /// We use the hook with 3 param correctly - internal unsafe class GenericMethod_GetMethod_Unity6_Hook : Hook - { - public override string TargetMethodName => "GenericMethod::GetMethod"; - public override MethodDelegate GetDetour() => Hook; - - // CRITICAL: Use Cdecl for Linux x64 (System V ABI) - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate Il2CppMethodInfo* MethodDelegate(Il2CppMethodInfo* methodDefinition, Il2CppGenericInst* classInst, Il2CppGenericInst* methodInst); - - private Il2CppMethodInfo* Hook(Il2CppMethodInfo* methodDefinition, Il2CppGenericInst* classInst, Il2CppGenericInst* methodInst) - { - // Direct pass-through if the definition is null for safety - if (methodDefinition == null) - return Original(methodDefinition, classInst, methodInst); - - try - { - // Check if the dictionary even exists before trying to access it - if (ClassInjector.InflatedMethodFromContextDictionary == null) - return Original(methodDefinition, classInst, methodInst); - - if (ClassInjector.InflatedMethodFromContextDictionary.TryGetValue((IntPtr)methodDefinition, out var methods)) - { - // If the dictionary entry is malformed, skip - if (methods.Item1 == null || methods.Item2 == null) - return Original(methodDefinition, classInst, methodInst); - - // If it's a class-level generic without method generics, methodInst is null - if (methodInst == null) - return Original(methodDefinition, classInst, methodInst); - - // Check our cache first - if (methods.Item2.TryGetValue((IntPtr)methodInst, out var inflatedMethodPointer)) - return (Il2CppMethodInfo*)inflatedMethodPointer; - - // Validate the GenericInst structure before reading it - // On Linux, memory alignment is strict. Ensure type_argc is sane. - if (methodInst->type_argc > 256 || methodInst->type_argv == null) - return Original(methodDefinition, classInst, methodInst); - - var typeArguments = new Type[methodInst->type_argc]; - for (var i = 0; i < methodInst->type_argc; i++) - { - var il2cppType = methodInst->type_argv[i]; - if (il2cppType == null) return Original(methodDefinition, classInst, methodInst); - - typeArguments[i] = ClassInjector.SystemTypeFromIl2CppType(il2cppType); - } - - var inflatedMethod = methods.Item1.MakeGenericMethod(typeArguments); - Logger.Instance.LogTrace("Inflated method: {InflatedMethod}", inflatedMethod.Name); - - // Use the specific UnityVersionHandler for Unity 6 - var wrappedMethod = UnityVersionHandler.Wrap(methodDefinition); - if (wrappedMethod == null) return Original(methodDefinition, classInst, methodInst); - - inflatedMethodPointer = (IntPtr)ClassInjector.ConvertMethodInfo( - inflatedMethod, UnityVersionHandler.Wrap(wrappedMethod.Class)); - - // Cache the result to prevent recalculating next time - methods.Item2.Add((IntPtr)methodInst, inflatedMethodPointer); - - return (Il2CppMethodInfo*)inflatedMethodPointer; - } - } - catch (Exception ex) - { - // CRITICAL: On Linux, an unhandled exception in a hook = SIGSEGV. - // We must catch and log, then return original. -#if DEBUG - Logger.Instance.LogError($"[GenericHook] Exception: {ex.Message}"); -#endif - } - - return Original(methodDefinition, classInst, methodInst); - } - - public override IntPtr FindTargetMethod() - { - var getVirtualMethodAPI = InjectorHelpers.GetIl2CppExport(nameof(IL2CPP.il2cpp_object_get_virtual_method)); - if (getVirtualMethodAPI == IntPtr.Zero) return IntPtr.Zero; - - // Follow the jump into the actual function body - var getVirtualMethod = XrefScannerLowLevel.JumpTargets(getVirtualMethodAPI).FirstOrDefault(); - if (getVirtualMethod == IntPtr.Zero) return IntPtr.Zero; - - // We are looking for the call to GetGenericVirtualMethod - var xrefs = XrefScannerLowLevel.JumpTargets(getVirtualMethod).ToArray(); - if (xrefs.Length == 0) return IntPtr.Zero; - - // On Linux Unity 6, it's usually the LAST jump before the end of the function - IntPtr getGenericVirtualMethod = xrefs.Last(); - - // Now, inside GetGenericVirtualMethod, there is a tail-call (jmp) to GenericMethod::GetMethod - var finalXrefs = XrefScannerLowLevel.JumpTargets(getGenericVirtualMethod).ToArray(); - - if (finalXrefs.Length == 0) - return IntPtr.Zero; - - IntPtr candidate = finalXrefs.Last(); - - // VERIFICATION: On Linux, GenericMethod::GetMethod is a large function. - // If the address is too close to the caller, it's probably wrong. -#if DEBUG - Logger.Instance.LogDebug($"[Scanner] Found GenericMethod::GetMethod candidate: 0x{candidate:X}"); -#endif - - return candidate; - } - } -} diff --git a/Il2CppInterop.Runtime/Injection/Hooks/MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.cs b/Il2CppInterop.Runtime/Injection/Hooks/MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.cs index 943a9ad0..147aa39b 100644 --- a/Il2CppInterop.Runtime/Injection/Hooks/MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.cs +++ b/Il2CppInterop.Runtime/Injection/Hooks/MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook.cs @@ -31,13 +31,13 @@ private IntPtr FindGetTypeInfoFromTypeDefinitionIndex(bool forceICallMethod = fa IntPtr getTypeInfoFromTypeDefinitionIndex = IntPtr.Zero; // il2cpp_image_get_class is added in 2018.3.0f1 - if (Il2CppInteropRuntime.Instance.UnityVersion < new Version(2018, 3, 0) || forceICallMethod) + if (Il2CppInteropRuntime.Instance.UnityVersion.LessThan(2018, 3, 0) || forceICallMethod) { // (Kasuromi): RuntimeHelpers.InitializeArray calls an il2cpp icall, proxy function does some magic before it invokes it // https://github.com/Unity-Technologies/mono/blob/unity-2018.2/mcs/class/corlib/System.Runtime.CompilerServices/RuntimeHelpers.cs#L53-L54 IntPtr runtimeHelpersInitializeArray = InjectorHelpers.GetIl2CppMethodPointer( typeof(Il2CppSystem.Runtime.CompilerServices.RuntimeHelpers) - .GetMethod("InitializeArray", new Type[] { typeof(Il2CppSystem.Array), typeof(IntPtr) }) + .GetMethod(nameof(Il2CppSystem.Runtime.CompilerServices.RuntimeHelpers.InitializeArray), new Type[] { typeof(Il2CppSystem.Array), typeof(IntPtr) }) ); Logger.Instance.LogTrace("Il2CppSystem.Runtime.CompilerServices.RuntimeHelpers::InitializeArray: 0x{RuntimeHelpersInitializeArrayAddress}", runtimeHelpersInitializeArray.ToInt64().ToString("X2")); diff --git a/Il2CppInterop.Runtime/Injection/InjectorHelpers.cs b/Il2CppInterop.Runtime/Injection/InjectorHelpers.cs index 9bd9b0de..7916eab3 100644 --- a/Il2CppInterop.Runtime/Injection/InjectorHelpers.cs +++ b/Il2CppInterop.Runtime/Injection/InjectorHelpers.cs @@ -4,20 +4,12 @@ using System.Diagnostics; using System.Linq; using System.Reflection; -using System.Reflection.Emit; using System.Runtime.InteropServices; using System.Threading; using Il2CppInterop.Common; -using Il2CppInterop.Common.Extensions; -using Il2CppInterop.Common.XrefScans; using Il2CppInterop.Runtime.Injection.Hooks; using Il2CppInterop.Runtime.Runtime; -using Il2CppInterop.Runtime.Runtime.VersionSpecific.Assembly; -using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class; -using Il2CppInterop.Runtime.Runtime.VersionSpecific.FieldInfo; -using Il2CppInterop.Runtime.Runtime.VersionSpecific.Image; using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo; -using Il2CppInterop.Runtime.Startup; using Microsoft.Extensions.Logging; namespace Il2CppInterop.Runtime.Injection @@ -25,56 +17,18 @@ namespace Il2CppInterop.Runtime.Injection internal static unsafe class InjectorHelpers { internal static Assembly Il2CppMscorlib = typeof(Il2CppSystem.Type).Assembly; - internal static INativeAssemblyStruct InjectedAssembly; - internal static INativeImageStruct InjectedImage; internal static ProcessModule Il2CppModule = Process.GetCurrentProcess() .Modules.OfType() .Single((x) => x.ModuleName is "GameAssembly.dll" or "GameAssembly.so" or "UserAssembly.dll"); internal static IntPtr Il2CppHandle = NativeLibrary.Load("GameAssembly", typeof(InjectorHelpers).Assembly, null); - internal static readonly Dictionary StIndOpcodes = new() - { - [typeof(byte)] = OpCodes.Stind_I1, - [typeof(sbyte)] = OpCodes.Stind_I1, - [typeof(bool)] = OpCodes.Stind_I1, - [typeof(short)] = OpCodes.Stind_I2, - [typeof(ushort)] = OpCodes.Stind_I2, - [typeof(int)] = OpCodes.Stind_I4, - [typeof(uint)] = OpCodes.Stind_I4, - [typeof(long)] = OpCodes.Stind_I8, - [typeof(ulong)] = OpCodes.Stind_I8, - [typeof(float)] = OpCodes.Stind_R4, - [typeof(double)] = OpCodes.Stind_R8 - }; - - private static void CreateInjectedAssembly() - { - InjectedAssembly = UnityVersionHandler.NewAssembly(); - InjectedImage = UnityVersionHandler.NewImage(); - - InjectedAssembly.Name.Name = Marshal.StringToCoTaskMemUTF8("InjectedMonoTypes"); - - InjectedImage.Assembly = InjectedAssembly.AssemblyPointer; - InjectedImage.Dynamic = 1; - InjectedImage.Name = InjectedAssembly.Name.Name; - if (InjectedImage.HasNameNoExt) - InjectedImage.NameNoExt = InjectedAssembly.Name.Name; - } - - private static readonly GenericMethod_GetMethod_Hook GenericMethodGetMethodHook = new(); - private static readonly GenericMethod_GetMethod_Unity6_Hook GenericMethodGetMethodHook_Unity6 = new(); private static readonly MetadataCache_GetTypeInfoFromTypeDefinitionIndex_Hook GetTypeInfoFromTypeDefinitionIndexHook = new(); private static readonly Class_GetFieldDefaultValue_Hook GetFieldDefaultValueHook = new(); private static readonly Class_FromIl2CppType_Hook FromIl2CppTypeHook = new(); private static readonly Class_FromName_Hook FromNameHook = new(); internal static void Setup() { - if (InjectedAssembly == null) CreateInjectedAssembly(); - if (Il2CppInteropRuntime.Instance.UnityVersion.Major >= 6000) - GenericMethodGetMethodHook_Unity6.ApplyHook(); - else - GenericMethodGetMethodHook.ApplyHook(); GetTypeInfoFromTypeDefinitionIndexHook.ApplyHook(); GetFieldDefaultValueHook.ApplyHook(); ClassInit ??= FindClassInit(); @@ -89,18 +43,10 @@ internal static long CreateClassToken(IntPtr classPointer) return newToken; } - internal static void AddTypeToLookup(IntPtr typePointer) where T : class => AddTypeToLookup(typeof(T), typePointer); - internal static void AddTypeToLookup(Type type, IntPtr typePointer) + internal static void AddTypeToLookup(string assemblyName, string namespaze, string klass, IntPtr typePointer) { - string klass = type.Name; - if (klass == null) return; - string namespaze = type.Namespace ?? string.Empty; - var attribute = Attribute.GetCustomAttribute(type, typeof(Il2CppInterop.Runtime.Attributes.ClassInjectionAssemblyTargetAttribute)) as Il2CppInterop.Runtime.Attributes.ClassInjectionAssemblyTargetAttribute; - - foreach (IntPtr image in (attribute is null) ? IL2CPP.GetIl2CppImages() : attribute.GetImagePointers()) - { - s_ClassNameLookup.Add((namespaze, klass, image), typePointer); - } + var image = AssemblyInjector.GetOrCreateImage(assemblyName).ImagePointer; + s_ClassNameLookup.Add((namespaze, klass, (IntPtr)image), typePointer); } internal static IntPtr GetIl2CppExport(string name) @@ -122,14 +68,13 @@ internal static IntPtr GetIl2CppMethodPointer(MethodBase proxyMethod) { if (proxyMethod == null) return IntPtr.Zero; - FieldInfo methodInfoPointerField = Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(proxyMethod); - if (methodInfoPointerField == null) - throw new ArgumentException($"Couldn't find the generated method info pointer for {proxyMethod.Name}"); + var methodInfoPointerField = Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(proxyMethod) + ?? throw new ArgumentException($"Couldn't find the generated method info pointer for {proxyMethod.Name}"); // Il2CppClassPointerStore calls the static constructor for the type Il2CppClassPointerStore.GetNativeClassPointer(proxyMethod.DeclaringType); - IntPtr methodInfoPointer = (IntPtr)methodInfoPointerField.GetValue(null); + var methodInfoPointer = (IntPtr)methodInfoPointerField.GetValue(null)!; if (methodInfoPointer == IntPtr.Zero) throw new ArgumentException($"Generated method info pointer for {proxyMethod.Name} doesn't point to any il2cpp method info"); INativeMethodInfoStruct methodInfo = UnityVersionHandler.Wrap((Il2CppMethodInfo*)methodInfoPointer); diff --git a/Il2CppInterop.Runtime/MemoryUtils.cs b/Il2CppInterop.Runtime/Injection/MemoryUtils.cs similarity index 97% rename from Il2CppInterop.Runtime/MemoryUtils.cs rename to Il2CppInterop.Runtime/Injection/MemoryUtils.cs index 71aa32e0..51b30a42 100644 --- a/Il2CppInterop.Runtime/MemoryUtils.cs +++ b/Il2CppInterop.Runtime/Injection/MemoryUtils.cs @@ -2,7 +2,7 @@ using System.Linq; using Il2CppInterop.Common.XrefScans; -namespace Il2CppInterop.Runtime; +namespace Il2CppInterop.Runtime.Injection; internal class MemoryUtils { diff --git a/Il2CppInterop.Runtime/Injection/TrampolineHelpers.cs b/Il2CppInterop.Runtime/Injection/TrampolineHelpers.cs index e2526e8c..0605f89a 100644 --- a/Il2CppInterop.Runtime/Injection/TrampolineHelpers.cs +++ b/Il2CppInterop.Runtime/Injection/TrampolineHelpers.cs @@ -1,18 +1,20 @@ using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.CompilerServices; using Il2CppInterop.Runtime.InteropTypes; namespace Il2CppInterop.Runtime.Injection; internal static class TrampolineHelpers { - private static AssemblyBuilder _fixedStructAssembly; - private static ModuleBuilder _fixedStructModuleBuilder; + private static AssemblyBuilder? _fixedStructAssembly; + private static ModuleBuilder? _fixedStructModuleBuilder; private static readonly Dictionary _fixedStructCache = new(); - private static Type GetFixedSizeStructType(int size) + internal static Type GetFixedSizeStructType(int size) { if (_fixedStructCache.TryGetValue(size, out var result)) { @@ -22,7 +24,14 @@ private static Type GetFixedSizeStructType(int size) _fixedStructAssembly ??= AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("FixedSizeStructAssembly"), AssemblyBuilderAccess.Run); _fixedStructModuleBuilder ??= _fixedStructAssembly.DefineDynamicModule("FixedSizeStructAssembly"); - var tb = _fixedStructModuleBuilder.DefineType($"IL2CPPDetour_FixedSizeStruct_{size}b", TypeAttributes.ExplicitLayout, typeof(ValueType), size); + var tb = _fixedStructModuleBuilder.DefineType($"IL2CPPDetour_FixedSizeStruct_{size}b", TypeAttributes.SequentialLayout | TypeAttributes.BeforeFieldInit | TypeAttributes.Sealed, typeof(ValueType)); + tb.DefineField("_element0", typeof(byte), FieldAttributes.Private); + + // Apply InlineArray attribute + var data = new byte[8]; + data[0] = 1; + BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(2), size); + tb.SetCustomAttribute(typeof(InlineArrayAttribute).GetConstructors()[0], data); var type = tb.CreateType(); return _fixedStructCache[size] = type; @@ -30,38 +39,49 @@ private static Type GetFixedSizeStructType(int size) internal static Type NativeType(this Type managedType) { - if (managedType.IsByRef) + if (managedType == typeof(void)) { - var directType = managedType.GetElementType(); - - // bool is byte in Il2Cpp, but int in CLR => force size to be correct - if (directType == typeof(bool)) - { - return typeof(byte).MakeByRefType(); - } - - if (directType == typeof(string) || directType.IsSubclassOf(typeof(Il2CppObjectBase))) - { - return typeof(IntPtr*); - } + return managedType; } - else if (managedType.IsSubclassOf(typeof(Il2CppSystem.ValueType)) && !Environment.Is64BitProcess) + else if (managedType.IsByRef) { - // Struct that's passed on the stack => handle as general struct - uint align = 0; - var fixedSize = IL2CPP.il2cpp_class_value_size(Il2CppClassPointerStore.GetNativeClassPointer(managedType), ref align); - return GetFixedSizeStructType(fixedSize); + throw new NotSupportedException("ByRef types are not supported in NativeType conversion."); } - else if (managedType == typeof(string) || managedType.IsSubclassOf(typeof(Il2CppObjectBase))) // General reference type + else if (managedType.IsArray || managedType.IsSZArray) { - return typeof(IntPtr); + throw new NotSupportedException("Array types are not supported in NativeType conversion."); } - else if (managedType == typeof(bool)) + else if (managedType == typeof(Il2CppSystem.Boolean)) { // bool is byte in Il2Cpp, but int in CLR => force size to be correct return typeof(byte); } + else if (typeof(IByReference).IsAssignableFrom(managedType)) + { + // ByReference types have no class, so we need this marker interface to identify them. + return typeof(IntPtr); + } + else if (typeof(IPointer).IsAssignableFrom(managedType)) + { + return typeof(IntPtr); + } + else if (managedType.IsValueType) + { + // Struct that's passed on the stack => handle as general struct - return managedType; + var nativeClassPtr = Il2CppClassPointerStore.GetNativeClassPointer(managedType); + if (nativeClassPtr == IntPtr.Zero) + { + throw new NotSupportedException($"Type {managedType.FullName} is not an Il2Cpp type."); + } + + var fixedSize = IL2CPP.GetIl2cppValueSize(nativeClassPtr); + return GetFixedSizeStructType(fixedSize); + } + else + { + // General reference type + return typeof(IntPtr); + } } } diff --git a/Il2CppInterop.Runtime/Injection/TypeInjector.cs b/Il2CppInterop.Runtime/Injection/TypeInjector.cs new file mode 100644 index 00000000..a6596066 --- /dev/null +++ b/Il2CppInterop.Runtime/Injection/TypeInjector.cs @@ -0,0 +1,1524 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO.Hashing; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using Il2CppInterop.Common; +using Il2CppInterop.Common.Attributes; +using Il2CppInterop.Runtime.InteropTypes; +using Il2CppInterop.Runtime.Runtime; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo; +using Microsoft.Extensions.Logging; + +namespace Il2CppInterop.Runtime.Injection; + +public static unsafe class TypeInjector +{ + private static readonly IntPtr value__Cached = Marshal.StringToCoTaskMemUTF8("value__"); + /// + /// fieldInfo : defaultValueBlob + /// + private static readonly ConcurrentDictionary s_DefaultValueOverrides = new(); + private static readonly ConcurrentDictionary<(Type type, FieldAttributes attrs), IntPtr> _injectedFieldTypes = new(); + private static readonly HashSet RegisteredTypes = new(); + private static readonly HashSet NeedsInitialized = new(); + private static readonly Dictionary NeedsVTableSet = new(); + private static readonly HashSet NeedsFieldsSet = new(); + private static readonly ConcurrentDictionary InvokerCache = new(); + + /// + /// If true, this type is part of the game and not something we are trying to inject nor something that has already been injected by us. + /// + /// The type to check. + /// True if the type is preexisting, false otherwise. + public static bool IsPreexistingType(Type type) + { + return Il2CppClassPointerStore.GetNativeClassPointer(type) is not 0 && !RegisteredTypes.Contains(type); + } + + public static void RegisterTypeInIl2Cpp() where T : IIl2CppType + { + RegisterTypeInIl2Cpp(typeof(T)); + } + + private static void RegisterTypeInIl2Cpp(Type type) + { + if (Il2CppClassPointerStore.GetNativeClassPointer(type) is not 0) + { + // Already registered + return; + } + // The above call guarantees that either: + // * the static constructor is currently running and called this method. + // * the static constructor is malformed and does not call this method. + + ValidateTypeUsingReflection(type); + InjectorHelpers.Setup(); + if (typeof(Il2CppSystem.IEnum).IsAssignableFrom(type)) + { + RegisterEnumInIl2Cpp(type); + return; + } + var vtableUpperBound = CalculateVTableUpperBoundUsingReflection(type); + var classPointer = UnityVersionHandler.NewClass(vtableUpperBound); + + // Initialize as much of the class pointer as possible without touching other types. + (var assemblyName, var @namespace, var name) = GetFullyQualifiedName(type); + classPointer.Image = AssemblyInjector.GetOrCreateImage(assemblyName).ImagePointer; + classPointer.Name = Marshal.StringToCoTaskMemUTF8(name); + classPointer.Namespace = Marshal.StringToCoTaskMemUTF8(@namespace); + classPointer.ElementClass = classPointer.Class = classPointer.CastClass = classPointer.ClassPointer; + + classPointer.NativeSize = -1; + classPointer.ActualSize = classPointer.InstanceSize = 0; + + classPointer.Initialized = true; + classPointer.InitializedAndNoError = true; + classPointer.SizeInited = false; + classPointer.HasFinalize = !type.IsValueType; + classPointer.IsVtableInitialized = false; + classPointer.ValueType = type.IsValueType; + + classPointer.ThisArg.Type = classPointer.ByValArg.Type = type.IsValueType ? Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE : Il2CppTypeEnum.IL2CPP_TYPE_CLASS; + classPointer.ThisArg.Data = classPointer.ByValArg.Data = (nint)InjectorHelpers.CreateClassToken(classPointer.Pointer); + classPointer.ThisArg.ValueType = classPointer.ByValArg.ValueType = type.IsValueType; + classPointer.ThisArg.ByRef = true; + + classPointer.Flags = TypeAttributesToClassAttributes(type.Attributes); + + // This guarantees that any future calls to GetNativeClassPointer will return the class pointer, + // even if the class is not fully registered yet, which allows us to handle circular dependencies between types. + RegisteredTypes.Add(type); + NeedsInitialized.Add(type); + NeedsVTableSet.Add(type, vtableUpperBound); + NeedsFieldsSet.Add(type); + Il2CppClassPointerStore.SetNativeClassPointer(type, (nint)classPointer.ClassPointer); + InjectorHelpers.AddTypeToLookup(assemblyName, @namespace, name, (nint)classPointer.ClassPointer); + + // Ensure that all other types that this type depends on are at least registered + { + if (type is not { BaseType: null } and not { IsValueType: true }) + { + EnsureNativeClassPointerNotNull(type.BaseType); + } + + foreach (var interfaceType in type.GetInterfaces().Where(IsIl2CppInterface)) + { + EnsureNativeClassPointerNotNull(interfaceType); + } + + foreach (var (fieldType, _, _) in GetIl2CppFields(type)) + { + EnsureNativeClassPointerNotNull(fieldType); + } + + foreach (var property in GetIl2CppProperties(type)) + { + EnsureNativeClassPointerNotNull(property.PropertyType); + } + + foreach (var method in GetIl2CppMethods(type)) + { + foreach (var methodType in GetMethodTypes(method)) + { + if (methodType.ContainsGenericParameters) + continue; + EnsureNativeClassPointerNotNull(methodType); + } + } + } + + if (NeedsInitialized.Contains(type)) + { + SetClassData(type, classPointer); + } + + if (NeedsVTableSet.ContainsKey(type)) + { + SetVTableAndInterfaces(type, classPointer, vtableUpperBound); + } + + if (NeedsFieldsSet.Contains(type)) + { + SetFields(type, classPointer); + } + + static void EnsureNativeClassPointerNotNull(Type type) => GetNativeClassPointerNotNull(type); + + static nint GetNativeClassPointerNotNull(Type type) + { + var classPointer = Il2CppClassPointerStore.GetNativeClassPointer(type); + if (classPointer is 0) + { + Logger.Instance.LogWarning("The static constructor of {Type} is malformed and did not call RegisterTypeInIl2Cpp. Registering it now.", type.FullName); + RegisterTypeInIl2Cpp(type); + classPointer = Il2CppClassPointerStore.GetNativeClassPointer(type); + Debug.Assert(classPointer is not 0); + } + else if (!RegisteredTypes.Contains(type)) + { + // Ensure that the vtable is initialized for this preexisting type + InjectorHelpers.ClassInit((Il2CppClass*)classPointer); + } + return classPointer; + } + + static void SetClassData(Type type, INativeClassStruct classPointer) + { + if (type.BaseType is not null && NeedsInitialized.Contains(type.BaseType)) + { + SetClassData(type.BaseType, UnityVersionHandler.Wrap((Il2CppClass*)GetNativeClassPointerNotNull(type.BaseType))); + } + + var baseClassPointer = type switch + { + { BaseType: null } => null, + { IsValueType: true } => UnityVersionHandler.Wrap((Il2CppClass*)Il2CppClassPointerStore.NativeClassPointer), + _ => UnityVersionHandler.Wrap((Il2CppClass*)GetNativeClassPointerNotNull(type.BaseType)), + }; + + // Static classes get unsealed during generation so that they can be used as generic parameters, which might mislead users into thinking they can inherit from them. + // This native check can be removed with changes to the generation, such as adding an attribute to indicate that a class is static and moving this check to the reflection validation. + if (baseClassPointer is not null && (baseClassPointer.Flags & Il2CppClassAttributes.TYPE_ATTRIBUTE_SEALED) != 0) + throw new ArgumentException($"Base class {type.BaseType} is sealed, so {type} can't inherit from it"); + + // Initialize the rest of the class pointer now that it's visible to other types. + classPointer.Parent = baseClassPointer?.ClassPointer; + if (baseClassPointer is not null) + { + classPointer.ActualSize = classPointer.InstanceSize = baseClassPointer.InstanceSize; + classPointer.SizeInited = true; + } + + //var properties = GetIl2CppProperties(type).ToArray(); + + var methods = GetIl2CppMethods(type).ToArray(); + var methodsOffset = type.IsInterface ? 0 : 1; // empty ctor + var methodCount = methodsOffset + methods.Length; + + classPointer.MethodCount = (ushort)methodCount; + var methodPointerArray = (Il2CppMethodInfo**)Marshal.AllocHGlobal(methodCount * IntPtr.Size); + classPointer.Methods = methodPointerArray; + + if (!type.IsInterface) + { + methodPointerArray[0] = CreateEmptyCtor(classPointer); + } + + for (var i = 0; i < methods.Length; i++) + { + var methodInfo = methods[i]; + methodPointerArray[i + methodsOffset] = ConvertMethodInfo(methodInfo, classPointer); + } + + NeedsInitialized.Remove(type); + } + + static void SetVTableAndInterfaces(Type type, INativeClassStruct classPointer, int vtableAllocatedSize) + { + if (NeedsInitialized.Contains(type)) + { + SetClassData(type, classPointer); + } + Type[] interfaceTypesNotImplementedByBaseType; + INativeClassStruct? baseClassPointer; + Type? baseType; + if (type is { BaseType: null }) + { + baseClassPointer = null; + baseType = null; + interfaceTypesNotImplementedByBaseType = type.GetInterfaces().Where(IsIl2CppInterface).ToArray(); + } + else + { + baseType = type.IsValueType ? typeof(Il2CppSystem.ValueType) : type.BaseType; + interfaceTypesNotImplementedByBaseType = type.GetInterfaces().Where(IsIl2CppInterface).Where(i => !i.IsAssignableFrom(baseType)).ToArray(); + baseClassPointer = UnityVersionHandler.Wrap((Il2CppClass*)GetNativeClassPointerNotNull(baseType)); + if (NeedsVTableSet.TryGetValue(baseType, out var baseTypeVTableMaxSize)) + { + SetVTableAndInterfaces(baseType, baseClassPointer, baseTypeVTableMaxSize); + } + } + foreach (var @interface in interfaceTypesNotImplementedByBaseType) + { + if (NeedsVTableSet.TryGetValue(@interface, out var interfaceVTableMaxSize)) + { + var interfaceClassPointer = UnityVersionHandler.Wrap((Il2CppClass*)GetNativeClassPointerNotNull(@interface)); + SetVTableAndInterfaces(@interface, interfaceClassPointer, interfaceVTableMaxSize); + } + } + + if (baseClassPointer is null) + { + classPointer.InterfaceCount = (ushort)interfaceTypesNotImplementedByBaseType.Length; + classPointer.ImplementedInterfaces = (Il2CppClass**)Marshal.AllocHGlobal(interfaceTypesNotImplementedByBaseType.Length * IntPtr.Size); + for (var i = 0; i < interfaceTypesNotImplementedByBaseType.Length; i++) + { + var interfaceType = interfaceTypesNotImplementedByBaseType[i]; + classPointer.ImplementedInterfaces[i] = (Il2CppClass*)GetNativeClassPointerNotNull(interfaceType); + } + } + else + { + classPointer.InterfaceCount = (ushort)(interfaceTypesNotImplementedByBaseType.Length + baseClassPointer.InterfaceCount); + classPointer.ImplementedInterfaces = (Il2CppClass**)Marshal.AllocHGlobal(classPointer.InterfaceCount * IntPtr.Size); + Buffer.MemoryCopy(baseClassPointer.ImplementedInterfaces, classPointer.ImplementedInterfaces, classPointer.InterfaceCount * IntPtr.Size, baseClassPointer.InterfaceCount * IntPtr.Size); + for (var i = 0; i < interfaceTypesNotImplementedByBaseType.Length; i++) + { + var interfaceType = interfaceTypesNotImplementedByBaseType[i]; + classPointer.ImplementedInterfaces[baseClassPointer.InterfaceCount + i] = (Il2CppClass*)GetNativeClassPointerNotNull(interfaceType); + } + } + + var pointersToInterfaces = type.GetInterfaces().Where(IsIl2CppInterface).ToDictionary(Il2CppClassPointerStore.GetNativeClassPointer); + + var interfaceOffsets = new List(); + + var map1 = CreateMethodInfoToHashDictionary(type); + var map2 = CreateHashToMethodInfoDictionary(type); + var map3 = CreateNativeMethodInfoToHashDictionary(classPointer); + var map4 = CreateHashToNativeMethodInfoDictionary(classPointer); + + var index = 0; + if (baseClassPointer is not null) + { + Debug.Assert(baseType is not null); + + var baseMethodToMethodMap = GetIl2CppMethods(type).ToDictionary(m => m.GetBaseDefinition(), m => m); + + var baseMap1 = CreateMethodInfoToHashDictionary(baseType); + var baseMap2 = CreateHashToMethodInfoDictionary(baseType); + var baseMap3 = CreateNativeMethodInfoToHashDictionary(baseClassPointer); + var baseMap4 = CreateHashToNativeMethodInfoDictionary(baseClassPointer); + + var lowestedInterfaceOffset = LowestInterfaceOffset(baseClassPointer); + for (; index < lowestedInterfaceOffset; index++) + { + ThrowIfNotEnoughAllocated(index, vtableAllocatedSize, type); + var vtableEntry = (VirtualInvokeData*)classPointer.VTable + index; + var baseVTableEntry = (VirtualInvokeData*)baseClassPointer.VTable + index; + var baseMethodInfo = baseMap2[baseMap3[(nint)baseVTableEntry->method]]; + if (baseMethodToMethodMap.TryGetValue(baseMethodInfo, out var methodInfo)) + { + var nativeMethodInfo = map4[map1[methodInfo]]; + vtableEntry->methodPtr = nativeMethodInfo.MethodPointer; + vtableEntry->method = nativeMethodInfo.MethodInfoPointer; + } + else + { + *vtableEntry = *baseVTableEntry; + } + } + + // Virtual methods declared in this class + foreach (var methodInfo in baseMethodToMethodMap.Values) + { + // If the method is not overridden, it will be a key in the dictionary. + if (!baseMethodToMethodMap.ContainsKey(methodInfo)) + continue; + + if (!methodInfo.IsAbstract && !methodInfo.IsVirtual) + continue; + + if (methodInfo.IsFinal) + continue; + + var nativeMethodInfo = map4[map1[methodInfo]]; + ThrowIfNotEnoughAllocated(index, vtableAllocatedSize, type); + ((VirtualInvokeData*)classPointer.VTable)[index] = new() + { + methodPtr = nativeMethodInfo.MethodPointer, + method = nativeMethodInfo.MethodInfoPointer + }; + index++; + } + + for (var interfaceIndex = 0; interfaceIndex < baseClassPointer.InterfaceOffsetsCount; interfaceIndex++) + { + var pair = baseClassPointer.InterfaceOffsets[interfaceIndex]; + var interfaceClassPointer = UnityVersionHandler.Wrap(pair.interfaceType); + var interfaceType = pointersToInterfaces[interfaceClassPointer.Pointer]; + var baseInterfaceOffset = pair.offset; + var interfaceOffset = index; + int interfaceVtableCount = interfaceClassPointer.VtableCount; + + interfaceOffsets.Add(new Il2CppRuntimeInterfaceOffsetPair + { + interfaceType = pair.interfaceType, + offset = interfaceOffset + }); + + Dictionary interfaceMethodToImplementingMethod; + { + var interfaceMapStruct = type.GetInterfaceMap(interfaceType); + interfaceMethodToImplementingMethod = new Dictionary(interfaceMapStruct.InterfaceMethods.Length); + for (var i = 0; i < interfaceMapStruct.InterfaceMethods.Length; i++) + { + interfaceMethodToImplementingMethod.Add(interfaceMapStruct.InterfaceMethods[i], interfaceMapStruct.TargetMethods[i]); + } + } + + var interfaceMap1 = CreateMethodInfoToHashDictionary(interfaceType); + var interfaceMap2 = CreateHashToMethodInfoDictionary(interfaceType); + var interfaceMap3 = CreateNativeMethodInfoToHashDictionary(interfaceClassPointer); + var interfaceMap4 = CreateHashToNativeMethodInfoDictionary(interfaceClassPointer); + + for (var i = 0; i < interfaceVtableCount; i++) + { + Debug.Assert(index == interfaceOffset + i); + ThrowIfNotEnoughAllocated(index, vtableAllocatedSize, type); + var vtableEntry = (VirtualInvokeData*)classPointer.VTable + interfaceOffset + i; + var interfaceVTableEntry = (VirtualInvokeData*)interfaceClassPointer.VTable + i; + var interfaceMethodInfo = interfaceMap2[interfaceMap3[(nint)interfaceVTableEntry->method]]; + if (interfaceMethodToImplementingMethod.TryGetValue(interfaceMethodInfo, out var methodInfo) && methodInfo.DeclaringType == type) + { + var nativeMethodInfo = map4[map1[methodInfo]]; + vtableEntry->methodPtr = nativeMethodInfo.MethodPointer; + vtableEntry->method = nativeMethodInfo.MethodInfoPointer; + } + else + { + var baseVTableEntry = (VirtualInvokeData*)baseClassPointer.VTable + baseInterfaceOffset + i; + *vtableEntry = *baseVTableEntry; + } + index++; + } + } + } + + foreach (var interfaceType in interfaceTypesNotImplementedByBaseType) + { + var interfaceClassPointer = UnityVersionHandler.Wrap((Il2CppClass*)GetNativeClassPointerNotNull(interfaceType)); + var interfaceOffset = index; + var interfaceVtableCount = LowestInterfaceOffset(interfaceClassPointer); // Not sure this is correct + + if (interfaceVtableCount == 0) + continue; + + interfaceOffsets.Add(new Il2CppRuntimeInterfaceOffsetPair + { + interfaceType = interfaceClassPointer.ClassPointer, + offset = interfaceOffset + }); + + Dictionary interfaceMethodToImplementingMethod; + { + var interfaceMapStruct = type.GetInterfaceMap(interfaceType); + interfaceMethodToImplementingMethod = new Dictionary(interfaceMapStruct.InterfaceMethods.Length); + for (var i = 0; i < interfaceMapStruct.InterfaceMethods.Length; i++) + { + interfaceMethodToImplementingMethod.Add(interfaceMapStruct.InterfaceMethods[i], interfaceMapStruct.TargetMethods[i]); + } + } + + var interfaceMap1 = CreateMethodInfoToHashDictionary(interfaceType); + var interfaceMap2 = CreateHashToMethodInfoDictionary(interfaceType); + var interfaceMap3 = CreateNativeMethodInfoToHashDictionary(interfaceClassPointer); + var interfaceMap4 = CreateHashToNativeMethodInfoDictionary(interfaceClassPointer); + + for (var i = 0; i < interfaceVtableCount; i++) + { + Debug.Assert(index == interfaceOffset + i); + ThrowIfNotEnoughAllocated(index, vtableAllocatedSize, type); + var vtableEntry = (VirtualInvokeData*)classPointer.VTable + interfaceOffset + i; + var interfaceVTableEntry = (VirtualInvokeData*)interfaceClassPointer.VTable + i; + var interfaceMethodInfo = interfaceMap2[interfaceMap3[(nint)interfaceVTableEntry->method]]; + var methodInfo = interfaceMethodToImplementingMethod[interfaceMethodInfo]; + var nativeMethodInfo = map4[map1[methodInfo]]; + vtableEntry->methodPtr = nativeMethodInfo.MethodPointer; + vtableEntry->method = nativeMethodInfo.MethodInfoPointer; + index++; + } + } + + classPointer.InterfaceOffsetsCount = (ushort)interfaceOffsets.Count; + classPointer.InterfaceOffsets = (Il2CppRuntimeInterfaceOffsetPair*)Marshal.AllocHGlobal(interfaceOffsets.Count * sizeof(Il2CppRuntimeInterfaceOffsetPair)); + for (var i = 0; i < interfaceOffsets.Count; i++) + { + classPointer.InterfaceOffsets[i] = interfaceOffsets[i]; + } + + classPointer.IsVtableInitialized = true; + NeedsVTableSet.Remove(type); + + static void ThrowIfNotEnoughAllocated(int index, int allocatedSize, Type type) + { + if (index >= allocatedSize) + throw new InvalidOperationException($"Not enough vtable space allocated for type {type}. Allocated: {allocatedSize}"); + } + } + + static void SetFields(Type type, INativeClassStruct classPointer) + { + if (NeedsInitialized.Contains(type)) + { + SetClassData(type, classPointer); + } + foreach (var fieldType in GetIl2CppInstanceFieldTypes(type)) + { + if (fieldType.IsValueType && NeedsFieldsSet.Contains(fieldType)) + { + SetFields(fieldType, UnityVersionHandler.Wrap((Il2CppClass*)GetNativeClassPointerNotNull(fieldType))); + } + } + + if (type.BaseType is not null && NeedsFieldsSet.Contains(type.BaseType)) + { + SetFields(type.BaseType, UnityVersionHandler.Wrap((Il2CppClass*)GetNativeClassPointerNotNull(type.BaseType))); + } + + var fieldsToInject = GetIl2CppFields(type).ToArray(); + classPointer.FieldCount = (ushort)fieldsToInject.Length; + + var il2cppFields = (Il2CppFieldInfo*)Marshal.AllocHGlobal(classPointer.FieldCount * UnityVersionHandler.FieldInfoSize()); + var fieldOffset = (int)classPointer.InstanceSize; + for (var i = 0; i < classPointer.FieldCount; i++) + { + var (fieldType, fieldAttributes, fieldName) = fieldsToInject[i]; + + var fieldInfoClass = Il2CppClassPointerStore.GetNativeClassPointer(fieldType); + if (fieldInfoClass == IntPtr.Zero) + throw new Exception($"Type {fieldType} in {type}.{fieldName} doesn't exist in Il2Cpp"); + if (!_injectedFieldTypes.TryGetValue((fieldType, fieldAttributes), out var fieldTypePtr)) + { + var classType = + UnityVersionHandler.Wrap((Il2CppTypeStruct*)IL2CPP.il2cpp_class_get_type(fieldInfoClass)); + + var duplicatedType = UnityVersionHandler.NewType(); + duplicatedType.Data = classType.Data; + duplicatedType.Attrs = (ushort)fieldAttributes; + duplicatedType.Type = classType.Type; + duplicatedType.ByRef = classType.ByRef; + duplicatedType.Pinned = classType.Pinned; + + _injectedFieldTypes[(fieldType, fieldAttributes)] = duplicatedType.Pointer; + fieldTypePtr = duplicatedType.Pointer; + } + + var fieldInfo = UnityVersionHandler.Wrap(il2cppFields + i * UnityVersionHandler.FieldInfoSize()); + fieldInfo.Name = Marshal.StringToCoTaskMemUTF8(fieldName); + fieldInfo.Parent = classPointer.ClassPointer; + fieldInfo.Type = (Il2CppTypeStruct*)fieldTypePtr; + + if (fieldAttributes.HasFlag(FieldAttributes.Static)) + { + fieldInfo.Offset = 0; + } + else + { + fieldInfo.Offset = fieldOffset; + if (IL2CPP.il2cpp_class_is_valuetype(fieldInfoClass)) + { + uint _align = 0; + var fieldSize = IL2CPP.il2cpp_class_value_size(fieldInfoClass, ref _align); + fieldOffset += fieldSize; + } + else + { + fieldOffset += sizeof(Il2CppObject*); + } + } + } + + classPointer.Fields = il2cppFields; + + classPointer.InstanceSize = (uint)fieldOffset; + classPointer.ActualSize = classPointer.InstanceSize; + classPointer.SizeInited = true; + + NeedsFieldsSet.Remove(type); + } + } + + private static int LowestInterfaceOffset(INativeClassStruct classPointer) + { + int result = classPointer.VtableCount; + for (var i = classPointer.InterfaceOffsetsCount - 1; i >= 0; i--) + { + var offset = (classPointer.InterfaceOffsets + i)->offset; + if (offset < result) + result = offset; + } + return result; + } + + private static Dictionary CreateHashToMethodInfoDictionary(Type type) + { + return GetAllIl2CppMethods(type).ToDictionary(HashSignature); + } + + private static Dictionary CreateMethodInfoToHashDictionary(Type type) + { + return GetAllIl2CppMethods(type).ToDictionary(m => m, HashSignature); + } + + private static Dictionary CreateHashToNativeMethodInfoDictionary(INativeClassStruct classPointer) + { + var dict = new Dictionary(classPointer.MethodCount); + var currentClassPointer = classPointer; + while (true) + { + for (var i = 0; i < currentClassPointer.MethodCount; i++) + { + var methodInfo = UnityVersionHandler.Wrap(currentClassPointer.Methods[i]); + dict.TryAdd(HashSignature(methodInfo), methodInfo); + } + if (currentClassPointer.Parent is null) + break; + currentClassPointer = UnityVersionHandler.Wrap(currentClassPointer.Parent); + } + return dict; + } + + private static Dictionary CreateNativeMethodInfoToHashDictionary(INativeClassStruct classPointer) + { + var dict = new Dictionary(classPointer.MethodCount); + var currentClassPointer = classPointer; + while (true) + { + for (var i = 0; i < currentClassPointer.MethodCount; i++) + { + var methodInfo = UnityVersionHandler.Wrap(currentClassPointer.Methods[i]); + dict.Add(methodInfo.Pointer, HashSignature(methodInfo)); + } + if (currentClassPointer.Parent is null) + break; + currentClassPointer = UnityVersionHandler.Wrap(currentClassPointer.Parent); + } + return dict; + } + + private static UInt128 HashSignature(INativeMethodInfoStruct methodInfo) + { + // The probability of a hash collision is infinitesimal. + XxHash128 hash = new(); + hash.Append(GetName(methodInfo)); + hash.Append((byte)((methodInfo.Flags & Il2CppMethodFlags.METHOD_ATTRIBUTE_STATIC) != 0 ? 1 : 0)); + hash.Append(GetFullName(methodInfo.ReturnType)); + for (var i = 0; i < methodInfo.ParametersCount; i++) + { + var parameter = UnityVersionHandler.Wrap(methodInfo.Parameters, i); + hash.Append(GetFullName(parameter.ParameterType)); + } + return hash.GetCurrentHashAsUInt128(); + } + + private static UInt128 HashSignature(MethodInfo methodInfo) + { + // The probability of a hash collision is infinitesimal. + XxHash128 hash = new(); + var name = methodInfo.GetCustomAttribute()?.Name ?? methodInfo.Name; + hash.Append(System.Text.Encoding.UTF8.GetBytes(name)); + hash.Append((byte)(methodInfo.IsStatic ? 1 : 0)); + hash.Append(GetFullName(methodInfo.ReturnType)); + foreach (var parameter in methodInfo.GetParameters()) + { + hash.Append(GetFullName(parameter.ParameterType)); + } + return hash.GetCurrentHashAsUInt128(); + } + + private static void Append(this XxHash128 hash, byte b) + { + ReadOnlySpan data = [b]; + hash.Append(data); + } + + private static void Append(this XxHash128 hash, Il2CppSystem.String? str) + { + var data = MemoryMarshal.AsBytes(GetSpan(str)); + hash.Append(data); + } + + private static ReadOnlySpan GetName(INativeMethodInfoStruct methodInfo) + { + var namePtr = methodInfo.Name; + if (namePtr == IntPtr.Zero) + return default; + + // Find null terminator + int length = 0; + while (Marshal.ReadByte(namePtr, length) != 0) + { + length++; + } + return new ReadOnlySpan((void*)namePtr, length); + } + + private static Il2CppSystem.String GetFullName(Type type) + { + return GetFullName((Il2CppTypeStruct*)Il2CppTypePointerStore.GetNativeTypePointer(type)); + } + + private static Il2CppSystem.String GetFullName(Il2CppTypeStruct* type) + { + return Il2CppSystem.Type.internal_from_handle((nint)type).FullName; + } + + private static ReadOnlySpan GetSpan(Il2CppSystem.String? str) + { + if (str is null) + return default; + + var pointer = str.Pointer; + var length = IL2CPP.il2cpp_string_length(pointer); + var characters = IL2CPP.il2cpp_string_chars(pointer); + return new ReadOnlySpan(characters, length); + } + + private static IEnumerable GetAllIl2CppMethods(Type type) => type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static).Where(IsIl2CppMethod); + private static IEnumerable GetIl2CppMethods(Type type) => type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly).Where(IsIl2CppMethod); + private static IEnumerable GetIl2CppProperties(Type type) => type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly).Where(IsIl2CppProperty); + private static IEnumerable<(Type FieldType, FieldAttributes Attributes, string Name)> GetIl2CppFields(Type type) + { + List<(Type, FieldAttributes, string)> fields = new(); + foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) + { + if (IsIl2CppField(property)) + { + var fieldAttribute = property.GetCustomAttribute(); + var fieldName = fieldAttribute?.Name ?? property.Name; + var fieldType = property.PropertyType; + FieldAttributes fieldAttributes = default; + if (property.IsStatic()) + { + fieldAttributes |= FieldAttributes.Static; + } + if (fieldName.EndsWith(">k__BackingField", StringComparison.Ordinal)) + { + fieldAttributes |= FieldAttributes.SpecialName; + fieldAttributes |= FieldAttributes.Private; + } + else + { + fieldAttributes |= FieldAttributes.Public; + } + fields.Add((fieldType, fieldAttributes, fieldName)); + } + } + foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + { + if (IsIl2CppField(field)) + { + var fieldAttribute = field.GetCustomAttribute(); + var fieldName = fieldAttribute?.Name ?? field.Name; + var fieldType = field.FieldType; + var fieldAttributes = field.Attributes; + fields.Add((fieldType, fieldAttributes, fieldName)); + } + } + return fields; + } + + private static IEnumerable GetIl2CppInstanceFieldTypes(Type type) + { + return GetIl2CppFields(type).Where(f => !f.Attributes.HasFlag(FieldAttributes.Static)).Select(f => f.FieldType); + } + + private static void ValidateTypeUsingReflection(Type type) + { + // Enums are transformed into structs during generation + if (type.IsEnum) + throw new ArgumentException("Enums must be written as structs to be registered in Il2Cpp. Use the IEnum interface to mark them as enums.", nameof(type)); + + // Injected types cannot be generic + if (type.IsGenericType) + throw new ArgumentException("Generic types cannot be registered in Il2Cpp.", nameof(type)); + + if (type.DeclaringType is not null) + throw new ArgumentException("Nested types cannot be registered in Il2Cpp.", nameof(type)); + + // Types must inherit from IIl2CppType + if (!typeof(IIl2CppType).IsAssignableFrom(type)) + throw new ArgumentException("Types must implement IIl2CppType to be registered in Il2Cpp.", nameof(type)); + + if (type.IsValueType) + { + // Value types must inherit from IValueType + if (!typeof(Il2CppSystem.IValueType).IsAssignableFrom(type)) + throw new ArgumentException("Value types must implement IValueType to be registered in Il2Cpp.", nameof(type)); + + // Enums must have a single instance field named "value__" that represents the underlying value of the enum + if (typeof(Il2CppSystem.IEnum).IsAssignableFrom(type)) + { + var instanceFields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (instanceFields.Length != 1) + throw new ArgumentException("Enums must have a single instance field that represents the underlying value of the enum to be registered in Il2Cpp.", nameof(type)); + + var instanceField = instanceFields[0]; + if (instanceField.Name != "value__") + throw new ArgumentException("Enums must have a single instance field named \"value__\" to be registered in Il2Cpp.", nameof(type)); + } + } + else if (type.IsClass) + { + // Class types must inherit from Il2CppSystem.Object + if (!typeof(Il2CppSystem.Object).IsAssignableFrom(type)) + throw new ArgumentException("Class types must inherit from Il2CppSystem.Object to be registered in Il2Cpp.", nameof(type)); + + if (typeof(Il2CppSystem.IValueType).IsAssignableFrom(type)) + throw new ArgumentException("Class types cannot implement IValueType to be registered in Il2Cpp.", nameof(type)); + + Debug.Assert(type.BaseType is not null); + + // The base type of class types cannot be generic + if (type.BaseType.IsGenericType) + throw new ArgumentException("The base type of class types cannot be generic to be registered in Il2Cpp.", nameof(type)); + + // The type must have a constructor that takes a ObjectPointer parameter + if (type.GetConstructor([typeof(ObjectPointer)]) is null) + throw new ArgumentException("Class types must have a constructor that takes an ObjectPointer parameter to be registered in Il2Cpp.", nameof(type)); + } + + // Types must inherit from IIl2CppType where T is the type itself + try + { + // This will throw if the type does not implement IIl2CppType where T is the type itself + // because T has a self-referential constraint that cannot be satisfied if the type does not implement the interface correctly. + // If https://github.com/dotnet/runtime/issues/28033 is implemented, we can replace this with a more direct check. + typeof(IIl2CppType<>).MakeGenericType(type); + } + catch + { + throw new ArgumentException("Types must implement IIl2CppType where T is the type itself to be registered in Il2Cpp.", nameof(type)); + } + } + + private static int CalculateVTableUpperBoundUsingReflection(Type type) + { + var count = 0; + foreach (var interfaceType in type.GetInterfaces().Where(IsIl2CppInterface)) + { + count += CountIl2CppMethods(interfaceType); + } + var currentType = type; + while (currentType is not null) + { + count += CountIl2CppMethods(currentType); + currentType = currentType.BaseType; + } + if (type.IsValueType) + { + count += UnityVersionHandler.Wrap((Il2CppClass*)Il2CppClassPointerStore.NativeClassPointer).VtableCount; + } + return int.Min(count, ushort.MaxValue); + + static int CountIl2CppMethods(Type type) => type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Count(IsIl2CppMethod); + } + + public static void InjectEnumValues(Dictionary valuesToAdd) where TEnum : Il2CppSystem.IEnum + { + InjectEnumValues(typeof(TEnum), valuesToAdd); + } + + public static void InjectEnumValues(Type type, Dictionary valuesToAdd) + { + ArgumentNullException.ThrowIfNull(type); + + if (!typeof(Il2CppSystem.IEnum).IsAssignableFrom(type)) + throw new ArgumentException("Type argument needs to be an enum", nameof(type)); + + var enumPtr = Il2CppClassPointerStore.GetNativeClassPointer(type); + if (enumPtr == IntPtr.Zero) + throw new ArgumentException("Type needs to be an Il2Cpp enum", nameof(type)); + + InjectorHelpers.Setup(); + + InjectorHelpers.ClassInit((Il2CppClass*)enumPtr); + + var il2cppEnum = UnityVersionHandler.Wrap((Il2CppClass*)enumPtr); + var newFieldCount = il2cppEnum.FieldCount + valuesToAdd.Count; + var newFields = (Il2CppFieldInfo*)Marshal.AllocHGlobal(newFieldCount * UnityVersionHandler.FieldInfoSize()); + + int fieldIdx; + for (fieldIdx = 0; fieldIdx < il2cppEnum.FieldCount; ++fieldIdx) + { + var offset = fieldIdx * UnityVersionHandler.FieldInfoSize(); + var oldField = UnityVersionHandler.Wrap(il2cppEnum.Fields + offset); + var newField = UnityVersionHandler.Wrap(newFields + offset); + + newField.Name = oldField.Name; + newField.Type = oldField.Type; + newField.Parent = oldField.Parent; + newField.Offset = oldField.Offset; + + // Move the default value blob from the old field to the new one + if (s_DefaultValueOverrides.TryRemove((IntPtr)oldField.FieldInfoPointer, out var blob)) + s_DefaultValueOverrides[(IntPtr)newField.FieldInfoPointer] = blob; + } + + var enumElementType = UnityVersionHandler.Wrap(il2cppEnum.ElementClass).ByValArg; + + foreach (var newData in valuesToAdd) + { + var offset = fieldIdx * UnityVersionHandler.FieldInfoSize(); + var newField = UnityVersionHandler.Wrap(newFields + offset); + newField.Name = Marshal.StringToCoTaskMemUTF8(newData.Key); + newField.Type = enumElementType.TypePointer; + newField.Parent = il2cppEnum.ClassPointer; + newField.Offset = 0; + + CreateOrUpdateFieldDefaultValue(newField.FieldInfoPointer, enumElementType.TypePointer, newData.Value); + + ++fieldIdx; + } + + il2cppEnum.FieldCount = (ushort)newFieldCount; + il2cppEnum.Fields = newFields; + + if (TypeFromClassPointer(enumPtr, type.FullName) is Il2CppSystem.RuntimeType runtimeEnumType) + // The mono runtime caches the enum names and values the first time they are requested, so we reset this cache + runtimeEnumType.GenericCache = null; + + static Il2CppSystem.Type TypeFromClassPointer(nint classPointer, string? typeName) + { + if (classPointer == IntPtr.Zero) + { + throw new ArgumentException($"{typeName} does not have a corresponding IL2CPP class pointer"); + } + + var il2CppType = IL2CPP.il2cpp_class_get_type(classPointer); + if (il2CppType == IntPtr.Zero) + { + throw new ArgumentException($"{typeName} does not have a corresponding IL2CPP type pointer"); + } + + return Il2CppSystem.Type.internal_from_handle(il2CppType); + } + } + + private static void RegisterEnumInIl2Cpp(Type type) + { + var baseEnum = UnityVersionHandler.Wrap((Il2CppClass*)Il2CppClassPointerStore.NativeClassPointer); + + InjectorHelpers.ClassInit(baseEnum.ClassPointer); + + var il2cppEnum = UnityVersionHandler.NewClass(baseEnum.VtableCount); + var elementClass = + UnityVersionHandler.Wrap( + (Il2CppClass*)Il2CppClassPointerStore.GetNativeClassPointer(GetEnumUnderlyingType(type))); + + (var assemblyName, var @namespace, var name) = GetFullyQualifiedName(type); + il2cppEnum.Image = AssemblyInjector.GetOrCreateImage(assemblyName).ImagePointer; + il2cppEnum.Class = il2cppEnum.CastClass = il2cppEnum.ElementClass = elementClass.ClassPointer; + il2cppEnum.Parent = baseEnum.ClassPointer; + il2cppEnum.ActualSize = il2cppEnum.InstanceSize = + (uint)(baseEnum.InstanceSize + GetEnumElementSize(elementClass.ByValArg.Type)); + il2cppEnum.NativeSize = -1; + + il2cppEnum.ValueType = true; + il2cppEnum.EnumType = true; + il2cppEnum.Initialized = true; + il2cppEnum.InitializedAndNoError = true; + il2cppEnum.SizeInited = true; + il2cppEnum.HasFinalize = true; + il2cppEnum.IsVtableInitialized = true; + + il2cppEnum.Name = Marshal.StringToCoTaskMemUTF8(name); + il2cppEnum.Namespace = Marshal.StringToCoTaskMemUTF8(@namespace); + + var token = InjectorHelpers.CreateClassToken(il2cppEnum.Pointer); + il2cppEnum.ThisArg.Data = il2cppEnum.ByValArg.Data = (IntPtr)token; + + // Has to be IL2CPP_TYPE_VALUETYPE because IL2CPP_TYPE_ENUM isn't used + il2cppEnum.ThisArg.Type = il2cppEnum.ByValArg.Type = Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE; + + il2cppEnum.Flags = (Il2CppClassAttributes)type.Attributes; + + il2cppEnum.VtableCount = baseEnum.VtableCount; + var vtable = (VirtualInvokeData*)il2cppEnum.VTable; + var baseVTable = (VirtualInvokeData*)baseEnum.VTable; + for (var i = 0; i < baseEnum.VtableCount; i++) + vtable[i] = baseVTable[i]; + + var enumNamesAndValues = GetEnumNamesAndValues(type); + il2cppEnum.FieldCount = (ushort)(enumNamesAndValues.Length + 1); // value__ + + var il2cppFields = + (Il2CppFieldInfo*)Marshal.AllocHGlobal(il2cppEnum.FieldCount * UnityVersionHandler.FieldInfoSize()); + var valueField = UnityVersionHandler.Wrap(il2cppFields); + valueField.Name = value__Cached; + valueField.Parent = il2cppEnum.ClassPointer; + valueField.Offset = (int)baseEnum.InstanceSize; + + var enumValueType = UnityVersionHandler.NewType(); + enumValueType.Data = elementClass.ThisArg.Data; + enumValueType.Attrs = (ushort)(FieldAttributes.Private | FieldAttributes.Family | FieldAttributes.SpecialName | + FieldAttributes.RTSpecialName); + enumValueType.Type = elementClass.ThisArg.Type; + enumValueType.ByRef = elementClass.ThisArg.ByRef; + enumValueType.Pinned = elementClass.ThisArg.Pinned; + + valueField.Type = enumValueType.TypePointer; + + var enumConstType = UnityVersionHandler.NewType(); + enumConstType.Data = il2cppEnum.ThisArg.Data; + enumConstType.Attrs = (ushort)(FieldAttributes.Private | FieldAttributes.Family | FieldAttributes.InitOnly | + FieldAttributes.Literal | FieldAttributes.HasDefault); + enumConstType.Type = Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE; + enumConstType.ByRef = false; + enumConstType.Pinned = false; + + for (var i = 1; i < il2cppEnum.FieldCount; i++) + { + var (fieldName, fieldValue) = enumNamesAndValues[i - 1]; + var il2cppField = UnityVersionHandler.Wrap(il2cppFields + i * UnityVersionHandler.FieldInfoSize()); + il2cppField.Name = Marshal.StringToCoTaskMemUTF8(fieldName); + il2cppField.Type = enumConstType.TypePointer; + il2cppField.Parent = il2cppEnum.ClassPointer; + il2cppField.Offset = 0; + + CreateOrUpdateFieldDefaultValue(il2cppField.FieldInfoPointer, elementClass.ThisArg.TypePointer, fieldValue); + } + + il2cppEnum.Fields = il2cppFields; + + il2cppEnum.TypeHierarchyDepth = (byte)(1 + baseEnum.TypeHierarchyDepth); + il2cppEnum.TypeHierarchy = (Il2CppClass**)Marshal.AllocHGlobal(il2cppEnum.TypeHierarchyDepth * sizeof(void*)); + for (var i = 0; i < il2cppEnum.TypeHierarchyDepth; i++) + il2cppEnum.TypeHierarchy[i] = baseEnum.TypeHierarchy[i]; + il2cppEnum.TypeHierarchy[il2cppEnum.TypeHierarchyDepth - 1] = il2cppEnum.ClassPointer; + + Il2CppClassPointerStore.SetNativeClassPointer(type, il2cppEnum.Pointer); + InjectorHelpers.AddTypeToLookup(assemblyName, @namespace, name, il2cppEnum.Pointer); + } + + private static bool IsIl2CppInterface(Type type) + { + return type.IsInterface + && typeof(Il2CppSystem.IObject).IsAssignableFrom(type) + && type != typeof(Il2CppSystem.IObject) + && type != typeof(Il2CppSystem.IValueType) + && type != typeof(Il2CppSystem.IEnum); + } + + private static bool IsIl2CppField(PropertyInfo property) + { + // Has Il2CppFieldAttribute + return property.GetCustomAttribute() is not null; + } + + private static bool IsIl2CppField(FieldInfo field) + { + // Has Il2CppFieldAttribute + return field.GetCustomAttribute() is not null; + } + + private static bool IsIl2CppProperty(PropertyInfo property) + { + // Has Il2CppPropertyAttribute + return property.GetCustomAttribute() is not null; + } + + private static bool IsIl2CppMethod(MethodInfo method) + { + // Has Il2CppMethodAttribute + return method.GetCustomAttribute() is not null && !method.ContainsGenericParameters && !method.IsConstructor; + } + + private static IEnumerable GetMethodTypes(MethodInfo method) + { + foreach (var parameter in method.GetParameters()) + { + yield return parameter.ParameterType; + } + if (method.ReturnType != typeof(void)) + { + yield return method.ReturnType; + } + } + + private static Il2CppMethodInfo* CreateEmptyCtor(INativeClassStruct declaringClass) + { + var converted = UnityVersionHandler.NewMethod(); + converted.Name = Marshal.StringToCoTaskMemUTF8(".ctor"); + converted.Class = declaringClass.ClassPointer; + + void* invoker; + if (UnityVersionHandler.IsMetadataV29OrHigher) + { + invoker = (delegate* unmanaged)&Invoker_MetadataV29; + } + else + { + invoker = (delegate* unmanaged)&Invoker; + } + + converted.InvokerMethod = (nint)invoker; + converted.MethodPointer = (nint)(delegate* unmanaged)&Method; + converted.Slot = ushort.MaxValue; + converted.ReturnType = (Il2CppTypeStruct*)IL2CPP.il2cpp_class_get_type(Il2CppClassPointerStore.NativeClassPointer); + + converted.Flags = Il2CppMethodFlags.METHOD_ATTRIBUTE_PUBLIC | + Il2CppMethodFlags.METHOD_ATTRIBUTE_HIDE_BY_SIG | + Il2CppMethodFlags.METHOD_ATTRIBUTE_SPECIAL_NAME | + Il2CppMethodFlags.METHOD_ATTRIBUTE_RT_SPECIAL_NAME; + + return converted.MethodInfoPointer; + + [UnmanagedCallersOnly] + static void Invoker_MetadataV29(IntPtr methodPointer, Il2CppMethodInfo* methodInfo, IntPtr obj, IntPtr* args, IntPtr* returnValue) + { + if (returnValue != null) + *returnValue = IntPtr.Zero; + } + + [UnmanagedCallersOnly] + static IntPtr Invoker(IntPtr methodPointer, Il2CppMethodInfo* methodInfo, IntPtr obj, IntPtr* args) + { + return IntPtr.Zero; + } + + [UnmanagedCallersOnly] + static void Method(IntPtr obj, Il2CppMethodInfo* methodInfo) + { + } + } + + private static Il2CppMethodInfo* ConvertMethodInfo(MethodInfo monoMethod, INativeClassStruct declaringClass) + { + if (monoMethod.ContainsGenericParameters) + throw new ArgumentException("Generic methods cannot be converted.", nameof(monoMethod)); + + var converted = UnityVersionHandler.NewMethod(); + converted.Name = Marshal.StringToCoTaskMemUTF8(monoMethod.Name); + converted.Class = declaringClass.ClassPointer; + + var parameters = monoMethod.GetParameters(); + if (parameters.Length > 0) + { + converted.ParametersCount = (byte)parameters.Length; + var paramsArray = UnityVersionHandler.NewMethodParameterArray(parameters.Length); + converted.Parameters = paramsArray[0]; + for (var i = 0; i < parameters.Length; i++) + { + var parameterInfo = parameters[i]; + var param = UnityVersionHandler.Wrap(paramsArray[i]); + if (param.HasNamePosToken) + { + param.Name = Marshal.StringToCoTaskMemUTF8(parameterInfo.Name); + param.Position = i; + param.Token = 0; + } + + param.ParameterType = (Il2CppTypeStruct*)Il2CppTypePointerStore.GetNativeTypePointer(parameterInfo.ParameterType); + } + } + + if (!monoMethod.IsAbstract) + { + converted.InvokerMethod = Marshal.GetFunctionPointerForDelegate(GetOrCreateInvoker(monoMethod)); + converted.MethodPointer = Marshal.GetFunctionPointerForDelegate(CreateTrampoline(monoMethod, false)); + if (monoMethod.IsVirtual && !monoMethod.IsFinal) + { + converted.VirtualMethodPointer = Marshal.GetFunctionPointerForDelegate(CreateTrampoline(monoMethod, true)); + } + else + { + converted.VirtualMethodPointer = converted.MethodPointer; // Not certain if this should be null + } + } + + converted.Slot = ushort.MaxValue; + + converted.ReturnType = (Il2CppTypeStruct*)Il2CppTypePointerStore.GetNativeTypePointer(monoMethod.ReturnType); + + converted.Flags = MethodAttributesToMethodFlags(monoMethod.Attributes); + + return converted.MethodInfoPointer; + } + + private static Delegate GetOrCreateInvoker(MethodInfo monoMethod) + { + return InvokerCache.GetOrAdd(HashSignature(monoMethod), + static (_, monoMethodInner) => CreateInvoker(monoMethodInner), monoMethod); + } + + private static Delegate CreateInvoker(MethodInfo monoMethod) + { + Debug.Assert(monoMethod.DeclaringType is not null); + + DynamicMethod method; + if (UnityVersionHandler.IsMetadataV29OrHigher) + { + var parameterTypes = new[] { typeof(IntPtr), typeof(Il2CppMethodInfo*), typeof(IntPtr), typeof(IntPtr*), typeof(IntPtr*) }; + // Method pointer + // Method info pointer + // this pointer + // arguments pointer + // return value pointer (if not void) + + method = new DynamicMethod($"Invoker_{HashSignature(monoMethod)}", + MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(void), + parameterTypes, typeof(TypeInjector), true); + } + else + { + var parameterTypes = new[] { typeof(IntPtr), typeof(Il2CppMethodInfo*), typeof(IntPtr), typeof(IntPtr*) }; + // Method pointer + // Method info pointer + // this pointer + // arguments pointer + + method = new DynamicMethod($"Invoker_{HashSignature(monoMethod)}", + MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(IntPtr), + parameterTypes, typeof(TypeInjector), true); + } + + var body = method.GetILGenerator(); + + if (!monoMethod.IsStatic) + body.Emit(OpCodes.Ldarg_2); // obj + for (var i = 0; i < monoMethod.GetParameters().Length; i++) + { + var parameterInfo = monoMethod.GetParameters()[i]; + body.Emit(OpCodes.Ldarg_3); + body.Emit(OpCodes.Ldc_I4, i * IntPtr.Size); + body.Emit(OpCodes.Add_Ovf_Un); + var nativeType = parameterInfo.ParameterType.NativeType(); + body.Emit(OpCodes.Ldobj, typeof(IntPtr)); + if (nativeType != typeof(IntPtr)) + body.Emit(OpCodes.Ldobj, nativeType); + } + + body.Emit(OpCodes.Ldarg_1); // methodMetadata + body.Emit(OpCodes.Ldarg_0); // methodPointer + + var nativeReturnType = monoMethod.ReturnType.NativeType(); + Type[] nativeParameterTypes = + [ + ..(ReadOnlySpan)(monoMethod.IsStatic ? [] : [typeof(IntPtr)]), + ..monoMethod.GetParameters().Select(it => it.ParameterType.NativeType()), + typeof(Il2CppMethodInfo*), + ]; + body.EmitCalli(OpCodes.Calli, CallingConventions.Standard, nativeReturnType, nativeParameterTypes, null); + + if (UnityVersionHandler.IsMetadataV29OrHigher) + { + if (monoMethod.ReturnType != typeof(void)) + { + var returnValue = body.DeclareLocal(nativeReturnType); + body.Emit(OpCodes.Stloc, returnValue); + body.Emit(OpCodes.Ldarg_S, (byte)4); + body.Emit(OpCodes.Ldloc, returnValue); + body.Emit(OpCodes.Stobj, returnValue.LocalType); + } + } + else + { + if (monoMethod.ReturnType == typeof(void)) + { + // Return null for void methods + body.Emit(OpCodes.Ldc_I4_0); + body.Emit(OpCodes.Conv_I); + } + else if (monoMethod.ReturnType.IsValueType && nativeReturnType != typeof(IntPtr)) + { + // If managed return type is a value type and native return type is IntPtr, it means the managed return type is Pointer<> or ByReference<>. + // Those get returned as-is, so we don't want to box them. + var returnValue = body.DeclareLocal(nativeReturnType); + body.Emit(OpCodes.Stloc, returnValue); + var classField = typeof(Il2CppClassPointerStore<>).MakeGenericType(monoMethod.ReturnType) + .GetField(nameof(Il2CppClassPointerStore<>.NativeClassPointer))!; + body.Emit(OpCodes.Ldsfld, classField); + body.Emit(OpCodes.Ldloca, returnValue); + body.Emit(OpCodes.Call, typeof(IL2CPP).GetMethod(nameof(IL2CPP.il2cpp_value_box))!); + } + } + + body.Emit(OpCodes.Ret); + + GCHandle.Alloc(method); + + var invokerDelegateType = UnityVersionHandler.IsMetadataV29OrHigher ? typeof(InvokerDelegateMetadataV29) : typeof(InvokerDelegate); + + var @delegate = method.CreateDelegate(invokerDelegateType); + GCHandle.Alloc(@delegate); + return @delegate; + } + + private static Delegate CreateTrampoline(MethodInfo monoMethod, bool callVirt) + { + Debug.Assert(monoMethod.DeclaringType is not null); + + var nativeReturnType = monoMethod.ReturnType.NativeType(); + Type[] nativeParameterTypes = + [ + ..(ReadOnlySpan)(monoMethod.IsStatic ? [] : [typeof(IntPtr)]), + ..monoMethod.GetParameters().Select(it => it.ParameterType.NativeType()), + typeof(Il2CppMethodInfo*), + ]; + + Type[] managedParameters = + [ + ..(ReadOnlySpan)(monoMethod.IsStatic ? [] : [monoMethod.DeclaringType!]), + ..monoMethod.GetParameters().Select(it => it.ParameterType), + ]; + + var method = new DynamicMethod( + $"Trampoline_{monoMethod.DeclaringType}_{monoMethod.Name}_{HashSignature(monoMethod)}{(callVirt ? "_Virtual" : "")}", + MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, + nativeReturnType, nativeParameterTypes, + typeof(TypeInjector), true); + + var signature = new DelegateSupport.MethodSignature(monoMethod, !monoMethod.IsStatic); + var delegateType = DelegateSupport.GetOrCreateDelegateType(signature, monoMethod); + + var body = method.GetILGenerator(); + + body.BeginExceptionBlock(); + + if (!monoMethod.IsStatic) + { + body.Emit(OpCodes.Ldarg_0); + body.Emit(OpCodes.Call, typeof(Il2CppObjectPool).GetMethod(nameof(Il2CppObjectPool.Get))!); + body.Emit(OpCodes.Castclass, monoMethod.DeclaringType); + } + + var argOffset = method.IsStatic ? 0 : 1; + + for (var i = argOffset; i < managedParameters.Length; i++) + { + body.Emit(OpCodes.Ldarg, i); + body.Emit(OpCodes.Call, typeof(TypeInjector).GetMethod(nameof(ConvertNativeToManaged), BindingFlags.Static | BindingFlags.NonPublic)!.MakeGenericMethod(nativeParameterTypes[i], managedParameters[i])); + } + + body.Emit(callVirt ? OpCodes.Callvirt : OpCodes.Call, monoMethod); + LocalBuilder? nativeReturnVariable = null; + if (monoMethod.ReturnType != typeof(void)) + { + nativeReturnVariable = body.DeclareLocal(nativeReturnType); + body.Emit(OpCodes.Call, typeof(TypeInjector).GetMethod(nameof(ConvertManagedToNative), BindingFlags.Static | BindingFlags.NonPublic)!.MakeGenericMethod(monoMethod.ReturnType, nativeReturnType)); + body.Emit(OpCodes.Stloc, nativeReturnVariable); + } + + body.BeginCatchBlock(typeof(Exception)); + body.Emit(OpCodes.Call, typeof(TypeInjector).GetMethod(nameof(LogError), BindingFlags.Static | BindingFlags.NonPublic)!); + + body.EndExceptionBlock(); + + if (nativeReturnVariable != null) + { + body.Emit(OpCodes.Ldloc, nativeReturnVariable); + } + + body.Emit(OpCodes.Ret); + + var @delegate = method.CreateDelegate(delegateType); + GCHandle.Alloc(@delegate); // pin it forever + return @delegate; + } + + private static void LogError(Exception exception) + { + Logger.Instance.LogError("Exception in IL2CPP-to-Managed trampoline, not passing it to il2cpp: {Exception}", exception); + } + + private static TManaged? ConvertNativeToManaged(TNative value) + where TNative : unmanaged + where TManaged : IIl2CppType + { + var span = new ReadOnlySpan(&value, sizeof(TNative)); + return TManaged.ReadFromSpan(span); + } + + private static TNative ConvertManagedToNative(TManaged? value) + where TNative : unmanaged + where TManaged : IIl2CppType + { + TNative result = default; + var span = new Span(&result, sizeof(TNative)); + TManaged.WriteToSpan(value, span); + return result; + } + + private static (string AssemblyName, string Namespace, string Name) GetFullyQualifiedName(Type type) + { + return typeof(Il2CppType).GetMethod(nameof(Il2CppType.GetFullyQualifiedName))!.MakeGenericMethod(type).Invoke(null, null) is ValueTuple result + ? result + : throw new InvalidOperationException("GetFullyQualifiedName should return a ValueTuple"); + } + + private static Il2CppClassAttributes TypeAttributesToClassAttributes(TypeAttributes typeAttributes) + { + Il2CppClassAttributes result = default; + result |= (Il2CppClassAttributes)(typeAttributes & TypeAttributes.VisibilityMask); + result |= (Il2CppClassAttributes)(typeAttributes & TypeAttributes.LayoutMask); + result |= (Il2CppClassAttributes)(typeAttributes & TypeAttributes.ClassSemanticsMask); + result |= (Il2CppClassAttributes)(typeAttributes & TypeAttributes.StringFormatMask); + result |= (typeAttributes & TypeAttributes.Abstract) != 0 ? Il2CppClassAttributes.TYPE_ATTRIBUTE_ABSTRACT : default; + result |= (typeAttributes & TypeAttributes.Sealed) != 0 ? Il2CppClassAttributes.TYPE_ATTRIBUTE_SEALED : default; + result |= (typeAttributes & TypeAttributes.SpecialName) != 0 ? Il2CppClassAttributes.TYPE_ATTRIBUTE_SPECIAL_NAME : default; + result |= (typeAttributes & TypeAttributes.RTSpecialName) != 0 ? Il2CppClassAttributes.TYPE_ATTRIBUTE_RT_SPECIAL_NAME : default; + result |= (typeAttributes & TypeAttributes.Import) != 0 ? Il2CppClassAttributes.TYPE_ATTRIBUTE_IMPORT : default; +#pragma warning disable SYSLIB0050 // Type or member is obsolete + result |= (typeAttributes & TypeAttributes.Serializable) != 0 ? Il2CppClassAttributes.TYPE_ATTRIBUTE_SERIALIZABLE : default; +#pragma warning restore SYSLIB0050 // Type or member is obsolete + result |= (typeAttributes & TypeAttributes.HasSecurity) != 0 ? Il2CppClassAttributes.TYPE_ATTRIBUTE_HAS_SECURITY : default; + result |= (typeAttributes & TypeAttributes.BeforeFieldInit) != 0 ? Il2CppClassAttributes.TYPE_ATTRIBUTE_BEFORE_FIELD_INIT : default; + return result; + } + + private static Il2CppMethodFlags MethodAttributesToMethodFlags(MethodAttributes methodAttributes) + { + Il2CppMethodFlags result = default; + result |= (Il2CppMethodFlags)(methodAttributes & MethodAttributes.MemberAccessMask); + result |= (Il2CppMethodFlags)(methodAttributes & MethodAttributes.VtableLayoutMask); + result |= (methodAttributes & MethodAttributes.Static) != 0 ? Il2CppMethodFlags.METHOD_ATTRIBUTE_STATIC : default; + result |= (methodAttributes & MethodAttributes.Final) != 0 ? Il2CppMethodFlags.METHOD_ATTRIBUTE_FINAL : default; + result |= (methodAttributes & MethodAttributes.Virtual) != 0 ? Il2CppMethodFlags.METHOD_ATTRIBUTE_VIRTUAL : default; + result |= (methodAttributes & MethodAttributes.HideBySig) != 0 ? Il2CppMethodFlags.METHOD_ATTRIBUTE_HIDE_BY_SIG : default; + result |= (methodAttributes & MethodAttributes.NewSlot) != 0 ? Il2CppMethodFlags.METHOD_ATTRIBUTE_NEW_SLOT : default; + result |= (methodAttributes & MethodAttributes.Abstract) != 0 ? Il2CppMethodFlags.METHOD_ATTRIBUTE_ABSTRACT : default; + result |= (methodAttributes & MethodAttributes.SpecialName) != 0 ? Il2CppMethodFlags.METHOD_ATTRIBUTE_SPECIAL_NAME : default; + result |= (methodAttributes & MethodAttributes.RTSpecialName) != 0 ? Il2CppMethodFlags.METHOD_ATTRIBUTE_RT_SPECIAL_NAME : default; + return result; + } + + private static Type GetEnumUnderlyingType(Type enumType) + { + // Single instance field + var instanceField = enumType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Single(); + return instanceField.FieldType; + } + + private static (string Name, object Value)[] GetEnumNamesAndValues(Type enumType) + { + var fields = enumType.GetFields(BindingFlags.Public | BindingFlags.Static); + if (AnyFieldWrongType(enumType, fields)) + fields = fields.Where(f => f.FieldType == enumType).ToArray(); + if (fields.Length == 0) + return []; + + var instanceField = enumType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Single(); + var underlyingType = instanceField.FieldType; + var primitiveType = underlyingType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Single().FieldType; + var underlyingTypeConversionToPrimitiveType = underlyingType.GetMethod("op_Implicit", BindingFlags.Public | BindingFlags.Static, null, [underlyingType], null)!; + + (string Name, object Value)[] result = new (string Name, object Value)[fields.Length]; + for (var i = 0; i < fields.Length; i++) + { + var field = fields[i]; + var enumValue = field.GetValue(null); + var underlyingValue = instanceField.GetValue(enumValue); + var primitiveValue = underlyingTypeConversionToPrimitiveType.Invoke(null, [underlyingValue])!; + result[i] = (field.Name, primitiveValue); + } + return result; + + static bool AnyFieldWrongType(Type enumType, FieldInfo[] fields) + { + foreach (var field in fields) + { + if (field.FieldType != enumType) + return true; + } + return false; + } + } + + private static int GetEnumElementSize(Il2CppTypeEnum type) + { + return type switch + { + Il2CppTypeEnum.IL2CPP_TYPE_I1 => sizeof(sbyte), + Il2CppTypeEnum.IL2CPP_TYPE_U1 => sizeof(byte), + + Il2CppTypeEnum.IL2CPP_TYPE_CHAR => sizeof(char), + + Il2CppTypeEnum.IL2CPP_TYPE_I2 => sizeof(short), + Il2CppTypeEnum.IL2CPP_TYPE_U2 => sizeof(ushort), + + Il2CppTypeEnum.IL2CPP_TYPE_I4 => sizeof(int), + Il2CppTypeEnum.IL2CPP_TYPE_U4 => sizeof(uint), + + Il2CppTypeEnum.IL2CPP_TYPE_I8 => sizeof(long), + Il2CppTypeEnum.IL2CPP_TYPE_U8 => sizeof(ulong), + + _ => throw new ArgumentOutOfRangeException(nameof(type), type, $"The type provided {type} is invalid") + }; + } + + private static IntPtr AllocateNewDefaultValueBlob(Il2CppTypeEnum type) + { + var size = GetEnumElementSize(type); + var blob = Marshal.AllocHGlobal(size); + Logger.Instance.LogTrace("Allocated default value blob at 0x{Blob:X2} of {Size} for {Type}", (long)blob, size, type); + return blob; + } + + internal static bool GetFieldDefaultValueOverride(Il2CppFieldInfo* fieldInfo, out IntPtr defaultValueBlob) + { + return s_DefaultValueOverrides.TryGetValue((IntPtr)fieldInfo, out defaultValueBlob); + } + + private static IntPtr CreateOrUpdateFieldDefaultValue(Il2CppFieldInfo* field, Il2CppTypeStruct* type, object value) + { + var typeEnum = UnityVersionHandler.Wrap(type).Type; + + if (!GetFieldDefaultValueOverride(field, out var newBlob)) + { + newBlob = AllocateNewDefaultValueBlob(typeEnum); + s_DefaultValueOverrides[(IntPtr)field] = newBlob; + } + + SetFieldDefaultValue(newBlob, typeEnum, value); + return newBlob; + } + + private static void SetFieldDefaultValue(IntPtr blob, Il2CppTypeEnum type, object value) + { + var valueData = Convert.ToInt64(value); + switch (type) + { + case Il2CppTypeEnum.IL2CPP_TYPE_I1: + *(sbyte*)blob = (sbyte)valueData; + break; + case Il2CppTypeEnum.IL2CPP_TYPE_U1: + *(byte*)blob = (byte)valueData; + break; + + case Il2CppTypeEnum.IL2CPP_TYPE_CHAR: + *(char*)blob = (char)valueData; + break; + + case Il2CppTypeEnum.IL2CPP_TYPE_I2: + *(short*)blob = (short)valueData; + break; + case Il2CppTypeEnum.IL2CPP_TYPE_U2: + *(ushort*)blob = (ushort)valueData; + break; + + case Il2CppTypeEnum.IL2CPP_TYPE_I4: + *(int*)blob = (int)valueData; + break; + case Il2CppTypeEnum.IL2CPP_TYPE_U4: + *(uint*)blob = (uint)valueData; + break; + + case Il2CppTypeEnum.IL2CPP_TYPE_I8: + *(long*)blob = valueData; + break; + case Il2CppTypeEnum.IL2CPP_TYPE_U8: + *(ulong*)blob = (ulong)valueData; + break; + + default: throw new ArgumentOutOfRangeException(nameof(type), type, $"The type provided {type} is invalid"); + } + } + + private static bool IsStatic(this PropertyInfo property) + { + var accessor = property.GetMethod ?? property.SetMethod; + return accessor is not null && accessor.IsStatic; + } + + private delegate void InvokerDelegateMetadataV29(IntPtr methodPointer, Il2CppMethodInfo* methodInfo, IntPtr obj, IntPtr* args, IntPtr* returnValue); + + private delegate IntPtr InvokerDelegate(IntPtr methodPointer, Il2CppMethodInfo* methodInfo, IntPtr obj, IntPtr* args); +} diff --git a/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayBase.cs b/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayBase.cs index 3fbf4b69..6fb291ad 100644 --- a/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayBase.cs +++ b/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayBase.cs @@ -1,14 +1,14 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; +using System.Buffers; +using System.Runtime.CompilerServices; +using Il2CppInterop.Common; using Il2CppInterop.Runtime.Runtime; namespace Il2CppInterop.Runtime.InteropTypes.Arrays; -public abstract class Il2CppArrayBase : Il2CppObjectBase, IEnumerable +public abstract class Il2CppArrayBase : Il2CppSystem.Array { - protected Il2CppArrayBase(IntPtr pointer) : base(pointer) + private protected Il2CppArrayBase(ObjectPointer pointer) : base(pointer) { } @@ -17,9 +17,7 @@ protected Il2CppArrayBase(IntPtr pointer) : base(pointer) /// private protected unsafe IntPtr ArrayStartPointer => IntPtr.Add(Pointer, sizeof(Il2CppObject) /* base */ + sizeof(void*) /* bounds */ + sizeof(nuint) /* max_length */); - public int Length => (int)IL2CPP.il2cpp_array_length(Pointer); - - public abstract IEnumerator GetEnumerator(); + public new int Length => base.Length; private protected static bool ThrowImmutableLength() { @@ -32,146 +30,99 @@ private protected void ThrowIfIndexOutOfRange(int index) throw new ArgumentOutOfRangeException(nameof(index), "Array index may not be negative or above length of the array"); } -} -public abstract class Il2CppArrayBase : Il2CppArrayBase, IList, IReadOnlyList -{ - protected Il2CppArrayBase(IntPtr pointer) : base(pointer) - { - } - public sealed override IEnumerator GetEnumerator() + public T LoadElementUnsafe(int index) where T : unmanaged { - return new IndexEnumerator(this); + ThrowIfIndexOutOfRange(index); + return Unsafe.As(ref GetUnsafeSpanForElement(index)[0]); } - void ICollection.Add(T item) + public void StoreElementUnsafe(int index, T value) where T : unmanaged { - ThrowImmutableLength(); + ThrowIfIndexOutOfRange(index); + ref var element = ref Unsafe.As(ref GetUnsafeSpanForElement(index)[0]); + element = value; } - void ICollection.Clear() + private protected virtual Span GetUnsafeSpanForElement(int index) { - ThrowImmutableLength(); + throw new NotSupportedException("Only rank 1 arrays support unsafe element access"); } - public bool Contains(T item) + private protected static unsafe ObjectPointer AllocateArray(ReadOnlySpan lengths, IntPtr arrayClass) { - return IndexOf(item) != -1; - } + if (lengths.Length <= 1) + { + throw new ArgumentException("Use single-dimensional array allocation for single-dimensional arrays.", nameof(lengths)); + } - public void CopyTo(T[] array, int arrayIndex) - { - if (array == null) throw new ArgumentNullException(nameof(array)); - if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex)); - if (array.Length - arrayIndex < Length) - throw new ArgumentException( - $"Not enough space in target array: need {Length} slots, have {array.Length - arrayIndex}"); - - for (var i = 0; i < Length; i++) - array[i + arrayIndex] = this[i]; - } + var sizes = ArrayPool.Shared.Rent(lengths.Length); + for (var i = 0; i < lengths.Length; i++) + { + ArgumentOutOfRangeException.ThrowIfNegative(lengths[i]); + sizes[i] = (ulong)lengths[i]; + } - bool ICollection.Remove(T item) - { - return ThrowImmutableLength(); - } + var lowerBounds = ArrayPool.Shared.Rent(lengths.Length); + lowerBounds.AsSpan().Clear(); - public new int Length => base.Length;// For binary compatibility - public int Count => Length; - bool ICollection.IsReadOnly => false; + ObjectPointer result; + fixed (ulong* pSizes = sizes) + { + fixed (ulong* pLowerBounds = lowerBounds) + { + result = (ObjectPointer)IL2CPP.il2cpp_array_new_full(arrayClass, pSizes, pLowerBounds); + } + } - public int IndexOf(T item) - { - for (var i = 0; i < Length; i++) - if (Equals(item, this[i])) - return i; + ArrayPool.Shared.Return(sizes); + ArrayPool.Shared.Return(lowerBounds); - return -1; + return result; } - void IList.Insert(int index, T item) + // https://github.com/js6pak/libil2cpp-archive/blob/90c6b7ed1c291d54b257d751a4d743d07dea8d62/vm/Array.cpp#L273-L286 + private protected long IndexFromIndices(ReadOnlySpan indices) { - ThrowImmutableLength(); + int rank = GetRank(); + long pos; + + pos = indices[0] - GetLowerBound(0); + + for (var i = 1; i < rank; i++) + pos = pos * GetLength(i) + indices[i] - GetLowerBound(i); + + return pos; } - void IList.RemoveAt(int index) + private protected static void SetClassPointer(uint rank) + where TArray : Il2CppArrayBase + where TElement : IIl2CppType { - ThrowImmutableLength(); + Il2CppClassPointerStore.NativeClassPointer = IL2CPP.il2cpp_array_class_get(Il2CppClassPointerStore.NativeClassPointer, rank); } - - public abstract T this[int index] { get; set; } - - protected static void StaticCtorBody(Type ownType) +} +public abstract class Il2CppArrayBase : Il2CppArrayBase + where T : IIl2CppType +{ + private protected Il2CppArrayBase(ObjectPointer pointer) : base(pointer) { - var nativeClassPtr = Il2CppClassPointerStore.NativeClassPtr; - if (nativeClassPtr == IntPtr.Zero) - return; - - var targetClassType = IL2CPP.il2cpp_array_class_get(nativeClassPtr, 1); - if (targetClassType == IntPtr.Zero) - return; - - Il2CppClassPointerStore.SetNativeClassPointer(ownType, targetClassType); - Il2CppClassPointerStore.SetNativeClassPointer(typeof(Il2CppArrayBase), targetClassType); - Il2CppClassPointerStore>.CreatedTypeRedirect = ownType; } - [return: NotNullIfNotNull(nameof(il2CppArray))] - public static implicit operator T[]?(Il2CppArrayBase? il2CppArray) + private protected Il2CppArrayBase(ReadOnlySpan lengths, IntPtr arrayClass) : this(AllocateArray(lengths, arrayClass)) { - if (il2CppArray == null) - return null; - - var arr = new T[il2CppArray.Length]; - for (var i = 0; i < arr.Length; i++) - arr[i] = il2CppArray[i]; - - return arr; } - public static Il2CppArrayBase? WrapNativeGenericArrayPointer(IntPtr pointer) + public T this[params ReadOnlySpan indices] { - if (pointer == IntPtr.Zero) return null; - - if (typeof(T) == typeof(string)) - return new Il2CppStringArray(pointer) as Il2CppArrayBase; - if (typeof(T).IsValueType) // can't construct required types here directly because of unfulfilled generic constraint - return Activator.CreateInstance(typeof(Il2CppStructArray<>).MakeGenericType(typeof(T)), pointer) as - Il2CppArrayBase; - if (typeof(Il2CppObjectBase).IsAssignableFrom(typeof(T))) - return Activator.CreateInstance(typeof(Il2CppReferenceArray<>).MakeGenericType(typeof(T)), pointer) as - Il2CppArrayBase; - - throw new ArgumentException( - $"{typeof(T)} is not a value type, not a string and not an IL2CPP object; it can't be used in IL2CPP arrays"); + get => GetElementAddress(indices).GetValue()!; + set => GetElementAddress(indices).SetValue(value); } - private class IndexEnumerator : IEnumerator + public unsafe ByReference GetElementAddress(params ReadOnlySpan indices) { - private Il2CppArrayBase myArray; - private int myIndex = -1; - - public IndexEnumerator(Il2CppArrayBase array) - { - myArray = array; - } - - public void Dispose() - { - myArray = null!; - } - - public bool MoveNext() - { - return ++myIndex < myArray.Count; - } - - public void Reset() - { - myIndex = -1; - } - - object? IEnumerator.Current => Current; - public T Current => myArray[myIndex]; + var flatIndex = IndexFromIndices(indices); + void* elementPtr = (byte*)ArrayStartPointer + flatIndex * T.Size; + return new ByReference(elementPtr); } } diff --git a/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayRank1.cs b/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayRank1.cs new file mode 100644 index 00000000..254572e5 --- /dev/null +++ b/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayRank1.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Il2CppInterop.Common; +using Il2CppInterop.Runtime.Runtime; + +namespace Il2CppInterop.Runtime.InteropTypes.Arrays; + +public static class Il2CppArrayRank1 +{ + public static Il2CppArrayRank1 Create(ReadOnlySpan span) where T : IIl2CppType + { + return new Il2CppArrayRank1(span); + } + + public static Il2CppArrayRank1 CreateUnmanaged(ReadOnlySpan span) + where T : unmanaged, IIl2CppType + where U : unmanaged + { + if (Unsafe.SizeOf() != Unsafe.SizeOf()) + throw new ArgumentException($"Cannot create an array of {typeof(T)} from a span of {typeof(U)}: sizes do not match"); + + return Create(MemoryMarshal.Cast(span)); + } +} +[CollectionBuilder(typeof(Il2CppArrayRank1), nameof(Il2CppArrayRank1.Create))] +public sealed class Il2CppArrayRank1 : Il2CppArrayBase, IIl2CppType>, IList, IReadOnlyList, IEnumerable, ICollection + where T : IIl2CppType +{ + static Il2CppArrayRank1() + { + SetClassPointer, T>(1); + Il2CppObjectPool.RegisterInitializer(Il2CppClassPointerStore>.NativeClassPointer, static (ptr) => new Il2CppArrayRank1(ptr)); + } + + public Il2CppArrayRank1(ObjectPointer pointer) : base(pointer) + { + } + + public Il2CppArrayRank1(int length) : base(AllocateArray(length)) + { + } + + public Il2CppArrayRank1(ReadOnlySpan values) : this(values.Length) + { + for (var i_0 = 0; i_0 < values.Length; i_0++) + { + this[i_0] = values[i_0]; + } + } + + private static ObjectPointer AllocateArray(int length) + { + ArgumentOutOfRangeException.ThrowIfNegative(length); + + var elementTypeClassPointer = Il2CppClassPointerStore.NativeClassPointer; + if (elementTypeClassPointer == IntPtr.Zero) + throw new ArgumentException($"{nameof(Il2CppArrayRank1<>)} requires an Il2Cpp type, which {typeof(T)} isn't"); + return (ObjectPointer)IL2CPP.il2cpp_array_new(elementTypeClassPointer, (ulong)length); + } + + public T this[int index] + { + get + { + ThrowIfIndexOutOfRange(index); + return T.ReadFromSpan(AsSpan().Slice(index * T.Size, T.Size))!; + } + set + { + ThrowIfIndexOutOfRange(index); + T.WriteToSpan(value, AsSpan().Slice(index * T.Size, T.Size)); + } + } + + public unsafe ByReference GetElementAddress(int index) + { + ThrowIfIndexOutOfRange(index); + return new ByReference((byte*)ArrayStartPointer.ToPointer() + index * T.Size); + } + + private unsafe Span AsSpan() + { + return new Span(ArrayStartPointer.ToPointer(), Length * T.Size); + } + + private protected override Span GetUnsafeSpanForElement(int index) + { + return GetElementAddress(index).AsSpan(); + } + + public IEnumerator GetEnumerator() + { + return new IndexEnumerator(this); + } + + public bool Contains(T item) + { + return IndexOf(item) != -1; + } + + public void CopyTo(T[] array, int arrayIndex) + { + ArgumentNullException.ThrowIfNull(array); + ArgumentOutOfRangeException.ThrowIfNegative(arrayIndex); + if (array.Length - arrayIndex < Length) + throw new ArgumentException( + $"Not enough space in target array: need {Length} slots, have {array.Length - arrayIndex}"); + + for (var i = 0; i < Length; i++) + array[i + arrayIndex] = this[i]; + } + + public int IndexOf(T item) + { + for (var i = 0; i < Length; i++) + if (Equals(item, this[i])) + return i; + + return -1; + } + + #region Conversions + [return: NotNullIfNotNull(nameof(array))] + public static explicit operator Il2CppArrayRank1?(T[]? array) => array is null ? null : new(array); + + public static explicit operator Il2CppArrayRank1(ReadOnlySpan span) + { + return new Il2CppArrayRank1(span); + } + + public static explicit operator Il2CppArrayRank1(Span span) + { + return new Il2CppArrayRank1(span); + } + + [return: NotNullIfNotNull(nameof(array))] + public static explicit operator T[]?(Il2CppArrayRank1? array) + { + if (array is null) + return null; + + var length_0 = array.GetLength(0); + var result = new T[length_0]; + for (var i_0 = 0; i_0 < length_0; i_0++) + { + result[i_0] = array[i_0]; + } + return result; + } + + public static explicit operator ReadOnlySpan(Il2CppArrayRank1? array) + { + return (ReadOnlySpan)(T[]?)array; + } + + public static explicit operator Span(Il2CppArrayRank1? array) + { + return (Span)(T[]?)array; + } + #endregion + + #region IIl2CppType Implementation + static int IIl2CppType>.Size => IntPtr.Size; + nint IIl2CppType.ObjectClass => Il2CppClassPointerStore>.NativeClassPointer; + static void IIl2CppType>.WriteToSpan(Il2CppArrayRank1? value, Span span) => Il2CppType.WriteReference(value, span); + static Il2CppArrayRank1? IIl2CppType>.ReadFromSpan(ReadOnlySpan span) => Il2CppType.ReadReference>(span); + #endregion + + #region Collection Implementation + int ICollection.Count => Length; + bool ICollection.IsSynchronized => false; + object ICollection.SyncRoot => throw new NotSupportedException(); + int ICollection.Count => Length; + int IReadOnlyCollection.Count => Length; + bool ICollection.IsReadOnly => false; + + void ICollection.Add(T item) + { + ThrowImmutableLength(); + } + + void ICollection.Clear() + { + ThrowImmutableLength(); + } + + void ICollection.CopyTo(Array array, int index) => CopyTo((T[])array, index); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + void IList.Insert(int index, T item) + { + ThrowImmutableLength(); + } + + bool ICollection.Remove(T item) + { + return ThrowImmutableLength(); + } + + void IList.RemoveAt(int index) + { + ThrowImmutableLength(); + } + + private sealed class IndexEnumerator(Il2CppArrayRank1 array) : IEnumerator + { + private int index = -1; + + public void Dispose() => array = null!; + + public bool MoveNext() + { + return ++index < ((ICollection)array).Count; + } + + public void Reset() => index = -1; + + object? IEnumerator.Current => Current; + public T Current => array[index]; + } + #endregion +} diff --git a/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayRank2.cs b/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayRank2.cs new file mode 100644 index 00000000..dbe7d0f0 --- /dev/null +++ b/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayRank2.cs @@ -0,0 +1,74 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Il2CppInterop.Common; +using Il2CppInterop.Runtime.Runtime; + +namespace Il2CppInterop.Runtime.InteropTypes.Arrays; + +public sealed class Il2CppArrayRank2 : Il2CppArrayBase, IIl2CppType> + where T : IIl2CppType +{ + static int IIl2CppType>.Size => IntPtr.Size; + + nint IIl2CppType.ObjectClass => Il2CppClassPointerStore>.NativeClassPointer; + + static Il2CppArrayRank2() + { + SetClassPointer, T>(2); + Il2CppObjectPool.RegisterInitializer(Il2CppClassPointerStore>.NativeClassPointer, static (ptr) => new Il2CppArrayRank2(ptr)); + } + + public Il2CppArrayRank2(ObjectPointer pointer) : base(pointer) + { + } + + public Il2CppArrayRank2(int length0, int length1) : base([length0, length1], Il2CppClassPointerStore>.NativeClassPointer) + { + } + + public Il2CppArrayRank2(T[,] values) : this(values.GetLength(0), values.GetLength(1)) + { + var length_0 = values.GetLength(0); + var length_1 = values.GetLength(1); + + for (var i_0 = 0; i_0 < length_0; i_0++) + { + for (var i_1 = 0; i_1 < length_1; i_1++) + { + this[i_0, i_1] = values[i_0, i_1]; + } + } + } + + public T this[int index0, int index1] + { + get => this[[index0, index1]]; + set => this[[index0, index1]] = value; + } + public ByReference GetElementAddress(int index0, int index1) => GetElementAddress([index0, index1]); + + static void IIl2CppType>.WriteToSpan(Il2CppArrayRank2? value, Span span) => Il2CppType.WriteReference(value, span); + static Il2CppArrayRank2? IIl2CppType>.ReadFromSpan(ReadOnlySpan span) => Il2CppType.ReadReference>(span); + + [return: NotNullIfNotNull(nameof(array))] + public static explicit operator Il2CppArrayRank2?(T[,]? array) => array is null ? null : new(array); + + [return: NotNullIfNotNull(nameof(array))] + public static explicit operator T[,]?(Il2CppArrayRank2? array) + { + if (array is null) + return null; + + var length_0 = array.GetLength(0); + var length_1 = array.GetLength(1); + var result = new T[length_0, length_1]; + for (var i_0 = 0; i_0 < length_0; i_0++) + { + for (var i_1 = 0; i_1 < length_1; i_1++) + { + result[i_0, i_1] = array[i_0, i_1]; + } + } + return result; + } +} diff --git a/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayRank3.cs b/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayRank3.cs new file mode 100644 index 00000000..84133bae --- /dev/null +++ b/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayRank3.cs @@ -0,0 +1,82 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Il2CppInterop.Common; +using Il2CppInterop.Runtime.Runtime; + +namespace Il2CppInterop.Runtime.InteropTypes.Arrays; + +public sealed class Il2CppArrayRank3 : Il2CppArrayBase, IIl2CppType> + where T : IIl2CppType +{ + static int IIl2CppType>.Size => IntPtr.Size; + + nint IIl2CppType.ObjectClass => Il2CppClassPointerStore>.NativeClassPointer; + + static Il2CppArrayRank3() + { + SetClassPointer, T>(3); + Il2CppObjectPool.RegisterInitializer(Il2CppClassPointerStore>.NativeClassPointer, static (ptr) => new Il2CppArrayRank3(ptr)); + } + + public Il2CppArrayRank3(ObjectPointer pointer) : base(pointer) + { + } + + public Il2CppArrayRank3(int length0, int length1, int length2) : base([length0, length1, length2], Il2CppClassPointerStore>.NativeClassPointer) + { + } + + public Il2CppArrayRank3(T[,,] values) : this(values.GetLength(0), values.GetLength(1), values.GetLength(2)) + { + var length_0 = values.GetLength(0); + var length_1 = values.GetLength(1); + var length_2 = values.GetLength(2); + + for (var i_0 = 0; i_0 < length_0; i_0++) + { + for (var i_1 = 0; i_1 < length_1; i_1++) + { + for (var i_2 = 0; i_2 < length_2; i_2++) + { + this[i_0, i_1, i_2] = values[i_0, i_1, i_2]; + } + } + } + } + + public T this[int index0, int index1] + { + get => this[[index0, index1]]; + set => this[[index0, index1]] = value; + } + public ByReference GetElementAddress(int index0, int index1, int index2) => GetElementAddress([index0, index1, index2]); + + static void IIl2CppType>.WriteToSpan(Il2CppArrayRank3? value, Span span) => Il2CppType.WriteReference(value, span); + static Il2CppArrayRank3? IIl2CppType>.ReadFromSpan(ReadOnlySpan span) => Il2CppType.ReadReference>(span); + + [return: NotNullIfNotNull(nameof(array))] + public static explicit operator Il2CppArrayRank3?(T[,,]? array) => array is null ? null : new(array); + + [return: NotNullIfNotNull(nameof(array))] + public static explicit operator T[,,]?(Il2CppArrayRank3? array) + { + if (array is null) + return null; + + var length_0 = array.GetLength(0); + var length_1 = array.GetLength(1); + var length_2 = array.GetLength(2); + var result = new T[length_0, length_1, length_2]; + for (var i_0 = 0; i_0 < length_0; i_0++) + { + for (var i_1 = 0; i_1 < length_1; i_1++) + { + for (var i_2 = 0; i_2 < length_2; i_2++) + { + result[i_0, i_1, i_2] = array[i_0, i_1, i_2]; + } + } + } + return result; + } +} diff --git a/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppReferenceArray.cs b/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppReferenceArray.cs deleted file mode 100644 index b2d9b181..00000000 --- a/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppReferenceArray.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using Il2CppInterop.Runtime.Runtime; - -namespace Il2CppInterop.Runtime.InteropTypes.Arrays; - -public class Il2CppReferenceArray : Il2CppArrayBase where T : Il2CppObjectBase? -{ - private static readonly int ourElementTypeSize; - private static readonly bool ourElementIsValueType; - - static Il2CppReferenceArray() - { - ourElementTypeSize = IntPtr.Size; - var nativeClassPtr = Il2CppClassPointerStore.NativeClassPtr; - if (nativeClassPtr == IntPtr.Zero) return; - uint align = 0; - if (IL2CPP.il2cpp_class_is_valuetype(nativeClassPtr)) - { - ourElementIsValueType = true; - ourElementTypeSize = IL2CPP.il2cpp_class_value_size(nativeClassPtr, ref align); - } - - StaticCtorBody(typeof(Il2CppReferenceArray)); - } - - public Il2CppReferenceArray(IntPtr nativeObject) : base(nativeObject) - { - } - - public Il2CppReferenceArray(long size) : base(AllocateArray(size)) - { - } - - public Il2CppReferenceArray(T[] arr) : base(AllocateArray(arr.Length)) - { - for (var i = 0; i < arr.Length; i++) - this[i] = arr[i]; - } - - public override T this[int index] - { - get => WrapElement(GetElementPointer(index))!; - set => StoreValue(GetElementPointer(index), value?.Pointer ?? IntPtr.Zero); - } - - private IntPtr GetElementPointer(int index) - { - ThrowIfIndexOutOfRange(index); - return IntPtr.Add(ArrayStartPointer, index * ourElementTypeSize); - } - - [return: NotNullIfNotNull(nameof(arr))] - public static implicit operator Il2CppReferenceArray?(T[]? arr) - { - if (arr == null) return null; - - return new Il2CppReferenceArray(arr); - } - - private static unsafe void StoreValue(IntPtr targetPointer, IntPtr valuePointer) - { - if (ourElementIsValueType) - { - if (valuePointer == IntPtr.Zero) - throw new NullReferenceException(); - - var valueRawPointer = (byte*)IL2CPP.il2cpp_object_unbox(valuePointer); - var targetRawPointer = (byte*)targetPointer; - - Unsafe.CopyBlock(targetRawPointer, valueRawPointer, (uint)ourElementTypeSize); - } - else - { - *(IntPtr*)targetPointer = valuePointer; - } - } - - private static unsafe T? WrapElement(IntPtr memberPointer) - { - if (ourElementIsValueType) - memberPointer = IL2CPP.il2cpp_value_box(Il2CppClassPointerStore.NativeClassPtr, memberPointer); - else - memberPointer = *(IntPtr*)memberPointer; - - if (memberPointer == IntPtr.Zero) - return default; - - return Il2CppObjectPool.Get(memberPointer); - } - - private static IntPtr AllocateArray(long size) - { - if (size < 0) - throw new ArgumentOutOfRangeException(nameof(size), "Array size must not be negative"); - - var elementTypeClassPointer = Il2CppClassPointerStore.NativeClassPtr; - if (elementTypeClassPointer == IntPtr.Zero) - throw new ArgumentException( - $"{nameof(Il2CppReferenceArray)} requires an Il2Cpp reference type, which {typeof(T)} isn't"); - return IL2CPP.il2cpp_array_new(elementTypeClassPointer, (ulong)size); - } -} diff --git a/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppStringArray.cs b/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppStringArray.cs deleted file mode 100644 index 834f24e2..00000000 --- a/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppStringArray.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; - -namespace Il2CppInterop.Runtime.InteropTypes.Arrays; - -public class Il2CppStringArray : Il2CppArrayBase -{ - static Il2CppStringArray() - { - StaticCtorBody(typeof(Il2CppStringArray)); - } - - public Il2CppStringArray(IntPtr pointer) : base(pointer) - { - } - - public Il2CppStringArray(long size) : base(AllocateArray(size)) - { - } - - public Il2CppStringArray(string?[] arr) : base(AllocateArray(arr.Length)) - { - for (var i = 0; i < arr.Length; i++) - this[i] = arr[i]; - } -#nullable disable - public override unsafe string this[int index] - { - get => IL2CPP.Il2CppStringToManaged(*GetElementPointer(index)); - set => *GetElementPointer(index) = IL2CPP.ManagedStringToIl2Cpp(value); - } -#nullable enable - private unsafe IntPtr* GetElementPointer(int index) - { - ThrowIfIndexOutOfRange(index); - return (IntPtr*)IntPtr.Add(ArrayStartPointer, index * IntPtr.Size).ToPointer(); - } - - [return: NotNullIfNotNull(nameof(arr))] - public static implicit operator Il2CppStringArray?(string?[]? arr) - { - if (arr == null) return null; - - return new Il2CppStringArray(arr); - } - - private static IntPtr AllocateArray(long size) - { - if (size < 0) - throw new ArgumentOutOfRangeException(nameof(size), "Array size must not be negative"); - - var elementTypeClassPointer = Il2CppClassPointerStore.NativeClassPtr; - if (elementTypeClassPointer == IntPtr.Zero) - throw new ArgumentException("String class pointer is missing, something is very wrong"); - return IL2CPP.il2cpp_array_new(elementTypeClassPointer, (ulong)size); - } -} diff --git a/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppStructArray.cs b/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppStructArray.cs deleted file mode 100644 index 044928d9..00000000 --- a/Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppStructArray.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; - -namespace Il2CppInterop.Runtime.InteropTypes.Arrays; - -public class Il2CppStructArray : Il2CppArrayBase where T : unmanaged -{ - static Il2CppStructArray() - { - StaticCtorBody(typeof(Il2CppStructArray)); - } - - public Il2CppStructArray(IntPtr nativeObject) : base(nativeObject) - { - } - - public Il2CppStructArray(long size) : base(AllocateArray(size)) - { - } - - public Il2CppStructArray(T[] arr) : base(AllocateArray(arr.Length)) - { - arr.CopyTo(this); - } - - public override T this[int index] - { - get => AsSpan()[index]; - set => AsSpan()[index] = value; - } - - public unsafe Span AsSpan() - { - return new Span(ArrayStartPointer.ToPointer(), Length); - } - - [return: NotNullIfNotNull(nameof(arr))] - public static implicit operator Il2CppStructArray?(T[]? arr) - { - if (arr == null) return null; - - return new Il2CppStructArray(arr); - } - - public static implicit operator Span(Il2CppStructArray? il2CppArray) - { - return il2CppArray is not null ? il2CppArray.AsSpan() : default; - } - - public static implicit operator ReadOnlySpan(Il2CppStructArray? il2CppArray) - { - return il2CppArray is not null ? il2CppArray.AsSpan() : default; - } - - private static IntPtr AllocateArray(long size) - { - if (size < 0) - throw new ArgumentOutOfRangeException(nameof(size), "Array size must not be negative"); - - var elementTypeClassPointer = Il2CppClassPointerStore.NativeClassPtr; - if (elementTypeClassPointer == IntPtr.Zero) - throw new ArgumentException( - $"{nameof(Il2CppStructArray)} requires an Il2Cpp reference type, which {typeof(T)} isn't"); - return IL2CPP.il2cpp_array_new(elementTypeClassPointer, (ulong)size); - } -} diff --git a/Il2CppInterop.Runtime/InteropTypes/ByReference.cs b/Il2CppInterop.Runtime/InteropTypes/ByReference.cs new file mode 100644 index 00000000..ff397e34 --- /dev/null +++ b/Il2CppInterop.Runtime/InteropTypes/ByReference.cs @@ -0,0 +1,103 @@ +using System; +using Il2CppInterop.Common; + +namespace Il2CppInterop.Runtime.InteropTypes; + +public static class ByReference +{ + public static T? GetValue(ByReference byReference) + where T : IIl2CppType + { + return byReference.GetValue(); + } + + public static void SetValue1(ByReference byReference, T? value) + where T : IIl2CppType + { + byReference.SetValue(value); + } + + public static void SetValue2(T? value, ByReference byReference) + where T : IIl2CppType + { + byReference.SetValue(value); + } + + // ldflda + public static unsafe ByReference GetReferenceAtOffset(ByReference byReference, int offset) + where T : IIl2CppType + where U : IIl2CppType + { + return new ByReference((byte*)byReference.ToPointer() + offset); + } +} +public readonly unsafe struct ByReference(void* pointer) : IIl2CppType>, IByReference + where T : IIl2CppType +{ + private readonly void* _pointer = pointer; + + public readonly bool IsNull => _pointer is null; + + public readonly T? GetValue() + { + ThrowIfNull(); + return Il2CppType.ReadFromPointer(_pointer); + } + + public readonly void SetValue(T? value) + { + ThrowIfNull(); + Il2CppType.WriteToPointer(value, _pointer); + } + + public readonly void Clear() + { + ThrowIfNull(); + Span span = new(_pointer, ReferenceSize); + span.Clear(); + } + + public readonly void CopyFrom(in T? value) => SetValue(value); + + public readonly void CopyTo(out T? value) => value = GetValue(); + + public readonly void CopyToUnmanaged(out U value) where U : unmanaged + { + ThrowIfNull(); + if (ReferenceSize != sizeof(U)) + { + throw new InvalidOperationException($"Cannot copy ByReference<{typeof(T).Name}> to unmanaged type {typeof(U).Name} because their sizes do not match. Size of ByReference<{typeof(T).Name}> is {ReferenceSize} bytes, size of {typeof(U).Name} is {sizeof(U)} bytes."); + } + value = *(U*)_pointer; + } + + public readonly void* ToPointer() => _pointer; + + private static int ReferenceSize => T.Size; + + readonly nint IIl2CppType.ObjectClass => 0; // ByReference types have no class + + static int IIl2CppType>.Size => IntPtr.Size; + + public static explicit operator ByReference(void* value) => new(value); + public static explicit operator void*(ByReference pointer) => pointer._pointer; + + public static explicit operator ByReference(IntPtr value) => new(value.ToPointer()); + public static explicit operator IntPtr(ByReference pointer) => new(pointer._pointer); + + public readonly Span AsSpan() + { + return new Span(_pointer, T.Size); + } + + private readonly void ThrowIfNull() + { + if (_pointer is null) + { + throw new NullReferenceException($"Cannot access reference of type {typeof(T).Name} because it is null."); + } + } + + static void IIl2CppType>.WriteToSpan(ByReference value, Span span) => Il2CppType.WritePointer((IntPtr)value._pointer, span); + static ByReference IIl2CppType>.ReadFromSpan(ReadOnlySpan span) => (ByReference)(void*)Il2CppType.ReadPointer(span); +} diff --git a/Il2CppInterop.Runtime/InteropTypes/CoreLib/AppDomainAccessor.cs b/Il2CppInterop.Runtime/InteropTypes/CoreLib/AppDomainAccessor.cs new file mode 100644 index 00000000..08c065e6 --- /dev/null +++ b/Il2CppInterop.Runtime/InteropTypes/CoreLib/AppDomainAccessor.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Il2CppInterop.Runtime.InteropTypes.Arrays; +using Il2CppSystem.Reflection; + +namespace Il2CppInterop.Runtime.InteropTypes.CoreLib; + +internal static class AppDomainAccessor +{ + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "GetAssemblies")] + [return: UnsafeAccessorType($"Il2CppInterop.Runtime.InteropTypes.Arrays.{nameof(Il2CppArrayRank1<>)}`1[[Il2CppSystem.Reflection.Assembly, Il2Cppmscorlib]]")] + public static extern object GetAssemblies([UnsafeAccessorType("Il2CppSystem.AppDomain, Il2Cppmscorlib")] object appDomain); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "get_CurrentDomain")] + [return: UnsafeAccessorType("Il2CppSystem.AppDomain, Il2Cppmscorlib")] + public static extern object GetCurrentDomain([UnsafeAccessorType("Il2CppSystem.AppDomain, Il2Cppmscorlib")] object? declaringType = null); + + public static IReadOnlyList GetAssembliesInCurrentDomain() + { + return (IReadOnlyList)GetAssemblies(GetCurrentDomain()); + } +} diff --git a/Il2CppInterop.Runtime/InteropTypes/CoreLib/MethodBaseAccessor.cs b/Il2CppInterop.Runtime/InteropTypes/CoreLib/MethodBaseAccessor.cs new file mode 100644 index 00000000..8905b9f5 --- /dev/null +++ b/Il2CppInterop.Runtime/InteropTypes/CoreLib/MethodBaseAccessor.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Il2CppInterop.Runtime.InteropTypes.Arrays; +using Il2CppSystem.Reflection; + +namespace Il2CppInterop.Runtime.InteropTypes.CoreLib; + +internal static class MethodBaseAccessor +{ + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "GetParameters")] + [return: UnsafeAccessorType($"Il2CppInterop.Runtime.InteropTypes.Arrays.{nameof(Il2CppArrayRank1<>)}`1[[Il2CppSystem.Reflection.ParameterInfo, Il2Cppmscorlib]]")] + private static extern object GetParametersInternal(MethodBase method); + + public static IReadOnlyList GetParameters(this MethodBase method) + { + return (IReadOnlyList)GetParametersInternal(method); + } +} diff --git a/Il2CppInterop.Runtime/InteropTypes/CoreLib/TypeAccessor.cs b/Il2CppInterop.Runtime/InteropTypes/CoreLib/TypeAccessor.cs new file mode 100644 index 00000000..f0dc66ce --- /dev/null +++ b/Il2CppInterop.Runtime/InteropTypes/CoreLib/TypeAccessor.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Il2CppInterop.Runtime.InteropTypes.Arrays; +using Il2CppSystem; +using Il2CppSystem.Reflection; + +namespace Il2CppInterop.Runtime.InteropTypes.CoreLib; + +internal static class TypeAccessor +{ + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "GetConstructors")] + [return: UnsafeAccessorType($"Il2CppInterop.Runtime.InteropTypes.Arrays.{nameof(Il2CppArrayRank1<>)}`1[[Il2CppSystem.Reflection.ConstructorInfo, Il2Cppmscorlib]]")] + private static extern object GetConstructorsInternal(Type type, BindingFlags bindingAttr); + + public static IReadOnlyList GetConstructors(this Type type, BindingFlags bindingAttr) + { + return (IReadOnlyList)GetConstructorsInternal(type, bindingAttr); + } +} diff --git a/Il2CppInterop.Runtime/InteropTypes/FieldAccess.cs b/Il2CppInterop.Runtime/InteropTypes/FieldAccess.cs new file mode 100644 index 00000000..ec5f77dd --- /dev/null +++ b/Il2CppInterop.Runtime/InteropTypes/FieldAccess.cs @@ -0,0 +1,91 @@ +using System; +using System.Runtime.InteropServices; +using Il2CppInterop.Common; + +namespace Il2CppInterop.Runtime.InteropTypes; + +public static unsafe class FieldAccess +{ + public static T? GetStaticFieldValue(IntPtr fieldInfoPtr) where T : IIl2CppType + { + ArgumentNullException.ThrowIfNull(fieldInfoPtr.ToPointer(), nameof(fieldInfoPtr)); + var data = stackalloc byte[T.Size]; + IL2CPP.il2cpp_field_static_get_value(fieldInfoPtr, data); + return T.ReadFromSpan(new ReadOnlySpan(data, T.Size)); + } + + public static void SetStaticFieldValue(IntPtr fieldInfoPtr, T? value) where T : IIl2CppType + { + ArgumentNullException.ThrowIfNull(fieldInfoPtr.ToPointer(), nameof(fieldInfoPtr)); + if (typeof(T).IsValueType) + { + var data = stackalloc byte[T.Size]; + value.WriteToPointer(data); + IL2CPP.il2cpp_field_static_set_value(fieldInfoPtr, data); + } + else + { + IL2CPP.il2cpp_field_static_set_value(fieldInfoPtr, (void*)value.Box()); + } + } + + public static T? GetInstanceFieldValue(Object instance, int fieldOffset) where T : IIl2CppType + { + ArgumentOutOfRangeException.ThrowIfNegative(fieldOffset); + var data = (byte*)instance.Pointer + fieldOffset; + return Il2CppType.ReadFromPointer(data); + } + + public static void SetInstanceFieldValue(Object instance, int fieldOffset, T? value) where T : IIl2CppType + { + FunctionPointerCache.SetInstanceFieldValue(instance, fieldOffset, value); + } + + public static void SetInstanceFieldValue_Wbarrior(Object instance, int fieldOffset, T? value) where T : IIl2CppType + { + ArgumentOutOfRangeException.ThrowIfNegative(fieldOffset); + var data = (byte*)instance.Pointer + fieldOffset; + if (typeof(T).IsValueType) + { + value!.WriteToPointer(data); + } + else + { + IL2CPP.il2cpp_gc_wbarrier_set_field(instance.Pointer, (IntPtr)data, value.Box()); + } + } + + public static void SetInstanceFieldValue_Pointer(Object instance, int fieldOffset, T? value) where T : IIl2CppType + { + ArgumentOutOfRangeException.ThrowIfNegative(fieldOffset); + var data = (byte*)instance.Pointer + fieldOffset; + if (typeof(T).IsValueType) + { + value!.WriteToPointer(data); + } + else + { + *(IntPtr*)data = value.Box(); + } + } + + private static bool HasWbarriorSupport() + { + if (NativeLibrary.TryLoad("GameAssembly", out var handle)) + { + var result = NativeLibrary.TryGetExport(handle, "il2cpp_gc_wbarrier_set_field", out _); + NativeLibrary.Free(handle); + return result; + } + return false; + } + + private static bool WbarriorSupport { get; } = HasWbarriorSupport(); + + private static class FunctionPointerCache where T : IIl2CppType + { + public static readonly delegate* SetInstanceFieldValue = WbarriorSupport + ? &FieldAccess.SetInstanceFieldValue_Wbarrior + : &FieldAccess.SetInstanceFieldValue_Pointer; + } +} diff --git a/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppReferenceField.cs b/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppReferenceField.cs deleted file mode 100644 index ee5c8024..00000000 --- a/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppReferenceField.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using Il2CppInterop.Runtime.Runtime; - -namespace Il2CppInterop.Runtime.InteropTypes.Fields; - -public unsafe class Il2CppReferenceField where TRefObj : Il2CppObjectBase -{ - private readonly IntPtr _fieldPtr; - private readonly Il2CppObjectBase _obj; - - internal Il2CppReferenceField(Il2CppObjectBase obj, string fieldName) - { - _obj = obj; - _fieldPtr = IL2CPP.GetIl2CppField(obj.ObjectClass, fieldName); - } - - public TRefObj Value - { - get => Get(); - set => Set(value); - } - - public TRefObj? Get() - { - var ptr = *GetPointerToData(); - return ptr == IntPtr.Zero ? null : Il2CppObjectPool.Get(ptr); - } - - public void Set(TRefObj value) - { - *GetPointerToData() = value != null ? value.Pointer : IntPtr.Zero; - } - - public static implicit operator TRefObj(Il2CppReferenceField _this) - { - return _this.Get(); - } - - public static implicit operator Il2CppReferenceField(TRefObj _) - { - throw null; - } - - private IntPtr* GetPointerToData() - { - return (IntPtr*)(IL2CPP.Il2CppObjectBaseToPtrNotNull(_obj) + (int)IL2CPP.il2cpp_field_get_offset(_fieldPtr)); - } -} diff --git a/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppStringField.cs b/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppStringField.cs deleted file mode 100644 index 50a98e98..00000000 --- a/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppStringField.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; - -namespace Il2CppInterop.Runtime.InteropTypes.Fields; - -public unsafe class Il2CppStringField -{ - private readonly IntPtr _fieldPtr; - - private readonly Il2CppObjectBase _obj; - - internal Il2CppStringField(Il2CppObjectBase obj, string fieldName) - { - _obj = obj; - _fieldPtr = IL2CPP.GetIl2CppField(obj.ObjectClass, fieldName); - } - - public string Value - { - get => Get(); - set => Set(value); - } - - public string? Get() - { - var ptr = *GetPointerToData(); - if (ptr == IntPtr.Zero) return null; - - return IL2CPP.Il2CppStringToManaged(ptr); - } - - public void Set(string value) - { - *GetPointerToData() = IL2CPP.ManagedStringToIl2Cpp(value); - } - - public static implicit operator string(Il2CppStringField _this) - { - return _this.Get(); - } - - public static implicit operator Il2CppStringField(string _) - { - throw null; - } - - public override string ToString() - { - return Get(); - } - - private IntPtr* GetPointerToData() - { - return (IntPtr*)(IL2CPP.Il2CppObjectBaseToPtrNotNull(_obj) + (int)IL2CPP.il2cpp_field_get_offset(_fieldPtr)); - } -} diff --git a/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppValueField.cs b/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppValueField.cs deleted file mode 100644 index 5c02564b..00000000 --- a/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppValueField.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; - -namespace Il2CppInterop.Runtime.InteropTypes.Fields; - -public unsafe class Il2CppValueField where T : unmanaged -{ - private readonly IntPtr _fieldPtr; - - private readonly Il2CppObjectBase _obj; - - internal Il2CppValueField(Il2CppObjectBase obj, string fieldName) - { - _obj = obj; - _fieldPtr = IL2CPP.GetIl2CppField(obj.ObjectClass, fieldName); - } - - public T Value - { - get => Get(); - set => Set(value); - } - - public T Get() - { - return *GetPointerToData(); - } - - public void Set(T value) - { - *GetPointerToData() = value; - } - - public static implicit operator T(Il2CppValueField _this) - { - return _this.Get(); - } - - public static implicit operator Il2CppValueField(T _) - { - throw null; - } - - private T* GetPointerToData() - { - return (T*)(IL2CPP.Il2CppObjectBaseToPtrNotNull(_obj) + (int)IL2CPP.il2cpp_field_get_offset(_fieldPtr)); - } -} diff --git a/Il2CppInterop.Runtime/InteropTypes/IByReference.cs b/Il2CppInterop.Runtime/InteropTypes/IByReference.cs new file mode 100644 index 00000000..865eba66 --- /dev/null +++ b/Il2CppInterop.Runtime/InteropTypes/IByReference.cs @@ -0,0 +1,11 @@ +namespace Il2CppInterop.Runtime.InteropTypes; + +/// +/// Marker interface for +/// +/// +/// Nothing else should implement this interface. +/// +internal interface IByReference +{ +} diff --git a/Il2CppInterop.Runtime/InteropTypes/IPointer.cs b/Il2CppInterop.Runtime/InteropTypes/IPointer.cs new file mode 100644 index 00000000..7a32682a --- /dev/null +++ b/Il2CppInterop.Runtime/InteropTypes/IPointer.cs @@ -0,0 +1,11 @@ +namespace Il2CppInterop.Runtime.InteropTypes; + +/// +/// Marker interface for +/// +/// +/// Nothing else should implement this interface. +/// +internal interface IPointer +{ +} diff --git a/Il2CppInterop.Runtime/InteropTypes/Il2CppObjectBase.cs b/Il2CppInterop.Runtime/InteropTypes/Il2CppObjectBase.cs deleted file mode 100644 index 7cbc0f1a..00000000 --- a/Il2CppInterop.Runtime/InteropTypes/Il2CppObjectBase.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Reflection; -using System.Reflection.Emit; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; -using Il2CppInterop.Runtime.Runtime; - -namespace Il2CppInterop.Runtime.InteropTypes; - -public class Il2CppObjectBase -{ - private static readonly MethodInfo _unboxMethod = typeof(Il2CppObjectBase).GetMethod(nameof(Unbox)); - internal bool isWrapped; - internal IntPtr pooledPtr; - - private nint myGcHandle; - - public Il2CppObjectBase(IntPtr pointer) - { - CreateGCHandle(pointer); - } - - public IntPtr ObjectClass => IL2CPP.il2cpp_object_get_class(Pointer); - - public IntPtr Pointer - { - get - { - var handleTarget = IL2CPP.il2cpp_gchandle_get_target(myGcHandle); - if (handleTarget == IntPtr.Zero) - throw new ObjectCollectedException("Object was garbage collected in IL2CPP domain"); - return handleTarget; - } - } - - public bool WasCollected - { - get - { - var handleTarget = IL2CPP.il2cpp_gchandle_get_target(myGcHandle); - if (handleTarget == IntPtr.Zero) return true; - return false; - } - } - - internal void CreateGCHandle(IntPtr objHdl) - { - if (objHdl == IntPtr.Zero) - throw new NullReferenceException(); - - // This object already wraps an Il2Cpp object, skip the pointer and let it be GC'd - if (isWrapped) - return; - - myGcHandle = IL2CPP.il2cpp_gchandle_new(objHdl, false); - } - - public T Cast() where T : Il2CppObjectBase - { - return TryCast() ?? throw new InvalidCastException( - $"Can't cast object of type {IL2CPP.il2cpp_class_get_name_(IL2CPP.il2cpp_object_get_class(Pointer))} to type {typeof(T)}"); - } - - internal static unsafe T UnboxUnsafe(IntPtr pointer) - { - var nestedTypeClassPointer = Il2CppClassPointerStore.NativeClassPtr; - if (nestedTypeClassPointer == IntPtr.Zero) - throw new ArgumentException($"{typeof(T)} is not an Il2Cpp reference type"); - - var ownClass = IL2CPP.il2cpp_object_get_class(pointer); - if (!IL2CPP.il2cpp_class_is_assignable_from(nestedTypeClassPointer, ownClass)) - throw new InvalidCastException( - $"Can't cast object of type {IL2CPP.il2cpp_class_get_name_(ownClass)} to type {typeof(T)}"); - - return Unsafe.AsRef(IL2CPP.il2cpp_object_unbox(pointer).ToPointer()); - } - - public T Unbox() where T : unmanaged - { - return UnboxUnsafe(Pointer); - } - - private static readonly Type[] _intPtrTypeArray = { typeof(IntPtr) }; - private static readonly MethodInfo _getUninitializedObject = typeof(RuntimeHelpers).GetMethod(nameof(RuntimeHelpers.GetUninitializedObject))!; - private static readonly MethodInfo _getTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle))!; - private static readonly MethodInfo _createGCHandle = typeof(Il2CppObjectBase).GetMethod(nameof(CreateGCHandle), BindingFlags.Instance | BindingFlags.NonPublic)!; - private static readonly FieldInfo _isWrapped = typeof(Il2CppObjectBase).GetField(nameof(isWrapped), BindingFlags.Instance | BindingFlags.NonPublic)!; - - internal static class InitializerStore - { - private static Func? _initializer; - - private static Func Create() - { - var type = Il2CppClassPointerStore.CreatedTypeRedirect ?? typeof(T); - - var dynamicMethod = new DynamicMethod($"Initializer<{typeof(T).AssemblyQualifiedName}>", type, _intPtrTypeArray); - dynamicMethod.DefineParameter(0, ParameterAttributes.None, "pointer"); - - var il = dynamicMethod.GetILGenerator(); - - if (type.GetConstructor(new[] { typeof(IntPtr) }) is { } pointerConstructor) - { - // Base case: Il2Cpp constructor => call it directly - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Newobj, pointerConstructor); - } - else - { - // Special case: We have a parameterless constructor - // However, it could be be user-made or implicit - // In that case we set the GCHandle and then call the ctor and let GC destroy any objects created by DerivedConstructorPointer - - // var obj = (T)RuntimeHelpers.GetUninitializedObject(type); - il.Emit(OpCodes.Ldtoken, type); - il.Emit(OpCodes.Call, _getTypeFromHandle); - il.Emit(OpCodes.Call, _getUninitializedObject); - il.Emit(OpCodes.Castclass, type); - - // obj.CreateGCHandle(pointer); - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Callvirt, _createGCHandle); - - // obj.isWrapped = true; - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Stfld, _isWrapped); - - var parameterlessConstructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, Type.EmptyTypes); - if (parameterlessConstructor != null) - { - // obj..ctor(); - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Callvirt, parameterlessConstructor); - } - } - - il.Emit(OpCodes.Ret); - - return dynamicMethod.CreateDelegate>(); - } - - public static Func Initializer => _initializer ??= Create(); - } - - public T? TryCast() where T : Il2CppObjectBase - { - var nestedTypeClassPointer = Il2CppClassPointerStore.NativeClassPtr; - if (nestedTypeClassPointer == IntPtr.Zero) - throw new ArgumentException($"{typeof(T)} is not an Il2Cpp reference type"); - - var ownClass = IL2CPP.il2cpp_object_get_class(Pointer); - if (!IL2CPP.il2cpp_class_is_assignable_from(nestedTypeClassPointer, ownClass)) - return null; - - if (RuntimeSpecificsStore.IsInjected(ownClass)) - { - if (ClassInjectorBase.GetMonoObjectFromIl2CppPointer(Pointer) is T monoObject) return monoObject; - } - - return InitializerStore.Initializer(Pointer); - } - - ~Il2CppObjectBase() - { - IL2CPP.il2cpp_gchandle_free(myGcHandle); - - if (pooledPtr == IntPtr.Zero) return; - Il2CppObjectPool.Remove(pooledPtr); - } -} diff --git a/Il2CppInterop.Runtime/InteropTypes/Il2CppType.cs b/Il2CppInterop.Runtime/InteropTypes/Il2CppType.cs new file mode 100644 index 00000000..a3279145 --- /dev/null +++ b/Il2CppInterop.Runtime/InteropTypes/Il2CppType.cs @@ -0,0 +1,175 @@ +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Il2CppInterop.Common; +using Il2CppInterop.Runtime.Runtime; + +namespace Il2CppInterop.Runtime.InteropTypes; + +public static class Il2CppType +{ + public static bool IsBlittable() where T : IIl2CppType + { + return T.Size == Unsafe.SizeOf() + && !RuntimeHelpers.IsReferenceOrContainsReferences() + && !typeof(T).IsGenericType; + } + + public static int SizeOf() where T : IIl2CppType + { + return T.Size; + } + + public static unsafe void InitializeObject(void* ptr) where T : IIl2CppType + { + default(T).WriteToPointer(ptr); + } + + public static unsafe void CopyObject(void* source, void* destination) where T : IIl2CppType + { + Buffer.MemoryCopy(source, destination, T.Size, T.Size); + } + + public static unsafe void StoreObject(void* ptr, T? value) where T : IIl2CppType + { + value.WriteToPointer(ptr); + } + + public static string GetAssemblyName() where T : IIl2CppType + { + return T.AssemblyName; + } + + public static string GetNamespace() where T : IIl2CppType + { + return T.Namespace; + } + + public static string GetName() where T : IIl2CppType + { + return T.Name; + } + + public static (string AssemblyName, string Namespace, string Name) GetFullyQualifiedName() where T : IIl2CppType + { + return (T.AssemblyName, T.Namespace, T.Name); + } + + public static void WriteToSpan(this T? value, Span span) where T : IIl2CppType + { + T.WriteToSpan(value, span); + } + + public static T? ReadFromSpan(ReadOnlySpan span) where T : IIl2CppType + { + return T.ReadFromSpan(span); + } + + public static void WriteToSpanAtOffset(this T? value, Span span, int offset) where T : IIl2CppType + { + T.WriteToSpan(value, span.Slice(offset, T.Size)); + } + + public static T? ReadFromSpanAtOffset(ReadOnlySpan span, int offset) where T : IIl2CppType + { + return T.ReadFromSpan(span.Slice(offset, T.Size)); + } + + public static void WriteToSpanBlittable(T value, Span span) where T : unmanaged + { + MemoryMarshal.Write(span, in value); + } + + public static T ReadFromSpanBlittable(ReadOnlySpan span) where T : unmanaged + { + return MemoryMarshal.Read(span); + } + + public static unsafe void WriteToPointer(this T? value, void* ptr) where T : IIl2CppType + { + T.WriteToSpan(value, new Span(ptr, T.Size)); + } + + public static unsafe T? ReadFromPointer(void* ptr) where T : IIl2CppType + { + return T.ReadFromSpan(new ReadOnlySpan(ptr, T.Size)); + } + + public static T? ReadReference(ReadOnlySpan span) where T : IIl2CppType + { + return (T?)Il2CppObjectPool.Get(ReadPointer(span)); + } + + public static void WriteReference(T? value, Span span) where T : IIl2CppType + { + WritePointer(value.Box(), span); + } + + public static void WriteClass(Object? value, Span span) + { + if (value == null) + { + WritePointer(IntPtr.Zero, span); + } + else + { + WritePointer(value.Pointer, span); + } + } + + public static nint ReadPointer(ReadOnlySpan span) + { + if (BitConverter.IsLittleEndian) + { + return BinaryPrimitives.ReadIntPtrLittleEndian(span); + } + else + { + return BinaryPrimitives.ReadIntPtrBigEndian(span); + } + } + + public static void WritePointer(nint pointer, Span span) + { + if (BitConverter.IsLittleEndian) + { + BinaryPrimitives.WriteIntPtrLittleEndian(span, pointer); + } + else + { + BinaryPrimitives.WriteIntPtrBigEndian(span, pointer); + } + } + + public static unsafe IntPtr Box(this T? value) where T : IIl2CppType + { + if (typeof(T).IsValueType) + { + byte* data = stackalloc byte[T.Size]; + WriteToPointer(value, data); + IntPtr boxedPtr = IL2CPP.il2cpp_value_box(value!.ObjectClass, (IntPtr)data); + return boxedPtr; + } + else if (value is null) + { + return IntPtr.Zero; + } + else if (value is Object @object) + { + return @object.Pointer; + } + else if (value is Il2CppSystem.IValueType valueType) + { + int size = valueType.Size; + byte* data = stackalloc byte[size]; + valueType.WriteToSpan(new Span(data, size)); + IntPtr boxedPtr = IL2CPP.il2cpp_value_box(value.ObjectClass, (IntPtr)data); + return boxedPtr; + } + else + { + throw new InvalidCastException(); + } + } +} diff --git a/Il2CppInterop.Runtime/InteropTypes/Pointer.cs b/Il2CppInterop.Runtime/InteropTypes/Pointer.cs new file mode 100644 index 00000000..780d923f --- /dev/null +++ b/Il2CppInterop.Runtime/InteropTypes/Pointer.cs @@ -0,0 +1,54 @@ +using System; +using Il2CppInterop.Common; + +namespace Il2CppInterop.Runtime.InteropTypes; + +public readonly unsafe struct Pointer(void* pointer) : IIl2CppType>, IPointer + where T : IIl2CppType +{ + private readonly void* _pointer = pointer; + + public readonly bool IsNull => _pointer is null; + + public readonly T? this[int index] + { + get + { + ThrowIfNull(); + void* start = (byte*)_pointer + T.Size * index; + return Il2CppType.ReadFromPointer(start); + } + set + { + ThrowIfNull(); + void* start = (byte*)_pointer + T.Size * index; + Il2CppType.WriteToPointer(value, start); + } + } + + public readonly void* ToPointer() => _pointer; + + private readonly void ThrowIfNull() + { + if (_pointer is null) + { + throw new NullReferenceException($"Cannot access reference of type {typeof(T).Name} because it is null."); + } + } + + static int IIl2CppType>.Size => IntPtr.Size; + + readonly nint IIl2CppType.ObjectClass => Il2CppClassPointerStore>.NativeClassPointer; + + static Pointer IIl2CppType>.ReadFromSpan(ReadOnlySpan span) => (Pointer)(void*)Il2CppType.ReadPointer(span); + static void IIl2CppType>.WriteToSpan(Pointer value, Span span) => Il2CppType.WritePointer((IntPtr)value._pointer, span); + + public static explicit operator Pointer(void* value) => new(value); + public static explicit operator void*(Pointer pointer) => pointer._pointer; + + static Pointer() + { + var elementType = Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(Il2CppClassPointerStore.NativeClassPointer)); + Il2CppClassPointerStore>.NativeClassPointer = IL2CPP.il2cpp_class_from_type(elementType.MakePointerType().TypeHandle.value); + } +} diff --git a/Il2CppInterop.Runtime/InteropTypes/RuntimeInvoke.cs b/Il2CppInterop.Runtime/InteropTypes/RuntimeInvoke.cs new file mode 100644 index 00000000..6e0d3a04 --- /dev/null +++ b/Il2CppInterop.Runtime/InteropTypes/RuntimeInvoke.cs @@ -0,0 +1,98 @@ +using System; +using System.Runtime.CompilerServices; +using Il2CppInterop.Common; +using Il2CppInterop.Runtime.Runtime; +using Il2CppException = Il2CppInterop.Runtime.Exceptions.Il2CppException; + +namespace Il2CppInterop.Runtime.InteropTypes; + +public static unsafe class RuntimeInvoke +{ + private static IntPtr Il2CppRuntimeInvoke(IntPtr method, IntPtr obj, void** parameters) + { + ArgumentNullException.ThrowIfNull(method.ToPointer(), nameof(method)); + IntPtr exception = default; + var result = IL2CPP.il2cpp_runtime_invoke(method, obj, parameters, ref exception); + Il2CppException.RaiseExceptionIfNecessary(exception); + return result; + } + + public static void InvokeAction(IntPtr method, IntPtr obj, void** parameters) + { + Il2CppRuntimeInvoke(method, obj, parameters); + } + + public static TResult? InvokeFunction(IntPtr method, IntPtr obj, void** parameters) + where TResult : IIl2CppType + { + var result = Il2CppRuntimeInvoke(method, obj, parameters); + if (IsPointerOrByRef()) + { + // Pointers and by refs are returned directly. + return Unsafe.As(ref result); + } + else if (IsValueType()) + { + // This is a performance optimization. The other code path would also return the correct result. + var data = (byte*)IL2CPP.il2cpp_object_unbox(result); + return Il2CppType.ReadFromPointer(data); + } + else + { + return (TResult?)Il2CppObjectPool.Get(result); + } + } + + public static IntPtr GetPointerForThis(ByReference @this) + where T : IIl2CppType + { + if (typeof(T).IsValueType) + { + return new IntPtr(@this.ToPointer()); + } + else + { + return @this.GetValue().Box(); + } + } + + public static IntPtr GetPointerForParameter(ByReference parameter) + where T : IIl2CppType + { + if (IsPointerOrByRef()) + { + // Pointer to pointer, which is passed directly + return *(IntPtr*)parameter.ToPointer(); + } + else if (IsValueType()) + { + // Pointer to value type data + return new IntPtr(parameter.ToPointer()); + } + else + { + // Pointer to object pointer, which is passed directly + return *(IntPtr*)parameter.ToPointer(); + } + } + + private static bool IsPointerOrByRef() + { + return IsPointer() || IsByRef(); + } + + private static bool IsPointer() + { + return typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Pointer<>); + } + + private static bool IsByRef() + { + return typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(ByReference<>); + } + + private static bool IsValueType() + { + return typeof(T).IsValueType; + } +} diff --git a/Il2CppInterop.Runtime/Libs/Il2Cppmscorlib.dll b/Il2CppInterop.Runtime/Libs/Il2Cppmscorlib.dll deleted file mode 100644 index 0a5715c3..00000000 Binary files a/Il2CppInterop.Runtime/Libs/Il2Cppmscorlib.dll and /dev/null differ diff --git a/Il2CppInterop.Runtime/Runtime/ApplicableToUnityVersionsSinceAttribute.cs b/Il2CppInterop.Runtime/Runtime/ApplicableToUnityVersionsSinceAttribute.cs new file mode 100644 index 00000000..22fde8ba --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/ApplicableToUnityVersionsSinceAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace Il2CppInterop.Runtime.Runtime; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] +internal class ApplicableToUnityVersionsSinceAttribute : Attribute +{ + public ApplicableToUnityVersionsSinceAttribute(string startVersion) + { + StartVersion = startVersion; + } + + public string StartVersion { get; } +} diff --git a/Il2CppInterop.Runtime/Runtime/ClassInjectorBase.cs b/Il2CppInterop.Runtime/Runtime/ClassInjectorBase.cs deleted file mode 100644 index 96ea8d05..00000000 --- a/Il2CppInterop.Runtime/Runtime/ClassInjectorBase.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Il2CppInterop.Runtime.Runtime; - -internal struct InjectedClassData -{ - public IntPtr managedGcHandle; -} - -public static class ClassInjectorBase -{ - public static object GetMonoObjectFromIl2CppPointer(IntPtr pointer) - { - var gcHandle = GetGcHandlePtrFromIl2CppObject(pointer); - - if (gcHandle == IntPtr.Zero) // The Garbage collector handle might return a null pointer - { - gcHandle = FallbackGetGcHandlePtrFromIl2CppDelegateMTarget(pointer); - } - - return GCHandle.FromIntPtr(gcHandle).Target; - } - - /// - /// Tries to get the Garbage collector pointer from the m_target object from the m_target of the delegate. - /// Fixes Harmony in Unity 2021.2.x . - /// - private static IntPtr FallbackGetGcHandlePtrFromIl2CppDelegateMTarget(IntPtr pointer) - { - if (IL2CPP.il2cpp_class_is_assignable_from(Il2CppClassPointerStore.NativeClassPtr, IL2CPP.il2cpp_object_get_class(pointer))) - { - var delegateObject = new Il2CppSystem.Delegate(pointer); - if (delegateObject.m_target != null && delegateObject.m_target.Pointer != IntPtr.Zero) - return GetGcHandlePtrFromIl2CppObject(delegateObject.m_target.Pointer); - } - return IntPtr.Zero; - } - - public static unsafe IntPtr GetGcHandlePtrFromIl2CppObject(IntPtr pointer) - { - return GetInjectedData(pointer)->managedGcHandle; - } - - internal static unsafe InjectedClassData* GetInjectedData(IntPtr objectPointer) - { - var pObjectClass = (Il2CppClass*)IL2CPP.il2cpp_object_get_class(objectPointer); - return (InjectedClassData*)(objectPointer + (int)UnityVersionHandler.Wrap(pObjectClass).InstanceSize - - sizeof(InjectedClassData)).ToPointer(); - } -} diff --git a/Il2CppInterop.Runtime/Runtime/Il2CppObjectPool.cs b/Il2CppInterop.Runtime/Runtime/Il2CppObjectPool.cs index 10abec31..44573352 100644 --- a/Il2CppInterop.Runtime/Runtime/Il2CppObjectPool.cs +++ b/Il2CppInterop.Runtime/Runtime/Il2CppObjectPool.cs @@ -1,8 +1,7 @@ using System; using System.Collections.Concurrent; -using System.Runtime.CompilerServices; +using Il2CppInterop.Common; using Il2CppInterop.Runtime.InteropTypes; -using Object = Il2CppSystem.Object; namespace Il2CppInterop.Runtime.Runtime; @@ -10,38 +9,62 @@ public static class Il2CppObjectPool { internal static bool DisableCaching { get; set; } - private static readonly ConcurrentDictionary> s_cache = new(); + private static readonly ConcurrentDictionary> s_cache = new(); - internal static void Remove(IntPtr ptr) + private static readonly ConcurrentDictionary> s_initializers = new(); + + public static void Remove(nint ptr) { s_cache.TryRemove(ptr, out _); } - public static T Get(IntPtr ptr) + public static object? Get(nint ptr) { - if (ptr == IntPtr.Zero) return default; + if (ptr == nint.Zero) + return null; + + if (s_cache.TryGetValue(ptr, out var reference) && reference.TryGetTarget(out var cachedObject)) + { + return cachedObject; + } var ownClass = IL2CPP.il2cpp_object_get_class(ptr); - if (RuntimeSpecificsStore.IsInjected(ownClass)) + if (!s_initializers.TryGetValue(ownClass, out var initializer)) { - var monoObject = ClassInjectorBase.GetMonoObjectFromIl2CppPointer(ptr); - if (monoObject is T monoObjectT) return monoObjectT; + var className = IL2CPP.il2cpp_class_get_name(ownClass); + throw new InvalidOperationException($"No initializer found for class {className}"); } - if (DisableCaching) return Il2CppObjectBase.InitializerStore.Initializer(ptr); + var newObj = initializer((ObjectPointer)ptr); + if (newObj is Object @object) + { + if (!DisableCaching) + { + s_cache[ptr] = new WeakReference(@object); + } + } - if (s_cache.TryGetValue(ptr, out var reference) && reference.TryGetTarget(out var cachedObject)) + return newObj; + } + + public static void RegisterInitializer(nint classPtr, Func initializer) + { + ArgumentOutOfRangeException.ThrowIfZero(classPtr); + if (!s_initializers.TryAdd(classPtr, initializer)) { - if (cachedObject is T cachedObjectT) return cachedObjectT; - cachedObject.pooledPtr = IntPtr.Zero; - // This leaves the case when you cast to an interface handled as if nothing was cached + var className = IL2CPP.il2cpp_class_get_name(classPtr); + throw new InvalidOperationException($"Initializer for class {className} is already registered"); } + } - var newObj = Il2CppObjectBase.InitializerStore.Initializer(ptr); + public static void RegisterValueTypeInitializer() where T : struct, IIl2CppType + { + RegisterInitializer(Il2CppClassPointerStore.NativeClassPointer, ValueTypeInitializer); + } - var il2CppObjectBase = Unsafe.As(ref newObj); - s_cache[ptr] = new WeakReference(il2CppObjectBase); - il2CppObjectBase.pooledPtr = ptr; - return newObj; + public static unsafe object ValueTypeInitializer(ObjectPointer obj) where T : struct, IIl2CppType + { + var unboxed = IL2CPP.il2cpp_object_unbox((nint)obj); + return Il2CppType.ReadFromPointer((void*)unboxed); } } diff --git a/Il2CppInterop.Runtime/Runtime/UnityVersionHandler.cs b/Il2CppInterop.Runtime/Runtime/UnityVersionHandler.cs index 0e4e1f7d..c3256fee 100644 --- a/Il2CppInterop.Runtime/Runtime/UnityVersionHandler.cs +++ b/Il2CppInterop.Runtime/Runtime/UnityVersionHandler.cs @@ -2,8 +2,10 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; +using AssetRipper.Primitives; using Il2CppInterop.Common; -using Il2CppInterop.Common.Extensions; +using Il2CppInterop.Runtime.Extensions; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Assembly; using Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class; @@ -20,21 +22,10 @@ namespace Il2CppInterop.Runtime.Runtime; -[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] -internal class ApplicableToUnityVersionsSinceAttribute : Attribute -{ - public ApplicableToUnityVersionsSinceAttribute(string startVersion) - { - StartVersion = startVersion; - } - - public string StartVersion { get; } -} - public static class UnityVersionHandler { private static readonly Type[] InterfacesOfInterest; - private static readonly Dictionary> VersionedHandlers = new(); + private static readonly Dictionary> VersionedHandlers = new(); private static readonly Dictionary Handlers = new(); internal static INativeAssemblyStructHandler assemblyStructHandler; @@ -51,22 +42,22 @@ public static class UnityVersionHandler static UnityVersionHandler() { - var allTypes = GetAllTypesSafe(); + var allTypes = typeof(UnityVersionHandler).Assembly.GetTypesSafe(); var interfacesOfInterest = allTypes.Where(t => t.IsInterface && typeof(INativeStructHandler).IsAssignableFrom(t) && t != typeof(INativeStructHandler)) .ToArray(); InterfacesOfInterest = interfacesOfInterest; - foreach (var i in interfacesOfInterest) VersionedHandlers[i] = new List<(Version Version, object Handler)>(); + foreach (var i in interfacesOfInterest) VersionedHandlers[i] = new List<(UnityVersion Version, object Handler)>(); foreach (var handlerImpl in allTypes.Where(t => !t.IsAbstract && interfacesOfInterest.Any(i => i.IsAssignableFrom(t)))) foreach (var startVersion in handlerImpl.GetCustomAttributes()) { - var instance = Activator.CreateInstance(handlerImpl); + var instance = Activator.CreateInstance(handlerImpl)!; foreach (var i in handlerImpl.GetInterfaces()) if (interfacesOfInterest.Contains(i)) - VersionedHandlers[i].Add((Version.Parse(startVersion.StartVersion), instance)); + VersionedHandlers[i].Add((UnityVersion.Parse(startVersion.StartVersion), instance)); } foreach (var handlerList in VersionedHandlers.Values) @@ -96,10 +87,10 @@ internal static void RecalculateHandlers() break; } - HasGetMethodFromReflection = unityVersion > new Version(2018, 1, 0); - IsMetadataV29OrHigher = unityVersion >= new Version(2021, 2, 0); + HasGetMethodFromReflection = unityVersion.GreaterThanOrEquals(2018, 2, 0, UnityVersionType.Beta, 6); + IsMetadataV29OrHigher = unityVersion.GreaterThanOrEquals(2021, 2, 0); - HasShimForGetMethod = unityVersion >= new Version(2020, 3, 41) || IsMetadataV29OrHigher; + HasShimForGetMethod = unityVersion.GreaterThanOrEquals(2020, 3, 41) || IsMetadataV29OrHigher; assemblyStructHandler = GetHandler(); assemblyNameStructHandler = GetHandler(); @@ -124,11 +115,6 @@ private static T GetHandler() throw new ApplicationException("No handler"); } - private static Type[] GetAllTypesSafe() - { - return typeof(UnityVersionHandler).Assembly.GetTypesSafe(); - } - //Assemblies public static INativeAssemblyStruct NewAssembly() { @@ -225,7 +211,6 @@ public static int FieldInfoSize() return fieldInfoStructHandler.Size(); } - //Images public static INativeImageStruct NewImage() { @@ -261,7 +246,19 @@ public static int MethodSize() //Parameters public static unsafe Il2CppParameterInfo*[] NewMethodParameterArray(int count) { - return parameterInfoStructHandler.CreateNewParameterInfoArray(count); + if (count == 0) + return []; + + var elementSize = parameterInfoStructHandler.Size(); + var totalSize = elementSize * count; + var startPointer = Marshal.AllocHGlobal(totalSize); + new Span(startPointer.ToPointer(), totalSize).Clear(); + var result = new Il2CppParameterInfo*[count]; + for (var i = 0; i < count; i++) + { + result[i] = (Il2CppParameterInfo*)(startPointer + i * elementSize); + } + return result; } public static unsafe INativeParameterInfoStruct Wrap(Il2CppParameterInfo* parameterInfo) @@ -271,15 +268,10 @@ public static unsafe INativeParameterInfoStruct Wrap(Il2CppParameterInfo* parame public static unsafe INativeParameterInfoStruct Wrap(Il2CppParameterInfo* parameterInfo, int index) { - return parameterInfoStructHandler.Wrap(parameterInfo, index); + var address = (nint)parameterInfo + index * parameterInfoStructHandler.Size(); + return parameterInfoStructHandler.Wrap((Il2CppParameterInfo*)address); } - public static bool ParameterInfoHasNamePosToken() - { - return parameterInfoStructHandler.HasNamePosToken; - } - - //Properties public static INativePropertyInfoStruct NewProperty() { diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_104_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_104_0.cs new file mode 100644 index 00000000..ca109c73 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_104_0.cs @@ -0,0 +1,44 @@ +using System.Runtime.InteropServices; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Assembly +{ + [ApplicableToUnityVersionsSince("6000.5.0")] + public unsafe class NativeAssemblyStructHandler_104_0 : INativeAssemblyStructHandler + { + public int Size() => sizeof(Il2CppAssembly_104_0); + public INativeAssemblyStruct CreateNewStruct() + { + nint ptr = Marshal.AllocHGlobal(Size()); + Il2CppAssembly_104_0* _ = (Il2CppAssembly_104_0*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeAssemblyStruct Wrap(Il2CppAssembly* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppAssembly_104_0 + { + public Il2CppImage* image; + public uint token; + public uint moduleToken; + public int referencedAssemblyStart; + public int referencedAssemblyCount; + public NativeAssemblyNameStructHandler_24_1.Il2CppAssemblyName_24_1 aname; + } + internal class NativeStructWrapper : INativeAssemblyStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } + private Il2CppAssembly_104_0* _ => (Il2CppAssembly_104_0*)Pointer; + public Il2CppAssembly* AssemblyPointer => (Il2CppAssembly*)Pointer; + public INativeAssemblyNameStruct Name + { + get => UnityVersionHandler.Wrap((Il2CppAssemblyName*)&_->aname); + set => _->aname = *(NativeAssemblyNameStructHandler_24_1.Il2CppAssemblyName_24_1*)Name.AssemblyNamePointer; + } + public ref Il2CppImage* Image => ref _->image; + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_16_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_16_0.cs index 22c2ebcb..a57d78e9 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_16_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_16_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Assembly @@ -9,7 +8,7 @@ public unsafe class NativeAssemblyStructHandler_16_0 : INativeAssemblyStructHand public int Size() => sizeof(Il2CppAssembly_16_0); public INativeAssemblyStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppAssembly_16_0* _ = (Il2CppAssembly_16_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeAssemblyStruct CreateNewStruct() public INativeAssemblyStruct Wrap(Il2CppAssembly* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppAssembly_16_0 { @@ -25,11 +24,10 @@ internal unsafe struct Il2CppAssembly_16_0 public int customAttributeIndex; public NativeAssemblyNameStructHandler_16_0.Il2CppAssemblyName_16_0 aname; } - internal class NativeStructWrapper : INativeAssemblyStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppAssembly_16_0* _ => (Il2CppAssembly_16_0*)Pointer; public Il2CppAssembly* AssemblyPointer => (Il2CppAssembly*)Pointer; public INativeAssemblyNameStruct Name @@ -37,9 +35,7 @@ public INativeAssemblyNameStruct Name get => UnityVersionHandler.Wrap((Il2CppAssemblyName*)&_->aname); set => _->aname = *(NativeAssemblyNameStructHandler_16_0.Il2CppAssemblyName_16_0*)Name.AssemblyNamePointer; } - public ref Il2CppImage* Image => throw new NotSupportedException(); + public ref Il2CppImage* Image => throw new System.NotSupportedException(); } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_20_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_20_0.cs index 24fd7b72..91c7d673 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_20_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_20_0.cs @@ -1,15 +1,14 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Assembly { - [ApplicableToUnityVersionsSince("5.3.3")] + [ApplicableToUnityVersionsSince("5.3.2p1")] public unsafe class NativeAssemblyStructHandler_20_0 : INativeAssemblyStructHandler { public int Size() => sizeof(Il2CppAssembly_20_0); public INativeAssemblyStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppAssembly_20_0* _ = (Il2CppAssembly_20_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeAssemblyStruct CreateNewStruct() public INativeAssemblyStruct Wrap(Il2CppAssembly* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppAssembly_20_0 { @@ -27,11 +26,10 @@ internal unsafe struct Il2CppAssembly_20_0 public int referencedAssemblyCount; public NativeAssemblyNameStructHandler_16_0.Il2CppAssemblyName_16_0 aname; } - internal class NativeStructWrapper : INativeAssemblyStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppAssembly_20_0* _ => (Il2CppAssembly_20_0*)Pointer; public Il2CppAssembly* AssemblyPointer => (Il2CppAssembly*)Pointer; public INativeAssemblyNameStruct Name @@ -39,9 +37,7 @@ public INativeAssemblyNameStruct Name get => UnityVersionHandler.Wrap((Il2CppAssemblyName*)&_->aname); set => _->aname = *(NativeAssemblyNameStructHandler_16_0.Il2CppAssemblyName_16_0*)Name.AssemblyNamePointer; } - public ref Il2CppImage* Image => throw new NotSupportedException(); + public ref Il2CppImage* Image => throw new System.NotSupportedException(); } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_24_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_24_0.cs index b01f3c0c..28e6c5a2 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_24_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_24_0.cs @@ -1,15 +1,14 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Assembly { - [ApplicableToUnityVersionsSince("2018.1.0")] + [ApplicableToUnityVersionsSince("2018.1.0b5")] public unsafe class NativeAssemblyStructHandler_24_0 : INativeAssemblyStructHandler { public int Size() => sizeof(Il2CppAssembly_24_0); public INativeAssemblyStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppAssembly_24_0* _ = (Il2CppAssembly_24_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeAssemblyStruct CreateNewStruct() public INativeAssemblyStruct Wrap(Il2CppAssembly* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppAssembly_24_0 { @@ -27,11 +26,10 @@ internal unsafe struct Il2CppAssembly_24_0 public int referencedAssemblyCount; public NativeAssemblyNameStructHandler_24_0.Il2CppAssemblyName_24_0 aname; } - internal class NativeStructWrapper : INativeAssemblyStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppAssembly_24_0* _ => (Il2CppAssembly_24_0*)Pointer; public Il2CppAssembly* AssemblyPointer => (Il2CppAssembly*)Pointer; public INativeAssemblyNameStruct Name @@ -41,7 +39,5 @@ public INativeAssemblyNameStruct Name } public ref Il2CppImage* Image => ref _->image; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_24_1.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_24_1.cs index 202e1d46..f480124d 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_24_1.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_24_1.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Assembly @@ -9,7 +8,7 @@ public unsafe class NativeAssemblyStructHandler_24_1 : INativeAssemblyStructHand public int Size() => sizeof(Il2CppAssembly_24_1); public INativeAssemblyStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppAssembly_24_1* _ = (Il2CppAssembly_24_1*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeAssemblyStruct CreateNewStruct() public INativeAssemblyStruct Wrap(Il2CppAssembly* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppAssembly_24_1 { @@ -27,11 +26,10 @@ internal unsafe struct Il2CppAssembly_24_1 public int referencedAssemblyCount; public NativeAssemblyNameStructHandler_24_0.Il2CppAssemblyName_24_0 aname; } - internal class NativeStructWrapper : INativeAssemblyStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppAssembly_24_1* _ => (Il2CppAssembly_24_1*)Pointer; public Il2CppAssembly* AssemblyPointer => (Il2CppAssembly*)Pointer; public INativeAssemblyNameStruct Name @@ -41,7 +39,5 @@ public INativeAssemblyNameStruct Name } public ref Il2CppImage* Image => ref _->image; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_24_2.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_24_2.cs index 06036de7..3b750592 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_24_2.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Assembly_24_2.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Assembly @@ -9,7 +8,7 @@ public unsafe class NativeAssemblyStructHandler_24_2 : INativeAssemblyStructHand public int Size() => sizeof(Il2CppAssembly_24_2); public INativeAssemblyStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppAssembly_24_2* _ = (Il2CppAssembly_24_2*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeAssemblyStruct CreateNewStruct() public INativeAssemblyStruct Wrap(Il2CppAssembly* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppAssembly_24_2 { @@ -27,11 +26,10 @@ internal unsafe struct Il2CppAssembly_24_2 public int referencedAssemblyCount; public NativeAssemblyNameStructHandler_24_1.Il2CppAssemblyName_24_1 aname; } - internal class NativeStructWrapper : INativeAssemblyStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppAssembly_24_2* _ => (Il2CppAssembly_24_2*)Pointer; public Il2CppAssembly* AssemblyPointer => (Il2CppAssembly*)Pointer; public INativeAssemblyNameStruct Name @@ -41,7 +39,5 @@ public INativeAssemblyNameStruct Name } public ref Il2CppImage* Image => ref _->image; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Interfaces.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Interfaces.cs index 9ecb5ea6..410f934a 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Interfaces.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Assembly/Interfaces.cs @@ -1,18 +1,19 @@ using Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName; -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Assembly; - -public interface INativeAssemblyStructHandler : INativeStructHandler +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Assembly { - INativeAssemblyStruct CreateNewStruct(); - unsafe INativeAssemblyStruct Wrap(Il2CppAssembly* assemblyPointer); -} + public interface INativeAssemblyStructHandler : INativeStructHandler + { + INativeAssemblyStruct CreateNewStruct(); + unsafe INativeAssemblyStruct Wrap(Il2CppAssembly* assemblyPointer); + } -public interface INativeAssemblyStruct : INativeStruct -{ - unsafe Il2CppAssembly* AssemblyPointer { get; } + public interface INativeAssemblyStruct : INativeStruct + { + unsafe Il2CppAssembly* AssemblyPointer { get; } - unsafe ref Il2CppImage* Image { get; } + unsafe ref Il2CppImage* Image { get; } - INativeAssemblyNameStruct Name { get; } + INativeAssemblyNameStruct Name { get; } + } } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/AssemblyName_16_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/AssemblyName_16_0.cs index c814fc2a..e785a1ff 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/AssemblyName_16_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/AssemblyName_16_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName { @@ -8,7 +7,7 @@ public unsafe class NativeAssemblyNameStructHandler_16_0 : INativeAssemblyNameSt public int Size() => sizeof(Il2CppAssemblyName_16_0); public INativeAssemblyNameStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppAssemblyName_16_0* _ = (Il2CppAssemblyName_16_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeAssemblyNameStruct CreateNewStruct() public INativeAssemblyNameStruct Wrap(Il2CppAssemblyName* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppAssemblyName_16_0 { @@ -33,23 +32,20 @@ internal unsafe struct Il2CppAssemblyName_16_0 public int revision; public ulong publicKeyToken; } - internal class NativeStructWrapper : INativeAssemblyNameStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppAssemblyName_16_0* _ => (Il2CppAssemblyName_16_0*)Pointer; public Il2CppAssemblyName* AssemblyNamePointer => (Il2CppAssemblyName*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->nameIndex; - public ref IntPtr Culture => ref *(IntPtr*)&_->cultureIndex; - public ref IntPtr PublicKey => ref *(IntPtr*)&_->publicKeyIndex; + public ref nint Name => ref *(nint*)&_->nameIndex; + public ref nint Culture => ref *(nint*)&_->cultureIndex; + public ref nint PublicKey => ref *(nint*)&_->publicKeyIndex; public ref int Major => ref _->major; public ref int Minor => ref _->minor; public ref int Build => ref _->build; public ref int Revision => ref _->revision; public ref ulong PublicKeyToken => ref _->publicKeyToken; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/AssemblyName_24_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/AssemblyName_24_0.cs index 2ef5ac77..105b14fd 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/AssemblyName_24_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/AssemblyName_24_0.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName { - [ApplicableToUnityVersionsSince("2018.1.0")] + [ApplicableToUnityVersionsSince("2018.1.0b5")] public unsafe class NativeAssemblyNameStructHandler_24_0 : INativeAssemblyNameStructHandler { public int Size() => sizeof(Il2CppAssemblyName_24_0); public INativeAssemblyNameStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppAssemblyName_24_0* _ = (Il2CppAssemblyName_24_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeAssemblyNameStruct CreateNewStruct() public INativeAssemblyNameStruct Wrap(Il2CppAssemblyName* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppAssemblyName_24_0 { @@ -33,23 +32,20 @@ internal unsafe struct Il2CppAssemblyName_24_0 public int revision; public ulong public_key_token; } - internal class NativeStructWrapper : INativeAssemblyNameStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppAssemblyName_24_0* _ => (Il2CppAssemblyName_24_0*)Pointer; public Il2CppAssemblyName* AssemblyNamePointer => (Il2CppAssemblyName*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Culture => ref *(IntPtr*)&_->culture; - public ref IntPtr PublicKey => ref *(IntPtr*)&_->public_key; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Culture => ref *(nint*)&_->culture; + public ref nint PublicKey => ref *(nint*)&_->public_key; public ref int Major => ref _->major; public ref int Minor => ref _->minor; public ref int Build => ref _->build; public ref int Revision => ref _->revision; public ref ulong PublicKeyToken => ref _->public_key_token; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/AssemblyName_24_1.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/AssemblyName_24_1.cs index ea761c5d..7a421498 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/AssemblyName_24_1.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/AssemblyName_24_1.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName { @@ -8,7 +7,7 @@ public unsafe class NativeAssemblyNameStructHandler_24_1 : INativeAssemblyNameSt public int Size() => sizeof(Il2CppAssemblyName_24_1); public INativeAssemblyNameStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppAssemblyName_24_1* _ = (Il2CppAssemblyName_24_1*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeAssemblyNameStruct CreateNewStruct() public INativeAssemblyNameStruct Wrap(Il2CppAssemblyName* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppAssemblyName_24_1 { @@ -32,23 +31,20 @@ internal unsafe struct Il2CppAssemblyName_24_1 public int revision; public ulong public_key_token; } - internal class NativeStructWrapper : INativeAssemblyNameStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppAssemblyName_24_1* _ => (Il2CppAssemblyName_24_1*)Pointer; public Il2CppAssemblyName* AssemblyNamePointer => (Il2CppAssemblyName*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Culture => ref *(IntPtr*)&_->culture; - public ref IntPtr PublicKey => ref *(IntPtr*)&_->public_key; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Culture => ref *(nint*)&_->culture; + public ref nint PublicKey => ref *(nint*)&_->public_key; public ref int Major => ref _->major; public ref int Minor => ref _->minor; public ref int Build => ref _->build; public ref int Revision => ref _->revision; public ref ulong PublicKeyToken => ref _->public_key_token; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/Interfaces.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/Interfaces.cs index bb55c1fc..f711d648 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/Interfaces.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/AssemblyName/Interfaces.cs @@ -1,22 +1,23 @@ using System; -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName; - -public interface INativeAssemblyNameStructHandler : INativeStructHandler +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.AssemblyName { - INativeAssemblyNameStruct CreateNewStruct(); - unsafe INativeAssemblyNameStruct Wrap(Il2CppAssemblyName* assemblyNamePointer); -} + public interface INativeAssemblyNameStructHandler : INativeStructHandler + { + INativeAssemblyNameStruct CreateNewStruct(); + unsafe INativeAssemblyNameStruct Wrap(Il2CppAssemblyName* assemblyNamePointer); + } -public interface INativeAssemblyNameStruct : INativeStruct -{ - unsafe Il2CppAssemblyName* AssemblyNamePointer { get; } - ref IntPtr Name { get; } - ref IntPtr Culture { get; } - ref IntPtr PublicKey { get; } - ref int Major { get; } - ref int Minor { get; } - ref int Build { get; } - ref int Revision { get; } - ref ulong PublicKeyToken { get; } + public interface INativeAssemblyNameStruct : INativeStruct + { + unsafe Il2CppAssemblyName* AssemblyNamePointer { get; } + ref IntPtr Name { get; } + ref IntPtr Culture { get; } + ref IntPtr PublicKey { get; } + ref int Major { get; } + ref int Minor { get; } + ref int Build { get; } + ref int Revision { get; } + ref ulong PublicKeyToken { get; } + } } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_104_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_104_0.cs new file mode 100644 index 00000000..c07927a3 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_104_0.cs @@ -0,0 +1,198 @@ +using System.Runtime.InteropServices; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class +{ + [ApplicableToUnityVersionsSince("6000.5.0")] + public unsafe class NativeClassStructHandler_104_0 : INativeClassStructHandler + { + public int Size() => sizeof(Il2CppClass_104_0); + public INativeClassStruct CreateNewStruct(int vTableSlots) + { + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + Il2CppClass_104_0* _ = (Il2CppClass_104_0*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeClassStruct Wrap(Il2CppClass* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppClass_104_0 + { + public Il2CppImage* image; + public void* gc_desc; + public byte* name; + public byte* namespaze; + public NativeTypeStructHandler_27_0.Il2CppType_27_0 byval_arg; + public NativeTypeStructHandler_27_0.Il2CppType_27_0 this_arg; + public Il2CppClass* element_class; + public Il2CppClass* castClass; + public Il2CppClass* declaringType; + public Il2CppClass* parent; + public void* generic_class; + public Il2CppMetadataTypeHandle typeMetadataHandle; + public void* interopData; + public Il2CppClass* klass; + public Il2CppFieldInfo* fields; + public Il2CppMethodInfo** methods; + public Il2CppClass** implementedInterfaces; + public Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + public void* static_fields; + public void* rgctx_data; + public Il2CppClass** typeHierarchy; + public void* events; + public void* properties; + public void* nestedTypes; + public void* unity_user_data; + public Il2CppGCHandle initializationExceptionGCHandle; + public uint cctor_started; + public uint cctor_finished_or_no_cctor; + public ulong cctor_thread; + public Il2CppMetadataGenericContainerHandle genericContainerHandle; + public uint instance_size; + public uint stack_slot_size; + public uint actualSize; + public uint element_size; + public int native_size; + public uint static_fields_size; + public uint thread_static_fields_size; + public int thread_static_fields_offset; + public uint flags; + public uint token; + public ushort method_count; + public ushort property_count; + public ushort field_count; + public ushort event_count; + public ushort nested_type_count; + public ushort vtable_count; + public ushort interfaces_count; + public ushort interface_offsets_count; + public byte typeHierarchyDepth; + public byte genericRecursionDepth; + public byte rank; + public byte minimumAlignment; + public byte packingSize; + public Bitfield0 _bitfield0; + public Bitfield1 _bitfield1; + internal enum Bitfield0 : byte + { + BIT_initialized_and_no_error = 0, + initialized_and_no_error = (1 << BIT_initialized_and_no_error), + BIT_initialized = 1, + initialized = (1 << BIT_initialized), + BIT_enumtype = 2, + enumtype = (1 << BIT_enumtype), + BIT_nullabletype = 3, + nullabletype = (1 << BIT_nullabletype), + BIT_is_generic = 4, + is_generic = (1 << BIT_is_generic), + BIT_has_references = 5, + has_references = (1 << BIT_has_references), + BIT_init_pending = 6, + init_pending = (1 << BIT_init_pending), + BIT_size_init_pending = 7, + size_init_pending = (1 << BIT_size_init_pending), + } + internal enum Bitfield1 : byte + { + BIT_size_inited = 0, + size_inited = (1 << BIT_size_inited), + BIT_has_finalize = 1, + has_finalize = (1 << BIT_has_finalize), + BIT_has_cctor = 2, + has_cctor = (1 << BIT_has_cctor), + BIT_is_blittable = 3, + is_blittable = (1 << BIT_is_blittable), + BIT_is_import_or_windows_runtime = 4, + is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), + BIT_is_vtable_initialized = 5, + is_vtable_initialized = (1 << BIT_is_vtable_initialized), + BIT_is_byref_like = 6, + is_byref_like = (1 << BIT_is_byref_like), + BIT_has_inline_array = 7, + has_inline_array = (1 << BIT_has_inline_array), + } + } + internal class NativeStructWrapper : INativeClassStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_104_0._bitfield0)).ToInt32(); + private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_104_0._bitfield1)).ToInt32(); + public nint Pointer { get; } + private Il2CppClass_104_0* _ => (Il2CppClass_104_0*)Pointer; + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_104_0)); + public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; + public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); + public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); + public ref uint InstanceSize => ref _->instance_size; + public ref ushort VtableCount => ref _->vtable_count; + public ref ushort InterfaceCount => ref _->interfaces_count; + public ref ushort InterfaceOffsetsCount => ref _->interface_offsets_count; + public ref byte TypeHierarchyDepth => ref _->typeHierarchyDepth; + public ref int NativeSize => ref _->native_size; + public ref uint ActualSize => ref _->actualSize; + public ref ushort MethodCount => ref _->method_count; + public ref ushort FieldCount => ref _->field_count; + public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; + public ref Il2CppImage* Image => ref _->image; + public ref Il2CppClass* Parent => ref _->parent; + public ref Il2CppClass* ElementClass => ref _->element_class; + public ref Il2CppClass* CastClass => ref _->castClass; + public ref Il2CppClass* DeclaringType => ref _->declaringType; + public ref Il2CppClass* Class => ref _->klass; + public ref Il2CppFieldInfo* Fields => ref _->fields; + public ref Il2CppMethodInfo** Methods => ref _->methods; + public ref Il2CppClass** ImplementedInterfaces => ref _->implementedInterfaces; + public ref Il2CppRuntimeInterfaceOffsetPair* InterfaceOffsets => ref _->interfaceOffsets; + public ref Il2CppClass** TypeHierarchy => ref _->typeHierarchy; + public bool ValueType + { + get => ByValArg.ValueType && ThisArg.ValueType; + set { } + } + public bool Initialized + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_104_0.Bitfield0.BIT_initialized); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_104_0.Bitfield0.BIT_initialized, value); + } + public bool EnumType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_104_0.Bitfield0.BIT_enumtype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_104_0.Bitfield0.BIT_enumtype, value); + } + public bool IsGeneric + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_104_0.Bitfield0.BIT_is_generic); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_104_0.Bitfield0.BIT_is_generic, value); + } + public bool HasReferences + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_104_0.Bitfield0.BIT_has_references); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_104_0.Bitfield0.BIT_has_references, value); + } + public bool SizeInited + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_104_0.Bitfield1.BIT_size_inited); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_104_0.Bitfield1.BIT_size_inited, value); + } + public bool HasFinalize + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_104_0.Bitfield1.BIT_has_finalize); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_104_0.Bitfield1.BIT_has_finalize, value); + } + public bool IsVtableInitialized + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_104_0.Bitfield1.BIT_is_vtable_initialized); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_104_0.Bitfield1.BIT_is_vtable_initialized, value); + } + public bool InitializedAndNoError + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_104_0.Bitfield0.BIT_initialized_and_no_error); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_104_0.Bitfield0.BIT_initialized_and_no_error, value); + } + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_105_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_105_0.cs new file mode 100644 index 00000000..1267ff11 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_105_0.cs @@ -0,0 +1,198 @@ +using System.Runtime.InteropServices; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class +{ + [ApplicableToUnityVersionsSince("6000.5.0a5")] + public unsafe class NativeClassStructHandler_105_0 : INativeClassStructHandler + { + public int Size() => sizeof(Il2CppClass_105_0); + public INativeClassStruct CreateNewStruct(int vTableSlots) + { + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + Il2CppClass_105_0* _ = (Il2CppClass_105_0*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeClassStruct Wrap(Il2CppClass* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppClass_105_0 + { + public Il2CppImage* image; + public void* gc_desc; + public byte* name; + public byte* namespaze; + public NativeTypeStructHandler_27_0.Il2CppType_27_0 byval_arg; + public NativeTypeStructHandler_27_0.Il2CppType_27_0 this_arg; + public Il2CppClass* element_class; + public Il2CppClass* castClass; + public Il2CppClass* declaringType; + public Il2CppClass* parent; + public void* generic_class; + public Il2CppMetadataTypeHandle typeMetadataHandle; + public void* interopData; + public Il2CppClass* klass; + public Il2CppFieldInfo* fields; + public Il2CppMethodInfo** methods; + public Il2CppClass** implementedInterfaces; + public Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + public void* static_fields; + public void* init_data; + public Il2CppClass** typeHierarchy; + public void* events; + public void* properties; + public void* nestedTypes; + public void* unity_user_data; + public Il2CppGCHandle initializationExceptionGCHandle; + public uint cctor_started; + public uint cctor_finished_or_no_cctor; + public ulong cctor_thread; + public Il2CppMetadataGenericContainerHandle genericContainerHandle; + public uint instance_size; + public uint stack_slot_size; + public uint actualSize; + public uint element_size; + public int native_size; + public uint static_fields_size; + public uint thread_static_fields_size; + public int thread_static_fields_offset; + public uint flags; + public uint token; + public ushort method_count; + public ushort property_count; + public ushort field_count; + public ushort event_count; + public ushort nested_type_count; + public ushort vtable_count; + public ushort interfaces_count; + public ushort interface_offsets_count; + public byte typeHierarchyDepth; + public byte genericRecursionDepth; + public byte rank; + public byte minimumAlignment; + public byte packingSize; + public Bitfield0 _bitfield0; + public Bitfield1 _bitfield1; + internal enum Bitfield0 : byte + { + BIT_initialized_and_no_error = 0, + initialized_and_no_error = (1 << BIT_initialized_and_no_error), + BIT_initialized = 1, + initialized = (1 << BIT_initialized), + BIT_enumtype = 2, + enumtype = (1 << BIT_enumtype), + BIT_nullabletype = 3, + nullabletype = (1 << BIT_nullabletype), + BIT_is_generic = 4, + is_generic = (1 << BIT_is_generic), + BIT_has_references = 5, + has_references = (1 << BIT_has_references), + BIT_init_pending = 6, + init_pending = (1 << BIT_init_pending), + BIT_size_init_pending = 7, + size_init_pending = (1 << BIT_size_init_pending), + } + internal enum Bitfield1 : byte + { + BIT_size_inited = 0, + size_inited = (1 << BIT_size_inited), + BIT_has_finalize = 1, + has_finalize = (1 << BIT_has_finalize), + BIT_has_cctor = 2, + has_cctor = (1 << BIT_has_cctor), + BIT_is_blittable = 3, + is_blittable = (1 << BIT_is_blittable), + BIT_is_import_or_windows_runtime = 4, + is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), + BIT_is_vtable_initialized = 5, + is_vtable_initialized = (1 << BIT_is_vtable_initialized), + BIT_is_byref_like = 6, + is_byref_like = (1 << BIT_is_byref_like), + BIT_has_inline_array = 7, + has_inline_array = (1 << BIT_has_inline_array), + } + } + internal class NativeStructWrapper : INativeClassStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_105_0._bitfield0)).ToInt32(); + private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_105_0._bitfield1)).ToInt32(); + public nint Pointer { get; } + private Il2CppClass_105_0* _ => (Il2CppClass_105_0*)Pointer; + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_105_0)); + public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; + public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); + public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); + public ref uint InstanceSize => ref _->instance_size; + public ref ushort VtableCount => ref _->vtable_count; + public ref ushort InterfaceCount => ref _->interfaces_count; + public ref ushort InterfaceOffsetsCount => ref _->interface_offsets_count; + public ref byte TypeHierarchyDepth => ref _->typeHierarchyDepth; + public ref int NativeSize => ref _->native_size; + public ref uint ActualSize => ref _->actualSize; + public ref ushort MethodCount => ref _->method_count; + public ref ushort FieldCount => ref _->field_count; + public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; + public ref Il2CppImage* Image => ref _->image; + public ref Il2CppClass* Parent => ref _->parent; + public ref Il2CppClass* ElementClass => ref _->element_class; + public ref Il2CppClass* CastClass => ref _->castClass; + public ref Il2CppClass* DeclaringType => ref _->declaringType; + public ref Il2CppClass* Class => ref _->klass; + public ref Il2CppFieldInfo* Fields => ref _->fields; + public ref Il2CppMethodInfo** Methods => ref _->methods; + public ref Il2CppClass** ImplementedInterfaces => ref _->implementedInterfaces; + public ref Il2CppRuntimeInterfaceOffsetPair* InterfaceOffsets => ref _->interfaceOffsets; + public ref Il2CppClass** TypeHierarchy => ref _->typeHierarchy; + public bool ValueType + { + get => ByValArg.ValueType && ThisArg.ValueType; + set { } + } + public bool Initialized + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_105_0.Bitfield0.BIT_initialized); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_105_0.Bitfield0.BIT_initialized, value); + } + public bool EnumType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_105_0.Bitfield0.BIT_enumtype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_105_0.Bitfield0.BIT_enumtype, value); + } + public bool IsGeneric + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_105_0.Bitfield0.BIT_is_generic); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_105_0.Bitfield0.BIT_is_generic, value); + } + public bool HasReferences + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_105_0.Bitfield0.BIT_has_references); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_105_0.Bitfield0.BIT_has_references, value); + } + public bool SizeInited + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_105_0.Bitfield1.BIT_size_inited); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_105_0.Bitfield1.BIT_size_inited, value); + } + public bool HasFinalize + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_105_0.Bitfield1.BIT_has_finalize); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_105_0.Bitfield1.BIT_has_finalize, value); + } + public bool IsVtableInitialized + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_105_0.Bitfield1.BIT_is_vtable_initialized); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_105_0.Bitfield1.BIT_is_vtable_initialized, value); + } + public bool InitializedAndNoError + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_105_0.Bitfield0.BIT_initialized_and_no_error); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_105_0.Bitfield0.BIT_initialized_and_no_error, value); + } + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_16_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_16_0.cs index 30614dcf..0f839d5a 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_16_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_16_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class @@ -9,17 +8,17 @@ public unsafe class NativeClassStructHandler_16_0 : INativeClassStructHandler public int Size() => sizeof(Il2CppClass_16_0); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_16_0* _ = (Il2CppClass_16_0*)ptr; *_ = default; Marshal.FreeHGlobal(ptr); - throw new NotSupportedException("The native struct 'Il2CppClass_16_0' has a vtable field which is not currently supported!"); + throw new System.NotSupportedException("The native struct 'Il2CppClass_16_0' has a vtable field which is not currently supported!"); return new NativeStructWrapper(ptr); } public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_16_0 { @@ -92,7 +91,6 @@ internal enum Bitfield0 : byte BIT_has_finalize = 7, has_finalize = (1 << BIT_has_finalize), } - internal enum Bitfield1 : byte { BIT_has_cctor = 0, @@ -100,18 +98,16 @@ internal enum Bitfield1 : byte BIT_is_blittable = 1, is_blittable = (1 << BIT_is_blittable), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_16_0._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_16_0._bitfield1)).ToInt32(); private Il2CppClass* _klassDummy; - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_16_0* _ => (Il2CppClass_16_0*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_16_0)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_16_0)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap(_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap(_->this_arg); @@ -125,8 +121,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -184,7 +180,5 @@ public bool InitializedAndNoError set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_19_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_19_0.cs index 93594b5e..adc5b6f2 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_19_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_19_0.cs @@ -1,25 +1,24 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("5.3.2")] + [ApplicableToUnityVersionsSince("5.3.1p3")] public unsafe class NativeClassStructHandler_19_0 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_19_0); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_19_0* _ = (Il2CppClass_19_0*)ptr; *_ = default; Marshal.FreeHGlobal(ptr); - throw new NotSupportedException("The native struct 'Il2CppClass_19_0' has a vtable field which is not currently supported!"); + throw new System.NotSupportedException("The native struct 'Il2CppClass_19_0' has a vtable field which is not currently supported!"); return new NativeStructWrapper(ptr); } public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_19_0 { @@ -93,7 +92,6 @@ internal enum Bitfield0 : byte BIT_has_finalize = 7, has_finalize = (1 << BIT_has_finalize), } - internal enum Bitfield1 : byte { BIT_has_cctor = 0, @@ -101,18 +99,16 @@ internal enum Bitfield1 : byte BIT_is_blittable = 1, is_blittable = (1 << BIT_is_blittable), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_19_0._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_19_0._bitfield1)).ToInt32(); private Il2CppClass* _klassDummy; - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_19_0* _ => (Il2CppClass_19_0*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_19_0)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_19_0)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap(_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap(_->this_arg); @@ -126,8 +122,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -185,7 +181,5 @@ public bool InitializedAndNoError set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_20_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_20_0.cs index dfffeddf..fa161ea5 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_20_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_20_0.cs @@ -1,25 +1,24 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("5.3.3")] + [ApplicableToUnityVersionsSince("5.3.2p2")] public unsafe class NativeClassStructHandler_20_0 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_20_0); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_20_0* _ = (Il2CppClass_20_0*)ptr; *_ = default; Marshal.FreeHGlobal(ptr); - throw new NotSupportedException("The native struct 'Il2CppClass_20_0' has a vtable field which is not currently supported!"); + throw new System.NotSupportedException("The native struct 'Il2CppClass_20_0' has a vtable field which is not currently supported!"); return new NativeStructWrapper(ptr); } public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_20_0 { @@ -93,7 +92,6 @@ internal enum Bitfield0 : byte BIT_has_finalize = 7, has_finalize = (1 << BIT_has_finalize), } - internal enum Bitfield1 : byte { BIT_has_cctor = 0, @@ -103,18 +101,16 @@ internal enum Bitfield1 : byte BIT_is_import = 2, is_import = (1 << BIT_is_import), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_20_0._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_20_0._bitfield1)).ToInt32(); private Il2CppClass* _klassDummy; - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_20_0* _ => (Il2CppClass_20_0*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_20_0)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_20_0)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap(_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap(_->this_arg); @@ -128,8 +124,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -187,7 +183,5 @@ public bool InitializedAndNoError set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_21_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_21_0.cs index 4beed49f..6d38f1f4 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_21_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_21_0.cs @@ -1,25 +1,24 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("5.3.6")] + [ApplicableToUnityVersionsSince("5.3.5p1")] public unsafe class NativeClassStructHandler_21_0 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_21_0); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_21_0* _ = (Il2CppClass_21_0*)ptr; *_ = default; Marshal.FreeHGlobal(ptr); - throw new NotSupportedException("The native struct 'Il2CppClass_21_0' has a vtable field which is not currently supported!"); + throw new System.NotSupportedException("The native struct 'Il2CppClass_21_0' has a vtable field which is not currently supported!"); return new NativeStructWrapper(ptr); } public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_21_0 { @@ -41,7 +40,7 @@ internal unsafe struct Il2CppClass_21_0 public Il2CppMethodInfo** methods; public Il2CppClass** nestedTypes; public Il2CppClass** implementedInterfaces; - public VirtualInvokeData* vtable; + public Il2CppMethodInfo** vtable; public Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; public void* static_fields; public void* rgctx_data; @@ -93,7 +92,6 @@ internal enum Bitfield0 : byte BIT_has_finalize = 7, has_finalize = (1 << BIT_has_finalize), } - internal enum Bitfield1 : byte { BIT_has_cctor = 0, @@ -103,18 +101,16 @@ internal enum Bitfield1 : byte BIT_is_import_or_windows_runtime = 2, is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_21_0._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_21_0._bitfield1)).ToInt32(); private Il2CppClass* _klassDummy; - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_21_0* _ => (Il2CppClass_21_0*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_21_0)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_21_0)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap(_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap(_->this_arg); @@ -128,8 +124,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -187,7 +183,5 @@ public bool InitializedAndNoError set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_21_1.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_21_1.cs index 5bbd149c..c12b64c4 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_21_1.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_21_1.cs @@ -1,25 +1,24 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("5.4.4")] + [ApplicableToUnityVersionsSince("5.3.5p3")] public unsafe class NativeClassStructHandler_21_1 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_21_1); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_21_1* _ = (Il2CppClass_21_1*)ptr; *_ = default; Marshal.FreeHGlobal(ptr); - throw new NotSupportedException("The native struct 'Il2CppClass_21_1' has a vtable field which is not currently supported!"); + throw new System.NotSupportedException("The native struct 'Il2CppClass_21_1' has a vtable field which is not currently supported!"); return new NativeStructWrapper(ptr); } public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_21_1 { @@ -69,7 +68,6 @@ internal unsafe struct Il2CppClass_21_1 public ushort interfaces_count; public ushort interface_offsets_count; public byte typeHierarchyDepth; - public byte genericRecursionDepth; public byte rank; public byte minimumAlignment; public byte packingSize; @@ -94,7 +92,6 @@ internal enum Bitfield0 : byte BIT_has_finalize = 7, has_finalize = (1 << BIT_has_finalize), } - internal enum Bitfield1 : byte { BIT_has_cctor = 0, @@ -104,18 +101,16 @@ internal enum Bitfield1 : byte BIT_is_import_or_windows_runtime = 2, is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_21_1._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_21_1._bitfield1)).ToInt32(); private Il2CppClass* _klassDummy; - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_21_1* _ => (Il2CppClass_21_1*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_21_1)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_21_1)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap(_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap(_->this_arg); @@ -129,8 +124,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -188,7 +183,5 @@ public bool InitializedAndNoError set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_21_2.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_21_2.cs new file mode 100644 index 00000000..0c9ef6f8 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_21_2.cs @@ -0,0 +1,188 @@ +using System.Runtime.InteropServices; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class +{ + [ApplicableToUnityVersionsSince("5.4.3p4")] + public unsafe class NativeClassStructHandler_21_2 : INativeClassStructHandler + { + public int Size() => sizeof(Il2CppClass_21_2); + public INativeClassStruct CreateNewStruct(int vTableSlots) + { + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + Il2CppClass_21_2* _ = (Il2CppClass_21_2*)ptr; + *_ = default; + Marshal.FreeHGlobal(ptr); + throw new System.NotSupportedException("The native struct 'Il2CppClass_21_2' has a vtable field which is not currently supported!"); + return new NativeStructWrapper(ptr); + } + public INativeClassStruct Wrap(Il2CppClass* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppClass_21_2 + { + public Il2CppImage* image; + public void* gc_desc; + public byte* name; + public byte* namespaze; + public Il2CppTypeStruct* byval_arg; + public Il2CppTypeStruct* this_arg; + public Il2CppClass* element_class; + public Il2CppClass* castClass; + public Il2CppClass* declaringType; + public Il2CppClass* parent; + public void* generic_class; + public void* typeDefinition; + public Il2CppFieldInfo* fields; + public Il2CppEventInfo* events; + public Il2CppPropertyInfo* properties; + public Il2CppMethodInfo** methods; + public Il2CppClass** nestedTypes; + public Il2CppClass** implementedInterfaces; + public VirtualInvokeData* vtable; + public Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + public void* static_fields; + public void* rgctx_data; + public Il2CppClass** typeHierarchy; + public uint cctor_started; + public uint cctor_finished; + public ulong cctor_thread; + public int genericContainerIndex; + public int customAttributeIndex; + public uint instance_size; + public uint actualSize; + public uint element_size; + public int native_size; + public uint static_fields_size; + public uint thread_static_fields_size; + public int thread_static_fields_offset; + public uint flags; + public uint token; + public ushort method_count; + public ushort property_count; + public ushort field_count; + public ushort event_count; + public ushort nested_type_count; + public ushort vtable_count; + public ushort interfaces_count; + public ushort interface_offsets_count; + public byte typeHierarchyDepth; + public byte genericRecursionDepth; + public byte rank; + public byte minimumAlignment; + public byte packingSize; + public Bitfield0 _bitfield0; + public Bitfield1 _bitfield1; + internal enum Bitfield0 : byte + { + BIT_valuetype = 0, + valuetype = (1 << BIT_valuetype), + BIT_initialized = 1, + initialized = (1 << BIT_initialized), + BIT_enumtype = 2, + enumtype = (1 << BIT_enumtype), + BIT_is_generic = 3, + is_generic = (1 << BIT_is_generic), + BIT_has_references = 4, + has_references = (1 << BIT_has_references), + BIT_init_pending = 5, + init_pending = (1 << BIT_init_pending), + BIT_size_inited = 6, + size_inited = (1 << BIT_size_inited), + BIT_has_finalize = 7, + has_finalize = (1 << BIT_has_finalize), + } + internal enum Bitfield1 : byte + { + BIT_has_cctor = 0, + has_cctor = (1 << BIT_has_cctor), + BIT_is_blittable = 1, + is_blittable = (1 << BIT_is_blittable), + BIT_is_import_or_windows_runtime = 2, + is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), + } + } + internal class NativeStructWrapper : INativeClassStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_21_2._bitfield0)).ToInt32(); + private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_21_2._bitfield1)).ToInt32(); + private Il2CppClass* _klassDummy; + public nint Pointer { get; } + private Il2CppClass_21_2* _ => (Il2CppClass_21_2*)Pointer; + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_21_2)); + public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; + public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap(_->byval_arg); + public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap(_->this_arg); + public ref uint InstanceSize => ref _->instance_size; + public ref ushort VtableCount => ref _->vtable_count; + public ref ushort InterfaceCount => ref _->interfaces_count; + public ref ushort InterfaceOffsetsCount => ref _->interface_offsets_count; + public ref byte TypeHierarchyDepth => ref _->typeHierarchyDepth; + public ref int NativeSize => ref _->native_size; + public ref uint ActualSize => ref _->actualSize; + public ref ushort MethodCount => ref _->method_count; + public ref ushort FieldCount => ref _->field_count; + public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; + public ref Il2CppImage* Image => ref _->image; + public ref Il2CppClass* Parent => ref _->parent; + public ref Il2CppClass* ElementClass => ref _->element_class; + public ref Il2CppClass* CastClass => ref _->castClass; + public ref Il2CppClass* DeclaringType => ref _->declaringType; + public ref Il2CppClass* Class => ref _klassDummy; + public ref Il2CppFieldInfo* Fields => ref _->fields; + public ref Il2CppMethodInfo** Methods => ref _->methods; + public ref Il2CppClass** ImplementedInterfaces => ref _->implementedInterfaces; + public ref Il2CppRuntimeInterfaceOffsetPair* InterfaceOffsets => ref _->interfaceOffsets; + public ref Il2CppClass** TypeHierarchy => ref _->typeHierarchy; + public bool ValueType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_valuetype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_valuetype, value); + } + public bool Initialized + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_initialized); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_initialized, value); + } + public bool EnumType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_enumtype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_enumtype, value); + } + public bool IsGeneric + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_is_generic); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_is_generic, value); + } + public bool HasReferences + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_has_references); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_has_references, value); + } + public bool SizeInited + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_size_inited); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_size_inited, value); + } + public bool HasFinalize + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_has_finalize); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_21_2.Bitfield0.BIT_has_finalize, value); + } + public bool IsVtableInitialized + { + get => false; + set { } + } + public bool InitializedAndNoError + { + get => true; + set { } + } + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_22_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_22_0.cs index c3457d0d..d7ad3c86 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_22_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_22_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class @@ -9,7 +8,7 @@ public unsafe class NativeClassStructHandler_22_0 : INativeClassStructHandler public int Size() => sizeof(Il2CppClass_22_0); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_22_0* _ = (Il2CppClass_22_0*)ptr; *_ = default; _->byval_arg = UnityVersionHandler.NewType().TypePointer; @@ -19,7 +18,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_22_0 { @@ -92,7 +91,6 @@ internal enum Bitfield0 : byte BIT_has_finalize = 7, has_finalize = (1 << BIT_has_finalize), } - internal enum Bitfield1 : byte { BIT_has_cctor = 0, @@ -104,18 +102,16 @@ internal enum Bitfield1 : byte BIT_is_vtable_initialized = 3, is_vtable_initialized = (1 << BIT_is_vtable_initialized), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_22_0._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_22_0._bitfield1)).ToInt32(); private Il2CppClass* _klassDummy; - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_22_0* _ => (Il2CppClass_22_0*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_22_0)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_22_0)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap(_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap(_->this_arg); @@ -129,8 +125,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -188,7 +184,5 @@ public bool InitializedAndNoError set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_22_1.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_22_1.cs index 2267aca9..96c93be1 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_22_1.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_22_1.cs @@ -1,15 +1,14 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("5.5.1")] + [ApplicableToUnityVersionsSince("5.5.0p2")] public unsafe class NativeClassStructHandler_22_1 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_22_1); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_22_1* _ = (Il2CppClass_22_1*)ptr; *_ = default; _->byval_arg = UnityVersionHandler.NewType().TypePointer; @@ -19,7 +18,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_22_1 { @@ -93,7 +92,6 @@ internal enum Bitfield0 : byte BIT_has_finalize = 7, has_finalize = (1 << BIT_has_finalize), } - internal enum Bitfield1 : byte { BIT_has_cctor = 0, @@ -105,18 +103,16 @@ internal enum Bitfield1 : byte BIT_is_vtable_initialized = 3, is_vtable_initialized = (1 << BIT_is_vtable_initialized), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_22_1._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_22_1._bitfield1)).ToInt32(); private Il2CppClass* _klassDummy; - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_22_1* _ => (Il2CppClass_22_1*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_22_1)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_22_1)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap(_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap(_->this_arg); @@ -130,8 +126,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -189,7 +185,5 @@ public bool InitializedAndNoError set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_23_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_23_0.cs index eed8c233..a54e3931 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_23_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_23_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class @@ -9,7 +8,7 @@ public unsafe class NativeClassStructHandler_23_0 : INativeClassStructHandler public int Size() => sizeof(Il2CppClass_23_0); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_23_0* _ = (Il2CppClass_23_0*)ptr; *_ = default; _->byval_arg = UnityVersionHandler.NewType().TypePointer; @@ -19,7 +18,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_23_0 { @@ -69,7 +68,6 @@ internal unsafe struct Il2CppClass_23_0 public ushort interfaces_count; public ushort interface_offsets_count; public byte typeHierarchyDepth; - public byte genericRecursionDepth; public byte rank; public byte minimumAlignment; public byte packingSize; @@ -94,7 +92,6 @@ internal enum Bitfield0 : byte BIT_has_finalize = 7, has_finalize = (1 << BIT_has_finalize), } - internal enum Bitfield1 : byte { BIT_has_cctor = 0, @@ -106,18 +103,16 @@ internal enum Bitfield1 : byte BIT_is_vtable_initialized = 3, is_vtable_initialized = (1 << BIT_is_vtable_initialized), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_23_0._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_23_0._bitfield1)).ToInt32(); private Il2CppClass* _klassDummy; - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_23_0* _ => (Il2CppClass_23_0*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_23_0)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_23_0)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap(_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap(_->this_arg); @@ -131,8 +126,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -190,7 +185,5 @@ public bool InitializedAndNoError set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_23_1.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_23_1.cs new file mode 100644 index 00000000..573c111a --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_23_1.cs @@ -0,0 +1,190 @@ +using System.Runtime.InteropServices; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class +{ + [ApplicableToUnityVersionsSince("5.6.0b3")] + public unsafe class NativeClassStructHandler_23_1 : INativeClassStructHandler + { + public int Size() => sizeof(Il2CppClass_23_1); + public INativeClassStruct CreateNewStruct(int vTableSlots) + { + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + Il2CppClass_23_1* _ = (Il2CppClass_23_1*)ptr; + *_ = default; + _->byval_arg = UnityVersionHandler.NewType().TypePointer; + _->this_arg = UnityVersionHandler.NewType().TypePointer; + return new NativeStructWrapper(ptr); + } + public INativeClassStruct Wrap(Il2CppClass* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppClass_23_1 + { + public Il2CppImage* image; + public void* gc_desc; + public byte* name; + public byte* namespaze; + public Il2CppTypeStruct* byval_arg; + public Il2CppTypeStruct* this_arg; + public Il2CppClass* element_class; + public Il2CppClass* castClass; + public Il2CppClass* declaringType; + public Il2CppClass* parent; + public void* generic_class; + public void* typeDefinition; + public void* interopData; + public Il2CppFieldInfo* fields; + public Il2CppEventInfo* events; + public Il2CppPropertyInfo* properties; + public Il2CppMethodInfo** methods; + public Il2CppClass** nestedTypes; + public Il2CppClass** implementedInterfaces; + public Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + public void* static_fields; + public void* rgctx_data; + public Il2CppClass** typeHierarchy; + public uint cctor_started; + public uint cctor_finished; + public ulong cctor_thread; + public int genericContainerIndex; + public int customAttributeIndex; + public uint instance_size; + public uint actualSize; + public uint element_size; + public int native_size; + public uint static_fields_size; + public uint thread_static_fields_size; + public int thread_static_fields_offset; + public uint flags; + public uint token; + public ushort method_count; + public ushort property_count; + public ushort field_count; + public ushort event_count; + public ushort nested_type_count; + public ushort vtable_count; + public ushort interfaces_count; + public ushort interface_offsets_count; + public byte typeHierarchyDepth; + public byte genericRecursionDepth; + public byte rank; + public byte minimumAlignment; + public byte packingSize; + public Bitfield0 _bitfield0; + public Bitfield1 _bitfield1; + internal enum Bitfield0 : byte + { + BIT_valuetype = 0, + valuetype = (1 << BIT_valuetype), + BIT_initialized = 1, + initialized = (1 << BIT_initialized), + BIT_enumtype = 2, + enumtype = (1 << BIT_enumtype), + BIT_is_generic = 3, + is_generic = (1 << BIT_is_generic), + BIT_has_references = 4, + has_references = (1 << BIT_has_references), + BIT_init_pending = 5, + init_pending = (1 << BIT_init_pending), + BIT_size_inited = 6, + size_inited = (1 << BIT_size_inited), + BIT_has_finalize = 7, + has_finalize = (1 << BIT_has_finalize), + } + internal enum Bitfield1 : byte + { + BIT_has_cctor = 0, + has_cctor = (1 << BIT_has_cctor), + BIT_is_blittable = 1, + is_blittable = (1 << BIT_is_blittable), + BIT_is_import_or_windows_runtime = 2, + is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), + BIT_is_vtable_initialized = 3, + is_vtable_initialized = (1 << BIT_is_vtable_initialized), + } + } + internal class NativeStructWrapper : INativeClassStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_23_1._bitfield0)).ToInt32(); + private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_23_1._bitfield1)).ToInt32(); + private Il2CppClass* _klassDummy; + public nint Pointer { get; } + private Il2CppClass_23_1* _ => (Il2CppClass_23_1*)Pointer; + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_23_1)); + public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; + public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap(_->byval_arg); + public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap(_->this_arg); + public ref uint InstanceSize => ref _->instance_size; + public ref ushort VtableCount => ref _->vtable_count; + public ref ushort InterfaceCount => ref _->interfaces_count; + public ref ushort InterfaceOffsetsCount => ref _->interface_offsets_count; + public ref byte TypeHierarchyDepth => ref _->typeHierarchyDepth; + public ref int NativeSize => ref _->native_size; + public ref uint ActualSize => ref _->actualSize; + public ref ushort MethodCount => ref _->method_count; + public ref ushort FieldCount => ref _->field_count; + public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; + public ref Il2CppImage* Image => ref _->image; + public ref Il2CppClass* Parent => ref _->parent; + public ref Il2CppClass* ElementClass => ref _->element_class; + public ref Il2CppClass* CastClass => ref _->castClass; + public ref Il2CppClass* DeclaringType => ref _->declaringType; + public ref Il2CppClass* Class => ref _klassDummy; + public ref Il2CppFieldInfo* Fields => ref _->fields; + public ref Il2CppMethodInfo** Methods => ref _->methods; + public ref Il2CppClass** ImplementedInterfaces => ref _->implementedInterfaces; + public ref Il2CppRuntimeInterfaceOffsetPair* InterfaceOffsets => ref _->interfaceOffsets; + public ref Il2CppClass** TypeHierarchy => ref _->typeHierarchy; + public bool ValueType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_valuetype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_valuetype, value); + } + public bool Initialized + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_initialized); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_initialized, value); + } + public bool EnumType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_enumtype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_enumtype, value); + } + public bool IsGeneric + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_is_generic); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_is_generic, value); + } + public bool HasReferences + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_has_references); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_has_references, value); + } + public bool SizeInited + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_size_inited); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_size_inited, value); + } + public bool HasFinalize + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_has_finalize); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_23_1.Bitfield0.BIT_has_finalize, value); + } + public bool IsVtableInitialized + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_23_1.Bitfield1.BIT_is_vtable_initialized); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_23_1.Bitfield1.BIT_is_vtable_initialized, value); + } + public bool InitializedAndNoError + { + get => true; + set { } + } + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_0.cs index 6f53a551..b1f4e29c 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_0.cs @@ -1,15 +1,14 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("2018.1.0")] + [ApplicableToUnityVersionsSince("2018.1.0b5")] public unsafe class NativeClassStructHandler_24_0 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_24_0); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_24_0* _ = (Il2CppClass_24_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_24_0 { @@ -93,7 +92,6 @@ internal enum Bitfield0 : byte BIT_has_finalize = 7, has_finalize = (1 << BIT_has_finalize), } - internal enum Bitfield1 : byte { BIT_has_cctor = 0, @@ -105,17 +103,15 @@ internal enum Bitfield1 : byte BIT_is_vtable_initialized = 3, is_vtable_initialized = (1 << BIT_is_vtable_initialized), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_24_0._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_24_0._bitfield1)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_24_0* _ => (Il2CppClass_24_0*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_24_0)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_24_0)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); @@ -129,8 +125,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -188,7 +184,5 @@ public bool InitializedAndNoError set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_1.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_1.cs index e2888fac..5a2ed557 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_1.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_1.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class @@ -9,7 +8,7 @@ public unsafe class NativeClassStructHandler_24_1 : INativeClassStructHandler public int Size() => sizeof(Il2CppClass_24_1); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_24_1* _ = (Il2CppClass_24_1*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_24_1 { @@ -94,7 +93,6 @@ internal enum Bitfield0 : byte BIT_has_finalize = 7, has_finalize = (1 << BIT_has_finalize), } - internal enum Bitfield1 : byte { BIT_has_cctor = 0, @@ -108,17 +106,15 @@ internal enum Bitfield1 : byte BIT_has_initialization_error = 4, has_initialization_error = (1 << BIT_has_initialization_error), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_24_1._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_24_1._bitfield1)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_24_1* _ => (Il2CppClass_24_1*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_24_1)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_24_1)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); @@ -132,8 +128,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -191,7 +187,5 @@ public bool InitializedAndNoError set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_2.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_2.cs index ddaaba9a..700d2a0d 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_2.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_2.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class @@ -9,7 +8,7 @@ public unsafe class NativeClassStructHandler_24_2 : INativeClassStructHandler public int Size() => sizeof(Il2CppClass_24_2); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_24_2* _ = (Il2CppClass_24_2*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_24_2 { @@ -76,50 +75,45 @@ internal unsafe struct Il2CppClass_24_2 public Bitfield1 _bitfield1; internal enum Bitfield0 : byte { - BIT_initialized_and_no_error = 0, - initialized_and_no_error = (1 << BIT_initialized_and_no_error), - BIT_valuetype = 1, + BIT_valuetype = 0, valuetype = (1 << BIT_valuetype), - BIT_initialized = 2, + BIT_initialized = 1, initialized = (1 << BIT_initialized), - BIT_enumtype = 3, + BIT_enumtype = 2, enumtype = (1 << BIT_enumtype), - BIT_is_generic = 4, + BIT_is_generic = 3, is_generic = (1 << BIT_is_generic), - BIT_has_references = 5, + BIT_has_references = 4, has_references = (1 << BIT_has_references), - BIT_init_pending = 6, + BIT_init_pending = 5, init_pending = (1 << BIT_init_pending), - BIT_size_inited = 7, + BIT_size_inited = 6, size_inited = (1 << BIT_size_inited), + BIT_has_finalize = 7, + has_finalize = (1 << BIT_has_finalize), } - internal enum Bitfield1 : byte { - BIT_has_finalize = 0, - has_finalize = (1 << BIT_has_finalize), - BIT_has_cctor = 1, + BIT_has_cctor = 0, has_cctor = (1 << BIT_has_cctor), - BIT_is_blittable = 2, + BIT_is_blittable = 1, is_blittable = (1 << BIT_is_blittable), - BIT_is_import_or_windows_runtime = 3, + BIT_is_import_or_windows_runtime = 2, is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), - BIT_is_vtable_initialized = 4, + BIT_is_vtable_initialized = 3, is_vtable_initialized = (1 << BIT_is_vtable_initialized), - BIT_has_initialization_error = 5, + BIT_has_initialization_error = 4, has_initialization_error = (1 << BIT_has_initialization_error), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_24_2._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_24_2._bitfield1)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_24_2* _ => (Il2CppClass_24_2*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_24_2)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_24_2)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); @@ -133,8 +127,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -178,8 +172,8 @@ public bool SizeInited } public bool HasFinalize { - get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_24_2.Bitfield1.BIT_has_finalize); - set => this.SetBit(_bitfield1offset, (int)Il2CppClass_24_2.Bitfield1.BIT_has_finalize, value); + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_2.Bitfield0.BIT_has_finalize); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_2.Bitfield0.BIT_has_finalize, value); } public bool IsVtableInitialized { @@ -188,11 +182,9 @@ public bool IsVtableInitialized } public bool InitializedAndNoError { - get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_2.Bitfield0.BIT_initialized_and_no_error); - set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_2.Bitfield0.BIT_initialized_and_no_error, value); + get => true; + set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_3.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_3.cs index f9388c3c..b51733b9 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_3.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_3.cs @@ -1,15 +1,14 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("2018.3.8")] + [ApplicableToUnityVersionsSince("2018.3.0b11")] public unsafe class NativeClassStructHandler_24_3 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_24_3); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_24_3* _ = (Il2CppClass_24_3*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_24_3 { @@ -71,7 +70,6 @@ internal unsafe struct Il2CppClass_24_3 public byte genericRecursionDepth; public byte rank; public byte minimumAlignment; - public byte naturalAligment; public byte packingSize; public Bitfield0 _bitfield0; public Bitfield1 _bitfield1; @@ -94,7 +92,6 @@ internal enum Bitfield0 : byte BIT_size_inited = 7, size_inited = (1 << BIT_size_inited), } - internal enum Bitfield1 : byte { BIT_has_finalize = 0, @@ -110,17 +107,15 @@ internal enum Bitfield1 : byte BIT_has_initialization_error = 5, has_initialization_error = (1 << BIT_has_initialization_error), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_24_3._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_24_3._bitfield1)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_24_3* _ => (Il2CppClass_24_3*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_24_3)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_24_3)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); @@ -134,8 +129,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -193,7 +188,5 @@ public bool InitializedAndNoError set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_3.Bitfield0.BIT_initialized_and_no_error, value); } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_4.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_4.cs index a5b4e6f3..baf485f0 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_4.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_4.cs @@ -1,15 +1,14 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("2019.1.0")] + [ApplicableToUnityVersionsSince("2018.3.8")] public unsafe class NativeClassStructHandler_24_4 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_24_4); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_24_4* _ = (Il2CppClass_24_4*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_24_4 { @@ -45,11 +44,10 @@ internal unsafe struct Il2CppClass_24_4 public void* static_fields; public void* rgctx_data; public Il2CppClass** typeHierarchy; - public void* unity_user_data; public uint initializationExceptionGCHandle; public uint cctor_started; public uint cctor_finished; - public IntPtr cctor_thread; + public ulong cctor_thread; public int genericContainerIndex; public uint instance_size; public uint actualSize; @@ -95,7 +93,6 @@ internal enum Bitfield0 : byte BIT_size_inited = 7, size_inited = (1 << BIT_size_inited), } - internal enum Bitfield1 : byte { BIT_has_finalize = 0, @@ -111,17 +108,15 @@ internal enum Bitfield1 : byte BIT_has_initialization_error = 5, has_initialization_error = (1 << BIT_has_initialization_error), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_24_4._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_24_4._bitfield1)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_24_4* _ => (Il2CppClass_24_4*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_24_4)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_24_4)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); @@ -135,8 +130,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -194,7 +189,5 @@ public bool InitializedAndNoError set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_4.Bitfield0.BIT_initialized_and_no_error, value); } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_5.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_5.cs new file mode 100644 index 00000000..d00ab4d4 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_5.cs @@ -0,0 +1,193 @@ +using System.Runtime.InteropServices; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class +{ + [ApplicableToUnityVersionsSince("2019.1.0a11")] + public unsafe class NativeClassStructHandler_24_5 : INativeClassStructHandler + { + public int Size() => sizeof(Il2CppClass_24_5); + public INativeClassStruct CreateNewStruct(int vTableSlots) + { + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + Il2CppClass_24_5* _ = (Il2CppClass_24_5*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeClassStruct Wrap(Il2CppClass* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppClass_24_5 + { + public Il2CppImage* image; + public void* gc_desc; + public byte* name; + public byte* namespaze; + public NativeTypeStructHandler_16_0.Il2CppType_16_0 byval_arg; + public NativeTypeStructHandler_16_0.Il2CppType_16_0 this_arg; + public Il2CppClass* element_class; + public Il2CppClass* castClass; + public Il2CppClass* declaringType; + public Il2CppClass* parent; + public void* generic_class; + public void* typeDefinition; + public void* interopData; + public Il2CppClass* klass; + public Il2CppFieldInfo* fields; + public Il2CppEventInfo* events; + public Il2CppPropertyInfo* properties; + public Il2CppMethodInfo** methods; + public Il2CppClass** nestedTypes; + public Il2CppClass** implementedInterfaces; + public Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + public void* static_fields; + public void* rgctx_data; + public Il2CppClass** typeHierarchy; + public void* unity_user_data; + public uint initializationExceptionGCHandle; + public uint cctor_started; + public uint cctor_finished; + public ulong cctor_thread; + public int genericContainerIndex; + public uint instance_size; + public uint actualSize; + public uint element_size; + public int native_size; + public uint static_fields_size; + public uint thread_static_fields_size; + public int thread_static_fields_offset; + public uint flags; + public uint token; + public ushort method_count; + public ushort property_count; + public ushort field_count; + public ushort event_count; + public ushort nested_type_count; + public ushort vtable_count; + public ushort interfaces_count; + public ushort interface_offsets_count; + public byte typeHierarchyDepth; + public byte genericRecursionDepth; + public byte rank; + public byte minimumAlignment; + public byte packingSize; + public Bitfield0 _bitfield0; + public Bitfield1 _bitfield1; + internal enum Bitfield0 : byte + { + BIT_initialized_and_no_error = 0, + initialized_and_no_error = (1 << BIT_initialized_and_no_error), + BIT_valuetype = 1, + valuetype = (1 << BIT_valuetype), + BIT_initialized = 2, + initialized = (1 << BIT_initialized), + BIT_enumtype = 3, + enumtype = (1 << BIT_enumtype), + BIT_is_generic = 4, + is_generic = (1 << BIT_is_generic), + BIT_has_references = 5, + has_references = (1 << BIT_has_references), + BIT_init_pending = 6, + init_pending = (1 << BIT_init_pending), + BIT_size_inited = 7, + size_inited = (1 << BIT_size_inited), + } + internal enum Bitfield1 : byte + { + BIT_has_finalize = 0, + has_finalize = (1 << BIT_has_finalize), + BIT_has_cctor = 1, + has_cctor = (1 << BIT_has_cctor), + BIT_is_blittable = 2, + is_blittable = (1 << BIT_is_blittable), + BIT_is_import_or_windows_runtime = 3, + is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), + BIT_is_vtable_initialized = 4, + is_vtable_initialized = (1 << BIT_is_vtable_initialized), + BIT_has_initialization_error = 5, + has_initialization_error = (1 << BIT_has_initialization_error), + } + } + internal class NativeStructWrapper : INativeClassStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_24_5._bitfield0)).ToInt32(); + private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_24_5._bitfield1)).ToInt32(); + public nint Pointer { get; } + private Il2CppClass_24_5* _ => (Il2CppClass_24_5*)Pointer; + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_24_5)); + public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; + public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); + public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); + public ref uint InstanceSize => ref _->instance_size; + public ref ushort VtableCount => ref _->vtable_count; + public ref ushort InterfaceCount => ref _->interfaces_count; + public ref ushort InterfaceOffsetsCount => ref _->interface_offsets_count; + public ref byte TypeHierarchyDepth => ref _->typeHierarchyDepth; + public ref int NativeSize => ref _->native_size; + public ref uint ActualSize => ref _->actualSize; + public ref ushort MethodCount => ref _->method_count; + public ref ushort FieldCount => ref _->field_count; + public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; + public ref Il2CppImage* Image => ref _->image; + public ref Il2CppClass* Parent => ref _->parent; + public ref Il2CppClass* ElementClass => ref _->element_class; + public ref Il2CppClass* CastClass => ref _->castClass; + public ref Il2CppClass* DeclaringType => ref _->declaringType; + public ref Il2CppClass* Class => ref _->klass; + public ref Il2CppFieldInfo* Fields => ref _->fields; + public ref Il2CppMethodInfo** Methods => ref _->methods; + public ref Il2CppClass** ImplementedInterfaces => ref _->implementedInterfaces; + public ref Il2CppRuntimeInterfaceOffsetPair* InterfaceOffsets => ref _->interfaceOffsets; + public ref Il2CppClass** TypeHierarchy => ref _->typeHierarchy; + public bool ValueType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_valuetype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_valuetype, value); + } + public bool Initialized + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_initialized); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_initialized, value); + } + public bool EnumType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_enumtype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_enumtype, value); + } + public bool IsGeneric + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_is_generic); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_is_generic, value); + } + public bool HasReferences + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_has_references); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_has_references, value); + } + public bool SizeInited + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_size_inited); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_size_inited, value); + } + public bool HasFinalize + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_24_5.Bitfield1.BIT_has_finalize); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_24_5.Bitfield1.BIT_has_finalize, value); + } + public bool IsVtableInitialized + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_24_5.Bitfield1.BIT_is_vtable_initialized); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_24_5.Bitfield1.BIT_is_vtable_initialized, value); + } + public bool InitializedAndNoError + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_initialized_and_no_error); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_5.Bitfield0.BIT_initialized_and_no_error, value); + } + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_6.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_6.cs new file mode 100644 index 00000000..3a4a45a6 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_6.cs @@ -0,0 +1,194 @@ +using System.Runtime.InteropServices; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class +{ + [ApplicableToUnityVersionsSince("2019.1.0b5")] + public unsafe class NativeClassStructHandler_24_6 : INativeClassStructHandler + { + public int Size() => sizeof(Il2CppClass_24_6); + public INativeClassStruct CreateNewStruct(int vTableSlots) + { + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + Il2CppClass_24_6* _ = (Il2CppClass_24_6*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeClassStruct Wrap(Il2CppClass* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppClass_24_6 + { + public Il2CppImage* image; + public void* gc_desc; + public byte* name; + public byte* namespaze; + public NativeTypeStructHandler_16_0.Il2CppType_16_0 byval_arg; + public NativeTypeStructHandler_16_0.Il2CppType_16_0 this_arg; + public Il2CppClass* element_class; + public Il2CppClass* castClass; + public Il2CppClass* declaringType; + public Il2CppClass* parent; + public void* generic_class; + public void* typeDefinition; + public void* interopData; + public Il2CppClass* klass; + public Il2CppFieldInfo* fields; + public Il2CppEventInfo* events; + public Il2CppPropertyInfo* properties; + public Il2CppMethodInfo** methods; + public Il2CppClass** nestedTypes; + public Il2CppClass** implementedInterfaces; + public Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + public void* static_fields; + public void* rgctx_data; + public Il2CppClass** typeHierarchy; + public void* unity_user_data; + public uint initializationExceptionGCHandle; + public uint cctor_started; + public uint cctor_finished; + public ulong cctor_thread; + public int genericContainerIndex; + public uint instance_size; + public uint actualSize; + public uint element_size; + public int native_size; + public uint static_fields_size; + public uint thread_static_fields_size; + public int thread_static_fields_offset; + public uint flags; + public uint token; + public ushort method_count; + public ushort property_count; + public ushort field_count; + public ushort event_count; + public ushort nested_type_count; + public ushort vtable_count; + public ushort interfaces_count; + public ushort interface_offsets_count; + public byte typeHierarchyDepth; + public byte genericRecursionDepth; + public byte rank; + public byte minimumAlignment; + public byte naturalAligment; + public byte packingSize; + public Bitfield0 _bitfield0; + public Bitfield1 _bitfield1; + internal enum Bitfield0 : byte + { + BIT_initialized_and_no_error = 0, + initialized_and_no_error = (1 << BIT_initialized_and_no_error), + BIT_valuetype = 1, + valuetype = (1 << BIT_valuetype), + BIT_initialized = 2, + initialized = (1 << BIT_initialized), + BIT_enumtype = 3, + enumtype = (1 << BIT_enumtype), + BIT_is_generic = 4, + is_generic = (1 << BIT_is_generic), + BIT_has_references = 5, + has_references = (1 << BIT_has_references), + BIT_init_pending = 6, + init_pending = (1 << BIT_init_pending), + BIT_size_inited = 7, + size_inited = (1 << BIT_size_inited), + } + internal enum Bitfield1 : byte + { + BIT_has_finalize = 0, + has_finalize = (1 << BIT_has_finalize), + BIT_has_cctor = 1, + has_cctor = (1 << BIT_has_cctor), + BIT_is_blittable = 2, + is_blittable = (1 << BIT_is_blittable), + BIT_is_import_or_windows_runtime = 3, + is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), + BIT_is_vtable_initialized = 4, + is_vtable_initialized = (1 << BIT_is_vtable_initialized), + BIT_has_initialization_error = 5, + has_initialization_error = (1 << BIT_has_initialization_error), + } + } + internal class NativeStructWrapper : INativeClassStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_24_6._bitfield0)).ToInt32(); + private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_24_6._bitfield1)).ToInt32(); + public nint Pointer { get; } + private Il2CppClass_24_6* _ => (Il2CppClass_24_6*)Pointer; + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_24_6)); + public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; + public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); + public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); + public ref uint InstanceSize => ref _->instance_size; + public ref ushort VtableCount => ref _->vtable_count; + public ref ushort InterfaceCount => ref _->interfaces_count; + public ref ushort InterfaceOffsetsCount => ref _->interface_offsets_count; + public ref byte TypeHierarchyDepth => ref _->typeHierarchyDepth; + public ref int NativeSize => ref _->native_size; + public ref uint ActualSize => ref _->actualSize; + public ref ushort MethodCount => ref _->method_count; + public ref ushort FieldCount => ref _->field_count; + public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; + public ref Il2CppImage* Image => ref _->image; + public ref Il2CppClass* Parent => ref _->parent; + public ref Il2CppClass* ElementClass => ref _->element_class; + public ref Il2CppClass* CastClass => ref _->castClass; + public ref Il2CppClass* DeclaringType => ref _->declaringType; + public ref Il2CppClass* Class => ref _->klass; + public ref Il2CppFieldInfo* Fields => ref _->fields; + public ref Il2CppMethodInfo** Methods => ref _->methods; + public ref Il2CppClass** ImplementedInterfaces => ref _->implementedInterfaces; + public ref Il2CppRuntimeInterfaceOffsetPair* InterfaceOffsets => ref _->interfaceOffsets; + public ref Il2CppClass** TypeHierarchy => ref _->typeHierarchy; + public bool ValueType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_valuetype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_valuetype, value); + } + public bool Initialized + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_initialized); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_initialized, value); + } + public bool EnumType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_enumtype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_enumtype, value); + } + public bool IsGeneric + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_is_generic); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_is_generic, value); + } + public bool HasReferences + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_has_references); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_has_references, value); + } + public bool SizeInited + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_size_inited); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_size_inited, value); + } + public bool HasFinalize + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_24_6.Bitfield1.BIT_has_finalize); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_24_6.Bitfield1.BIT_has_finalize, value); + } + public bool IsVtableInitialized + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_24_6.Bitfield1.BIT_is_vtable_initialized); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_24_6.Bitfield1.BIT_is_vtable_initialized, value); + } + public bool InitializedAndNoError + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_initialized_and_no_error); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_6.Bitfield0.BIT_initialized_and_no_error, value); + } + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_7.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_7.cs new file mode 100644 index 00000000..2b328b34 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_24_7.cs @@ -0,0 +1,194 @@ +using System.Runtime.InteropServices; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class +{ + [ApplicableToUnityVersionsSince("2020.2.0")] + public unsafe class NativeClassStructHandler_24_7 : INativeClassStructHandler + { + public int Size() => sizeof(Il2CppClass_24_7); + public INativeClassStruct CreateNewStruct(int vTableSlots) + { + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + Il2CppClass_24_7* _ = (Il2CppClass_24_7*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeClassStruct Wrap(Il2CppClass* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppClass_24_7 + { + public Il2CppImage* image; + public void* gc_desc; + public byte* name; + public byte* namespaze; + public NativeTypeStructHandler_16_0.Il2CppType_16_0 byval_arg; + public NativeTypeStructHandler_16_0.Il2CppType_16_0 this_arg; + public Il2CppClass* element_class; + public Il2CppClass* castClass; + public Il2CppClass* declaringType; + public Il2CppClass* parent; + public void* generic_class; + public Il2CppMetadataTypeHandle typeMetadataHandle; + public void* interopData; + public Il2CppClass* klass; + public Il2CppFieldInfo* fields; + public Il2CppEventInfo* events; + public Il2CppPropertyInfo* properties; + public Il2CppMethodInfo** methods; + public Il2CppClass** nestedTypes; + public Il2CppClass** implementedInterfaces; + public Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + public void* static_fields; + public void* rgctx_data; + public Il2CppClass** typeHierarchy; + public void* unity_user_data; + public uint initializationExceptionGCHandle; + public uint cctor_started; + public uint cctor_finished; + public ulong cctor_thread; + public Il2CppMetadataGenericContainerHandle genericContainerHandle; + public uint instance_size; + public uint actualSize; + public uint element_size; + public int native_size; + public uint static_fields_size; + public uint thread_static_fields_size; + public int thread_static_fields_offset; + public uint flags; + public uint token; + public ushort method_count; + public ushort property_count; + public ushort field_count; + public ushort event_count; + public ushort nested_type_count; + public ushort vtable_count; + public ushort interfaces_count; + public ushort interface_offsets_count; + public byte typeHierarchyDepth; + public byte genericRecursionDepth; + public byte rank; + public byte minimumAlignment; + public byte naturalAligment; + public byte packingSize; + public Bitfield0 _bitfield0; + public Bitfield1 _bitfield1; + internal enum Bitfield0 : byte + { + BIT_initialized_and_no_error = 0, + initialized_and_no_error = (1 << BIT_initialized_and_no_error), + BIT_valuetype = 1, + valuetype = (1 << BIT_valuetype), + BIT_initialized = 2, + initialized = (1 << BIT_initialized), + BIT_enumtype = 3, + enumtype = (1 << BIT_enumtype), + BIT_is_generic = 4, + is_generic = (1 << BIT_is_generic), + BIT_has_references = 5, + has_references = (1 << BIT_has_references), + BIT_init_pending = 6, + init_pending = (1 << BIT_init_pending), + BIT_size_inited = 7, + size_inited = (1 << BIT_size_inited), + } + internal enum Bitfield1 : byte + { + BIT_has_finalize = 0, + has_finalize = (1 << BIT_has_finalize), + BIT_has_cctor = 1, + has_cctor = (1 << BIT_has_cctor), + BIT_is_blittable = 2, + is_blittable = (1 << BIT_is_blittable), + BIT_is_import_or_windows_runtime = 3, + is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), + BIT_is_vtable_initialized = 4, + is_vtable_initialized = (1 << BIT_is_vtable_initialized), + BIT_has_initialization_error = 5, + has_initialization_error = (1 << BIT_has_initialization_error), + } + } + internal class NativeStructWrapper : INativeClassStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_24_7._bitfield0)).ToInt32(); + private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_24_7._bitfield1)).ToInt32(); + public nint Pointer { get; } + private Il2CppClass_24_7* _ => (Il2CppClass_24_7*)Pointer; + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_24_7)); + public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; + public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); + public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); + public ref uint InstanceSize => ref _->instance_size; + public ref ushort VtableCount => ref _->vtable_count; + public ref ushort InterfaceCount => ref _->interfaces_count; + public ref ushort InterfaceOffsetsCount => ref _->interface_offsets_count; + public ref byte TypeHierarchyDepth => ref _->typeHierarchyDepth; + public ref int NativeSize => ref _->native_size; + public ref uint ActualSize => ref _->actualSize; + public ref ushort MethodCount => ref _->method_count; + public ref ushort FieldCount => ref _->field_count; + public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; + public ref Il2CppImage* Image => ref _->image; + public ref Il2CppClass* Parent => ref _->parent; + public ref Il2CppClass* ElementClass => ref _->element_class; + public ref Il2CppClass* CastClass => ref _->castClass; + public ref Il2CppClass* DeclaringType => ref _->declaringType; + public ref Il2CppClass* Class => ref _->klass; + public ref Il2CppFieldInfo* Fields => ref _->fields; + public ref Il2CppMethodInfo** Methods => ref _->methods; + public ref Il2CppClass** ImplementedInterfaces => ref _->implementedInterfaces; + public ref Il2CppRuntimeInterfaceOffsetPair* InterfaceOffsets => ref _->interfaceOffsets; + public ref Il2CppClass** TypeHierarchy => ref _->typeHierarchy; + public bool ValueType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_valuetype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_valuetype, value); + } + public bool Initialized + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_initialized); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_initialized, value); + } + public bool EnumType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_enumtype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_enumtype, value); + } + public bool IsGeneric + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_is_generic); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_is_generic, value); + } + public bool HasReferences + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_has_references); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_has_references, value); + } + public bool SizeInited + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_size_inited); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_size_inited, value); + } + public bool HasFinalize + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_24_7.Bitfield1.BIT_has_finalize); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_24_7.Bitfield1.BIT_has_finalize, value); + } + public bool IsVtableInitialized + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_24_7.Bitfield1.BIT_is_vtable_initialized); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_24_7.Bitfield1.BIT_is_vtable_initialized, value); + } + public bool InitializedAndNoError + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_initialized_and_no_error); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_24_7.Bitfield0.BIT_initialized_and_no_error, value); + } + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_0.cs index fbb2793e..2407f200 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_0.cs @@ -1,15 +1,14 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("2020.2.0")] + [ApplicableToUnityVersionsSince("2020.3.20")] public unsafe class NativeClassStructHandler_27_0 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_27_0); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_27_0* _ = (Il2CppClass_27_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_27_0 { @@ -49,7 +48,7 @@ internal unsafe struct Il2CppClass_27_0 public uint initializationExceptionGCHandle; public uint cctor_started; public uint cctor_finished; - public IntPtr cctor_thread; + public ulong cctor_thread; public Il2CppMetadataGenericContainerHandle genericContainerHandle; public uint instance_size; public uint actualSize; @@ -92,36 +91,35 @@ internal enum Bitfield0 : byte has_references = (1 << BIT_has_references), BIT_init_pending = 6, init_pending = (1 << BIT_init_pending), - BIT_size_inited = 7, - size_inited = (1 << BIT_size_inited), + BIT_size_init_pending = 7, + size_init_pending = (1 << BIT_size_init_pending), } - internal enum Bitfield1 : byte { - BIT_has_finalize = 0, + BIT_size_inited = 0, + size_inited = (1 << BIT_size_inited), + BIT_has_finalize = 1, has_finalize = (1 << BIT_has_finalize), - BIT_has_cctor = 1, + BIT_has_cctor = 2, has_cctor = (1 << BIT_has_cctor), - BIT_is_blittable = 2, + BIT_is_blittable = 3, is_blittable = (1 << BIT_is_blittable), - BIT_is_import_or_windows_runtime = 3, + BIT_is_import_or_windows_runtime = 4, is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), - BIT_is_vtable_initialized = 4, + BIT_is_vtable_initialized = 5, is_vtable_initialized = (1 << BIT_is_vtable_initialized), - BIT_has_initialization_error = 5, + BIT_has_initialization_error = 6, has_initialization_error = (1 << BIT_has_initialization_error), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_27_0._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_27_0._bitfield1)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_27_0* _ => (Il2CppClass_27_0*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_27_0)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_27_0)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); @@ -135,8 +133,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -175,8 +173,8 @@ public bool HasReferences } public bool SizeInited { - get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_0.Bitfield0.BIT_size_inited); - set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_0.Bitfield0.BIT_size_inited, value); + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_27_0.Bitfield1.BIT_size_inited); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_27_0.Bitfield1.BIT_size_inited, value); } public bool HasFinalize { @@ -194,7 +192,5 @@ public bool InitializedAndNoError set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_0.Bitfield0.BIT_initialized_and_no_error, value); } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_1.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_1.cs index 7288f061..bebe271a 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_1.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_1.cs @@ -1,15 +1,14 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("2020.3.20")] + [ApplicableToUnityVersionsSince("2021.1.0")] public unsafe class NativeClassStructHandler_27_1 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_27_1); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_27_1* _ = (Il2CppClass_27_1*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_27_1 { @@ -25,8 +24,8 @@ internal unsafe struct Il2CppClass_27_1 public void* gc_desc; public byte* name; public byte* namespaze; - public NativeTypeStructHandler_16_0.Il2CppType_16_0 byval_arg; - public NativeTypeStructHandler_16_0.Il2CppType_16_0 this_arg; + public NativeTypeStructHandler_27_0.Il2CppType_27_0 byval_arg; + public NativeTypeStructHandler_27_0.Il2CppType_27_0 this_arg; public Il2CppClass* element_class; public Il2CppClass* castClass; public Il2CppClass* declaringType; @@ -49,7 +48,7 @@ internal unsafe struct Il2CppClass_27_1 public uint initializationExceptionGCHandle; public uint cctor_started; public uint cctor_finished; - public IntPtr cctor_thread; + public ulong cctor_thread; public Il2CppMetadataGenericContainerHandle genericContainerHandle; public uint instance_size; public uint actualSize; @@ -80,50 +79,43 @@ internal enum Bitfield0 : byte { BIT_initialized_and_no_error = 0, initialized_and_no_error = (1 << BIT_initialized_and_no_error), - BIT_valuetype = 1, - valuetype = (1 << BIT_valuetype), - BIT_initialized = 2, + BIT_initialized = 1, initialized = (1 << BIT_initialized), - BIT_enumtype = 3, + BIT_enumtype = 2, enumtype = (1 << BIT_enumtype), - BIT_is_generic = 4, + BIT_is_generic = 3, is_generic = (1 << BIT_is_generic), - BIT_has_references = 5, + BIT_has_references = 4, has_references = (1 << BIT_has_references), - BIT_init_pending = 6, + BIT_init_pending = 5, init_pending = (1 << BIT_init_pending), - BIT_size_init_pending = 7, - size_init_pending = (1 << BIT_size_init_pending), + BIT_size_inited = 6, + size_inited = (1 << BIT_size_inited), + BIT_has_finalize = 7, + has_finalize = (1 << BIT_has_finalize), } - internal enum Bitfield1 : byte { - BIT_size_inited = 0, - size_inited = (1 << BIT_size_inited), - BIT_has_finalize = 1, - has_finalize = (1 << BIT_has_finalize), - BIT_has_cctor = 2, + BIT_has_cctor = 0, has_cctor = (1 << BIT_has_cctor), - BIT_is_blittable = 3, + BIT_is_blittable = 1, is_blittable = (1 << BIT_is_blittable), - BIT_is_import_or_windows_runtime = 4, + BIT_is_import_or_windows_runtime = 2, is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), - BIT_is_vtable_initialized = 5, + BIT_is_vtable_initialized = 3, is_vtable_initialized = (1 << BIT_is_vtable_initialized), - BIT_has_initialization_error = 6, + BIT_has_initialization_error = 4, has_initialization_error = (1 << BIT_has_initialization_error), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_27_1._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_27_1._bitfield1)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_27_1* _ => (Il2CppClass_27_1*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_27_1)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_27_1)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); @@ -137,8 +129,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -152,8 +144,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref Il2CppClass** TypeHierarchy => ref _->typeHierarchy; public bool ValueType { - get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_1.Bitfield0.BIT_valuetype); - set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_1.Bitfield0.BIT_valuetype, value); + get => ByValArg.ValueType && ThisArg.ValueType; + set { } } public bool Initialized { @@ -177,13 +169,13 @@ public bool HasReferences } public bool SizeInited { - get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_27_1.Bitfield1.BIT_size_inited); - set => this.SetBit(_bitfield1offset, (int)Il2CppClass_27_1.Bitfield1.BIT_size_inited, value); + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_1.Bitfield0.BIT_size_inited); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_1.Bitfield0.BIT_size_inited, value); } public bool HasFinalize { - get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_27_1.Bitfield1.BIT_has_finalize); - set => this.SetBit(_bitfield1offset, (int)Il2CppClass_27_1.Bitfield1.BIT_has_finalize, value); + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_1.Bitfield0.BIT_has_finalize); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_1.Bitfield0.BIT_has_finalize, value); } public bool IsVtableInitialized { @@ -196,7 +188,5 @@ public bool InitializedAndNoError set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_1.Bitfield0.BIT_initialized_and_no_error, value); } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_2.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_2.cs index f64e2199..bb5f8146 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_2.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_2.cs @@ -1,15 +1,14 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("2021.1.0")] + [ApplicableToUnityVersionsSince("2021.1.24")] public unsafe class NativeClassStructHandler_27_2 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_27_2); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_27_2* _ = (Il2CppClass_27_2*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_27_2 { @@ -49,7 +48,7 @@ internal unsafe struct Il2CppClass_27_2 public uint initializationExceptionGCHandle; public uint cctor_started; public uint cctor_finished; - public IntPtr cctor_thread; + public ulong cctor_thread; public Il2CppMetadataGenericContainerHandle genericContainerHandle; public uint instance_size; public uint actualSize; @@ -90,36 +89,35 @@ internal enum Bitfield0 : byte has_references = (1 << BIT_has_references), BIT_init_pending = 5, init_pending = (1 << BIT_init_pending), - BIT_size_inited = 6, + BIT_size_init_pending = 6, + size_init_pending = (1 << BIT_size_init_pending), + BIT_size_inited = 7, size_inited = (1 << BIT_size_inited), - BIT_has_finalize = 7, - has_finalize = (1 << BIT_has_finalize), } - internal enum Bitfield1 : byte { - BIT_has_cctor = 0, + BIT_has_finalize = 0, + has_finalize = (1 << BIT_has_finalize), + BIT_has_cctor = 1, has_cctor = (1 << BIT_has_cctor), - BIT_is_blittable = 1, + BIT_is_blittable = 2, is_blittable = (1 << BIT_is_blittable), - BIT_is_import_or_windows_runtime = 2, + BIT_is_import_or_windows_runtime = 3, is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), - BIT_is_vtable_initialized = 3, + BIT_is_vtable_initialized = 4, is_vtable_initialized = (1 << BIT_is_vtable_initialized), - BIT_has_initialization_error = 4, + BIT_has_initialization_error = 5, has_initialization_error = (1 << BIT_has_initialization_error), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_27_2._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_27_2._bitfield1)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_27_2* _ => (Il2CppClass_27_2*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_27_2)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_27_2)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); @@ -133,8 +131,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -178,8 +176,8 @@ public bool SizeInited } public bool HasFinalize { - get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_2.Bitfield0.BIT_has_finalize); - set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_2.Bitfield0.BIT_has_finalize, value); + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_27_2.Bitfield1.BIT_has_finalize); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_27_2.Bitfield1.BIT_has_finalize, value); } public bool IsVtableInitialized { @@ -192,7 +190,5 @@ public bool InitializedAndNoError set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_2.Bitfield0.BIT_initialized_and_no_error, value); } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_3.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_3.cs index 72afe19d..9d0516c6 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_3.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_3.cs @@ -1,15 +1,14 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("2021.1.24")] + [ApplicableToUnityVersionsSince("2021.2.0")] public unsafe class NativeClassStructHandler_27_3 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_27_3); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_27_3* _ = (Il2CppClass_27_3*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_27_3 { @@ -49,7 +48,7 @@ internal unsafe struct Il2CppClass_27_3 public uint initializationExceptionGCHandle; public uint cctor_started; public uint cctor_finished; - public IntPtr cctor_thread; + public ulong cctor_thread; public Il2CppMetadataGenericContainerHandle genericContainerHandle; public uint instance_size; public uint actualSize; @@ -84,18 +83,17 @@ internal enum Bitfield0 : byte initialized = (1 << BIT_initialized), BIT_enumtype = 2, enumtype = (1 << BIT_enumtype), - BIT_is_generic = 3, + BIT_nullabletype = 3, + nullabletype = (1 << BIT_nullabletype), + BIT_is_generic = 4, is_generic = (1 << BIT_is_generic), - BIT_has_references = 4, + BIT_has_references = 5, has_references = (1 << BIT_has_references), - BIT_init_pending = 5, + BIT_init_pending = 6, init_pending = (1 << BIT_init_pending), - BIT_size_init_pending = 6, - size_init_pending = (1 << BIT_size_init_pending), BIT_size_inited = 7, size_inited = (1 << BIT_size_inited), } - internal enum Bitfield1 : byte { BIT_has_finalize = 0, @@ -111,17 +109,15 @@ internal enum Bitfield1 : byte BIT_has_initialization_error = 5, has_initialization_error = (1 << BIT_has_initialization_error), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_27_3._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_27_3._bitfield1)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_27_3* _ => (Il2CppClass_27_3*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_27_3)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_27_3)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); @@ -135,8 +131,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -194,7 +190,5 @@ public bool InitializedAndNoError set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_3.Bitfield0.BIT_initialized_and_no_error, value); } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_4.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_4.cs new file mode 100644 index 00000000..f57e8db3 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_4.cs @@ -0,0 +1,192 @@ +using System.Runtime.InteropServices; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class +{ + [ApplicableToUnityVersionsSince("2021.2.0a12")] + public unsafe class NativeClassStructHandler_27_4 : INativeClassStructHandler + { + public int Size() => sizeof(Il2CppClass_27_4); + public INativeClassStruct CreateNewStruct(int vTableSlots) + { + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + Il2CppClass_27_4* _ = (Il2CppClass_27_4*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeClassStruct Wrap(Il2CppClass* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppClass_27_4 + { + public Il2CppImage* image; + public void* gc_desc; + public byte* name; + public byte* namespaze; + public NativeTypeStructHandler_27_0.Il2CppType_27_0 byval_arg; + public NativeTypeStructHandler_27_0.Il2CppType_27_0 this_arg; + public Il2CppClass* element_class; + public Il2CppClass* castClass; + public Il2CppClass* declaringType; + public Il2CppClass* parent; + public void* generic_class; + public Il2CppMetadataTypeHandle typeMetadataHandle; + public void* interopData; + public Il2CppClass* klass; + public Il2CppFieldInfo* fields; + public Il2CppEventInfo* events; + public Il2CppPropertyInfo* properties; + public Il2CppMethodInfo** methods; + public Il2CppClass** nestedTypes; + public Il2CppClass** implementedInterfaces; + public Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + public void* static_fields; + public void* rgctx_data; + public Il2CppClass** typeHierarchy; + public void* unity_user_data; + public uint initializationExceptionGCHandle; + public uint cctor_started; + public uint cctor_finished_or_no_cctor; + public ulong cctor_thread; + public Il2CppMetadataGenericContainerHandle genericContainerHandle; + public uint instance_size; + public uint actualSize; + public uint element_size; + public int native_size; + public uint static_fields_size; + public uint thread_static_fields_size; + public int thread_static_fields_offset; + public uint flags; + public uint token; + public ushort method_count; + public ushort property_count; + public ushort field_count; + public ushort event_count; + public ushort nested_type_count; + public ushort vtable_count; + public ushort interfaces_count; + public ushort interface_offsets_count; + public byte typeHierarchyDepth; + public byte genericRecursionDepth; + public byte rank; + public byte minimumAlignment; + public byte naturalAligment; + public byte packingSize; + public Bitfield0 _bitfield0; + public Bitfield1 _bitfield1; + internal enum Bitfield0 : byte + { + BIT_initialized_and_no_error = 0, + initialized_and_no_error = (1 << BIT_initialized_and_no_error), + BIT_initialized = 1, + initialized = (1 << BIT_initialized), + BIT_enumtype = 2, + enumtype = (1 << BIT_enumtype), + BIT_nullabletype = 3, + nullabletype = (1 << BIT_nullabletype), + BIT_is_generic = 4, + is_generic = (1 << BIT_is_generic), + BIT_has_references = 5, + has_references = (1 << BIT_has_references), + BIT_init_pending = 6, + init_pending = (1 << BIT_init_pending), + BIT_size_inited = 7, + size_inited = (1 << BIT_size_inited), + } + internal enum Bitfield1 : byte + { + BIT_has_finalize = 0, + has_finalize = (1 << BIT_has_finalize), + BIT_has_cctor = 1, + has_cctor = (1 << BIT_has_cctor), + BIT_is_blittable = 2, + is_blittable = (1 << BIT_is_blittable), + BIT_is_import_or_windows_runtime = 3, + is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), + BIT_is_vtable_initialized = 4, + is_vtable_initialized = (1 << BIT_is_vtable_initialized), + } + } + internal class NativeStructWrapper : INativeClassStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_27_4._bitfield0)).ToInt32(); + private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_27_4._bitfield1)).ToInt32(); + public nint Pointer { get; } + private Il2CppClass_27_4* _ => (Il2CppClass_27_4*)Pointer; + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_27_4)); + public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; + public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); + public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); + public ref uint InstanceSize => ref _->instance_size; + public ref ushort VtableCount => ref _->vtable_count; + public ref ushort InterfaceCount => ref _->interfaces_count; + public ref ushort InterfaceOffsetsCount => ref _->interface_offsets_count; + public ref byte TypeHierarchyDepth => ref _->typeHierarchyDepth; + public ref int NativeSize => ref _->native_size; + public ref uint ActualSize => ref _->actualSize; + public ref ushort MethodCount => ref _->method_count; + public ref ushort FieldCount => ref _->field_count; + public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; + public ref Il2CppImage* Image => ref _->image; + public ref Il2CppClass* Parent => ref _->parent; + public ref Il2CppClass* ElementClass => ref _->element_class; + public ref Il2CppClass* CastClass => ref _->castClass; + public ref Il2CppClass* DeclaringType => ref _->declaringType; + public ref Il2CppClass* Class => ref _->klass; + public ref Il2CppFieldInfo* Fields => ref _->fields; + public ref Il2CppMethodInfo** Methods => ref _->methods; + public ref Il2CppClass** ImplementedInterfaces => ref _->implementedInterfaces; + public ref Il2CppRuntimeInterfaceOffsetPair* InterfaceOffsets => ref _->interfaceOffsets; + public ref Il2CppClass** TypeHierarchy => ref _->typeHierarchy; + public bool ValueType + { + get => ByValArg.ValueType && ThisArg.ValueType; + set { } + } + public bool Initialized + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_4.Bitfield0.BIT_initialized); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_4.Bitfield0.BIT_initialized, value); + } + public bool EnumType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_4.Bitfield0.BIT_enumtype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_4.Bitfield0.BIT_enumtype, value); + } + public bool IsGeneric + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_4.Bitfield0.BIT_is_generic); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_4.Bitfield0.BIT_is_generic, value); + } + public bool HasReferences + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_4.Bitfield0.BIT_has_references); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_4.Bitfield0.BIT_has_references, value); + } + public bool SizeInited + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_4.Bitfield0.BIT_size_inited); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_4.Bitfield0.BIT_size_inited, value); + } + public bool HasFinalize + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_27_4.Bitfield1.BIT_has_finalize); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_27_4.Bitfield1.BIT_has_finalize, value); + } + public bool IsVtableInitialized + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_27_4.Bitfield1.BIT_is_vtable_initialized); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_27_4.Bitfield1.BIT_is_vtable_initialized, value); + } + public bool InitializedAndNoError + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_4.Bitfield0.BIT_initialized_and_no_error); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_4.Bitfield0.BIT_initialized_and_no_error, value); + } + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_5.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_5.cs new file mode 100644 index 00000000..cf62e742 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_27_5.cs @@ -0,0 +1,194 @@ +using System.Runtime.InteropServices; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class +{ + [ApplicableToUnityVersionsSince("2021.2.0a21")] + public unsafe class NativeClassStructHandler_27_5 : INativeClassStructHandler + { + public int Size() => sizeof(Il2CppClass_27_5); + public INativeClassStruct CreateNewStruct(int vTableSlots) + { + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + Il2CppClass_27_5* _ = (Il2CppClass_27_5*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeClassStruct Wrap(Il2CppClass* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppClass_27_5 + { + public Il2CppImage* image; + public void* gc_desc; + public byte* name; + public byte* namespaze; + public NativeTypeStructHandler_27_0.Il2CppType_27_0 byval_arg; + public NativeTypeStructHandler_27_0.Il2CppType_27_0 this_arg; + public Il2CppClass* element_class; + public Il2CppClass* castClass; + public Il2CppClass* declaringType; + public Il2CppClass* parent; + public void* generic_class; + public Il2CppMetadataTypeHandle typeMetadataHandle; + public void* interopData; + public Il2CppClass* klass; + public Il2CppFieldInfo* fields; + public Il2CppEventInfo* events; + public Il2CppPropertyInfo* properties; + public Il2CppMethodInfo** methods; + public Il2CppClass** nestedTypes; + public Il2CppClass** implementedInterfaces; + public Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + public void* static_fields; + public void* rgctx_data; + public Il2CppClass** typeHierarchy; + public void* unity_user_data; + public uint initializationExceptionGCHandle; + public uint cctor_started; + public uint cctor_finished_or_no_cctor; + public ulong cctor_thread; + public Il2CppMetadataGenericContainerHandle genericContainerHandle; + public uint instance_size; + public uint actualSize; + public uint element_size; + public int native_size; + public uint static_fields_size; + public uint thread_static_fields_size; + public int thread_static_fields_offset; + public uint flags; + public uint token; + public ushort method_count; + public ushort property_count; + public ushort field_count; + public ushort event_count; + public ushort nested_type_count; + public ushort vtable_count; + public ushort interfaces_count; + public ushort interface_offsets_count; + public byte typeHierarchyDepth; + public byte genericRecursionDepth; + public byte rank; + public byte minimumAlignment; + public byte naturalAligment; + public byte packingSize; + public Bitfield0 _bitfield0; + public Bitfield1 _bitfield1; + internal enum Bitfield0 : byte + { + BIT_initialized_and_no_error = 0, + initialized_and_no_error = (1 << BIT_initialized_and_no_error), + BIT_initialized = 1, + initialized = (1 << BIT_initialized), + BIT_enumtype = 2, + enumtype = (1 << BIT_enumtype), + BIT_nullabletype = 3, + nullabletype = (1 << BIT_nullabletype), + BIT_is_generic = 4, + is_generic = (1 << BIT_is_generic), + BIT_has_references = 5, + has_references = (1 << BIT_has_references), + BIT_init_pending = 6, + init_pending = (1 << BIT_init_pending), + BIT_size_inited = 7, + size_inited = (1 << BIT_size_inited), + } + internal enum Bitfield1 : byte + { + BIT_has_finalize = 0, + has_finalize = (1 << BIT_has_finalize), + BIT_has_cctor = 1, + has_cctor = (1 << BIT_has_cctor), + BIT_is_blittable = 2, + is_blittable = (1 << BIT_is_blittable), + BIT_is_import_or_windows_runtime = 3, + is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), + BIT_is_vtable_initialized = 4, + is_vtable_initialized = (1 << BIT_is_vtable_initialized), + BIT_is_byref_like = 5, + is_byref_like = (1 << BIT_is_byref_like), + } + } + internal class NativeStructWrapper : INativeClassStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_27_5._bitfield0)).ToInt32(); + private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_27_5._bitfield1)).ToInt32(); + public nint Pointer { get; } + private Il2CppClass_27_5* _ => (Il2CppClass_27_5*)Pointer; + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_27_5)); + public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; + public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); + public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); + public ref uint InstanceSize => ref _->instance_size; + public ref ushort VtableCount => ref _->vtable_count; + public ref ushort InterfaceCount => ref _->interfaces_count; + public ref ushort InterfaceOffsetsCount => ref _->interface_offsets_count; + public ref byte TypeHierarchyDepth => ref _->typeHierarchyDepth; + public ref int NativeSize => ref _->native_size; + public ref uint ActualSize => ref _->actualSize; + public ref ushort MethodCount => ref _->method_count; + public ref ushort FieldCount => ref _->field_count; + public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; + public ref Il2CppImage* Image => ref _->image; + public ref Il2CppClass* Parent => ref _->parent; + public ref Il2CppClass* ElementClass => ref _->element_class; + public ref Il2CppClass* CastClass => ref _->castClass; + public ref Il2CppClass* DeclaringType => ref _->declaringType; + public ref Il2CppClass* Class => ref _->klass; + public ref Il2CppFieldInfo* Fields => ref _->fields; + public ref Il2CppMethodInfo** Methods => ref _->methods; + public ref Il2CppClass** ImplementedInterfaces => ref _->implementedInterfaces; + public ref Il2CppRuntimeInterfaceOffsetPair* InterfaceOffsets => ref _->interfaceOffsets; + public ref Il2CppClass** TypeHierarchy => ref _->typeHierarchy; + public bool ValueType + { + get => ByValArg.ValueType && ThisArg.ValueType; + set { } + } + public bool Initialized + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_5.Bitfield0.BIT_initialized); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_5.Bitfield0.BIT_initialized, value); + } + public bool EnumType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_5.Bitfield0.BIT_enumtype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_5.Bitfield0.BIT_enumtype, value); + } + public bool IsGeneric + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_5.Bitfield0.BIT_is_generic); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_5.Bitfield0.BIT_is_generic, value); + } + public bool HasReferences + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_5.Bitfield0.BIT_has_references); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_5.Bitfield0.BIT_has_references, value); + } + public bool SizeInited + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_5.Bitfield0.BIT_size_inited); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_5.Bitfield0.BIT_size_inited, value); + } + public bool HasFinalize + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_27_5.Bitfield1.BIT_has_finalize); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_27_5.Bitfield1.BIT_has_finalize, value); + } + public bool IsVtableInitialized + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_27_5.Bitfield1.BIT_is_vtable_initialized); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_27_5.Bitfield1.BIT_is_vtable_initialized, value); + } + public bool InitializedAndNoError + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_27_5.Bitfield0.BIT_initialized_and_no_error); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_27_5.Bitfield0.BIT_initialized_and_no_error, value); + } + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_29_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_29_0.cs index fd38878f..115ce8eb 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_29_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_29_0.cs @@ -1,15 +1,14 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("2021.2.0")] + [ApplicableToUnityVersionsSince("2021.2.0b13")] public unsafe class NativeClassStructHandler_29_0 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_29_0); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_29_0* _ = (Il2CppClass_29_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_29_0 { @@ -49,7 +48,7 @@ internal unsafe struct Il2CppClass_29_0 public uint initializationExceptionGCHandle; public uint cctor_started; public uint cctor_finished_or_no_cctor; - public IntPtr cctor_thread; + public ulong cctor_thread; public Il2CppMetadataGenericContainerHandle genericContainerHandle; public uint instance_size; public uint actualSize; @@ -95,7 +94,6 @@ internal enum Bitfield0 : byte BIT_size_init_pending = 7, size_init_pending = (1 << BIT_size_init_pending), } - internal enum Bitfield1 : byte { BIT_size_inited = 0, @@ -113,17 +111,15 @@ internal enum Bitfield1 : byte BIT_is_byref_like = 6, is_byref_like = (1 << BIT_is_byref_like), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_29_0._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_29_0._bitfield1)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_29_0* _ => (Il2CppClass_29_0*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_29_0)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_29_0)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); @@ -137,8 +133,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -196,7 +192,5 @@ public bool InitializedAndNoError set => this.SetBit(_bitfield0offset, (int)Il2CppClass_29_0.Bitfield0.BIT_initialized_and_no_error, value); } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_29_1.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_29_1.cs index 1764e56f..2feb3ed5 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_29_1.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_29_1.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class @@ -9,7 +8,7 @@ public unsafe class NativeClassStructHandler_29_1 : INativeClassStructHandler public int Size() => sizeof(Il2CppClass_29_1); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_29_1* _ = (Il2CppClass_29_1*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_29_1 { @@ -49,7 +48,7 @@ internal unsafe struct Il2CppClass_29_1 public uint initializationExceptionGCHandle; public uint cctor_started; public uint cctor_finished_or_no_cctor; - public IntPtr cctor_thread; + public ulong cctor_thread; public Il2CppMetadataGenericContainerHandle genericContainerHandle; public uint instance_size; public uint stack_slot_size; @@ -95,7 +94,6 @@ internal enum Bitfield0 : byte BIT_size_init_pending = 7, size_init_pending = (1 << BIT_size_init_pending), } - internal enum Bitfield1 : byte { BIT_size_inited = 0, @@ -113,17 +111,15 @@ internal enum Bitfield1 : byte BIT_is_byref_like = 6, is_byref_like = (1 << BIT_is_byref_like), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_29_1._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_29_1._bitfield1)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_29_1* _ => (Il2CppClass_29_1*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_29_1)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_29_1)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); @@ -137,8 +133,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -196,7 +192,5 @@ public bool InitializedAndNoError set => this.SetBit(_bitfield0offset, (int)Il2CppClass_29_1.Bitfield0.BIT_initialized_and_no_error, value); } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_29_2.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_29_2.cs index 68d97a4c..81a60330 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_29_2.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_29_2.cs @@ -1,15 +1,14 @@ -using System; using System.Runtime.InteropServices; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - [ApplicableToUnityVersionsSince("2023.1.0")] + [ApplicableToUnityVersionsSince("2023.1.0a15")] public unsafe class NativeClassStructHandler_29_2 : INativeClassStructHandler { public int Size() => sizeof(Il2CppClass_29_2); public INativeClassStruct CreateNewStruct(int vTableSlots) { - IntPtr ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); Il2CppClass_29_2* _ = (Il2CppClass_29_2*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -17,7 +16,7 @@ public INativeClassStruct CreateNewStruct(int vTableSlots) public INativeClassStruct Wrap(Il2CppClass* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppClass_29_2 { @@ -49,7 +48,7 @@ internal unsafe struct Il2CppClass_29_2 public Il2CppGCHandle initializationExceptionGCHandle; public uint cctor_started; public uint cctor_finished_or_no_cctor; - public IntPtr cctor_thread; + public ulong cctor_thread; public Il2CppMetadataGenericContainerHandle genericContainerHandle; public uint instance_size; public uint stack_slot_size; @@ -95,7 +94,6 @@ internal enum Bitfield0 : byte BIT_size_init_pending = 7, size_init_pending = (1 << BIT_size_init_pending), } - internal enum Bitfield1 : byte { BIT_size_inited = 0, @@ -113,17 +111,15 @@ internal enum Bitfield1 : byte BIT_is_byref_like = 6, is_byref_like = (1 << BIT_is_byref_like), } - } - internal class NativeStructWrapper : INativeClassStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_29_2._bitfield0)).ToInt32(); private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_29_2._bitfield1)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppClass_29_2* _ => (Il2CppClass_29_2*)Pointer; - public IntPtr VTable => IntPtr.Add(Pointer, sizeof(Il2CppClass_29_2)); + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_29_2)); public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); @@ -137,8 +133,8 @@ internal class NativeStructWrapper : INativeClassStruct public ref ushort MethodCount => ref _->method_count; public ref ushort FieldCount => ref _->field_count; public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr Namespace => ref *(IntPtr*)&_->namespaze; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; public ref Il2CppImage* Image => ref _->image; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppClass* ElementClass => ref _->element_class; @@ -196,7 +192,5 @@ public bool InitializedAndNoError set => this.SetBit(_bitfield0offset, (int)Il2CppClass_29_2.Bitfield0.BIT_initialized_and_no_error, value); } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_39_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_39_0.cs new file mode 100644 index 00000000..0ebfa0b1 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Class_39_0.cs @@ -0,0 +1,196 @@ +using System.Runtime.InteropServices; +using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class +{ + [ApplicableToUnityVersionsSince("6000.3.3")] + public unsafe class NativeClassStructHandler_39_0 : INativeClassStructHandler + { + public int Size() => sizeof(Il2CppClass_39_0); + public INativeClassStruct CreateNewStruct(int vTableSlots) + { + nint ptr = Marshal.AllocHGlobal(Size() + sizeof(VirtualInvokeData) * vTableSlots); + Il2CppClass_39_0* _ = (Il2CppClass_39_0*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeClassStruct Wrap(Il2CppClass* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppClass_39_0 + { + public Il2CppImage* image; + public void* gc_desc; + public byte* name; + public byte* namespaze; + public NativeTypeStructHandler_27_0.Il2CppType_27_0 byval_arg; + public NativeTypeStructHandler_27_0.Il2CppType_27_0 this_arg; + public Il2CppClass* element_class; + public Il2CppClass* castClass; + public Il2CppClass* declaringType; + public Il2CppClass* parent; + public void* generic_class; + public Il2CppMetadataTypeHandle typeMetadataHandle; + public void* interopData; + public Il2CppClass* klass; + public Il2CppFieldInfo* fields; + public Il2CppEventInfo* events; + public Il2CppPropertyInfo* properties; + public Il2CppMethodInfo** methods; + public Il2CppClass** nestedTypes; + public Il2CppClass** implementedInterfaces; + public Il2CppRuntimeInterfaceOffsetPair* interfaceOffsets; + public void* static_fields; + public void* init_data; + public Il2CppClass** typeHierarchy; + public void* unity_user_data; + public Il2CppGCHandle initializationExceptionGCHandle; + public uint cctor_started; + public uint cctor_finished_or_no_cctor; + public ulong cctor_thread; + public Il2CppMetadataGenericContainerHandle genericContainerHandle; + public uint instance_size; + public uint stack_slot_size; + public uint actualSize; + public uint element_size; + public int native_size; + public uint static_fields_size; + public uint thread_static_fields_size; + public int thread_static_fields_offset; + public uint flags; + public uint token; + public ushort method_count; + public ushort property_count; + public ushort field_count; + public ushort event_count; + public ushort nested_type_count; + public ushort vtable_count; + public ushort interfaces_count; + public ushort interface_offsets_count; + public byte typeHierarchyDepth; + public byte genericRecursionDepth; + public byte rank; + public byte minimumAlignment; + public byte packingSize; + public Bitfield0 _bitfield0; + public Bitfield1 _bitfield1; + internal enum Bitfield0 : byte + { + BIT_initialized_and_no_error = 0, + initialized_and_no_error = (1 << BIT_initialized_and_no_error), + BIT_initialized = 1, + initialized = (1 << BIT_initialized), + BIT_enumtype = 2, + enumtype = (1 << BIT_enumtype), + BIT_nullabletype = 3, + nullabletype = (1 << BIT_nullabletype), + BIT_is_generic = 4, + is_generic = (1 << BIT_is_generic), + BIT_has_references = 5, + has_references = (1 << BIT_has_references), + BIT_init_pending = 6, + init_pending = (1 << BIT_init_pending), + BIT_size_init_pending = 7, + size_init_pending = (1 << BIT_size_init_pending), + } + internal enum Bitfield1 : byte + { + BIT_size_inited = 0, + size_inited = (1 << BIT_size_inited), + BIT_has_finalize = 1, + has_finalize = (1 << BIT_has_finalize), + BIT_has_cctor = 2, + has_cctor = (1 << BIT_has_cctor), + BIT_is_blittable = 3, + is_blittable = (1 << BIT_is_blittable), + BIT_is_import_or_windows_runtime = 4, + is_import_or_windows_runtime = (1 << BIT_is_import_or_windows_runtime), + BIT_is_vtable_initialized = 5, + is_vtable_initialized = (1 << BIT_is_vtable_initialized), + BIT_is_byref_like = 6, + is_byref_like = (1 << BIT_is_byref_like), + } + } + internal class NativeStructWrapper : INativeClassStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppClass_39_0._bitfield0)).ToInt32(); + private static int _bitfield1offset = Marshal.OffsetOf(nameof(Il2CppClass_39_0._bitfield1)).ToInt32(); + public nint Pointer { get; } + private Il2CppClass_39_0* _ => (Il2CppClass_39_0*)Pointer; + public nint VTable => nint.Add(Pointer, sizeof(Il2CppClass_39_0)); + public Il2CppClass* ClassPointer => (Il2CppClass*)Pointer; + public INativeTypeStruct ByValArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->byval_arg); + public INativeTypeStruct ThisArg => UnityVersionHandler.Wrap((Il2CppTypeStruct*)&_->this_arg); + public ref uint InstanceSize => ref _->instance_size; + public ref ushort VtableCount => ref _->vtable_count; + public ref ushort InterfaceCount => ref _->interfaces_count; + public ref ushort InterfaceOffsetsCount => ref _->interface_offsets_count; + public ref byte TypeHierarchyDepth => ref _->typeHierarchyDepth; + public ref int NativeSize => ref _->native_size; + public ref uint ActualSize => ref _->actualSize; + public ref ushort MethodCount => ref _->method_count; + public ref ushort FieldCount => ref _->field_count; + public ref Il2CppClassAttributes Flags => ref *(Il2CppClassAttributes*)&_->flags; + public ref nint Name => ref *(nint*)&_->name; + public ref nint Namespace => ref *(nint*)&_->namespaze; + public ref Il2CppImage* Image => ref _->image; + public ref Il2CppClass* Parent => ref _->parent; + public ref Il2CppClass* ElementClass => ref _->element_class; + public ref Il2CppClass* CastClass => ref _->castClass; + public ref Il2CppClass* DeclaringType => ref _->declaringType; + public ref Il2CppClass* Class => ref _->klass; + public ref Il2CppFieldInfo* Fields => ref _->fields; + public ref Il2CppMethodInfo** Methods => ref _->methods; + public ref Il2CppClass** ImplementedInterfaces => ref _->implementedInterfaces; + public ref Il2CppRuntimeInterfaceOffsetPair* InterfaceOffsets => ref _->interfaceOffsets; + public ref Il2CppClass** TypeHierarchy => ref _->typeHierarchy; + public bool ValueType + { + get => ByValArg.ValueType && ThisArg.ValueType; + set { } + } + public bool Initialized + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_39_0.Bitfield0.BIT_initialized); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_39_0.Bitfield0.BIT_initialized, value); + } + public bool EnumType + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_39_0.Bitfield0.BIT_enumtype); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_39_0.Bitfield0.BIT_enumtype, value); + } + public bool IsGeneric + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_39_0.Bitfield0.BIT_is_generic); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_39_0.Bitfield0.BIT_is_generic, value); + } + public bool HasReferences + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_39_0.Bitfield0.BIT_has_references); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_39_0.Bitfield0.BIT_has_references, value); + } + public bool SizeInited + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_39_0.Bitfield1.BIT_size_inited); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_39_0.Bitfield1.BIT_size_inited, value); + } + public bool HasFinalize + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_39_0.Bitfield1.BIT_has_finalize); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_39_0.Bitfield1.BIT_has_finalize, value); + } + public bool IsVtableInitialized + { + get => this.CheckBit(_bitfield1offset, (int)Il2CppClass_39_0.Bitfield1.BIT_is_vtable_initialized); + set => this.SetBit(_bitfield1offset, (int)Il2CppClass_39_0.Bitfield1.BIT_is_vtable_initialized, value); + } + public bool InitializedAndNoError + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppClass_39_0.Bitfield0.BIT_initialized_and_no_error); + set => this.SetBit(_bitfield0offset, (int)Il2CppClass_39_0.Bitfield0.BIT_initialized_and_no_error, value); + } + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Interfaces.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Interfaces.cs index 930da95f..986c2195 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Interfaces.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Interfaces.cs @@ -1,55 +1,56 @@ using System; using Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class; - -public interface INativeClassStructHandler : INativeStructHandler -{ - INativeClassStruct CreateNewStruct(int vTableSlots); - unsafe INativeClassStruct Wrap(Il2CppClass* classPointer); -} - -public interface INativeClassStruct : INativeStruct +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Class { - unsafe Il2CppClass* ClassPointer { get; } - IntPtr VTable { get; } - - ref uint InstanceSize { get; } - ref ushort VtableCount { get; } - ref ushort InterfaceCount { get; } - ref ushort InterfaceOffsetsCount { get; } - ref byte TypeHierarchyDepth { get; } - ref int NativeSize { get; } - ref uint ActualSize { get; } - ref ushort MethodCount { get; } - ref ushort FieldCount { get; } - ref Il2CppClassAttributes Flags { get; } - - bool ValueType { get; set; } - bool EnumType { get; set; } - bool IsGeneric { get; set; } - bool Initialized { get; set; } - bool InitializedAndNoError { get; set; } - bool SizeInited { get; set; } - bool HasFinalize { get; set; } - bool IsVtableInitialized { get; set; } - - ref IntPtr Name { get; } - ref IntPtr Namespace { get; } - - INativeTypeStruct ByValArg { get; } - INativeTypeStruct ThisArg { get; } - - unsafe ref Il2CppImage* Image { get; } - unsafe ref Il2CppClass* Parent { get; } - unsafe ref Il2CppClass* ElementClass { get; } - unsafe ref Il2CppClass* CastClass { get; } - unsafe ref Il2CppClass* DeclaringType { get; } - unsafe ref Il2CppClass* Class { get; } - - unsafe ref Il2CppFieldInfo* Fields { get; } - unsafe ref Il2CppMethodInfo** Methods { get; } - unsafe ref Il2CppClass** ImplementedInterfaces { get; } - unsafe ref Il2CppRuntimeInterfaceOffsetPair* InterfaceOffsets { get; } - unsafe ref Il2CppClass** TypeHierarchy { get; } + public interface INativeClassStructHandler : INativeStructHandler + { + INativeClassStruct CreateNewStruct(int vTableSlots); + unsafe INativeClassStruct Wrap(Il2CppClass* classPointer); + } + + public interface INativeClassStruct : INativeStruct + { + unsafe Il2CppClass* ClassPointer { get; } + IntPtr VTable { get; } + + ref uint InstanceSize { get; } + ref ushort VtableCount { get; } + ref ushort InterfaceCount { get; } + ref ushort InterfaceOffsetsCount { get; } + ref byte TypeHierarchyDepth { get; } + ref int NativeSize { get; } + ref uint ActualSize { get; } + ref ushort MethodCount { get; } + ref ushort FieldCount { get; } + ref Il2CppClassAttributes Flags { get; } + + bool ValueType { get; set; } + bool EnumType { get; set; } + bool IsGeneric { get; set; } + bool Initialized { get; set; } + bool InitializedAndNoError { get; set; } + bool SizeInited { get; set; } + bool HasFinalize { get; set; } + bool IsVtableInitialized { get; set; } + + ref IntPtr Name { get; } + ref IntPtr Namespace { get; } + + INativeTypeStruct ByValArg { get; } + INativeTypeStruct ThisArg { get; } + + unsafe ref Il2CppImage* Image { get; } + unsafe ref Il2CppClass* Parent { get; } + unsafe ref Il2CppClass* ElementClass { get; } + unsafe ref Il2CppClass* CastClass { get; } + unsafe ref Il2CppClass* DeclaringType { get; } + unsafe ref Il2CppClass* Class { get; } + + unsafe ref Il2CppFieldInfo* Fields { get; } + unsafe ref Il2CppMethodInfo** Methods { get; } + unsafe ref Il2CppClass** ImplementedInterfaces { get; } + unsafe ref Il2CppRuntimeInterfaceOffsetPair* InterfaceOffsets { get; } + unsafe ref Il2CppClass** TypeHierarchy { get; } + } } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/EventInfo_16_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/EventInfo_16_0.cs index 41a3ef8e..2fe25683 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/EventInfo_16_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/EventInfo_16_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.EventInfo { @@ -8,7 +7,7 @@ public unsafe class NativeEventInfoStructHandler_16_0 : INativeEventInfoStructHa public int Size() => sizeof(Il2CppEventInfo_16_0); public INativeEventInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppEventInfo_16_0* _ = (Il2CppEventInfo_16_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeEventInfoStruct CreateNewStruct() public INativeEventInfoStruct Wrap(Il2CppEventInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppEventInfo_16_0 { @@ -28,21 +27,18 @@ internal unsafe struct Il2CppEventInfo_16_0 public Il2CppMethodInfo* raise; public int customAttributeIndex; } - internal class NativeStructWrapper : INativeEventInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppEventInfo_16_0* _ => (Il2CppEventInfo_16_0*)Pointer; public Il2CppEventInfo* EventInfoPointer => (Il2CppEventInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref Il2CppTypeStruct* EventType => ref _->eventType; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppMethodInfo* Add => ref _->add; public ref Il2CppMethodInfo* Remove => ref _->remove; public ref Il2CppMethodInfo* Raise => ref _->raise; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/EventInfo_19_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/EventInfo_19_0.cs index b2a0b52e..c0b8ed73 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/EventInfo_19_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/EventInfo_19_0.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.EventInfo { - [ApplicableToUnityVersionsSince("5.3.2")] + [ApplicableToUnityVersionsSince("5.3.1p3")] public unsafe class NativeEventInfoStructHandler_19_0 : INativeEventInfoStructHandler { public int Size() => sizeof(Il2CppEventInfo_19_0); public INativeEventInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppEventInfo_19_0* _ = (Il2CppEventInfo_19_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeEventInfoStruct CreateNewStruct() public INativeEventInfoStruct Wrap(Il2CppEventInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppEventInfo_19_0 { @@ -29,21 +28,18 @@ internal unsafe struct Il2CppEventInfo_19_0 public int customAttributeIndex; public uint token; } - internal class NativeStructWrapper : INativeEventInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppEventInfo_19_0* _ => (Il2CppEventInfo_19_0*)Pointer; public Il2CppEventInfo* EventInfoPointer => (Il2CppEventInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref Il2CppTypeStruct* EventType => ref _->eventType; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppMethodInfo* Add => ref _->add; public ref Il2CppMethodInfo* Remove => ref _->remove; public ref Il2CppMethodInfo* Raise => ref _->raise; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/EventInfo_24_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/EventInfo_24_0.cs index c5158fff..859510a9 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/EventInfo_24_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/EventInfo_24_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.EventInfo { @@ -8,7 +7,7 @@ public unsafe class NativeEventInfoStructHandler_24_0 : INativeEventInfoStructHa public int Size() => sizeof(Il2CppEventInfo_24_0); public INativeEventInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppEventInfo_24_0* _ = (Il2CppEventInfo_24_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeEventInfoStruct CreateNewStruct() public INativeEventInfoStruct Wrap(Il2CppEventInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppEventInfo_24_0 { @@ -28,21 +27,18 @@ internal unsafe struct Il2CppEventInfo_24_0 public Il2CppMethodInfo* raise; public uint token; } - internal class NativeStructWrapper : INativeEventInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppEventInfo_24_0* _ => (Il2CppEventInfo_24_0*)Pointer; public Il2CppEventInfo* EventInfoPointer => (Il2CppEventInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref Il2CppTypeStruct* EventType => ref _->eventType; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppMethodInfo* Add => ref _->add; public ref Il2CppMethodInfo* Remove => ref _->remove; public ref Il2CppMethodInfo* Raise => ref _->raise; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/Interfaces.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/Interfaces.cs index e5514719..ad2d6eba 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/Interfaces.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/EventInfo/Interfaces.cs @@ -1,26 +1,27 @@ using System; -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.EventInfo; - -public interface INativeEventInfoStructHandler : INativeStructHandler +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.EventInfo { - INativeEventInfoStruct CreateNewStruct(); - unsafe INativeEventInfoStruct Wrap(Il2CppEventInfo* eventInfoPointer); -} + public interface INativeEventInfoStructHandler : INativeStructHandler + { + INativeEventInfoStruct CreateNewStruct(); + unsafe INativeEventInfoStruct Wrap(Il2CppEventInfo* eventInfoPointer); + } -public interface INativeEventInfoStruct : INativeStruct -{ - unsafe Il2CppEventInfo* EventInfoPointer { get; } + public interface INativeEventInfoStruct : INativeStruct + { + unsafe Il2CppEventInfo* EventInfoPointer { get; } - ref IntPtr Name { get; } + ref IntPtr Name { get; } - unsafe ref Il2CppTypeStruct* EventType { get; } + unsafe ref Il2CppTypeStruct* EventType { get; } - unsafe ref Il2CppClass* Parent { get; } + unsafe ref Il2CppClass* Parent { get; } - unsafe ref Il2CppMethodInfo* Add { get; } + unsafe ref Il2CppMethodInfo* Add { get; } - unsafe ref Il2CppMethodInfo* Remove { get; } + unsafe ref Il2CppMethodInfo* Remove { get; } - unsafe ref Il2CppMethodInfo* Raise { get; } + unsafe ref Il2CppMethodInfo* Raise { get; } + } } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_16_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_16_0.cs index 3a5b50ea..155f0fc5 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_16_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_16_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Exception { @@ -8,7 +7,7 @@ public unsafe class NativeExceptionStructHandler_16_0 : INativeExceptionStructHa public int Size() => sizeof(Il2CppException_16_0); public INativeExceptionStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppException_16_0* _ = (Il2CppException_16_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeExceptionStruct CreateNewStruct() public INativeExceptionStruct Wrap(Il2CppException* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppException_16_0 { @@ -33,11 +32,10 @@ internal unsafe struct Il2CppException_16_0 public Il2CppString* source; public Il2CppObject* _data; } - internal class NativeStructWrapper : INativeExceptionStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppException_16_0* _ => (Il2CppException_16_0*)Pointer; public Il2CppException* ExceptionPointer => (Il2CppException*)Pointer; public ref Il2CppException* InnerException => ref *(Il2CppException**)&_->inner_ex; @@ -47,7 +45,5 @@ internal class NativeStructWrapper : INativeExceptionStruct public ref Il2CppString* StackTrace => ref _->stack_trace; public ref Il2CppString* RemoteStackTrace => ref _->remote_stack_trace; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_20_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_20_0.cs index cf7f829d..94ad77bb 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_20_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_20_0.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Exception { - [ApplicableToUnityVersionsSince("5.3.3")] + [ApplicableToUnityVersionsSince("5.3.2p2")] public unsafe class NativeExceptionStructHandler_20_0 : INativeExceptionStructHandler { public int Size() => sizeof(Il2CppException_20_0); public INativeExceptionStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppException_20_0* _ = (Il2CppException_20_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeExceptionStruct CreateNewStruct() public INativeExceptionStruct Wrap(Il2CppException* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppException_20_0 { @@ -33,11 +32,10 @@ internal unsafe struct Il2CppException_20_0 public Il2CppString* source; public Il2CppObject* _data; } - internal class NativeStructWrapper : INativeExceptionStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppException_20_0* _ => (Il2CppException_20_0*)Pointer; public Il2CppException* ExceptionPointer => (Il2CppException*)Pointer; public ref Il2CppException* InnerException => ref *(Il2CppException**)&_->inner_ex; @@ -47,7 +45,5 @@ internal class NativeStructWrapper : INativeExceptionStruct public ref Il2CppString* StackTrace => ref _->stack_trace; public ref Il2CppString* RemoteStackTrace => ref _->remote_stack_trace; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_21_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_21_0.cs index 06cbb443..2d7f31f6 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_21_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_21_0.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Exception { - [ApplicableToUnityVersionsSince("5.3.5")] + [ApplicableToUnityVersionsSince("5.3.4p3")] public unsafe class NativeExceptionStructHandler_21_0 : INativeExceptionStructHandler { public int Size() => sizeof(Il2CppException_21_0); public INativeExceptionStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppException_21_0* _ = (Il2CppException_21_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeExceptionStruct CreateNewStruct() public INativeExceptionStruct Wrap(Il2CppException* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppException_21_0 { @@ -33,11 +32,10 @@ internal unsafe struct Il2CppException_21_0 public Il2CppString* source; public Il2CppObject* _data; } - internal class NativeStructWrapper : INativeExceptionStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppException_21_0* _ => (Il2CppException_21_0*)Pointer; public Il2CppException* ExceptionPointer => (Il2CppException*)Pointer; public ref Il2CppException* InnerException => ref _->inner_ex; @@ -47,7 +45,5 @@ internal class NativeStructWrapper : INativeExceptionStruct public ref Il2CppString* StackTrace => ref _->stack_trace; public ref Il2CppString* RemoteStackTrace => ref _->remote_stack_trace; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_22_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_22_0.cs new file mode 100644 index 00000000..6417c039 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_22_0.cs @@ -0,0 +1,53 @@ +using System.Runtime.InteropServices; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Exception +{ + [ApplicableToUnityVersionsSince("5.5.0")] + public unsafe class NativeExceptionStructHandler_22_0 : INativeExceptionStructHandler + { + public int Size() => sizeof(Il2CppException_22_0); + public INativeExceptionStruct CreateNewStruct() + { + nint ptr = Marshal.AllocHGlobal(Size()); + Il2CppException_22_0* _ = (Il2CppException_22_0*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeExceptionStruct Wrap(Il2CppException* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppException_22_0 + { + public Il2CppObject _object; + public Il2CppString* className; + public Il2CppString* message; + public Il2CppObject* _data; + public Il2CppException* inner_ex; + public Il2CppString* _helpURL; + public void* trace_ips; + public Il2CppString* stack_trace; + public Il2CppString* remote_stack_trace; + public int remote_stack_index; + public Il2CppObject* _dynamicMethods; + public int hresult; + public Il2CppString* source; + public Il2CppObject* safeSerializationManager; + public void* captured_traces; + public void* native_trace_ips; + } + internal class NativeStructWrapper : INativeExceptionStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } + private Il2CppException_22_0* _ => (Il2CppException_22_0*)Pointer; + public Il2CppException* ExceptionPointer => (Il2CppException*)Pointer; + public ref Il2CppException* InnerException => ref _->inner_ex; + public ref Il2CppString* Message => ref _->message; + public ref Il2CppString* HelpLink => ref _->_helpURL; + public ref Il2CppString* ClassName => ref _->className; + public ref Il2CppString* StackTrace => ref _->stack_trace; + public ref Il2CppString* RemoteStackTrace => ref _->remote_stack_trace; + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_24_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_23_0.cs similarity index 74% rename from Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_24_0.cs rename to Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_23_0.cs index 9ed8b731..888297e0 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_24_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_23_0.cs @@ -1,24 +1,23 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Exception { - [ApplicableToUnityVersionsSince("2019.3.0")] - public unsafe class NativeExceptionStructHandler_24_0 : INativeExceptionStructHandler + [ApplicableToUnityVersionsSince("5.6.0b3")] + public unsafe class NativeExceptionStructHandler_23_0 : INativeExceptionStructHandler { - public int Size() => sizeof(Il2CppException_24_0); + public int Size() => sizeof(Il2CppException_23_0); public INativeExceptionStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); - Il2CppException_24_0* _ = (Il2CppException_24_0*)ptr; + nint ptr = Marshal.AllocHGlobal(Size()); + Il2CppException_23_0* _ = (Il2CppException_23_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); } public INativeExceptionStruct Wrap(Il2CppException* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } - internal unsafe struct Il2CppException_24_0 + internal unsafe struct Il2CppException_23_0 { public Il2CppObject _object; public Il2CppString* className; @@ -37,12 +36,11 @@ internal unsafe struct Il2CppException_24_0 public void* captured_traces; public void* native_trace_ips; } - internal class NativeStructWrapper : INativeExceptionStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } - private Il2CppException_24_0* _ => (Il2CppException_24_0*)Pointer; + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } + private Il2CppException_23_0* _ => (Il2CppException_23_0*)Pointer; public Il2CppException* ExceptionPointer => (Il2CppException*)Pointer; public ref Il2CppException* InnerException => ref _->inner_ex; public ref Il2CppString* Message => ref _->message; @@ -51,7 +49,5 @@ internal class NativeStructWrapper : INativeExceptionStruct public ref Il2CppString* StackTrace => ref _->stack_trace; public ref Il2CppString* RemoteStackTrace => ref _->remote_stack_trace; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_23_1.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_23_1.cs new file mode 100644 index 00000000..23aa7ab2 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_23_1.cs @@ -0,0 +1,53 @@ +using System.Runtime.InteropServices; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Exception +{ + [ApplicableToUnityVersionsSince("5.6.0b6")] + public unsafe class NativeExceptionStructHandler_23_1 : INativeExceptionStructHandler + { + public int Size() => sizeof(Il2CppException_23_1); + public INativeExceptionStruct CreateNewStruct() + { + nint ptr = Marshal.AllocHGlobal(Size()); + Il2CppException_23_1* _ = (Il2CppException_23_1*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeExceptionStruct Wrap(Il2CppException* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppException_23_1 + { + public Il2CppObject Object; + public Il2CppString* className; + public Il2CppString* message; + public Il2CppObject* _data; + public Il2CppException* inner_ex; + public Il2CppString* _helpURL; + public void* trace_ips; + public Il2CppString* stack_trace; + public Il2CppString* remote_stack_trace; + public int remote_stack_index; + public Il2CppObject* _dynamicMethods; + public il2cpp_hresult_t hresult; + public Il2CppString* source; + public Il2CppObject* safeSerializationManager; + public void* captured_traces; + public void* native_trace_ips; + } + internal class NativeStructWrapper : INativeExceptionStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } + private Il2CppException_23_1* _ => (Il2CppException_23_1*)Pointer; + public Il2CppException* ExceptionPointer => (Il2CppException*)Pointer; + public ref Il2CppException* InnerException => ref _->inner_ex; + public ref Il2CppString* Message => ref _->message; + public ref Il2CppString* HelpLink => ref _->_helpURL; + public ref Il2CppString* ClassName => ref _->className; + public ref Il2CppString* StackTrace => ref _->stack_trace; + public ref Il2CppString* RemoteStackTrace => ref _->remote_stack_trace; + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_29_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_27_0.cs similarity index 74% rename from Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_29_0.cs rename to Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_27_0.cs index e1bc326a..7f386251 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_29_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Exception_27_0.cs @@ -1,24 +1,23 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Exception { - [ApplicableToUnityVersionsSince("2021.2.0")] - public unsafe class NativeExceptionStructHandler_29_0 : INativeExceptionStructHandler + [ApplicableToUnityVersionsSince("2021.2.0a19")] + public unsafe class NativeExceptionStructHandler_27_0 : INativeExceptionStructHandler { - public int Size() => sizeof(Il2CppException_29_0); + public int Size() => sizeof(Il2CppException_27_0); public INativeExceptionStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); - Il2CppException_29_0* _ = (Il2CppException_29_0*)ptr; + nint ptr = Marshal.AllocHGlobal(Size()); + Il2CppException_27_0* _ = (Il2CppException_27_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); } public INativeExceptionStruct Wrap(Il2CppException* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } - internal unsafe struct Il2CppException_29_0 + internal unsafe struct Il2CppException_27_0 { public Il2CppObject _object; public Il2CppString* className; @@ -38,12 +37,11 @@ internal unsafe struct Il2CppException_29_0 public void* native_trace_ips; public int caught_in_unmanaged; } - internal class NativeStructWrapper : INativeExceptionStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } - private Il2CppException_29_0* _ => (Il2CppException_29_0*)Pointer; + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } + private Il2CppException_27_0* _ => (Il2CppException_27_0*)Pointer; public Il2CppException* ExceptionPointer => (Il2CppException*)Pointer; public ref Il2CppException* InnerException => ref _->inner_ex; public ref Il2CppString* Message => ref _->message; @@ -52,7 +50,5 @@ internal class NativeStructWrapper : INativeExceptionStruct public ref Il2CppString* StackTrace => ref _->stack_trace; public ref Il2CppString* RemoteStackTrace => ref _->remote_stack_trace; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Interfaces.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Interfaces.cs index 16bc3b45..43d382f3 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Interfaces.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Exception/Interfaces.cs @@ -1,24 +1,25 @@ -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Exception; - -public interface INativeExceptionStructHandler : INativeStructHandler +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Exception { - INativeExceptionStruct CreateNewStruct(); - unsafe INativeExceptionStruct Wrap(Il2CppException* exceptionPointer); -} + public interface INativeExceptionStructHandler : INativeStructHandler + { + INativeExceptionStruct CreateNewStruct(); + unsafe INativeExceptionStruct Wrap(Il2CppException* exceptionPointer); + } -public interface INativeExceptionStruct : INativeStruct -{ - unsafe Il2CppException* ExceptionPointer { get; } + public interface INativeExceptionStruct : INativeStruct + { + unsafe Il2CppException* ExceptionPointer { get; } - unsafe ref Il2CppException* InnerException { get; } + unsafe ref Il2CppException* InnerException { get; } - unsafe ref Il2CppString* Message { get; } + unsafe ref Il2CppString* Message { get; } - unsafe ref Il2CppString* HelpLink { get; } + unsafe ref Il2CppString* HelpLink { get; } - unsafe ref Il2CppString* ClassName { get; } + unsafe ref Il2CppString* ClassName { get; } - unsafe ref Il2CppString* StackTrace { get; } + unsafe ref Il2CppString* StackTrace { get; } - unsafe ref Il2CppString* RemoteStackTrace { get; } + unsafe ref Il2CppString* RemoteStackTrace { get; } + } } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/FieldInfo_16_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/FieldInfo_16_0.cs index 9cdfd89f..be5ef660 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/FieldInfo_16_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/FieldInfo_16_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.FieldInfo { @@ -8,7 +7,7 @@ public unsafe class NativeFieldInfoStructHandler_16_0 : INativeFieldInfoStructHa public int Size() => sizeof(Il2CppFieldInfo_16_0); public INativeFieldInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppFieldInfo_16_0* _ = (Il2CppFieldInfo_16_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeFieldInfoStruct CreateNewStruct() public INativeFieldInfoStruct Wrap(Il2CppFieldInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppFieldInfo_16_0 { @@ -26,19 +25,16 @@ internal unsafe struct Il2CppFieldInfo_16_0 public int offset; public int customAttributeIndex; } - internal class NativeStructWrapper : INativeFieldInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppFieldInfo_16_0* _ => (Il2CppFieldInfo_16_0*)Pointer; public Il2CppFieldInfo* FieldInfoPointer => (Il2CppFieldInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref Il2CppTypeStruct* Type => ref _->type; public ref Il2CppClass* Parent => ref _->parent; public ref int Offset => ref _->offset; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/FieldInfo_19_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/FieldInfo_19_0.cs index b84d6d4e..d75ad208 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/FieldInfo_19_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/FieldInfo_19_0.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.FieldInfo { - [ApplicableToUnityVersionsSince("5.3.2")] + [ApplicableToUnityVersionsSince("5.3.1p3")] public unsafe class NativeFieldInfoStructHandler_19_0 : INativeFieldInfoStructHandler { public int Size() => sizeof(Il2CppFieldInfo_19_0); public INativeFieldInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppFieldInfo_19_0* _ = (Il2CppFieldInfo_19_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeFieldInfoStruct CreateNewStruct() public INativeFieldInfoStruct Wrap(Il2CppFieldInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppFieldInfo_19_0 { @@ -27,19 +26,16 @@ internal unsafe struct Il2CppFieldInfo_19_0 public int customAttributeIndex; public uint token; } - internal class NativeStructWrapper : INativeFieldInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppFieldInfo_19_0* _ => (Il2CppFieldInfo_19_0*)Pointer; public Il2CppFieldInfo* FieldInfoPointer => (Il2CppFieldInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref Il2CppTypeStruct* Type => ref _->type; public ref Il2CppClass* Parent => ref _->parent; public ref int Offset => ref _->offset; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/FieldInfo_24_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/FieldInfo_24_0.cs index f94f88e6..6399c135 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/FieldInfo_24_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/FieldInfo_24_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.FieldInfo { @@ -8,7 +7,7 @@ public unsafe class NativeFieldInfoStructHandler_24_0 : INativeFieldInfoStructHa public int Size() => sizeof(Il2CppFieldInfo_24_0); public INativeFieldInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppFieldInfo_24_0* _ = (Il2CppFieldInfo_24_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeFieldInfoStruct CreateNewStruct() public INativeFieldInfoStruct Wrap(Il2CppFieldInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppFieldInfo_24_0 { @@ -26,19 +25,16 @@ internal unsafe struct Il2CppFieldInfo_24_0 public int offset; public uint token; } - internal class NativeStructWrapper : INativeFieldInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppFieldInfo_24_0* _ => (Il2CppFieldInfo_24_0*)Pointer; public Il2CppFieldInfo* FieldInfoPointer => (Il2CppFieldInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref Il2CppTypeStruct* Type => ref _->type; public ref Il2CppClass* Parent => ref _->parent; public ref int Offset => ref _->offset; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/Interfaces.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/Interfaces.cs index 03a60831..6a22da01 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/Interfaces.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/FieldInfo/Interfaces.cs @@ -1,22 +1,23 @@ using System; -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.FieldInfo; - -public interface INativeFieldInfoStructHandler : INativeStructHandler +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.FieldInfo { - INativeFieldInfoStruct CreateNewStruct(); - unsafe INativeFieldInfoStruct Wrap(Il2CppFieldInfo* fieldInfoPointer); -} + public interface INativeFieldInfoStructHandler : INativeStructHandler + { + INativeFieldInfoStruct CreateNewStruct(); + unsafe INativeFieldInfoStruct Wrap(Il2CppFieldInfo* fieldInfoPointer); + } -public interface INativeFieldInfoStruct : INativeStruct -{ - unsafe Il2CppFieldInfo* FieldInfoPointer { get; } + public interface INativeFieldInfoStruct : INativeStruct + { + unsafe Il2CppFieldInfo* FieldInfoPointer { get; } - ref IntPtr Name { get; } + ref IntPtr Name { get; } - unsafe ref Il2CppTypeStruct* Type { get; } + unsafe ref Il2CppTypeStruct* Type { get; } - unsafe ref Il2CppClass* Parent { get; } + unsafe ref Il2CppClass* Parent { get; } - ref int Offset { get; } + ref int Offset { get; } + } } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_16_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_16_0.cs index aefc0cf6..1878e284 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_16_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_16_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Image { @@ -8,7 +7,7 @@ public unsafe class NativeImageStructHandler_16_0 : INativeImageStructHandler public int Size() => sizeof(Il2CppImage_16_0); public INativeImageStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppImage_16_0* _ = (Il2CppImage_16_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeImageStruct CreateNewStruct() public INativeImageStruct Wrap(Il2CppImage* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppImage_16_0 { @@ -27,21 +26,18 @@ internal unsafe struct Il2CppImage_16_0 public int entryPointIndex; public void* nameToClassHashTable; } - internal class NativeStructWrapper : INativeImageStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private byte _dynamicDummy; - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppImage_16_0* _ => (Il2CppImage_16_0*)Pointer; public Il2CppImage* ImagePointer => (Il2CppImage*)Pointer; public bool HasNameNoExt => false; - public ref Il2CppAssembly* Assembly => throw new NotSupportedException(); + public ref Il2CppAssembly* Assembly => throw new System.NotSupportedException(); public ref byte Dynamic => ref _dynamicDummy; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr NameNoExt => throw new NotSupportedException(); + public ref nint Name => ref *(nint*)&_->name; + public ref nint NameNoExt => throw new System.NotSupportedException(); } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_19_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_19_0.cs index 335ce2f9..886b2f9e 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_19_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_19_0.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Image { - [ApplicableToUnityVersionsSince("5.3.2")] + [ApplicableToUnityVersionsSince("5.3.1p3")] public unsafe class NativeImageStructHandler_19_0 : INativeImageStructHandler { public int Size() => sizeof(Il2CppImage_19_0); public INativeImageStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppImage_19_0* _ = (Il2CppImage_19_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeImageStruct CreateNewStruct() public INativeImageStruct Wrap(Il2CppImage* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppImage_19_0 { @@ -28,21 +27,18 @@ internal unsafe struct Il2CppImage_19_0 public void* nameToClassHashTable; public uint token; } - internal class NativeStructWrapper : INativeImageStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private byte _dynamicDummy; - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppImage_19_0* _ => (Il2CppImage_19_0*)Pointer; public Il2CppImage* ImagePointer => (Il2CppImage*)Pointer; public bool HasNameNoExt => false; - public ref Il2CppAssembly* Assembly => throw new NotSupportedException(); + public ref Il2CppAssembly* Assembly => throw new System.NotSupportedException(); public ref byte Dynamic => ref _dynamicDummy; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr NameNoExt => throw new NotSupportedException(); + public ref nint Name => ref *(nint*)&_->name; + public ref nint NameNoExt => throw new System.NotSupportedException(); } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_0.cs index 002c3f76..078eb1bb 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Image { @@ -8,7 +7,7 @@ public unsafe class NativeImageStructHandler_24_0 : INativeImageStructHandler public int Size() => sizeof(Il2CppImage_24_0); public INativeImageStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppImage_24_0* _ = (Il2CppImage_24_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeImageStruct CreateNewStruct() public INativeImageStruct Wrap(Il2CppImage* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppImage_24_0 { @@ -30,21 +29,18 @@ internal unsafe struct Il2CppImage_24_0 public void* nameToClassHashTable; public uint token; } - internal class NativeStructWrapper : INativeImageStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private byte _dynamicDummy; - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppImage_24_0* _ => (Il2CppImage_24_0*)Pointer; public Il2CppImage* ImagePointer => (Il2CppImage*)Pointer; public bool HasNameNoExt => false; - public ref Il2CppAssembly* Assembly => throw new NotSupportedException(); + public ref Il2CppAssembly* Assembly => throw new System.NotSupportedException(); public ref byte Dynamic => ref _dynamicDummy; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr NameNoExt => throw new NotSupportedException(); + public ref nint Name => ref *(nint*)&_->name; + public ref nint NameNoExt => throw new System.NotSupportedException(); } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_1.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_1.cs index 583b1558..8ccd689a 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_1.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_1.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Image { - [ApplicableToUnityVersionsSince("2017.1.3")] + [ApplicableToUnityVersionsSince("2017.1.2p1")] public unsafe class NativeImageStructHandler_24_1 : INativeImageStructHandler { public int Size() => sizeof(Il2CppImage_24_1); public INativeImageStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppImage_24_1* _ = (Il2CppImage_24_1*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeImageStruct CreateNewStruct() public INativeImageStruct Wrap(Il2CppImage* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppImage_24_1 { @@ -31,21 +30,18 @@ internal unsafe struct Il2CppImage_24_1 public void* nameToClassHashTable; public uint token; } - internal class NativeStructWrapper : INativeImageStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private byte _dynamicDummy; - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppImage_24_1* _ => (Il2CppImage_24_1*)Pointer; public Il2CppImage* ImagePointer => (Il2CppImage*)Pointer; public bool HasNameNoExt => true; - public ref Il2CppAssembly* Assembly => throw new NotSupportedException(); + public ref Il2CppAssembly* Assembly => throw new System.NotSupportedException(); public ref byte Dynamic => ref _dynamicDummy; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr NameNoExt => ref *(IntPtr*)&_->nameNoExt; + public ref nint Name => ref *(nint*)&_->name; + public ref nint NameNoExt => ref *(nint*)&_->nameNoExt; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_2.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_2.cs index 7f13bf7c..eaab898e 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_2.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_2.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Image { @@ -8,7 +7,7 @@ public unsafe class NativeImageStructHandler_24_2 : INativeImageStructHandler public int Size() => sizeof(Il2CppImage_24_2); public INativeImageStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppImage_24_2* _ = (Il2CppImage_24_2*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeImageStruct CreateNewStruct() public INativeImageStruct Wrap(Il2CppImage* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppImage_24_2 { @@ -30,22 +29,19 @@ internal unsafe struct Il2CppImage_24_2 public int entryPointIndex; public void* nameToClassHashTable; public uint token; - public byte dynamic; } - internal class NativeStructWrapper : INativeImageStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private byte _dynamicDummy; + public nint Pointer { get; } private Il2CppImage_24_2* _ => (Il2CppImage_24_2*)Pointer; public Il2CppImage* ImagePointer => (Il2CppImage*)Pointer; public bool HasNameNoExt => true; public ref Il2CppAssembly* Assembly => ref _->assembly; - public ref byte Dynamic => ref _->dynamic; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr NameNoExt => ref *(IntPtr*)&_->nameNoExt; + public ref byte Dynamic => ref _dynamicDummy; + public ref nint Name => ref *(nint*)&_->name; + public ref nint NameNoExt => ref *(nint*)&_->nameNoExt; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_3.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_3.cs index 0c761322..0f5218cc 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_3.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_3.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Image { - [ApplicableToUnityVersionsSince("2018.3.0")] + [ApplicableToUnityVersionsSince("2018.1.0b5")] public unsafe class NativeImageStructHandler_24_3 : INativeImageStructHandler { public int Size() => sizeof(Il2CppImage_24_3); public INativeImageStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppImage_24_3* _ = (Il2CppImage_24_3*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeImageStruct CreateNewStruct() public INativeImageStruct Wrap(Il2CppImage* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppImage_24_3 { @@ -27,27 +26,22 @@ internal unsafe struct Il2CppImage_24_3 public uint typeCount; public int exportedTypeStart; public uint exportedTypeCount; - public int customAttributeStart; - public uint customAttributeCount; public int entryPointIndex; public void* nameToClassHashTable; public uint token; public byte dynamic; } - internal class NativeStructWrapper : INativeImageStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppImage_24_3* _ => (Il2CppImage_24_3*)Pointer; public Il2CppImage* ImagePointer => (Il2CppImage*)Pointer; public bool HasNameNoExt => true; public ref Il2CppAssembly* Assembly => ref _->assembly; public ref byte Dynamic => ref _->dynamic; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr NameNoExt => ref *(IntPtr*)&_->nameNoExt; + public ref nint Name => ref *(nint*)&_->name; + public ref nint NameNoExt => ref *(nint*)&_->nameNoExt; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_4.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_4.cs index 76647007..ffbe16d1 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_4.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_4.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Image { - [ApplicableToUnityVersionsSince("2019.1.0")] + [ApplicableToUnityVersionsSince("2018.3.0")] public unsafe class NativeImageStructHandler_24_4 : INativeImageStructHandler { public int Size() => sizeof(Il2CppImage_24_4); public INativeImageStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppImage_24_4* _ = (Il2CppImage_24_4*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeImageStruct CreateNewStruct() public INativeImageStruct Wrap(Il2CppImage* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppImage_24_4 { @@ -31,24 +30,20 @@ internal unsafe struct Il2CppImage_24_4 public uint customAttributeCount; public int entryPointIndex; public void* nameToClassHashTable; - public void* codeGenModule; public uint token; public byte dynamic; } - internal class NativeStructWrapper : INativeImageStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppImage_24_4* _ => (Il2CppImage_24_4*)Pointer; public Il2CppImage* ImagePointer => (Il2CppImage*)Pointer; public bool HasNameNoExt => true; public ref Il2CppAssembly* Assembly => ref _->assembly; public ref byte Dynamic => ref _->dynamic; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr NameNoExt => ref *(IntPtr*)&_->nameNoExt; + public ref nint Name => ref *(nint*)&_->name; + public ref nint NameNoExt => ref *(nint*)&_->nameNoExt; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_5.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_5.cs new file mode 100644 index 00000000..6870247c --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_5.cs @@ -0,0 +1,50 @@ +using System.Runtime.InteropServices; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Image +{ + [ApplicableToUnityVersionsSince("2019.1.0a12")] + public unsafe class NativeImageStructHandler_24_5 : INativeImageStructHandler + { + public int Size() => sizeof(Il2CppImage_24_5); + public INativeImageStruct CreateNewStruct() + { + nint ptr = Marshal.AllocHGlobal(Size()); + Il2CppImage_24_5* _ = (Il2CppImage_24_5*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeImageStruct Wrap(Il2CppImage* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppImage_24_5 + { + public byte* name; + public byte* nameNoExt; + public Il2CppAssembly* assembly; + public int typeStart; + public uint typeCount; + public int exportedTypeStart; + public uint exportedTypeCount; + public int customAttributeStart; + public uint customAttributeCount; + public int entryPointIndex; + public void* nameToClassHashTable; + public void* codeGenModule; + public uint token; + public byte dynamic; + } + internal class NativeStructWrapper : INativeImageStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } + private Il2CppImage_24_5* _ => (Il2CppImage_24_5*)Pointer; + public Il2CppImage* ImagePointer => (Il2CppImage*)Pointer; + public bool HasNameNoExt => true; + public ref Il2CppAssembly* Assembly => ref _->assembly; + public ref byte Dynamic => ref _->dynamic; + public ref nint Name => ref *(nint*)&_->name; + public ref nint NameNoExt => ref *(nint*)&_->nameNoExt; + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_27_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_6.cs similarity index 69% rename from Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_27_0.cs rename to Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_6.cs index 80d72a3a..4f2bd1b2 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_27_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Image_24_6.cs @@ -1,15 +1,14 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Image { [ApplicableToUnityVersionsSince("2020.2.0")] - public unsafe class NativeImageStructHandler_27_0 : INativeImageStructHandler + public unsafe class NativeImageStructHandler_24_6 : INativeImageStructHandler { - public int Size() => sizeof(Il2CppImage_27_0); + public int Size() => sizeof(Il2CppImage_24_6); public INativeImageStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); - Il2CppImage_27_0* _ = (Il2CppImage_27_0*)ptr; + nint ptr = Marshal.AllocHGlobal(Size()); + Il2CppImage_24_6* _ = (Il2CppImage_24_6*)ptr; *_ = default; Il2CppImageGlobalMetadata* metadata = (Il2CppImageGlobalMetadata*)Marshal.AllocHGlobal(sizeof(Il2CppImageGlobalMetadata)); metadata->image = (Il2CppImage*)_; @@ -19,9 +18,9 @@ public INativeImageStruct CreateNewStruct() public INativeImageStruct Wrap(Il2CppImage* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } - internal unsafe struct Il2CppImage_27_0 + internal unsafe struct Il2CppImage_24_6 { public byte* name; public byte* nameNoExt; @@ -35,20 +34,17 @@ internal unsafe struct Il2CppImage_27_0 public uint token; public byte dynamic; } - internal class NativeStructWrapper : INativeImageStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } - private Il2CppImage_27_0* _ => (Il2CppImage_27_0*)Pointer; + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } + private Il2CppImage_24_6* _ => (Il2CppImage_24_6*)Pointer; public Il2CppImage* ImagePointer => (Il2CppImage*)Pointer; public bool HasNameNoExt => true; public ref Il2CppAssembly* Assembly => ref _->assembly; public ref byte Dynamic => ref _->dynamic; - public ref IntPtr Name => ref *(IntPtr*)&_->name; - public ref IntPtr NameNoExt => ref *(IntPtr*)&_->nameNoExt; + public ref nint Name => ref *(nint*)&_->name; + public ref nint NameNoExt => ref *(nint*)&_->nameNoExt; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Interfaces.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Interfaces.cs index a0d6265b..bfe927d3 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Interfaces.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Image/Interfaces.cs @@ -1,24 +1,25 @@ using System; -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Image; - -public interface INativeImageStructHandler : INativeStructHandler +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Image { - INativeImageStruct CreateNewStruct(); - unsafe INativeImageStruct Wrap(Il2CppImage* imagePointer); -} + public interface INativeImageStructHandler : INativeStructHandler + { + INativeImageStruct CreateNewStruct(); + unsafe INativeImageStruct Wrap(Il2CppImage* imagePointer); + } -public interface INativeImageStruct : INativeStruct -{ - unsafe Il2CppImage* ImagePointer { get; } + public interface INativeImageStruct : INativeStruct + { + unsafe Il2CppImage* ImagePointer { get; } - unsafe ref Il2CppAssembly* Assembly { get; } + unsafe ref Il2CppAssembly* Assembly { get; } - ref byte Dynamic { get; } + ref byte Dynamic { get; } - ref IntPtr Name { get; } + ref IntPtr Name { get; } - bool HasNameNoExt { get; } + bool HasNameNoExt { get; } - ref IntPtr NameNoExt { get; } + ref IntPtr NameNoExt { get; } + } } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/Interfaces.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/Interfaces.cs index 8c553178..e1dd504d 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/Interfaces.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/Interfaces.cs @@ -1,29 +1,30 @@ using System; -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo; - -public interface INativeMethodInfoStructHandler : INativeStructHandler +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo { - INativeMethodInfoStruct CreateNewStruct(); - unsafe INativeMethodInfoStruct Wrap(Il2CppMethodInfo* methodPointer); -} + public interface INativeMethodInfoStructHandler : INativeStructHandler + { + INativeMethodInfoStruct CreateNewStruct(); + unsafe INativeMethodInfoStruct Wrap(Il2CppMethodInfo* methodPointer); + } -public interface INativeMethodInfoStruct : INativeStruct -{ - unsafe Il2CppMethodInfo* MethodInfoPointer { get; } - ref IntPtr Name { get; } - ref ushort Slot { get; } - ref IntPtr MethodPointer { get; } + public interface INativeMethodInfoStruct : INativeStruct + { + unsafe Il2CppMethodInfo* MethodInfoPointer { get; } + ref IntPtr Name { get; } + ref ushort Slot { get; } + ref IntPtr MethodPointer { get; } - ref IntPtr VirtualMethodPointer { get; } - unsafe ref Il2CppClass* Class { get; } - ref IntPtr InvokerMethod { get; } - unsafe ref Il2CppTypeStruct* ReturnType { get; } - ref Il2CppMethodFlags Flags { get; } - ref byte ParametersCount { get; } - unsafe ref Il2CppParameterInfo* Parameters { get; } - ref uint Token { get; } - bool IsGeneric { get; set; } - bool IsInflated { get; set; } - bool IsMarshalledFromNative { get; set; } + ref IntPtr VirtualMethodPointer { get; } + unsafe ref Il2CppClass* Class { get; } + ref IntPtr InvokerMethod { get; } + unsafe ref Il2CppTypeStruct* ReturnType { get; } + ref Il2CppMethodFlags Flags { get; } + ref byte ParametersCount { get; } + unsafe ref Il2CppParameterInfo* Parameters { get; } + ref uint Token { get; } + bool IsGeneric { get; set; } + bool IsInflated { get; set; } + bool IsMarshalledFromNative { get; set; } + } } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_16_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_16_0.cs index 3fba3bed..82906fc1 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_16_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_16_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo { @@ -8,7 +7,7 @@ public unsafe class NativeMethodInfoStructHandler_16_0 : INativeMethodInfoStruct public int Size() => sizeof(Il2CppMethodInfo_16_0); public INativeMethodInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppMethodInfo_16_0* _ = (Il2CppMethodInfo_16_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeMethodInfoStruct CreateNewStruct() public INativeMethodInfoStruct Wrap(Il2CppMethodInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppMethodInfo_16_0 { @@ -42,22 +41,20 @@ internal enum Bitfield0 : byte BIT_is_inflated = 1, is_inflated = (1 << BIT_is_inflated), } - } - internal class NativeStructWrapper : INativeMethodInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppMethodInfo_16_0._bitfield0)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppMethodInfo_16_0* _ => (Il2CppMethodInfo_16_0*)Pointer; public Il2CppMethodInfo* MethodInfoPointer => (Il2CppMethodInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref ushort Slot => ref _->slot; - public ref IntPtr MethodPointer => ref *(IntPtr*)&_->method; - public ref IntPtr VirtualMethodPointer => ref *(IntPtr*)&_->method; + public ref nint MethodPointer => ref *(nint*)&_->method; + public ref nint VirtualMethodPointer => ref *(nint*)&_->method; public ref Il2CppClass* Class => ref _->declaring_type; - public ref IntPtr InvokerMethod => ref *(IntPtr*)&_->invoker_method; + public ref nint InvokerMethod => ref *(nint*)&_->invoker_method; public ref Il2CppTypeStruct* ReturnType => ref _->return_type; public ref Il2CppMethodFlags Flags => ref *(Il2CppMethodFlags*)&_->flags; public ref byte ParametersCount => ref _->parameters_count; @@ -79,7 +76,5 @@ public bool IsMarshalledFromNative set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_21_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_21_0.cs index a6fd6b18..e02d3d08 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_21_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_21_0.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo { - [ApplicableToUnityVersionsSince("5.3.6")] + [ApplicableToUnityVersionsSince("5.3.5p3")] public unsafe class NativeMethodInfoStructHandler_21_0 : INativeMethodInfoStructHandler { public int Size() => sizeof(Il2CppMethodInfo_21_0); public INativeMethodInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppMethodInfo_21_0* _ = (Il2CppMethodInfo_21_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeMethodInfoStruct CreateNewStruct() public INativeMethodInfoStruct Wrap(Il2CppMethodInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppMethodInfo_21_0 { @@ -42,22 +41,20 @@ internal enum Bitfield0 : byte BIT_is_inflated = 1, is_inflated = (1 << BIT_is_inflated), } - } - internal class NativeStructWrapper : INativeMethodInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppMethodInfo_21_0._bitfield0)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppMethodInfo_21_0* _ => (Il2CppMethodInfo_21_0*)Pointer; public Il2CppMethodInfo* MethodInfoPointer => (Il2CppMethodInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref ushort Slot => ref _->slot; - public ref IntPtr MethodPointer => ref *(IntPtr*)&_->methodPointer; - public ref IntPtr VirtualMethodPointer => ref *(IntPtr*)&_->methodPointer; + public ref nint MethodPointer => ref *(nint*)&_->methodPointer; + public ref nint VirtualMethodPointer => ref *(nint*)&_->methodPointer; public ref Il2CppClass* Class => ref _->declaring_type; - public ref IntPtr InvokerMethod => ref *(IntPtr*)&_->invoker_method; + public ref nint InvokerMethod => ref *(nint*)&_->invoker_method; public ref Il2CppTypeStruct* ReturnType => ref _->return_type; public ref Il2CppMethodFlags Flags => ref *(Il2CppMethodFlags*)&_->flags; public ref byte ParametersCount => ref _->parameters_count; @@ -79,7 +76,5 @@ public bool IsMarshalledFromNative set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_24_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_24_0.cs index 28828708..2a9814a7 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_24_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_24_0.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo { - [ApplicableToUnityVersionsSince("2018.1.0")] + [ApplicableToUnityVersionsSince("2018.1.0b5")] public unsafe class NativeMethodInfoStructHandler_24_0 : INativeMethodInfoStructHandler { public int Size() => sizeof(Il2CppMethodInfo_24_0); public INativeMethodInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppMethodInfo_24_0* _ = (Il2CppMethodInfo_24_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeMethodInfoStruct CreateNewStruct() public INativeMethodInfoStruct Wrap(Il2CppMethodInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppMethodInfo_24_0 { @@ -43,25 +42,21 @@ internal enum Bitfield0 : byte is_inflated = (1 << BIT_is_inflated), BIT_wrapper_type = 2, wrapper_type = (1 << BIT_wrapper_type), - BIT_is_marshaled_from_native = 3, - is_marshaled_from_native = (1 << BIT_is_marshaled_from_native), } - } - internal class NativeStructWrapper : INativeMethodInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppMethodInfo_24_0._bitfield0)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppMethodInfo_24_0* _ => (Il2CppMethodInfo_24_0*)Pointer; public Il2CppMethodInfo* MethodInfoPointer => (Il2CppMethodInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref ushort Slot => ref _->slot; - public ref IntPtr MethodPointer => ref *(IntPtr*)&_->methodPointer; - public ref IntPtr VirtualMethodPointer => ref *(IntPtr*)&_->methodPointer; + public ref nint MethodPointer => ref *(nint*)&_->methodPointer; + public ref nint VirtualMethodPointer => ref *(nint*)&_->methodPointer; public ref Il2CppClass* Class => ref _->klass; - public ref IntPtr InvokerMethod => ref *(IntPtr*)&_->invoker_method; + public ref nint InvokerMethod => ref *(nint*)&_->invoker_method; public ref Il2CppTypeStruct* ReturnType => ref _->return_type; public ref Il2CppMethodFlags Flags => ref *(Il2CppMethodFlags*)&_->flags; public ref byte ParametersCount => ref _->parameters_count; @@ -79,11 +74,9 @@ public bool IsInflated } public bool IsMarshalledFromNative { - get => this.CheckBit(_bitfield0offset, (int)Il2CppMethodInfo_24_0.Bitfield0.BIT_is_marshaled_from_native); - set => this.SetBit(_bitfield0offset, (int)Il2CppMethodInfo_24_0.Bitfield0.BIT_is_marshaled_from_native, value); + get => false; + set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_24_1.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_24_1.cs index 26d69c24..9ce38ba3 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_24_1.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_24_1.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo { - [ApplicableToUnityVersionsSince("2018.3.0")] + [ApplicableToUnityVersionsSince("2018.1.0f1")] public unsafe class NativeMethodInfoStructHandler_24_1 : INativeMethodInfoStructHandler { public int Size() => sizeof(Il2CppMethodInfo_24_1); public INativeMethodInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppMethodInfo_24_1* _ = (Il2CppMethodInfo_24_1*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeMethodInfoStruct CreateNewStruct() public INativeMethodInfoStruct Wrap(Il2CppMethodInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppMethodInfo_24_1 { @@ -28,6 +27,7 @@ internal unsafe struct Il2CppMethodInfo_24_1 public Il2CppParameterInfo* parameters; public void* runtime_data; public void* generic_data; + public int customAttributeIndex; public uint token; public ushort flags; public ushort iflags; @@ -45,22 +45,20 @@ internal enum Bitfield0 : byte BIT_is_marshaled_from_native = 3, is_marshaled_from_native = (1 << BIT_is_marshaled_from_native), } - } - internal class NativeStructWrapper : INativeMethodInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppMethodInfo_24_1._bitfield0)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppMethodInfo_24_1* _ => (Il2CppMethodInfo_24_1*)Pointer; public Il2CppMethodInfo* MethodInfoPointer => (Il2CppMethodInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref ushort Slot => ref _->slot; - public ref IntPtr MethodPointer => ref *(IntPtr*)&_->methodPointer; - public ref IntPtr VirtualMethodPointer => ref *(IntPtr*)&_->methodPointer; + public ref nint MethodPointer => ref *(nint*)&_->methodPointer; + public ref nint VirtualMethodPointer => ref *(nint*)&_->methodPointer; public ref Il2CppClass* Class => ref _->klass; - public ref IntPtr InvokerMethod => ref *(IntPtr*)&_->invoker_method; + public ref nint InvokerMethod => ref *(nint*)&_->invoker_method; public ref Il2CppTypeStruct* ReturnType => ref _->return_type; public ref Il2CppMethodFlags Flags => ref *(Il2CppMethodFlags*)&_->flags; public ref byte ParametersCount => ref _->parameters_count; @@ -82,7 +80,5 @@ public bool IsMarshalledFromNative set => this.SetBit(_bitfield0offset, (int)Il2CppMethodInfo_24_1.Bitfield0.BIT_is_marshaled_from_native, value); } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_24_2.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_24_2.cs new file mode 100644 index 00000000..b192a0fc --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_24_2.cs @@ -0,0 +1,83 @@ +using System.Runtime.InteropServices; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo +{ + [ApplicableToUnityVersionsSince("2018.3.0")] + public unsafe class NativeMethodInfoStructHandler_24_2 : INativeMethodInfoStructHandler + { + public int Size() => sizeof(Il2CppMethodInfo_24_2); + public INativeMethodInfoStruct CreateNewStruct() + { + nint ptr = Marshal.AllocHGlobal(Size()); + Il2CppMethodInfo_24_2* _ = (Il2CppMethodInfo_24_2*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeMethodInfoStruct Wrap(Il2CppMethodInfo* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppMethodInfo_24_2 + { + public void* methodPointer; + public void* invoker_method; + public byte* name; + public Il2CppClass* klass; + public Il2CppTypeStruct* return_type; + public Il2CppParameterInfo* parameters; + public void* runtime_data; + public void* generic_data; + public uint token; + public ushort flags; + public ushort iflags; + public ushort slot; + public byte parameters_count; + public Bitfield0 _bitfield0; + internal enum Bitfield0 : byte + { + BIT_is_generic = 0, + is_generic = (1 << BIT_is_generic), + BIT_is_inflated = 1, + is_inflated = (1 << BIT_is_inflated), + BIT_wrapper_type = 2, + wrapper_type = (1 << BIT_wrapper_type), + BIT_is_marshaled_from_native = 3, + is_marshaled_from_native = (1 << BIT_is_marshaled_from_native), + } + } + internal class NativeStructWrapper : INativeMethodInfoStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppMethodInfo_24_2._bitfield0)).ToInt32(); + public nint Pointer { get; } + private Il2CppMethodInfo_24_2* _ => (Il2CppMethodInfo_24_2*)Pointer; + public Il2CppMethodInfo* MethodInfoPointer => (Il2CppMethodInfo*)Pointer; + public ref nint Name => ref *(nint*)&_->name; + public ref ushort Slot => ref _->slot; + public ref nint MethodPointer => ref *(nint*)&_->methodPointer; + public ref nint VirtualMethodPointer => ref *(nint*)&_->methodPointer; + public ref Il2CppClass* Class => ref _->klass; + public ref nint InvokerMethod => ref *(nint*)&_->invoker_method; + public ref Il2CppTypeStruct* ReturnType => ref _->return_type; + public ref Il2CppMethodFlags Flags => ref *(Il2CppMethodFlags*)&_->flags; + public ref byte ParametersCount => ref _->parameters_count; + public ref Il2CppParameterInfo* Parameters => ref _->parameters; + public ref uint Token => ref _->token; + public bool IsGeneric + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppMethodInfo_24_2.Bitfield0.BIT_is_generic); + set => this.SetBit(_bitfield0offset, (int)Il2CppMethodInfo_24_2.Bitfield0.BIT_is_generic, value); + } + public bool IsInflated + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppMethodInfo_24_2.Bitfield0.BIT_is_inflated); + set => this.SetBit(_bitfield0offset, (int)Il2CppMethodInfo_24_2.Bitfield0.BIT_is_inflated, value); + } + public bool IsMarshalledFromNative + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppMethodInfo_24_2.Bitfield0.BIT_is_marshaled_from_native); + set => this.SetBit(_bitfield0offset, (int)Il2CppMethodInfo_24_2.Bitfield0.BIT_is_marshaled_from_native, value); + } + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_27_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_27_0.cs new file mode 100644 index 00000000..690efa45 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_27_0.cs @@ -0,0 +1,84 @@ +using System.Runtime.InteropServices; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo +{ + [ApplicableToUnityVersionsSince("2021.2.0")] + public unsafe class NativeMethodInfoStructHandler_27_0 : INativeMethodInfoStructHandler + { + public int Size() => sizeof(Il2CppMethodInfo_27_0); + public INativeMethodInfoStruct CreateNewStruct() + { + nint ptr = Marshal.AllocHGlobal(Size()); + Il2CppMethodInfo_27_0* _ = (Il2CppMethodInfo_27_0*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeMethodInfoStruct Wrap(Il2CppMethodInfo* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppMethodInfo_27_0 + { + public void* methodPointer; + public void* virtualMethodPointer; + public void* invoker_method; + public byte* name; + public Il2CppClass* klass; + public Il2CppTypeStruct* return_type; + public Il2CppParameterInfo* parameters; + public void* runtime_data; + public void* generic_data; + public uint token; + public ushort flags; + public ushort iflags; + public ushort slot; + public byte parameters_count; + public Bitfield0 _bitfield0; + internal enum Bitfield0 : byte + { + BIT_is_generic = 0, + is_generic = (1 << BIT_is_generic), + BIT_is_inflated = 1, + is_inflated = (1 << BIT_is_inflated), + BIT_wrapper_type = 2, + wrapper_type = (1 << BIT_wrapper_type), + BIT_is_marshaled_from_native = 3, + is_marshaled_from_native = (1 << BIT_is_marshaled_from_native), + } + } + internal class NativeStructWrapper : INativeMethodInfoStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppMethodInfo_27_0._bitfield0)).ToInt32(); + public nint Pointer { get; } + private Il2CppMethodInfo_27_0* _ => (Il2CppMethodInfo_27_0*)Pointer; + public Il2CppMethodInfo* MethodInfoPointer => (Il2CppMethodInfo*)Pointer; + public ref nint Name => ref *(nint*)&_->name; + public ref ushort Slot => ref _->slot; + public ref nint MethodPointer => ref *(nint*)&_->methodPointer; + public ref nint VirtualMethodPointer => ref *(nint*)&_->virtualMethodPointer; + public ref Il2CppClass* Class => ref _->klass; + public ref nint InvokerMethod => ref *(nint*)&_->invoker_method; + public ref Il2CppTypeStruct* ReturnType => ref _->return_type; + public ref Il2CppMethodFlags Flags => ref *(Il2CppMethodFlags*)&_->flags; + public ref byte ParametersCount => ref _->parameters_count; + public ref Il2CppParameterInfo* Parameters => ref _->parameters; + public ref uint Token => ref _->token; + public bool IsGeneric + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppMethodInfo_27_0.Bitfield0.BIT_is_generic); + set => this.SetBit(_bitfield0offset, (int)Il2CppMethodInfo_27_0.Bitfield0.BIT_is_generic, value); + } + public bool IsInflated + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppMethodInfo_27_0.Bitfield0.BIT_is_inflated); + set => this.SetBit(_bitfield0offset, (int)Il2CppMethodInfo_27_0.Bitfield0.BIT_is_inflated, value); + } + public bool IsMarshalledFromNative + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppMethodInfo_27_0.Bitfield0.BIT_is_marshaled_from_native); + set => this.SetBit(_bitfield0offset, (int)Il2CppMethodInfo_27_0.Bitfield0.BIT_is_marshaled_from_native, value); + } + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_28_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_28_0.cs new file mode 100644 index 00000000..99999e1b --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_28_0.cs @@ -0,0 +1,82 @@ +using System.Runtime.InteropServices; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo +{ + [ApplicableToUnityVersionsSince("2021.2.0b7")] + public unsafe class NativeMethodInfoStructHandler_28_0 : INativeMethodInfoStructHandler + { + public int Size() => sizeof(Il2CppMethodInfo_28_0); + public INativeMethodInfoStruct CreateNewStruct() + { + nint ptr = Marshal.AllocHGlobal(Size()); + Il2CppMethodInfo_28_0* _ = (Il2CppMethodInfo_28_0*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeMethodInfoStruct Wrap(Il2CppMethodInfo* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppMethodInfo_28_0 + { + public void* methodPointer; + public void* virtualMethodPointer; + public void* invoker_method; + public byte* name; + public Il2CppClass* klass; + public Il2CppTypeStruct* return_type; + public Il2CppParameterInfo* parameters; + public void* runtime_data; + public void* generic_data; + public uint token; + public ushort flags; + public ushort iflags; + public ushort slot; + public byte parameters_count; + public Bitfield0 _bitfield0; + internal enum Bitfield0 : byte + { + BIT_is_generic = 0, + is_generic = (1 << BIT_is_generic), + BIT_is_inflated = 1, + is_inflated = (1 << BIT_is_inflated), + BIT_wrapper_type = 2, + wrapper_type = (1 << BIT_wrapper_type), + } + } + internal class NativeStructWrapper : INativeMethodInfoStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppMethodInfo_28_0._bitfield0)).ToInt32(); + public nint Pointer { get; } + private Il2CppMethodInfo_28_0* _ => (Il2CppMethodInfo_28_0*)Pointer; + public Il2CppMethodInfo* MethodInfoPointer => (Il2CppMethodInfo*)Pointer; + public ref nint Name => ref *(nint*)&_->name; + public ref ushort Slot => ref _->slot; + public ref nint MethodPointer => ref *(nint*)&_->methodPointer; + public ref nint VirtualMethodPointer => ref *(nint*)&_->virtualMethodPointer; + public ref Il2CppClass* Class => ref _->klass; + public ref nint InvokerMethod => ref *(nint*)&_->invoker_method; + public ref Il2CppTypeStruct* ReturnType => ref _->return_type; + public ref Il2CppMethodFlags Flags => ref *(Il2CppMethodFlags*)&_->flags; + public ref byte ParametersCount => ref _->parameters_count; + public ref Il2CppParameterInfo* Parameters => ref _->parameters; + public ref uint Token => ref _->token; + public bool IsGeneric + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppMethodInfo_28_0.Bitfield0.BIT_is_generic); + set => this.SetBit(_bitfield0offset, (int)Il2CppMethodInfo_28_0.Bitfield0.BIT_is_generic, value); + } + public bool IsInflated + { + get => this.CheckBit(_bitfield0offset, (int)Il2CppMethodInfo_28_0.Bitfield0.BIT_is_inflated); + set => this.SetBit(_bitfield0offset, (int)Il2CppMethodInfo_28_0.Bitfield0.BIT_is_inflated, value); + } + public bool IsMarshalledFromNative + { + get => false; + set { } + } + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_29_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_29_0.cs index 5c1e50d0..78c78695 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_29_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_29_0.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo { - [ApplicableToUnityVersionsSince("2021.2.0")] + [ApplicableToUnityVersionsSince("2021.2.0b8")] public unsafe class NativeMethodInfoStructHandler_29_0 : INativeMethodInfoStructHandler { public int Size() => sizeof(Il2CppMethodInfo_29_0); public INativeMethodInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppMethodInfo_29_0* _ = (Il2CppMethodInfo_29_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeMethodInfoStruct CreateNewStruct() public INativeMethodInfoStruct Wrap(Il2CppMethodInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppMethodInfo_29_0 { @@ -26,7 +25,7 @@ internal unsafe struct Il2CppMethodInfo_29_0 public byte* name; public Il2CppClass* klass; public Il2CppTypeStruct* return_type; - public Il2CppTypeStruct** parameters; + public Il2CppParameterInfo* parameters; public void* runtime_data; public void* generic_data; public uint token; @@ -45,29 +44,25 @@ internal enum Bitfield0 : byte wrapper_type = (1 << BIT_wrapper_type), BIT_has_full_generic_sharing_signature = 3, has_full_generic_sharing_signature = (1 << BIT_has_full_generic_sharing_signature), - BIT_indirect_call_via_invokers = 4, - indirect_call_via_invokers = (1 << BIT_indirect_call_via_invokers), } - } - internal class NativeStructWrapper : INativeMethodInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppMethodInfo_29_0._bitfield0)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppMethodInfo_29_0* _ => (Il2CppMethodInfo_29_0*)Pointer; public Il2CppMethodInfo* MethodInfoPointer => (Il2CppMethodInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref ushort Slot => ref _->slot; - public ref IntPtr MethodPointer => ref *(IntPtr*)&_->methodPointer; - public ref IntPtr VirtualMethodPointer => ref *(IntPtr*)&_->virtualMethodPointer; + public ref nint MethodPointer => ref *(nint*)&_->methodPointer; + public ref nint VirtualMethodPointer => ref *(nint*)&_->virtualMethodPointer; public ref Il2CppClass* Class => ref _->klass; - public ref IntPtr InvokerMethod => ref *(IntPtr*)&_->invoker_method; + public ref nint InvokerMethod => ref *(nint*)&_->invoker_method; public ref Il2CppTypeStruct* ReturnType => ref _->return_type; public ref Il2CppMethodFlags Flags => ref *(Il2CppMethodFlags*)&_->flags; public ref byte ParametersCount => ref _->parameters_count; - public ref Il2CppParameterInfo* Parameters => ref *(Il2CppParameterInfo**)&_->parameters; + public ref Il2CppParameterInfo* Parameters => ref _->parameters; public ref uint Token => ref _->token; public bool IsGeneric { @@ -85,7 +80,5 @@ public bool IsMarshalledFromNative set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_29_1.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_29_1.cs index 4ebb747b..84899536 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_29_1.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_29_1.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo { - [ApplicableToUnityVersionsSince("2022.1.0")] + [ApplicableToUnityVersionsSince("2021.2.0b13")] public unsafe class NativeMethodInfoStructHandler_29_1 : INativeMethodInfoStructHandler { public int Size() => sizeof(Il2CppMethodInfo_29_1); public INativeMethodInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppMethodInfo_29_1* _ = (Il2CppMethodInfo_29_1*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeMethodInfoStruct CreateNewStruct() public INativeMethodInfoStruct Wrap(Il2CppMethodInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppMethodInfo_29_1 { @@ -26,7 +25,7 @@ internal unsafe struct Il2CppMethodInfo_29_1 public byte* name; public Il2CppClass* klass; public Il2CppTypeStruct* return_type; - public Il2CppTypeStruct** parameters; + public Il2CppParameterInfo* parameters; public void* runtime_data; public void* generic_data; public uint token; @@ -45,27 +44,27 @@ internal enum Bitfield0 : byte wrapper_type = (1 << BIT_wrapper_type), BIT_has_full_generic_sharing_signature = 3, has_full_generic_sharing_signature = (1 << BIT_has_full_generic_sharing_signature), + BIT_indirect_call_via_invokers = 4, + indirect_call_via_invokers = (1 << BIT_indirect_call_via_invokers), } - } - internal class NativeStructWrapper : INativeMethodInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppMethodInfo_29_1._bitfield0)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppMethodInfo_29_1* _ => (Il2CppMethodInfo_29_1*)Pointer; public Il2CppMethodInfo* MethodInfoPointer => (Il2CppMethodInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref ushort Slot => ref _->slot; - public ref IntPtr MethodPointer => ref *(IntPtr*)&_->methodPointer; - public ref IntPtr VirtualMethodPointer => ref *(IntPtr*)&_->virtualMethodPointer; + public ref nint MethodPointer => ref *(nint*)&_->methodPointer; + public ref nint VirtualMethodPointer => ref *(nint*)&_->virtualMethodPointer; public ref Il2CppClass* Class => ref _->klass; - public ref IntPtr InvokerMethod => ref *(IntPtr*)&_->invoker_method; + public ref nint InvokerMethod => ref *(nint*)&_->invoker_method; public ref Il2CppTypeStruct* ReturnType => ref _->return_type; public ref Il2CppMethodFlags Flags => ref *(Il2CppMethodFlags*)&_->flags; public ref byte ParametersCount => ref _->parameters_count; - public ref Il2CppParameterInfo* Parameters => ref *(Il2CppParameterInfo**)&_->parameters; + public ref Il2CppParameterInfo* Parameters => ref _->parameters; public ref uint Token => ref _->token; public bool IsGeneric { @@ -83,7 +82,5 @@ public bool IsMarshalledFromNative set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_29_2.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_29_2.cs index 0cd0a703..a2981bc3 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_29_2.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/MethodInfo/MethodInfo_29_2.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo { - [ApplicableToUnityVersionsSince("2023.2.0")] + [ApplicableToUnityVersionsSince("2023.2.0a22")] public unsafe class NativeMethodInfoStructHandler_29_2 : INativeMethodInfoStructHandler { public int Size() => sizeof(Il2CppMethodInfo_29_2); public INativeMethodInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppMethodInfo_29_2* _ = (Il2CppMethodInfo_29_2*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeMethodInfoStruct CreateNewStruct() public INativeMethodInfoStruct Wrap(Il2CppMethodInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppMethodInfo_29_2 { @@ -26,7 +25,7 @@ internal unsafe struct Il2CppMethodInfo_29_2 public byte* name; public Il2CppClass* klass; public Il2CppTypeStruct* return_type; - public Il2CppTypeStruct** parameters; + public Il2CppParameterInfo* parameters; public void* runtime_data; public void* generic_data; public uint token; @@ -48,26 +47,24 @@ internal enum Bitfield0 : byte BIT_is_unmanaged_callers_only = 4, is_unmanaged_callers_only = (1 << BIT_is_unmanaged_callers_only), } - } - internal class NativeStructWrapper : INativeMethodInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppMethodInfo_29_2._bitfield0)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppMethodInfo_29_2* _ => (Il2CppMethodInfo_29_2*)Pointer; public Il2CppMethodInfo* MethodInfoPointer => (Il2CppMethodInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref ushort Slot => ref _->slot; - public ref IntPtr MethodPointer => ref *(IntPtr*)&_->methodPointer; - public ref IntPtr VirtualMethodPointer => ref *(IntPtr*)&_->virtualMethodPointer; + public ref nint MethodPointer => ref *(nint*)&_->methodPointer; + public ref nint VirtualMethodPointer => ref *(nint*)&_->virtualMethodPointer; public ref Il2CppClass* Class => ref _->klass; - public ref IntPtr InvokerMethod => ref *(IntPtr*)&_->invoker_method; + public ref nint InvokerMethod => ref *(nint*)&_->invoker_method; public ref Il2CppTypeStruct* ReturnType => ref _->return_type; public ref Il2CppMethodFlags Flags => ref *(Il2CppMethodFlags*)&_->flags; public ref byte ParametersCount => ref _->parameters_count; - public ref Il2CppParameterInfo* Parameters => ref *(Il2CppParameterInfo**)&_->parameters; + public ref Il2CppParameterInfo* Parameters => ref _->parameters; public ref uint Token => ref _->token; public bool IsGeneric { @@ -85,7 +82,5 @@ public bool IsMarshalledFromNative set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/Interfaces.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/Interfaces.cs index ec14427e..b50079f1 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/Interfaces.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/Interfaces.cs @@ -1,24 +1,19 @@ using System; -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.ParameterInfo; - -public interface INativeParameterInfoStructHandler : INativeStructHandler +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.ParameterInfo { - unsafe Il2CppParameterInfo*[] CreateNewParameterInfoArray(int paramCount); - unsafe INativeParameterInfoStruct Wrap(Il2CppParameterInfo* paramInfoPointer); - unsafe INativeParameterInfoStruct Wrap(Il2CppParameterInfo* paramInfoListBegin, int index); - bool HasNamePosToken { get; } -#if DEBUG - string GetName(); -#endif -} + public interface INativeParameterInfoStructHandler : INativeStructHandler + { + unsafe INativeParameterInfoStruct Wrap(Il2CppParameterInfo* paramInfoPointer); + } -public interface INativeParameterInfoStruct : INativeStruct -{ - unsafe Il2CppParameterInfo* ParameterInfoPointer { get; } - bool HasNamePosToken { get; } - ref IntPtr Name { get; } - ref int Position { get; } - ref uint Token { get; } - unsafe ref Il2CppTypeStruct* ParameterType { get; } + public interface INativeParameterInfoStruct : INativeStruct + { + unsafe Il2CppParameterInfo* ParameterInfoPointer { get; } + bool HasNamePosToken { get; } + ref IntPtr Name { get; } + ref int Position { get; } + ref uint Token { get; } + unsafe ref Il2CppTypeStruct* ParameterType { get; } + } } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_16_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_16_0.cs index c718abce..94d03344 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_16_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_16_0.cs @@ -1,80 +1,41 @@ -using System; using System.Runtime.InteropServices; - -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.ParameterInfo; - -[ApplicableToUnityVersionsSince("5.3.0")] -internal class NativeParameterInfoStructHandler_16_0 : INativeParameterInfoStructHandler +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.ParameterInfo { - public unsafe int Size() + [ApplicableToUnityVersionsSince("5.2.2")] + public unsafe class NativeParameterInfoStructHandler_16_0 : INativeParameterInfoStructHandler { - return sizeof(Il2CppParameterInfo_16_0); - } - - public unsafe Il2CppParameterInfo*[] CreateNewParameterInfoArray(int paramCount) - { - var ptr = (Il2CppParameterInfo_16_0*)Marshal.AllocHGlobal(Marshal.SizeOf() * - paramCount); - var res = new Il2CppParameterInfo*[paramCount]; - for (var i = 0; i < paramCount; i++) + public int Size() => sizeof(Il2CppParameterInfo_16_0); + public INativeParameterInfoStruct CreateNewStruct() { - ptr[i] = default; - res[i] = (Il2CppParameterInfo*)&ptr[i]; + nint ptr = Marshal.AllocHGlobal(Size()); + Il2CppParameterInfo_16_0* _ = (Il2CppParameterInfo_16_0*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); } - - return res; - } - - public unsafe INativeParameterInfoStruct? Wrap(Il2CppParameterInfo* paramInfoPointer) - { - if ((IntPtr)paramInfoPointer == IntPtr.Zero) return null; - return new NativeParameterInfoStructWrapper((IntPtr)paramInfoPointer); - } - - public unsafe INativeParameterInfoStruct? Wrap(Il2CppParameterInfo* paramInfoListBegin, int index) - { - if ((IntPtr)paramInfoListBegin == IntPtr.Zero) return null; - return new NativeParameterInfoStructWrapper((IntPtr)paramInfoListBegin + - Marshal.SizeOf() * index); - } - - public bool HasNamePosToken => true; - -#if DEBUG - public string GetName() => "NativeParameterInfoStructHandler_16_0"; -#endif - - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct Il2CppParameterInfo_16_0 - { - public IntPtr name; // const char* - public int position; - public uint token; - public int customAttributeIndex; - public Il2CppTypeStruct* parameter_type; // const - } - - internal unsafe class NativeParameterInfoStructWrapper : INativeParameterInfoStruct - { - public NativeParameterInfoStructWrapper(IntPtr pointer) + public INativeParameterInfoStruct Wrap(Il2CppParameterInfo* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppParameterInfo_16_0 + { + public byte* name; + public int position; + public uint token; + public int customAttributeIndex; + public Il2CppTypeStruct* parameter_type; + } + internal class NativeStructWrapper : INativeParameterInfoStruct { - Pointer = pointer; + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } + private Il2CppParameterInfo_16_0* _ => (Il2CppParameterInfo_16_0*)Pointer; + public Il2CppParameterInfo* ParameterInfoPointer => (Il2CppParameterInfo*)Pointer; + public bool HasNamePosToken => true; + public ref nint Name => ref *(nint*)&_->name; + public ref int Position => ref _->position; + public ref uint Token => ref _->token; + public ref Il2CppTypeStruct* ParameterType => ref _->parameter_type; } - - private Il2CppParameterInfo_16_0* NativeParameter => (Il2CppParameterInfo_16_0*)Pointer; - - public IntPtr Pointer { get; } - - public Il2CppParameterInfo* ParameterInfoPointer => (Il2CppParameterInfo*)Pointer; - - public bool HasNamePosToken => true; - - public ref IntPtr Name => ref NativeParameter->name; - - public ref int Position => ref NativeParameter->position; - - public ref uint Token => ref NativeParameter->token; - - public ref Il2CppTypeStruct* ParameterType => ref NativeParameter->parameter_type; } } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_24_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_24_0.cs new file mode 100644 index 00000000..2741bd86 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_24_0.cs @@ -0,0 +1,40 @@ +using System.Runtime.InteropServices; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.ParameterInfo +{ + [ApplicableToUnityVersionsSince("2018.3.0")] + public unsafe class NativeParameterInfoStructHandler_24_0 : INativeParameterInfoStructHandler + { + public int Size() => sizeof(Il2CppParameterInfo_24_0); + public INativeParameterInfoStruct CreateNewStruct() + { + nint ptr = Marshal.AllocHGlobal(Size()); + Il2CppParameterInfo_24_0* _ = (Il2CppParameterInfo_24_0*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeParameterInfoStruct Wrap(Il2CppParameterInfo* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppParameterInfo_24_0 + { + public byte* name; + public int position; + public uint token; + public Il2CppTypeStruct* parameter_type; + } + internal class NativeStructWrapper : INativeParameterInfoStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } + private Il2CppParameterInfo_24_0* _ => (Il2CppParameterInfo_24_0*)Pointer; + public Il2CppParameterInfo* ParameterInfoPointer => (Il2CppParameterInfo*)Pointer; + public bool HasNamePosToken => true; + public ref nint Name => ref *(nint*)&_->name; + public ref int Position => ref _->position; + public ref uint Token => ref _->token; + public ref Il2CppTypeStruct* ParameterType => ref _->parameter_type; + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_24_1.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_24_1.cs deleted file mode 100644 index a9cc117c..00000000 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_24_1.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.ParameterInfo; - -[ApplicableToUnityVersionsSince("2018.3.0")] -internal class NativeParameterInfoStructHandler_24_1 : INativeParameterInfoStructHandler -{ - public unsafe int Size() - { - return sizeof(Il2CppParameterInfo_24_1); - } - - public unsafe Il2CppParameterInfo*[] CreateNewParameterInfoArray(int paramCount) - { - var ptr = (Il2CppParameterInfo_24_1*)Marshal.AllocHGlobal(Marshal.SizeOf() * - paramCount); - var res = new Il2CppParameterInfo*[paramCount]; - for (var i = 0; i < paramCount; i++) - { - ptr[i] = default; - res[i] = (Il2CppParameterInfo*)&ptr[i]; - } - - return res; - } - - public unsafe INativeParameterInfoStruct? Wrap(Il2CppParameterInfo* paramInfoPointer) - { - if ((IntPtr)paramInfoPointer == IntPtr.Zero) return null; - return new NativeParameterInfoStructWrapper((IntPtr)paramInfoPointer); - } - - public unsafe INativeParameterInfoStruct? Wrap(Il2CppParameterInfo* paramInfoListBegin, int index) - { - if ((IntPtr)paramInfoListBegin == IntPtr.Zero) return null; - return new NativeParameterInfoStructWrapper((IntPtr)paramInfoListBegin + - Marshal.SizeOf() * index); - } - - public bool HasNamePosToken => true; - -#if DEBUG - public string GetName() => "NativeParameterInfoStructHandler_24_1"; -#endif - - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct Il2CppParameterInfo_24_1 - { - public IntPtr name; // const char* - public int position; - public uint token; - public Il2CppTypeStruct* parameter_type; // const - } - - internal unsafe class NativeParameterInfoStructWrapper : INativeParameterInfoStruct - { - public NativeParameterInfoStructWrapper(IntPtr pointer) - { - Pointer = pointer; - } - - private Il2CppParameterInfo_24_1* NativeParameter => (Il2CppParameterInfo_24_1*)Pointer; - - public IntPtr Pointer { get; } - - public Il2CppParameterInfo* ParameterInfoPointer => (Il2CppParameterInfo*)Pointer; - - public bool HasNamePosToken => true; - - public ref IntPtr Name => ref NativeParameter->name; - - public ref int Position => ref NativeParameter->position; - - public ref uint Token => ref NativeParameter->token; - - public ref Il2CppTypeStruct* ParameterType => ref NativeParameter->parameter_type; - } -} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_27_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_27_0.cs new file mode 100644 index 00000000..d1886b65 --- /dev/null +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_27_0.cs @@ -0,0 +1,37 @@ +using System.Runtime.InteropServices; +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.ParameterInfo +{ + [ApplicableToUnityVersionsSince("2021.2.0")] + public unsafe class NativeParameterInfoStructHandler_27_0 : INativeParameterInfoStructHandler + { + public int Size() => sizeof(Il2CppParameterInfo_27_0); + public INativeParameterInfoStruct CreateNewStruct() + { + nint ptr = Marshal.AllocHGlobal(Size()); + Il2CppParameterInfo_27_0* _ = (Il2CppParameterInfo_27_0*)ptr; + *_ = default; + return new NativeStructWrapper(ptr); + } + public INativeParameterInfoStruct Wrap(Il2CppParameterInfo* ptr) + { + if (ptr == null) return null; + return new NativeStructWrapper((nint)ptr); + } + internal unsafe struct Il2CppParameterInfo_27_0 + { + public Il2CppTypeStruct* parameter_type; + } + internal class NativeStructWrapper : INativeParameterInfoStruct + { + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } + private Il2CppParameterInfo_27_0* _ => (Il2CppParameterInfo_27_0*)Pointer; + public Il2CppParameterInfo* ParameterInfoPointer => (Il2CppParameterInfo*)Pointer; + public bool HasNamePosToken => false; + public ref nint Name => throw new System.NotSupportedException(); + public ref int Position => throw new System.NotSupportedException(); + public ref uint Token => throw new System.NotSupportedException(); + public ref Il2CppTypeStruct* ParameterType => ref _->parameter_type; + } + } +} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_27_3.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_27_3.cs deleted file mode 100644 index 84d7dd75..00000000 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/ParameterInfo/ParameterInfo_27_3.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.ParameterInfo; - -[ApplicableToUnityVersionsSince("2021.2.0")] -internal class NativeParameterInfoStructHandler_27_3 : INativeParameterInfoStructHandler -{ - public unsafe int Size() - { - return sizeof(Il2CppParameterInfo_27_3); - } - - public unsafe Il2CppParameterInfo*[] CreateNewParameterInfoArray(int paramCount) - { - var ptr = (Il2CppParameterInfo_27_3*)Marshal.AllocHGlobal(Marshal.SizeOf() * - paramCount); - var res = new Il2CppParameterInfo*[paramCount]; - for (var i = 0; i < paramCount; i++) - { - ptr[i] = default; - res[i] = (Il2CppParameterInfo*)&ptr[i]; - } - - return res; - } - - public unsafe INativeParameterInfoStruct? Wrap(Il2CppParameterInfo* paramInfoPointer) - { - if ((IntPtr)paramInfoPointer == IntPtr.Zero) return null; - return new NativeParameterInfoStructWrapper((IntPtr)paramInfoPointer); - } - - public unsafe INativeParameterInfoStruct? Wrap(Il2CppParameterInfo* paramInfoListBegin, int index) - { - if ((IntPtr)paramInfoListBegin == IntPtr.Zero) return null; - return new NativeParameterInfoStructWrapper((IntPtr)paramInfoListBegin + - Marshal.SizeOf() * index); - } - - public bool HasNamePosToken => false; - -#if DEBUG - public string GetName() => "NativeParameterInfoStructHandler_27_3"; -#endif - - //Doesn't actually exist; just using this for type pointer storage in MethodInfo 27_3 + - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct Il2CppParameterInfo_27_3 - { - public Il2CppTypeStruct* parameter_type; - } - - internal unsafe class NativeParameterInfoStructWrapper : INativeParameterInfoStruct - { - public NativeParameterInfoStructWrapper(IntPtr pointer) - { - Pointer = pointer; - } - - private Il2CppParameterInfo_27_3* NativeParameter => (Il2CppParameterInfo_27_3*)Pointer; - - public IntPtr Pointer { get; } - - public Il2CppParameterInfo* ParameterInfoPointer => (Il2CppParameterInfo*)Pointer; - - public bool HasNamePosToken => false; - - public ref IntPtr Name => throw new NotSupportedException("ParameterInfo does not exist in Unity 2021.2.0+"); - - public ref int Position => throw new NotSupportedException("ParameterInfo does not exist in Unity 2021.2.0+"); - - public ref uint Token => throw new NotSupportedException("ParameterInfo does not exist in Unity 2021.2.0+"); - - public ref Il2CppTypeStruct* ParameterType => ref NativeParameter->parameter_type; - } -} diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/Interfaces.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/Interfaces.cs index 9caca5b7..03e00cfb 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/Interfaces.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/Interfaces.cs @@ -1,24 +1,25 @@ using System; -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.PropertyInfo; - -public interface INativePropertyInfoStructHandler : INativeStructHandler +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.PropertyInfo { - INativePropertyInfoStruct CreateNewStruct(); - unsafe INativePropertyInfoStruct Wrap(Il2CppPropertyInfo* propertyInfoPointer); -} + public interface INativePropertyInfoStructHandler : INativeStructHandler + { + INativePropertyInfoStruct CreateNewStruct(); + unsafe INativePropertyInfoStruct Wrap(Il2CppPropertyInfo* propertyInfoPointer); + } -public interface INativePropertyInfoStruct : INativeStruct -{ - unsafe Il2CppPropertyInfo* PropertyInfoPointer { get; } + public interface INativePropertyInfoStruct : INativeStruct + { + unsafe Il2CppPropertyInfo* PropertyInfoPointer { get; } - ref IntPtr Name { get; } + ref IntPtr Name { get; } - unsafe ref Il2CppClass* Parent { get; } + unsafe ref Il2CppClass* Parent { get; } - unsafe ref Il2CppMethodInfo* Get { get; } + unsafe ref Il2CppMethodInfo* Get { get; } - unsafe ref Il2CppMethodInfo* Set { get; } + unsafe ref Il2CppMethodInfo* Set { get; } - ref uint Attrs { get; } + ref uint Attrs { get; } + } } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/PropertyInfo_16_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/PropertyInfo_16_0.cs index d8a147e1..bd81d3a4 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/PropertyInfo_16_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/PropertyInfo_16_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.PropertyInfo { @@ -8,7 +7,7 @@ public unsafe class NativePropertyInfoStructHandler_16_0 : INativePropertyInfoSt public int Size() => sizeof(Il2CppPropertyInfo_16_0); public INativePropertyInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppPropertyInfo_16_0* _ = (Il2CppPropertyInfo_16_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativePropertyInfoStruct CreateNewStruct() public INativePropertyInfoStruct Wrap(Il2CppPropertyInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppPropertyInfo_16_0 { @@ -27,20 +26,17 @@ internal unsafe struct Il2CppPropertyInfo_16_0 public uint attrs; public int customAttributeIndex; } - internal class NativeStructWrapper : INativePropertyInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppPropertyInfo_16_0* _ => (Il2CppPropertyInfo_16_0*)Pointer; public Il2CppPropertyInfo* PropertyInfoPointer => (Il2CppPropertyInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppMethodInfo* Get => ref _->get; public ref Il2CppMethodInfo* Set => ref _->set; public ref uint Attrs => ref _->attrs; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/PropertyInfo_19_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/PropertyInfo_19_0.cs index 9e54d6e1..b5ba6ca2 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/PropertyInfo_19_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/PropertyInfo_19_0.cs @@ -1,14 +1,13 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.PropertyInfo { - [ApplicableToUnityVersionsSince("5.3.2")] + [ApplicableToUnityVersionsSince("5.3.1p3")] public unsafe class NativePropertyInfoStructHandler_19_0 : INativePropertyInfoStructHandler { public int Size() => sizeof(Il2CppPropertyInfo_19_0); public INativePropertyInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppPropertyInfo_19_0* _ = (Il2CppPropertyInfo_19_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativePropertyInfoStruct CreateNewStruct() public INativePropertyInfoStruct Wrap(Il2CppPropertyInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppPropertyInfo_19_0 { @@ -28,20 +27,17 @@ internal unsafe struct Il2CppPropertyInfo_19_0 public int customAttributeIndex; public uint token; } - internal class NativeStructWrapper : INativePropertyInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppPropertyInfo_19_0* _ => (Il2CppPropertyInfo_19_0*)Pointer; public Il2CppPropertyInfo* PropertyInfoPointer => (Il2CppPropertyInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppMethodInfo* Get => ref _->get; public ref Il2CppMethodInfo* Set => ref _->set; public ref uint Attrs => ref _->attrs; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/PropertyInfo_24_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/PropertyInfo_24_0.cs index 87ccb420..4dc6f451 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/PropertyInfo_24_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/PropertyInfo/PropertyInfo_24_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.PropertyInfo { @@ -8,7 +7,7 @@ public unsafe class NativePropertyInfoStructHandler_24_0 : INativePropertyInfoSt public int Size() => sizeof(Il2CppPropertyInfo_24_0); public INativePropertyInfoStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppPropertyInfo_24_0* _ = (Il2CppPropertyInfo_24_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativePropertyInfoStruct CreateNewStruct() public INativePropertyInfoStruct Wrap(Il2CppPropertyInfo* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppPropertyInfo_24_0 { @@ -27,20 +26,17 @@ internal unsafe struct Il2CppPropertyInfo_24_0 public uint attrs; public uint token; } - internal class NativeStructWrapper : INativePropertyInfoStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; - public IntPtr Pointer { get; } + public NativeStructWrapper(nint ptr) => Pointer = ptr; + public nint Pointer { get; } private Il2CppPropertyInfo_24_0* _ => (Il2CppPropertyInfo_24_0*)Pointer; public Il2CppPropertyInfo* PropertyInfoPointer => (Il2CppPropertyInfo*)Pointer; - public ref IntPtr Name => ref *(IntPtr*)&_->name; + public ref nint Name => ref *(nint*)&_->name; public ref Il2CppClass* Parent => ref _->parent; public ref Il2CppMethodInfo* Get => ref _->get; public ref Il2CppMethodInfo* Set => ref _->set; public ref uint Attrs => ref _->attrs; } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Type/Interfaces.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Type/Interfaces.cs index 31d73eb3..fda03e13 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Type/Interfaces.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Type/Interfaces.cs @@ -1,25 +1,26 @@ using System; -namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Type; - -public interface INativeTypeStructHandler : INativeStructHandler +namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Type { - INativeTypeStruct CreateNewStruct(); - unsafe INativeTypeStruct Wrap(Il2CppTypeStruct* typePointer); -} + public interface INativeTypeStructHandler : INativeStructHandler + { + INativeTypeStruct CreateNewStruct(); + unsafe INativeTypeStruct Wrap(Il2CppTypeStruct* typePointer); + } -public interface INativeTypeStruct : INativeStruct -{ - unsafe Il2CppTypeStruct* TypePointer { get; } + public interface INativeTypeStruct : INativeStruct + { + unsafe Il2CppTypeStruct* TypePointer { get; } - ref IntPtr Data { get; } + ref IntPtr Data { get; } - ref ushort Attrs { get; } + ref ushort Attrs { get; } - ref Il2CppTypeEnum Type { get; } + ref Il2CppTypeEnum Type { get; } - bool ByRef { get; set; } + bool ByRef { get; set; } - bool Pinned { get; set; } - bool ValueType { get; set; } + bool Pinned { get; set; } + bool ValueType { get; set; } + } } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Type/Type_16_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Type/Type_16_0.cs index 434adf12..2d5310b2 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Type/Type_16_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Type/Type_16_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Type { @@ -8,7 +7,7 @@ public unsafe class NativeTypeStructHandler_16_0 : INativeTypeStructHandler public int Size() => sizeof(Il2CppType_16_0); public INativeTypeStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppType_16_0* _ = (Il2CppType_16_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeTypeStruct CreateNewStruct() public INativeTypeStruct Wrap(Il2CppTypeStruct* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppType_16_0 { @@ -31,17 +30,15 @@ internal enum Bitfield0 : byte BIT_pinned = 7, pinned = (1 << BIT_pinned), } - } - internal class NativeStructWrapper : INativeTypeStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppType_16_0._bitfield0)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppType_16_0* _ => (Il2CppType_16_0*)Pointer; public Il2CppTypeStruct* TypePointer => (Il2CppTypeStruct*)Pointer; - public ref IntPtr Data => ref *(IntPtr*)&_->data; + public ref nint Data => ref *(nint*)&_->data; public ref ushort Attrs => ref _->attrs; public ref Il2CppTypeEnum Type => ref *(Il2CppTypeEnum*)&_->type; public bool ByRef @@ -60,7 +57,5 @@ public bool ValueType set { } } } - } - } diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Type/Type_27_0.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Type/Type_27_0.cs index 7645de19..53e5a306 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Type/Type_27_0.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Type/Type_27_0.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Il2CppInterop.Runtime.Runtime.VersionSpecific.Type { @@ -8,7 +7,7 @@ public unsafe class NativeTypeStructHandler_27_0 : INativeTypeStructHandler public int Size() => sizeof(Il2CppType_27_0); public INativeTypeStruct CreateNewStruct() { - IntPtr ptr = Marshal.AllocHGlobal(Size()); + nint ptr = Marshal.AllocHGlobal(Size()); Il2CppType_27_0* _ = (Il2CppType_27_0*)ptr; *_ = default; return new NativeStructWrapper(ptr); @@ -16,7 +15,7 @@ public INativeTypeStruct CreateNewStruct() public INativeTypeStruct Wrap(Il2CppTypeStruct* ptr) { if (ptr == null) return null; - return new NativeStructWrapper((IntPtr)ptr); + return new NativeStructWrapper((nint)ptr); } internal unsafe struct Il2CppType_27_0 { @@ -33,17 +32,15 @@ internal enum Bitfield0 : byte BIT_valuetype = 7, valuetype = (1 << BIT_valuetype), } - } - internal class NativeStructWrapper : INativeTypeStruct { - public NativeStructWrapper(IntPtr ptr) => Pointer = ptr; + public NativeStructWrapper(nint ptr) => Pointer = ptr; private static int _bitfield0offset = Marshal.OffsetOf(nameof(Il2CppType_27_0._bitfield0)).ToInt32(); - public IntPtr Pointer { get; } + public nint Pointer { get; } private Il2CppType_27_0* _ => (Il2CppType_27_0*)Pointer; public Il2CppTypeStruct* TypePointer => (Il2CppTypeStruct*)Pointer; - public ref IntPtr Data => ref *(IntPtr*)&_->data; + public ref nint Data => ref *(nint*)&_->data; public ref ushort Attrs => ref _->attrs; public ref Il2CppTypeEnum Type => ref *(Il2CppTypeEnum*)&_->type; public bool ByRef @@ -62,7 +59,5 @@ public bool ValueType set => this.SetBit(_bitfield0offset, (int)Il2CppType_27_0.Bitfield0.BIT_valuetype, value); } } - } - } diff --git a/Il2CppInterop.Runtime/RuntimeReflectionHelper.cs b/Il2CppInterop.Runtime/RuntimeReflectionHelper.cs deleted file mode 100644 index bcd93b98..00000000 --- a/Il2CppInterop.Runtime/RuntimeReflectionHelper.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using Il2CppSystem.Reflection; -using RuntimeTypeHandle = Il2CppSystem.RuntimeTypeHandle; -using Type = Il2CppSystem.Type; - -namespace Il2CppInterop.Runtime; - -public static class RuntimeReflectionHelper -{ - public static IntPtr GetNestedTypeViaReflection(IntPtr enclosingClass, string nestedTypeName) - { - var reflectionType = Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(enclosingClass)); - var nestedType = reflectionType.GetNestedType(nestedTypeName, BindingFlags.Public | BindingFlags.NonPublic); - - return nestedType != null ? IL2CPP.il2cpp_class_from_system_type(nestedType.Pointer) : IntPtr.Zero; - } - - public static RuntimeTypeHandle GetRuntimeTypeHandle() - { - return Il2CppType.Of().TypeHandle; - } -} diff --git a/Il2CppInterop.Runtime/RuntimeSpecificsStore.cs b/Il2CppInterop.Runtime/RuntimeSpecificsStore.cs deleted file mode 100644 index 0edb90ca..00000000 --- a/Il2CppInterop.Runtime/RuntimeSpecificsStore.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; - -namespace Il2CppInterop.Runtime; - -public static class RuntimeSpecificsStore -{ - private static readonly ReaderWriterLockSlim Lock = new(); - private static readonly Dictionary WasInjectedStore = new(); - - public static bool IsInjected(IntPtr nativeClass) - { - Lock.EnterReadLock(); - try - { - return WasInjectedStore.TryGetValue(nativeClass, out var result) && result; - } - finally - { - Lock.ExitReadLock(); - } - } - - public static void SetClassInfo(IntPtr nativeClass, bool wasInjected) - { - Lock.EnterWriteLock(); - try - { - WasInjectedStore[nativeClass] = wasInjected; - } - finally - { - Lock.ExitWriteLock(); - } - } -} diff --git a/Il2CppInterop.Runtime/Startup/Il2CppInteropRuntime.cs b/Il2CppInterop.Runtime/Startup/Il2CppInteropRuntime.cs index 3147986b..ad3c415b 100644 --- a/Il2CppInterop.Runtime/Startup/Il2CppInteropRuntime.cs +++ b/Il2CppInterop.Runtime/Startup/Il2CppInteropRuntime.cs @@ -1,4 +1,4 @@ -using System; +using AssetRipper.Primitives; using Il2CppInterop.Common.Host; using Il2CppInterop.Common.XrefScans; using Il2CppInterop.Runtime.Injection; @@ -7,12 +7,6 @@ namespace Il2CppInterop.Runtime.Startup; -public record RuntimeConfiguration -{ - public Version UnityVersion { get; init; } - public IDetourProvider DetourProvider { get; init; } -} - public sealed class Il2CppInteropRuntime : BaseHost { private Il2CppInteropRuntime() @@ -21,9 +15,9 @@ private Il2CppInteropRuntime() public static Il2CppInteropRuntime Instance => GetInstance(); - public Version UnityVersion { get; private init; } + public UnityVersion UnityVersion { get; private init; } - public IDetourProvider DetourProvider { get; private init; } + public IDetourProvider DetourProvider { get; private init; } = null!; public static Il2CppInteropRuntime Create(RuntimeConfiguration configuration) { diff --git a/Il2CppInterop.Runtime/Startup/RuntimeConfiguration.cs b/Il2CppInterop.Runtime/Startup/RuntimeConfiguration.cs new file mode 100644 index 00000000..8a7b1edd --- /dev/null +++ b/Il2CppInterop.Runtime/Startup/RuntimeConfiguration.cs @@ -0,0 +1,10 @@ +using AssetRipper.Primitives; +using Il2CppInterop.Runtime.Injection; + +namespace Il2CppInterop.Runtime.Startup; + +public record RuntimeConfiguration +{ + public required UnityVersion UnityVersion { get; init; } + public required IDetourProvider DetourProvider { get; init; } +} diff --git a/Il2CppInterop.Runtime/Usings.cs b/Il2CppInterop.Runtime/Usings.cs new file mode 100644 index 00000000..3a84ddb6 --- /dev/null +++ b/Il2CppInterop.Runtime/Usings.cs @@ -0,0 +1 @@ +global using Object = Il2CppSystem.Object; diff --git a/Il2CppInterop.Runtime/XrefScans/XrefInstanceExtensions.cs b/Il2CppInterop.Runtime/XrefScans/XrefInstanceExtensions.cs index d77b83cf..220f026a 100644 --- a/Il2CppInterop.Runtime/XrefScans/XrefInstanceExtensions.cs +++ b/Il2CppInterop.Runtime/XrefScans/XrefInstanceExtensions.cs @@ -1,7 +1,7 @@ using System.Reflection; using System.Runtime.InteropServices; using Il2CppInterop.Common.XrefScans; -using Il2CppSystem; +using Il2CppInterop.Runtime.Runtime; using IntPtr = System.IntPtr; using InvalidOperationException = System.InvalidOperationException; @@ -17,10 +17,10 @@ public static class XrefInstanceExtensions if (valueAtPointer == IntPtr.Zero) return null; - return new Object(valueAtPointer); + return Il2CppObjectPool.Get(valueAtPointer) as Object; } - public static MethodBase TryResolve(this XrefInstance self) + public static MethodBase? TryResolve(this XrefInstance self) { if (self.Type != XrefType.Method) throw new InvalidOperationException("Can't resolve non-method xrefs"); diff --git a/Il2CppInterop.Runtime/XrefScans/XrefScanImpl.cs b/Il2CppInterop.Runtime/XrefScans/XrefScanImpl.cs index 50157020..ce3d37f1 100644 --- a/Il2CppInterop.Runtime/XrefScans/XrefScanImpl.cs +++ b/Il2CppInterop.Runtime/XrefScans/XrefScanImpl.cs @@ -1,43 +1,35 @@ -using System; -using System.Linq; +using System.Linq; using System.Runtime.InteropServices; using Il2CppInterop.Common.XrefScans; -using Il2CppInterop.Runtime.InteropTypes.Arrays; +using Il2CppInterop.Runtime.InteropTypes.CoreLib; +using Il2CppSystem; using Il2CppSystem.Reflection; -using BindingFlags = System.Reflection.BindingFlags; namespace Il2CppInterop.Runtime.XrefScans; internal class XrefScanImpl : IXrefScannerImpl { - private static Func>? getAssemblies; - - public unsafe (XrefScanUtil.InitMetadataForMethod, IntPtr)? GetMetadataResolver() + public unsafe (XrefScanUtil.InitMetadataForMethod, nint)? GetMetadataResolver() { - getAssemblies ??= - typeof(AppDomain).GetMethod("GetAssemblies", BindingFlags.Public | BindingFlags.Static) - ?.CreateDelegate(typeof(Func>)) as - Func>; - - var unityObjectCctor = getAssemblies(AppDomain.CurrentDomain) + var unityObjectCctor = AppDomainAccessor.GetAssembliesInCurrentDomain() .Single(it => it.GetName().Name == "UnityEngine.CoreModule").GetType("UnityEngine.Object") - .GetConstructors(Il2CppSystem.Reflection.BindingFlags.Static | - Il2CppSystem.Reflection.BindingFlags.NonPublic).Single(); + .GetConstructors(BindingFlags.Static | BindingFlags.NonPublic) + .Single(); var nativeMethodInfo = IL2CPP.il2cpp_method_get_from_reflection(unityObjectCctor.Pointer); - var ourMetadataInitForMethodPointer = XrefScannerLowLevel.JumpTargets(*(IntPtr*)nativeMethodInfo).First(); + var ourMetadataInitForMethodPointer = XrefScannerLowLevel.JumpTargets(*(nint*)nativeMethodInfo).First(); var ourMetadataInitForMethodDelegate = Marshal.GetDelegateForFunctionPointer(ourMetadataInitForMethodPointer); return (ourMetadataInitForMethodDelegate, ourMetadataInitForMethodPointer); } - public bool XrefGlobalClassFilter(IntPtr movTarget) + public bool XrefGlobalClassFilter(nint movTarget) { - var valueAtMov = (IntPtr)Marshal.ReadInt64(movTarget); - if (valueAtMov != IntPtr.Zero) + var valueAtMov = (nint)Marshal.ReadInt64(movTarget); + if (valueAtMov != nint.Zero) { - var targetClass = (IntPtr)Marshal.ReadInt64(valueAtMov); - return targetClass == Il2CppClassPointerStore.NativeClassPtr || - targetClass == Il2CppClassPointerStore.NativeClassPtr; + var targetClass = (nint)Marshal.ReadInt64(valueAtMov); + return targetClass == Il2CppClassPointerStore.NativeClassPointer || + targetClass == Il2CppClassPointerStore.NativeClassPointer; } return false; diff --git a/Il2CppInterop.SourceGenerator/EquatableArray.cs b/Il2CppInterop.SourceGenerator/EquatableArray.cs new file mode 100644 index 00000000..6b666c1a --- /dev/null +++ b/Il2CppInterop.SourceGenerator/EquatableArray.cs @@ -0,0 +1,45 @@ +using System.Collections; + +namespace Il2CppInterop.SourceGenerator; + +internal readonly struct EquatableArray : IEquatable>, IReadOnlyList + where T : IEquatable +{ + private readonly IReadOnlyList array; + public EquatableArray(IReadOnlyList array) + { + this.array = array; + } + + public T this[int index] => array[index]; + + public int Count => array.Count; + + public bool Equals(EquatableArray other) + { + if (array.Count != other.array.Count) + { + return false; + } + for (var i = array.Count - 1; i >= 0; i--) + { + if (!array[i].Equals(other.array[i])) + { + return false; + } + } + return true; + } + + public IEnumerator GetEnumerator() + { + return array.GetEnumerator(); + } + + public override int GetHashCode() => array.Count; + + IEnumerator IEnumerable.GetEnumerator() + { + return array.GetEnumerator(); + } +} diff --git a/Il2CppInterop.SourceGenerator/Extensions.cs b/Il2CppInterop.SourceGenerator/Extensions.cs new file mode 100644 index 00000000..498732d8 --- /dev/null +++ b/Il2CppInterop.SourceGenerator/Extensions.cs @@ -0,0 +1,52 @@ +using System.CodeDom.Compiler; +using Microsoft.CodeAnalysis; + +namespace Il2CppInterop.SourceGenerator; + +internal static class Extensions +{ + // Converts a Roslyn Accessibility to the keyword(s) needed in emitted source. + // Protected/ProtectedAndInternal are included for completeness but are unusual + // on a top-level injected type. + internal static string GetAccessibilityKeyword(this Accessibility a) => a switch + { + Accessibility.Public => "public", + Accessibility.Internal => "internal", + Accessibility.Private => "private", + Accessibility.Protected => "protected", + Accessibility.ProtectedOrInternal => "protected internal", + Accessibility.ProtectedAndInternal => "private protected", + _ => "public", + }; + + internal static string GetTypeKeyword(this TypeKind kind) => kind switch + { + TypeKind.Class => "class", + TypeKind.Struct => "struct", + TypeKind.Interface => "interface", + _ => throw new ArgumentOutOfRangeException(nameof(kind), kind, null), + }; + + extension(INamedTypeSymbol? symbol) + { + public bool IsType(string name, ReadOnlySpan namespaceParts) => + symbol is not null && + symbol.Name == name && + MatchesNamespace(symbol.ContainingNamespace, namespaceParts); + } + + private static bool MatchesNamespace(INamespaceSymbol? ns, ReadOnlySpan parts) + { + // Walk from innermost to outermost + for (var i = parts.Length - 1; i >= 0; i--) + { + if (ns is null or { IsGlobalNamespace: true } || ns.Name != parts[i]) + return false; + ns = ns.ContainingNamespace; + } + + // Make sure we've consumed all namespaces up to global + return ns is null or { IsGlobalNamespace: true }; + } + internal static void WriteLineNoTabs(this IndentedTextWriter writer) => writer.WriteLineNoTabs(null); +} diff --git a/Il2CppInterop.SourceGenerator/Il2CppInterop.SourceGenerator.csproj b/Il2CppInterop.SourceGenerator/Il2CppInterop.SourceGenerator.csproj new file mode 100644 index 00000000..b443c106 --- /dev/null +++ b/Il2CppInterop.SourceGenerator/Il2CppInterop.SourceGenerator.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + enable + enable + + latest + true + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/Il2CppInterop.SourceGenerator/Il2CppTypeGenerator.cs b/Il2CppInterop.SourceGenerator/Il2CppTypeGenerator.cs new file mode 100644 index 00000000..27254b4d --- /dev/null +++ b/Il2CppInterop.SourceGenerator/Il2CppTypeGenerator.cs @@ -0,0 +1,436 @@ +using System.CodeDom.Compiler; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Il2CppInterop.SourceGenerator; + +[Generator] +public sealed class Il2CppTypeGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var types = context.SyntaxProvider + .ForAttributeWithMetadataName( + "Il2CppInterop.Common.Attributes.InjectedTypeAttribute", + predicate: static (node, _) => + { + if (node is not TypeDeclarationSyntax tds) + return false; + if (!tds.Modifiers.Any(SyntaxKind.PartialKeyword)) + return false; + // Type must be non-generic + return tds switch + { + ClassDeclarationSyntax s => s.TypeParameterList is null, + InterfaceDeclarationSyntax s => s.TypeParameterList is null, + StructDeclarationSyntax s => s.TypeParameterList is null, + _ => false, + }; + }, + transform: static (ctx, ct) => { + + var injectedTypeAttr = ctx.Attributes[0]; + var assemblyName = injectedTypeAttr.NamedArguments + .FirstOrDefault(a => a.Key == "Assembly") + .Value.Value as string; + return TypeModel.FromSymbol((INamedTypeSymbol)ctx.TargetSymbol, assemblyName, ct); + }) + .Where(static m => m is not null); + + context.RegisterSourceOutput(types, static (ctx, model) => Emit(ctx, model!)); + } + + + private static void Emit(SourceProductionContext ctx, TypeModel model) + { + using var sw = new StringWriter(); + using var writer = new IndentedTextWriter(sw, " "); + + writer.WriteLine("// "); + writer.WriteLine("#nullable enable"); + writer.WriteLine(); + + if (model.Namespace is not null) + { + writer.WriteLine($"namespace {model.Namespace}"); + writer.WriteLine("{"); + writer.Indent++; + } + + EmitPartialType(writer, model); + writer.WriteLineNoTabs(); + EmitInternalsClass(writer, model); + + if (model.Namespace is not null) + { + writer.Indent--; + writer.WriteLine("}"); + } + + var hint = model.Namespace is null + ? $"{model.TypeName}.g.cs" + : $"{model.Namespace}.{model.TypeName}.g.cs"; + + ctx.AddSource(hint, sw.ToString()); + } + + private static void EmitPartialType(IndentedTextWriter writer, TypeModel model) + { + var access = model.DeclaredAccessibility?.GetAccessibilityKeyword(); + + writer.WriteLine("[global::Il2CppInterop.Common.Attributes.Il2CppType(typeof(Il2CppInternals))]"); + + if (model.TypeKind == TypeKind.Struct) + { + writer.WriteLine($"{access} partial struct {model.TypeName} :"); + writer.Indent++; + writer.WriteLine("global::Il2CppSystem.IValueType,"); + writer.WriteLine($"global::Il2CppInterop.Common.IIl2CppType<{model.TypeName}>"); + writer.Indent--; + } + else + { + writer.WriteLine($"{access} partial {model.TypeKind.GetTypeKeyword()} {model.TypeName} : global::Il2CppInterop.Common.IIl2CppType<{model.TypeName}>"); + } + + writer.WriteLine("{"); + writer.Indent++; + + if (model.TypeKind == TypeKind.Struct) + { + EmitValueTypeBridgeMethods(writer); + writer.WriteLineNoTabs(); + EmitValueTypeInstanceInterfaceMembers(writer, model); + writer.WriteLineNoTabs(); + } + foreach (var member in model.Members) + { + if (member.IsStatic) + EmitStaticIl2CppProperty(writer, member); + else if (member.Kind == MemberKind.Il2Cpp) + EmitIl2CppProperty(writer, member); + else + EmitManagedProperty(writer, member); + + writer.WriteLineNoTabs(); + } + if (model.NeedsObjectPointerConstructor) + { + EmitConstructor(writer, model); + writer.WriteLineNoTabs(); + } + + if (model.FinalizerMethodNames.Count > 0 || model.Members.Any(m => m.Kind == MemberKind.Managed)) + { + EmitFinalizer(writer, model); + writer.WriteLineNoTabs(); + EmitLogErrorFinalizer(writer); + writer.WriteLineNoTabs(); + } + + EmitStaticInterfaceMembers(writer, model); + + writer.Indent--; + writer.WriteLine("}"); + } + + #region Value-type-only emit + + private static void EmitValueTypeBridgeMethods(IndentedTextWriter writer) + { + writer.WriteLine("[global::Il2CppInterop.Common.Attributes.Il2CppMethod]"); + writer.WriteLine("public global::Il2CppSystem.Boolean Equals(global::Il2CppSystem.IObject obj)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("return this.Equals((object)obj);"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLineNoTabs(); + + writer.WriteLine("[global::Il2CppInterop.Common.Attributes.Il2CppMethod(Name = \"GetHashCode\")]"); + writer.WriteLine("public global::Il2CppSystem.Int32 GetIl2CppHashCode()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("return this.GetHashCode();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLineNoTabs(); + + writer.WriteLine("[global::Il2CppInterop.Common.Attributes.Il2CppMethod(Name = \"ToString\")]"); + writer.WriteLine("public global::Il2CppSystem.String ToIl2CppString()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("return this.ToString();"); + writer.Indent--; + writer.WriteLine("}"); + } + + private static void EmitValueTypeInstanceInterfaceMembers(IndentedTextWriter writer, TypeModel model) + { + writer.WriteLine("readonly nint global::Il2CppInterop.Common.IIl2CppType.ObjectClass =>"); + writer.Indent++; + writer.WriteLine($"global::Il2CppInterop.Runtime.Il2CppClassPointerStore<{model.TypeName}>.NativeClassPointer;"); + writer.Indent--; + writer.WriteLineNoTabs(); + + writer.WriteLine("readonly int global::Il2CppSystem.IValueType.Size => Il2CppInternals.Size;"); + writer.WriteLineNoTabs(); + + writer.WriteLine("readonly void global::Il2CppSystem.IValueType.WriteToSpan(global::System.Span span) =>"); + writer.Indent++; + writer.WriteLine("global::Il2CppInterop.Runtime.InteropTypes.Il2CppType.WriteToSpan(this, span);"); + writer.Indent--; + } + + #endregion + + #region Class/interface-only emit + + private static void EmitStaticIl2CppProperty(IndentedTextWriter writer, MemberModel member) + { + var accessPrefix = member.Accessibility is { } a ? a.GetAccessibilityKeyword() + " " : ""; + writer.WriteLine($"{accessPrefix}static partial {member.Type} {member.Name}"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine($"get => global::Il2CppInterop.Runtime.InteropTypes.FieldAccess.GetStaticFieldValue<{member.Type}>(Il2CppInternals.FieldInfoPtr_{member.Index});"); + writer.WriteLine($"set => global::Il2CppInterop.Runtime.InteropTypes.FieldAccess.SetStaticFieldValue(Il2CppInternals.FieldInfoPtr_{member.Index}, value);"); + writer.Indent--; + writer.WriteLine("}"); + } + + private static void EmitIl2CppProperty(IndentedTextWriter writer, MemberModel member) + { + var accessPrefix = member.Accessibility is { } a ? a.GetAccessibilityKeyword() + " " : ""; + writer.WriteLine($"{accessPrefix}partial {member.Type} {member.Name}"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine($"get => global::Il2CppInterop.Runtime.InteropTypes.FieldAccess.GetInstanceFieldValue<{member.Type}>(this, Il2CppInternals.FieldOffset_{member.Index});"); + writer.WriteLine($"set => global::Il2CppInterop.Runtime.InteropTypes.FieldAccess.SetInstanceFieldValue(this, Il2CppInternals.FieldOffset_{member.Index}, value);"); + writer.Indent--; + writer.WriteLine("}"); + } + + private static void EmitManagedProperty(IndentedTextWriter writer, MemberModel member) + { + // Backing field + writer.WriteLine($"[global::Il2CppInterop.Common.Attributes.Il2CppField(Name = nameof({member.Name}))]"); + writer.WriteLine($"private global::Il2CppSystem.IntPtr {member.Name}__BackingField"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine($"get => global::Il2CppInterop.Runtime.InteropTypes.FieldAccess.GetInstanceFieldValue(this, Il2CppInternals.FieldOffset_{member.Index});"); + writer.WriteLine($"set => global::Il2CppInterop.Runtime.InteropTypes.FieldAccess.SetInstanceFieldValue(this, Il2CppInternals.FieldOffset_{member.Index}, value);"); + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLineNoTabs(); + + // Public partial property + var accessPrefix = member.Accessibility is { } a ? a.GetAccessibilityKeyword() + " " : ""; + writer.WriteLine($"{accessPrefix}partial {member.Type} {member.Name}"); + writer.WriteLine("{"); + writer.Indent++; + + writer.WriteLine($"get => global::System.Runtime.InteropServices.GCHandle<{member.Type}>.FromIntPtr({member.Name}__BackingField).Target;"); + + writer.WriteLine("set"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine($"global::System.Runtime.InteropServices.GCHandle<{member.Type}>.FromIntPtr({member.Name}__BackingField).Dispose();"); + writer.WriteLine($"{member.Name}__BackingField = value is not null"); + writer.Indent++; + writer.WriteLine($"? global::System.Runtime.InteropServices.GCHandle<{member.Type}>.ToIntPtr(new global::System.Runtime.InteropServices.GCHandle<{member.Type}>(value))"); + writer.WriteLine(": default;"); + writer.Indent--; + writer.Indent--; + writer.WriteLine("}"); + + writer.Indent--; + writer.WriteLine("}"); + } + + private static void EmitConstructor(IndentedTextWriter writer, TypeModel model) + { + writer.WriteLine($"public {model.TypeName}(global::Il2CppInterop.Common.ObjectPointer obj0) : base(obj0)"); + writer.WriteLine("{"); + writer.WriteLine("}"); + } + + private static void EmitFinalizer(IndentedTextWriter writer, TypeModel model) + { + writer.WriteLine("[global::Il2CppInterop.Common.Attributes.Il2CppMethod(Name = \"Finalize\")]"); + writer.WriteLine("public override void Il2CppFinalize()"); + writer.WriteLine("{"); + writer.Indent++; + + writer.WriteLine("// This disposal happens when the object is collected by the Il2Cpp GC instead of the managed GC."); + writer.WriteLine("// That ensures that the referenced value is kept alive as long as the Il2Cpp object is alive, even if the managed wrapper gets collected."); + writer.WriteLine("// In theory, the managed wrapper could be collected and recreated multiple times during the lifetime of the Il2Cpp object,"); + writer.WriteLine("// so this ensures that the managed fields are not disposed prematurely."); + + writer.WriteLine("try"); + writer.WriteLine("{"); + writer.Indent++; + + foreach (var name in model.FinalizerMethodNames) + writer.WriteLine($"this.{name}();"); + + foreach (var f in model.Members.Where(f => f.Kind == MemberKind.Managed)) + writer.WriteLine($"{f.Name} = null!;"); + + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLine("catch (global::System.Exception ex)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("LogErrorIl2CppFinalize(ex);"); + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLine("finally"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("base.Il2CppFinalize(); // Must call base method"); + writer.Indent--; + writer.WriteLine("}"); + + writer.Indent--; + writer.WriteLine("}"); + } + + private static void EmitLogErrorFinalizer(IndentedTextWriter writer) + { + writer.WriteLine("partial void LogErrorIl2CppFinalize(global::System.Exception exception);"); + } + + #endregion + + #region Shared emit + + private static void EmitStaticInterfaceMembers(IndentedTextWriter writer, TypeModel model) + { + var tn = model.TypeName; + if (model.TypeKind == TypeKind.Struct) + { + writer.WriteLine($"static int global::Il2CppInterop.Common.IIl2CppType<{tn}>.Size => Il2CppInternals.Size;"); + writer.WriteLineNoTabs(); + + // ReadFromSpan — constructs via object initializer, one field per offset + writer.WriteLine($"static {tn} global::Il2CppInterop.Common.IIl2CppType<{tn}>.ReadFromSpan(global::System.ReadOnlySpan span)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("#nullable disable"); + writer.WriteLine($"return new {tn}"); + writer.WriteLine("{"); + writer.Indent++; + foreach (var member in model.Members.Where(x => !x.IsStatic)) + writer.WriteLine($"{member.Name} = global::Il2CppInterop.Runtime.InteropTypes.Il2CppType.ReadFromSpanAtOffset<{member.Type}>(span, Il2CppInternals.FieldOffset_{member.Index}),"); + writer.Indent--; + writer.WriteLine("};"); + writer.WriteLine("#nullable restore"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLineNoTabs(); + + // WriteToSpan — one WriteToSpanAtOffset call per field + writer.WriteLine($"static void global::Il2CppInterop.Common.IIl2CppType<{tn}>.WriteToSpan({tn} value, global::System.Span span)"); + writer.WriteLine("{"); + writer.Indent++; + foreach (var member in model.Members.Where(x => !x.IsStatic)) + writer.WriteLine($"global::Il2CppInterop.Runtime.InteropTypes.Il2CppType.WriteToSpanAtOffset(value.{member.Name}, span, Il2CppInternals.FieldOffset_{member.Index});"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLineNoTabs(); + } + else + { + writer.WriteLine($"static int global::Il2CppInterop.Common.IIl2CppType<{tn}>.Size => nint.Size;"); + writer.WriteLineNoTabs(); + + writer.WriteLine($"nint global::Il2CppInterop.Common.IIl2CppType.ObjectClass => global::Il2CppInterop.Runtime.Il2CppClassPointerStore<{tn}>.NativeClassPointer;"); + writer.WriteLineNoTabs(); + + writer.WriteLine($"static {tn}? global::Il2CppInterop.Common.IIl2CppType<{tn}>.ReadFromSpan(global::System.ReadOnlySpan span)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine($"return global::Il2CppInterop.Runtime.InteropTypes.Il2CppType.ReadReference<{tn}>(span);"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLineNoTabs(); + + writer.WriteLine($"static void global::Il2CppInterop.Common.IIl2CppType<{tn}>.WriteToSpan({tn}? value, global::System.Span span)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("global::Il2CppInterop.Runtime.InteropTypes.Il2CppType.WriteReference(value, span);"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLineNoTabs(); + } + + if (!string.IsNullOrEmpty(model.AssemblyName)) + { + writer.WriteLine($"static string global::Il2CppInterop.Common.IIl2CppType<{tn}>.AssemblyName => \"{model.AssemblyName}\";"); + writer.WriteLineNoTabs(); + } + + writer.WriteLine($"static {tn}()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("global::System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Il2CppInternals).TypeHandle);"); + writer.Indent--; + writer.WriteLine("}"); + } + + private static void EmitInternalsClass(IndentedTextWriter writer, TypeModel model) + { + var isValueType = model.TypeKind == TypeKind.Struct; + + writer.WriteLine("file static class Il2CppInternals"); + writer.WriteLine("{"); + writer.Indent++; + + if (isValueType) + { + writer.WriteLine("public static readonly int Size;"); + } + foreach (var member in model.Members) + { + if (member.IsStatic) + writer.WriteLine($"public static readonly nint FieldInfoPtr_{member.Index}; // {member.Name}"); + else + writer.WriteLine($"public static readonly int FieldOffset_{member.Index}; // {member.Name}"); + } + writer.WriteLineNoTabs(); + writer.WriteLine("static Il2CppInternals()"); + writer.WriteLine("{"); + writer.Indent++; + + writer.WriteLine($"global::Il2CppInterop.Runtime.Injection.TypeInjector.RegisterTypeInIl2Cpp<{model.TypeName}>();"); + + foreach (var member in model.Members) + { + if (member.IsStatic) + writer.WriteLine($"FieldInfoPtr_{member.Index} = global::Il2CppInterop.Runtime.IL2CPP.GetIl2CppField(global::Il2CppInterop.Runtime.Il2CppClassPointerStore<{model.TypeName}>.NativeClassPointer, \"{member.Name}\");"); + else + writer.WriteLine($"FieldOffset_{member.Index} = (int)global::Il2CppInterop.Runtime.IL2CPP.il2cpp_field_get_offset(global::Il2CppInterop.Runtime.IL2CPP.GetIl2CppField(global::Il2CppInterop.Runtime.Il2CppClassPointerStore<{model.TypeName}>.NativeClassPointer, \"{member.Name}\"));"); + } + + if (isValueType) + { + writer.WriteLine($"Size = global::Il2CppInterop.Runtime.IL2CPP.GetIl2cppValueSize(global::Il2CppInterop.Runtime.Il2CppClassPointerStore<{model.TypeName}>.NativeClassPointer);"); + writer.WriteLine($"global::Il2CppInterop.Runtime.Runtime.Il2CppObjectPool.RegisterValueTypeInitializer<{model.TypeName}>();"); + } + else if (!model.IsAbstract) + { + writer.WriteLine($"global::Il2CppInterop.Runtime.Runtime.Il2CppObjectPool.RegisterInitializer(global::Il2CppInterop.Runtime.Il2CppClassPointerStore<{model.TypeName}>.NativeClassPointer, ptr => new {model.TypeName}(ptr));"); + } + + writer.Indent--; + writer.WriteLine("}"); + + writer.Indent--; + writer.WriteLine("}"); + } + #endregion +} diff --git a/Il2CppInterop.SourceGenerator/MemberKind.cs b/Il2CppInterop.SourceGenerator/MemberKind.cs new file mode 100644 index 00000000..be17cb96 --- /dev/null +++ b/Il2CppInterop.SourceGenerator/MemberKind.cs @@ -0,0 +1,7 @@ +namespace Il2CppInterop.SourceGenerator; + +internal enum MemberKind +{ + Il2Cpp, + Managed +} diff --git a/Il2CppInterop.SourceGenerator/MemberModel.cs b/Il2CppInterop.SourceGenerator/MemberModel.cs new file mode 100644 index 00000000..7d4c3420 --- /dev/null +++ b/Il2CppInterop.SourceGenerator/MemberModel.cs @@ -0,0 +1,11 @@ +using Microsoft.CodeAnalysis; + +namespace Il2CppInterop.SourceGenerator; + +internal readonly record struct MemberModel( + string Name, + string Type, + MemberKind Kind, + int Index, + Accessibility? Accessibility, + bool IsStatic); diff --git a/Il2CppInterop.SourceGenerator/TypeModel.cs b/Il2CppInterop.SourceGenerator/TypeModel.cs new file mode 100644 index 00000000..2fc8efe1 --- /dev/null +++ b/Il2CppInterop.SourceGenerator/TypeModel.cs @@ -0,0 +1,130 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Il2CppInterop.SourceGenerator; + +internal sealed record TypeModel( + string? Namespace, + string TypeName, + TypeKind TypeKind, // "class", "struct", or "interface" + string? AssemblyName, + EquatableArray Members, + EquatableArray FinalizerMethodNames, + bool NeedsObjectPointerConstructor, + Accessibility? DeclaredAccessibility, + bool IsAbstract +) +{ + internal static TypeModel? FromSymbol(INamedTypeSymbol node, string? assemblyName, CancellationToken ct) + { + + var typeKind = node.TypeKind; + + switch (typeKind) + { + case TypeKind.Unknown: + case TypeKind.Class when (node.BaseType?.SpecialType == SpecialType.System_Object): + return null; + } + + + var members = new List(); + var index = 0; + + if (typeKind == TypeKind.Struct) + { + foreach (var field in node.GetMembers().OfType()) + { + ct.ThrowIfCancellationRequested(); + + var attrs = field.GetAttributes(); + if (!attrs.Any(a => a.AttributeClass.IsType("Il2CppFieldAttribute", ["Il2CppInterop", "Common", "Attributes"]))) + continue; + + members.Add(new MemberModel( + Name: field.Name, + Type: field.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), + Kind: MemberKind.Il2Cpp, + Index: index++, + Accessibility: null, + IsStatic: field.IsStatic + )); + } + } + foreach (var property in node.GetMembers().OfType()) + { + ct.ThrowIfCancellationRequested(); + + var attrs = property.GetAttributes(); + var il2cpp = attrs.Any(a => a.AttributeClass.IsType("Il2CppFieldAttribute", ["Il2CppInterop", "Common", "Attributes"])); + var managed = attrs.Any(a => a.AttributeClass.IsType("ManagedFieldAttribute", ["Il2CppInterop", "Common", "Attributes"])); + + if (!il2cpp && !managed) + continue; + if (!property.IsPartialDefinition) + continue; + if (managed && property.IsStatic) + continue; + + // DeclaredAccessibility defaults to Private when no modifier is written, + // so we check the syntax directly to distinguish explicit "private" from omitted. + var explicitAccess = property.DeclaringSyntaxReferences + .Select(r => r.GetSyntax(ct)) + .OfType() + .SelectMany(s => s.Modifiers) + .Any(m => m.IsKind(SyntaxKind.PublicKeyword) + || m.IsKind(SyntaxKind.InternalKeyword) + || m.IsKind(SyntaxKind.PrivateKeyword) + || m.IsKind(SyntaxKind.ProtectedKeyword)); + + members.Add(new MemberModel( + Name: property.Name, + Type: property.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), + Kind: managed ? MemberKind.Managed : MemberKind.Il2Cpp, + Index: index++, + Accessibility: !explicitAccess ? null : property.DeclaredAccessibility, + IsStatic: property.IsStatic)); + } + + IReadOnlyList finalizerMethods = []; + var needsObjectPointerConstructor = false; + + if (typeKind == TypeKind.Class) + { + finalizerMethods = [ + ..node.GetMembers() + .OfType() + .Where(m => + !m.IsStatic && + m.Parameters.IsEmpty && + m.ReturnsVoid && + m.GetAttributes().Any(a => + a.AttributeClass.IsType("Il2CppFinalizerAttribute", ["Il2CppInterop", "Common", "Attributes"]))) + .Select(m => m.Name) + ]; + + needsObjectPointerConstructor = !node.GetMembers() + .OfType() + .Any(m => m.MethodKind == MethodKind.Constructor && + m.Parameters is [{ Type: INamedTypeSymbol s }] && + s.IsType("ObjectPointer", ["Il2CppInterop", "Common"])); + } + + return new TypeModel( + Namespace: node.ContainingNamespace.IsGlobalNamespace + ? null + : node.ContainingNamespace.ToDisplayString(), + TypeName: node.Name, + TypeKind: typeKind, + AssemblyName: assemblyName, + Members: new EquatableArray(members), + FinalizerMethodNames: new EquatableArray(finalizerMethods), + NeedsObjectPointerConstructor: needsObjectPointerConstructor, + DeclaredAccessibility: node.DeclaredAccessibility, + IsAbstract: node.IsAbstract); + } + +} + diff --git a/Il2CppInterop.StructGenerator/BitfieldAccessor.cs b/Il2CppInterop.StructGenerator/BitfieldAccessor.cs new file mode 100644 index 00000000..f6a4ddf8 --- /dev/null +++ b/Il2CppInterop.StructGenerator/BitfieldAccessor.cs @@ -0,0 +1,27 @@ +using System.CodeDom.Compiler; + +namespace Il2CppInterop.StructGenerator; + +internal class BitfieldAccessor +{ + public BitfieldAccessor(string accessorName, string elementName, string accessorType = "bool", + bool generateIfNotPresent = true, string? defaultGetter = "", Action? defaultGetBuilder = null, + Action? defaultSetBuilder = null) + { + AccessorName = accessorName; + ElementName = elementName; + AccessorType = accessorType; + GenerateIfNotPresent = generateIfNotPresent; + DefaultImmediateGetter = defaultGetter; + DefaultGetBuilder = defaultGetBuilder; + DefaultSetBuilder = defaultSetBuilder; + } + + public string AccessorName { get; } + public string ElementName { get; } + public string AccessorType { get; } + public bool GenerateIfNotPresent { get; } + public string? DefaultImmediateGetter { get; } + public Action? DefaultGetBuilder { get; } + public Action? DefaultSetBuilder { get; } +} diff --git a/Il2CppInterop.StructGenerator/ByRefWrapper.cs b/Il2CppInterop.StructGenerator/ByRefWrapper.cs new file mode 100644 index 00000000..02774c04 --- /dev/null +++ b/Il2CppInterop.StructGenerator/ByRefWrapper.cs @@ -0,0 +1,22 @@ +namespace Il2CppInterop.StructGenerator; + +internal class ByRefWrapper +{ + public ByRefWrapper(string wrappedType, string wrappedName, string[] nativeNames, string? forcedNativeType = null, + bool addNotSupportedIfNotExist = false, bool makeDummyIfNotExist = false) + { + WrappedType = wrappedType; + WrappedName = wrappedName; + NativeNames = nativeNames; + ForcedNativeType = forcedNativeType; + AddNotSupported = addNotSupportedIfNotExist; + MakeDummyIfNotSupported = makeDummyIfNotExist; + } + + public string WrappedType { get; } + public string WrappedName { get; } + public string[] NativeNames { get; } + public string? ForcedNativeType { get; } + public bool AddNotSupported { get; } + public bool MakeDummyIfNotSupported { get; } +} diff --git a/Il2CppInterop.StructGenerator/CodeGen/CodeGenClass.cs b/Il2CppInterop.StructGenerator/CodeGen/CodeGenClass.cs index 18b95bd7..dc39a73b 100644 --- a/Il2CppInterop.StructGenerator/CodeGen/CodeGenClass.cs +++ b/Il2CppInterop.StructGenerator/CodeGen/CodeGenClass.cs @@ -1,95 +1,10 @@ -using System.Text; -using Il2CppInterop.StructGenerator.CodeGen.Enums; +namespace Il2CppInterop.StructGenerator.CodeGen; -namespace Il2CppInterop.StructGenerator.CodeGen; - -internal class CodeGenClass : CodeGenElement +internal sealed class CodeGenClass : CodeGenType { public CodeGenClass(ElementProtection protection, string name) : base(protection, name) { } - public override byte IndentAmount { get; set; } = 1; public override string Type => "class"; - public List InterfaceNames { get; } = new(); - public List Attributes { get; } = new(); - public List Methods { get; } = new(); - public List Fields { get; } = new(); - public List Properties { get; } = new(); - public List NestedElements { get; } = new(); - - public override string Build() - { - StringBuilder builder = new(); - if (Attributes.Count > 0) - { - for (var i = 0; i < Attributes.Count; i++) - { - if (i > 0) builder.Append(Indent); - builder.AppendLine($"[{Attributes[i]}]"); - } - - builder.Append(Indent); - } - - builder.Append($"{base.Build()}"); - if (InterfaceNames.Count > 0) - builder.Append($" : {string.Join(", ", InterfaceNames)}"); - builder.AppendLine(); - builder.AppendLine($"{Indent}{{"); - foreach (var method in Methods) - { - method.IndentAmount = (byte)(IndentAmount + 1); - builder.Append($"{IndentInner}{method.Build()}"); - } - - foreach (var field in Fields) - builder.AppendLine($"{IndentInner}{field.Build()}"); - foreach (var property in Properties) - { - property.IndentAmount = (byte)(IndentAmount + 1); - builder.Append($"{IndentInner}{property.Build()}"); - } - - foreach (var nestedElement in NestedElements) - { - nestedElement.IndentAmount = (byte)(IndentAmount + 1); - builder.AppendLine($"{IndentInner}{nestedElement.Build()}"); - } - - builder.AppendLine($"{Indent}}}"); - return builder.ToString(); - } - - public static bool operator !=(CodeGenClass lhs, CodeGenClass rhs) - { - return !(lhs == rhs); - } - - public static bool operator ==(CodeGenClass lhs, CodeGenClass rhs) - { - if (lhs.Fields.Count != rhs.Fields.Count) return false; - if (lhs.NestedElements.Count != rhs.NestedElements.Count) return false; - for (var i = 0; i < lhs.Fields.Count; i++) - if (lhs.Fields[i] != rhs.Fields[i]) - return false; - for (var i = 0; i < lhs.NestedElements.Count; i++) - { - if (lhs.NestedElements[i] is not CodeGenEnum lhsEnum) continue; - if (rhs.NestedElements[i] is not CodeGenEnum rhsEnum) continue; - if (lhsEnum != rhsEnum) return false; - } - - return true; - } - - public override bool Equals(object obj) - { - return obj is CodeGenClass @class && this == @class; - } - - public override int GetHashCode() - { - return HashCode.Combine(Fields.Count, NestedElements.Count); - } } diff --git a/Il2CppInterop.StructGenerator/CodeGen/CodeGenConstructor.cs b/Il2CppInterop.StructGenerator/CodeGen/CodeGenConstructor.cs index 829f0797..8891aa97 100644 --- a/Il2CppInterop.StructGenerator/CodeGen/CodeGenConstructor.cs +++ b/Il2CppInterop.StructGenerator/CodeGen/CodeGenConstructor.cs @@ -1,6 +1,4 @@ -using Il2CppInterop.StructGenerator.CodeGen.Enums; - -namespace Il2CppInterop.StructGenerator.CodeGen; +namespace Il2CppInterop.StructGenerator.CodeGen; internal class CodeGenConstructor : CodeGenMethod { @@ -9,5 +7,5 @@ public CodeGenConstructor(string returnType, ElementProtection protection) : bas { } - public override string Declaration => $"{Protection.ToString().ToLower()} {Keywords}{Type}"; + public override string Declaration => $"{Protection.ToCSharpString()} {Keywords}{Type}"; } diff --git a/Il2CppInterop.StructGenerator/CodeGen/CodeGenElement.cs b/Il2CppInterop.StructGenerator/CodeGen/CodeGenElement.cs index a9b4af71..acdd1c8e 100644 --- a/Il2CppInterop.StructGenerator/CodeGen/CodeGenElement.cs +++ b/Il2CppInterop.StructGenerator/CodeGen/CodeGenElement.cs @@ -1,4 +1,4 @@ -using Il2CppInterop.StructGenerator.CodeGen.Enums; +using System.CodeDom.Compiler; namespace Il2CppInterop.StructGenerator.CodeGen; @@ -6,36 +6,37 @@ internal abstract class CodeGenElement { public CodeGenElement(ElementProtection protection, string name) { + ArgumentException.ThrowIfNullOrEmpty(name); Protection = protection; Name = name; } - public abstract byte IndentAmount { get; set; } public abstract string Type { get; } public bool IsStatic { get; set; } public bool IsUnsafe { get; set; } public string Name { get; } public ElementProtection Protection { get; } - public string Indent => new(' ', (IndentAmount - 1) * 4); - public string IndentInner => new(' ', IndentAmount * 4); - private List KeywordList + public string Keywords { get { - var list = new List(); - if (IsStatic) list.Add("static"); - if (IsUnsafe) list.Add("unsafe"); - return list; + if (IsStatic) + { + return IsUnsafe ? "static unsafe " : "static "; + } + else + { + return IsUnsafe ? "unsafe " : string.Empty; + } } } - public string Keywords => KeywordList.Count > 0 ? $"{string.Join(' ', KeywordList)} " : string.Empty; - public virtual string Declaration => $"{Protection.ToString().ToLower()} {Keywords}{Type} {Name}"; + public virtual string Declaration => $"{Protection.ToCSharpString()} {Keywords}{Type} {Name}"; - public virtual string Build() + public virtual void Build(IndentedTextWriter writer) { - return $"{Declaration}"; + writer.Write(Declaration); } } diff --git a/Il2CppInterop.StructGenerator/CodeGen/CodeGenEnum.cs b/Il2CppInterop.StructGenerator/CodeGen/CodeGenEnum.cs index f0bdb56c..e8fb0ac7 100644 --- a/Il2CppInterop.StructGenerator/CodeGen/CodeGenEnum.cs +++ b/Il2CppInterop.StructGenerator/CodeGen/CodeGenEnum.cs @@ -1,58 +1,7 @@ -using System.Text; -using Il2CppInterop.StructGenerator.CodeGen.Enums; +using System.CodeDom.Compiler; namespace Il2CppInterop.StructGenerator.CodeGen; -internal class CodeGenEnumElement -{ - public CodeGenEnumElement(string name, string? value = null) - { - Name = name; - Value = value; - } - - public string Name { get; } - public string? Value { get; } - - public string BuildFrom(CodeGenEnum origin) - { - StringBuilder builder = new($"{origin.IndentInner}{Name}"); - if (Value != null) builder.Append($" = {Value}"); - builder.Append(','); - return builder.ToString(); - } - - public static bool operator !=(CodeGenEnumElement lhs, CodeGenEnumElement rhs) - { - return !(lhs == rhs); - } - - public static bool operator ==(CodeGenEnumElement lhs, CodeGenEnumElement rhs) - { - if (lhs.Name != rhs.Name) return false; - return lhs.Value == rhs.Value; - } - - public override bool Equals(object obj) - { - return obj is CodeGenEnumElement element && this == element; - } - - public override int GetHashCode() - { - return HashCode.Combine(Name, Value); - } -} - -internal enum EnumUnderlyingType -{ - Byte = 0, - UShort, - Int, - UInt, - ULong -} - internal class CodeGenEnum : CodeGenElement { public CodeGenEnum(EnumUnderlyingType underlyingType, ElementProtection protection, string name) : base(protection, @@ -61,7 +10,6 @@ public CodeGenEnum(EnumUnderlyingType underlyingType, ElementProtection protecti UnderlyingType = underlyingType; } - public override byte IndentAmount { get; set; } = 1; public override string Type => "enum"; public EnumUnderlyingType UnderlyingType { get; set; } @@ -75,18 +23,19 @@ public CodeGenEnum(EnumUnderlyingType underlyingType, ElementProtection protecti _ => throw new Exception("exhausted enum") }; - public List Elements { get; } = new(); + public List Elements { get; } = []; - public override string Build() + public override void Build(IndentedTextWriter writer) { - StringBuilder builder = new(base.Build()); - if (UnderlyingType != EnumUnderlyingType.Int) builder.Append($" : {UnderlyingType.ToString().ToLower()}"); - builder.AppendLine(); - builder.AppendLine($"{Indent}{{"); - foreach (var element in Elements) - builder.AppendLine(element.BuildFrom(this)); - builder.AppendLine($"{Indent}}}"); - return builder.ToString(); + base.Build(writer); + if (UnderlyingType != EnumUnderlyingType.Int) + writer.Write($" : {UnderlyingType.ToCSharpString()}"); + writer.WriteLine(); + using (new CurlyBrackets(writer)) + { + foreach (var element in Elements) + element.Build(writer); + } } public static bool operator !=(CodeGenEnum lhs, CodeGenEnum rhs) @@ -105,7 +54,7 @@ public override string Build() return true; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is CodeGenEnum @enum && this == @enum; } diff --git a/Il2CppInterop.StructGenerator/CodeGen/CodeGenEnumElement.cs b/Il2CppInterop.StructGenerator/CodeGen/CodeGenEnumElement.cs new file mode 100644 index 00000000..405d120c --- /dev/null +++ b/Il2CppInterop.StructGenerator/CodeGen/CodeGenEnumElement.cs @@ -0,0 +1,14 @@ +using System.CodeDom.Compiler; + +namespace Il2CppInterop.StructGenerator.CodeGen; + +internal readonly record struct CodeGenEnumElement(string Name, string? Value = null) +{ + public void Build(IndentedTextWriter writer) + { + writer.Write(Name); + if (Value != null) + writer.Write($" = {Value}"); + writer.WriteLine(','); + } +} diff --git a/Il2CppInterop.StructGenerator/CodeGen/CodeGenField.cs b/Il2CppInterop.StructGenerator/CodeGen/CodeGenField.cs index f656abbe..6b752124 100644 --- a/Il2CppInterop.StructGenerator/CodeGen/CodeGenField.cs +++ b/Il2CppInterop.StructGenerator/CodeGen/CodeGenField.cs @@ -1,5 +1,4 @@ -using System.Text; -using Il2CppInterop.StructGenerator.CodeGen.Enums; +using System.CodeDom.Compiler; namespace Il2CppInterop.StructGenerator.CodeGen; @@ -7,21 +6,21 @@ internal class CodeGenField : CodeGenElement { public CodeGenField(string type, ElementProtection protection, string name) : base(protection, name) { + ArgumentException.ThrowIfNullOrEmpty(type); FieldType = type; } - public override byte IndentAmount { get; set; } = 1; public override string Type => FieldType; public string? DefaultValue { get; set; } = null; public string FieldType { get; set; } - public override string Build() + public override void Build(IndentedTextWriter writer) { - StringBuilder builder = new($"{base.Build()}"); - if (DefaultValue != null) builder.Append($" = {DefaultValue}"); - builder.Append(';'); - return builder.ToString(); + base.Build(writer); + if (DefaultValue != null) + writer.Write($" = {DefaultValue}"); + writer.WriteLine(';'); } public static bool operator !=(CodeGenField lhs, CodeGenField rhs) @@ -36,7 +35,7 @@ public override string Build() return lhs.DefaultValue == rhs.DefaultValue; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is CodeGenField field && this == field; } diff --git a/Il2CppInterop.StructGenerator/CodeGen/CodeGenFile.cs b/Il2CppInterop.StructGenerator/CodeGen/CodeGenFile.cs index 1a757948..15a13337 100644 --- a/Il2CppInterop.StructGenerator/CodeGen/CodeGenFile.cs +++ b/Il2CppInterop.StructGenerator/CodeGen/CodeGenFile.cs @@ -1,40 +1,39 @@ -using System.Text; +using System.CodeDom.Compiler; namespace Il2CppInterop.StructGenerator.CodeGen; internal class CodeGenFile { - private byte myMIndentAmount; public string? Namespace { get; set; } - public List Elements { get; } = new(); - public List Usings { get; } = new(); + public List Elements { get; } = []; + public List Usings { get; } = []; - private string Indent => new(' ', myMIndentAmount * 4); - - public string Build() + private void Build(IndentedTextWriter writer) { - StringBuilder builder = new(); foreach (var @using in Usings) - builder.AppendLine($"using {@using};"); + writer.WriteUsing(@using); if (Namespace != null) { - builder.AppendLine($"namespace {Namespace}"); - builder.AppendLine("{"); - myMIndentAmount += 1; + writer.WriteLine($"namespace {Namespace}"); + writer.WriteLine("{"); + writer.Indent++; } - foreach (var element in Elements) + element.Build(writer); + if (Namespace != null) { - element.IndentAmount = (byte)(myMIndentAmount + 1); - builder.AppendLine($"{Indent}{element.Build()}"); + writer.Indent--; + writer.WriteLine("}"); } - - if (Namespace != null) builder.AppendLine("}"); - return builder.ToString(); } public void WriteTo(string path) { - File.WriteAllText(path, Build()); + using StreamWriter writer = new(path) + { + NewLine = "\n" + }; + using IndentedTextWriter indentedWriter = new(writer); + Build(indentedWriter); } } diff --git a/Il2CppInterop.StructGenerator/CodeGen/CodeGenInterface.cs b/Il2CppInterop.StructGenerator/CodeGen/CodeGenInterface.cs new file mode 100644 index 00000000..bb604115 --- /dev/null +++ b/Il2CppInterop.StructGenerator/CodeGen/CodeGenInterface.cs @@ -0,0 +1,10 @@ +namespace Il2CppInterop.StructGenerator.CodeGen; + +internal sealed class CodeGenInterface : CodeGenType +{ + public CodeGenInterface(ElementProtection protection, string name) : base(protection, name) + { + } + + public override string Type => "interface"; +} diff --git a/Il2CppInterop.StructGenerator/CodeGen/CodeGenMethod.cs b/Il2CppInterop.StructGenerator/CodeGen/CodeGenMethod.cs index 163e22fd..fadd9fbb 100644 --- a/Il2CppInterop.StructGenerator/CodeGen/CodeGenMethod.cs +++ b/Il2CppInterop.StructGenerator/CodeGen/CodeGenMethod.cs @@ -1,5 +1,4 @@ -using System.Text; -using Il2CppInterop.StructGenerator.CodeGen.Enums; +using System.CodeDom.Compiler; namespace Il2CppInterop.StructGenerator.CodeGen; @@ -10,36 +9,42 @@ public CodeGenMethod(string returnType, ElementProtection protection, string nam Type = returnType; } - public override byte IndentAmount { get; set; } = 1; public override string Type { get; } - public List Parameters { get; } = new(); - public Action? MethodBodyBuilder { get; set; } = null; + public List Parameters { get; } = []; + public Action? MethodBodyBuilder { get; set; } = null; public string? ImmediateReturn { get; set; } = null; - public string BuildBody() + public void BuildBody(IndentedTextWriter writer) { - StringBuilder builder = new(); if (ImmediateReturn != null) { - if (ImmediateReturn == "") builder.AppendLine(" { }"); - else builder.AppendLine($" => {ImmediateReturn};"); - return builder.ToString(); + if (ImmediateReturn == "") + writer.WriteLine(" { }"); + else + writer.WriteLine($" => {ImmediateReturn};"); + } + else + { + writer.WriteLine(); + using (new CurlyBrackets(writer)) + { + MethodBodyBuilder?.Invoke(writer); + } } - - builder.AppendLine(); - builder.AppendLine($"{Indent}{{"); - StringBuilder body = new(); - MethodBodyBuilder?.Invoke(body); - foreach (var line in body.ToString().Split(Environment.NewLine)) builder.AppendLine($"{IndentInner}{line}"); - builder.AppendLine($"{Indent}}}"); - return builder.ToString(); } - public override string Build() + public override void Build(IndentedTextWriter writer) { - StringBuilder builder = new($"{base.Build()}({string.Join(", ", Parameters.Select(x => x.Build()))})"); - builder.Append(BuildBody()); - return builder.ToString(); + base.Build(writer); + writer.Write('('); + for (var i = 0; i < Parameters.Count; i++) + { + Parameters[i].Build(writer); + if (i != Parameters.Count - 1) + writer.Write(", "); + } + writer.Write(')'); + BuildBody(writer); } } diff --git a/Il2CppInterop.StructGenerator/CodeGen/CodeGenParameter.cs b/Il2CppInterop.StructGenerator/CodeGen/CodeGenParameter.cs index 08b0c011..1ca03d79 100644 --- a/Il2CppInterop.StructGenerator/CodeGen/CodeGenParameter.cs +++ b/Il2CppInterop.StructGenerator/CodeGen/CodeGenParameter.cs @@ -1,21 +1,13 @@ -using Il2CppInterop.StructGenerator.CodeGen.Enums; +using System.CodeDom.Compiler; namespace Il2CppInterop.StructGenerator.CodeGen; -internal class CodeGenParameter : CodeGenElement +internal readonly record struct CodeGenParameter(string Type, string Name) { - private readonly string myMParameterType; - - public CodeGenParameter(string parameterType, string name) : base(ElementProtection.Private, name) - { - myMParameterType = parameterType; - } - - public override byte IndentAmount { get; set; } = 1; - public override string Type => myMParameterType; - - public override string Build() + public void Build(IndentedTextWriter writer) { - return $"{myMParameterType} {Name}"; + writer.Write(Type); + writer.Write(' '); + writer.Write(Name); } } diff --git a/Il2CppInterop.StructGenerator/CodeGen/CodeGenProperty.cs b/Il2CppInterop.StructGenerator/CodeGen/CodeGenProperty.cs index ba121db9..0e09b8e8 100644 --- a/Il2CppInterop.StructGenerator/CodeGen/CodeGenProperty.cs +++ b/Il2CppInterop.StructGenerator/CodeGen/CodeGenProperty.cs @@ -1,5 +1,4 @@ -using System.Text; -using Il2CppInterop.StructGenerator.CodeGen.Enums; +using System.CodeDom.Compiler; namespace Il2CppInterop.StructGenerator.CodeGen; @@ -10,7 +9,6 @@ public CodeGenProperty(string propertyType, ElementProtection protection, string Type = propertyType; } - public override byte IndentAmount { get; set; } = 1; public override string Type { get; } public string? ImmediateGet { get; set; } @@ -21,42 +19,40 @@ public CodeGenProperty(string propertyType, ElementProtection protection, string public bool EmptySet { get; set; } public CodeGenMethod? SetMethod { get; set; } - public override string Build() + public override void Build(IndentedTextWriter writer) { - StringBuilder builder = new(base.Build()); - if ((SetMethod == null && GetMethod != null && GetMethod.ImmediateReturn != null) || ImmediateGet != null) + base.Build(writer); + if (ImmediateGet != null) { - if (ImmediateGet != null) - builder.AppendLine($" => {ImmediateGet};"); - else - builder.Append(GetMethod.BuildBody()); - return builder.ToString(); + writer.WriteLine($" => {ImmediateGet};"); } - - if (EmptyGet || EmptySet) + else if (SetMethod == null && GetMethod != null && GetMethod.ImmediateReturn != null) { - builder.Append(" {"); - if (EmptyGet) builder.Append(" get;"); - if (EmptySet) builder.Append(" set;"); - builder.AppendLine(" }"); - return builder.ToString(); + GetMethod.BuildBody(writer); } - - builder.AppendLine(); - builder.AppendLine($"{Indent}{{"); - if (GetMethod != null) + else if (EmptyGet || EmptySet) { - GetMethod.IndentAmount = (byte)(IndentAmount + 1); - builder.Append($"{IndentInner}get{GetMethod.BuildBody()}"); + writer.Write(" {"); + if (EmptyGet) writer.Write(" get;"); + if (EmptySet) writer.Write(" set;"); + writer.WriteLine(" }"); } - - if (SetMethod != null) + else { - SetMethod.IndentAmount = (byte)(IndentAmount + 1); - builder.Append($"{IndentInner}set{SetMethod.BuildBody()}"); + writer.WriteLine(); + using (new CurlyBrackets(writer)) + { + if (GetMethod != null) + { + writer.Write("get"); + GetMethod.BuildBody(writer); + } + if (SetMethod != null) + { + writer.Write("set"); + SetMethod.BuildBody(writer); + } + } } - - builder.AppendLine($"{Indent}}}"); - return builder.ToString(); } } diff --git a/Il2CppInterop.StructGenerator/CodeGen/CodeGenStruct.cs b/Il2CppInterop.StructGenerator/CodeGen/CodeGenStruct.cs index 22ec7ebe..d34ec37d 100644 --- a/Il2CppInterop.StructGenerator/CodeGen/CodeGenStruct.cs +++ b/Il2CppInterop.StructGenerator/CodeGen/CodeGenStruct.cs @@ -1,8 +1,6 @@ -using Il2CppInterop.StructGenerator.CodeGen.Enums; +namespace Il2CppInterop.StructGenerator.CodeGen; -namespace Il2CppInterop.StructGenerator.CodeGen; - -internal class CodeGenStruct : CodeGenClass +internal sealed class CodeGenStruct : CodeGenType { public CodeGenStruct(ElementProtection protection, string name) : base(protection, name) { diff --git a/Il2CppInterop.StructGenerator/CodeGen/CodeGenType.cs b/Il2CppInterop.StructGenerator/CodeGen/CodeGenType.cs new file mode 100644 index 00000000..3b9b0257 --- /dev/null +++ b/Il2CppInterop.StructGenerator/CodeGen/CodeGenType.cs @@ -0,0 +1,80 @@ +using System.CodeDom.Compiler; + +namespace Il2CppInterop.StructGenerator.CodeGen; + +internal abstract class CodeGenType : CodeGenElement +{ + public CodeGenType(ElementProtection protection, string name) : base(protection, name) + { + } + + public List InterfaceNames { get; } = []; + public List Attributes { get; } = []; + public List Methods { get; } = []; + public List Fields { get; } = []; + public List Properties { get; } = []; + public List NestedElements { get; } = []; + + public override void Build(IndentedTextWriter writer) + { + foreach (var attribute in Attributes) + { + writer.WriteLine($"[{attribute}]"); + } + base.Build(writer); + if (InterfaceNames.Count > 0) + { + writer.Write(" : "); + writer.Write(InterfaceNames[0]); + for (var i = 1; i < InterfaceNames.Count; i++) + { + writer.Write(", "); + writer.Write(InterfaceNames[i]); + } + } + writer.WriteLine(); + using (new CurlyBrackets(writer)) + { + foreach (var method in Methods) + method.Build(writer); + foreach (var field in Fields) + field.Build(writer); + foreach (var property in Properties) + property.Build(writer); + foreach (var nestedElement in NestedElements) + nestedElement.Build(writer); + } + } + + public static bool operator !=(CodeGenType lhs, CodeGenType rhs) + { + return !(lhs == rhs); + } + + public static bool operator ==(CodeGenType lhs, CodeGenType rhs) + { + if (lhs.Fields.Count != rhs.Fields.Count) return false; + if (lhs.NestedElements.Count != rhs.NestedElements.Count) return false; + for (var i = 0; i < lhs.Fields.Count; i++) + if (lhs.Fields[i] != rhs.Fields[i]) + return false; + for (var i = 0; i < lhs.NestedElements.Count; i++) + { + if (lhs.NestedElements[i] is not CodeGenEnum lhsEnum) continue; + if (rhs.NestedElements[i] is not CodeGenEnum rhsEnum) continue; + if (lhsEnum != rhsEnum) return false; + } + + return true; + } + + public override bool Equals(object? obj) + { + return obj is CodeGenType type && this == type; + } + + public override int GetHashCode() + { + return HashCode.Combine(Fields.Count, NestedElements.Count); + } +} diff --git a/Il2CppInterop.StructGenerator/CodeGen/ElementProtection.cs b/Il2CppInterop.StructGenerator/CodeGen/ElementProtection.cs new file mode 100644 index 00000000..0c551cba --- /dev/null +++ b/Il2CppInterop.StructGenerator/CodeGen/ElementProtection.cs @@ -0,0 +1,20 @@ +namespace Il2CppInterop.StructGenerator.CodeGen; + +internal enum ElementProtection +{ + Private, + Protected, + Internal, + Public +} +internal static class ElementProtectionExtensions +{ + public static string ToCSharpString(this ElementProtection value) => value switch + { + ElementProtection.Private => "private", + ElementProtection.Protected => "protected", + ElementProtection.Internal => "internal", + ElementProtection.Public => "public", + _ => throw new InvalidOperationException($"Unknown ElementProtection value: {value}") + }; +} diff --git a/Il2CppInterop.StructGenerator/CodeGen/EnumUnderlyingType.cs b/Il2CppInterop.StructGenerator/CodeGen/EnumUnderlyingType.cs new file mode 100644 index 00000000..1af71e90 --- /dev/null +++ b/Il2CppInterop.StructGenerator/CodeGen/EnumUnderlyingType.cs @@ -0,0 +1,22 @@ +namespace Il2CppInterop.StructGenerator.CodeGen; + +internal enum EnumUnderlyingType +{ + Byte = 0, + UShort, + Int, + UInt, + ULong +} +internal static class EnumUnderlyingTypeExtensions +{ + public static string ToCSharpString(this EnumUnderlyingType value) => value switch + { + EnumUnderlyingType.Byte => "byte", + EnumUnderlyingType.UShort => "ushort", + EnumUnderlyingType.Int => "int", + EnumUnderlyingType.UInt => "uint", + EnumUnderlyingType.ULong => "ulong", + _ => throw new InvalidOperationException($"Unknown EnumUnderlyingType value: {value}") + }; +} diff --git a/Il2CppInterop.StructGenerator/CodeGen/Enums/ElementProtection.cs b/Il2CppInterop.StructGenerator/CodeGen/Enums/ElementProtection.cs deleted file mode 100644 index 27a9ae8b..00000000 --- a/Il2CppInterop.StructGenerator/CodeGen/Enums/ElementProtection.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Il2CppInterop.StructGenerator.CodeGen.Enums; - -internal enum ElementProtection -{ - Private, - Protected, - Internal, - Public -} diff --git a/Il2CppInterop.StructGenerator/Config.cs b/Il2CppInterop.StructGenerator/Config.cs new file mode 100644 index 00000000..a981a558 --- /dev/null +++ b/Il2CppInterop.StructGenerator/Config.cs @@ -0,0 +1,65 @@ +using System.Diagnostics.CodeAnalysis; +using CppAst; +using Il2CppInterop.StructGenerator.TypeGenerators; + +namespace Il2CppInterop.StructGenerator; + +internal static class Config +{ + /// + /// NOTE: Ignores are handled BEFORE renames + /// + public static readonly string[] ClassForcedIgnores = + [ + // Ignore the reflection structs + "Il2CppPropertyInfo", + "Il2CppMethodInfo" + ]; + + public static readonly Dictionary ClassRenames = new() + { + ["TypeInfo"] = "Il2CppClass", + ["FieldInfo"] = "Il2CppFieldInfo", + ["EventInfo"] = "Il2CppEventInfo", + ["ParameterInfo"] = "Il2CppParameterInfo", + ["PropertyInfo"] = "Il2CppPropertyInfo", + ["MethodInfo"] = "Il2CppMethodInfo" + }; + + public static readonly HashSet ClassNames = + [ + "Il2CppAssembly", + "Il2CppAssemblyName", + "Il2CppClass", + "Il2CppEventInfo", + "Il2CppException", + "Il2CppFieldInfo", + "Il2CppImage", + "Il2CppMethodInfo", + "Il2CppParameterInfo", + "Il2CppPropertyInfo", + "Il2CppType", + ]; + + public static VersionSpecificGenerator? TryCreateGenerator(CppClass nativeClass, string metadataSuffix) => nativeClass.Name switch + { + "Il2CppAssembly" => new Il2CppAssemblyGenerator(metadataSuffix, nativeClass), + "Il2CppAssemblyName" => new Il2CppAssemblyNameGenerator(metadataSuffix, nativeClass), + "Il2CppClass" => new Il2CppClassGenerator(metadataSuffix, nativeClass), + "Il2CppEventInfo" => new Il2CppEventInfoGenerator(metadataSuffix, nativeClass), + "Il2CppException" => new Il2CppExceptionGenerator(metadataSuffix, nativeClass), + "Il2CppFieldInfo" => new Il2CppFieldInfoGenerator(metadataSuffix, nativeClass), + "Il2CppImage" => new Il2CppImageGenerator(metadataSuffix, nativeClass), + "Il2CppMethodInfo" => new Il2CppMethodInfoGenerator(metadataSuffix, nativeClass), + "Il2CppParameterInfo" => new Il2CppParameterInfoGenerator(metadataSuffix, nativeClass), + "Il2CppPropertyInfo" => new Il2CppPropertyInfoGenerator(metadataSuffix, nativeClass), + "Il2CppType" => new Il2CppTypeGenerator(metadataSuffix, nativeClass), + _ => null + }; + + public static bool TryCreateGenerator(CppClass nativeClass, string metadataSuffix, [NotNullWhen(true)] out VersionSpecificGenerator? generator) + { + generator = TryCreateGenerator(nativeClass, metadataSuffix); + return generator != null; + } +} diff --git a/Il2CppInterop.StructGenerator/Utilities/ConversionUtils.cs b/Il2CppInterop.StructGenerator/ConversionUtils.cs similarity index 75% rename from Il2CppInterop.StructGenerator/Utilities/ConversionUtils.cs rename to Il2CppInterop.StructGenerator/ConversionUtils.cs index 956a524b..b986ac86 100644 --- a/Il2CppInterop.StructGenerator/Utilities/ConversionUtils.cs +++ b/Il2CppInterop.StructGenerator/ConversionUtils.cs @@ -1,7 +1,6 @@ using CppAst; -using Il2CppInterop.StructGenerator.Resources; -namespace Il2CppInterop.StructGenerator.Utilities; +namespace Il2CppInterop.StructGenerator; internal static class ConversionUtils { @@ -30,6 +29,7 @@ internal static class ConversionUtils ["methodPointerType"] = "void*", ["Il2CppMethodPointer"] = "void*", ["InvokerMethod"] = "void*", + ["Il2CppClass_InitDataUnion"] = "void*", ["TypeIndex"] = "int", ["TypeDefinitionIndex"] = "int", @@ -58,53 +58,51 @@ internal static class ConversionUtils ["InteropDataIndex"] = "int", ["char"] = "byte", + ["int8_t"] = "sbyte", ["uint8_t"] = "byte", + ["int16_t"] = "short", ["uint16_t"] = "ushort", ["int32_t"] = "int", ["uint32_t"] = "uint", - ["unsigned int"] = "uint", + ["int64_t"] = "long", ["uint64_t"] = "ulong", - ["size_t"] = "IntPtr" - }; + ["intptr_t"] = "nint", + ["uintptr_t"] = "nuint", + ["size_t"] = "nint", - private static readonly string[] SInvalidNames = - { - "object", - "class", - "struct", - "base" + ["unsigned int"] = "uint", + ["unsigned long"] = "nuint", + ["unsigned long long"] = "ulong", }; - public static string NormalizeName(string name) - { - return SInvalidNames.Contains(name) ? $"_{name}" : name; - } - public static string CppTypeToCSharpName(CppType type, out bool needsImport) { needsImport = false; if (type is CppArrayType arrayType) { - if (arrayType.SizeOf == 0) return ""; - if (arrayType.SizeOf == 4) return "uint"; - if (arrayType.SizeOf == 8) return "ulong"; - return $"{CppTypeToCSharpName(arrayType.ElementType, out needsImport)}*"; + return arrayType.SizeOf switch + { + 0 => "", + 4 => "uint", + 8 => "ulong", + _ => $"{CppTypeToCSharpName(arrayType.ElementType, out needsImport)}*" + }; } if (type is CppClass fieldType && fieldType.ClassKind == CppClassKind.Union) return "void*"; // Forgive me for my sins var oldTypeName = type.GetDisplayName().Replace("const ", string.Empty); var ptrCount = oldTypeName.Count(x => x == '*'); - if (ptrCount == 0 && Config.ClassToGenerator.ContainsKey(oldTypeName)) + if (ptrCount == 0 && Config.ClassNames.Contains(oldTypeName)) needsImport = true; string ptrs = new('*', ptrCount); - oldTypeName = oldTypeName.Replace("*", string.Empty); - if (STypeRenames.ContainsKey(oldTypeName)) - oldTypeName = STypeRenames[oldTypeName]; + oldTypeName = oldTypeName.Replace("*", string.Empty).Trim(); + if (STypeRenames.TryGetValue(oldTypeName, out var renamed)) + oldTypeName = renamed; return STypeConversions.TryGetValue(oldTypeName, out var converted) - ? $"{converted!}{ptrs}" + ? $"{converted}{ptrs}" : $"{oldTypeName}{ptrs}"; } } diff --git a/Il2CppInterop.StructGenerator/Il2CppInterop.StructGenerator.csproj b/Il2CppInterop.StructGenerator/Il2CppInterop.StructGenerator.csproj index 0fa1a0cc..6d01babf 100644 --- a/Il2CppInterop.StructGenerator/Il2CppInterop.StructGenerator.csproj +++ b/Il2CppInterop.StructGenerator/Il2CppInterop.StructGenerator.csproj @@ -1,20 +1,19 @@ - + - netstandard2.1 + net10.0 + Exe enable - Il2CppInterop.StructGenerator false + $(NETCoreSdkRuntimeIdentifier) - - - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Il2CppInterop.StructGenerator/Il2CppStructWrapperGenerator.cs b/Il2CppInterop.StructGenerator/Il2CppStructWrapperGenerator.cs index e98b023e..a78efd5b 100644 --- a/Il2CppInterop.StructGenerator/Il2CppStructWrapperGenerator.cs +++ b/Il2CppInterop.StructGenerator/Il2CppStructWrapperGenerator.cs @@ -1,92 +1,59 @@ using System.Text.RegularExpressions; +using AssetRipper.Primitives; using CppAst; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.Resources; -using Il2CppInterop.StructGenerator.Utilities; using Microsoft.Extensions.Logging; namespace Il2CppInterop.StructGenerator; -public record Il2CppStructWrapperGeneratorOptions( - string HeadersDirectory, - string OutputDirectory, - ILogger? Logger -); - -// TODO: Instead expose as source generator (might not be viable since clang is platform-dependent) -public static class Il2CppStructWrapperGenerator +public static partial class Il2CppStructWrapperGenerator { - private static readonly Dictionary> SGenerators = new(); + private static readonly Dictionary> SGenerators = []; internal static ILogger? Logger { get; set; } - private static int GetMetadataVersion(string libil2CppPath) - { - var metadataVersion = -1; - foreach (var versionContainer in Config.MetadataVersionContainers) - { - var fullPath = Path.Combine(libil2CppPath, versionContainer); - if (File.Exists(fullPath)) - { - var metadataMatch = Regex.Match(File.ReadAllText(fullPath), - @"\(s_GlobalMetadataHeader->version == ([0-9]+)\);"); - - if (metadataMatch.Success) - { - metadataVersion = int.Parse(metadataMatch.Groups[1].Value); - break; - } - } - } - - return metadataVersion; - } - - private static VersionSpecificGenerator? VisitClass(CppClass @class, int metadataVersion, - UnityVersion unityVersion, CppClass[] classes) + private static VersionSpecificGenerator? VisitClass(CppClass @class, int metadataVersion, UnityVersion unityVersion) { - if (Config.ClassForcedIgnores.Contains(@class.Name)) return null; - if (Config.ClassRenames.TryGetValue(@class.Name, out var rename)) @class.Name = rename; - if (!Config.ClassToGenerator.TryGetValue(@class.Name, out var generatorType)) return null; - if (!typeof(VersionSpecificGenerator).IsAssignableFrom(generatorType)) - throw new Exception($"{@class.Name} has an invalid generator"); - + if (Config.ClassForcedIgnores.Contains(@class.Name)) + return null; + if (Config.ClassRenames.TryGetValue(@class.Name, out var rename)) + @class.Name = rename; + if (!Config.ClassNames.Contains(@class.Name)) + return null; var existingVersionGeneratorCount = - SGenerators[metadataVersion].Count(x => x.GetType() == generatorType); - var existingGenerators = - SGenerators.Values.SelectMany(x => x).Where(x => x.GetType() == generatorType).ToList(); - var generator = (VersionSpecificGenerator)Activator.CreateInstance(generatorType, - $"{metadataVersion}_{existingVersionGeneratorCount}", @class, - new Func(dependencyName => { return classes.Single(x => x.Name == dependencyName); }))!; + SGenerators[metadataVersion].Count(x => x.CppClassName == @class.Name); + if (!Config.TryCreateGenerator(@class, $"{metadataVersion}_{existingVersionGeneratorCount}", out var generator)) + return null; foreach (var field in generator.NativeStructGenerator.FieldsToImport.ToList()) { var cppField = generator.NativeStructGenerator.CppClass.Fields.Single(x => x.Name == field.Name); - CppClass? typeClass = null; - if (cppField.Type is CppClass) - typeClass = (CppClass)cppField.Type; - if (cppField.Type is CppTypedef typeDef && typeDef.ElementType is CppClass) - typeClass = (CppClass)typeDef.ElementType; + var typeClass = cppField.Type as CppClass ?? (cppField.Type as CppTypedef)?.ElementType as CppClass; + if (typeClass != null) { - var gen = VisitClass(typeClass, metadataVersion, unityVersion, classes); - if (gen == null) continue; + var gen = VisitClass(typeClass, metadataVersion, unityVersion); + if (gen == null) + continue; field.FieldType = $"{gen.HandlerGenerator.HandlerClass.Name}.{gen.NativeStructGenerator.NativeStruct.Name}"; generator.NativeStructGenerator.FieldsToImport.Remove(field); - if (Config.ClassToGenerator.ContainsKey(gen.NativeStructGenerator.CppClass.Name)) - generator.AddExtraUsing( - $"Il2CppInterop.Runtime.Runtime.VersionSpecific.{gen.NativeStructGenerator.CppClass.Name.Replace("Il2Cpp", string.Empty)}"); + if (Config.ClassNames.Contains(gen.NativeStructGenerator.CppClass.Name)) + generator.ExtraUsings.Add( + $"Il2CppInterop.Runtime.Runtime.VersionSpecific.{gen.NativeStructGenerator.CppClass.Name.Replace("Il2Cpp", null)}"); } } - generator.SetupElements(); + + var existingGenerators = SGenerators.Values.SelectMany(x => x).Where(x => x.CppClassName == generator.CppClassName); foreach (var existingGenerator in existingGenerators) + { if (existingGenerator.NativeStructGenerator.NativeStruct == generator.NativeStructGenerator.NativeStruct) { existingGenerator.ApplicableVersions.Add(unityVersion); return existingGenerator; } + } generator.ApplicableVersions.Add(unityVersion); SGenerators[metadataVersion].Add(generator); @@ -99,90 +66,59 @@ public static void Generate(Il2CppStructWrapperGeneratorOptions options) if (Directory.Exists(options.OutputDirectory)) Directory.Delete(options.OutputDirectory, true); Directory.CreateDirectory(options.OutputDirectory); - foreach (var (libil2CppDir, version) in Directory.GetDirectories(options.HeadersDirectory) - .Select(x => (x, new UnityVersion(Path.GetFileName(x)))).OrderBy(x => x.Item2)) + var previousVersion = UnityVersion.MinVersion; + foreach (var (headerPath, version) in Directory.GetFiles(options.HeadersDirectory, "*.h") + .Select(x => (x, UnityVersion.Parse(Path.GetFileNameWithoutExtension(x)))).OrderBy(x => x.Item2)) { - var classInternalsPath = Path.Combine(libil2CppDir, "il2cpp-class-internals.h"); - if (!File.Exists(classInternalsPath)) - { - Logger?.LogWarning( - "{} doesn't have il2cpp-class-internals.h - falling back to class-internals.h", version); - classInternalsPath = Path.Combine(libil2CppDir, "class-internals.h"); - if (!File.Exists(classInternalsPath)) - { - Logger?.LogWarning("{} doesn't have class-internals.h", version); - continue; - } - } + var headerText = File.ReadAllText(headerPath); + var metadataMatch = MetadataVersionRegex.Match(headerText); - var objectInternalsPath = Path.Combine(libil2CppDir, "il2cpp-object-internals.h"); - if (!File.Exists(objectInternalsPath)) + int metadataVersion; + if (metadataMatch.Success) { - Logger?.LogWarning( - "{} doesn't have il2cpp-object-internals.h - falling back to object-internals.h", version); - objectInternalsPath = Path.Combine(libil2CppDir, "object-internals.h"); - if (!File.Exists(objectInternalsPath)) - { - Logger?.LogWarning("{} doesn't have object-internals.h", version); - continue; - } + metadataVersion = int.Parse(metadataMatch.Groups[1].Value); } - - var metadataVersion = GetMetadataVersion(libil2CppDir); - if (metadataVersion == -1) + else { Logger?.LogWarning("{} has an invalid metadata version", version); continue; } - var classInternalsIsTmp = true; - // Graduated top of my class by the way - { - if (!File.Exists($"{classInternalsPath}_backup")) - { - var classInternalsData = File.ReadAllText(classInternalsPath); - // I have to do this because the lib I use doesn't recognize these unions, so I have to name them in the most disgusting way imaginable - classInternalsData = Regex.Replace(classInternalsData, - @"(union.{0,60}?rgctx_data;.*?method(?:Definition|MetadataHandle);.*?});", "$1 runtime_data;", - RegexOptions.Singleline); - classInternalsData = Regex.Replace(classInternalsData, - @"(union.{0,60}?genericMethod;.*?genericContainer(?:Handle)?;.*?});", "$1 generic_data;", - RegexOptions.Singleline); - - File.Move(classInternalsPath, $"{classInternalsPath}_backup"); - File.WriteAllText(classInternalsPath, classInternalsData); - } - } if (!SGenerators.ContainsKey(metadataVersion)) - SGenerators[metadataVersion] = new List(); - var compilation = CppParser.ParseFiles(new List { objectInternalsPath, classInternalsPath }, + SGenerators[metadataVersion] = []; + var compilation = CppParser.Parse(ProcessHeaderText(headerText), new CppParserOptions { - ParseAsCpp = true, + ParserKind = CppParserKind.Cpp, AutoSquashTypedef = false, - ParseMacros = true + ParseMacros = false }); Logger?.LogInformation("Parsing {}", version); - var classes = compilation.Classes.ToArray(); - foreach (var @class in classes) VisitClass(@class, metadataVersion, version, classes); - if (classInternalsIsTmp) + if (compilation.HasErrors) + { + Logger?.LogError("Failed to parse {}", version); + continue; + } + // If this is the first version with this major.minor.build, strip the rest of the information. + var actualVersion = version.Major == previousVersion.Major && version.Minor == previousVersion.Minor && version.Build == previousVersion.Build + ? version + : new UnityVersion(version.Major, version.Minor, version.Build); + foreach (var @class in compilation.Classes) { - File.Delete(classInternalsPath); - File.Move($"{classInternalsPath}_backup", classInternalsPath); + VisitClass(@class, metadataVersion, actualVersion); } + previousVersion = version; } Logger?.LogInformation("Building version specific classes"); - // In the eyes of god - I am a disappointment - Dictionary> versionToGeneratorLookup = new(); + Dictionary> versionToGeneratorLookup = []; foreach (var generator in SGenerators.Values.SelectMany(x => x)) { - if (!versionToGeneratorLookup.ContainsKey(generator.GetType())) - versionToGeneratorLookup[generator.GetType()] = - new Dictionary(); + if (!versionToGeneratorLookup.TryGetValue(generator.GetType(), out var versionDictionary)) + versionDictionary = versionToGeneratorLookup[generator.GetType()] = []; foreach (var version in generator.ApplicableVersions) - versionToGeneratorLookup[generator.GetType()][version] = generator; + versionDictionary[version] = generator; } foreach (var kvp in versionToGeneratorLookup) @@ -190,26 +126,24 @@ public static void Generate(Il2CppStructWrapperGeneratorOptions options) VersionSpecificGenerator? last = null; foreach (var kvp2 in kvp.Value.Where(kvp2 => last is null || last != kvp2.Value)) { + var versionString = kvp2.Key is { Type: UnityVersionType.Alpha, TypeNumber: 0 } + ? kvp2.Key.ToStringWithoutType() + : kvp2.Key.ToString(); kvp2.Value.HandlerGenerator.HandlerClass.Attributes.Add( - $"ApplicableToUnityVersionsSince(\"{kvp2.Key.ToStringShort()}\")"); + $"ApplicableToUnityVersionsSince(\"{versionString}\")"); last = kvp2.Value; } } foreach (var generator in SGenerators.Values.SelectMany(x => x)) { - var generatorOutputDir = - Path.Combine(options.OutputDirectory, - generator.NativeStructGenerator.CppClass.Name.Replace("Il2Cpp", string.Empty)); - if (!Directory.Exists(generatorOutputDir)) - Directory.CreateDirectory(generatorOutputDir); + var generatorOutputDirectory = Path.Join(options.OutputDirectory, generator.NativeStructGenerator.CppClass.Name.Replace("Il2Cpp", null)); + Directory.CreateDirectory(generatorOutputDirectory); CodeGenFile file = new() { - Namespace = - $"Il2CppInterop.Runtime.Runtime.VersionSpecific.{generator.NativeStructGenerator.CppClass.Name.Replace("Il2Cpp", string.Empty)}", + Namespace = $"Il2CppInterop.Runtime.Runtime.VersionSpecific.{generator.NativeStructGenerator.CppClass.Name.Replace("Il2Cpp", null)}", Usings = { - "System", "System.Runtime.InteropServices" }, Elements = @@ -217,12 +151,57 @@ public static void Generate(Il2CppStructWrapperGeneratorOptions options) generator.HandlerGenerator.HandlerClass } }; - foreach (var extraUsing in generator.ExtraUsings) - file.Usings.Add(extraUsing); - file.WriteTo(Path.Combine(generatorOutputDir, - $"{generator.NativeStructGenerator.NativeStruct.Name.Replace("Il2Cpp", string.Empty)}.cs")); + file.Usings.AddRange(generator.ExtraUsings); + file.WriteTo(Path.Join(generatorOutputDirectory, $"{generator.NativeStructGenerator.NativeStruct.Name.Replace("Il2Cpp", null)}.cs")); } Logger = null; } + + private static string ProcessHeaderText(string headerText) + { + const string HeaderPrefix = """ + #line 1 "Prefix.h" + typedef int int32_t; + typedef unsigned int uint32_t; + typedef short int16_t; + typedef unsigned short uint16_t; + typedef char int8_t; + typedef unsigned char uint8_t; + typedef long long int64_t; + typedef unsigned long long uint64_t; + typedef long intptr_t; + typedef unsigned long uintptr_t; + """; + + string processedHeaderText; + if (headerText.Contains("struct ParameterInfo", StringComparison.Ordinal)) + { + processedHeaderText = $""" + {HeaderPrefix} + #line 1 "Header.h" + {headerText} + """; + } + else + { + // ParameterInfo was removed in v27, but we add it back in manually. + processedHeaderText = $$""" + {{HeaderPrefix}} + #line 1 "ParameterInfo.h" + typedef struct Il2CppType Il2CppType; + typedef struct ParameterInfo + { + const Il2CppType* parameter_type; + } ParameterInfo; + #line 1 "Header.h" + {{headerText.Replace("const Il2CppType** parameters;", "const ParameterInfo* parameters;")}} + """; + } + + return processedHeaderText; + } + + [GeneratedRegex(@"const int METADATA_VERSION = ([0-9]+);")] + private static partial Regex MetadataVersionRegex { get; } } diff --git a/Il2CppInterop.StructGenerator/Il2CppStructWrapperGeneratorOptions.cs b/Il2CppInterop.StructGenerator/Il2CppStructWrapperGeneratorOptions.cs new file mode 100644 index 00000000..ce15f49d --- /dev/null +++ b/Il2CppInterop.StructGenerator/Il2CppStructWrapperGeneratorOptions.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Logging; + +namespace Il2CppInterop.StructGenerator; + +public record Il2CppStructWrapperGeneratorOptions( + string HeadersDirectory, + string OutputDirectory, + ILogger? Logger +); diff --git a/Il2CppInterop.StructGenerator/NativeStructGenerator.cs b/Il2CppInterop.StructGenerator/NativeStructGenerator.cs index 17d61e98..cfefeb4e 100644 --- a/Il2CppInterop.StructGenerator/NativeStructGenerator.cs +++ b/Il2CppInterop.StructGenerator/NativeStructGenerator.cs @@ -1,7 +1,6 @@ -using CppAst; +using System.Diagnostics; +using CppAst; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; -using Il2CppInterop.StructGenerator.Utilities; namespace Il2CppInterop.StructGenerator; @@ -17,13 +16,13 @@ public NativeStructGenerator(string metadataSuffix, CppClass cppClass) FillStruct(); } - public List FieldsToImport { get; } = new(); + public List FieldsToImport { get; } = []; public CodeGenStruct NativeStruct { get; } public CppClass CppClass { get; } private void FillStruct() { - List bitfields = new(); + List bitfields = []; CodeGenEnum? lastBitfield = null; var bitfieldNextBit = 0; @@ -69,15 +68,7 @@ void FinalizeBitfield() bitfieldNextBit = 0; } - foreach (var baseType in CppClass.BaseTypes) - { - var normalizedType = ConversionUtils.CppTypeToCSharpName(baseType.Type, out var needsImport); - CodeGenField field = new(normalizedType, ElementProtection.Public, - ConversionUtils.NormalizeName(baseType.Type.GetDisplayName().Replace("Il2Cpp", string.Empty) - .ToLower())); - if (needsImport) FieldsToImport.Add(field); - NativeStruct.Fields.Add(field); - } + Debug.Assert(CppClass.BaseTypes.Count == 0); foreach (var field in CppClass.Fields) { @@ -92,8 +83,7 @@ void FinalizeBitfield() else { FinalizeBitfield(); - CodeGenField codeGenField = new(normalizedType, ElementProtection.Public, - ConversionUtils.NormalizeName(field.Name)); + CodeGenField codeGenField = new(normalizedType, ElementProtection.Public, GetFieldName(field)); if (needsImport) FieldsToImport.Add(codeGenField); NativeStruct.Fields.Add(codeGenField); } @@ -103,8 +93,32 @@ void FinalizeBitfield() NativeStruct.NestedElements.AddRange(bitfields); } - public string Build() + private static string GetFieldName(CppField field) { - return NativeStruct.Build(); + var name = field.Name; + if (name is "object" or "class" or "struct" or "base") + { + return $"_{name}"; + } + else if (name.Length == 0) + { + if (field.Parent is CppClass { Name: "Il2CppMethodInfo" } && field.Type is CppClass { ClassKind: CppClassKind.Union } unionType) + { + // IlCppMethodInfo has two unnamed union fields + if (unionType.Fields.Any(f => f.Name is "rgctx_data")) + { + return "runtime_data"; + } + if (unionType.Fields.Any(f => f.Name is "genericMethod")) + { + return "generic_data"; + } + } + throw new ArgumentException("Field has no name and is not part of a known union", nameof(field)); + } + else + { + return name; + } } } diff --git a/Il2CppInterop.StructGenerator/Program.cs b/Il2CppInterop.StructGenerator/Program.cs new file mode 100644 index 00000000..7b759a75 --- /dev/null +++ b/Il2CppInterop.StructGenerator/Program.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.Logging; + +namespace Il2CppInterop.StructGenerator; + +internal static class Program +{ + internal static void Main(string[] args) + { + // Directory that contains il2cpp headers. Directory must contain .h files named with their unity version. + // https://github.com/nneonneo/Il2CppVersions/tree/master/headers + var headersDirectory = args[0]; + + // Directory to write managed struct wrapper sources to + var outputDirectory = args[1]; + + Il2CppStructWrapperGeneratorOptions options = new(headersDirectory, outputDirectory, new ConsoleLogger()); + Il2CppStructWrapperGenerator.Generate(options); + } + + private sealed class ConsoleLogger : ILogger + { + public IDisposable? BeginScope(TState state) where TState : notnull + { + return null; + } + + public bool IsEnabled(LogLevel logLevel) + { + return true; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + Console.WriteLine($"[{logLevel}] {formatter.Invoke(state, exception)}"); + } + } +} diff --git a/Il2CppInterop.StructGenerator/Resources/Config.cs b/Il2CppInterop.StructGenerator/Resources/Config.cs deleted file mode 100644 index f89a2ec5..00000000 --- a/Il2CppInterop.StructGenerator/Resources/Config.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Il2CppInterop.StructGenerator.TypeGenerators; - -namespace Il2CppInterop.StructGenerator.Resources; - -internal static class Config -{ - // NOTE: Ignores are handled BEFORE renames - public static readonly string[] ClassForcedIgnores = - { - // Ignore the reflection structs in object-internals.h - "Il2CppPropertyInfo", - "Il2CppMethodInfo" - }; - - public static readonly Dictionary ClassRenames = new() - { - ["TypeInfo"] = "Il2CppClass", - ["FieldInfo"] = "Il2CppFieldInfo", - ["EventInfo"] = "Il2CppEventInfo", - ["PropertyInfo"] = "Il2CppPropertyInfo", - ["MethodInfo"] = "Il2CppMethodInfo" - }; - - public static readonly Dictionary ClassToGenerator = new() - { - ["Il2CppClass"] = typeof(Il2CppClassGenerator), - ["Il2CppType"] = typeof(Il2CppTypeGenerator), - ["Il2CppAssembly"] = typeof(Il2CppAssemblyGenerator), - ["Il2CppAssemblyName"] = typeof(Il2CppAssemblyNameGenerator), - ["Il2CppFieldInfo"] = typeof(Il2CppFieldInfoGenerator), - ["Il2CppImage"] = typeof(Il2CppImageGenerator), - ["Il2CppEventInfo"] = typeof(Il2CppEventInfoGenerator), - ["Il2CppException"] = typeof(Il2CppExceptionGenerator), - ["Il2CppPropertyInfo"] = typeof(Il2CppPropertyInfoGenerator), - ["Il2CppMethodInfo"] = typeof(Il2CppMethodInfoGenerator) - }; - - public static readonly string[] MetadataVersionContainers = - { - Path.Combine("vm", "MetadataCache.cpp"), - Path.Combine("vm", "GlobalMetadata.cpp") - }; -} diff --git a/Il2CppInterop.StructGenerator/StructHandlerGenerator.cs b/Il2CppInterop.StructGenerator/StructHandlerGenerator.cs index aa32cc5b..4893087a 100644 --- a/Il2CppInterop.StructGenerator/StructHandlerGenerator.cs +++ b/Il2CppInterop.StructGenerator/StructHandlerGenerator.cs @@ -1,14 +1,13 @@ -using System.Text; +using System.CodeDom.Compiler; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; namespace Il2CppInterop.StructGenerator; internal class StructHandlerGenerator { public StructHandlerGenerator(string name, string handlerInterface, string nativeInterface, string nativeStub, - NativeStructGenerator nativeStructGen, List? parameterOverride = null, - Action? extraBodyProvider = null) + NativeStructGenerator nativeStructGen, IEnumerable? parameterOverride = null, + Action? extraBodyProvider = null) { NativeGenerator = nativeStructGen; HandlerClass = new CodeGenClass(ElementProtection.Public, name) @@ -22,17 +21,18 @@ public StructHandlerGenerator(string name, string handlerInterface, string nativ }); CodeGenMethod createNewMethod = new(nativeInterface, ElementProtection.Public, "CreateNewStruct") { - MethodBodyBuilder = builder => + MethodBodyBuilder = writer => { - builder.Append("IntPtr ptr = Marshal.AllocHGlobal("); - if (SizeProviderOverride != null) builder.AppendLine($"{SizeProviderOverride});"); - else builder.AppendLine("Size());"); - builder.AppendLine( + writer.Write("nint ptr = Marshal.AllocHGlobal("); + if (SizeProviderOverride != null) + writer.WriteLine($"{SizeProviderOverride});"); + else + writer.WriteLine("Size());"); + writer.WriteLine( $"{nativeStructGen.NativeStruct.Name}* _ = ({nativeStructGen.NativeStruct.Name}*)ptr;"); - builder.AppendLine("*_ = default;"); - if (extraBodyProvider != null) - extraBodyProvider(builder); - builder.Append("return new NativeStructWrapper(ptr);"); + writer.WriteLine("*_ = default;"); + extraBodyProvider?.Invoke(writer); + writer.WriteLine("return new NativeStructWrapper(ptr);"); } }; if (parameterOverride != null) @@ -41,10 +41,10 @@ public StructHandlerGenerator(string name, string handlerInterface, string nativ HandlerClass.Methods.Add(new CodeGenMethod(nativeInterface, ElementProtection.Public, "Wrap") { Parameters = { new CodeGenParameter($"{nativeStub}*", "ptr") }, - MethodBodyBuilder = builder => + MethodBodyBuilder = writer => { - builder.AppendLine("if (ptr == null) return null;"); - builder.Append("return new NativeStructWrapper((IntPtr)ptr);"); + writer.WriteLine("if (ptr == null) return null;"); + writer.WriteLine("return new NativeStructWrapper((nint)ptr);"); } }); } diff --git a/Il2CppInterop.StructGenerator/StructWrapperGenerator.cs b/Il2CppInterop.StructGenerator/StructWrapperGenerator.cs index dadabb2c..9203d57d 100644 --- a/Il2CppInterop.StructGenerator/StructWrapperGenerator.cs +++ b/Il2CppInterop.StructGenerator/StructWrapperGenerator.cs @@ -1,29 +1,7 @@ using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; namespace Il2CppInterop.StructGenerator; -internal class ByRefWrapper -{ - public ByRefWrapper(string wrappedType, string wrappedName, string[] nativeNames, string? forcedNativeType = null, - bool addNotSupportedIfNotExist = false, bool makeDummyIfNotExist = false) - { - WrappedType = wrappedType; - WrappedName = wrappedName; - NativeNames = nativeNames; - ForcedNativeType = forcedNativeType; - AddNotSupported = addNotSupportedIfNotExist; - MakeDummyIfNotSupported = makeDummyIfNotExist; - } - - public string WrappedType { get; } - public string WrappedName { get; } - public string[] NativeNames { get; } - public string? ForcedNativeType { get; } - public bool AddNotSupported { get; } - public bool MakeDummyIfNotSupported { get; } -} - internal class StructWrapperGenerator { public StructWrapperGenerator(string nativeInterface) @@ -34,11 +12,11 @@ public StructWrapperGenerator(string nativeInterface) }; WrapperClass.Methods.Add(new CodeGenConstructor("NativeStructWrapper", ElementProtection.Public) { - Parameters = { new CodeGenParameter("IntPtr", "ptr") }, + Parameters = { new CodeGenParameter("nint", "ptr") }, ImmediateReturn = "Pointer = ptr" }); - WrapperClass.Properties.Add(new CodeGenProperty("IntPtr", ElementProtection.Public, "Pointer") + WrapperClass.Properties.Add(new CodeGenProperty("nint", ElementProtection.Public, "Pointer") { EmptyGet = true }); diff --git a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppAssemblyGenerator.cs b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppAssemblyGenerator.cs index 0cadaa54..2283920f 100644 --- a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppAssemblyGenerator.cs +++ b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppAssemblyGenerator.cs @@ -1,27 +1,26 @@ using CppAst; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; namespace Il2CppInterop.StructGenerator.TypeGenerators; internal class Il2CppAssemblyGenerator : VersionSpecificGenerator { - public Il2CppAssemblyGenerator(string metadataSuffix, CppClass nativeClass, - Func? dependencyResolver = null) : base(metadataSuffix, nativeClass, dependencyResolver) + public Il2CppAssemblyGenerator(string metadataSuffix, CppClass nativeClass) : base(metadataSuffix, nativeClass) { } + public override string CppClassName => "Il2CppAssembly"; protected override string HandlerName => "NativeAssemblyStructHandler"; protected override string HandlerInterface => "INativeAssemblyStructHandler"; protected override string NativeInterface => "INativeAssemblyStruct"; protected override string NativeStub => "Il2CppAssembly"; - protected override List? WrapperFields => null; - - protected override List? WrapperProperties => new() - { + protected override IReadOnlyList? WrapperProperties => + [ new CodeGenProperty($"{NativeStub}*", ElementProtection.Public, "AssemblyPointer") - { ImmediateGet = $"({NativeStub}*)Pointer" }, + { + ImmediateGet = $"({NativeStub}*)Pointer" + }, new CodeGenProperty("INativeAssemblyNameStruct", ElementProtection.Public, "Name") { GetMethod = new CodeGenMethod("INativeAssemblyNameStruct", ElementProtection.Private, "get") @@ -33,12 +32,10 @@ public Il2CppAssemblyGenerator(string metadataSuffix, CppClass nativeClass, ImmediateReturn = $"_->aname = *({GetNativeField("aname")!.Type}*)Name.AssemblyNamePointer" } } - }; - - protected override List? ByRefWrappers => new() - { - new ByRefWrapper("Il2CppImage*", "Image", new[] { "image" }, addNotSupportedIfNotExist: true) - }; + ]; - protected override List? BitfieldAccessors => null; + protected override IReadOnlyList? ByRefWrappers => + [ + new ByRefWrapper("Il2CppImage*", "Image", ["image"], addNotSupportedIfNotExist: true) + ]; } diff --git a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppAssemblyNameGenerator.cs b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppAssemblyNameGenerator.cs index 9c7bba13..4f36d963 100644 --- a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppAssemblyNameGenerator.cs +++ b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppAssemblyNameGenerator.cs @@ -1,40 +1,37 @@ using CppAst; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; namespace Il2CppInterop.StructGenerator.TypeGenerators; internal class Il2CppAssemblyNameGenerator : VersionSpecificGenerator { - public Il2CppAssemblyNameGenerator(string metadataSuffix, CppClass nativeClass, - Func? dependencyResolver = null) : base(metadataSuffix, nativeClass, dependencyResolver) + public Il2CppAssemblyNameGenerator(string metadataSuffix, CppClass nativeClass) : base(metadataSuffix, nativeClass) { } + public override string CppClassName => "Il2CppAssemblyName"; protected override string HandlerName => "NativeAssemblyNameStructHandler"; protected override string HandlerInterface => "INativeAssemblyNameStructHandler"; protected override string NativeInterface => "INativeAssemblyNameStruct"; protected override string NativeStub => "Il2CppAssemblyName"; - protected override List? WrapperFields => null; - - protected override List? WrapperProperties => new() - { + protected override IReadOnlyList? WrapperProperties => + [ new CodeGenProperty($"{NativeStub}*", ElementProtection.Public, "AssemblyNamePointer") - { ImmediateGet = $"({NativeStub}*)Pointer" } - }; - - protected override List? ByRefWrappers => new() - { - new ByRefWrapper("IntPtr", "Name", new[] { "name", "nameIndex" }), - new ByRefWrapper("IntPtr", "Culture", new[] { "culture", "cultureIndex" }), - new ByRefWrapper("IntPtr", "PublicKey", new[] { "public_key", "publicKeyIndex" }), - new ByRefWrapper("int", "Major", new[] { "major" }), - new ByRefWrapper("int", "Minor", new[] { "minor" }), - new ByRefWrapper("int", "Build", new[] { "build" }), - new ByRefWrapper("int", "Revision", new[] { "revision" }), - new ByRefWrapper("ulong", "PublicKeyToken", new[] { "public_key_token", "publicKeyToken" }) - }; + { + ImmediateGet = $"({NativeStub}*)Pointer" + } + ]; - protected override List? BitfieldAccessors => null; + protected override IReadOnlyList? ByRefWrappers => + [ + new ByRefWrapper("nint", "Name", ["name", "nameIndex"]), + new ByRefWrapper("nint", "Culture", ["culture", "cultureIndex"]), + new ByRefWrapper("nint", "PublicKey", ["public_key", "publicKeyIndex"]), + new ByRefWrapper("int", "Major", ["major"]), + new ByRefWrapper("int", "Minor", ["minor"]), + new ByRefWrapper("int", "Build", ["build"]), + new ByRefWrapper("int", "Revision", ["revision"]), + new ByRefWrapper("ulong", "PublicKeyToken", ["public_key_token", "publicKeyToken"]) + ]; } diff --git a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppClassGenerator.cs b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppClassGenerator.cs index f84fe987..c3958dd1 100644 --- a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppClassGenerator.cs +++ b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppClassGenerator.cs @@ -1,81 +1,87 @@ -using System.Text; +using System.CodeDom.Compiler; using CppAst; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; namespace Il2CppInterop.StructGenerator.TypeGenerators; internal class Il2CppClassGenerator : VersionSpecificGenerator { - public Il2CppClassGenerator(string metadataSuffix, CppClass nativeClass, - Func? dependencyResolver = null) : base(metadataSuffix, nativeClass, dependencyResolver) + public Il2CppClassGenerator(string metadataSuffix, CppClass nativeClass) : base(metadataSuffix, nativeClass) { - var lastField = NativeStructGenerator.NativeStruct.Fields.Last(); - if (lastField.Name == "vtable") NativeStructGenerator.NativeStruct.Fields.Remove(lastField); + var lastField = NativeStructGenerator.NativeStruct.Fields[^1]; + if (lastField.Name == "vtable") + NativeStructGenerator.NativeStruct.Fields.RemoveAt(NativeStructGenerator.NativeStruct.Fields.Count - 1); + ExtraUsings.Add("Il2CppInterop.Runtime.Runtime.VersionSpecific.Type"); } + public override string CppClassName => "Il2CppClass"; protected override string HandlerName => "NativeClassStructHandler"; protected override string HandlerInterface => "INativeClassStructHandler"; protected override string NativeInterface => "INativeClassStruct"; protected override string NativeStub => "Il2CppClass"; - protected override List? CreateNewParameters => - new() { new CodeGenParameter("int", "vTableSlots") }; + protected override IEnumerable? CreateNewParameters => [new CodeGenParameter("int", "vTableSlots")]; protected override string? SizeOverride => "Size() + sizeof(VirtualInvokeData) * vTableSlots"; - protected override List? WrapperFields => null; - private bool ByValArgIsPointer => GetNativeField("byval_arg")?.FieldType.EndsWith("*") ?? false; - private bool ThisArgIsPointer => GetNativeField("this_arg")?.FieldType.EndsWith("*") ?? false; + private bool ByValArgIsPointer => GetNativeField("byval_arg")?.FieldType.EndsWith('*') ?? false; + private bool ThisArgIsPointer => GetNativeField("this_arg")?.FieldType.EndsWith('*') ?? false; - protected override Action? CreateNewExtraBody => builder => + protected override Action? CreateNewExtraBody => writer => { if (GetNativeField("vtable") is not null) { - builder.AppendLine("Marshal.FreeHGlobal(ptr);"); - builder.AppendLine( - $"throw new NotSupportedException(\"The native struct '{NativeStructGenerator.NativeStruct.Name}' has a vtable field which is not currently supported!\");"); + writer.WriteLine("Marshal.FreeHGlobal(ptr);"); + writer.WriteLine( + $"throw new System.NotSupportedException(\"The native struct '{NativeStructGenerator.NativeStruct.Name}' has a vtable field which is not currently supported!\");"); return; } - if (ByValArgIsPointer) builder.AppendLine("_->byval_arg = UnityVersionHandler.NewType().TypePointer;"); - if (ThisArgIsPointer) builder.AppendLine("_->this_arg = UnityVersionHandler.NewType().TypePointer;"); + if (ByValArgIsPointer) + writer.WriteLine("_->byval_arg = UnityVersionHandler.NewType().TypePointer;"); + if (ThisArgIsPointer) + writer.WriteLine("_->this_arg = UnityVersionHandler.NewType().TypePointer;"); }; - protected override List? WrapperProperties + protected override IReadOnlyList WrapperProperties { get { - List properties = new() + CodeGenProperty byvalArg = new("INativeTypeStruct", ElementProtection.Public, "ByValArg") { - new CodeGenProperty("IntPtr", ElementProtection.Public, "VTable") - { - ImmediateGet = $"IntPtr.Add(Pointer, sizeof({NativeStructGenerator.NativeStruct.Name}))" - }, - new CodeGenProperty($"{NativeStub}*", ElementProtection.Public, "ClassPointer") - { ImmediateGet = $"({NativeStub}*)Pointer" } + ImmediateGet = "UnityVersionHandler.Wrap(" }; - CodeGenProperty byvalArg = new("INativeTypeStruct", ElementProtection.Public, "ByValArg") - { ImmediateGet = "UnityVersionHandler.Wrap(" }; CodeGenProperty thisArg = new("INativeTypeStruct", ElementProtection.Public, "ThisArg") - { ImmediateGet = "UnityVersionHandler.Wrap(" }; - AddExtraUsing("Il2CppInterop.Runtime.Runtime.VersionSpecific.Type"); - if (!ByValArgIsPointer) byvalArg.ImmediateGet += "(Il2CppTypeStruct*)&"; - if (!ThisArgIsPointer) thisArg.ImmediateGet += "(Il2CppTypeStruct*)&"; + { + ImmediateGet = "UnityVersionHandler.Wrap(" + }; + if (!ByValArgIsPointer) + byvalArg.ImmediateGet += "(Il2CppTypeStruct*)&"; + if (!ThisArgIsPointer) + thisArg.ImmediateGet += "(Il2CppTypeStruct*)&"; byvalArg.ImmediateGet += "_->byval_arg)"; thisArg.ImmediateGet += "_->this_arg)"; - properties.Add(byvalArg); - properties.Add(thisArg); - return properties; + return [ + new CodeGenProperty("nint", ElementProtection.Public, "VTable") + { + ImmediateGet = $"nint.Add(Pointer, sizeof({NativeStructGenerator.NativeStruct.Name}))" + }, + new CodeGenProperty($"{NativeStub}*", ElementProtection.Public, "ClassPointer") + { + ImmediateGet = $"({NativeStub}*)Pointer" + }, + byvalArg, + thisArg, + ]; } } - protected override List? BitfieldAccessors => new() - { + protected override IReadOnlyList BitfieldAccessors => + [ new BitfieldAccessor("ValueType", "valuetype", defaultGetter: "ByValArg.ValueType && ThisArg.ValueType", - defaultSetBuilder: builder => + defaultSetBuilder: writer => { - builder.AppendLine("ByValArg.ValueType = value;"); - builder.Append("ThisArg.ValueType = value;"); + writer.WriteLine("ByValArg.ValueType = value;"); + writer.WriteLine("ThisArg.ValueType = value;"); }), new BitfieldAccessor("Initialized", "initialized"), new BitfieldAccessor("EnumType", "enumtype"), @@ -85,32 +91,32 @@ protected override List? WrapperProperties new BitfieldAccessor("HasFinalize", "has_finalize"), new BitfieldAccessor("IsVtableInitialized", "is_vtable_initialized", defaultGetter: "false"), new BitfieldAccessor("InitializedAndNoError", "initialized_and_no_error", defaultGetter: "true") - }; + ]; - protected override List? ByRefWrappers => new() - { - new ByRefWrapper("uint", "InstanceSize", new[] { "instance_size" }), - new ByRefWrapper("ushort", "VtableCount", new[] { "vtable_count" }), - new ByRefWrapper("ushort", "InterfaceCount", new[] { "interfaces_count" }), - new ByRefWrapper("ushort", "InterfaceOffsetsCount", new[] { "interface_offsets_count" }), - new ByRefWrapper("byte", "TypeHierarchyDepth", new[] { "typeHierarchyDepth" }), - new ByRefWrapper("int", "NativeSize", new[] { "native_size" }), - new ByRefWrapper("uint", "ActualSize", new[] { "actualSize" }), - new ByRefWrapper("ushort", "MethodCount", new[] { "method_count" }), - new ByRefWrapper("ushort", "FieldCount", new[] { "field_count" }), - new ByRefWrapper("Il2CppClassAttributes", "Flags", new[] { "flags" }), - new ByRefWrapper("IntPtr", "Name", new[] { "name" }), - new ByRefWrapper("IntPtr", "Namespace", new[] { "namespaze" }), - new ByRefWrapper("Il2CppImage*", "Image", new[] { "image" }), - new ByRefWrapper("Il2CppClass*", "Parent", new[] { "parent" }), - new ByRefWrapper("Il2CppClass*", "ElementClass", new[] { "element_class" }), - new ByRefWrapper("Il2CppClass*", "CastClass", new[] { "castClass" }), - new ByRefWrapper("Il2CppClass*", "DeclaringType", new[] { "declaringType" }), - new ByRefWrapper("Il2CppClass*", "Class", new[] { "klass" }, makeDummyIfNotExist: true), - new ByRefWrapper("Il2CppFieldInfo*", "Fields", new[] { "fields" }), - new ByRefWrapper("Il2CppMethodInfo**", "Methods", new[] { "methods" }), - new ByRefWrapper("Il2CppClass**", "ImplementedInterfaces", new[] { "implementedInterfaces" }), - new ByRefWrapper("Il2CppRuntimeInterfaceOffsetPair*", "InterfaceOffsets", new[] { "interfaceOffsets" }), - new ByRefWrapper("Il2CppClass**", "TypeHierarchy", new[] { "typeHierarchy" }) - }; + protected override IReadOnlyList ByRefWrappers => + [ + new ByRefWrapper("uint", "InstanceSize", ["instance_size"]), + new ByRefWrapper("ushort", "VtableCount", ["vtable_count"]), + new ByRefWrapper("ushort", "InterfaceCount", ["interfaces_count"]), + new ByRefWrapper("ushort", "InterfaceOffsetsCount", ["interface_offsets_count"]), + new ByRefWrapper("byte", "TypeHierarchyDepth", ["typeHierarchyDepth"]), + new ByRefWrapper("int", "NativeSize", ["native_size"]), + new ByRefWrapper("uint", "ActualSize", ["actualSize"]), + new ByRefWrapper("ushort", "MethodCount", ["method_count"]), + new ByRefWrapper("ushort", "FieldCount", ["field_count"]), + new ByRefWrapper("Il2CppClassAttributes", "Flags", ["flags"]), + new ByRefWrapper("nint", "Name", ["name"]), + new ByRefWrapper("nint", "Namespace", ["namespaze"]), + new ByRefWrapper("Il2CppImage*", "Image", ["image"]), + new ByRefWrapper("Il2CppClass*", "Parent", ["parent"]), + new ByRefWrapper("Il2CppClass*", "ElementClass", ["element_class"]), + new ByRefWrapper("Il2CppClass*", "CastClass", ["castClass"]), + new ByRefWrapper("Il2CppClass*", "DeclaringType", ["declaringType"]), + new ByRefWrapper("Il2CppClass*", "Class", ["klass"], makeDummyIfNotExist: true), + new ByRefWrapper("Il2CppFieldInfo*", "Fields", ["fields"]), + new ByRefWrapper("Il2CppMethodInfo**", "Methods", ["methods"]), + new ByRefWrapper("Il2CppClass**", "ImplementedInterfaces", ["implementedInterfaces"]), + new ByRefWrapper("Il2CppRuntimeInterfaceOffsetPair*", "InterfaceOffsets", ["interfaceOffsets"]), + new ByRefWrapper("Il2CppClass**", "TypeHierarchy", ["typeHierarchy"]) + ]; } diff --git a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppEventInfoGenerator.cs b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppEventInfoGenerator.cs index ad1c7d97..fe9be9ad 100644 --- a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppEventInfoGenerator.cs +++ b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppEventInfoGenerator.cs @@ -1,37 +1,35 @@ using CppAst; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; namespace Il2CppInterop.StructGenerator.TypeGenerators; internal class Il2CppEventInfoGenerator : VersionSpecificGenerator { - public Il2CppEventInfoGenerator(string metadataSuffix, CppClass nativeClass, - Func? dependencyResolver = null) : base(metadataSuffix, nativeClass, dependencyResolver) + public Il2CppEventInfoGenerator(string metadataSuffix, CppClass nativeClass) : base(metadataSuffix, nativeClass) { } + public override string CppClassName => "Il2CppEventInfo"; protected override string HandlerName => "NativeEventInfoStructHandler"; protected override string HandlerInterface => "INativeEventInfoStructHandler"; protected override string NativeInterface => "INativeEventInfoStruct"; protected override string NativeStub => "Il2CppEventInfo"; - protected override List? WrapperFields => null; - protected override List? WrapperProperties => new() - { + protected override IReadOnlyList? WrapperProperties => + [ new CodeGenProperty($"{NativeStub}*", ElementProtection.Public, "EventInfoPointer") - { ImmediateGet = $"({NativeStub}*)Pointer" } - }; - - protected override List? ByRefWrappers => new() - { - new ByRefWrapper("IntPtr", "Name", new[] { "name" }), - new ByRefWrapper("Il2CppTypeStruct*", "EventType", new[] { "eventType" }), - new ByRefWrapper("Il2CppClass*", "Parent", new[] { "parent" }), - new ByRefWrapper("Il2CppMethodInfo*", "Add", new[] { "add" }), - new ByRefWrapper("Il2CppMethodInfo*", "Remove", new[] { "remove" }), - new ByRefWrapper("Il2CppMethodInfo*", "Raise", new[] { "raise" }) - }; + { + ImmediateGet = $"({NativeStub}*)Pointer" + } + ]; - protected override List? BitfieldAccessors => null; + protected override IReadOnlyList? ByRefWrappers => + [ + new ByRefWrapper("nint", "Name", ["name"]), + new ByRefWrapper("Il2CppTypeStruct*", "EventType", ["eventType"]), + new ByRefWrapper("Il2CppClass*", "Parent", ["parent"]), + new ByRefWrapper("Il2CppMethodInfo*", "Add", ["add"]), + new ByRefWrapper("Il2CppMethodInfo*", "Remove", ["remove"]), + new ByRefWrapper("Il2CppMethodInfo*", "Raise", ["raise"]) + ]; } diff --git a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppExceptionGenerator.cs b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppExceptionGenerator.cs index 321fd24d..8fe5ddd2 100644 --- a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppExceptionGenerator.cs +++ b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppExceptionGenerator.cs @@ -1,38 +1,35 @@ using CppAst; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; namespace Il2CppInterop.StructGenerator.TypeGenerators; internal class Il2CppExceptionGenerator : VersionSpecificGenerator { - public Il2CppExceptionGenerator(string metadataSuffix, CppClass nativeClass, - Func? dependencyResolver = null) : base(metadataSuffix, nativeClass, dependencyResolver) + public Il2CppExceptionGenerator(string metadataSuffix, CppClass nativeClass) : base(metadataSuffix, nativeClass) { } + public override string CppClassName => "Il2CppException"; protected override string HandlerName => "NativeExceptionStructHandler"; protected override string HandlerInterface => "INativeExceptionStructHandler"; protected override string NativeInterface => "INativeExceptionStruct"; protected override string NativeStub => "Il2CppException"; - protected override List? WrapperFields => null; - - protected override List? WrapperProperties => new() - { + protected override IReadOnlyList? WrapperProperties => + [ new CodeGenProperty($"{NativeStub}*", ElementProtection.Public, "ExceptionPointer") - { ImmediateGet = $"({NativeStub}*)Pointer" } - }; - - protected override List? ByRefWrappers => new() - { - new ByRefWrapper("Il2CppException*", "InnerException", new[] { "inner_ex" }), - new ByRefWrapper("Il2CppString*", "Message", new[] { "message" }), - new ByRefWrapper("Il2CppString*", "HelpLink", new[] { "_helpURL", "help_link" }), - new ByRefWrapper("Il2CppString*", "ClassName", new[] { "className", "class_name" }), - new ByRefWrapper("Il2CppString*", "StackTrace", new[] { "stack_trace" }), - new ByRefWrapper("Il2CppString*", "RemoteStackTrace", new[] { "remote_stack_trace" }) - }; + { + ImmediateGet = $"({NativeStub}*)Pointer" + } + ]; - protected override List? BitfieldAccessors => null; + protected override IReadOnlyList? ByRefWrappers => + [ + new ByRefWrapper("Il2CppException*", "InnerException", ["inner_ex"]), + new ByRefWrapper("Il2CppString*", "Message", ["message"]), + new ByRefWrapper("Il2CppString*", "HelpLink", ["_helpURL", "help_link"]), + new ByRefWrapper("Il2CppString*", "ClassName", ["className", "class_name"]), + new ByRefWrapper("Il2CppString*", "StackTrace", ["stack_trace"]), + new ByRefWrapper("Il2CppString*", "RemoteStackTrace", ["remote_stack_trace"]) + ]; } diff --git a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppFieldInfoGenerator.cs b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppFieldInfoGenerator.cs index 3805e46e..9a66ed70 100644 --- a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppFieldInfoGenerator.cs +++ b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppFieldInfoGenerator.cs @@ -1,36 +1,33 @@ using CppAst; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; namespace Il2CppInterop.StructGenerator.TypeGenerators; internal class Il2CppFieldInfoGenerator : VersionSpecificGenerator { - public Il2CppFieldInfoGenerator(string metadataSuffix, CppClass nativeClass, - Func? dependencyResolver = null) : base(metadataSuffix, nativeClass, dependencyResolver) + public Il2CppFieldInfoGenerator(string metadataSuffix, CppClass nativeClass) : base(metadataSuffix, nativeClass) { } + public override string CppClassName => "Il2CppFieldInfo"; protected override string HandlerName => "NativeFieldInfoStructHandler"; protected override string HandlerInterface => "INativeFieldInfoStructHandler"; protected override string NativeInterface => "INativeFieldInfoStruct"; protected override string NativeStub => "Il2CppFieldInfo"; - protected override List? WrapperFields => null; - - protected override List? WrapperProperties => new() - { + protected override IReadOnlyList? WrapperProperties => + [ new CodeGenProperty($"{NativeStub}*", ElementProtection.Public, "FieldInfoPointer") - { ImmediateGet = $"({NativeStub}*)Pointer" } - }; - - protected override List? ByRefWrappers => new() - { - new ByRefWrapper("IntPtr", "Name", new[] { "name" }), - new ByRefWrapper("Il2CppTypeStruct*", "Type", new[] { "type" }), - new ByRefWrapper("Il2CppClass*", "Parent", new[] { "parent" }), - new ByRefWrapper("int", "Offset", new[] { "offset" }) - }; + { + ImmediateGet = $"({NativeStub}*)Pointer" + } + ]; - protected override List? BitfieldAccessors => null; + protected override IReadOnlyList? ByRefWrappers => + [ + new ByRefWrapper("nint", "Name", ["name"]), + new ByRefWrapper("Il2CppTypeStruct*", "Type", ["type"]), + new ByRefWrapper("Il2CppClass*", "Parent", ["parent"]), + new ByRefWrapper("int", "Offset", ["offset"]) + ]; } diff --git a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppImageGenerator.cs b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppImageGenerator.cs index d5fe4188..650a6a1d 100644 --- a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppImageGenerator.cs +++ b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppImageGenerator.cs @@ -1,50 +1,48 @@ -using System.Text; +using System.CodeDom.Compiler; using CppAst; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; namespace Il2CppInterop.StructGenerator.TypeGenerators; internal class Il2CppImageGenerator : VersionSpecificGenerator { - public Il2CppImageGenerator(string metadataSuffix, CppClass nativeClass, - Func? dependencyResolver = null) : base(metadataSuffix, nativeClass, dependencyResolver) + public Il2CppImageGenerator(string metadataSuffix, CppClass nativeClass) : base(metadataSuffix, nativeClass) { } + public override string CppClassName => "Il2CppImage"; protected override string HandlerName => "NativeImageStructHandler"; protected override string HandlerInterface => "INativeImageStructHandler"; protected override string NativeInterface => "INativeImageStruct"; protected override string NativeStub => "Il2CppImage"; - protected override List? WrapperFields => null; - - protected override List? WrapperProperties => new() - { + protected override IReadOnlyList? WrapperProperties => + [ new CodeGenProperty($"{NativeStub}*", ElementProtection.Public, "ImagePointer") - { ImmediateGet = $"({NativeStub}*)Pointer" }, + { + ImmediateGet = $"({NativeStub}*)Pointer" + }, new CodeGenProperty("bool", ElementProtection.Public, "HasNameNoExt") - { ImmediateGet = GetNativeField("nameNoExt") is not null ? "true" : "false" } - }; - - protected override List? ByRefWrappers => new() - { - new ByRefWrapper("Il2CppAssembly*", "Assembly", new[] { "assembly" }, addNotSupportedIfNotExist: true), - new ByRefWrapper("byte", "Dynamic", new[] { "dynamic" }, makeDummyIfNotExist: true), - new ByRefWrapper("IntPtr", "Name", new[] { "name" }), - new ByRefWrapper("IntPtr", "NameNoExt", new[] { "nameNoExt" }, addNotSupportedIfNotExist: true) - }; + { + ImmediateGet = GetNativeField("nameNoExt") is not null ? "true" : "false" + } + ]; - protected override List? BitfieldAccessors => null; + protected override IReadOnlyList? ByRefWrappers => + [ + new ByRefWrapper("Il2CppAssembly*", "Assembly", ["assembly"], addNotSupportedIfNotExist: true), + new ByRefWrapper("byte", "Dynamic", ["dynamic"], makeDummyIfNotExist: true), + new ByRefWrapper("nint", "Name", ["name"]), + new ByRefWrapper("nint", "NameNoExt", ["nameNoExt"], addNotSupportedIfNotExist: true) + ]; - protected override Action? CreateNewExtraBody => builder => + protected override Action? CreateNewExtraBody => writer => { if (GetNativeField("metadataHandle") is not null) { - builder.AppendLine( - "Il2CppImageGlobalMetadata* metadata = (Il2CppImageGlobalMetadata*)Marshal.AllocHGlobal(sizeof(Il2CppImageGlobalMetadata));"); - builder.AppendLine("metadata->image = (Il2CppImage*)_;"); - builder.AppendLine("*(Il2CppImageGlobalMetadata**)&_->metadataHandle = metadata;"); + writer.WriteLine("Il2CppImageGlobalMetadata* metadata = (Il2CppImageGlobalMetadata*)Marshal.AllocHGlobal(sizeof(Il2CppImageGlobalMetadata));"); + writer.WriteLine("metadata->image = (Il2CppImage*)_;"); + writer.WriteLine("*(Il2CppImageGlobalMetadata**)&_->metadataHandle = metadata;"); } }; } diff --git a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppMethodInfoGenerator.cs b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppMethodInfoGenerator.cs index 9452d783..0f5cd173 100644 --- a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppMethodInfoGenerator.cs +++ b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppMethodInfoGenerator.cs @@ -1,48 +1,47 @@ using CppAst; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; namespace Il2CppInterop.StructGenerator.TypeGenerators; internal class Il2CppMethodInfoGenerator : VersionSpecificGenerator { - public Il2CppMethodInfoGenerator(string metadataSuffix, CppClass nativeClass, - Func? dependencyResolver = null) : base(metadataSuffix, nativeClass, dependencyResolver) + public Il2CppMethodInfoGenerator(string metadataSuffix, CppClass nativeClass) : base(metadataSuffix, nativeClass) { } + public override string CppClassName => "Il2CppMethodInfo"; protected override string HandlerName => "NativeMethodInfoStructHandler"; protected override string HandlerInterface => "INativeMethodInfoStructHandler"; protected override string NativeInterface => "INativeMethodInfoStruct"; protected override string NativeStub => "Il2CppMethodInfo"; - protected override List? WrapperFields => null; - - protected override List? WrapperProperties => new() - { + protected override IReadOnlyList? WrapperProperties => + [ new CodeGenProperty($"{NativeStub}*", ElementProtection.Public, "MethodInfoPointer") - { ImmediateGet = $"({NativeStub}*)Pointer" } - }; + { + ImmediateGet = $"({NativeStub}*)Pointer" + } + ]; - protected override List? ByRefWrappers => new() - { - new ByRefWrapper("IntPtr", "Name", new[] { "name" }), - new ByRefWrapper("ushort", "Slot", new[] { "slot" }), - new ByRefWrapper("IntPtr", "MethodPointer", new[] { "methodPointer", "method" }), - new ByRefWrapper("IntPtr", "VirtualMethodPointer", new[] { "virtualMethodPointer", "methodPointer", "method" }), - new ByRefWrapper("Il2CppClass*", "Class", new[] { "declaring_type", "klass" }), - new ByRefWrapper("IntPtr", "InvokerMethod", new[] { "invoker_method" }), - new ByRefWrapper("Il2CppTypeStruct*", "ReturnType", new[] { "return_type" }), - new ByRefWrapper("Il2CppMethodFlags", "Flags", new[] { "flags" }), - new ByRefWrapper("byte", "ParametersCount", new[] { "parameters_count" }), - new ByRefWrapper("Il2CppParameterInfo*", "Parameters", new[] { "parameters" }), - new ByRefWrapper("uint", "Token", new[] { "token" }, addNotSupportedIfNotExist: true) - }; + protected override IReadOnlyList? ByRefWrappers => + [ + new ByRefWrapper("nint", "Name", ["name"]), + new ByRefWrapper("ushort", "Slot", ["slot"]), + new ByRefWrapper("nint", "MethodPointer", ["methodPointer", "method"]), + new ByRefWrapper("nint", "VirtualMethodPointer", ["virtualMethodPointer", "methodPointer", "method"]), + new ByRefWrapper("Il2CppClass*", "Class", ["declaring_type", "klass"]), + new ByRefWrapper("nint", "InvokerMethod", ["invoker_method"]), + new ByRefWrapper("Il2CppTypeStruct*", "ReturnType", ["return_type"]), + new ByRefWrapper("Il2CppMethodFlags", "Flags", ["flags"]), + new ByRefWrapper("byte", "ParametersCount", ["parameters_count"]), + new ByRefWrapper("Il2CppParameterInfo*", "Parameters", ["parameters"]), + new ByRefWrapper("uint", "Token", ["token"], addNotSupportedIfNotExist: true) + ]; - protected override List? BitfieldAccessors => new() - { + protected override IReadOnlyList? BitfieldAccessors => + [ new BitfieldAccessor("IsGeneric", "is_generic"), new BitfieldAccessor("IsInflated", "is_inflated"), new BitfieldAccessor("IsMarshalledFromNative", "is_marshaled_from_native", defaultGetter: "false") - }; + ]; } diff --git a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppParameterInfoGenerator.cs b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppParameterInfoGenerator.cs new file mode 100644 index 00000000..604b83cd --- /dev/null +++ b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppParameterInfoGenerator.cs @@ -0,0 +1,37 @@ +using CppAst; +using Il2CppInterop.StructGenerator.CodeGen; + +namespace Il2CppInterop.StructGenerator.TypeGenerators; + +internal class Il2CppParameterInfoGenerator : VersionSpecificGenerator +{ + public Il2CppParameterInfoGenerator(string metadataSuffix, CppClass nativeClass) : base(metadataSuffix, nativeClass) + { + } + + public override string CppClassName => "Il2CppParameterInfo"; + protected override string HandlerName => "NativeParameterInfoStructHandler"; + protected override string HandlerInterface => "INativeParameterInfoStructHandler"; + protected override string NativeInterface => "INativeParameterInfoStruct"; + protected override string NativeStub => "Il2CppParameterInfo"; + + protected override IReadOnlyList? WrapperProperties => + [ + new CodeGenProperty($"{NativeStub}*", ElementProtection.Public, "ParameterInfoPointer") + { + ImmediateGet = $"({NativeStub}*)Pointer" + }, + new CodeGenProperty("bool", ElementProtection.Public, "HasNamePosToken") + { + ImmediateGet = GetNativeField("name") is not null ? "true" : "false" + } + ]; + + protected override IReadOnlyList? ByRefWrappers => + [ + new ByRefWrapper("nint", "Name", ["name"], addNotSupportedIfNotExist: true), + new ByRefWrapper("int", "Position", ["position"], addNotSupportedIfNotExist: true), + new ByRefWrapper("uint", "Token", ["token"], addNotSupportedIfNotExist: true), + new ByRefWrapper("Il2CppTypeStruct*", "ParameterType", ["parameter_type"]), + ]; +} diff --git a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppPropertyInfoGenerator.cs b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppPropertyInfoGenerator.cs index a34e95bb..f5e4d601 100644 --- a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppPropertyInfoGenerator.cs +++ b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppPropertyInfoGenerator.cs @@ -1,37 +1,34 @@ using CppAst; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; namespace Il2CppInterop.StructGenerator.TypeGenerators; internal class Il2CppPropertyInfoGenerator : VersionSpecificGenerator { - public Il2CppPropertyInfoGenerator(string metadataSuffix, CppClass nativeClass, - Func? dependencyResolver = null) : base(metadataSuffix, nativeClass, dependencyResolver) + public Il2CppPropertyInfoGenerator(string metadataSuffix, CppClass nativeClass) : base(metadataSuffix, nativeClass) { } + public override string CppClassName => "Il2CppPropertyInfo"; protected override string HandlerName => "NativePropertyInfoStructHandler"; protected override string HandlerInterface => "INativePropertyInfoStructHandler"; protected override string NativeInterface => "INativePropertyInfoStruct"; protected override string NativeStub => "Il2CppPropertyInfo"; - protected override List? WrapperFields => null; - - protected override List? WrapperProperties => new() - { + protected override IReadOnlyList? WrapperProperties => + [ new CodeGenProperty($"{NativeStub}*", ElementProtection.Public, "PropertyInfoPointer") - { ImmediateGet = $"({NativeStub}*)Pointer" } - }; - - protected override List? ByRefWrappers => new() - { - new ByRefWrapper("IntPtr", "Name", new[] { "name" }), - new ByRefWrapper("Il2CppClass*", "Parent", new[] { "parent" }), - new ByRefWrapper("Il2CppMethodInfo*", "Get", new[] { "get" }), - new ByRefWrapper("Il2CppMethodInfo*", "Set", new[] { "set" }), - new ByRefWrapper("uint", "Attrs", new[] { "attrs" }) - }; + { + ImmediateGet = $"({NativeStub}*)Pointer" + } + ]; - protected override List? BitfieldAccessors => null; + protected override IReadOnlyList? ByRefWrappers => + [ + new ByRefWrapper("nint", "Name", ["name"]), + new ByRefWrapper("Il2CppClass*", "Parent", ["parent"]), + new ByRefWrapper("Il2CppMethodInfo*", "Get", ["get"]), + new ByRefWrapper("Il2CppMethodInfo*", "Set", ["set"]), + new ByRefWrapper("uint", "Attrs", ["attrs"]) + ]; } diff --git a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppTypeGenerator.cs b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppTypeGenerator.cs index 1ba98e55..6377407c 100644 --- a/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppTypeGenerator.cs +++ b/Il2CppInterop.StructGenerator/TypeGenerators/Il2CppTypeGenerator.cs @@ -1,40 +1,40 @@ using CppAst; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; namespace Il2CppInterop.StructGenerator.TypeGenerators; internal class Il2CppTypeGenerator : VersionSpecificGenerator { - public Il2CppTypeGenerator(string metadataSuffix, CppClass nativeClass, - Func? dependencyResolver = null) : base(metadataSuffix, nativeClass, dependencyResolver) + public Il2CppTypeGenerator(string metadataSuffix, CppClass nativeClass) : base(metadataSuffix, nativeClass) { } + public override string CppClassName => "Il2CppType"; protected override string HandlerName => "NativeTypeStructHandler"; protected override string HandlerInterface => "INativeTypeStructHandler"; protected override string NativeInterface => "INativeTypeStruct"; protected override string NativeStub => "Il2CppTypeStruct"; - protected override List? WrapperFields => null; - protected override List? WrapperProperties => new() - { + protected override IReadOnlyList? WrapperProperties => + [ new CodeGenProperty($"{NativeStub}*", ElementProtection.Public, "TypePointer") - { ImmediateGet = $"({NativeStub}*)Pointer" } - }; + { + ImmediateGet = $"({NativeStub}*)Pointer" + } + ]; - protected override List? ByRefWrappers => new() - { - new ByRefWrapper("IntPtr", "Data", new[] { "data" }), - new ByRefWrapper("ushort", "Attrs", new[] { "attrs" }), - new ByRefWrapper("Il2CppTypeEnum", "Type", new[] { "type" }) - }; + protected override IReadOnlyList? ByRefWrappers => + [ + new ByRefWrapper("nint", "Data", ["data"]), + new ByRefWrapper("ushort", "Attrs", ["attrs"]), + new ByRefWrapper("Il2CppTypeEnum", "Type", ["type"]) + ]; - protected override List? BitfieldAccessors => new() - { + protected override IReadOnlyList? BitfieldAccessors => + [ new BitfieldAccessor("ByRef", "byref"), new BitfieldAccessor("Pinned", "pinned"), // maybe throw if not exist new BitfieldAccessor("ValueType", "valuetype", defaultGetter: "false") - }; + ]; } diff --git a/Il2CppInterop.StructGenerator/Usings.cs b/Il2CppInterop.StructGenerator/Usings.cs new file mode 100644 index 00000000..b00d5055 --- /dev/null +++ b/Il2CppInterop.StructGenerator/Usings.cs @@ -0,0 +1 @@ +global using AssetRipper.Text.SourceGeneration; diff --git a/Il2CppInterop.StructGenerator/Utilities/UnityVersion.cs b/Il2CppInterop.StructGenerator/Utilities/UnityVersion.cs deleted file mode 100644 index 797d6fd1..00000000 --- a/Il2CppInterop.StructGenerator/Utilities/UnityVersion.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.Text.RegularExpressions; - -namespace Il2CppInterop.StructGenerator.Utilities; - -internal struct UnityVersion : IComparable, IComparable -{ - public int Major => unchecked((int)((myMBlob >> 48) & 0xFFFFUL)); - public int Minor => unchecked((int)((myMBlob >> 40) & 0xFFUL)); - public int Build => unchecked((int)((myMBlob >> 32) & 0xFFUL)); - public char Type => (char)unchecked((int)((myMBlob >> 24) & 0xFFUL)); - public int Revision => unchecked((int)((myMBlob >> 16) & 0xFFUL)); - - public UnityVersion(string versionString) - { - var match = Regex.Match(versionString, @"([0-9]+).([0-9]+).([0-9]+)(?:([abcfpx])([0-9]+))?"); - if (!match.Success) throw new Exception("invalid unity version string"); - var major = int.Parse(match.Groups[1].Value); - var minor = int.Parse(match.Groups[2].Value); - var build = int.Parse(match.Groups[3].Value); - var type = match.Groups[4].Success ? match.Groups[4].Value[0] : 'f'; - var revision = match.Groups[5].Success ? int.Parse(match.Groups[5].Value) : 1; - myMBlob = MakeBlob(major, minor, build, type, revision); - } - - private static ulong MakeBlob(int major, int minor, int build, char type, int revision) - { - var blob = ((ulong)(major & 0xFFFF) << 48) | ((ulong)(minor & 0xFF) << 40) | ((ulong)(build & 0xFF) << 32) - | ((ulong)(type & 0xFF) << 24) | ((ulong)(revision & 0xFF) << 16); - return blob; - } - - public int CompareTo(object? obj) - { - if (obj is UnityVersion version) - return CompareTo(version); - return 1; - } - - public int CompareTo(UnityVersion other) - { - if (this > other) - return 1; - if (this < other) - return -1; - return 0; - } - - public static bool operator !=(UnityVersion lhs, UnityVersion rhs) - { - return lhs.myMBlob != rhs.myMBlob; - } - - public static bool operator ==(UnityVersion lhs, UnityVersion rhs) - { - return lhs.myMBlob == rhs.myMBlob; - } - - public static bool operator <(UnityVersion lhs, UnityVersion rhs) - { - return lhs.myMBlob < rhs.myMBlob; - } - - public static bool operator <=(UnityVersion lhs, UnityVersion rhs) - { - return lhs.myMBlob <= rhs.myMBlob; - } - - public static bool operator >(UnityVersion lhs, UnityVersion rhs) - { - return lhs.myMBlob > rhs.myMBlob; - } - - public static bool operator >=(UnityVersion lhs, UnityVersion rhs) - { - return lhs.myMBlob >= rhs.myMBlob; - } - - public string ToStringShort() - { - return $"{Major}.{Minor}.{Build}"; - } - - public override string ToString() - { - return $"{Major}.{Minor}.{Build}{Type}{Revision}"; - } - - private readonly ulong myMBlob; - - public override readonly bool Equals(object obj) - { - return obj is UnityVersion version && this == version; - } - - public override readonly int GetHashCode() => myMBlob.GetHashCode(); -} diff --git a/Il2CppInterop.StructGenerator/VersionSpecificGenerator.cs b/Il2CppInterop.StructGenerator/VersionSpecificGenerator.cs index 51a6334d..47273c15 100644 --- a/Il2CppInterop.StructGenerator/VersionSpecificGenerator.cs +++ b/Il2CppInterop.StructGenerator/VersionSpecificGenerator.cs @@ -1,39 +1,13 @@ -using System.Text; +using System.CodeDom.Compiler; +using AssetRipper.Primitives; using CppAst; using Il2CppInterop.StructGenerator.CodeGen; -using Il2CppInterop.StructGenerator.CodeGen.Enums; -using Il2CppInterop.StructGenerator.Utilities; namespace Il2CppInterop.StructGenerator; -internal class BitfieldAccessor -{ - public BitfieldAccessor(string accessorName, string elementName, string accessorType = "bool", - bool generateIfNotPresent = true, string? defaultGetter = "", Action? defaultGetBuilder = null, - Action? defaultSetBuilder = null) - { - AccessorName = accessorName; - ElementName = elementName; - AccessorType = accessorType; - GenerateIfNotPresent = generateIfNotPresent; - DefaultImmediateGetter = defaultGetter; - DefaultGetBuilder = defaultGetBuilder; - DefaultSetBuilder = defaultSetBuilder; - } - - public string AccessorName { get; } - public string ElementName { get; } - public string AccessorType { get; } - public bool GenerateIfNotPresent { get; } - public string? DefaultImmediateGetter { get; } - public Action? DefaultGetBuilder { get; } - public Action? DefaultSetBuilder { get; } -} - internal abstract class VersionSpecificGenerator { - public VersionSpecificGenerator(string metadataSuffix, CppClass nativeClass, - Func? dependencyResolver = null) + public VersionSpecificGenerator(string metadataSuffix, CppClass nativeClass) { MetadataSuffix = metadataSuffix; NativeStructGenerator = new NativeStructGenerator(MetadataSuffix, nativeClass); @@ -44,10 +18,6 @@ public VersionSpecificGenerator(string metadataSuffix, CppClass nativeClass, SizeProviderOverride = SizeOverride }; HandlerGenerator.HandlerClass.NestedElements.Add(NativeStructGenerator.NativeStruct); - if (DependsOnClasses != null && dependencyResolver != null) - foreach (var dependency in DependsOnClasses) - HandlerGenerator.HandlerClass.NestedElements.Add( - new NativeStructGenerator(MetadataSuffix, dependencyResolver(dependency)).NativeStruct); WrapperGenerator = new StructWrapperGenerator(NativeInterface); foreach (var bitfieldField in NativeStructGenerator.NativeStruct.Fields.Where(x => @@ -62,42 +32,36 @@ public VersionSpecificGenerator(string metadataSuffix, CppClass nativeClass, HandlerGenerator.HandlerClass.NestedElements.Add(WrapperGenerator.WrapperClass); } + public abstract string CppClassName { get; } protected abstract string HandlerName { get; } protected abstract string HandlerInterface { get; } protected abstract string NativeInterface { get; } protected abstract string NativeStub { get; } - protected virtual string[]? DependsOnClasses { get; } - protected virtual List? CreateNewParameters => null; + protected virtual IEnumerable? CreateNewParameters => null; protected virtual string? SizeOverride => null; - protected abstract List? WrapperFields { get; } - protected abstract List? WrapperProperties { get; } - protected abstract List? ByRefWrappers { get; } - protected abstract List? BitfieldAccessors { get; } + protected virtual IEnumerable? WrapperFields => null; + protected virtual IEnumerable? WrapperProperties => null; + protected virtual IEnumerable? ByRefWrappers => null; + protected virtual IEnumerable? BitfieldAccessors => null; - protected virtual Action? CreateNewExtraBody => null; + protected virtual Action? CreateNewExtraBody => null; public string MetadataSuffix { get; } public NativeStructGenerator NativeStructGenerator { get; } public StructHandlerGenerator HandlerGenerator { get; } public StructWrapperGenerator WrapperGenerator { get; } - public HashSet ApplicableVersions { get; } = new(); - public HashSet ExtraUsings { get; } = new(); - - public void AddExtraUsing(string @using) - { - if (ExtraUsings.Contains(@using)) return; - ExtraUsings.Add(@using); - } + public HashSet ApplicableVersions { get; } = []; + public HashSet ExtraUsings { get; } = []; public void SetupElements() { - List properties = new() - { + List properties = + [ new CodeGenProperty($"{NativeStructGenerator.NativeStruct.Name}*", ElementProtection.Private, "_") { ImmediateGet = $"({NativeStructGenerator.NativeStruct.Name}*)Pointer" } - }; + ]; var wrapperProperties = WrapperProperties; if (wrapperProperties != null) properties.AddRange(wrapperProperties); WrapperGenerator.ImplementProperties(properties); @@ -126,7 +90,7 @@ public void SetupElements() { if (wrapper.AddNotSupported) { - property.ImmediateGet = "throw new NotSupportedException()"; + property.ImmediateGet = "throw new System.NotSupportedException()"; WrapperGenerator.WrapperClass.Properties.Add(property); } @@ -226,9 +190,4 @@ public void SetupElements() { return NativeStructGenerator.NativeStruct.Fields.SingleOrDefault(x => x.Name == name); } - - public virtual string Build() - { - return HandlerGenerator.HandlerClass.Build(); - } } diff --git a/Il2CppInterop.sln b/Il2CppInterop.sln index 80d120c6..df002fff 100644 --- a/Il2CppInterop.sln +++ b/Il2CppInterop.sln @@ -1,82 +1,96 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31402.337 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Il2CppInterop.Generator", "Il2CppInterop.Generator\Il2CppInterop.Generator.csproj", "{7C3FD45B-A563-47AF-90DF-8B051A8C33A0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Il2CppInterop.Runtime", "Il2CppInterop.Runtime\Il2CppInterop.Runtime.csproj", "{F6405B5F-9162-49EA-9C8A-CE1AA0916CF3}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{463B7E3B-94E8-4EEB-B54A-EF15AD9C7C7E}" - ProjectSection(SolutionItems) = preProject - .gitignore = .gitignore - README.md = README.md - Directory.Build.props = Directory.Build.props - build.cake = build.cake - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentation", "{99E71726-78FB-4376-89DA-90E5C08F5CD4}" - ProjectSection(SolutionItems) = preProject - Documentation\Class-Injection.md = Documentation\Class-Injection.md - Documentation\Command-Line-Usage.md = Documentation\Command-Line-Usage.md - Documentation\Common-Problems.md = Documentation\Common-Problems.md - Documentation\Injected-Components-In-Asset-Bundles.md = Documentation\Injected-Components-In-Asset-Bundles.md - Documentation\Implementing-Interfaces.md = Documentation\Implementing-Interfaces.md - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.CLI", "Il2CppInterop.CLI\Il2CppInterop.CLI.csproj", "{F5AA88D1-5E62-46B8-A11C-3FAC40C25600}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{771D8BE3-C373-4754-94EA-4A68B117BAEF}" - ProjectSection(SolutionItems) = preProject - .github\workflows\dotnet.yml = .github\workflows\dotnet.yml - .github\workflows\format_check.yml = .github\workflows\format_check.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.StructGenerator", "Il2CppInterop.StructGenerator\Il2CppInterop.StructGenerator.csproj", "{DE781BD4-650F-4ED8-B615-5CB36E6D476A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.Common", "Il2CppInterop.Common\Il2CppInterop.Common.csproj", "{E7D3A81B-11CD-402C-A447-015B00DCA3FD}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.HarmonySupport", "Il2CppInterop.HarmonySupport\Il2CppInterop.HarmonySupport.csproj", "{EBC23884-3417-4F24-8623-E913C5151CC3}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7C3FD45B-A563-47AF-90DF-8B051A8C33A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7C3FD45B-A563-47AF-90DF-8B051A8C33A0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7C3FD45B-A563-47AF-90DF-8B051A8C33A0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7C3FD45B-A563-47AF-90DF-8B051A8C33A0}.Release|Any CPU.Build.0 = Release|Any CPU - {F6405B5F-9162-49EA-9C8A-CE1AA0916CF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F6405B5F-9162-49EA-9C8A-CE1AA0916CF3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F6405B5F-9162-49EA-9C8A-CE1AA0916CF3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F6405B5F-9162-49EA-9C8A-CE1AA0916CF3}.Release|Any CPU.Build.0 = Release|Any CPU - {F5AA88D1-5E62-46B8-A11C-3FAC40C25600}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5AA88D1-5E62-46B8-A11C-3FAC40C25600}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F5AA88D1-5E62-46B8-A11C-3FAC40C25600}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F5AA88D1-5E62-46B8-A11C-3FAC40C25600}.Release|Any CPU.Build.0 = Release|Any CPU - {DE781BD4-650F-4ED8-B615-5CB36E6D476A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DE781BD4-650F-4ED8-B615-5CB36E6D476A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DE781BD4-650F-4ED8-B615-5CB36E6D476A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DE781BD4-650F-4ED8-B615-5CB36E6D476A}.Release|Any CPU.Build.0 = Release|Any CPU - {E7D3A81B-11CD-402C-A447-015B00DCA3FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7D3A81B-11CD-402C-A447-015B00DCA3FD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7D3A81B-11CD-402C-A447-015B00DCA3FD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7D3A81B-11CD-402C-A447-015B00DCA3FD}.Release|Any CPU.Build.0 = Release|Any CPU - {EBC23884-3417-4F24-8623-E913C5151CC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EBC23884-3417-4F24-8623-E913C5151CC3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EBC23884-3417-4F24-8623-E913C5151CC3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EBC23884-3417-4F24-8623-E913C5151CC3}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {99E71726-78FB-4376-89DA-90E5C08F5CD4} = {463B7E3B-94E8-4EEB-B54A-EF15AD9C7C7E} - {771D8BE3-C373-4754-94EA-4A68B117BAEF} = {463B7E3B-94E8-4EEB-B54A-EF15AD9C7C7E} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {CA7C3EAB-EDE4-4DB0-BEEF-9E74940B2978} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11109.219 d18.0-oob +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Il2CppInterop.Generator", "Il2CppInterop.Generator\Il2CppInterop.Generator.csproj", "{7C3FD45B-A563-47AF-90DF-8B051A8C33A0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Il2CppInterop.Runtime", "Il2CppInterop.Runtime\Il2CppInterop.Runtime.csproj", "{F6405B5F-9162-49EA-9C8A-CE1AA0916CF3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{463B7E3B-94E8-4EEB-B54A-EF15AD9C7C7E}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitignore = .gitignore + build.cake = build.cake + Directory.Build.props = Directory.Build.props + nuget.config = nuget.config + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentation", "{99E71726-78FB-4376-89DA-90E5C08F5CD4}" + ProjectSection(SolutionItems) = preProject + Documentation\Class-Injection.md = Documentation\Class-Injection.md + Documentation\Command-Line-Usage.md = Documentation\Command-Line-Usage.md + Documentation\Common-Problems.md = Documentation\Common-Problems.md + Documentation\Implementing-Interfaces.md = Documentation\Implementing-Interfaces.md + Documentation\Injected-Components-In-Asset-Bundles.md = Documentation\Injected-Components-In-Asset-Bundles.md + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.CLI", "Il2CppInterop.CLI\Il2CppInterop.CLI.csproj", "{F5AA88D1-5E62-46B8-A11C-3FAC40C25600}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{771D8BE3-C373-4754-94EA-4A68B117BAEF}" + ProjectSection(SolutionItems) = preProject + .github\workflows\dotnet.yml = .github\workflows\dotnet.yml + .github\workflows\format_check.yml = .github\workflows\format_check.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.StructGenerator", "Il2CppInterop.StructGenerator\Il2CppInterop.StructGenerator.csproj", "{DE781BD4-650F-4ED8-B615-5CB36E6D476A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.Common", "Il2CppInterop.Common\Il2CppInterop.Common.csproj", "{E7D3A81B-11CD-402C-A447-015B00DCA3FD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.HarmonySupport", "Il2CppInterop.HarmonySupport\Il2CppInterop.HarmonySupport.csproj", "{EBC23884-3417-4F24-8623-E913C5151CC3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2Cppmscorlib", "Il2Cppmscorlib\Il2Cppmscorlib.csproj", "{78406496-DB4C-4012-B5DE-D7CDF27652B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInterop.SourceGenerator", "Il2CppInterop.SourceGenerator\Il2CppInterop.SourceGenerator.csproj", "{3110C3CE-A58E-4B3A-8BE2-455AFD69606E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7C3FD45B-A563-47AF-90DF-8B051A8C33A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C3FD45B-A563-47AF-90DF-8B051A8C33A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C3FD45B-A563-47AF-90DF-8B051A8C33A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C3FD45B-A563-47AF-90DF-8B051A8C33A0}.Release|Any CPU.Build.0 = Release|Any CPU + {F6405B5F-9162-49EA-9C8A-CE1AA0916CF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F6405B5F-9162-49EA-9C8A-CE1AA0916CF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F6405B5F-9162-49EA-9C8A-CE1AA0916CF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F6405B5F-9162-49EA-9C8A-CE1AA0916CF3}.Release|Any CPU.Build.0 = Release|Any CPU + {F5AA88D1-5E62-46B8-A11C-3FAC40C25600}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5AA88D1-5E62-46B8-A11C-3FAC40C25600}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5AA88D1-5E62-46B8-A11C-3FAC40C25600}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5AA88D1-5E62-46B8-A11C-3FAC40C25600}.Release|Any CPU.Build.0 = Release|Any CPU + {DE781BD4-650F-4ED8-B615-5CB36E6D476A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DE781BD4-650F-4ED8-B615-5CB36E6D476A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE781BD4-650F-4ED8-B615-5CB36E6D476A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DE781BD4-650F-4ED8-B615-5CB36E6D476A}.Release|Any CPU.Build.0 = Release|Any CPU + {E7D3A81B-11CD-402C-A447-015B00DCA3FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7D3A81B-11CD-402C-A447-015B00DCA3FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7D3A81B-11CD-402C-A447-015B00DCA3FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7D3A81B-11CD-402C-A447-015B00DCA3FD}.Release|Any CPU.Build.0 = Release|Any CPU + {EBC23884-3417-4F24-8623-E913C5151CC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EBC23884-3417-4F24-8623-E913C5151CC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EBC23884-3417-4F24-8623-E913C5151CC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EBC23884-3417-4F24-8623-E913C5151CC3}.Release|Any CPU.Build.0 = Release|Any CPU + {78406496-DB4C-4012-B5DE-D7CDF27652B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78406496-DB4C-4012-B5DE-D7CDF27652B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78406496-DB4C-4012-B5DE-D7CDF27652B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78406496-DB4C-4012-B5DE-D7CDF27652B8}.Release|Any CPU.Build.0 = Release|Any CPU + {3110C3CE-A58E-4B3A-8BE2-455AFD69606E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3110C3CE-A58E-4B3A-8BE2-455AFD69606E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3110C3CE-A58E-4B3A-8BE2-455AFD69606E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3110C3CE-A58E-4B3A-8BE2-455AFD69606E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {99E71726-78FB-4376-89DA-90E5C08F5CD4} = {463B7E3B-94E8-4EEB-B54A-EF15AD9C7C7E} + {771D8BE3-C373-4754-94EA-4A68B117BAEF} = {463B7E3B-94E8-4EEB-B54A-EF15AD9C7C7E} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CA7C3EAB-EDE4-4DB0-BEEF-9E74940B2978} + EndGlobalSection +EndGlobal diff --git a/Il2Cppmscorlib/Array.cs b/Il2Cppmscorlib/Array.cs new file mode 100644 index 00000000..373b72ee --- /dev/null +++ b/Il2Cppmscorlib/Array.cs @@ -0,0 +1,32 @@ +using Il2CppInterop.Common; + +namespace Il2CppSystem; + +public abstract class Array : Object +{ + protected Array(ObjectPointer ptr) : base(ptr) + { + } + + public Int32 Length => default; + + public Int32 GetLowerBound(Int32 dimension) + { + return default; + } + + public Int32 GetUpperBound(Int32 dimension) + { + return default; + } + + public Int32 GetLength(Int32 dimension) + { + return default; + } + + public Int32 GetRank() + { + return default; + } +} diff --git a/Il2Cppmscorlib/Boolean.cs b/Il2Cppmscorlib/Boolean.cs new file mode 100644 index 00000000..416a9859 --- /dev/null +++ b/Il2Cppmscorlib/Boolean.cs @@ -0,0 +1,14 @@ +using Il2CppInterop.Common; + +namespace Il2CppSystem; + +public struct Boolean : IIl2CppType +{ + static int IIl2CppType.Size => throw null; + readonly nint IIl2CppType.ObjectClass => throw null; + static Boolean IIl2CppType.ReadFromSpan(System.ReadOnlySpan span) => throw null; + static void IIl2CppType.WriteToSpan(Boolean value, System.Span span) => throw null; + + public static implicit operator bool(Boolean value) => throw null; + public static implicit operator Boolean(bool value) => throw null; +} diff --git a/Il2Cppmscorlib/Delegate.cs b/Il2Cppmscorlib/Delegate.cs new file mode 100644 index 00000000..d08cc0d7 --- /dev/null +++ b/Il2Cppmscorlib/Delegate.cs @@ -0,0 +1,30 @@ +using Il2CppSystem.Reflection; + +namespace Il2CppSystem; + +public abstract class Delegate : Object +{ + public IntPtr method_ptr { get; set; } + + public IntPtr invoke_impl { get; set; } + + public IObject m_target { get; set; } + + public IntPtr method { get; set; } + + public IntPtr delegate_trampoline { get; set; } + + public IntPtr extra_arg { get; set; } + + public IntPtr method_code { get; set; } + + public IntPtr interp_method { get; set; } + + public IntPtr interp_invoke_impl { get; set; } + + public MethodInfo method_info { get; set; } + + public MethodInfo original_method_info { get; set; } + + public Boolean method_is_virtual { get; set; } +} diff --git a/Il2Cppmscorlib/Enum.cs b/Il2Cppmscorlib/Enum.cs new file mode 100644 index 00000000..29a8acef --- /dev/null +++ b/Il2Cppmscorlib/Enum.cs @@ -0,0 +1,5 @@ +namespace Il2CppSystem; + +public abstract class Enum : ValueType +{ +} diff --git a/Il2Cppmscorlib/Exception.cs b/Il2Cppmscorlib/Exception.cs new file mode 100644 index 00000000..5810d04f --- /dev/null +++ b/Il2Cppmscorlib/Exception.cs @@ -0,0 +1,9 @@ +namespace Il2CppSystem; + +public class Exception : Object +{ + public String ToString(Boolean needFileLineInfo, Boolean needMessage) + { + throw null; + } +} diff --git a/Il2Cppmscorlib/GC.cs b/Il2Cppmscorlib/GC.cs new file mode 100644 index 00000000..fc511c17 --- /dev/null +++ b/Il2Cppmscorlib/GC.cs @@ -0,0 +1,9 @@ +namespace Il2CppSystem; + +public static class GC +{ + public static void ReRegisterForFinalize(IObject obj) + { + throw null; + } +} diff --git a/Il2Cppmscorlib/IEnum.cs b/Il2Cppmscorlib/IEnum.cs new file mode 100644 index 00000000..cfaa3906 --- /dev/null +++ b/Il2Cppmscorlib/IEnum.cs @@ -0,0 +1,5 @@ +namespace Il2CppSystem; + +public interface IEnum : IValueType +{ +} diff --git a/Il2Cppmscorlib/IObject.cs b/Il2Cppmscorlib/IObject.cs new file mode 100644 index 00000000..ce4603e5 --- /dev/null +++ b/Il2Cppmscorlib/IObject.cs @@ -0,0 +1,12 @@ +namespace Il2CppSystem; + +public interface IObject +{ + Boolean Equals(IObject @object) => default; + Int32 GetIl2CppHashCode() => default; + void Il2CppFinalize() + { + } + + String ToIl2CppString() => default; +} diff --git a/Il2Cppmscorlib/IValueType.cs b/Il2Cppmscorlib/IValueType.cs new file mode 100644 index 00000000..fb63b240 --- /dev/null +++ b/Il2Cppmscorlib/IValueType.cs @@ -0,0 +1,7 @@ +namespace Il2CppSystem; + +public interface IValueType : IObject +{ + int Size { get; } + void WriteToSpan(System.Span span); +} diff --git a/Il2Cppmscorlib/Il2Cppmscorlib.csproj b/Il2Cppmscorlib/Il2Cppmscorlib.csproj new file mode 100644 index 00000000..68233a2b --- /dev/null +++ b/Il2Cppmscorlib/Il2Cppmscorlib.csproj @@ -0,0 +1,14 @@ + + + + net10.0 + Il2CppSystem + disable + disable + + + + + + + diff --git a/Il2Cppmscorlib/Int32.cs b/Il2Cppmscorlib/Int32.cs new file mode 100644 index 00000000..8455cb53 --- /dev/null +++ b/Il2Cppmscorlib/Int32.cs @@ -0,0 +1,14 @@ +using Il2CppInterop.Common; + +namespace Il2CppSystem; + +public struct Int32 : IIl2CppType +{ + static int IIl2CppType.Size => throw null; + readonly nint IIl2CppType.ObjectClass => throw null; + static Int32 IIl2CppType.ReadFromSpan(System.ReadOnlySpan span) => throw null; + static void IIl2CppType.WriteToSpan(Int32 value, System.Span span) => throw null; + + public static implicit operator int(Int32 value) => throw null; + public static implicit operator Int32(int value) => throw null; +} diff --git a/Il2Cppmscorlib/IntPtr.cs b/Il2Cppmscorlib/IntPtr.cs new file mode 100644 index 00000000..928786ad --- /dev/null +++ b/Il2Cppmscorlib/IntPtr.cs @@ -0,0 +1,14 @@ +using Il2CppInterop.Common; + +namespace Il2CppSystem; + +public struct IntPtr : IIl2CppType +{ + static int IIl2CppType.Size => throw null; + readonly nint IIl2CppType.ObjectClass => throw null; + static IntPtr IIl2CppType.ReadFromSpan(System.ReadOnlySpan span) => throw null; + static void IIl2CppType.WriteToSpan(IntPtr value, System.Span span) => throw null; + + public static implicit operator nint(IntPtr value) => throw null; + public static implicit operator IntPtr(nint value) => throw null; +} diff --git a/Il2Cppmscorlib/Object.cs b/Il2Cppmscorlib/Object.cs new file mode 100644 index 00000000..35b9f3a2 --- /dev/null +++ b/Il2Cppmscorlib/Object.cs @@ -0,0 +1,26 @@ +using Il2CppInterop.Common; + +namespace Il2CppSystem; + +public class Object : IObject +{ + protected Object() + { + } + + protected Object(ObjectPointer ptr) + { + } + + public nint Pointer => default; + + public bool WasCollected => default; + + public virtual Boolean Equals(IObject @object) => default; + public virtual Int32 GetIl2CppHashCode() => default; + public virtual void Il2CppFinalize() + { + } + + public virtual String ToIl2CppString() => default; +} diff --git a/Il2Cppmscorlib/README.md b/Il2Cppmscorlib/README.md new file mode 100644 index 00000000..174804dc --- /dev/null +++ b/Il2Cppmscorlib/README.md @@ -0,0 +1,5 @@ +# Il2Cppmscorlib + +This is a stub assembly to enable a circular dependency with Il2CppInterop.Runtime. + +To enforce project standards, nearly all classes in this assembly are marked as abstract, even if they aren't actually abstract in the real assembly. Similarly, constructors are marked as protected to avoid misuse. Objects should be created using the `Il2CppObjectPool.Get` API instead. diff --git a/Il2Cppmscorlib/Reflection/Assembly.cs b/Il2Cppmscorlib/Reflection/Assembly.cs new file mode 100644 index 00000000..97134dae --- /dev/null +++ b/Il2Cppmscorlib/Reflection/Assembly.cs @@ -0,0 +1,13 @@ +namespace Il2CppSystem.Reflection; + +public abstract class Assembly : Object +{ + public virtual AssemblyName GetName() + { + throw null; + } + public virtual Type GetType(String name) + { + throw null; + } +} diff --git a/Il2Cppmscorlib/Reflection/AssemblyName.cs b/Il2Cppmscorlib/Reflection/AssemblyName.cs new file mode 100644 index 00000000..01a71ca4 --- /dev/null +++ b/Il2Cppmscorlib/Reflection/AssemblyName.cs @@ -0,0 +1,6 @@ +namespace Il2CppSystem.Reflection; + +public abstract class AssemblyName : Object +{ + public String Name { get; } +} diff --git a/Il2Cppmscorlib/Reflection/BindingFlags.cs b/Il2Cppmscorlib/Reflection/BindingFlags.cs new file mode 100644 index 00000000..52f7aa75 --- /dev/null +++ b/Il2Cppmscorlib/Reflection/BindingFlags.cs @@ -0,0 +1,34 @@ +namespace Il2CppSystem.Reflection; + +public struct BindingFlags +{ + private readonly int value__; + public static readonly BindingFlags Default = (BindingFlags)0; + public static readonly BindingFlags IgnoreCase = (BindingFlags)1; + public static readonly BindingFlags DeclaredOnly = (BindingFlags)2; + public static readonly BindingFlags Instance = (BindingFlags)4; + public static readonly BindingFlags Static = (BindingFlags)8; + public static readonly BindingFlags Public = (BindingFlags)0x10; + public static readonly BindingFlags NonPublic = (BindingFlags)0x20; + public static readonly BindingFlags FlattenHierarchy = (BindingFlags)0x40; + public static readonly BindingFlags InvokeMethod = (BindingFlags)0x100; + public static readonly BindingFlags CreateInstance = (BindingFlags)0x200; + public static readonly BindingFlags GetField = (BindingFlags)0x400; + public static readonly BindingFlags SetField = (BindingFlags)0x800; + public static readonly BindingFlags GetProperty = (BindingFlags)0x1000; + public static readonly BindingFlags SetProperty = (BindingFlags)0x2000; + public static readonly BindingFlags PutDispProperty = (BindingFlags)0x4000; + public static readonly BindingFlags PutRefDispProperty = (BindingFlags)0x8000; + public static readonly BindingFlags ExactBinding = (BindingFlags)0x10000; + public static readonly BindingFlags SuppressChangeType = (BindingFlags)0x20000; + public static readonly BindingFlags OptionalParamBinding = (BindingFlags)0x40000; + public static readonly BindingFlags IgnoreReturn = (BindingFlags)0x1000000; + public static readonly BindingFlags DoNotWrapExceptions = (BindingFlags)0x2000000; + + public static explicit operator BindingFlags(int value) => throw null; + + public static BindingFlags operator &(BindingFlags left, BindingFlags right) => throw null; + public static BindingFlags operator |(BindingFlags left, BindingFlags right) => throw null; + public static BindingFlags operator ^(BindingFlags left, BindingFlags right) => throw null; + public static BindingFlags operator ~(BindingFlags value) => throw null; +} diff --git a/Il2Cppmscorlib/Reflection/ConstructorInfo.cs b/Il2Cppmscorlib/Reflection/ConstructorInfo.cs new file mode 100644 index 00000000..fd3fa6f9 --- /dev/null +++ b/Il2Cppmscorlib/Reflection/ConstructorInfo.cs @@ -0,0 +1,5 @@ +namespace Il2CppSystem.Reflection; + +public abstract class ConstructorInfo : MethodBase +{ +} diff --git a/Il2Cppmscorlib/Reflection/FieldInfo.cs b/Il2Cppmscorlib/Reflection/FieldInfo.cs new file mode 100644 index 00000000..66d08c65 --- /dev/null +++ b/Il2Cppmscorlib/Reflection/FieldInfo.cs @@ -0,0 +1,5 @@ +namespace Il2CppSystem.Reflection; + +public abstract class FieldInfo : MemberInfo +{ +} diff --git a/Il2Cppmscorlib/Reflection/MemberInfo.cs b/Il2Cppmscorlib/Reflection/MemberInfo.cs new file mode 100644 index 00000000..bb342ca6 --- /dev/null +++ b/Il2Cppmscorlib/Reflection/MemberInfo.cs @@ -0,0 +1,8 @@ +namespace Il2CppSystem.Reflection; + +public abstract class MemberInfo : Object +{ + public virtual String Name { get; } + + public virtual Type DeclaringType { get; } +} diff --git a/Il2Cppmscorlib/Reflection/MethodBase.cs b/Il2Cppmscorlib/Reflection/MethodBase.cs new file mode 100644 index 00000000..0f206f5b --- /dev/null +++ b/Il2Cppmscorlib/Reflection/MethodBase.cs @@ -0,0 +1,5 @@ +namespace Il2CppSystem.Reflection; + +public abstract class MethodBase : MemberInfo +{ +} diff --git a/Il2Cppmscorlib/Reflection/MethodInfo.cs b/Il2Cppmscorlib/Reflection/MethodInfo.cs new file mode 100644 index 00000000..733c8b43 --- /dev/null +++ b/Il2Cppmscorlib/Reflection/MethodInfo.cs @@ -0,0 +1,17 @@ +namespace Il2CppSystem.Reflection; + +public abstract class MethodInfo : MethodBase +{ + public virtual Type ReturnType + { + get + { + throw null; + } + } + + public MethodInfo MakeGenericMethod(Type[] typeArguments) + { + throw null; + } +} diff --git a/Il2Cppmscorlib/Reflection/MonoField.cs b/Il2Cppmscorlib/Reflection/MonoField.cs new file mode 100644 index 00000000..e96e7e15 --- /dev/null +++ b/Il2Cppmscorlib/Reflection/MonoField.cs @@ -0,0 +1,6 @@ +namespace Il2CppSystem.Reflection; + +public abstract class MonoField : RtFieldInfo +{ + public IObject GetValueInternal(IObject obj) => null; +} diff --git a/Il2Cppmscorlib/Reflection/ParameterInfo.cs b/Il2Cppmscorlib/Reflection/ParameterInfo.cs new file mode 100644 index 00000000..ee0b4900 --- /dev/null +++ b/Il2Cppmscorlib/Reflection/ParameterInfo.cs @@ -0,0 +1,6 @@ +namespace Il2CppSystem.Reflection; + +public abstract class ParameterInfo : Object +{ + public Type ParameterType { get; } +} diff --git a/Il2Cppmscorlib/Reflection/RtFieldInfo.cs b/Il2Cppmscorlib/Reflection/RtFieldInfo.cs new file mode 100644 index 00000000..d6f3caac --- /dev/null +++ b/Il2Cppmscorlib/Reflection/RtFieldInfo.cs @@ -0,0 +1,5 @@ +namespace Il2CppSystem.Reflection; + +public abstract class RtFieldInfo : RuntimeFieldInfo +{ +} diff --git a/Il2Cppmscorlib/Reflection/RuntimeFieldInfo.cs b/Il2Cppmscorlib/Reflection/RuntimeFieldInfo.cs new file mode 100644 index 00000000..b95f0f0f --- /dev/null +++ b/Il2Cppmscorlib/Reflection/RuntimeFieldInfo.cs @@ -0,0 +1,5 @@ +namespace Il2CppSystem.Reflection; + +public abstract class RuntimeFieldInfo : FieldInfo +{ +} diff --git a/Il2Cppmscorlib/Reflection/TypeInfo.cs b/Il2Cppmscorlib/Reflection/TypeInfo.cs new file mode 100644 index 00000000..f9f59f36 --- /dev/null +++ b/Il2Cppmscorlib/Reflection/TypeInfo.cs @@ -0,0 +1,5 @@ +namespace Il2CppSystem.Reflection; + +public abstract class TypeInfo : Type +{ +} diff --git a/Il2Cppmscorlib/Runtime/CompilerServices/RuntimeHelpers.cs b/Il2Cppmscorlib/Runtime/CompilerServices/RuntimeHelpers.cs new file mode 100644 index 00000000..a1c978be --- /dev/null +++ b/Il2Cppmscorlib/Runtime/CompilerServices/RuntimeHelpers.cs @@ -0,0 +1,9 @@ +namespace Il2CppSystem.Runtime.CompilerServices; + +public static class RuntimeHelpers +{ + public static void InitializeArray(Array array, IntPtr fldHandle) + { + throw null; + } +} diff --git a/Il2Cppmscorlib/RuntimeType.cs b/Il2Cppmscorlib/RuntimeType.cs new file mode 100644 index 00000000..776600a8 --- /dev/null +++ b/Il2Cppmscorlib/RuntimeType.cs @@ -0,0 +1,8 @@ +using Il2CppSystem.Reflection; + +namespace Il2CppSystem; + +public abstract class RuntimeType : TypeInfo +{ + public IObject GenericCache { get; set; } +} diff --git a/Il2Cppmscorlib/RuntimeTypeHandle.cs b/Il2Cppmscorlib/RuntimeTypeHandle.cs new file mode 100644 index 00000000..cfed52d4 --- /dev/null +++ b/Il2Cppmscorlib/RuntimeTypeHandle.cs @@ -0,0 +1,6 @@ +namespace Il2CppSystem; + +public struct RuntimeTypeHandle +{ + public IntPtr value; +} diff --git a/Il2Cppmscorlib/String.cs b/Il2Cppmscorlib/String.cs new file mode 100644 index 00000000..f9ad8588 --- /dev/null +++ b/Il2Cppmscorlib/String.cs @@ -0,0 +1,23 @@ +using Il2CppInterop.Common; + +namespace Il2CppSystem; + +public sealed class String : Object, IIl2CppType +{ + static int IIl2CppType.Size => throw null; + nint IIl2CppType.ObjectClass => throw null; + static String IIl2CppType.ReadFromSpan(System.ReadOnlySpan span) => throw null; + static void IIl2CppType.WriteToSpan(String value, System.Span span) => throw null; + + public String(ObjectPointer pointer) : base(pointer) + { + } + public static implicit operator String(string str) + { + throw null; + } + public static implicit operator string(String str) + { + throw null; + } +} diff --git a/Il2Cppmscorlib/Type.cs b/Il2Cppmscorlib/Type.cs new file mode 100644 index 00000000..b19c0c57 --- /dev/null +++ b/Il2Cppmscorlib/Type.cs @@ -0,0 +1,58 @@ +using Il2CppInterop.Common; +using Il2CppSystem.Reflection; + +namespace Il2CppSystem; + +public abstract class Type : Object, IIl2CppType +{ + static int IIl2CppType.Size => throw null; + nint IIl2CppType.ObjectClass => throw null; + static Type IIl2CppType.ReadFromSpan(System.ReadOnlySpan span) => throw null; + static void IIl2CppType.WriteToSpan(Type value, System.Span span) => throw null; + + public RuntimeTypeHandle _impl { get; set; } + + public abstract Type GetNestedType(String name, BindingFlags bindingAttr); + + public static Type internal_from_handle(IntPtr handle) + { + throw null; + } + + public virtual RuntimeTypeHandle TypeHandle + { + get + { + throw null; + } + } + + public Boolean IsPrimitive + { + get + { + throw null; + } + } + + public Boolean IsByRef + { + get + { + throw null; + } + } + + public MethodInfo GetMethod(String name) + { + throw null; + } + + public abstract String FullName { get; } + + public Type MakeByRefType() => throw null; + + public Type MakeGenericType(Type[] typeArguments) => throw null; + + public Type MakePointerType() => throw null; +} diff --git a/Il2Cppmscorlib/ValueType.cs b/Il2Cppmscorlib/ValueType.cs new file mode 100644 index 00000000..21efb1f4 --- /dev/null +++ b/Il2Cppmscorlib/ValueType.cs @@ -0,0 +1,5 @@ +namespace Il2CppSystem; + +public abstract class ValueType : Object +{ +} diff --git a/Il2Cppmscorlib/Void.cs b/Il2Cppmscorlib/Void.cs new file mode 100644 index 00000000..bd1e5213 --- /dev/null +++ b/Il2Cppmscorlib/Void.cs @@ -0,0 +1,11 @@ +using Il2CppInterop.Common; + +namespace Il2CppSystem; + +public struct Void : IIl2CppType +{ + static int IIl2CppType.Size => throw null; + readonly nint IIl2CppType.ObjectClass => throw null; + static Void IIl2CppType.ReadFromSpan(System.ReadOnlySpan span) => throw null; + static void IIl2CppType.WriteToSpan(Void value, System.Span span) => throw null; +} diff --git a/global.json b/global.json deleted file mode 100644 index 3fea262b..00000000 --- a/global.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "sdk": { - "version": "8.0.0", - "rollForward": "latestFeature" - } -} diff --git a/nuget.config b/nuget.config new file mode 100644 index 00000000..60c52367 --- /dev/null +++ b/nuget.config @@ -0,0 +1,5 @@ + + + + +