Compare commits

..

12 Commits
1.7.3 ... 1.7.4

Author SHA1 Message Date
a1198f3a92 Update CacheColor.cs 2020-09-21 05:59:01 +10:00
04248a89ce Fix for cases when structs return null (due to null declaring instance) 2020-09-20 20:26:05 +10:00
3639824df3 Cleanup 2020-09-19 01:55:27 +10:00
939861b5f0 Cleanup 2020-09-19 01:44:38 +10:00
ad5fc04a3b Fix methods with multiple generic constraints 2020-09-19 01:27:33 +10:00
c39e097378 Add support for methods with ref/in/out args 2020-09-19 00:14:04 +10:00
129a7e3765 Improved interaction with generic methods and some minor UI fixes 2020-09-18 23:10:46 +10:00
643bb4519c Remove and sort usings 2020-09-18 18:38:11 +10:00
b154cbf39d Add support for generic methods, improved non-generic dictionary output 2020-09-18 18:03:17 +10:00
db91968519 Cleanup and improve syntax highlighting
* Static class members are now displayed in Italics and in a darker color, making them easier to distinguish.
* Cleaned up some issues related to syntax highlighting and refactored it into a global class.
* Methods and properties no longer display their arguments as part of the member name, they are only displayed when "Evaluate" is pressed.
2020-09-16 20:03:57 +10:00
5d58993b07 1.7.31
* Added support for Il2Cpp Hashtable (non-generic Dict)
* Dictionaries should now display CacheOther values better (smaller buttons)
* Cleaned up and improved some of CacheDictionary performance
2020-09-15 17:38:10 +10:00
eea581f8d5 Update ResizeDrag.cs 2020-09-14 20:27:49 +10:00
43 changed files with 469 additions and 385 deletions

View File

@ -99,9 +99,9 @@ CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Re
* Filter by name, type, etc. * Filter by name, type, etc.
* For GameObjects and Transforms you can filter which scene they are found in too. * For GameObjects and Transforms you can filter which scene they are found in too.
### C# REPL console ### C# console
* A simple C# REPL console, allows you to execute a method body on the fly. * A simple C# console, allows you to execute a method body on the fly.
### Inspect-under-mouse ### Inspect-under-mouse

View File

