diff --git a/src/Core/Tests/TestClass.cs b/src/Core/Tests/TestClass.cs index c65948e..f60aea6 100644 --- a/src/Core/Tests/TestClass.cs +++ b/src/Core/Tests/TestClass.cs @@ -14,8 +14,40 @@ using UnhollowerBaseLib; namespace UnityExplorer.Tests { + public class TestIndexer : IList + { + private readonly List list = new List() { 1,2,3,4,5 }; + + public int Count => list.Count; + public bool IsReadOnly => false; + + int IList.this[int index] + { + get => list[index]; + set => list[index] = value; + } + + public int IndexOf(int item) => list.IndexOf(item); + public bool Contains(int item) => list.Contains(item); + + public void Add(int item) => list.Add(item); + public void Insert(int index, int item) => list.Insert(index, item); + + public bool Remove(int item) => list.Remove(item); + public void RemoveAt(int index) => list.RemoveAt(index); + + public void Clear() => list.Clear(); + + public void CopyTo(int[] array, int arrayIndex) => list.CopyTo(array, arrayIndex); + + public IEnumerator GetEnumerator() => list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator(); + } + public static class TestClass { + public static readonly TestIndexer AAAAATest = new TestIndexer(); + public static void ATestMethod(string s, float f, Vector3 vector, DateTime date, Quaternion quater, bool b, CameraClearFlags enumvalue) { ExplorerCore.Log($"{s}, {f}, {vector.ToString()}, {date}, {quater.eulerAngles.ToString()}, {b}, {enumvalue}"); diff --git a/src/ExplorerCore.cs b/src/ExplorerCore.cs index e82a073..b8092f4 100644 --- a/src/ExplorerCore.cs +++ b/src/ExplorerCore.cs @@ -19,7 +19,7 @@ namespace UnityExplorer public static class ExplorerCore { public const string NAME = "UnityExplorer"; - public const string VERSION = "4.0.0"; + public const string VERSION = "4.0.1"; public const string AUTHOR = "Sinai"; public const string GUID = "com.sinai.unityexplorer"; diff --git a/src/UI/IValues/InteractiveList.cs b/src/UI/IValues/InteractiveList.cs index c861653..16a13ed 100644 --- a/src/UI/IValues/InteractiveList.cs +++ b/src/UI/IValues/InteractiveList.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Reflection; using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI.CacheObject; @@ -19,11 +20,14 @@ namespace UnityExplorer.UI.IValues object ICacheObjectController.Target => this.CurrentOwner.Value; public Type TargetType { get; private set; } - public override bool CanWrite => base.CanWrite && RefIList != null && !RefIList.IsReadOnly; + public override bool CanWrite => base.CanWrite && ((RefIList != null && !RefIList.IsReadOnly) || IsWritableGenericIList); public Type EntryType; public IList RefIList; + private bool IsWritableGenericIList; + private PropertyInfo genericIndexer; + public int ItemCount => values.Count; private readonly List values = new List(); private readonly List cachedEntries = new List(); @@ -92,6 +96,12 @@ namespace UnityExplorer.UI.IValues { RefIList = value as IList; + // Check if the type implements IList but not IList (ie. Il2CppArrayBase) + if (RefIList == null) + CheckGenericIList(value); + else + IsWritableGenericIList = false; + values.Clear(); int idx = 0; @@ -141,14 +151,61 @@ namespace UnityExplorer.UI.IValues } } + private void CheckGenericIList(object value) + { + try + { + var type = value.GetType(); + if (type.GetInterfaces().Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == typeof(IList<>))) + IsWritableGenericIList = !(bool)type.GetProperty("IsReadOnly").GetValue(value, null); + else + IsWritableGenericIList = false; + + if (IsWritableGenericIList) + { + // Find the "this[int index]" property. + // It might be a private implementation. + foreach (var prop in type.GetProperties(ReflectionUtility.FLAGS)) + { + if ((prop.Name == "Item" + || (prop.Name.StartsWith("System.Collections.Generic.IList<") && prop.Name.EndsWith(">.Item"))) + && prop.GetIndexParameters() is ParameterInfo[] parameters + && parameters.Length == 1 + && parameters[0].ParameterType == typeof(int)) + { + genericIndexer = prop; + break; + } + } + + if (genericIndexer == null) + { + ExplorerCore.LogWarning($"Failed to find indexer property for IList type '{type.FullName}'!"); + IsWritableGenericIList = false; + } + } + } + catch (Exception ex) + { + ExplorerCore.LogWarning($"Exception processing IEnumerable for IList check: {ex.ReflectionExToString()}"); + IsWritableGenericIList = false; + } + } + // Setting the value of an index to the list public void TrySetValueToIndex(object value, int index) { try { - //value = value.TryCast(this.EntryType); - RefIList[index] = value; + if (!IsWritableGenericIList) + { + RefIList[index] = value; + } + else + { + genericIndexer.SetValue(CurrentOwner.Value, value, new object[] { index }); + } var entry = cachedEntries[index]; entry.SetValueFromSource(value);