diff --git a/src/Helpers/ReflectionHelpers.cs b/src/Helpers/ReflectionHelpers.cs index 8fa1c96..53e2b0e 100644 --- a/src/Helpers/ReflectionHelpers.cs +++ b/src/Helpers/ReflectionHelpers.cs @@ -5,11 +5,11 @@ using System.IO; using System.Reflection; using UnityEngine; using BF = System.Reflection.BindingFlags; - #if CPP using ILType = Il2CppSystem.Type; using UnhollowerBaseLib; using UnhollowerRuntimeLib; +using System.Runtime.InteropServices; #endif namespace Explorer.Helpers @@ -20,79 +20,69 @@ namespace Explorer.Helpers #if CPP public static ILType GameObjectType => Il2CppType.Of(); - public static ILType TransformType => Il2CppType.Of(); - public static ILType ObjectType => Il2CppType.Of(); - public static ILType ComponentType => Il2CppType.Of(); - public static ILType BehaviourType => Il2CppType.Of(); + public static ILType TransformType => Il2CppType.Of(); + public static ILType ObjectType => Il2CppType.Of(); + public static ILType ComponentType => Il2CppType.Of(); + public static ILType BehaviourType => Il2CppType.Of(); +#else + public static Type GameObjectType => typeof(GameObject); + public static Type TransformType => typeof(Transform); + public static Type ObjectType => typeof(UnityEngine.Object); + public static Type ComponentType => typeof(Component); + public static Type BehaviourType => typeof(Behaviour); +#endif - private static readonly MethodInfo tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast"); - private static readonly Dictionary cachedTryCastMethods = new Dictionary(); +#if CPP + private static readonly Dictionary ClassPointers = new Dictionary(); public static object Il2CppCast(object obj, Type castTo) { - if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj; + if (!(obj is Il2CppSystem.Object ilObj)) + return obj; - if (!cachedTryCastMethods.ContainsKey(castTo)) + if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) + return obj; + + IntPtr castToPtr; + if (!ClassPointers.ContainsKey(castTo)) { - cachedTryCastMethods.Add(castTo, tryCastMethodInfo.MakeGenericMethod(castTo)); - } + castToPtr = (IntPtr)typeof(Il2CppClassPointerStore<>) + .MakeGenericType(new Type[] { castTo }) + .GetField("NativeClassPtr", BF.Public | BF.Static) + .GetValue(null); - return cachedTryCastMethods[castTo].Invoke(obj, null); - } -#else - public static Type GameObjectType => typeof(GameObject); - public static Type TransformType => typeof(Transform); - public static Type ObjectType => typeof(UnityEngine.Object); - public static Type ComponentType => typeof(Component); - public static Type BehaviourType => typeof(Behaviour); -#endif + if (castToPtr == IntPtr.Zero) + { + ExplorerCore.LogWarning($"[Il2CppCast] Could not get an IntPtr for castTo '{castTo.FullName}'!"); + return obj; + } - public static bool IsEnumerable(Type t) - { - if (typeof(IEnumerable).IsAssignableFrom(t)) - { - return true; - } - -#if CPP - if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g) - { - return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g) - || typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g) - || typeof(Il2CppSystem.Collections.Generic.HashSet<>).IsAssignableFrom(g); + ClassPointers.Add(castTo, castToPtr); } else { - return typeof(Il2CppSystem.Collections.IList).IsAssignableFrom(t); - } -#else - return false; -#endif - } - - public static bool IsDictionary(Type t) - { - if (typeof(IDictionary).IsAssignableFrom(t)) - { - return true; + castToPtr = ClassPointers[castTo]; } -#if CPP - if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g) - { - return typeof(Il2CppSystem.Collections.Generic.Dictionary<,>).IsAssignableFrom(g) - || typeof(Il2CppSystem.Collections.Generic.IDictionary<,>).IsAssignableFrom(g); - } - else - { - return typeof(Il2CppSystem.Collections.IDictionary).IsAssignableFrom(t) - || typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(t); - } -#else - return false; -#endif + IntPtr objPtr = ilObj.Pointer; + var classPtr = il2cpp_object_get_class(objPtr); + + //if (RuntimeSpecificsStore.IsInjected(classPtr)) + // return obj; + + if (!il2cpp_class_is_assignable_from(castToPtr, classPtr)) + return obj; + + return Activator.CreateInstance(castTo, objPtr); } + [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + public static extern bool il2cpp_class_is_assignable_from(IntPtr klass, IntPtr oklass); + + [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + public static extern IntPtr il2cpp_object_get_class(IntPtr obj); +#endif + public static Type GetTypeByName(string fullName) { foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) @@ -169,48 +159,55 @@ namespace Explorer.Helpers return false; } + public static bool IsEnumerable(Type t) + { + if (typeof(IEnumerable).IsAssignableFrom(t)) + { + return true; + } + +#if CPP + if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g) + { + return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g) + || typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g) + || typeof(Il2CppSystem.Collections.Generic.HashSet<>).IsAssignableFrom(g); + } + else + { + return typeof(Il2CppSystem.Collections.IList).IsAssignableFrom(t); + } +#else + return false; +#endif + } + + public static bool IsDictionary(Type t) + { + if (typeof(IDictionary).IsAssignableFrom(t)) + { + return true; + } + +#if CPP + if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g) + { + return typeof(Il2CppSystem.Collections.Generic.Dictionary<,>).IsAssignableFrom(g) + || typeof(Il2CppSystem.Collections.Generic.IDictionary<,>).IsAssignableFrom(g); + } + else + { + return typeof(Il2CppSystem.Collections.IDictionary).IsAssignableFrom(t) + || typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(t); + } +#else + return false; +#endif + } + public static string ExceptionToString(Exception e) { -#if CPP - if (IsFailedGeneric(e)) - { - return "Unable to initialize this type."; - } - else if (IsObjectCollected(e)) - { - return "Garbage collected in Il2Cpp."; - } -#endif return e.GetType() + ", " + e.Message; } - -#if CPP - public static bool IsFailedGeneric(Exception e) - { - return IsExceptionOfType(e, typeof(TargetInvocationException)) && IsExceptionOfType(e, typeof(TypeLoadException)); - } - - public static bool IsObjectCollected(Exception e) - { - return IsExceptionOfType(e, typeof(ObjectCollectedException)); - } - - public static bool IsExceptionOfType(Exception e, Type t, bool strict = true, bool checkInner = true) - { - bool isType; - - if (strict) - isType = e.GetType() == t; - else - isType = t.IsAssignableFrom(e.GetType()); - - if (isType) return true; - - if (e.InnerException != null && checkInner) - return IsExceptionOfType(e.InnerException, t, strict); - else - return false; - } -#endif } } diff --git a/src/Helpers/Texture2DHelpers.cs b/src/Helpers/Texture2DHelpers.cs index 791d4fb..2d5f4b0 100644 --- a/src/Helpers/Texture2DHelpers.cs +++ b/src/Helpers/Texture2DHelpers.cs @@ -143,21 +143,7 @@ namespace Explorer.Helpers } else { -#if CPP - // The Il2Cpp EncodeToPNG() method does return System.Byte[], - // but for some reason it is not recognized or valid. - // Simple fix is iterating into a new array manually. - - byte[] safeData = new byte[data.Length]; - for (int i = 0; i < data.Length; i++) - { - safeData[i] = (byte)data[i]; // not sure if cast is needed - } - - File.WriteAllBytes(savepath, safeData); -#else File.WriteAllBytes(savepath, data); -#endif } } diff --git a/src/Unstrip/ImageConversion/ImageConversionUnstrip.cs b/src/Unstrip/ImageConversion/ImageConversionUnstrip.cs index 0be4c7e..bac3c0b 100644 --- a/src/Unstrip/ImageConversion/ImageConversionUnstrip.cs +++ b/src/Unstrip/ImageConversion/ImageConversionUnstrip.cs @@ -7,6 +7,9 @@ using UnhollowerBaseLib; using UnityEngine; using System.IO; using Explorer.Helpers; +using System.Runtime.InteropServices; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; namespace Explorer.Unstrip.ImageConversion { @@ -18,18 +21,41 @@ namespace Explorer.Unstrip.ImageConversion public static byte[] EncodeToPNG(this Texture2D tex) { - return ICallHelper.GetICall("UnityEngine.ImageConversion::EncodeToPNG") + var data = ICallHelper.GetICall("UnityEngine.ImageConversion::EncodeToPNG") .Invoke(tex.Pointer); + + // The Il2Cpp EncodeToPNG() method does return System.Byte[], + // but for some reason it is not recognized or valid. + // Simple fix is iterating into a new array manually. + + byte[] safeData = new byte[data.Length]; + for (int i = 0; i < data.Length; i++) + { + safeData[i] = (byte)data[i]; // not sure if cast is needed + } + + return safeData; } + // ******** LoadImage not yet working. ******** + // bool ImageConversion.LoadImage(this Texture2D tex, byte[] data, bool markNonReadable); - internal delegate bool d_LoadImage(IntPtr tex, byte[] data, bool markNonReadable); + internal delegate bool d_LoadImage(IntPtr tex, IntPtr data, bool markNonReadable); public static bool LoadImage(this Texture2D tex, byte[] data, bool markNonReadable) { - return ICallHelper.GetICall("UnityEngine.ImageConversion::LoadImage") - .Invoke(tex.Pointer, data, markNonReadable); + IntPtr unmanagedArray = Marshal.AllocHGlobal(data.Length); + Marshal.Copy(data, 0, unmanagedArray, data.Length); + + var ret = ICallHelper.GetICall("UnityEngine.ImageConversion::LoadImage") + .Invoke(tex.Pointer, unmanagedArray, markNonReadable); + + // var ret = tex.LoadRawTextureDataImpl(unmanagedArray, data.Length); + + Marshal.FreeHGlobal(unmanagedArray); + + return ret; } // Helper for LoadImage from filepath