* Added support for Properties with an index parameter on the Reflection Window (ie. "this[index]")
* Fixed a crash that occured when inspecting Il2CppSystem.Type objects
* Back-end cleanups
This commit is contained in:
sinaioutlander 2020-09-01 18:03:44 +10:00
parent 916bdea59b
commit 2f3b779199
12 changed files with 351 additions and 257 deletions

View File

@ -3,6 +3,7 @@ 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 MelonLoader; using MelonLoader;
using UnityEngine; using UnityEngine;
@ -14,10 +15,25 @@ namespace Explorer
public string[] EnumNames; public string[] EnumNames;
public override void Init() public override void Init()
{
try
{ {
EnumType = Value.GetType(); EnumType = Value.GetType();
}
catch
{
EnumType = (MemInfo as FieldInfo)?.FieldType ?? (MemInfo as PropertyInfo).PropertyType;
}
if (EnumType != null)
{
EnumNames = Enum.GetNames(EnumType); EnumNames = Enum.GetNames(EnumType);
} }
else
{
ReflectionException = "Unknown, could not get Enum names.";
}
}
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {

View File

@ -145,16 +145,16 @@ namespace Explorer
{ {
if (m_entryType == null) if (m_entryType == null)
{ {
if (this.MemberInfo != null) if (this.MemInfo != null)
{ {
Type memberType = null; Type memberType = null;
switch (this.MemberInfo.MemberType) switch (this.MemInfo.MemberType)
{ {
case MemberTypes.Field: case MemberTypes.Field:
memberType = (MemberInfo as FieldInfo).FieldType; memberType = (MemInfo as FieldInfo).FieldType;
break; break;
case MemberTypes.Property: case MemberTypes.Property:
memberType = (MemberInfo as PropertyInfo).PropertyType; memberType = (MemInfo as PropertyInfo).PropertyType;
break; break;
} }
@ -201,18 +201,48 @@ namespace Explorer
while (enumerator.MoveNext()) while (enumerator.MoveNext())
{ {
var obj = enumerator.Current; var obj = enumerator.Current;
var type = ReflectionHelpers.GetActualType(obj);
if (obj != null && ReflectionHelpers.GetActualType(obj) is Type t)
{
if (obj is Il2CppSystem.Object iObj) if (obj is Il2CppSystem.Object iObj)
{ {
obj = iObj.Il2CppCast(type); try
{
var cast = iObj.Il2CppCast(t);
if (cast != null)
{
obj = cast;
}
}
catch { }
} }
var cached = GetCacheObject(obj, null, null, type); if (GetCacheObject(obj, t) is CacheObjectBase cached)
cached.UpdateValue(); {
list.Add(cached); list.Add(cached);
} }
else
{
list.Add(null);
}
}
else
{
list.Add(null);
}
//var type = ReflectionHelpers.GetActualType(obj);
//if (obj is Il2CppSystem.Object iObj)
//{
// obj = iObj.Il2CppCast(type);
//}
//var cached = GetCacheObject(obj, null, null, type);
//cached.UpdateValue();
//list.Add(cached);
}
m_cachedEntries = list.ToArray(); m_cachedEntries = list.ToArray();
} }
@ -310,16 +340,18 @@ namespace Explorer
GUILayout.Space(whitespace); GUILayout.Space(whitespace);
if (entry.Value == null) if (entry == null || entry.Value == null)
{ {
GUILayout.Label(i + "<i><color=grey> (null)</color></i>", null); GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", null);
} }
else else
{ {
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) });
entry.DrawValue(window, window.width - (whitespace + 85)); entry.DrawValue(window, window.width - (whitespace + 85));
} }
} }
GUI.skin.label.alignment = TextAnchor.UpperLeft; GUI.skin.label.alignment = TextAnchor.UpperLeft;

View File