@ -2,10 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using MelonLoader; using MelonLoader;
using UnhollowerBaseLib;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer
@ -13,14 +10,14 @@ namespace Explorer
public abstract class CacheObjectBase public abstract class CacheObjectBase
{ {
public object Value; public object Value;
public string ValueTypeName;
public Type ValueType; public Type ValueType;
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 bool HasParameters => m_arguments != null && m_arguments.Length > 0; public virtual bool HasParameters => m_arguments != null && m_arguments.Length > 0;
public bool m_evaluated = false; public bool m_evaluated = false;
public bool m_isEvaluating; public bool m_isEvaluating;
public ParameterInfo[] m_arguments = new ParameterInfo[0]; public ParameterInfo[] m_arguments = new ParameterInfo[0];
@ -117,8 +114,9 @@ namespace Explorer
var pi = memberInfo as PropertyInfo; var pi = memberInfo as PropertyInfo;
var mi = memberInfo as MethodInfo; var mi = memberInfo as MethodInfo;
// if PropertyInfo, check if can process args // Check if can process args
if (pi != null && !CanProcessArgs(pi.GetIndexParameters())) if ((pi != null && !CanProcessArgs(pi.GetIndexParameters()))
|| (mi != null && !CanProcessArgs(mi.GetParameters())))
{ {
return null; return null;
} }
@ -130,14 +128,7 @@ namespace Explorer
if (mi != null) if (mi != null)
{ {
if (CacheMethod.CanEvaluate(mi)) holder = new CacheMethod();
{
holder = new CacheMethod();
}
else
{
return null;
}
} }
else if (valueType == typeof(GameObject) || valueType == typeof(Transform)) else if (valueType == typeof(GameObject) || valueType == typeof(Transform))
{ {
@ -172,7 +163,7 @@ namespace Explorer
{ {
holder = new CacheDictionary(); holder = new CacheDictionary();
} }
else if (ReflectionHelpers.IsEnumerable(valueType) || ReflectionHelpers.IsCppEnumerable(valueType)) else if (ReflectionHelpers.IsEnumerable(valueType))
{ {
holder = new CacheList(); holder = new CacheList();
} }
@ -183,7 +174,6 @@ namespace Explorer
holder.Value = obj; holder.Value = obj;
holder.ValueType = valueType; holder.ValueType = valueType;
holder.ValueTypeName = valueType.FullName;
if (memberInfo != null) if (memberInfo != null)
{ {
@ -214,7 +204,18 @@ namespace Explorer
{ {
foreach (var param in parameters) foreach (var param in parameters)
{ {
if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string)) var pType = param.ParameterType;
if (pType.IsByRef && pType.HasElementType)
{
pType = pType.GetElementType();
}
if (pType.IsPrimitive || pType == typeof(string))
{
continue;
}
else
{ {
return false; return false;
} }
@ -244,6 +245,11 @@ namespace Explorer
var input = m_argumentInput[i]; var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType; var type = m_arguments[i].ParameterType;
if (type.IsByRef)
{
type = type.GetElementType();
}
if (!string.IsNullOrEmpty(input)) if (!string.IsNullOrEmpty(input))
{ {
// strings can obviously just be used directly // strings can obviously just be used directly
@ -308,14 +314,7 @@ namespace Explorer
var pi = MemInfo as PropertyInfo; var pi = MemInfo as PropertyInfo;
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance; var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
if (HasParameters) Value = pi.GetValue(target, ParseArguments());
{
Value = pi.GetValue(target, ParseArguments());
}
else
{
Value = pi.GetValue(target, null);
}
} }
ReflectionException = null; ReflectionException = null;
@ -394,25 +393,73 @@ namespace Explorer
if (m_isEvaluating) if (m_isEvaluating)
{ {
for (int i = 0; i < m_arguments.Length; i++) if (cm != null && cm.GenericArgs.Length > 0)
{ {
var name = m_arguments[i].Name; GUILayout.Label($"<b><color=orange>Generic Arguments:</color></b>", null);
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType.Name;
var label = "<color=#2df7b2>" + type + "</color> <color=#a6e9e9>" + name + "</color>"; for (int i = 0; i < cm.GenericArgs.Length; i++)
if (m_arguments[i].HasDefaultValue)
{ {
label = $"<i>[{label} = {m_arguments[i].DefaultValue}]</i>"; string types = "";
if (cm.GenericConstraints[i].Length > 0)
{
foreach (var constraint in cm.GenericConstraints[i])
{
if (types != "") types += ", ";
string type;
if (constraint == null)
type = "Any";
else
type = constraint.ToString();
types += $"<color={UIStyles.Syntax.Class_Instance}>{type}</color>";
}
}
else
{
types = $"<color={UIStyles.Syntax.Class_Instance}>Any</color>";
}
var input = cm.GenericArgInput[i];
GUILayout.BeginHorizontal(null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label($"<color={UIStyles.Syntax.StructGreen}>{cm.GenericArgs[i].Name}</color>", new GUILayoutOption[] { GUILayout.Width(15) });
cm.GenericArgInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label(types, null);
GUILayout.EndHorizontal();
} }
}
GUILayout.BeginHorizontal(null); if (m_arguments.Length > 0)
{
GUILayout.Label($"<b><color=orange>Arguments:</color></b>", null);
for (int i = 0; i < m_arguments.Length; i++)
{
var name = m_arguments[i].Name;
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType.Name;
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(20) }); var label = $"<color={UIStyles.Syntax.Class_Instance}>{type}</color> ";
m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) }); label += $"<color={UIStyles.Syntax.Local}>{name}</color>";
GUILayout.Label(label, null); if (m_arguments[i].HasDefaultValue)
{
label = $"<i>[{label} = {m_arguments[i].DefaultValue ?? "null"}]</i>";
}
GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(15) });
m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label(label, null);
GUILayout.EndHorizontal();
}
} }
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
@ -435,7 +482,12 @@ namespace Explorer
} }
else else
{ {
if (GUILayout.Button($"Evaluate ({m_arguments.Length} params)", new GUILayoutOption[] { GUILayout.Width(150) })) var lbl = $"Evaluate (";
int len = m_arguments.Length;
if (cm != null) len += cm.GenericArgs.Length;
lbl += len + " params)";
if (GUILayout.Button(lbl, new GUILayoutOption[] { GUILayout.Width(150) }))
{ {
m_isEvaluating = true; m_isEvaluating = true;
} }
@ -463,17 +515,19 @@ namespace Explorer
GUIUnstrip.Space(labelWidth); GUIUnstrip.Space(labelWidth);
} }
string typeName = $"<color={UIStyles.Syntax.Class_Instance}>{ValueType.FullName}</color>";
if (!string.IsNullOrEmpty(ReflectionException)) if (!string.IsNullOrEmpty(ReflectionException))
{ {
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null); GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null);
} }
else if ((HasParameters || this is CacheMethod) && !m_evaluated) else if ((HasParameters || this is CacheMethod) && !m_evaluated)
{ {
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=#2df7b2>{ValueTypeName}</color>)", null); GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> ({typeName})", null);
} }
else if (Value == null && !(this is CacheMethod)) else if (Value == null && !(this is CacheMethod))
{ {
GUILayout.Label("<i>null (" + ValueTypeName + ")</i>", null); GUILayout.Label($"<i>null ({typeName})</i>", null);
} }
else else
{ {
@ -484,31 +538,80 @@ namespace Explorer
private string GetRichTextName() private string GetRichTextName()
{ {
string memberColor = ""; string memberColor = "";
switch (MemInfo.MemberType) bool isStatic = false;
{
case MemberTypes.Field:
memberColor = "#c266ff"; break;
case MemberTypes.Property:
memberColor = "#72a6a6"; break;
case MemberTypes.Method:
memberColor = "#ff8000"; break;
};
m_richTextName = $"<color=#2df7b2>{MemInfo.DeclaringType.Name}</color>.<color={memberColor}>{MemInfo.Name}</color>"; if (MemInfo is FieldInfo fi)
if (m_arguments.Length > 0 || this is CacheMethod)
{ {
m_richTextName += "("; if (fi.IsStatic)
var _params = "";
foreach (var param in m_arguments)
{ {
if (_params != "") _params += ", "; isStatic = true;
memberColor = UIStyles.Syntax.Field_Static;
_params += $"<color=#2df7b2>{param.ParameterType.Name}</color> <color=#a6e9e9>{param.Name}</color>";
} }
m_richTextName += _params; else
m_richTextName += ")"; memberColor = UIStyles.Syntax.Field_Instance;
} }
else if (MemInfo is MethodInfo mi)
{
if (mi.IsStatic)
{
isStatic = true;
memberColor = UIStyles.Syntax.Method_Static;
}
else
memberColor = UIStyles.Syntax.Method_Instance;
}
else if (MemInfo is PropertyInfo pi)
{
if (pi.GetAccessors()[0].IsStatic)
{
isStatic = true;
memberColor = UIStyles.Syntax.Prop_Static;
}
else
memberColor = UIStyles.Syntax.Prop_Instance;
}
string classColor = MemInfo.DeclaringType.IsAbstract && MemInfo.DeclaringType.IsSealed
? UIStyles.Syntax.Class_Static
: UIStyles.Syntax.Class_Instance;
m_richTextName = $"<color={classColor}>{MemInfo.DeclaringType.Name}</color>.";
if (isStatic) m_richTextName += "<i>";
m_richTextName += $"<color={memberColor}>{MemInfo.Name}</color>";
if (isStatic) m_richTextName += "</i>";
// generic method args
if (this is CacheMethod cm && cm.GenericArgs.Length > 0)
{
m_richTextName += "<";
var args = "";
for (int i = 0; i < cm.GenericArgs.Length; i++)
{
if (args != "") args += ", ";
args += $"<color={UIStyles.Syntax.StructGreen}>{cm.GenericArgs[i].Name}</color>";
}
m_richTextName += args;
m_richTextName += ">";
}
// Method / Property arguments
//if (m_arguments.Length > 0 || this is CacheMethod)
//{
// m_richTextName += "(";
// var args = "";
// foreach (var param in m_arguments)
// {
// if (args != "") args += ", ";
// args += $"<color={classColor}>{param.ParameterType.Name}</color> ";
// args += $"<color={UIStyles.Syntax.Local}>{param.Name}</color>";
// }
// m_richTextName += args;
// m_richTextName += ")";
//}
return m_richTextName; return m_richTextName;
} }

View File

@ -1,10 +1,4 @@
using System; namespace Explorer
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Explorer
{ {
interface IExpandHeight interface IExpandHeight
{ {

View File

@ -1,13 +1,8 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine;
using System.Reflection;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -98,37 +93,16 @@ namespace Explorer
private void GetGenericArguments() private void GetGenericArguments()
{ {
if (this.MemInfo != null) if (ValueType.IsGenericType)
{ {
Type memberType = null; m_keysType = ValueType.GetGenericArguments()[0];
switch (this.MemInfo.MemberType) m_valuesType = ValueType.GetGenericArguments()[1];
{
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];
}
} }
else if (Value != null) else
{ {
var type = Value.GetType(); // It's non-generic, just use System.Object to allow for anything.
if (type.IsGenericType) m_keysType = typeof(object);
{ m_valuesType = typeof(object);
m_keysType = type.GetGenericArguments()[0];
m_valuesType = type.GetGenericArguments()[1];
}
else
{
MelonLogger.Log("TODO? Dictionary is of type: " + Value.GetType().FullName);
}
} }
} }
@ -154,14 +128,16 @@ namespace Explorer
var keys = new List<CacheObjectBase>(); var keys = new List<CacheObjectBase>();
foreach (var key in IDict.Keys) foreach (var key in IDict.Keys)
{ {
var cache = GetCacheObject(key, TypeOfKeys); Type t = ReflectionHelpers.GetActualType(key) ?? TypeOfKeys;
var cache = GetCacheObject(key, t);
keys.Add(cache); keys.Add(cache);
} }
var values = new List<CacheObjectBase>(); var values = new List<CacheObjectBase>();
foreach (var val in IDict.Values) foreach (var val in IDict.Values)
{ {
var cache = GetCacheObject(val, TypeOfValues); Type t = ReflectionHelpers.GetActualType(val) ?? TypeOfValues;
var cache = GetCacheObject(val, t);
values.Add(cache); values.Add(cache);
} }
@ -288,10 +264,10 @@ namespace Explorer
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
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) - 80f);
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) - 80f);
} }
} }

