* Fix for when inspected object gets destroyed
* Fix for displaying Dictionaries/Lists nested inside a Dictionary
* Cleanups
This commit is contained in:
sinaioutlander 2020-09-07 17:05:37 +10:00
parent 4e8b84b67e
commit 72d31eaa64
10 changed files with 186 additions and 165 deletions

View File

@ -16,15 +16,14 @@ namespace Explorer
public string ValueTypeName; public string ValueTypeName;
public Type ValueType; public Type ValueType;
// Reflection Inspector only
public MemberInfo MemInfo { get; set; } public MemberInfo MemInfo { get; set; }
public Type DeclaringType { get; set; } public Type DeclaringType { get; set; }
public object DeclaringInstance { get; set; } public object DeclaringInstance { get; set; }
public string ReflectionException { get; set; }
public int PropertyIndex { get; private set; } public int PropertyIndex { get; private set; }
private string m_propertyIndexInput = "0"; private string m_propertyIndexInput = "0";
public string ReflectionException { get; set; }
public string RichTextName => m_richTextName ?? GetRichTextName(); public string RichTextName => m_richTextName ?? GetRichTextName();
private string m_richTextName; private string m_richTextName;

View File

@ -10,13 +10,13 @@ using System.Reflection;
namespace Explorer namespace Explorer
{ {
public class CacheDictionary : CacheObjectBase public class CacheDictionary : CacheObjectBase, IExpandHeight
{ {
public bool IsExpanded { get; set; } 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 PageHelper Pages = new PageHelper();
public float ButtonWidthOffset = 290f;
private CacheObjectBase[] m_cachedKeys; private CacheObjectBase[] m_cachedKeys;
private CacheObjectBase[] m_cachedValues; private CacheObjectBase[] m_cachedValues;
@ -48,6 +48,8 @@ namespace Explorer
} }
private IDictionary m_iDictionary; private IDictionary m_iDictionary;
// ========== Methods ==========
// This is a bit janky due to Il2Cpp Dictionary not implementing IDictionary. // This is a bit janky due to Il2Cpp Dictionary not implementing IDictionary.
private IDictionary Il2CppDictionaryToMono() private IDictionary Il2CppDictionaryToMono()
{ {
@ -94,43 +96,36 @@ namespace Explorer
return dict; return dict;
} }
// ========== Methods ==========
private void GetGenericArguments() 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; case MemberTypes.Field:
switch (this.MemInfo.MemberType) memberType = (MemInfo as FieldInfo).FieldType;
{ break;
case MemberTypes.Field: case MemberTypes.Property:
memberType = (MemInfo as FieldInfo).FieldType; memberType = (MemInfo as PropertyInfo).PropertyType;
break; 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];
}
} }
else if (Value != null)
if (memberType != null && memberType.IsGenericType)
{ {
var type = Value.GetType(); m_keysType = memberType.GetGenericArguments()[0];
if (type.IsGenericType) m_valuesType = memberType.GetGenericArguments()[1];
{ }
m_keysType = type.GetGenericArguments()[0]; }
m_valuesType = type.GetGenericArguments()[1]; else if (Value != null)
} {
var type = Value.GetType();
if (type.IsGenericType)
{
m_keysType = type.GetGenericArguments()[0];
m_valuesType = type.GetGenericArguments()[1];
} }
} }
return;
} }
public override void UpdateValue() public override void UpdateValue()
@ -258,15 +253,11 @@ namespace Explorer
GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) }); 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) }); GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) });
key.DrawValue(window, (window.width / 2) - 30f); key.DrawValue(window, (window.width / 2) - 30f);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) }); GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) });
val.DrawValue(window, (window.width / 2) - 30f); val.DrawValue(window, (window.width / 2) - 30f);
GUILayout.EndHorizontal();
} }
} }

View File