@ -24,7 +24,7 @@ namespace Explorer
{ {
if (m_hasParams == null) if (m_hasParams == null)
{ {
m_hasParams = (MemberInfo as MethodInfo).GetParameters().Length > 0; m_hasParams = (MemInfo as MethodInfo).GetParameters().Length > 0;
} }
return (bool)m_hasParams; return (bool)m_hasParams;
} }
@ -55,7 +55,7 @@ namespace Explorer
{ {
base.Init(); base.Init();
var mi = MemberInfo as MethodInfo; var mi = MemInfo as MethodInfo;
m_arguments = mi.GetParameters(); m_arguments = mi.GetParameters();
m_argumentInput = new string[m_arguments.Length]; m_argumentInput = new string[m_arguments.Length];
@ -136,12 +136,12 @@ namespace Explorer
} }
else else
{ {
GUILayout.Label($"null (<color=yellow>{ValueType}</color>)", null); GUILayout.Label($"null (<color=yellow>{ValueTypeName}</color>)", null);
} }
} }
else else
{ {
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=yellow>{ValueType}</color>)", null); GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=yellow>{ValueTypeName}</color>)", null);
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
@ -150,7 +150,7 @@ namespace Explorer
private void Evaluate() private void Evaluate()
{ {
var mi = MemberInfo as MethodInfo; var mi = MemInfo as MethodInfo;
object ret = null; object ret = null;

View File

@ -13,67 +13,70 @@ namespace Explorer
public abstract class CacheObjectBase public abstract class CacheObjectBase
{ {
public object Value; public object Value;
public string ValueType; public string ValueTypeName;
// Reflection window only // Reflection Inspector only
public MemberInfo MemberInfo { 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 string RichTextName public int PropertyIndex { get; private set; }
{ private string m_propertyIndexInput = "0";
get
{ public string RichTextName => m_richTextName ?? GetRichTextName();
if (m_richTextName == null)
{
GetRichTextName();
}
return m_richTextName;
}
}
private string m_richTextName; private string m_richTextName;
public string ReflectionException;
public bool CanWrite public bool CanWrite
{ {
get get
{ {
if (MemberInfo is FieldInfo fi) if (MemInfo is FieldInfo fi)
{
return !(fi.IsLiteral && !fi.IsInitOnly); return !(fi.IsLiteral && !fi.IsInitOnly);
} else if (MemInfo is PropertyInfo pi)
else if (MemberInfo is PropertyInfo pi)
{
return pi.CanWrite; return pi.CanWrite;
}
else else
{
return false; return false;
} }
} }
}
// methods // ===== Abstract/Virtual Methods ===== //
public virtual void Init() { } public virtual void Init() { }
public abstract void DrawValue(Rect window, float width); public abstract void DrawValue(Rect window, float width);
// ===== Static Methods ===== //
/// <summary>
/// Get CacheObject from only an object instance
/// Calls GetCacheObject(obj, memberInfo, declaringInstance) with (obj, null, null)</summary>
public static CacheObjectBase GetCacheObject(object obj) public static CacheObjectBase GetCacheObject(object obj)
{ {
return GetCacheObject(obj, null, null); return GetCacheObject(obj, null, null);
} }
/// <summary> /// <summary>
/// Gets the CacheObject subclass for an object or MemberInfo /// Get CacheObject from an object instance and provide the value type
/// </summary> /// Calls GetCacheObjectImpl directly</summary>
/// <param name="obj">The current value (can be null if memberInfo is not null)</param> public static CacheObjectBase GetCacheObject(object obj, Type valueType)
/// <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> return GetCacheObjectImpl(obj, null, null, valueType);
/// <returns></returns> }
/// <summary>
/// Get CacheObject from only a MemberInfo and declaring instance
/// Calls GetCacheObject(obj, memberInfo, declaringInstance) with (null, memberInfo, declaringInstance)</summary>
public static CacheObjectBase GetCacheObject(MemberInfo memberInfo, object declaringInstance)
{
return GetCacheObject(null, memberInfo, declaringInstance);
}
/// <summary>
/// Get CacheObject from either an object or MemberInfo, and don't provide the type.
/// This gets the type and then calls GetCacheObjectImpl</summary>
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance) public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance)
{ {
//var type = ReflectionHelpers.GetActualType(obj) ?? (memberInfo as FieldInfo)?.FieldType ?? (memberInfo as PropertyInfo)?.PropertyType;
Type type = null; Type type = null;
if (obj != null) if (obj != null)
@ -101,18 +104,13 @@ namespace Explorer
return null; return null;
} }
return GetCacheObject(obj, memberInfo, declaringInstance, type); return GetCacheObjectImpl(obj, memberInfo, declaringInstance, type);
} }
/// <summary> /// <summary>
/// Gets the CacheObject subclass for an object or MemberInfo /// Actual GetCacheObject implementation (private)
/// </summary> /// </summary>
/// <param name="obj">The current value (can be null if memberInfo is not null)</param> private static CacheObjectBase GetCacheObjectImpl(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
/// <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>
/// <param name="valueType">The type of the object or MemberInfo value.</param>
/// <returns></returns>
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
{ {
CacheObjectBase holder; CacheObjectBase holder;
@ -153,11 +151,11 @@ namespace Explorer
} }
holder.Value = obj; holder.Value = obj;
holder.ValueType = valueType.FullName; holder.ValueTypeName = valueType.FullName;
if (memberInfo != null) if (memberInfo != null)
{ {
holder.MemberInfo = memberInfo; holder.MemInfo = memberInfo;
holder.DeclaringType = memberInfo.DeclaringType; holder.DeclaringType = memberInfo.DeclaringType;
holder.DeclaringInstance = declaringInstance; holder.DeclaringInstance = declaringInstance;
@ -169,30 +167,40 @@ namespace Explorer
return holder; return holder;
} }
// ======== Updating and Setting Value (memberinfo only) ========= // ======== Instance Methods =========
public virtual void UpdateValue() public virtual void UpdateValue()
{ {
if (MemberInfo == null || !string.IsNullOrEmpty(ReflectionException)) if (MemInfo == null || !string.IsNullOrEmpty(ReflectionException))
{ {
return; return;
} }
try try
{ {
if (MemberInfo.MemberType == MemberTypes.Field) if (MemInfo.MemberType == MemberTypes.Field)
{ {
var fi = MemberInfo as FieldInfo; var fi = MemInfo as FieldInfo;
Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance); Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance);
} }
else if (MemberInfo.MemberType == MemberTypes.Property) else if (MemInfo.MemberType == MemberTypes.Property)
{ {
var pi = MemberInfo as PropertyInfo; var pi = MemInfo as PropertyInfo;
bool isStatic = pi.GetAccessors()[0].IsStatic; bool isStatic = pi.GetAccessors()[0].IsStatic;
var target = isStatic ? null : DeclaringInstance; var target = isStatic ? null : DeclaringInstance;
if (pi.GetIndexParameters().Length > 0)
{
var indexes = new object[] { PropertyIndex };
Value = pi.GetValue(target, indexes);
}
else
{
Value = pi.GetValue(target, null); Value = pi.GetValue(target, null);
} }
//ReflectionException = null; }
ReflectionException = null;
} }
catch (Exception e) catch (Exception e)
{ {
@ -204,24 +212,33 @@ namespace Explorer
{ {
try try
{ {
if (MemberInfo.MemberType == MemberTypes.Field) if (MemInfo.MemberType == MemberTypes.Field)
{ {
var fi = MemberInfo as FieldInfo; var fi = MemInfo as FieldInfo;
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, Value); fi.SetValue(fi.IsStatic ? null : DeclaringInstance, Value);
} }
else if (MemberInfo.MemberType == MemberTypes.Property) else if (MemInfo.MemberType == MemberTypes.Property)
{
var pi = MemInfo as PropertyInfo;
if (pi.GetIndexParameters().Length > 0)
{
var indexes = new object[] { PropertyIndex };
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value, indexes);
}
else
{ {
var pi = MemberInfo as PropertyInfo;
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value); pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value);
} }
} }
}
catch (Exception e) catch (Exception e)
{ {
MelonLogger.LogWarning($"Error setting value: {e.GetType()}, {e.Message}"); MelonLogger.LogWarning($"Error setting value: {e.GetType()}, {e.Message}");
} }
} }
// ========= Gui Draw ========== // ========= Instance Gui Draw ==========
public const float MAX_LABEL_WIDTH = 400f; public const float MAX_LABEL_WIDTH = 400f;
@ -240,11 +257,15 @@ namespace Explorer
ClampLabelWidth(window, ref labelWidth); ClampLabelWidth(window, ref labelWidth);
} }
if (MemberInfo != null) if (MemInfo != null)
{ {
var name = RichTextName;
if (MemInfo is PropertyInfo pi && pi.GetIndexParameters().Length > 0)
{
name += $"[{PropertyIndex}]";
}
GUILayout.Label(name, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
GUILayout.Label(RichTextName, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
} }
else else
{ {
@ -255,20 +276,44 @@ namespace Explorer
{ {
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null); GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null);
} }
else if (Value == null && MemberInfo?.MemberType != MemberTypes.Method) else if (Value == null && MemInfo?.MemberType != MemberTypes.Method)
{ {
GUILayout.Label("<i>null (" + ValueType + ")</i>", null); GUILayout.Label("<i>null (" + ValueTypeName + ")</i>", null);
} }
else else
{ {
if (MemInfo is PropertyInfo pi && pi.GetIndexParameters().Length > 0)
{
GUILayout.Label("index:", new GUILayoutOption[] { GUILayout.Width(50) });
m_propertyIndexInput = GUILayout.TextField(m_propertyIndexInput, new GUILayoutOption[] { GUILayout.Width(100) });
if (GUILayout.Button("Set", new GUILayoutOption[] { GUILayout.Width(60) }))
{
if (int.TryParse(m_propertyIndexInput, out int i))
{
PropertyIndex = i;
UpdateValue();
}
else
{
MelonLogger.Log($"Could not parse '{m_propertyIndexInput}' to an int!");
}
}
// new line and space
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Space(labelWidth);
}
DrawValue(window, window.width - labelWidth - 90); DrawValue(window, window.width - labelWidth - 90);
} }
} }
private void GetRichTextName() private string GetRichTextName()
{ {
string memberColor = ""; string memberColor = "";
switch (MemberInfo.MemberType) switch (MemInfo.MemberType)
{ {
case MemberTypes.Field: case MemberTypes.Field:
memberColor = "#c266ff"; break; memberColor = "#c266ff"; break;
@ -278,9 +323,9 @@ namespace Explorer
memberColor = "#ff8000"; break; memberColor = "#ff8000"; break;
}; };
m_richTextName = $"<color=#2df7b2>{MemberInfo.DeclaringType.Name}</color>.<color={memberColor}>{MemberInfo.Name}</color>"; m_richTextName = $"<color=#2df7b2>{MemInfo.DeclaringType.Name}</color>.<color={memberColor}>{MemInfo.Name}</color>";
if (MemberInfo is MethodInfo mi) if (MemInfo is MethodInfo mi)
{ {
m_richTextName += "("; m_richTextName += "(";
var _params = ""; var _params = "";
@ -293,6 +338,8 @@ namespace Explorer
m_richTextName += _params; m_richTextName += _params;
m_richTextName += ")"; m_richTextName += ")";
} }
return m_richTextName;
} }
} }
} }