View File

@ -1,10 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using MelonLoader; using MelonLoader;
using UnityEngine; using UnityEngine;

View File

@ -1,11 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
using UnityEngine;
using MelonLoader; using MelonLoader;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -13,16 +11,24 @@ namespace Explorer
{ {
private CacheObjectBase m_cachedReturnValue; private CacheObjectBase m_cachedReturnValue;
public static bool CanEvaluate(MethodInfo mi) public override bool HasParameters => base.HasParameters || GenericArgs.Length > 0;
{
// TODO generic args
if (mi.GetGenericArguments().Length > 0)
{
return false;
}
// primitive and string args supported public Type[] GenericArgs { get; private set; }
return CanProcessArgs(mi.GetParameters()); public Type[][] GenericConstraints { get; private set; }
public string[] GenericArgInput = new string[0];
public override void Init()
{
var mi = (MemInfo as MethodInfo);
GenericArgs = mi.GetGenericArguments();
GenericConstraints = GenericArgs.Select(x => x.GetGenericParameterConstraints())
.ToArray();
GenericArgInput = new string[GenericArgs.Length];
ValueType = mi.ReturnType;
} }
public override void UpdateValue() public override void UpdateValue()
@ -32,27 +38,29 @@ namespace Explorer
public void Evaluate() public void Evaluate()
{ {
m_isEvaluating = false; MethodInfo mi;
if (GenericArgs.Length > 0)
var mi = MemInfo as MethodInfo;
object ret = null;
if (!HasParameters)
{ {
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, new object[0]); mi = MakeGenericMethodFromInput();
m_evaluated = true; if (mi == null) return;
} }
else else
{ {
try mi = MemInfo as MethodInfo;
{ }
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, ParseArguments());
m_evaluated = true; object ret = null;
}
catch (Exception e) try
{ {
MelonLogger.Log($"Exception evaluating: {e.GetType()}, {e.Message}"); ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, ParseArguments());
} m_evaluated = true;
m_isEvaluating = false;
}
catch (Exception e)
{
MelonLogger.LogWarning($"Exception evaluating: {e.GetType()}, {e.Message}");
ReflectionException = ReflectionHelpers.ExceptionToString(e);
} }
if (ret != null) if (ret != null)
@ -66,10 +74,54 @@ namespace Explorer
} }
} }
private MethodInfo MakeGenericMethodFromInput()
{
var mi = MemInfo as MethodInfo;
var list = new List<Type>();
for (int i = 0; i < GenericArgs.Length; i++)
{
var input = GenericArgInput[i];
if (ReflectionHelpers.GetTypeByName(input) is Type t)
{
if (GenericConstraints[i].Length == 0)
{
list.Add(t);
}
else
{
foreach (var constraint in GenericConstraints[i].Where(x => x != null))
{
if (!constraint.IsAssignableFrom(t))
{
MelonLogger.LogWarning($"Generic argument #{i}, '{input}' is not assignable from the constraint '{constraint}'!");
return null;
}
}
list.Add(t);
}
}
else
{
MelonLogger.LogWarning($"Generic argument #{i}, could not get any type by the name of '{input}'!" +
$" Make sure you use the full name, including the NameSpace.");
return null;
}
}
// make into a generic with type list
mi = mi.MakeGenericMethod(list.ToArray());
return mi;
}
// ==== GUI DRAW ==== // ==== GUI DRAW ====
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
string typeLabel = $"<color={UIStyles.Syntax.Class_Instance}>{ValueType.FullName}</color>";
if (m_evaluated) if (m_evaluated)
{ {
if (m_cachedReturnValue != null) if (m_cachedReturnValue != null)
@ -78,12 +130,12 @@ namespace Explorer
} }
else else
{ {
GUILayout.Label($"null (<color=#2df7b2>{ValueTypeName}</color>)", null); GUILayout.Label($"null ({typeLabel})", null);
} }
} }
else else
{ {
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=#2df7b2>{ValueTypeName}</color>)", null); GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> ({typeLabel})", null);
} }
} }
} }

