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

View File

@ -145,16 +145,16 @@ namespace Explorer
{
if (m_entryType == null)
{
if (this.MemberInfo != null)
if (this.MemInfo != null)
{
Type memberType = null;
switch (this.MemberInfo.MemberType)
switch (this.MemInfo.MemberType)
{
case MemberTypes.Field:
memberType = (MemberInfo as FieldInfo).FieldType;
memberType = (MemInfo as FieldInfo).FieldType;
break;
case MemberTypes.Property:
memberType = (MemberInfo as PropertyInfo).PropertyType;
memberType = (MemInfo as PropertyInfo).PropertyType;
break;
}
@ -201,18 +201,48 @@ namespace Explorer
while (enumerator.MoveNext())
{
var obj = enumerator.Current;
var type = ReflectionHelpers.GetActualType(obj);
if (obj != null && ReflectionHelpers.GetActualType(obj) is Type t)
{
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);
cached.UpdateValue();
if (GetCacheObject(obj, t) is CacheObjectBase 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();
}
@ -310,16 +340,18 @@ namespace Explorer
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
{
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
entry.DrawValue(window, window.width - (whitespace + 85));
}
}
GUI.skin.label.alignment = TextAnchor.UpperLeft;

View File

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

View File

@ -13,67 +13,70 @@ namespace Explorer
public abstract class CacheObjectBase
{
public object Value;
public string ValueType;
public string ValueTypeName;
// Reflection window only
public MemberInfo MemberInfo { get; set; }
// Reflection Inspector only
public MemberInfo MemInfo { get; set; }
public Type DeclaringType { get; set; }
public object DeclaringInstance { get; set; }
public string ReflectionException { get; set; }
public string RichTextName
{
get
{
if (m_richTextName == null)
{
GetRichTextName();
}
return m_richTextName;
}
}
public int PropertyIndex { get; private set; }
private string m_propertyIndexInput = "0";
public string RichTextName => m_richTextName ?? GetRichTextName();
private string m_richTextName;
public string ReflectionException;
public bool CanWrite
{
get
{
if (MemberInfo is FieldInfo fi)
{
if (MemInfo is FieldInfo fi)
return !(fi.IsLiteral && !fi.IsInitOnly);
}
else if (MemberInfo is PropertyInfo pi)
{
else if (MemInfo is PropertyInfo pi)
return pi.CanWrite;
}
else
{
return false;
}
}
}
// methods
// ===== Abstract/Virtual Methods ===== //
public virtual void Init() { }
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)
{
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>
/// Get CacheObject from an object instance and provide the value type
/// Calls GetCacheObjectImpl directly</summary>
public static CacheObjectBase GetCacheObject(object obj, Type valueType)
{
return GetCacheObjectImpl(obj, null, null, valueType);
}
/// <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)
{
//var type = ReflectionHelpers.GetActualType(obj) ?? (memberInfo as FieldInfo)?.FieldType ?? (memberInfo as PropertyInfo)?.PropertyType;
Type type = null;
if (obj != null)
@ -101,18 +104,13 @@ namespace Explorer
return null;
}
return GetCacheObject(obj, memberInfo, declaringInstance, type);
return GetCacheObjectImpl(obj, memberInfo, declaringInstance, type);
}
/// <summary>
/// Gets the CacheObject subclass for an object or MemberInfo
/// Actual GetCacheObject implementation (private)
/// </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>
/// <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)
private static CacheObjectBase GetCacheObjectImpl(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
{
CacheObjectBase holder;
@ -153,11 +151,11 @@ namespace Explorer
}
holder.Value = obj;
holder.ValueType = valueType.FullName;
holder.ValueTypeName = valueType.FullName;
if (memberInfo != null)
{
holder.MemberInfo = memberInfo;
holder.MemInfo = memberInfo;
holder.DeclaringType = memberInfo.DeclaringType;
holder.DeclaringInstance = declaringInstance;
@ -169,30 +167,40 @@ namespace Explorer
return holder;
}
// ======== Updating and Setting Value (memberinfo only) =========
// ======== Instance Methods =========
public virtual void UpdateValue()
{
if (MemberInfo == null || !string.IsNullOrEmpty(ReflectionException))
if (MemInfo == null || !string.IsNullOrEmpty(ReflectionException))
{
return;
}
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);
}
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;
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);
}
//ReflectionException = null;
}
ReflectionException = null;
}
catch (Exception e)
{
@ -204,24 +212,33 @@ namespace Explorer
{
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);
}
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);
}
}
}
catch (Exception e)
{
MelonLogger.LogWarning($"Error setting value: {e.GetType()}, {e.Message}");
}
}
// ========= Gui Draw ==========
// ========= Instance Gui Draw ==========
public const float MAX_LABEL_WIDTH = 400f;
@ -240,11 +257,15 @@ namespace Explorer
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(RichTextName, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
GUILayout.Label(name, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
}
else
{
@ -255,20 +276,44 @@ namespace Explorer
{
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
{
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);
}
}
private void GetRichTextName()
private string GetRichTextName()
{
string memberColor = "";
switch (MemberInfo.MemberType)
switch (MemInfo.MemberType)
{
case MemberTypes.Field:
memberColor = "#c266ff"; break;
@ -278,9 +323,9 @@ namespace Explorer
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 += "(";
var _params = "";
@ -293,6 +338,8 @@ namespace Explorer
m_richTextName += _params;
m_richTextName += ")";
}
return m_richTextName;
}
}
}

