using System.Collections; using System.Collections.Generic; using System.Reflection.Emit; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.Injection; using Il2CppSystem; using ArgumentNullException = System.ArgumentNullException; using Il2CppIEnumerator = Il2CppSystem.Collections.IEnumerator; using IntPtr = System.IntPtr; using NotSupportedException = System.NotSupportedException; using Type = System.Type; namespace BepInEx.Unity.IL2CPP.Utils.Collections; public class Il2CppManagedEnumerator : Object { private static readonly Dictionary> boxers = new(); private readonly IEnumerator enumerator; static Il2CppManagedEnumerator() { ClassInjector.RegisterTypeInIl2Cpp(new RegisterTypeOptions { Interfaces = new[] { typeof(Il2CppIEnumerator) } }); } public Il2CppManagedEnumerator(IntPtr ptr) : base(ptr) { } public Il2CppManagedEnumerator(IEnumerator enumerator) : base(ClassInjector.DerivedConstructorPointer()) { this.enumerator = enumerator ?? throw new ArgumentNullException(nameof(enumerator)); ClassInjector.DerivedConstructorBody(this); } public Object Current => enumerator.Current switch { Il2CppIEnumerator i => i.Cast(), IEnumerator e => new Il2CppManagedEnumerator(e), Object oo => oo, { } obj => ManagedToIl2CppObject(obj), null => null }; public bool MoveNext() => enumerator.MoveNext(); public void Reset() => enumerator.Reset(); private static System.Func GetValueBoxer(Type t) { if (boxers.TryGetValue(t, out var conv)) return conv; var dm = new DynamicMethod($"Il2CppUnbox_{t.FullDescription()}", typeof(Object), new[] { typeof(object) }); var il = dm.GetILGenerator(); var loc = il.DeclareLocal(t); var classField = typeof(Il2CppClassPointerStore<>).MakeGenericType(t) .GetField(nameof(Il2CppClassPointerStore .NativeClassPtr)); il.Emit(OpCodes.Ldsfld, classField); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Unbox_Any, t); il.Emit(OpCodes.Stloc, loc); il.Emit(OpCodes.Ldloca, loc); il.Emit(OpCodes.Call, typeof(Il2CppInterop.Runtime.IL2CPP).GetMethod(nameof(Il2CppInterop.Runtime.IL2CPP.il2cpp_value_box))); il.Emit(OpCodes.Newobj, typeof(Object).GetConstructor(new[] { typeof(IntPtr) })); il.Emit(OpCodes.Ret); var converter = dm.CreateDelegate(typeof(System.Func)) as System.Func; boxers[t] = converter; return converter; } private static Object ManagedToIl2CppObject(object obj) { var t = obj.GetType(); if (obj is string s) return new Object(Il2CppInterop.Runtime.IL2CPP.ManagedStringToIl2Cpp(s)); if (t.IsPrimitive) return GetValueBoxer(t)(obj); throw new NotSupportedException($"Type {t} cannot be converted directly to an Il2Cpp object"); } }