@ -8,13 +8,13 @@ using UnityEngine;
namespace Explorer namespace Explorer
{ {
public class CacheList : CacheObjectBase public class CacheList : CacheObjectBase, IExpandHeight
{ {
public bool IsExpanded { get; set; } 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 PageHelper Pages = new PageHelper();
public float ButtonWidthOffset = 290f;
private CacheObjectBase[] m_cachedEntries; private CacheObjectBase[] m_cachedEntries;
@ -52,6 +52,7 @@ namespace Explorer
{ {
get => GetItemProperty(); get => GetItemProperty();
} }
private PropertyInfo m_itemProperty; private PropertyInfo m_itemProperty;
// ========== Methods ========== // ========== Methods ==========

View File

@ -18,28 +18,17 @@ namespace Explorer
private ParameterInfo[] m_arguments; private ParameterInfo[] m_arguments;
private string[] m_argumentInput; private string[] m_argumentInput;
public bool HasParameters public bool HasParameters => m_arguments != null && m_arguments.Length > 0;
{
get
{
if (m_hasParams == null)
{
m_hasParams = (MemInfo as MethodInfo).GetParameters().Length > 0;
}
return (bool)m_hasParams;
}
}
private bool? m_hasParams;
public static bool CanEvaluate(MethodInfo mi) public static bool CanEvaluate(MethodInfo mi)
{ {
// generic type args not supported yet // TODO generic args
if (mi.GetGenericArguments().Length > 0) if (mi.GetGenericArguments().Length > 0)
{ {
return false; return false;
} }
// only primitive and string args supported // primitive and string args supported
foreach (var param in mi.GetParameters()) foreach (var param in mi.GetParameters())
{ {
if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string)) if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string))
@ -64,7 +53,84 @@ namespace Explorer
public override void UpdateValue() public override void UpdateValue()
{ {
//base.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<object>();
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) public override void DrawValue(Rect window, float width)
{ {
@ -124,15 +190,7 @@ namespace Explorer
{ {
if (m_cachedReturnValue != null) if (m_cachedReturnValue != null)
{ {
try m_cachedReturnValue.DrawValue(window, width);
{
m_cachedReturnValue.DrawValue(window, width);
}
catch (Exception e)
{
MelonLogger.Log("Exception drawing m_cachedReturnValue!");
MelonLogger.Log(e.ToString());
}
} }
else else
{ {
@ -147,82 +205,5 @@ namespace Explorer
GUILayout.EndVertical(); 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<object>();
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;
}
}
} }
} }

View File

@ -13,7 +13,7 @@ namespace Explorer
public class CppExplorer : MelonMod public class CppExplorer : MelonMod
{ {
public const string GUID = "com.sinai.cppexplorer"; 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 AUTHOR = "Sinai";
public const string NAME = "CppExplorer" public const string NAME = "CppExplorer"

View File

@ -120,6 +120,7 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Helpers\IExpandHeight.cs" />
<Compile Include="CachedObjects\Struct\CacheColor.cs" /> <Compile Include="CachedObjects\Struct\CacheColor.cs" />
<Compile Include="CachedObjects\Object\CacheDictionary.cs" /> <Compile Include="CachedObjects\Object\CacheDictionary.cs" />
<Compile Include="CachedObjects\Struct\CacheEnum.cs" /> <Compile Include="CachedObjects\Struct\CacheEnum.cs" />

View File

@ -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; }
}
}

View File

@ -9,6 +9,8 @@ using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using BF = System.Reflection.BindingFlags; using BF = System.Reflection.BindingFlags;
using MelonLoader; using MelonLoader;
using System.Collections;
using Mono.CSharp;
namespace Explorer namespace Explorer
{ {
@ -76,31 +78,39 @@ namespace Explorer
public static bool IsEnumerable(Type t) 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. // Only Il2Cpp List needs this check. C# List is IEnumerable.
public static bool IsCppList(Type t) public static bool IsCppList(Type t)
{ {
if (t.IsGenericType) if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
{ {
var generic = t.GetGenericTypeDefinition(); return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g)
|| typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g);
return generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))
|| generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.IList<>));
} }
else else
{ {
return t.IsAssignableFrom(typeof(Il2CppSystem.Collections.IList)); return typeof(Il2CppSystem.Collections.IList).IsAssignableFrom(t);
} }
} }
public static bool IsDictionary(Type t) public static bool IsDictionary(Type t)
{ {
return t.IsGenericType if (typeof(IDictionary).IsAssignableFrom(t))
&& t.GetGenericTypeDefinition() is Type typeDef {
&& (typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.Dictionary<,>)) return true;
|| typeDef.IsAssignableFrom(typeof(Dictionary<,>))); }
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) public static Type GetTypeByName(string typeName)

View File

@ -48,12 +48,6 @@ namespace Explorer
public bool GetObjectAsGameObject() public bool GetObjectAsGameObject()
{ {
if (Target == null)
{
MelonLogger.Log("Target is null!");
return false;
}
var targetType = Target.GetType(); var targetType = Target.GetType();
if (targetType == typeof(GameObject)) if (targetType == typeof(GameObject))
@ -108,6 +102,22 @@ namespace Explorer
{ {
try 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()) if (!m_object && !GetObjectAsGameObject())
{ {
throw new Exception("Object is null!"); throw new Exception("Object is null!");

View File

@ -22,8 +22,6 @@ namespace Explorer
private CacheObjectBase[] m_cachedMembersFiltered; private CacheObjectBase[] m_cachedMembersFiltered;
public PageHelper Pages = new PageHelper(); public PageHelper Pages = new PageHelper();
//private int m_pageOffset;
//private int m_limitPerPage = 20;
private bool m_autoUpdate = false; private bool m_autoUpdate = false;
private string m_search = ""; private string m_search = "";
@ -69,6 +67,20 @@ namespace Explorer
public override void Update() 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(); m_cachedMembersFiltered = m_allCachedMembers.Where(x => ShouldProcessMember(x)).ToArray();
if (m_autoUpdate) if (m_autoUpdate)