View File

@ -1,9 +1,4 @@
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
@ -11,55 +6,75 @@ namespace Explorer
{ {
public class CacheOther : CacheObjectBase public class CacheOther : CacheObjectBase
{ {
public string ButtonLabel => m_btnLabel ?? GetButtonLabel();
private string m_btnLabel;
public MethodInfo ToStringMethod => m_toStringMethod ?? GetToStringMethod();
private MethodInfo m_toStringMethod; private MethodInfo m_toStringMethod;
public MethodInfo ToStringMethod public override void UpdateValue()
{ {
get base.UpdateValue();
{
if (m_toStringMethod == null)
{
try
{
m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0])
?? typeof(object).GetMethod("ToString", new Type[0]);
// test invoke GetButtonLabel();
m_toStringMethod.Invoke(Value, null);
}
catch
{
m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]);
}
}
return m_toStringMethod;
}
} }
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
if (!label.Contains(ValueTypeName))
{
label += $" (<color=#2df7b2>{ValueTypeName}</color>)";
}
else
{
label = label.Replace(ValueTypeName, $"<color=#2df7b2>{ValueTypeName}</color>");
}
if (Value is UnityEngine.Object unityObj && !label.Contains(unityObj.name))
{
label = unityObj.name + " | " + label;
}
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(width - 15) })) if (GUILayout.Button(ButtonLabel, new GUILayoutOption[] { GUILayout.Width(width - 15) }))
{ {
WindowManager.InspectObject(Value, out bool _); WindowManager.InspectObject(Value, out bool _);
} }
GUI.skin.button.alignment = TextAnchor.MiddleCenter; GUI.skin.button.alignment = TextAnchor.MiddleCenter;
} }
private MethodInfo GetToStringMethod()
{
try
{
m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0])
?? typeof(object).GetMethod("ToString", new Type[0]);
// test invoke
m_toStringMethod.Invoke(Value, null);
}
catch
{
m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]);
}
return m_toStringMethod;
}
private string GetButtonLabel()
{
if (Value == null) return null;
string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
var classColor = ValueType.IsAbstract && ValueType.IsSealed
? UIStyles.Syntax.Class_Static
: UIStyles.Syntax.Class_Instance;
string typeLabel = $"<color={classColor}>{ValueType.FullName}</color>";
if (Value is UnityEngine.Object)
{
label = label.Replace($"({ValueType.FullName})", $"({typeLabel})");
}
else
{
if (!label.Contains(ValueType.FullName))
{
label += $" ({typeLabel})";
}
else
{
label = label.Replace(ValueType.FullName, typeLabel);
}
}
return m_btnLabel = label;
}
} }
} }

