diff --git a/src/Core/Reflection/Il2CppReflection.cs b/src/Core/Reflection/Il2CppReflection.cs index 3d3e03f..350ffa4 100644 --- a/src/Core/Reflection/Il2CppReflection.cs +++ b/src/Core/Reflection/Il2CppReflection.cs @@ -222,7 +222,7 @@ namespace UnityExplorer // from il2cpp objects... - if (!(obj is Il2CppSystem.Object cppObj)) + if (!(obj is Il2CppObjectBase cppObj)) return obj; // from Il2CppSystem.Object to a struct @@ -267,7 +267,7 @@ namespace UnityExplorer internal static readonly Dictionary unboxMethods = new Dictionary(); // Unbox an il2cpp object to a struct or System primitive. - public object UnboxCppObject(Il2CppSystem.Object cppObj, Type toType) + public object UnboxCppObject(Il2CppObjectBase cppObj, Type toType) { if (!toType.IsValueType) return null; @@ -411,7 +411,6 @@ namespace UnityExplorer #endregion - #region Singleton finder internal override void Internal_FindSingleton(string[] possibleNames, Type type, BF flags, List instances) @@ -480,8 +479,6 @@ namespace UnityExplorer #endregion - - #region Il2cpp reflection blacklist public override string DefaultReflectionBlacklist => string.Join(";", defaultIl2CppBlacklist); @@ -629,6 +626,191 @@ namespace UnityExplorer }; #endregion + + + // (Disabled) + #region Temp il2cpp list/dictionary fixes + + //// Temp fix until Unhollower interface support improves + // + //internal static IntPtr s_cppEnumerableClassPtr; + //internal static IntPtr s_cppDictionaryClassPtr; + // + //public override bool Internal_IsEnumerable(Type type) + //{ + // if (base.Internal_IsEnumerable(type)) + // return true; + // + // try + // { + // if (s_cppEnumerableClassPtr == IntPtr.Zero) + // Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IEnumerable), out s_cppEnumerableClassPtr); + // + // if (s_cppEnumerableClassPtr != IntPtr.Zero + // && Il2CppTypeNotNull(type, out IntPtr assignFromPtr) + // && il2cpp_class_is_assignable_from(s_cppEnumerableClassPtr, assignFromPtr)) + // { + // return true; + // } + // } + // catch { } + // + // return false; + //} + // + //// Lists + // + //internal static readonly Dictionary s_getEnumeratorMethods = new Dictionary(); + // + //internal static readonly Dictionary s_enumeratorInfos = new Dictionary(); + // + //internal class EnumeratorInfo + //{ + // internal MethodInfo moveNext; + // internal PropertyInfo current; + //} + // + //internal static IEnumerator EnumerateCppList(object list) + //{ + // if (list == null) + // yield break; + // + // var cppEnumerable = list.TryCast(); + // if (cppEnumerable == null) + // { + // ExplorerCore.LogWarning("Failed to cast an IEnumerable to the interface!"); + // yield break; + // } + // + // // Some ugly reflection to use the il2cpp interface for the instance type + // + // var type = cppEnumerable.GetActualType(); + // if (!s_getEnumeratorMethods.ContainsKey(type)) + // s_getEnumeratorMethods.Add(type, type.GetMethod("GetEnumerator")); + // + // var enumerator = s_getEnumeratorMethods[type].Invoke(cppEnumerable.TryCast(type), null); + // var enumeratorType = enumerator.GetType(); + // + // if (!s_enumeratorInfos.ContainsKey(enumeratorType)) + // { + // s_enumeratorInfos.Add(enumeratorType, new EnumeratorInfo + // { + // current = enumeratorType.GetProperty("Current"), + // moveNext = enumeratorType.GetMethod("MoveNext"), + // }); + // } + // var info = s_enumeratorInfos[enumeratorType]; + // + // // Yield and return the actual entries + // while ((bool)info.moveNext.Invoke(enumerator, null)) + // yield return info.current.GetValue(enumerator); + //} + // + //// Dicts todo + // + //public override bool Internal_IsDictionary(Type type) + //{ + // if (base.Internal_IsDictionary(type)) + // return true; + // + // try + // { + // if (s_cppDictionaryClassPtr == IntPtr.Zero) + // if (!Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IDictionary), out s_cppDictionaryClassPtr)) + // return false; + // + // if (Il2CppTypeNotNull(type, out IntPtr classPtr)) + // { + // if (il2cpp_class_is_assignable_from(s_cppDictionaryClassPtr, classPtr)) + // return true; + // } + // } + // catch { } + // + // return false; + //} + // + //internal static IEnumerator> EnumerateCppDictionary(object dictionary) + //{ + // var cppDict = dictionary?.TryCast(); + // if (cppDict == null) + // yield break; + // + //} + // + // + // + ////public IDictionary EnumerateDictionary(object value) + ////{ + //// var valueType = value.GetActualType(); + //// + //// Type typeOfKeys, typeOfValues; + //// if (valueType.IsGenericType && valueType.GetGenericArguments() is var args && args.Length == 2) + //// { + //// typeOfKeys = args[0]; + //// typeOfValues = args[1]; + //// } + //// else + //// typeOfKeys = typeOfValues = typeof(object); + //// + //// var keyList = new List(); + //// var valueList = new List(); + //// + //// var hashtable = value.TryCast(); + //// + //// if (hashtable != null) + //// { + //// EnumerateCppHashtable(hashtable, keyList, valueList); + //// } + //// else + //// { + //// var keys = valueType.GetProperty("Keys").GetValue(value, null); + //// EnumerateCppCollection(keys, keyList); + //// + //// var values = valueType.GetProperty("Values").GetValue(value, null); + //// EnumerateCppCollection(values, valueList); + //// } + //// + //// var dict = Activator.CreateInstance(typeof(Dictionary<,>) + //// .MakeGenericType(typeOfKeys, typeOfValues)) + //// as IDictionary; + //// + //// for (int i = 0; i < keyList.Count; i++) + //// dict.Add(keyList[i], valueList[i]); + //// + //// return dict; + ////} + //// + ////private void EnumerateCppCollection(object collection, List list) + ////{ + //// // invoke GetEnumerator + //// var enumerator = collection.GetType().GetMethod("GetEnumerator").Invoke(collection, null); + //// // get the type of it + //// var enumeratorType = enumerator.GetType(); + //// // reflect MoveNext and Current + //// var moveNext = enumeratorType.GetMethod("MoveNext"); + //// var current = enumeratorType.GetProperty("Current"); + //// // iterate + //// while ((bool)moveNext.Invoke(enumerator, null)) + //// { + //// list.Add(current.GetValue(enumerator, null)); + //// } + ////} + //// + ////private void EnumerateCppHashtable(Il2CppSystem.Collections.Hashtable hashtable, List keys, List values) + ////{ + //// for (int i = 0; i < hashtable.buckets.Count; i++) + //// { + //// var bucket = hashtable.buckets[i]; + //// if (bucket == null || bucket.key == null) + //// continue; + //// keys.Add(bucket.key); + //// values.Add(bucket.val); + //// } + ////} + + #endregion + } } diff --git a/src/Core/Reflection/ReflectionUtility.cs b/src/Core/Reflection/ReflectionUtility.cs index 3012d91..f60811b 100644 --- a/src/Core/Reflection/ReflectionUtility.cs +++ b/src/Core/Reflection/ReflectionUtility.cs @@ -206,6 +206,7 @@ namespace UnityExplorer #endregion + #region Type and Generic Parameter implementation cache // cache for GetImplementationsOf @@ -340,6 +341,7 @@ namespace UnityExplorer #endregion + #region Internal MemberInfo Cache internal static Dictionary> fieldInfos = new Dictionary>(); @@ -411,6 +413,7 @@ namespace UnityExplorer #endregion + #region Reflection Blacklist public virtual string DefaultReflectionBlacklist => string.Empty; @@ -454,5 +457,29 @@ namespace UnityExplorer #endregion + + // This is NOT working properly. Fails on IL2CPP IList, probably more stuff too. + + //// Temp fix for IL2CPP until interface support improves + // + //public static bool IsEnumerable(Type type) + //{ + // return Instance.Internal_IsEnumerable(type); + //} + // + //public virtual bool Internal_IsEnumerable(Type type) + //{ + // return typeof(IEnumerable).IsAssignableFrom(type); + //} + // + //public static bool IsDictionary(Type type) + //{ + // return Instance.Internal_IsDictionary(type); + //} + // + //public virtual bool Internal_IsDictionary(Type type) + //{ + // return typeof(IDictionary).IsAssignableFrom(type); + //} } } diff --git a/src/UI/CacheObject/CacheObjectBase.cs b/src/UI/CacheObject/CacheObjectBase.cs index 75620fa..bdc2da4 100644 --- a/src/UI/CacheObject/CacheObjectBase.cs +++ b/src/UI/CacheObject/CacheObjectBase.cs @@ -183,9 +183,9 @@ namespace UnityExplorer.UI.CacheObject return ValueState.Color; else if (InteractiveValueStruct.SupportsType(type)) return ValueState.ValueStruct; - else if (typeof(IDictionary).IsAssignableFrom(type)) + else if (typeof(IDictionary).IsAssignableFrom(type)) //(ReflectionUtility.IsDictionary(type)) return ValueState.Dictionary; - else if (!typeof(Transform).IsAssignableFrom(type) && typeof(IEnumerable).IsAssignableFrom(type)) + else if (!typeof(Transform).IsAssignableFrom(type) && typeof(IEnumerable).IsAssignableFrom(type)) //ReflectionUtility.IsEnumerable(type)) return ValueState.Collection; else return ValueState.Unsupported; diff --git a/src/UI/IValues/InteractiveDictionary.cs b/src/UI/IValues/InteractiveDictionary.cs index e91dcad..c55a1bf 100644 --- a/src/UI/IValues/InteractiveDictionary.cs +++ b/src/UI/IValues/InteractiveDictionary.cs @@ -53,6 +53,7 @@ namespace UnityExplorer.UI.IValues private void ClearAndRelease() { + RefIDictionary = null; keys.Clear(); values.Clear(); diff --git a/src/UI/IValues/InteractiveList.cs b/src/UI/IValues/InteractiveList.cs index 7e6c514..9ddb33d 100644 --- a/src/UI/IValues/InteractiveList.cs +++ b/src/UI/IValues/InteractiveList.cs @@ -22,7 +22,7 @@ namespace UnityExplorer.UI.IValues public override bool CanWrite => base.CanWrite && RefIList != null && !RefIList.IsReadOnly; public Type EntryType; - public IEnumerable RefIEnumerable; + //public IEnumerable RefIEnumerable; public IList RefIList; public int ItemCount => values.Count; @@ -49,6 +49,8 @@ namespace UnityExplorer.UI.IValues private void ClearAndRelease() { + //RefIEnumerable = null; + RefIList = null; values.Clear(); foreach (var entry in cachedEntries) @@ -90,20 +92,20 @@ namespace UnityExplorer.UI.IValues private void CacheEntries(object value) { - RefIEnumerable = value as IEnumerable; RefIList = value as IList; - if (RefIEnumerable == null) - { - // todo il2cpp - return; - } + IEnumerator enumerator = (value as IEnumerable).GetEnumerator(); + + //if (value is IEnumerable enumerable) + // enumerator = enumerable.GetEnumerator(); + //else + // enumerator = Il2CppReflection.EnumerateCppList(value); values.Clear(); int idx = 0; - foreach (var entry in RefIEnumerable) + while (enumerator.MoveNext()) { - // var entry = item.TryCast(); + var entry = enumerator.Current; values.Add(entry);