View File

@ -12,34 +12,26 @@ namespace Explorer
public class CacheOther : CacheObjectBase public class CacheOther : CacheObjectBase
{ {
private MethodInfo m_toStringMethod; private MethodInfo m_toStringMethod;
private bool m_triedToGetMethod;
public MethodInfo ToStringMethod public MethodInfo ToStringMethod
{ {
get get
{ {
if (m_toStringMethod == null && !m_triedToGetMethod) if (m_toStringMethod == null)
{ {
if (Value == null) return null;
m_triedToGetMethod = true;
try try
{ {
var methods = ReflectionHelpers.GetActualType(Value) m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0])
.GetMethods(ReflectionHelpers.CommonFlags) ?? typeof(object).GetMethod("ToString", new Type[0]);
.Where(x => x.Name == "ToString")
.GetEnumerator();
while (methods.MoveNext()) // test invoke
m_toStringMethod.Invoke(Value, null);
}
catch
{ {
// just get the first (top-most level) method, then break. m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]);
m_toStringMethod = methods.Current;
break;
} }
} }
catch { }
}
return m_toStringMethod; return m_toStringMethod;
} }
} }
@ -48,9 +40,9 @@ namespace Explorer
{ {
string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString(); string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
if (!label.Contains(ValueType)) if (!label.Contains(ValueTypeName))
{ {
label += $" ({ValueType})"; label += $" ({ValueTypeName})";
} }
if (Value is UnityEngine.Object unityObj && !label.Contains(unityObj.name)) if (Value is UnityEngine.Object unityObj && !label.Contains(unityObj.name))
{ {
@ -58,7 +50,7 @@ namespace Explorer
} }
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(width + 40) })) if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.Width(width) }))
{ {
WindowManager.InspectObject(Value, out bool _); WindowManager.InspectObject(Value, out bool _);
} }