View File

@ -21,6 +21,8 @@ namespace Explorer
{ {
base.UpdateValue(); base.UpdateValue();
if (Value == null) return;
var color = (Color)Value; var color = (Color)Value;
r = color.r.ToString(); r = color.r.ToString();
@ -104,7 +106,7 @@ namespace Explorer
&& float.TryParse(b, out float fB) && float.TryParse(b, out float fB)
&& float.TryParse(a, out float fA)) && float.TryParse(a, out float fA))
{ {
Value = new Color(fR, fB, fG, fA); Value = new Color(fR, fG, fB, fA);
SetValue(); SetValue();
} }
} }

View File

@ -11,23 +11,19 @@ namespace Explorer
{ {
public class CacheEnum : CacheObjectBase public class CacheEnum : CacheObjectBase
{ {
public Type EnumType; // public Type EnumType;
public string[] EnumNames; public string[] EnumNames;
public override void Init() public override void Init()
{ {
try if (ValueType == null && Value != null)
{ {
EnumType = Value.GetType(); ValueType = Value.GetType();
}
catch
{
EnumType = (MemInfo as FieldInfo)?.FieldType ?? (MemInfo as PropertyInfo).PropertyType;
} }
if (EnumType != null) if (ValueType != null)
{ {
EnumNames = Enum.GetNames(EnumType); EnumNames = Enum.GetNames(ValueType);
} }
else else
{ {
@ -62,7 +58,7 @@ namespace Explorer
if ((change < 0 && newindex >= 0) || (change > 0 && newindex < names.Count)) if ((change < 0 && newindex >= 0) || (change > 0 && newindex < names.Count))
{ {
value = Enum.Parse(EnumType, names[newindex]); value = Enum.Parse(ValueType, names[newindex]);
} }
} }
} }

View File

@ -20,6 +20,8 @@ namespace Explorer
{ {
base.UpdateValue(); base.UpdateValue();
if (Value == null) return;
var euler = ((Quaternion)Value).eulerAngles; var euler = ((Quaternion)Value).eulerAngles;
x = euler.x.ToString(); x = euler.x.ToString();

View File

@ -21,6 +21,8 @@ namespace Explorer
{ {
base.UpdateValue(); base.UpdateValue();
if (Value == null) return;
var rect = (Rect)Value; var rect = (Rect)Value;
x = rect.x.ToString(); x = rect.x.ToString();

View File

@ -24,20 +24,26 @@ namespace Explorer
public override void Init() public override void Init()
{ {
if (Value is Vector2) if (ValueType == null && Value != null)
{
ValueType = Value.GetType();
}
if (ValueType == typeof(Vector2))
{ {
VectorSize = 2; VectorSize = 2;
m_toStringMethod = typeof(Vector2).GetMethod("ToString", new Type[0]);
} }
else if (Value is Vector3) else if (ValueType == typeof(Vector3))
{ {
VectorSize = 3; VectorSize = 3;
m_toStringMethod = typeof(Vector3).GetMethod("ToString", new Type[0]);
} }
else else
{ {
VectorSize = 4; VectorSize = 4;
m_toStringMethod = typeof(Vector4).GetMethod("ToString", new Type[0]);
} }
m_toStringMethod = Value.GetType().GetMethod("ToString", new Type[0]);
} }
public override void UpdateValue() public override void UpdateValue()

View File

@ -1,9 +1,4 @@
using System; using System.IO;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
using UnityEngine; using UnityEngine;

View File

@ -1,11 +1,4 @@
using System; using MelonLoader;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Harmony;
using MelonLoader;
using UnhollowerBaseLib;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer
@ -13,7 +6,7 @@ namespace Explorer
public class CppExplorer : MelonMod public class CppExplorer : MelonMod
{ {
public const string NAME = "CppExplorer"; public const string NAME = "CppExplorer";
public const string VERSION = "1.7.3"; public const string VERSION = "1.7.4";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.cppexplorer"; public const string GUID = "com.sinai.cppexplorer";

View File

@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
namespace Explorer namespace Explorer

View File

@ -1,10 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnhollowerBaseLib;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,12 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
using UnityEngine;
using MelonLoader; using MelonLoader;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,9 +1,8 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.IO; using System.IO;
using System.Reflection;
using MelonLoader; using MelonLoader;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
@ -36,7 +35,7 @@ namespace Explorer
public static bool IsEnumerable(Type t) public static bool IsEnumerable(Type t)
{ {
return typeof(IEnumerable).IsAssignableFrom(t); return typeof(IEnumerable).IsAssignableFrom(t) || IsCppEnumerable(t);
} }
// Checks for Il2Cpp List or HashSet. // Checks for Il2Cpp List or HashSet.
@ -68,7 +67,8 @@ namespace Explorer
} }
else else
{ {
return typeof(Il2CppSystem.Collections.IDictionary).IsAssignableFrom(t); return typeof(Il2CppSystem.Collections.IDictionary).IsAssignableFrom(t)
|| typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(t);
} }
} }

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,12 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Harmony; using Harmony;
using MelonLoader; using MelonLoader;
using System.Reflection; using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,9 +1,4 @@
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Mono.CSharp; using Mono.CSharp;
using UnityEngine; using UnityEngine;
using Attribute = System.Attribute; using Attribute = System.Attribute;

View File

@ -1,8 +1,5 @@
using System.Collections; using System;
//using Il2CppSystem;
using MelonLoader;
using UnityEngine; using UnityEngine;
using System;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
namespace Explorer namespace Explorer

View File

@ -1,14 +1,11 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using System.Reflection;
using Mono.CSharp;
using System.IO; using System.IO;
using System.Reflection;
using System.Text;
using MelonLoader; using MelonLoader;
using Mono.CSharp;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -43,7 +40,7 @@ namespace Explorer
try try
{ {
MethodInput = @"// This is a basic C# REPL console. MethodInput = @"// This is a basic C# console.
// Some common using directives are added by default, you can add more below. // Some common using directives are added by default, you can add more below.
// If you want to return some output, MelonLogger.Log() it. // If you want to return some output, MelonLogger.Log() it.
@ -123,7 +120,7 @@ MelonLogger.Log(""hello world"");";
public override void DrawWindow() public override void DrawWindow()
{ {
GUILayout.Label("<b><size=15><color=cyan>C# REPL Console</color></size></b>", null); GUILayout.Label("<b><size=15><color=cyan>C# Console</color></size></b>", null);
GUI.skin.label.alignment = TextAnchor.UpperLeft; GUI.skin.label.alignment = TextAnchor.UpperLeft;

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using MelonLoader; using MelonLoader;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;

View File

@ -2,10 +2,9 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using UnityEngine;
using System.Reflection; using System.Reflection;
using MelonLoader; using MelonLoader;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -428,7 +427,7 @@ namespace Explorer
{ {
var t = ReflectionHelpers.GetActualType(obj); var t = ReflectionHelpers.GetActualType(obj);
if (!FilterName(t.FullName) || ReflectionHelpers.IsEnumerable(t) || ReflectionHelpers.IsCppEnumerable(t)) if (!FilterName(t.FullName) || ReflectionHelpers.IsEnumerable(t))
{ {
continue; continue;
} }

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,11 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using MelonLoader; using MelonLoader;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -58,7 +54,11 @@ namespace Explorer
_rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y _rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y
} }
} }
catch { } catch
{
// throw safe Managed exception
throw new Exception("");
}
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }

View File

@ -1,10 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;

View File

@ -1,16 +1,29 @@
using System; using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
namespace Explorer namespace Explorer
{ {
public class UIStyles public class UIStyles
{ {
public class Syntax
{
public const string Field_Static = "#8d8dc6";
public const string Field_Instance = "#c266ff";
public const string Method_Static = "#b55b02";
public const string Method_Instance = "#ff8000";
public const string Prop_Static = "#588075";
public const string Prop_Instance = "#55a38e";
public const string Class_Static = "#3a8d71";
public const string Class_Instance = "#2df7b2";
public const string Local = "#a6e9e9";
public const string StructGreen = "#b8d7a3";
}
public static Color LightGreen = new Color(Color.green.r - 0.3f, Color.green.g - 0.3f, Color.green.b - 0.3f); public static Color LightGreen = new Color(Color.green.r - 0.3f, Color.green.g - 0.3f, Color.green.b - 0.3f);
public static GUISkin WindowSkin public static GUISkin WindowSkin

View File

@ -1,11 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using MelonLoader; using MelonLoader;
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement;
namespace Explorer namespace Explorer
{ {
@ -363,7 +360,8 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
m_addComponentInput = GUILayout.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(130) }); var width = m_rect.width / 2 - 115f;
m_addComponentInput = GUILayout.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(width) });
if (GUILayout.Button("Add Comp", null)) if (GUILayout.Button("Add Comp", null))
{ {
if (ReflectionHelpers.GetTypeByName(m_addComponentInput) is Type compType) if (ReflectionHelpers.GetTypeByName(m_addComponentInput) is Type compType)

View File

@ -1,6 +1,4 @@
using System; using System;
using System.CodeDom;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -163,15 +161,13 @@ namespace Explorer
continue; continue;
// check blacklisted members // check blacklisted members
var name = member.DeclaringType.Name + "." + member.Name; var sig = $"{member.DeclaringType.Name}.{member.Name}";
if (_typeAndMemberBlacklist.Any(it => it == name)) if (_typeAndMemberBlacklist.Any(it => it == sig))
continue; continue;
if (_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it))) if (_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it)))
continue; continue;
// compare signature to already cached members
var signature = $"{member.DeclaringType.Name}.{member.Name}";
if (member is MethodInfo mi) if (member is MethodInfo mi)
{ {
AppendParams(mi.GetParameters()); AppendParams(mi.GetParameters());
@ -183,15 +179,15 @@ namespace Explorer
void AppendParams(ParameterInfo[] _args) void AppendParams(ParameterInfo[] _args)
{ {
signature += " ("; sig += " (";
foreach (var param in _args) foreach (var param in _args)
{ {
signature += $"{param.ParameterType.Name} {param.Name}, "; sig += $"{param.ParameterType.Name} {param.Name}, ";
} }
signature += ")"; sig += ")";
} }
if (cachedSigs.Contains(signature)) if (cachedSigs.Contains(sig))
{ {
continue; continue;
} }
@ -203,14 +199,14 @@ namespace Explorer
var cached = CacheObjectBase.GetCacheObject(member, target); var cached = CacheObjectBase.GetCacheObject(member, target);
if (cached != null) if (cached != null)
{ {
cachedSigs.Add(signature); cachedSigs.Add(sig);
list.Add(cached); list.Add(cached);
cached.ReflectionException = exception; cached.ReflectionException = exception;
} }
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.LogWarning($"Exception caching member {signature}!"); MelonLogger.LogWarning($"Exception caching member {sig}!");
MelonLogger.Log(e.ToString()); MelonLogger.Log(e.ToString());
} }
} }

View File

@ -1,10 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,11 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Harmony;
using MelonLoader;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer

View File

@ -1,13 +1,5 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Harmony;
using MelonLoader;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using UnityEngine.Events;
namespace Explorer namespace Explorer
{ {

View File

@ -1,12 +1,16 @@
using System; using System.Collections;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine; using UnityEngine;
// used to test multiple generic constraints
public class TestGeneric : IComparable<string>
{
public TestGeneric() { }
public int CompareTo(string other) => throw new NotImplementedException();
}
namespace Explorer.Tests namespace Explorer.Tests
{ {
public class TestClass public class TestClass
@ -22,6 +26,53 @@ namespace Explorer.Tests
ILHashSetTest.Add("3"); ILHashSetTest.Add("3");
} }
public static int StaticProperty => 5;
public static int StaticField = 5;
public int NonStaticField;
public static string TestGeneric<C, T>(string arg0) where C : Component where T : TestGeneric, IComparable<string>
{
return $"C: '{typeof(C).FullName}', T: '{typeof(T).FullName}', arg0: '{arg0}'";
}
public static string TestRefInOutGeneric<T>(ref string arg0, in int arg1, out string arg2)
{
arg2 = "this is arg2";
return $"T: '{typeof(T).FullName}', ref arg0: '{arg0}', in arg1: '{arg1}', out arg2: '{arg2}'";
}
//// this type of generic is not supported, due to requiring a non-primitive argument.
//public static T TestDifferentGeneric<T>(T obj) where T : Component
//{
// return obj;
//}
// test a non-generic dictionary
public Hashtable TestNonGenericDict()
{
return new Hashtable
{
{ "One", 1 },
{ "Two", 2 },
{ "Three", 3 },
};
}
// IL2CPP HASHTABLE NOT SUPPORTED! Cannot assign Il2CppSystem.Object from primitive struct / string.
// Technically they are "supported" but if they contain System types they will not work.
//public Il2CppSystem.Collections.Hashtable TestIl2CppNonGenericDict()
//{
// var table = new Il2CppSystem.Collections.Hashtable();
// table.Add("One", 1);
// table.Add("One", 2);
// table.Add("One", 3);
// return table;
//}
// test HashSets // test HashSets
public static HashSet<string> HashSetTest = new HashSet<string> public static HashSet<string> HashSetTest = new HashSet<string>

View File

@ -1,15 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using MelonLoader;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using System.Reflection; using System.Reflection;
using MelonLoader;
using UnhollowerRuntimeLib;
using UnityEngine;
using UnityEngineInternal; using UnityEngineInternal;
using Harmony;
namespace Explorer namespace Explorer
{ {

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,10 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Harmony;
using MelonLoader;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer

View File

@ -1,14 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
public struct SliderHandlerUnstrip public struct SliderHandlerUnstrip
{ {
private readonly Rect position; private readonly Rect position;
private readonly float currentValue; private readonly float currentValue;

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {