diff --git a/src/CachedObjects/CacheObjectBase.cs b/src/CachedObjects/CacheObjectBase.cs index f990963b..cea0613e 100644 --- a/src/CachedObjects/CacheObjectBase.cs +++ b/src/CachedObjects/CacheObjectBase.cs @@ -16,15 +16,14 @@ public abstract class CacheObjectBase public string ValueTypeName; public Type ValueType; - // Reflection Inspector only public MemberInfo MemInfo { get; set; } public Type DeclaringType { get; set; } public object DeclaringInstance { get; set; } - public string ReflectionException { get; set; } - public int PropertyIndex { get; private set; } private string m_propertyIndexInput = "0"; + public string ReflectionException { get; set; } + public string RichTextName => m_richTextName ?? GetRichTextName(); private string m_richTextName; diff --git a/src/CachedObjects/Object/CacheDictionary.cs b/src/CachedObjects/Object/CacheDictionary.cs index 3abb6a76..266e0d29 100644 --- a/src/CachedObjects/Object/CacheDictionary.cs +++ b/src/CachedObjects/Object/CacheDictionary.cs @@ -10,13 +10,13 @@ namespace Explorer { - public class CacheDictionary : CacheObjectBase + public class CacheDictionary : CacheObjectBase, IExpandHeight { public bool IsExpanded { get; set; } - public PageHelper Pages = new PageHelper(); + public float WhiteSpace { get; set; } = 215f; + public float ButtonWidthOffset { get; set; } = 290f; - public float WhiteSpace = 215f; - public float ButtonWidthOffset = 290f; + public PageHelper Pages = new PageHelper(); private CacheObjectBase[] m_cachedKeys; private CacheObjectBase[] m_cachedValues; @@ -48,6 +48,8 @@ public IDictionary IDict } private IDictionary m_iDictionary; + // ========== Methods ========== + // This is a bit janky due to Il2Cpp Dictionary not implementing IDictionary. private IDictionary Il2CppDictionaryToMono() { @@ -94,43 +96,36 @@ private IDictionary Il2CppDictionaryToMono() return dict; } - // ========== Methods ========== - private void GetGenericArguments() { - if (m_keysType == null || m_valuesType == null) + if (this.MemInfo != null) { - if (this.MemInfo != null) + Type memberType = null; + switch (this.MemInfo.MemberType) { - Type memberType = null; - switch (this.MemInfo.MemberType) - { - case MemberTypes.Field: - memberType = (MemInfo as FieldInfo).FieldType; - break; - case MemberTypes.Property: - memberType = (MemInfo as PropertyInfo).PropertyType; - break; - } + case MemberTypes.Field: + memberType = (MemInfo as FieldInfo).FieldType; + break; + case MemberTypes.Property: + memberType = (MemInfo as PropertyInfo).PropertyType; + break; + } - if (memberType != null && memberType.IsGenericType) - { - m_keysType = memberType.GetGenericArguments()[0]; - m_valuesType = memberType.GetGenericArguments()[1]; - } + if (memberType != null && memberType.IsGenericType) + { + m_keysType = memberType.GetGenericArguments()[0]; + m_valuesType = memberType.GetGenericArguments()[1]; } - else if (Value != null) + } + else if (Value != null) + { + var type = Value.GetType(); + if (type.IsGenericType) { - var type = Value.GetType(); - if (type.IsGenericType) - { - m_keysType = type.GetGenericArguments()[0]; - m_valuesType = type.GetGenericArguments()[1]; - } + m_keysType = type.GetGenericArguments()[0]; + m_valuesType = type.GetGenericArguments()[1]; } } - - return; } public override void UpdateValue() @@ -258,15 +253,11 @@ public override void DrawValue(Rect window, float width) GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) }); - GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.MinWidth((window.width / 3) - 60f) }); GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) }); key.DrawValue(window, (window.width / 2) - 30f); - GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(null); GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) }); val.DrawValue(window, (window.width / 2) - 30f); - GUILayout.EndHorizontal(); } } diff --git a/src/CachedObjects/Object/CacheList.cs b/src/CachedObjects/Object/CacheList.cs index 10365161..d334573d 100644 --- a/src/CachedObjects/Object/CacheList.cs +++ b/src/CachedObjects/Object/CacheList.cs @@ -8,13 +8,13 @@ namespace Explorer { - public class CacheList : CacheObjectBase + public class CacheList : CacheObjectBase, IExpandHeight { public bool IsExpanded { get; set; } - public PageHelper Pages = new PageHelper(); + public float WhiteSpace { get; set; } = 215f; + public float ButtonWidthOffset { get; set; } = 290f; - public float WhiteSpace = 215f; - public float ButtonWidthOffset = 290f; + public PageHelper Pages = new PageHelper(); private CacheObjectBase[] m_cachedEntries; @@ -52,6 +52,7 @@ public PropertyInfo ItemProperty { get => GetItemProperty(); } + private PropertyInfo m_itemProperty; // ========== Methods ========== diff --git a/src/CachedObjects/Other/CacheMethod.cs b/src/CachedObjects/Other/CacheMethod.cs index 9dcf3012..4f70a337 100644 --- a/src/CachedObjects/Other/CacheMethod.cs +++ b/src/CachedObjects/Other/CacheMethod.cs @@ -18,28 +18,17 @@ public class CacheMethod : CacheObjectBase private ParameterInfo[] m_arguments; private string[] m_argumentInput; - public bool HasParameters - { - get - { - if (m_hasParams == null) - { - m_hasParams = (MemInfo as MethodInfo).GetParameters().Length > 0; - } - return (bool)m_hasParams; - } - } - private bool? m_hasParams; + public bool HasParameters => m_arguments != null && m_arguments.Length > 0; public static bool CanEvaluate(MethodInfo mi) { - // generic type args not supported yet + // TODO generic args if (mi.GetGenericArguments().Length > 0) { return false; } - // only primitive and string args supported + // primitive and string args supported foreach (var param in mi.GetParameters()) { if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string)) @@ -64,7 +53,84 @@ public override void Init() public override void UpdateValue() { //base.UpdateValue(); - } + } + + private void Evaluate() + { + var mi = MemInfo as MethodInfo; + + object ret = null; + + if (!HasParameters) + { + ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, new object[0]); + m_evaluated = true; + } + else + { + var parsedArgs = new List(); + for (int i = 0; i < m_arguments.Length; i++) + { + var input = m_argumentInput[i]; + var type = m_arguments[i].ParameterType; + + if (type == typeof(string)) + { + parsedArgs.Add(input); + } + else + { + try + { + if (type.GetMethod("Parse", new Type[] { typeof(string) }).Invoke(null, new object[] { input }) is object parsed) + { + parsedArgs.Add(parsed); + } + else + { + // try add a null arg i guess + parsedArgs.Add(null); + } + + } + catch + { + MelonLogger.Log($"Unable to parse '{input}' to type '{type.Name}'"); + break; + } + } + } + + try + { + ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, parsedArgs.ToArray()); + m_evaluated = true; + } + catch (Exception e) + { + MelonLogger.Log($"Exception evaluating: {e.GetType()}, {e.Message}"); + } + } + + if (ret != null) + { + m_cachedReturnValue = GetCacheObject(ret); + + if (m_cachedReturnValue is IExpandHeight expander) + { + expander.WhiteSpace = 0f; + expander.ButtonWidthOffset += 70f; + } + + m_cachedReturnValue.UpdateValue(); + } + else + { + m_cachedReturnValue = null; + } + } + + // ==== GUI DRAW ==== public override void DrawValue(Rect window, float width) { @@ -124,15 +190,7 @@ public override void DrawValue(Rect window, float width) { if (m_cachedReturnValue != null) { - try - { - m_cachedReturnValue.DrawValue(window, width); - } - catch (Exception e) - { - MelonLogger.Log("Exception drawing m_cachedReturnValue!"); - MelonLogger.Log(e.ToString()); - } + m_cachedReturnValue.DrawValue(window, width); } else { @@ -147,82 +205,5 @@ public override void DrawValue(Rect window, float width) GUILayout.EndVertical(); } - - private void Evaluate() - { - var mi = MemInfo as MethodInfo; - - object ret = null; - - if (!HasParameters) - { - ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, new object[0]); - m_evaluated = true; - } - else - { - var arguments = new List(); - for (int i = 0; i < m_arguments.Length; i++) - { - var input = m_argumentInput[i]; - var type = m_arguments[i].ParameterType; - - if (type == typeof(string)) - { - arguments.Add(input); - } - else - { - try - { - if (type.GetMethod("Parse", new Type[] { typeof(string) }).Invoke(null, new object[] { input }) is object parsed) - { - arguments.Add(parsed); - } - else - { - throw new Exception(); - } - - } - catch - { - MelonLogger.Log($"Unable to parse '{input}' to type '{type.Name}'"); - break; - } - } - } - - if (arguments.Count == m_arguments.Length) - { - ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, arguments.ToArray()); - m_evaluated = true; - } - else - { - MelonLogger.Log($"Did not invoke because {m_arguments.Length - arguments.Count} arguments could not be parsed!"); - } - } - - if (ret != null) - { - m_cachedReturnValue = GetCacheObject(ret); - if (m_cachedReturnValue is CacheList cacheList) - { - cacheList.WhiteSpace = 0f; - cacheList.ButtonWidthOffset += 70f; - } - else if (m_cachedReturnValue is CacheDictionary cacheDict) - { - cacheDict.WhiteSpace = 0f; - cacheDict.ButtonWidthOffset += 70f; - } - m_cachedReturnValue.UpdateValue(); - } - else - { - m_cachedReturnValue = null; - } - } } } diff --git a/src/CppExplorer.cs b/src/CppExplorer.cs index a965731e..be69ac9d 100644 --- a/src/CppExplorer.cs +++ b/src/CppExplorer.cs @@ -13,7 +13,7 @@ namespace Explorer public class CppExplorer : MelonMod { public const string GUID = "com.sinai.cppexplorer"; - public const string VERSION = "1.6.0"; + public const string VERSION = "1.6.1"; public const string AUTHOR = "Sinai"; public const string NAME = "CppExplorer" diff --git a/src/CppExplorer.csproj b/src/CppExplorer.csproj index f10917de..5c9711d2 100644 --- a/src/CppExplorer.csproj +++ b/src/CppExplorer.csproj @@ -120,6 +120,7 @@ + diff --git a/src/Helpers/IExpandHeight.cs b/src/Helpers/IExpandHeight.cs new file mode 100644 index 00000000..20b8e728 --- /dev/null +++ b/src/Helpers/IExpandHeight.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Explorer +{ + interface IExpandHeight + { + bool IsExpanded { get; set; } + + float WhiteSpace { get; set; } + float ButtonWidthOffset { get; set; } + } +} diff --git a/src/Helpers/ReflectionHelpers.cs b/src/Helpers/ReflectionHelpers.cs index b0b82770..e1126ac4 100644 --- a/src/Helpers/ReflectionHelpers.cs +++ b/src/Helpers/ReflectionHelpers.cs @@ -9,6 +9,8 @@ using UnityEngine; using BF = System.Reflection.BindingFlags; using MelonLoader; +using System.Collections; +using Mono.CSharp; namespace Explorer { @@ -76,31 +78,39 @@ public static bool IsExceptionOfType(Exception e, Type t, bool strict = true, bo public static bool IsEnumerable(Type t) { - return typeof(System.Collections.IEnumerable).IsAssignableFrom(t); + return typeof(IEnumerable).IsAssignableFrom(t); } // Only Il2Cpp List needs this check. C# List is IEnumerable. public static bool IsCppList(Type t) { - if (t.IsGenericType) + if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g) { - var generic = t.GetGenericTypeDefinition(); - - return generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>)) - || generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.IList<>)); + return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g) + || typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g); } else { - return t.IsAssignableFrom(typeof(Il2CppSystem.Collections.IList)); + return typeof(Il2CppSystem.Collections.IList).IsAssignableFrom(t); } } public static bool IsDictionary(Type t) { - return t.IsGenericType - && t.GetGenericTypeDefinition() is Type typeDef - && (typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.Dictionary<,>)) - || typeDef.IsAssignableFrom(typeof(Dictionary<,>))); + if (typeof(IDictionary).IsAssignableFrom(t)) + { + return true; + } + + 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); + } } public static Type GetTypeByName(string typeName) diff --git a/src/Windows/GameObjectWindow.cs b/src/Windows/GameObjectWindow.cs index 5ae22d46..ec7c2a60 100644 --- a/src/Windows/GameObjectWindow.cs +++ b/src/Windows/GameObjectWindow.cs @@ -48,12 +48,6 @@ public class GameObjectWindow : UIWindow public bool GetObjectAsGameObject() { - if (Target == null) - { - MelonLogger.Log("Target is null!"); - return false; - } - var targetType = Target.GetType(); if (targetType == typeof(GameObject)) @@ -108,6 +102,22 @@ public override void Update() { try { + if (Target == null) + { + MelonLogger.Log("Target is null!"); + DestroyWindow(); + return; + } + else if (Target is UnityEngine.Object uObj) + { + if (!uObj) + { + MelonLogger.Log("Target was destroyed!"); + DestroyWindow(); + return; + } + } + if (!m_object && !GetObjectAsGameObject()) { throw new Exception("Object is null!"); diff --git a/src/Windows/ReflectionWindow.cs b/src/Windows/ReflectionWindow.cs index 30efbfae..827dc6f7 100644 --- a/src/Windows/ReflectionWindow.cs +++ b/src/Windows/ReflectionWindow.cs @@ -22,8 +22,6 @@ public class ReflectionWindow : UIWindow private CacheObjectBase[] m_cachedMembersFiltered; public PageHelper Pages = new PageHelper(); - //private int m_pageOffset; - //private int m_limitPerPage = 20; private bool m_autoUpdate = false; private string m_search = ""; @@ -69,6 +67,20 @@ public override void Init() public override void Update() { + if (Target == null) + { + DestroyWindow(); + return; + } + else if (Target is UnityEngine.Object uObj) + { + if (!uObj) + { + DestroyWindow(); + return; + } + } + m_cachedMembersFiltered = m_allCachedMembers.Where(x => ShouldProcessMember(x)).ToArray(); if (m_autoUpdate)