View File

@ -12,34 +12,26 @@ namespace Explorer
public class CacheOther : CacheObjectBase
{
private MethodInfo m_toStringMethod;
private bool m_triedToGetMethod;
public MethodInfo ToStringMethod
{
get
{
if (m_toStringMethod == null && !m_triedToGetMethod)
if (m_toStringMethod == null)
{
if (Value == null) return null;
m_triedToGetMethod = true;
try
{
var methods = ReflectionHelpers.GetActualType(Value)
.GetMethods(ReflectionHelpers.CommonFlags)
.Where(x => x.Name == "ToString")
.GetEnumerator();
m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0])
?? typeof(object).GetMethod("ToString", new Type[0]);
while (methods.MoveNext())
// test invoke
m_toStringMethod.Invoke(Value, null);
}
catch
{
// just get the first (top-most level) method, then break.
m_toStringMethod = methods.Current;
break;
m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]);
}
}
catch { }
}
return m_toStringMethod;
}
}
@ -48,9 +40,9 @@ namespace Explorer
{
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))
{
@ -58,7 +50,7 @@ namespace Explorer
}
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 _);
}

View File

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

View File

@ -133,7 +133,7 @@ namespace Explorer
// value that we set back to when we close the menu or disable force-unlock.
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Setter)]
public class Cursor_lockState
public class Cursor_set_lockState
{
[HarmonyPrefix]
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.
[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)]
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>
<Private>False</Private>
</Reference>
<!-- Unity 2019 build (InputLegacyModule.dll) -->
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath>
<Private>False</Private>
@ -89,7 +91,9 @@
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- Unity 2018 build (InputModule.dll) -->
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.dll</HintPath>
<Private>False</Private>
@ -118,6 +122,7 @@
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CachedObjects\CacheDictionary.cs" />

View File