View File

@ -152,7 +152,7 @@ namespace Explorer
public void SetValueFromInput(string value) public void SetValueFromInput(string value)
{ {
if (MemberInfo == null) if (MemInfo == null)
{ {
MelonLogger.Log("Trying to SetValue but the MemberInfo is null!"); MelonLogger.Log("Trying to SetValue but the MemberInfo is null!");
return; return;

View File

@ -133,7 +133,7 @@ namespace Explorer
// value that we set back to when we close the menu or disable force-unlock. // value that we set back to when we close the menu or disable force-unlock.
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Setter)] [HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Setter)]
public class Cursor_lockState public class Cursor_set_lockState
{ {
[HarmonyPrefix] [HarmonyPrefix]
public static void Prefix(ref CursorLockMode value) public static void Prefix(ref CursorLockMode value)
@ -170,19 +170,6 @@ namespace Explorer
// Make it appear as though UnlockMouse is disabled to the rest of the application. // Make it appear as though UnlockMouse is disabled to the rest of the application.
[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Getter)]
public class Cursor_get_visible
{
[HarmonyPostfix]
public static void Postfix(ref bool __result)
{
if (ShouldForceMouse)
{
__result = m_lastVisibleState;
}
}
}
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Getter)] [HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Getter)]
public class Cursor_get_lockState public class Cursor_get_lockState
{ {
@ -195,5 +182,18 @@ namespace Explorer
} }
} }
} }
[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Getter)]
public class Cursor_get_visible
{
[HarmonyPostfix]
public static void Postfix(ref bool __result)
{
if (ShouldForceMouse)
{
__result = m_lastVisibleState;
}
}
}
} }
} }

