mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-16 22:27:45 +08:00
1.4.1
* Cleanup some small bugs introduced in 1.4.0 * Added better exception handling for failed Reflection, and the ability to hide failed reflection members in the Reflection window, as well as see the error type. * Reflection window members now display the full name instead of just the member name (eg. "Camera.main" instead of just "main").
This commit is contained in:
parent
62b1688d53
commit
6bafab785b
@ -14,9 +14,12 @@ namespace Explorer
|
|||||||
private readonly string[] m_names;
|
private readonly string[] m_names;
|
||||||
|
|
||||||
public CacheEnum(object obj)
|
public CacheEnum(object obj)
|
||||||
|
{
|
||||||
|
if (obj != null)
|
||||||
{
|
{
|
||||||
m_enumType = obj.GetType();
|
m_enumType = obj.GetType();
|
||||||
m_names = Enum.GetNames(obj.GetType());
|
m_names = Enum.GetNames(m_enumType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
public override void DrawValue(Rect window, float width)
|
||||||
|
@ -43,9 +43,9 @@ namespace Explorer
|
|||||||
throw new NotImplementedException("TODO");
|
throw new NotImplementedException("TODO");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateValue(object obj)
|
public override void UpdateValue()
|
||||||
{
|
{
|
||||||
base.UpdateValue(obj);
|
base.UpdateValue();
|
||||||
|
|
||||||
m_gameObject = GetGameObject(Value);
|
m_gameObject = GetGameObject(Value);
|
||||||
}
|
}
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Explorer
|
|
||||||
{
|
|
||||||
public class CacheIl2CppObject : CacheObject
|
|
||||||
{
|
|
||||||
public override void DrawValue(Rect window, float width)
|
|
||||||
{
|
|
||||||
var label = ValueType ?? Value.ToString();
|
|
||||||
if (!label.Contains(ValueType))
|
|
||||||
{
|
|
||||||
label += $" ({ValueType})";
|
|
||||||
}
|
|
||||||
if (Value is UnityEngine.Object unityObj)
|
|
||||||
{
|
|
||||||
label = unityObj.name + " | " + label;
|
|
||||||
}
|
|
||||||
|
|
||||||
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
|
||||||
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(width) }))
|
|
||||||
{
|
|
||||||
WindowManager.InspectObject(Value, out bool _);
|
|
||||||
}
|
|
||||||
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetValue()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException("TODO");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Mono.CSharp;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
@ -13,30 +14,56 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
public bool IsExpanded { get; set; }
|
public bool IsExpanded { get; set; }
|
||||||
public int ArrayOffset { get; set; }
|
public int ArrayOffset { get; set; }
|
||||||
public Type EntryType { get; set; }
|
|
||||||
|
public Type EntryType
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (m_entryType == null)
|
||||||
|
{
|
||||||
|
m_entryType = Value?.GetType().GetGenericArguments()[0];
|
||||||
|
}
|
||||||
|
return m_entryType;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
m_entryType = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private Type m_entryType;
|
||||||
|
|
||||||
|
public IEnumerable Enumerable
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (m_enumerable == null && Value != null)
|
||||||
|
{
|
||||||
|
m_enumerable = Value as IEnumerable ?? CppListToEnumerable(Value);
|
||||||
|
}
|
||||||
|
return m_enumerable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private IEnumerable m_enumerable;
|
private IEnumerable m_enumerable;
|
||||||
private CacheObject[] m_cachedEntries;
|
private CacheObject[] m_cachedEntries;
|
||||||
|
|
||||||
public CacheList(object obj)
|
public CacheList(object obj)
|
||||||
{
|
{
|
||||||
GetEnumerable(obj);
|
if (obj != null)
|
||||||
EntryType = m_enumerable.GetType().GetGenericArguments()[0];
|
{
|
||||||
|
Value = obj;
|
||||||
|
EntryType = obj.GetType().GetGenericArguments()[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetEnumerable(object obj)
|
private IEnumerable CppListToEnumerable(object list)
|
||||||
{
|
{
|
||||||
if (obj is IEnumerable isEnumerable)
|
if (EntryType == null) return null;
|
||||||
{
|
|
||||||
m_enumerable = isEnumerable;
|
return (IEnumerable)typeof(Il2CppSystem.Collections.Generic.List<>)
|
||||||
}
|
.MakeGenericType(new Type[] { EntryType })
|
||||||
else
|
.GetMethod("ToArray")
|
||||||
{
|
.Invoke(list, new object[0]);
|
||||||
var listValueType = obj.GetType().GetGenericArguments()[0];
|
|
||||||
var listType = typeof(Il2CppSystem.Collections.Generic.List<>).MakeGenericType(new Type[] { listValueType });
|
|
||||||
var method = listType.GetMethod("ToArray");
|
|
||||||
m_enumerable = (IEnumerable)method.Invoke(obj, new object[0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
public override void DrawValue(Rect window, float width)
|
||||||
@ -73,7 +100,7 @@ namespace Explorer
|
|||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Space(190);
|
GUILayout.Space(190);
|
||||||
int maxOffset = (int)Mathf.Ceil(count / CppExplorer.ArrayLimit);
|
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)CppExplorer.ArrayLimit)) - 1;
|
||||||
GUILayout.Label($"Page {ArrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
|
GUILayout.Label($"Page {ArrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
|
||||||
// prev/next page buttons
|
// prev/next page buttons
|
||||||
if (GUILayout.Button("< Prev", null))
|
if (GUILayout.Button("< Prev", null))
|
||||||
@ -99,7 +126,7 @@ namespace Explorer
|
|||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Space(190);
|
GUILayout.Space(190);
|
||||||
|
|
||||||
if (entry == null)
|
if (entry.Value == null)
|
||||||
{
|
{
|
||||||
GUILayout.Label("<i><color=grey>null</color></i>", null);
|
GUILayout.Label("<i><color=grey>null</color></i>", null);
|
||||||
}
|
}
|
||||||
@ -108,22 +135,6 @@ namespace Explorer
|
|||||||
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(30) });
|
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(30) });
|
||||||
|
|
||||||
entry.DrawValue(window, window.width - 250);
|
entry.DrawValue(window, window.width - 250);
|
||||||
|
|
||||||
//var lbl = i + ": <color=cyan>" + obj.Value.ToString() + "</color>";
|
|
||||||
|
|
||||||
//if (EntryType.IsPrimitive || typeof(string).IsAssignableFrom(EntryType))
|
|
||||||
//{
|
|
||||||
// GUILayout.Label(lbl, null);
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
|
||||||
// if (GUILayout.Button(lbl, null))
|
|
||||||
// {
|
|
||||||
// WindowManager.InspectObject(obj, out _);
|
|
||||||
// }
|
|
||||||
// GUI.skin.button.alignment = TextAnchor.MiddleCenter;
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,14 +145,20 @@ namespace Explorer
|
|||||||
throw new NotImplementedException("TODO");
|
throw new NotImplementedException("TODO");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateValue(object obj)
|
/// <summary>
|
||||||
|
/// Called when the user presses the "Update" button, or if AutoUpdate is on.
|
||||||
|
/// </summary>
|
||||||
|
public override void UpdateValue()
|
||||||
{
|
{
|
||||||
GetEnumerable(Value);
|
base.UpdateValue();
|
||||||
|
|
||||||
|
if (Value == null) return;
|
||||||
|
|
||||||
|
var enumerator = Enumerable?.GetEnumerator();
|
||||||
|
|
||||||
|
if (enumerator == null) return;
|
||||||
|
|
||||||
var list = new List<CacheObject>();
|
var list = new List<CacheObject>();
|
||||||
|
|
||||||
var enumerator = m_enumerable.GetEnumerator();
|
|
||||||
|
|
||||||
while (enumerator.MoveNext())
|
while (enumerator.MoveNext())
|
||||||
{
|
{
|
||||||
list.Add(GetCacheObject(enumerator.Current));
|
list.Add(GetCacheObject(enumerator.Current));
|
||||||
|
@ -20,7 +20,10 @@ namespace Explorer
|
|||||||
public ReflectionWindow.MemberInfoType MemberInfoType { get; set; }
|
public ReflectionWindow.MemberInfoType MemberInfoType { get; set; }
|
||||||
public Type DeclaringType { get; set; }
|
public Type DeclaringType { get; set; }
|
||||||
public object DeclaringInstance { get; set; }
|
public object DeclaringInstance { get; set; }
|
||||||
|
public string FullName => $"{MemberInfo.DeclaringType.Name}.{MemberInfo.Name}";
|
||||||
|
public string ReflectionException;
|
||||||
|
|
||||||
|
// methods
|
||||||
public abstract void DrawValue(Rect window, float width);
|
public abstract void DrawValue(Rect window, float width);
|
||||||
public abstract void SetValue();
|
public abstract void SetValue();
|
||||||
|
|
||||||
@ -29,27 +32,25 @@ namespace Explorer
|
|||||||
return GetCacheObject(obj, null, null);
|
return GetCacheObject(obj, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the CacheObject subclass for an object or MemberInfo
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The current value (can be null if memberInfo is not null)</param>
|
||||||
|
/// <param name="memberInfo">The MemberInfo (can be null if obj is not null)</param>
|
||||||
|
/// <param name="declaringInstance">If MemberInfo is not null, the declaring class instance. Can be null if static.</param>
|
||||||
|
/// <returns></returns>
|
||||||
public static CacheObject GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance)
|
public static CacheObject GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance)
|
||||||
{
|
{
|
||||||
CacheObject holder;
|
CacheObject holder;
|
||||||
|
|
||||||
var type = ReflectionHelpers.GetActualType(obj) ?? (memberInfo as FieldInfo)?.FieldType ?? (memberInfo as PropertyInfo)?.PropertyType;
|
var type = ReflectionHelpers.GetActualType(obj) ?? (memberInfo as FieldInfo)?.FieldType ?? (memberInfo as PropertyInfo)?.PropertyType;
|
||||||
|
|
||||||
if (obj is Il2CppSystem.Object || typeof(Il2CppSystem.Object).IsAssignableFrom(type))
|
if ((obj is Il2CppSystem.Object || typeof(Il2CppSystem.Object).IsAssignableFrom(type))
|
||||||
{
|
&& (type.FullName.Contains("UnityEngine.GameObject") || type.FullName.Contains("UnityEngine.Transform")))
|
||||||
var name = type.FullName;
|
|
||||||
if (name == "UnityEngine.GameObject" || name == "UnityEngine.Transform")
|
|
||||||
{
|
{
|
||||||
holder = new CacheGameObject(obj);
|
holder = new CacheGameObject(obj);
|
||||||
}
|
}
|
||||||
else
|
else if (type.IsPrimitive || type == typeof(string))
|
||||||
{
|
|
||||||
holder = new CacheIl2CppObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (type.IsPrimitive || type == typeof(string))
|
|
||||||
{
|
{
|
||||||
holder = new CachePrimitive(obj);
|
holder = new CachePrimitive(obj);
|
||||||
}
|
}
|
||||||
@ -61,41 +62,33 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
holder = new CacheList(obj);
|
holder = new CacheList(obj);
|
||||||
}
|
}
|
||||||
else if (type.IsValueType)
|
|
||||||
{
|
|
||||||
holder = new CacheStruct(obj);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
holder = new CacheOther();
|
holder = new CacheOther();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (holder == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (memberInfo != null)
|
if (memberInfo != null)
|
||||||
{
|
{
|
||||||
holder.MemberInfo = memberInfo;
|
holder.MemberInfo = memberInfo;
|
||||||
holder.DeclaringType = memberInfo.DeclaringType;
|
holder.DeclaringType = memberInfo.DeclaringType;
|
||||||
|
holder.DeclaringInstance = declaringInstance;
|
||||||
|
|
||||||
if (declaringInstance is Il2CppSystem.Object ilInstance && ilInstance.GetType() != memberInfo.DeclaringType)
|
//if (declaringInstance is Il2CppSystem.Object ilInstance && ilInstance.GetType() != memberInfo.DeclaringType)
|
||||||
{
|
//{
|
||||||
try
|
// try
|
||||||
{
|
// {
|
||||||
holder.DeclaringInstance = ilInstance.Il2CppCast(holder.DeclaringType);
|
// holder.DeclaringInstance = ilInstance.Il2CppCast(holder.DeclaringType);
|
||||||
}
|
// }
|
||||||
catch
|
// catch (Exception e)
|
||||||
{
|
// {
|
||||||
holder.DeclaringInstance = declaringInstance;
|
// holder.ReflectionException = ReflectionHelpers.ExceptionToString(e);
|
||||||
}
|
// holder.DeclaringInstance = declaringInstance;
|
||||||
}
|
// }
|
||||||
else
|
//}
|
||||||
{
|
//else
|
||||||
holder.DeclaringInstance = declaringInstance;
|
//{
|
||||||
}
|
// holder.DeclaringInstance = declaringInstance;
|
||||||
|
//}
|
||||||
|
|
||||||
if (memberInfo.MemberType == MemberTypes.Field)
|
if (memberInfo.MemberType == MemberTypes.Field)
|
||||||
{
|
{
|
||||||
@ -121,14 +114,18 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (MemberInfo != null)
|
if (MemberInfo != null)
|
||||||
{
|
{
|
||||||
GUILayout.Label("<color=cyan>" + MemberInfo.Name + ":</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });
|
GUILayout.Label("<color=cyan>" + FullName + ":</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GUILayout.Space(labelWidth);
|
GUILayout.Space(labelWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Value == null)
|
if (!string.IsNullOrEmpty(ReflectionException))
|
||||||
|
{
|
||||||
|
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null);
|
||||||
|
}
|
||||||
|
else if (Value == null)
|
||||||
{
|
{
|
||||||
GUILayout.Label("<i>null (" + this.ValueType + ")</i>", null);
|
GUILayout.Label("<i>null (" + this.ValueType + ")</i>", null);
|
||||||
}
|
}
|
||||||
@ -138,9 +135,9 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void UpdateValue(object obj)
|
public virtual void UpdateValue()
|
||||||
{
|
{
|
||||||
if (MemberInfo == null)
|
if (MemberInfo == null || !string.IsNullOrEmpty(ReflectionException))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -155,12 +152,15 @@ namespace Explorer
|
|||||||
else if (MemberInfo.MemberType == MemberTypes.Property)
|
else if (MemberInfo.MemberType == MemberTypes.Property)
|
||||||
{
|
{
|
||||||
var pi = MemberInfo as PropertyInfo;
|
var pi = MemberInfo as PropertyInfo;
|
||||||
Value = pi.GetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, null);
|
bool isStatic = pi.GetAccessors()[0].IsStatic;
|
||||||
|
var target = isStatic ? null : DeclaringInstance;
|
||||||
|
Value = pi.GetValue(target, null);
|
||||||
}
|
}
|
||||||
|
//ReflectionException = null;
|
||||||
}
|
}
|
||||||
catch //(Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
//MelonLogger.Log($"Error updating MemberInfo value | {e.GetType()}: {e.Message}\r\n{e.StackTrace}");
|
ReflectionException = ReflectionHelpers.ExceptionToString(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,35 +1,64 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
public class CacheOther : CacheObject
|
public class CacheOther : CacheObject
|
||||||
{
|
{
|
||||||
|
private MethodInfo m_toStringMethod;
|
||||||
|
private bool m_triedToGetMethod;
|
||||||
|
|
||||||
|
public MethodInfo ToStringMethod
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (m_toStringMethod == null && !m_triedToGetMethod)
|
||||||
|
{
|
||||||
|
if (Value == null) return null;
|
||||||
|
|
||||||
|
m_triedToGetMethod = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var methods = ReflectionHelpers.GetActualType(Value)
|
||||||
|
.GetMethods(ReflectionHelpers.CommonFlags)
|
||||||
|
.Where(x => x.Name == "ToString")
|
||||||
|
.GetEnumerator();
|
||||||
|
|
||||||
|
while (methods.MoveNext())
|
||||||
|
{
|
||||||
|
// just get the first (top-most level) method, then break.
|
||||||
|
m_toStringMethod = methods.Current;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
return m_toStringMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
public override void DrawValue(Rect window, float width)
|
||||||
{
|
{
|
||||||
string label;
|
string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
|
||||||
if (Value is UnityEngine.Object uObj)
|
|
||||||
{
|
|
||||||
label = uObj.name;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
label = Value.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
string typeLabel = Value.GetType().FullName;
|
if (!label.Contains(ValueType))
|
||||||
|
|
||||||
if (!label.Contains(typeLabel))
|
|
||||||
{
|
{
|
||||||
label += $" ({typeLabel})";
|
label += $" ({ValueType})";
|
||||||
|
}
|
||||||
|
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("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(window.width - 230) }))
|
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(width) }))
|
||||||
{
|
{
|
||||||
WindowManager.InspectObject(Value, out bool _);
|
WindowManager.InspectObject(Value, out bool _);
|
||||||
}
|
}
|
||||||
@ -38,12 +67,12 @@ namespace Explorer
|
|||||||
|
|
||||||
public override void SetValue()
|
public override void SetValue()
|
||||||
{
|
{
|
||||||
|
throw new NotImplementedException("TODO");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateValue(object obj)
|
//public override void UpdateValue(object obj)
|
||||||
{
|
//{
|
||||||
|
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@ namespace Explorer
|
|||||||
|
|
||||||
public CachePrimitive(object obj)
|
public CachePrimitive(object obj)
|
||||||
{
|
{
|
||||||
|
if (obj == null) return;
|
||||||
|
|
||||||
if (obj is bool)
|
if (obj is bool)
|
||||||
{
|
{
|
||||||
m_primitiveType = PrimitiveType.Bool;
|
m_primitiveType = PrimitiveType.Bool;
|
||||||
@ -35,7 +37,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
m_primitiveType = PrimitiveType.Float;
|
m_primitiveType = PrimitiveType.Float;
|
||||||
}
|
}
|
||||||
else if (obj is int)
|
else if (obj is int || obj is IntPtr || obj is uint)
|
||||||
{
|
{
|
||||||
m_primitiveType = PrimitiveType.Int;
|
m_primitiveType = PrimitiveType.Int;
|
||||||
}
|
}
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Explorer
|
|
||||||
{
|
|
||||||
public class CacheStruct : CacheObject
|
|
||||||
{
|
|
||||||
public MethodInfo ToStringMethod { get; private set; }
|
|
||||||
private static readonly MethodInfo m_defaultToString = typeof(object).GetMethod("ToString");
|
|
||||||
|
|
||||||
public CacheStruct(object obj)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var methods = obj.GetType().GetMethods(ReflectionHelpers.CommonFlags).Where(x => x.Name == "ToString");
|
|
||||||
var enumerator = methods.GetEnumerator();
|
|
||||||
while (enumerator.MoveNext())
|
|
||||||
{
|
|
||||||
ToStringMethod = enumerator.Current;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
ToStringMethod = m_defaultToString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
|
||||||
{
|
|
||||||
string label;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
label = (string)ToStringMethod.Invoke(Value, null);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
label = Value.ToString();
|
|
||||||
}
|
|
||||||
string typeLabel = Value.GetType().FullName;
|
|
||||||
|
|
||||||
if (!label.Contains(typeLabel))
|
|
||||||
{
|
|
||||||
label += $" ({typeLabel})";
|
|
||||||
}
|
|
||||||
|
|
||||||
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
|
||||||
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(window.width - 230) }))
|
|
||||||
{
|
|
||||||
WindowManager.InspectObject(Value, out bool _);
|
|
||||||
}
|
|
||||||
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetValue()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException("TODO");
|
|
||||||
}
|
|
||||||
|
|
||||||
//public override void UpdateValue(object obj)
|
|
||||||
//{
|
|
||||||
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using MelonLoader;
|
using MelonLoader;
|
||||||
|
using UnhollowerBaseLib;
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
@ -12,7 +13,7 @@ namespace Explorer
|
|||||||
// consts
|
// consts
|
||||||
|
|
||||||
public const string ID = "com.sinai.cppexplorer";
|
public const string ID = "com.sinai.cppexplorer";
|
||||||
public const string VERSION = "1.4.0";
|
public const string VERSION = "1.4.1";
|
||||||
public const string AUTHOR = "Sinai";
|
public const string AUTHOR = "Sinai";
|
||||||
|
|
||||||
public const string NAME = "CppExplorer"
|
public const string NAME = "CppExplorer"
|
||||||
|
@ -140,8 +140,6 @@
|
|||||||
<Compile Include="CachedObjects\CacheGameObject.cs" />
|
<Compile Include="CachedObjects\CacheGameObject.cs" />
|
||||||
<Compile Include="CachedObjects\CacheList.cs" />
|
<Compile Include="CachedObjects\CacheList.cs" />
|
||||||
<Compile Include="CachedObjects\CachePrimitive.cs" />
|
<Compile Include="CachedObjects\CachePrimitive.cs" />
|
||||||
<Compile Include="CachedObjects\CacheIl2CppObject.cs" />
|
|
||||||
<Compile Include="CachedObjects\CacheStruct.cs" />
|
|
||||||
<Compile Include="CachedObjects\CacheOther.cs" />
|
<Compile Include="CachedObjects\CacheOther.cs" />
|
||||||
<Compile Include="CppExplorer.cs" />
|
<Compile Include="CppExplorer.cs" />
|
||||||
<Compile Include="Extensions\ReflectionExtensions.cs" />
|
<Compile Include="Extensions\ReflectionExtensions.cs" />
|
||||||
|
@ -9,6 +9,7 @@ using UnhollowerRuntimeLib;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using BF = System.Reflection.BindingFlags;
|
using BF = System.Reflection.BindingFlags;
|
||||||
using ILBF = Il2CppSystem.Reflection.BindingFlags;
|
using ILBF = Il2CppSystem.Reflection.BindingFlags;
|
||||||
|
using MelonLoader;
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
@ -26,10 +27,53 @@ namespace Explorer
|
|||||||
|
|
||||||
public static object Il2CppCast(object obj, Type castTo)
|
public static object Il2CppCast(object obj, Type castTo)
|
||||||
{
|
{
|
||||||
|
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj;
|
||||||
|
|
||||||
var generic = m_tryCastMethodInfo.MakeGenericMethod(castTo);
|
var generic = m_tryCastMethodInfo.MakeGenericMethod(castTo);
|
||||||
return generic.Invoke(obj, null);
|
return generic.Invoke(obj, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string ExceptionToString(Exception e)
|
||||||
|
{
|
||||||
|
if (IsFailedGeneric(e))
|
||||||
|
{
|
||||||
|
return "Unable to initialize this type.";
|
||||||
|
}
|
||||||
|
else if (IsObjectCollected(e))
|
||||||
|
{
|
||||||
|
return "Garbage collected in Il2Cpp.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.GetType() + ", " + e.Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsFailedGeneric(Exception e)
|
||||||
|
{
|
||||||
|
return IsExceptionOfType(e, typeof(TargetInvocationException)) && IsExceptionOfType(e, typeof(TypeLoadException));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsObjectCollected(Exception e)
|
||||||
|
{
|
||||||
|
return IsExceptionOfType(e, typeof(ObjectCollectedException));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsExceptionOfType(Exception e, Type t, bool strict = true, bool checkInner = true)
|
||||||
|
{
|
||||||
|
bool isType;
|
||||||
|
|
||||||
|
if (strict)
|
||||||
|
isType = e.GetType() == t;
|
||||||
|
else
|
||||||
|
isType = t.IsAssignableFrom(e.GetType());
|
||||||
|
|
||||||
|
if (isType) return true;
|
||||||
|
|
||||||
|
if (e.InnerException != null && checkInner)
|
||||||
|
return IsExceptionOfType(e.InnerException, t, strict);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static bool IsList(Type t)
|
public static bool IsList(Type t)
|
||||||
{
|
{
|
||||||
return t.IsGenericType
|
return t.IsGenericType
|
||||||
|
@ -111,7 +111,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
// prev/next page buttons
|
// prev/next page buttons
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
int maxOffset = (int)Mathf.Ceil(count / this.m_limit);
|
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)m_limit)) - 1;
|
||||||
if (GUILayout.Button("< Prev", null))
|
if (GUILayout.Button("< Prev", null))
|
||||||
{
|
{
|
||||||
if (m_pageOffset > 0) m_pageOffset--;
|
if (m_pageOffset > 0) m_pageOffset--;
|
||||||
@ -270,6 +270,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
var findType = ReflectionHelpers.GetTypeByName(_type);
|
var findType = ReflectionHelpers.GetTypeByName(_type);
|
||||||
searchType = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName);
|
searchType = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName);
|
||||||
|
//MelonLogger.Log("Search type: " + findType.AssemblyQualifiedName);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -299,8 +300,13 @@ namespace Explorer
|
|||||||
|
|
||||||
var allObjectsOfType = Resources.FindObjectsOfTypeAll(searchType);
|
var allObjectsOfType = Resources.FindObjectsOfTypeAll(searchType);
|
||||||
|
|
||||||
|
//MelonLogger.Log("Found count: " + allObjectsOfType.Length);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
foreach (var obj in allObjectsOfType)
|
foreach (var obj in allObjectsOfType)
|
||||||
{
|
{
|
||||||
|
if (i >= 2000) break;
|
||||||
|
|
||||||
if (_search != "" && !obj.name.ToLower().Contains(_search.ToLower()))
|
if (_search != "" && !obj.name.ToLower().Contains(_search.ToLower()))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -322,6 +328,8 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
matches.Add(obj);
|
matches.Add(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return matches;
|
return matches;
|
||||||
|
@ -20,10 +20,12 @@ namespace Explorer
|
|||||||
private CacheObject[] m_cachedMembers;
|
private CacheObject[] m_cachedMembers;
|
||||||
private CacheObject[] m_cachedMemberFiltered;
|
private CacheObject[] m_cachedMemberFiltered;
|
||||||
private int m_pageOffset;
|
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 = "";
|
||||||
public MemberInfoType m_filter = MemberInfoType.Property;
|
public MemberInfoType m_filter = MemberInfoType.Property;
|
||||||
|
private bool m_hideFailedReflection = true;
|
||||||
|
|
||||||
public enum MemberInfoType
|
public enum MemberInfoType
|
||||||
{
|
{
|
||||||
@ -72,7 +74,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
foreach (var member in m_cachedMemberFiltered)
|
foreach (var member in m_cachedMemberFiltered)
|
||||||
{
|
{
|
||||||
member.UpdateValue(Target);
|
member.UpdateValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,74 +82,66 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (m_filter != MemberInfoType.All && m_filter != holder.MemberInfoType) return false;
|
if (m_filter != MemberInfoType.All && m_filter != holder.MemberInfoType) return false;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) return false;
|
||||||
|
|
||||||
if (m_search == "" || holder.MemberInfo == null) return true;
|
if (m_search == "" || holder.MemberInfo == null) return true;
|
||||||
|
|
||||||
return holder.MemberInfo.Name
|
return holder.FullName
|
||||||
.ToLower()
|
.ToLower()
|
||||||
.Contains(m_search.ToLower());
|
.Contains(m_search.ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CacheMembers(Type[] types, List<string> names = null)
|
private void CacheMembers(Type[] types)
|
||||||
{
|
{
|
||||||
if (names == null)
|
|
||||||
{
|
|
||||||
names = new List<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
var list = new List<CacheObject>();
|
var list = new List<CacheObject>();
|
||||||
|
|
||||||
foreach (var type in types)
|
var names = new List<string>();
|
||||||
|
|
||||||
|
foreach (var declaringType in types)
|
||||||
{
|
{
|
||||||
|
if (declaringType == typeof(Il2CppObjectBase)) continue;
|
||||||
|
|
||||||
MemberInfo[] infos;
|
MemberInfo[] infos;
|
||||||
|
|
||||||
|
string exception = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
infos = type.GetMembers(ReflectionHelpers.CommonFlags);
|
infos = declaringType.GetMembers(ReflectionHelpers.CommonFlags);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
MelonLogger.Log("Exception getting members for type: " + type.Name);
|
MelonLogger.Log("Exception getting members for type: " + declaringType.Name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var member in infos)
|
//object value = null;
|
||||||
|
object target = Target;
|
||||||
|
if (target is Il2CppSystem.Object ilObject)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
{
|
||||||
|
target = ilObject.Il2CppCast(declaringType);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
exception = ReflectionHelpers.ExceptionToString(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var member in infos)
|
||||||
{
|
{
|
||||||
if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property)
|
if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property)
|
||||||
{
|
{
|
||||||
if (member.Name == "Il2CppType") continue;
|
if (member.Name == "Il2CppType") continue;
|
||||||
|
|
||||||
if (names.Contains(member.Name)) continue;
|
var name = member.DeclaringType.Name + "." + member.Name;
|
||||||
names.Add(member.Name);
|
if (names.Contains(name)) continue;
|
||||||
|
names.Add(name);
|
||||||
|
|
||||||
object value = null;
|
var cached = CacheObject.GetCacheObject(null, member, target);
|
||||||
object target = Target;
|
list.Add(cached);
|
||||||
|
cached.ReflectionException = exception;
|
||||||
if (target is Il2CppSystem.Object ilObject)
|
|
||||||
{
|
|
||||||
if (member.DeclaringType == typeof(Il2CppObjectBase)) continue;
|
|
||||||
|
|
||||||
target = ilObject.Il2CppCast(member.DeclaringType);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (member is FieldInfo)
|
|
||||||
{
|
|
||||||
value = (member as FieldInfo).GetValue(target);
|
|
||||||
}
|
|
||||||
else if (member is PropertyInfo)
|
|
||||||
{
|
|
||||||
value = (member as PropertyInfo).GetValue(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
list.Add(CacheObject.GetCacheObject(value, member, Target));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
MelonLogger.Log("Exception caching member " + member.Name + "!");
|
|
||||||
MelonLogger.Log(e.GetType() + ", " + e.Message);
|
|
||||||
MelonLogger.Log(e.StackTrace);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,6 +197,13 @@ namespace Explorer
|
|||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Label("<b>Search:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
|
GUILayout.Label("<b>Search:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
|
||||||
m_search = GUILayout.TextField(m_search, null);
|
m_search = GUILayout.TextField(m_search, null);
|
||||||
|
GUILayout.Label("<b>Limit per page:</b>", new GUILayoutOption[] { GUILayout.Width(125) });
|
||||||
|
var limitString = m_limitPerPage.ToString();
|
||||||
|
limitString = GUILayout.TextField(limitString, new GUILayoutOption[] { GUILayout.Width(60) });
|
||||||
|
if (int.TryParse(limitString, out int i))
|
||||||
|
{
|
||||||
|
m_limitPerPage = i;
|
||||||
|
}
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
@ -220,6 +221,8 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
GUI.color = m_autoUpdate ? Color.green : Color.red;
|
GUI.color = m_autoUpdate ? Color.green : Color.red;
|
||||||
m_autoUpdate = GUILayout.Toggle(m_autoUpdate, "Auto-update?", new GUILayoutOption[] { GUILayout.Width(100) });
|
m_autoUpdate = GUILayout.Toggle(m_autoUpdate, "Auto-update?", new GUILayoutOption[] { GUILayout.Width(100) });
|
||||||
|
GUI.color = m_hideFailedReflection ? Color.green : Color.red;
|
||||||
|
m_hideFailedReflection = GUILayout.Toggle(m_hideFailedReflection, "Hide failed Reflection?", new GUILayoutOption[] { GUILayout.Width(150) });
|
||||||
GUI.color = Color.white;
|
GUI.color = Color.white;
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
@ -227,11 +230,11 @@ namespace Explorer
|
|||||||
|
|
||||||
int count = m_cachedMemberFiltered.Length;
|
int count = m_cachedMemberFiltered.Length;
|
||||||
|
|
||||||
if (count > 20)
|
if (count > m_limitPerPage)
|
||||||
{
|
{
|
||||||
// prev/next page buttons
|
// prev/next page buttons
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
int maxOffset = (int)Mathf.Ceil(count / 20);
|
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)m_limitPerPage)) - 1;
|
||||||
if (GUILayout.Button("< Prev", null))
|
if (GUILayout.Button("< Prev", null))
|
||||||
{
|
{
|
||||||
if (m_pageOffset > 0) m_pageOffset--;
|
if (m_pageOffset > 0) m_pageOffset--;
|
||||||
@ -286,7 +289,7 @@ namespace Explorer
|
|||||||
|
|
||||||
GUILayout.Label($"<size=18><b><color=gold>{title}</color></b></size>", null);
|
GUILayout.Label($"<size=18><b><color=gold>{title}</color></b></size>", null);
|
||||||
|
|
||||||
int offset = (m_pageOffset * 20) + index;
|
int offset = (m_pageOffset * m_limitPerPage) + index;
|
||||||
|
|
||||||
if (offset >= m_cachedMemberFiltered.Length)
|
if (offset >= m_cachedMemberFiltered.Length)
|
||||||
{
|
{
|
||||||
@ -294,7 +297,7 @@ namespace Explorer
|
|||||||
offset = 0;
|
offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = offset; j < offset + 20 && j < members.Length; j++)
|
for (int j = offset; j < offset + m_limitPerPage && j < members.Length; j++)
|
||||||
{
|
{
|
||||||
var holder = members[j];
|
var holder = members[j];
|
||||||
|
|
||||||
@ -314,7 +317,7 @@ namespace Explorer
|
|||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
if (index >= 20) break;
|
if (index >= m_limitPerPage) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user