@ -8,6 +8,7 @@ using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine;
using BF = System.Reflection.BindingFlags;
using MelonLoader;
namespace Explorer
{
@ -27,8 +28,9 @@ namespace Explorer
{
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj;
var generic = m_tryCastMethodInfo.MakeGenericMethod(castTo);
return generic.Invoke(obj, null);
return m_tryCastMethodInfo
.MakeGenericMethod(castTo)
.Invoke(obj, null);
}
public static string ExceptionToString(Exception e)
@ -81,9 +83,10 @@ namespace Explorer
{
if (t.IsGenericType)
{
return t.GetGenericTypeDefinition() is Type typeDef
&& (typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))
|| typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.IList<>)));
var generic = t.GetGenericTypeDefinition();
return generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))
|| generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.IList<>));
}
else
{
@ -115,59 +118,37 @@ namespace Explorer
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();
if (Type.GetType(iltype.AssemblyQualifiedName) is Type type)
var ilTypeName = ilObject.GetIl2CppType().AssemblyQualifiedName;
if (Type.GetType(ilTypeName) is Type t && !t.FullName.Contains("System.RuntimeType"))
{
return type;
return t;
}
else
{
return ilObject.GetType();
}
}
else
{
return m_object.GetType();
}
return obj.GetType();
}
public static Type[] GetAllBaseTypes(object m_object)
public static Type[] GetAllBaseTypes(object obj)
{
var list = new List<Type>();
if (m_object is Il2CppSystem.Object ilObject)
{
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();
var type = GetActualType(obj);
list.Add(type);
while (type.BaseType != null)
{
type = type.BaseType;
list.Add(type);
}
}
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;
GUI.color = color;
GUILayout.Box(GUIContent.none, HorizontalBar, null);
GUI.color = c;
var orig = GUI.color;
GUI.color = _color;
GUILayout.Box(GUIContent.none, !small ? HorizontalBar : HorizontalBarSmall, null);
GUI.color = orig;
}
private static GUISkin _customSkin;
@ -46,8 +48,6 @@ namespace Explorer
public static Texture2D m_nofocusTex;
public static Texture2D m_focusTex;
private static GUIStyle _horizBarStyle;
private static GUIStyle HorizontalBar
{
get
@ -63,6 +63,24 @@ namespace Explorer
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()
{

View File

@ -13,10 +13,10 @@ namespace Explorer
public class ReflectionWindow : UIWindow
{
public override string Title => WindowManager.TabView
? $"<color=cyan>[R]</color> {ObjectType.Name}"
: $"Reflection Inspector ({ObjectType.Name})";
? $"<color=cyan>[R]</color> {TargetType.Name}"
: $"Reflection Inspector ({TargetType.Name})";
public Type ObjectType;
public Type TargetType;
private CacheObjectBase[] m_allCachedMembers;
private CacheObjectBase[] m_cachedMembersFiltered;
@ -35,16 +35,11 @@ namespace Explorer
public override void Init()
{
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);
CacheMembers(types);
if (Target is Il2CppSystem.Object ilObject)
@ -90,13 +85,13 @@ namespace Explorer
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 (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());
}
@ -118,7 +113,7 @@ namespace Explorer
}
catch
{
MelonLogger.Log("Exception getting members for type: " + declaringType.Name);
MelonLogger.Log($"Exception getting members for type: {declaringType.FullName}");
continue;
}
@ -140,11 +135,14 @@ namespace Explorer
{
if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Method)
{
// ignore these
if (member.Name.Contains("Il2CppType") || member.Name.StartsWith("get_") || member.Name.StartsWith("set_"))
continue;
var name = $"{member.DeclaringType.Name}.{member.Name}";
// 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)
{
name += " (";
@ -161,7 +159,7 @@ namespace Explorer
try
{
var cached = CacheObjectBase.GetCacheObject(null, member, target);
var cached = CacheObjectBase.GetCacheObject(member, target);
if (cached != null)
{
names.Add(name);
@ -198,7 +196,7 @@ namespace Explorer
}
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)
{
GUILayout.Label("Name: " + m_uObj.name, null);
@ -298,11 +296,10 @@ namespace Explorer
GUILayout.BeginVertical(GUI.skin.box, null);
int index = 0;
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;
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];
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
try
{
holder.Draw(rect, 180f);
}
catch { }
catch
{
GUILayout.EndHorizontal();
continue;
}
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();

View File

@ -86,61 +86,6 @@ namespace Explorer
// ========= 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)
{
createdNew = false;
@ -206,5 +151,62 @@ namespace Explorer
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;
}
}
}