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..dbdd5b64 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -5,17 +5,16 @@
../bin/NuGet
../bin/$(MSBuildProjectName)
latest
- disable
+ enable
true
- Il2CppInterop
latest
logo_icon.png
LGPL-3.0-only
README.md
true
- true
embedded
enable
+ true
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..acf4371b 100644
--- a/Il2CppInterop.CLI/Il2CppInterop.CLI.csproj
+++ b/Il2CppInterop.CLI/Il2CppInterop.CLI.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net10.0
Exe
true
il2cppinterop
@@ -9,20 +9,17 @@
Il2CppInterop.CLI
BepInEx
CLI tool for generating managed proxy assemblies for Il2Cpp
- Debug;Release
- AnyCPU
- enable
+ true
-
-
+
-
-
-
+
+
+
diff --git a/Il2CppInterop.CLI/Program.cs b/Il2CppInterop.CLI/Program.cs
index 5796435c..06045404 100644
--- a/Il2CppInterop.CLI/Program.cs
+++ b/Il2CppInterop.CLI/Program.cs
@@ -1,316 +1,32 @@
-using System.CommandLine;
-using System.CommandLine.NamingConventionBinder;
-using System.Text.RegularExpressions;
-using Il2CppInterop;
-using Il2CppInterop.Common;
+using Cpp2IL.Core.Logging;
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
+// For other platforms, the paths will be slightly different.
+
+Logger.InfoLog += Console.WriteLine;
+Logger.WarningLog += Console.WriteLine;
+Logger.ErrorLog += Console.WriteLine;
+Logger.VerboseLog += Console.WriteLine;
+
+Il2CppGame.Process(
+ gameExePath,
+ outputFolder,
+ new AsmResolverDllOutputFormatBinding(),
+ Il2CppGame.GetDefaultProcessingLayers(),
+ [new(UnstripBaseProcessingLayer.DirectoryKey, unstripDirectory)]);
+Console.WriteLine("Done!");
+
+/*
+Todo
+- Add attributes to "Unsafe" methods so that users cannot see them
+- Lazily create field offset accessors
+- Reduce number of implementation methods
+- Make generic inflation more robust
+*/
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/CachedScanResultsAttribute.cs b/Il2CppInterop.Common/Attributes/CachedScanResultsAttribute.cs
deleted file mode 100644
index 4f2396d8..00000000
--- a/Il2CppInterop.Common/Attributes/CachedScanResultsAttribute.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace Il2CppInterop.Common.Attributes;
-
-[AttributeUsage(AttributeTargets.Method, Inherited = false)]
-public class CachedScanResultsAttribute : Attribute
-{
- // Data for metadata init call
- public long MetadataInitFlagRva;
- public long MetadataInitTokenRva;
- public int RefRangeEnd;
-
- // Methods that call this method
- public int RefRangeStart;
-
- public int XrefRangeEnd;
-
- // Items that this method calls/uses
- public int XrefRangeStart;
-}
diff --git a/Il2CppInterop.Common/Attributes/CallerCountAttribute.cs b/Il2CppInterop.Common/Attributes/CallerCountAttribute.cs
deleted file mode 100644
index ebb1043a..00000000
--- a/Il2CppInterop.Common/Attributes/CallerCountAttribute.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Il2CppInterop.Common.Attributes;
-
-[AttributeUsage(AttributeTargets.Method, Inherited = false)]
-public class CallerCountAttribute : Attribute
-{
- public readonly int Count;
-
- public CallerCountAttribute(int count)
- {
- Count = count;
- }
-}
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..cb6bc49f
--- /dev/null
+++ b/Il2CppInterop.Common/IIl2CppType.cs
@@ -0,0 +1,148 @@
+namespace Il2CppInterop.Common;
+
+public interface IIl2CppType
+{
+ IntPtr ObjectClass { get; }
+ ///
+ /// Box this managed object as a native Il2Cpp object
+ ///
+ ///
+ /// For classes, this returns a pointer to the existing Il2Cpp object.
+ /// For value types, this creates a new boxed Il2Cpp object.
+ ///
+ /// The pointer to the boxed Il2Cpp object
+ ObjectPointer BoxNative();
+ ///
+ /// Box this managed object
+ ///
+ ///
+ /// This method is necessary for semantically accurate boxing of Il2CppSystem.Nullable<T> in unstripped code.
+ /// All other types are expected to use the default implementation.
+ ///
+ /// The boxed object
+ object Box() => this;
+}
+public interface IIl2CppType : IIl2CppType where TSelf : notnull, IIl2CppType
+{
+ ///
+ /// The native size of the type in bytes
+ ///
+ static virtual int Size
+ {
+ get
+ {
+ if (typeof(TSelf).IsValueType)
+ {
+ throw new NotImplementedException($"{typeof(TSelf).FullName} did not implement this property.");
+ }
+ else
+ {
+ // All reference types are the size of a pointer in native code
+ return IntPtr.Size;
+ }
+ }
+ }
+ ///
+ /// The file name of the assembly that the type is defined in
+ ///
+ ///
+ /// Technically, this is the image name, not the assembly name.
+ /// In practice, the assembly name has no file extension, whereas the image name does.
+ ///
+ 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 virtual void WriteToSpan(TSelf? value, Span span)
+ {
+ if (typeof(TSelf).IsValueType)
+ {
+ throw new NotImplementedException($"{typeof(TSelf).FullName} did not implement this method.");
+ }
+ else
+ {
+ Il2CppType.WriteReference(value, 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 virtual TSelf? ReadFromSpan(ReadOnlySpan span)
+ {
+ if (typeof(TSelf).IsValueType)
+ {
+ throw new NotImplementedException($"{typeof(TSelf).FullName} did not implement this method.");
+ }
+ else
+ {
+ return Il2CppType.ReadReference(span);
+ }
+ }
+ ///
+ /// Unboxes the provided native Il2Cpp object to the type
+ ///
+ ///
+ /// This always returns -typed objects, not subclasses of .
+ /// Callers are expected to know that this is the exact type of the object being unboxed.
+ /// Otherwise, they should use and .
+ ///
+ /// The pointer to the native Il2Cpp object.
+ /// The unboxed value.
+ /// is an interface or abstract class.
+ /// did not implement this method.
+ static virtual unsafe TSelf? UnboxNative(ObjectPointer pointer)
+ {
+ if (typeof(TSelf).IsValueType)
+ {
+ var unboxed = IL2CPP.il2cpp_object_unbox((nint)pointer);
+ return TSelf.ReadFromSpan(new ReadOnlySpan((void*)unboxed, TSelf.Size));
+ }
+ else if (typeof(TSelf).IsInterface)
+ {
+ throw new NotSupportedException("Interfaces cannot be unboxed because they are not concrete types.");
+ }
+ else if (typeof(TSelf).IsAbstract)
+ {
+ throw new NotSupportedException("Abstract classes cannot be unboxed because they are not concrete types.");
+ }
+ else
+ {
+ throw new NotImplementedException($"{typeof(TSelf).FullName} did not implement this method.");
+ }
+ }
+ ///
+ /// Unboxes the provided object to the type
+ ///
+ ///
+ /// This is only intended to be overridden by Il2CppSystem.Nullable<T>, which has special unboxing behavior.
+ ///
+ /// The object to unbox.
+ /// The unboxed value.
+ static virtual TSelf? Unbox(object? obj)
+ {
+ return (TSelf?)obj;
+ }
+}
diff --git a/Il2CppInterop.Common/IL2CPP.cs b/Il2CppInterop.Common/IL2CPP.cs
new file mode 100644
index 00000000..c7994ba7
--- /dev/null
+++ b/Il2CppInterop.Common/IL2CPP.cs
@@ -0,0 +1,871 @@
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+
+namespace Il2CppInterop.Common;
+
+///
+/// IL2CPP Functions
+///
+public static unsafe partial class IL2CPP
+{
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_init(nint domain_name);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_init_utf16(nint domain_name);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_shutdown();
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_set_config_dir(nint config_path);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_set_data_dir(nint data_path);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_set_temp_dir(nint temp_path);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_set_commandline_arguments(int argc, nint argv, nint basedir);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_set_commandline_arguments_utf16(int argc, nint argv, nint basedir);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_set_config_utf16(nint executablePath);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_set_config(nint executablePath);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_set_memory_callbacks(nint callbacks);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_get_corlib();
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_add_internal_call(nint name, nint method);
+
+ [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);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_alloc(uint size);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_free(nint ptr);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_array_class_get(nint element_class, uint rank);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial uint il2cpp_array_length(nint array);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial uint il2cpp_array_get_byte_length(nint array);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_array_new(nint elementTypeInfo, ulong length);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_array_new_specific(nint arrayTypeInfo, ulong length);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_array_new_full(nint array_class, ulong* lengths, ulong* lower_bounds);
+
+ [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);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial int il2cpp_array_element_size(nint array_class);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_assembly_get_image(nint assembly);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_enum_basetype(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_class_is_generic(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_class_is_inflated(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_class_is_assignable_from(nint klass, nint oklass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_class_is_subclass_of(nint klass, nint klassc, [MarshalAs(UnmanagedType.I1)] bool check_interfaces);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_class_has_parent(nint klass, nint klassc);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_from_il2cpp_type(nint 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);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_from_system_type(nint type);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_get_element_class(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_get_events(nint klass, ref nint iter);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_get_fields(nint klass, ref nint iter);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_get_nested_types(nint klass, ref nint iter);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_get_interfaces(nint klass, ref nint iter);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_get_properties(nint klass, ref nint iter);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_get_property_from_name(nint klass, nint name);
+
+ [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);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_get_methods(nint klass, ref nint iter);
+
+ [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);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalUsing(typeof(Il2CppStringMarshaller))]
+ public static partial string il2cpp_class_get_name(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalUsing(typeof(Il2CppStringMarshaller))]
+ public static partial string il2cpp_class_get_namespace(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_get_parent(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_get_declaring_type(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial int il2cpp_class_instance_size(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial uint il2cpp_class_num_fields(nint enumKlass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_class_is_valuetype(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial int il2cpp_class_value_size(nint klass, out uint align);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_class_is_blittable(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial int il2cpp_class_get_flags(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_class_is_abstract(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_class_is_interface(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial int il2cpp_class_array_element_size(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_from_type(nint type);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_get_type(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial uint il2cpp_class_get_type_token(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_class_has_attribute(nint klass, nint attr_class);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_class_has_references(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_class_is_enum(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_get_image(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_class_get_assemblyname(nint klass);
+
+ public static string? il2cpp_class_get_assemblyname_(nint klass)
+ => Marshal.PtrToStringUTF8(il2cpp_class_get_assemblyname(klass));
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial int il2cpp_class_get_rank(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial uint il2cpp_class_get_bitmap_size(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_class_get_bitmap(nint klass, ref uint bitmap);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ 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);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_domain_get();
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_domain_assembly_open(nint domain, nint name);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint* il2cpp_domain_get_assemblies(nint domain, out uint size);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_raise_exception(nint ex);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_exception_from_name_msg(nint image, nint name_space, nint name, nint msg);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_get_exception_argument_null(nint arg);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_format_exception(nint ex, void* message, int message_size);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_format_stack_trace(nint ex, void* output, int output_size);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_unhandled_exception(nint ex);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial int il2cpp_field_get_flags(nint field);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalUsing(typeof(Il2CppStringMarshaller))]
+ public static partial string il2cpp_field_get_name(nint field);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_field_get_parent(nint field);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial uint il2cpp_field_get_offset(nint field);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_field_get_type(nint field);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_field_get_value(nint obj, nint field, void* value);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_field_get_value_object(nint field, nint obj);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_field_has_attribute(nint field, nint attr_class);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_field_set_value(nint obj, nint field, void* value);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_field_static_get_value(nint field, void* value);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_field_static_set_value(nint field, void* value);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_field_set_value_object(nint instance, nint field, nint value);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_gc_collect(int maxGenerations);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial int il2cpp_gc_collect_a_little();
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_gc_disable();
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_gc_enable();
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_gc_is_disabled();
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial long il2cpp_gc_get_used_size();
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial long il2cpp_gc_get_heap_size();
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_gc_wbarrier_set_field(nint obj, nint targetAddress, nint gcObj);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_gchandle_new(nint obj, [MarshalAs(UnmanagedType.I1)] bool pinned);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_gchandle_new_weakref(nint obj, [MarshalAs(UnmanagedType.I1)] bool track_resurrection);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_gchandle_get_target(nint gchandle);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_gchandle_free(nint gchandle);
+
+ [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);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_unity_liveness_calculation_end(nint state);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_unity_liveness_calculation_from_root(nint root, nint state);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_unity_liveness_calculation_from_statics(nint state);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_method_get_return_type(nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_method_get_declaring_type(nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalUsing(typeof(Il2CppStringMarshaller))]
+ public static partial string il2cpp_method_get_name(nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_method_get_from_reflection(nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_method_get_object(nint method, nint refclass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_method_is_generic(nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_method_is_inflated(nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_method_is_instance(nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial uint il2cpp_method_get_param_count(nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_method_get_param(nint method, uint index);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_method_get_class(nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_method_has_attribute(nint method, nint attr_class);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial uint il2cpp_method_get_flags(nint method, ref uint iflags);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial uint il2cpp_method_get_token(nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalUsing(typeof(Il2CppStringMarshaller))]
+ public static partial string il2cpp_method_get_param_name(nint method, uint index);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_profiler_install(nint prof, nint shutdown_callback);
+
+ //[LibraryImport("GameAssembly")]
+ //[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ //public static partial void il2cpp_profiler_set_events(IL2CPP_ProfileFlags events);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_profiler_install_enter_leave(nint enter, nint fleave);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_profiler_install_allocation(nint callback);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_profiler_install_gc(nint callback, nint heap_resize_callback);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_profiler_install_fileio(nint callback);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_profiler_install_thread(nint start, nint end);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial uint il2cpp_property_get_flags(nint prop);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_property_get_get_method(nint prop);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_property_get_set_method(nint prop);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalUsing(typeof(Il2CppStringMarshaller))]
+ public static partial string il2cpp_property_get_name(nint prop);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_property_get_parent(nint prop);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_object_get_class(nint obj);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial uint il2cpp_object_get_size(nint obj);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_object_get_virtual_method(nint obj, nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_object_new(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_object_unbox(nint obj);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_value_box(nint klass, nint data);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_monitor_enter(nint obj);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_monitor_try_enter(nint obj, uint timeout);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_monitor_exit(nint obj);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_monitor_pulse(nint obj);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_monitor_pulse_all(nint obj);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_monitor_wait(nint obj);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_monitor_try_wait(nint obj, uint timeout);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_runtime_invoke(nint method, nint obj, void** param, ref nint exc);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ // param can be of Il2CppObject*
+ public static partial nint il2cpp_runtime_invoke_convert_args(nint method, nint obj, void** param, int paramCount, ref nint exc);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_runtime_class_init(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_runtime_object_init(nint obj);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_runtime_object_init_exception(nint obj, ref nint exc);
+
+ //[LibraryImport("GameAssembly")]
+ //[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ //public static partial void il2cpp_runtime_unhandled_exception_policy_set(IL2CPP_RuntimeUnhandledExceptionPolicy value);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial int il2cpp_string_length(nint str);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial char* il2cpp_string_chars(nint str);
+
+ [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);
+
+ [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);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint 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_wrapper(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);
+
+ [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);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_thread_current();
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_thread_attach(nint domain);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_thread_detach(nint thread);
+
+ [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 partial bool il2cpp_is_vm_thread(nint thread);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_current_thread_walk_frame_stack(nint func, nint user_data);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_thread_walk_frame_stack(nint thread, nint func, nint user_data);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_current_thread_get_top_frame(nint frame);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_thread_get_top_frame(nint thread, nint frame);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_current_thread_get_frame_at(int offset, nint frame);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_thread_get_frame_at(nint thread, int offset, nint frame);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial int il2cpp_current_thread_get_stack_depth();
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial int il2cpp_thread_get_stack_depth(nint thread);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_type_get_object(nint type);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial int il2cpp_type_get_type(nint type);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_type_get_class_or_element_class(nint type);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalUsing(typeof(Il2CppStringMarshaller))]
+ public static partial string il2cpp_type_get_name(nint type);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_type_is_byref(nint type);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial uint il2cpp_type_get_attrs(nint type);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_type_equals(nint type, nint otherType);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_type_get_assembly_qualified_name(nint type);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_image_get_assembly(nint image);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalUsing(typeof(Il2CppStringMarshaller))]
+ public static partial string il2cpp_image_get_name(nint image);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalUsing(typeof(Il2CppStringMarshaller))]
+ public static partial string il2cpp_image_get_filename(nint image);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_image_get_entry_point(nint image);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial uint il2cpp_image_get_class_count(nint image);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_image_get_class(nint image, uint index);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_capture_memory_snapshot();
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_free_captured_memory_snapshot(nint snapshot);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_set_find_plugin_callback(nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_register_log_callback(nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_debugger_set_agent_options(nint options);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_is_debugger_attached();
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_unity_install_unitytls_interface(void* unitytlsInterfaceStruct);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_custom_attrs_from_class(nint klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_custom_attrs_from_method(nint method);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_custom_attrs_get_attr(nint ainfo, nint attr_klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ [return: MarshalAs(UnmanagedType.I1)]
+ public static partial bool il2cpp_custom_attrs_has_attr(nint ainfo, nint attr_klass);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial nint il2cpp_custom_attrs_construct(nint cinfo);
+
+ [LibraryImport("GameAssembly")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ public static partial void il2cpp_custom_attrs_free(nint ainfo);
+
+ [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedOut, typeof(Il2CppStringMarshaller))]
+ private static class Il2CppStringMarshaller
+ {
+ public static string? ConvertToManaged(byte* unmanaged)
+ => Marshal.PtrToStringUTF8((nint)unmanaged);
+
+ public static void Free(byte* unmanaged)
+ {
+ // Intentionally empty, il2cpp owns this pointer
+ _ = unmanaged;
+ }
+ }
+}
diff --git a/Il2CppInterop.Common/Il2CppInternalsAccess.cs b/Il2CppInterop.Common/Il2CppInternalsAccess.cs
new file mode 100644
index 00000000..dfa37d92
--- /dev/null
+++ b/Il2CppInterop.Common/Il2CppInternalsAccess.cs
@@ -0,0 +1,65 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using Il2CppInterop.Common.Attributes;
+
+namespace Il2CppInterop.Common;
+
+[RequiresUnreferencedCode("This API uses reflection to access fields in the corresponding Il2CppInternals type.")]
+[RequiresDynamicCode("This API generically instantiates the corresponding Il2CppInternals type if necessary.")]
+public static class Il2CppInternalsAccess
+{
+ private static FieldInfo? GetFieldInfo(Type declaringType, string prefix, int index)
+ {
+ 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)
+ {
+ var attr = declaringType.GetCustomAttribute();
+ if (attr == null)
+ return null;
+
+ var internals = attr.Internals;
+
+ if (internals.IsGenericTypeDefinition && declaringType.IsConstructedGenericType)
+ {
+ internals = internals.MakeGenericType(declaringType.GetGenericArguments());
+ }
+
+ return internals;
+ }
+ }
+
+ public static FieldInfo? GetIl2CppMethodInfoPointerFieldForGeneratedMethod(MethodBase method)
+ {
+ var declaringType = method.DeclaringType;
+ if (declaringType == null)
+ return null;
+
+ var index = method.GetCustomAttribute()?.Index ?? -1;
+
+ return GetFieldInfo(declaringType, "MethodInfoPtr_", index);
+ }
+
+ [RequiresUnreferencedCode("This API uses reflection to access fields in the corresponding Il2CppInternals type and to access property metadata for the method's declaring type.")]
+ public static FieldInfo? GetIl2CppFieldInfoPointerFieldForGeneratedFieldAccessor(MethodBase method)
+ {
+ 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/Il2CppInterop.Common.csproj b/Il2CppInterop.Common/Il2CppInterop.Common.csproj
index d9320b4d..2ffa912e 100644
--- a/Il2CppInterop.Common/Il2CppInterop.Common.csproj
+++ b/Il2CppInterop.Common/Il2CppInterop.Common.csproj
@@ -1,16 +1,15 @@
-
+
- netstandard2.0
- BepInEx, knah et al.
+ Il2CppInterop.Common
+ net10.0
Common API for Il2CppInterop packages
- Il2CppInterop.Common
+ true
-
-
-
+
+
diff --git a/Il2CppInterop.Common/Il2CppInteropUtils.cs b/Il2CppInterop.Common/Il2CppInteropUtils.cs
deleted file mode 100644
index 99948d3c..00000000
--- a/Il2CppInterop.Common/Il2CppInteropUtils.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using System.Reflection;
-using System.Reflection.Emit;
-
-namespace Il2CppInterop.Common;
-
-public static class Il2CppInteropUtils
-{
- private static FieldInfo? GetFieldInfoFromMethod(MethodBase method, string prefix)
- {
- 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 (opCode != OpCodes.Ldsfld) continue;
-
- var fieldInfo = methodModule.ResolveField((int)opArg, method.DeclaringType.GenericTypeArguments, method.GetGenericArguments());
- if (fieldInfo?.FieldType != typeof(IntPtr)) continue;
-
- if (fieldInfo.Name.StartsWith(prefix)) return fieldInfo;
-
- // Resolve generic method info pointer fields
- if (method.IsGenericMethod && fieldInfo.DeclaringType.Name.StartsWith("MethodInfoStoreGeneric_") && fieldInfo.Name == "Pointer") return fieldInfo;
- }
-
- return null;
- }
-
- public static FieldInfo GetIl2CppMethodInfoPointerFieldForGeneratedMethod(MethodBase method)
- {
- return GetFieldInfoFromMethod(method, "NativeMethodInfoPtr_");
- }
-
- public static FieldInfo GetIl2CppFieldInfoPointerFieldForGeneratedFieldAccessor(MethodBase method)
- {
- return GetFieldInfoFromMethod(method, "NativeFieldInfoPtr_");
- }
-}
diff --git a/Il2CppInterop.Common/Il2CppObjectPool.cs b/Il2CppInterop.Common/Il2CppObjectPool.cs
new file mode 100644
index 00000000..514fcc45
--- /dev/null
+++ b/Il2CppInterop.Common/Il2CppObjectPool.cs
@@ -0,0 +1,103 @@
+using System.Collections.Concurrent;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+
+namespace Il2CppInterop.Common;
+
+public static class Il2CppObjectPool
+{
+ private static readonly ConcurrentDictionary> s_cache = new();
+
+ private static readonly ConcurrentDictionary s_initializers = new();
+
+ public static void Remove(nint ptr)
+ {
+ s_cache.TryRemove(ptr, out _);
+ }
+
+ [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "This will never be called in a trimmed context.")]
+ [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "This will never be called in an AOT context.")]
+ public static object? Get(nint ptr)
+ {
+ 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 (!s_initializers.TryGetValue(ownClass, out var initializer))
+ {
+ var systemType = EnsureClassInitialized(ownClass);
+ if (!s_initializers.TryGetValue(ownClass, out initializer))
+ {
+ throw new InvalidOperationException($"No initializer found for class {systemType}");
+ }
+ }
+
+ var newObj = initializer.Invoke((ObjectPointer)ptr);
+ if (!newObj.GetType().IsValueType)
+ {
+ s_cache[ptr] = new WeakReference