View File

@ -60,7 +60,9 @@
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath> <HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<!-- Unity 2019 build (InputLegacyModule.dll) --> <!-- Unity 2019 build (InputLegacyModule.dll) -->
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Debug'"> <Reference Include="UnityEngine" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath>
<Private>False</Private> <Private>False</Private>
@ -89,7 +91,9 @@
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<!-- Unity 2018 build (InputModule.dll) --> <!-- Unity 2018 build (InputModule.dll) -->
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Release_Unity2018'"> <Reference Include="UnityEngine" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.dll</HintPath>
<Private>False</Private> <Private>False</Private>
@ -118,6 +122,7 @@
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UI.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="CachedObjects\CacheDictionary.cs" /> <Compile Include="CachedObjects\CacheDictionary.cs" />

View File

@ -8,6 +8,7 @@ using UnhollowerBaseLib;
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using BF = System.Reflection.BindingFlags; using BF = System.Reflection.BindingFlags;
using MelonLoader;
namespace Explorer namespace Explorer
{ {
@ -27,8 +28,9 @@ namespace Explorer
{ {
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj; if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj;
var generic = m_tryCastMethodInfo.MakeGenericMethod(castTo); return m_tryCastMethodInfo
return generic.Invoke(obj, null); .MakeGenericMethod(castTo)
.Invoke(obj, null);
} }
public static string ExceptionToString(Exception e) public static string ExceptionToString(Exception e)
@ -81,9 +83,10 @@ namespace Explorer
{ {
if (t.IsGenericType) if (t.IsGenericType)
{ {
return t.GetGenericTypeDefinition() is Type typeDef var generic = t.GetGenericTypeDefinition();
&& (typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))
|| typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.IList<>))); return generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))
|| generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.IList<>));
} }
else else
{ {
@ -115,59 +118,37 @@ namespace Explorer
return null; return null;
} }
public static Type GetActualType(object m_object) public static Type GetActualType(object obj)
{ {
if (m_object == null) return null; if (obj == null) return null;
if (m_object is Il2CppSystem.Object ilObject) if (obj is Il2CppSystem.Object ilObject)
{ {
var iltype = ilObject.GetIl2CppType(); var ilTypeName = ilObject.GetIl2CppType().AssemblyQualifiedName;
if (Type.GetType(iltype.AssemblyQualifiedName) is Type type)
if (Type.GetType(ilTypeName) is Type t && !t.FullName.Contains("System.RuntimeType"))
{ {
return type; return t;
} }
else
{
return ilObject.GetType(); return ilObject.GetType();
} }
}
else return obj.GetType();
{
return m_object.GetType();
}
} }
public static Type[] GetAllBaseTypes(object m_object) public static Type[] GetAllBaseTypes(object obj)
{ {
var list = new List<Type>(); var list = new List<Type>();
if (m_object is Il2CppSystem.Object ilObject) var type = GetActualType(obj);
{
var ilType = ilObject.GetIl2CppType();
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilTypeToManaged)
{
list.Add(ilTypeToManaged);
while (ilType.BaseType != null)
{
ilType = ilType.BaseType;
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilBaseTypeToManaged)
{
list.Add(ilBaseTypeToManaged);
}
}
}
}
else
{
var type = m_object.GetType();
list.Add(type); list.Add(type);
while (type.BaseType != null) while (type.BaseType != null)
{ {
type = type.BaseType; type = type.BaseType;
list.Add(type); list.Add(type);
} }
}
return list.ToArray(); return list.ToArray();
} }

View File

@ -33,12 +33,14 @@ namespace Explorer
} }
} }
public static void HorizontalLine(Color color) public static void HorizontalLine(Color _color, bool small = false)
{ {
var c = GUI.color; var orig = GUI.color;
GUI.color = color;
GUILayout.Box(GUIContent.none, HorizontalBar, null); GUI.color = _color;
GUI.color = c; GUILayout.Box(GUIContent.none, !small ? HorizontalBar : HorizontalBarSmall, null);
GUI.color = orig;
} }
private static GUISkin _customSkin; private static GUISkin _customSkin;
@ -46,8 +48,6 @@ namespace Explorer
public static Texture2D m_nofocusTex; public static Texture2D m_nofocusTex;
public static Texture2D m_focusTex; public static Texture2D m_focusTex;
private static GUIStyle _horizBarStyle;
private static GUIStyle HorizontalBar private static GUIStyle HorizontalBar
{ {
get get
@ -63,6 +63,24 @@ namespace Explorer
return _horizBarStyle; return _horizBarStyle;
} }
} }
private static GUIStyle _horizBarStyle;
private static GUIStyle HorizontalBarSmall
{
get
{
if (_horizBarSmallStyle == null)
{
_horizBarSmallStyle = new GUIStyle();
_horizBarSmallStyle.normal.background = Texture2D.whiteTexture;
_horizBarSmallStyle.margin = new RectOffset(0, 0, 2, 2);
_horizBarSmallStyle.fixedHeight = 1;
}
return _horizBarSmallStyle;
}
}
private static GUIStyle _horizBarSmallStyle;
private static GUISkin CreateWindowSkin() private static GUISkin CreateWindowSkin()
{ {

View File

@ -13,10 +13,10 @@ namespace Explorer
public class ReflectionWindow : UIWindow public class ReflectionWindow : UIWindow
{ {
public override string Title => WindowManager.TabView public override string Title => WindowManager.TabView
? $"<color=cyan>[R]</color> {ObjectType.Name}" ? $"<color=cyan>[R]</color> {TargetType.Name}"
: $"Reflection Inspector ({ObjectType.Name})"; : $"Reflection Inspector ({TargetType.Name})";
public Type ObjectType; public Type TargetType;
private CacheObjectBase[] m_allCachedMembers; private CacheObjectBase[] m_allCachedMembers;
private CacheObjectBase[] m_cachedMembersFiltered; private CacheObjectBase[] m_cachedMembersFiltered;
@ -35,16 +35,11 @@ namespace Explorer
public override void Init() public override void Init()
{ {
var type = ReflectionHelpers.GetActualType(Target); var type = ReflectionHelpers.GetActualType(Target);
if (type == null)
{
MelonLogger.Log($"Could not get underlying type for object! Type: {Target?.GetType().AssemblyQualifiedName}, Value ToString: {Target?.ToString()}");
DestroyWindow();
return;
}
ObjectType = type; TargetType = type;
var types = ReflectionHelpers.GetAllBaseTypes(Target); var types = ReflectionHelpers.GetAllBaseTypes(Target);
CacheMembers(types); CacheMembers(types);
if (Target is Il2CppSystem.Object ilObject) if (Target is Il2CppSystem.Object ilObject)
@ -90,13 +85,13 @@ namespace Explorer
private bool ShouldProcessMember(CacheObjectBase holder) private bool ShouldProcessMember(CacheObjectBase holder)
{ {
if (m_filter != MemberTypes.All && m_filter != holder.MemberInfo?.MemberType) return false; if (m_filter != MemberTypes.All && m_filter != holder.MemInfo?.MemberType) return false;
if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) return false; if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) return false;
if (m_search == "" || holder.MemberInfo == null) return true; if (m_search == "" || holder.MemInfo == null) return true;
var name = holder.MemberInfo.DeclaringType.Name + "." + holder.MemberInfo.Name; var name = holder.MemInfo.DeclaringType.Name + "." + holder.MemInfo.Name;
return name.ToLower().Contains(m_search.ToLower()); return name.ToLower().Contains(m_search.ToLower());
} }
@ -118,7 +113,7 @@ namespace Explorer
} }
catch catch
{ {
MelonLogger.Log("Exception getting members for type: " + declaringType.Name); MelonLogger.Log($"Exception getting members for type: {declaringType.FullName}");
continue; continue;
} }
@ -140,11 +135,14 @@ namespace Explorer
{ {
if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Method) if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Method)
{ {
// ignore these var name = $"{member.DeclaringType.Name}.{member.Name}";
if (member.Name.Contains("Il2CppType") || member.Name.StartsWith("get_") || member.Name.StartsWith("set_"))
continue; // blacklist (should probably make a proper implementation)
if (name == "Type.DeclaringMethod" || member.Name.Contains("Il2CppType") || member.Name.StartsWith("get_") || member.Name.StartsWith("set_"))
{
continue;
}
var name = member.DeclaringType.Name + "." + member.Name;
if (member is MethodInfo mi) if (member is MethodInfo mi)
{ {
name += " ("; name += " (";
@ -161,7 +159,7 @@ namespace Explorer
try try
{ {
var cached = CacheObjectBase.GetCacheObject(null, member, target); var cached = CacheObjectBase.GetCacheObject(member, target);
if (cached != null) if (cached != null)
{ {
names.Add(name); names.Add(name);
@ -198,7 +196,7 @@ namespace Explorer
} }
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Label("<b>Type:</b> <color=cyan>" + ObjectType.FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(245f) }); GUILayout.Label("<b>Type:</b> <color=cyan>" + TargetType.FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(245f) });
if (m_uObj) if (m_uObj)
{ {
GUILayout.Label("Name: " + m_uObj.name, null); GUILayout.Label("Name: " + m_uObj.name, null);
@ -298,11 +296,10 @@ namespace Explorer
GUILayout.BeginVertical(GUI.skin.box, null); GUILayout.BeginVertical(GUI.skin.box, null);
int index = 0;
var members = this.m_cachedMembersFiltered; var members = this.m_cachedMembersFiltered;
int offsetIndex = (m_pageOffset * m_limitPerPage) + index; int start = m_pageOffset * m_limitPerPage;
if (offsetIndex >= count) if (start >= count)
{ {
int maxOffset = (int)Mathf.Ceil((float)(m_cachedMembersFiltered.Length / (decimal)m_limitPerPage)) - 1; int maxOffset = (int)Mathf.Ceil((float)(m_cachedMembersFiltered.Length / (decimal)m_limitPerPage)) - 1;
if (m_pageOffset > maxOffset) if (m_pageOffset > maxOffset)
@ -311,21 +308,25 @@ namespace Explorer
} }
} }
for (int j = offsetIndex; (j < offsetIndex + m_limitPerPage && j < members.Length); j++) for (int j = start; (j < start + m_limitPerPage && j < members.Length); j++)
{ {
var holder = members[j]; var holder = members[j];
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) }); GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
try try
{ {
holder.Draw(rect, 180f); holder.Draw(rect, 180f);
} }
catch { } catch
{
GUILayout.EndHorizontal();
continue;
}
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
index++; // if not last element
if (!(j == (start + m_limitPerPage - 1) || j == (members.Length - 1)))
UIStyles.HorizontalLine(new Color(0.07f, 0.07f, 0.07f), true);
} }
GUILayout.EndVertical(); GUILayout.EndVertical();

View File

@ -86,61 +86,6 @@ namespace Explorer
// ========= Public Helpers ========= // ========= Public Helpers =========
public static bool IsMouseInWindow
{
get
{
if (!CppExplorer.ShowMenu)
{
return false;
}
foreach (var window in Windows)
{
if (RectContainsMouse(window.m_rect))
{
return true;
}
}
return RectContainsMouse(MainMenu.MainRect);
}
}
private static bool RectContainsMouse(Rect rect)
{
return rect.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y));
}
public static int NextWindowID()
{
return CurrentWindowID++;
}
public static Rect GetNewWindowRect()
{
return GetNewWindowRect(ref m_lastWindowRect);
}
public static Rect GetNewWindowRect(ref Rect lastRect)
{
Rect rect = new Rect(0, 0, 550, 700);
var mainrect = MainMenu.MainRect;
if (mainrect.x <= (Screen.width - mainrect.width - 100))
{
rect = new Rect(mainrect.x + mainrect.width + 20, mainrect.y, rect.width, rect.height);
}
if (lastRect.x == rect.x)
{
rect = new Rect(rect.x + 25, rect.y + 25, rect.width, rect.height);
}
lastRect = rect;
return rect;
}
public static UIWindow InspectObject(object obj, out bool createdNew) public static UIWindow InspectObject(object obj, out bool createdNew)
{ {
createdNew = false; createdNew = false;
@ -206,5 +151,62 @@ namespace Explorer
return new_window; return new_window;
} }
// === Misc Helpers ===
public static bool IsMouseInWindow
{
get
{
if (!CppExplorer.ShowMenu)
{
return false;
}
foreach (var window in Windows)
{
if (RectContainsMouse(window.m_rect))
{
return true;
}
}
return RectContainsMouse(MainMenu.MainRect);
}
}
private static bool RectContainsMouse(Rect rect)
{
return rect.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y));
}
public static int NextWindowID()
{
return CurrentWindowID++;
}
public static Rect GetNewWindowRect()
{
return GetNewWindowRect(ref m_lastWindowRect);
}
public static Rect GetNewWindowRect(ref Rect lastRect)
{
Rect rect = new Rect(0, 0, 550, 700);
var mainrect = MainMenu.MainRect;
if (mainrect.x <= (Screen.width - mainrect.width - 100))
{
rect = new Rect(mainrect.x + mainrect.width + 20, mainrect.y, rect.width, rect.height);
}
if (lastRect.x == rect.x)
{
rect = new Rect(rect.x + 25, rect.y + 25, rect.width, rect.height);
}
lastRect = rect;
return rect;
}
} }
} }