mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-03 03:52:28 +08:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
5de771389e | |||
51cfbe524e | |||
217b93ef4f | |||
42156e1160 | |||
e7208d0c9d | |||
2f3b779199 | |||
916bdea59b | |||
d8688193d5 | |||
30b48b1f1f | |||
0fd382c1f6 |
@ -1,4 +1,4 @@
|
||||
# CppExplorer []()
|
||||
# CppExplorer []()
|
||||
|
||||
<p align="center">
|
||||
<img align="center" src="https://sinai-dev.github.io/images/thumbs/02.png">
|
||||
@ -51,7 +51,9 @@ Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be ins
|
||||
|
||||
CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Reflection Inspector</b>.
|
||||
|
||||
<b>Tip:</b> when in Tab View, GameObjects are denoted by a [G] prefix, and Reflection objects are denoted by a [R] prefix.
|
||||
<b>Tips:</b>
|
||||
* When in Tab View, GameObjects are denoted by a [G] prefix, and Reflection objects are denoted by a [R] prefix.
|
||||
* Hold <b>Left Shift</b> when you click the Inspect button to force Reflection mode for GameObjects and Transforms.
|
||||
|
||||
### GameObject Inspector
|
||||
|
||||
@ -66,7 +68,7 @@ CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Re
|
||||
* Allows you to inspect Properties, Fields and basic Methods, as well as set primitive values and evaluate primitive methods.
|
||||
* Can search and filter members for the ones you are interested in.
|
||||
|
||||
[](https://i.imgur.com/eFVTQdh.png)
|
||||
[](https://i.imgur.com/iq92m0l.png)
|
||||
|
||||
### Object Search
|
||||
|
||||
|
@ -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;
|
||||
|
||||
@ -15,8 +16,23 @@ namespace Explorer
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
EnumType = Value.GetType();
|
||||
EnumNames = Enum.GetNames(EnumType);
|
||||
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)
|
||||
|
@ -10,32 +10,9 @@ namespace Explorer
|
||||
{
|
||||
public class CacheGameObject : CacheObjectBase
|
||||
{
|
||||
private GameObject GameObj
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_gameObject == null)
|
||||
{
|
||||
if (Value is Il2CppSystem.Object ilObj)
|
||||
{
|
||||
var ilType = ilObj.GetIl2CppType();
|
||||
|
||||
if (ilType == ReflectionHelpers.GameObjectType || ilType == ReflectionHelpers.TransformType)
|
||||
{
|
||||
m_gameObject = ilObj.TryCast<GameObject>() ?? ilObj.TryCast<Transform>()?.gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
private GameObject m_gameObject;
|
||||
|
||||
public override void DrawValue(Rect window, float width)
|
||||
{
|
||||
UIHelpers.GameobjButton(GameObj, null, false, width);
|
||||
UIHelpers.GameobjButton(Value, null, false, width);
|
||||
}
|
||||
|
||||
public override void UpdateValue()
|
||||
|
@ -3,87 +3,247 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MelonLoader;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Explorer
|
||||
{
|
||||
public partial class CacheList : CacheObjectBase
|
||||
public class CacheList : CacheObjectBase
|
||||
{
|
||||
public bool IsExpanded { get; set; }
|
||||
public int ArrayOffset { get; set; }
|
||||
public int ArrayLimit { get; set; } = 20;
|
||||
public PageHelper Pages = new PageHelper();
|
||||
|
||||
public float WhiteSpace = 215f;
|
||||
public float ButtonWidthOffset = 290f;
|
||||
|
||||
private CacheObjectBase[] m_cachedEntries;
|
||||
|
||||
// Type of Entries in the Array
|
||||
public Type EntryType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_entryType == null)
|
||||
{
|
||||
if (this.MemberInfo != null)
|
||||
{
|
||||
switch (this.MemberInfo.MemberType)
|
||||
{
|
||||
case MemberTypes.Field:
|
||||
m_entryType = (MemberInfo as FieldInfo).FieldType.GetGenericArguments()[0];
|
||||
break;
|
||||
case MemberTypes.Property:
|
||||
m_entryType = (MemberInfo as PropertyInfo).PropertyType.GetGenericArguments()[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Value != null)
|
||||
{
|
||||
m_entryType = Value.GetType().GetGenericArguments()[0];
|
||||
}
|
||||
}
|
||||
return m_entryType;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_entryType = value;
|
||||
}
|
||||
{
|
||||
get => GetEntryType();
|
||||
set => m_entryType = value;
|
||||
}
|
||||
private Type m_entryType;
|
||||
|
||||
// Cached IEnumerable object
|
||||
public IEnumerable Enumerable
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_enumerable == null && Value != null)
|
||||
{
|
||||
m_enumerable = Value as IEnumerable ?? CastValueFromList();
|
||||
}
|
||||
return m_enumerable;
|
||||
}
|
||||
get => GetEnumerable();
|
||||
}
|
||||
|
||||
private IEnumerable m_enumerable;
|
||||
private CacheObjectBase[] m_cachedEntries;
|
||||
|
||||
// Generic Type Definition for Lists
|
||||
public Type GenericTypeDef
|
||||
{
|
||||
get => GetGenericTypeDef();
|
||||
}
|
||||
private Type m_genericTypeDef;
|
||||
|
||||
// Cached ToArray method for Lists
|
||||
public MethodInfo GenericToArrayMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
if (EntryType == null) return null;
|
||||
|
||||
return m_genericToArray ??
|
||||
(m_genericToArray = typeof(Il2CppSystem.Collections.Generic.List<>)
|
||||
.MakeGenericType(new Type[] { this.EntryType })
|
||||
.GetMethod("ToArray"));
|
||||
}
|
||||
get => GetGenericToArrayMethod();
|
||||
}
|
||||
private MethodInfo m_genericToArray;
|
||||
|
||||
private IEnumerable CastValueFromList()
|
||||
// Cached Item Property for ILists
|
||||
public PropertyInfo ItemProperty
|
||||
{
|
||||
return (Value == null) ? null : (IEnumerable)GenericToArrayMethod?.Invoke(Value, new object[0]);
|
||||
get => GetItemProperty();
|
||||
}
|
||||
private PropertyInfo m_itemProperty;
|
||||
|
||||
// ========== Methods ==========
|
||||
|
||||
private IEnumerable GetEnumerable()
|
||||
{
|
||||
if (m_enumerable == null && Value != null)
|
||||
{
|
||||
m_enumerable = Value as IEnumerable ?? GetEnumerableFromIl2CppList();
|
||||
}
|
||||
return m_enumerable;
|
||||
}
|
||||
|
||||
private Type GetGenericTypeDef()
|
||||
{
|
||||
if (m_genericTypeDef == null && Value != null)
|
||||
{
|
||||
var type = Value.GetType();
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
m_genericTypeDef = type.GetGenericTypeDefinition();
|
||||
}
|
||||
}
|
||||
return m_genericTypeDef;
|
||||
}
|
||||
|
||||
private MethodInfo GetGenericToArrayMethod()
|
||||
{
|
||||
if (GenericTypeDef == null) return null;
|
||||
|
||||
if (m_genericToArray == null)
|
||||
{
|
||||
m_genericToArray = GenericTypeDef
|
||||
.MakeGenericType(new Type[] { this.EntryType })
|
||||
.GetMethod("ToArray");
|
||||
}
|
||||
return m_genericToArray;
|
||||
}
|
||||
|
||||
private PropertyInfo GetItemProperty()
|
||||
{
|
||||
if (m_itemProperty == null)
|
||||
{
|
||||
m_itemProperty = Value?.GetType().GetProperty("Item");
|
||||
}
|
||||
return m_itemProperty;
|
||||
}
|
||||
|
||||
private IEnumerable GetEnumerableFromIl2CppList()
|
||||
{
|
||||
if (Value == null) return null;
|
||||
|
||||
if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.List<>))
|
||||
{
|
||||
return (IEnumerable)GenericToArrayMethod?.Invoke(Value, new object[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ConvertIListToMono();
|
||||
}
|
||||
}
|
||||
|
||||
private IList ConvertIListToMono()
|
||||
{
|
||||
try
|
||||
{
|
||||
var genericType = typeof(List<>).MakeGenericType(new Type[] { this.EntryType });
|
||||
var list = (IList)Activator.CreateInstance(genericType);
|
||||
|
||||
for (int i = 0; ; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var itm = ItemProperty.GetValue(Value, new object[] { i });
|
||||
list.Add(itm);
|
||||
}
|
||||
catch { break; }
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MelonLogger.Log("Exception converting Il2Cpp IList to Mono IList: " + e.GetType() + ", " + e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Type GetEntryType()
|
||||
{
|
||||
if (m_entryType == null)
|
||||
{
|
||||
if (this.MemInfo != null)
|
||||
{
|
||||
Type memberType = null;
|
||||
switch (this.MemInfo.MemberType)
|
||||
{
|
||||
case MemberTypes.Field:
|
||||
memberType = (MemInfo as FieldInfo).FieldType;
|
||||
break;
|
||||
case MemberTypes.Property:
|
||||
memberType = (MemInfo as PropertyInfo).PropertyType;
|
||||
break;
|
||||
}
|
||||
|
||||
if (memberType != null && memberType.IsGenericType)
|
||||
{
|
||||
m_entryType = memberType.GetGenericArguments()[0];
|
||||
}
|
||||
}
|
||||
else if (Value != null)
|
||||
{
|
||||
var type = Value.GetType();
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
m_entryType = type.GetGenericArguments()[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IList probably won't be able to get any EntryType.
|
||||
if (m_entryType == null)
|
||||
{
|
||||
m_entryType = typeof(object);
|
||||
}
|
||||
|
||||
return m_entryType;
|
||||
}
|
||||
|
||||
public override void UpdateValue()
|
||||
{
|
||||
base.UpdateValue();
|
||||
|
||||
if (Value == null || Enumerable == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var enumerator = Enumerable.GetEnumerator();
|
||||
if (enumerator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var list = new List<CacheObjectBase>();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var obj = enumerator.Current;
|
||||
|
||||
if (obj != null && ReflectionHelpers.GetActualType(obj) is Type t)
|
||||
{
|
||||
if (obj is Il2CppSystem.Object iObj)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cast = iObj.Il2CppCast(t);
|
||||
if (cast != null)
|
||||
{
|
||||
obj = cast;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (GetCacheObject(obj, t) is CacheObjectBase cached)
|
||||
{
|
||||
list.Add(cached);
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(null);
|
||||
}
|
||||
}
|
||||
|
||||
m_cachedEntries = list.ToArray();
|
||||
}
|
||||
|
||||
// ============= GUI Draw =============
|
||||
|
||||
public override void DrawValue(Rect window, float width)
|
||||
{
|
||||
if (m_cachedEntries == null)
|
||||
{
|
||||
GUILayout.Label("m_cachedEntries is null!", null);
|
||||
return;
|
||||
}
|
||||
|
||||
int count = m_cachedEntries.Length;
|
||||
|
||||
if (!IsExpanded)
|
||||
@ -113,51 +273,51 @@ namespace Explorer
|
||||
|
||||
if (IsExpanded)
|
||||
{
|
||||
float whitespace = WhiteSpace;
|
||||
|
||||
float whitespace = WhiteSpace;
|
||||
if (whitespace > 0)
|
||||
{
|
||||
ClampLabelWidth(window, ref whitespace);
|
||||
}
|
||||
|
||||
if (count > ArrayLimit)
|
||||
Pages.Count = count;
|
||||
|
||||
if (count > Pages.PageLimit)
|
||||
{
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.BeginHorizontal(null);
|
||||
|
||||
GUILayout.Space(whitespace);
|
||||
|
||||
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)ArrayLimit)) - 1;
|
||||
GUILayout.Label($"Page {ArrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
|
||||
//int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)ArrayLimit)) - 1;
|
||||
Pages.CalculateMaxOffset();
|
||||
|
||||
//GUILayout.Label($"Page {PH.ArrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
|
||||
Pages.CurrentPageLabel();
|
||||
|
||||
// prev/next page buttons
|
||||
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(60) }))
|
||||
{
|
||||
if (ArrayOffset > 0) ArrayOffset--;
|
||||
Pages.TurnPage(Turn.Left);
|
||||
}
|
||||
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(60) }))
|
||||
{
|
||||
if (ArrayOffset < maxOffset) ArrayOffset++;
|
||||
}
|
||||
GUILayout.Label("Limit: ", new GUILayoutOption[] { GUILayout.Width(50) });
|
||||
var limit = this.ArrayLimit.ToString();
|
||||
limit = GUILayout.TextField(limit, new GUILayoutOption[] { GUILayout.Width(50) });
|
||||
if (limit != ArrayLimit.ToString() && int.TryParse(limit, out int i))
|
||||
{
|
||||
ArrayLimit = i;
|
||||
Pages.TurnPage(Turn.Right);
|
||||
}
|
||||
|
||||
Pages.DrawLimitInputArea();
|
||||
|
||||
GUILayout.Space(5);
|
||||
}
|
||||
|
||||
int offset = ArrayOffset * ArrayLimit;
|
||||
//int offset = ArrayOffset * ArrayLimit;
|
||||
//if (offset >= count)
|
||||
//{
|
||||
// offset = 0;
|
||||
// ArrayOffset = 0;
|
||||
//}
|
||||
int offset = Pages.CalculateOffsetIndex();
|
||||
|
||||
if (offset >= count)
|
||||
{
|
||||
offset = 0;
|
||||
ArrayOffset = 0;
|
||||
}
|
||||
|
||||
for (int i = offset; i < offset + ArrayLimit && i < count; i++)
|
||||
for (int i = offset; i < offset + Pages.PageLimit && i < count; i++)
|
||||
{
|
||||
var entry = m_cachedEntries[i];
|
||||
|
||||
@ -167,42 +327,22 @@ 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));
|
||||
|
||||
entry.DrawValue(window, window.width - (whitespace + 85));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called only when the user presses the "Update" button, or if AutoUpdate is on.
|
||||
/// </summary>
|
||||
public override void UpdateValue()
|
||||
{
|
||||
base.UpdateValue();
|
||||
|
||||
if (Value == null) return;
|
||||
|
||||
var enumerator = Enumerable?.GetEnumerator();
|
||||
|
||||
if (enumerator == null) return;
|
||||
|
||||
var list = new List<CacheObjectBase>();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
list.Add(GetCacheObject(enumerator.Current, null, null, this.EntryType));
|
||||
}
|
||||
|
||||
m_cachedEntries = list.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
@ -63,7 +63,7 @@ namespace Explorer
|
||||
|
||||
public override void UpdateValue()
|
||||
{
|
||||
base.UpdateValue();
|
||||
//base.UpdateValue();
|
||||
}
|
||||
|
||||
public override void DrawValue(Rect window, float width)
|
||||
@ -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;
|
||||
|
||||
|
@ -13,54 +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 FullName => $"{MemberInfo.DeclaringType.Name}.{MemberInfo.Name}";
|
||||
public string ReflectionException;
|
||||
public string ReflectionException { get; set; }
|
||||
|
||||
public int PropertyIndex { get; private set; }
|
||||
private string m_propertyIndexInput = "0";
|
||||
|
||||
public string RichTextName => m_richTextName ?? GetRichTextName();
|
||||
private string m_richTextName;
|
||||
|
||||
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)
|
||||
@ -85,26 +101,16 @@ namespace Explorer
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
MelonLogger.Log("Could not get type for object or memberinfo!");
|
||||
if (memberInfo is MethodInfo)
|
||||
{
|
||||
MelonLogger.Log("is it void?");
|
||||
}
|
||||
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;
|
||||
|
||||
@ -145,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;
|
||||
|
||||
@ -161,6 +167,79 @@ namespace Explorer
|
||||
return holder;
|
||||
}
|
||||
|
||||
// ======== Instance Methods =========
|
||||
|
||||
public virtual void UpdateValue()
|
||||
{
|
||||
if (MemInfo == null || !string.IsNullOrEmpty(ReflectionException))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (MemInfo.MemberType == MemberTypes.Field)
|
||||
{
|
||||
var fi = MemInfo as FieldInfo;
|
||||
Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance);
|
||||
}
|
||||
else if (MemInfo.MemberType == MemberTypes.Property)
|
||||
{
|
||||
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;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ReflectionException = ReflectionHelpers.ExceptionToString(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValue()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (MemInfo.MemberType == MemberTypes.Field)
|
||||
{
|
||||
var fi = MemInfo as FieldInfo;
|
||||
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, Value);
|
||||
}
|
||||
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
|
||||
{
|
||||
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MelonLogger.LogWarning($"Error setting value: {e.GetType()}, {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// ========= Instance Gui Draw ==========
|
||||
|
||||
public const float MAX_LABEL_WIDTH = 400f;
|
||||
|
||||
public static void ClampLabelWidth(Rect window, ref float labelWidth)
|
||||
@ -178,9 +257,15 @@ namespace Explorer
|
||||
ClampLabelWidth(window, ref labelWidth);
|
||||
}
|
||||
|
||||
if (MemberInfo != null)
|
||||
if (MemInfo != null)
|
||||
{
|
||||
GUILayout.Label("<color=cyan>" + FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });
|
||||
var name = RichTextName;
|
||||
if (MemInfo is PropertyInfo pi && pi.GetIndexParameters().Length > 0)
|
||||
{
|
||||
name += $"[{PropertyIndex}]";
|
||||
}
|
||||
|
||||
GUILayout.Label(name, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -191,64 +276,70 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void UpdateValue()
|
||||
private string GetRichTextName()
|
||||
{
|
||||
if (MemberInfo == null || !string.IsNullOrEmpty(ReflectionException))
|
||||
string memberColor = "";
|
||||
switch (MemInfo.MemberType)
|
||||
{
|
||||
return;
|
||||
case MemberTypes.Field:
|
||||
memberColor = "#c266ff"; break;
|
||||
case MemberTypes.Property:
|
||||
memberColor = "#72a6a6"; break;
|
||||
case MemberTypes.Method:
|
||||
memberColor = "#ff8000"; break;
|
||||
};
|
||||
|
||||
m_richTextName = $"<color=#2df7b2>{MemInfo.DeclaringType.Name}</color>.<color={memberColor}>{MemInfo.Name}</color>";
|
||||
|
||||
if (MemInfo is MethodInfo mi)
|
||||
{
|
||||
m_richTextName += "(";
|
||||
var _params = "";
|
||||
foreach (var param in mi.GetParameters())
|
||||
{
|
||||
if (_params != "") _params += ", ";
|
||||
|
||||
_params += $"<color=#a6e9e9>{param.Name}</color>";
|
||||
}
|
||||
m_richTextName += _params;
|
||||
m_richTextName += ")";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (MemberInfo.MemberType == MemberTypes.Field)
|
||||
{
|
||||
var fi = MemberInfo as FieldInfo;
|
||||
Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance);
|
||||
}
|
||||
else if (MemberInfo.MemberType == MemberTypes.Property)
|
||||
{
|
||||
var pi = MemberInfo as PropertyInfo;
|
||||
bool isStatic = pi.GetAccessors()[0].IsStatic;
|
||||
var target = isStatic ? null : DeclaringInstance;
|
||||
Value = pi.GetValue(target, null);
|
||||
}
|
||||
//ReflectionException = null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ReflectionException = ReflectionHelpers.ExceptionToString(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValue()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (MemberInfo.MemberType == MemberTypes.Field)
|
||||
{
|
||||
var fi = MemberInfo as FieldInfo;
|
||||
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, Value);
|
||||
}
|
||||
else if (MemberInfo.MemberType == MemberTypes.Property)
|
||||
{
|
||||
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}");
|
||||
}
|
||||
return m_richTextName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,33 +12,25 @@ 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())
|
||||
{
|
||||
// just get the first (top-most level) method, then break.
|
||||
m_toStringMethod = methods.Current;
|
||||
break;
|
||||
}
|
||||
// test invoke
|
||||
m_toStringMethod.Invoke(Value, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
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 _);
|
||||
}
|
||||
|
@ -28,21 +28,7 @@ namespace Explorer
|
||||
{
|
||||
if (m_parseMethod == null)
|
||||
{
|
||||
Type t = null;
|
||||
switch (PrimitiveType)
|
||||
{
|
||||
case PrimitiveTypes.Bool:
|
||||
t = typeof(bool); break;
|
||||
case PrimitiveTypes.Double:
|
||||
t = typeof(double); break;
|
||||
case PrimitiveTypes.Float:
|
||||
t = typeof(float); break;
|
||||
case PrimitiveTypes.Int:
|
||||
t = typeof(int); break;
|
||||
case PrimitiveTypes.Char:
|
||||
t = typeof(char); break;
|
||||
}
|
||||
m_parseMethod = t?.GetMethod("Parse", new Type[] { typeof(string) });
|
||||
m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) });
|
||||
}
|
||||
return m_parseMethod;
|
||||
}
|
||||
@ -127,7 +113,7 @@ namespace Explorer
|
||||
b = GUILayout.Toggle(b, label, null);
|
||||
if (b != (bool)Value)
|
||||
{
|
||||
SetValue(m_valueToString);
|
||||
SetValueFromInput(b.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -156,7 +142,7 @@ namespace Explorer
|
||||
{
|
||||
if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))
|
||||
{
|
||||
SetValue(m_valueToString);
|
||||
SetValueFromInput(m_valueToString);
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,9 +150,9 @@ namespace Explorer
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValue(string value)
|
||||
public void SetValueFromInput(string value)
|
||||
{
|
||||
if (MemberInfo == null)
|
||||
if (MemInfo == null)
|
||||
{
|
||||
MelonLogger.Log("Trying to SetValue but the MemberInfo is null!");
|
||||
return;
|
||||
|
@ -12,7 +12,7 @@ namespace Explorer
|
||||
public class CppExplorer : MelonMod
|
||||
{
|
||||
public const string GUID = "com.sinai.cppexplorer";
|
||||
public const string VERSION = "1.4.5";
|
||||
public const string VERSION = "1.5.3";
|
||||
public const string AUTHOR = "Sinai";
|
||||
|
||||
public const string NAME = "CppExplorer"
|
||||
@ -99,6 +99,8 @@ namespace Explorer
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
if (!ShowMenu) return;
|
||||
|
||||
MainMenu.Instance.OnGUI();
|
||||
WindowManager.Instance.OnGUI();
|
||||
InspectUnderMouse.OnGUI();
|
||||
@ -133,7 +135,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 +172,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 +184,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,6 +130,7 @@
|
||||
<Compile Include="CppExplorer.cs" />
|
||||
<Compile Include="Extensions\ReflectionExtensions.cs" />
|
||||
<Compile Include="Extensions\UnityExtensions.cs" />
|
||||
<Compile Include="Helpers\PageHelper.cs" />
|
||||
<Compile Include="Helpers\ReflectionHelpers.cs" />
|
||||
<Compile Include="Helpers\UIHelpers.cs" />
|
||||
<Compile Include="Helpers\UnityHelpers.cs" />
|
||||
|
88
src/Helpers/PageHelper.cs
Normal file
88
src/Helpers/PageHelper.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Explorer
|
||||
{
|
||||
public enum Turn
|
||||
{
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
|
||||
public class PageHelper
|
||||
{
|
||||
public int PageOffset { get; set; }
|
||||
public int PageLimit { get; set; } = 20;
|
||||
public int Count { get; set; }
|
||||
public int MaxOffset { get; set; } = -1;
|
||||
|
||||
public int CalculateMaxOffset()
|
||||
{
|
||||
return MaxOffset = (int)Mathf.Ceil((float)(Count / (decimal)PageLimit)) - 1;
|
||||
}
|
||||
|
||||
public void CurrentPageLabel()
|
||||
{
|
||||
var orig = GUI.skin.label.alignment;
|
||||
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
||||
|
||||
GUILayout.Label($"Page {PageOffset + 1}/{MaxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
|
||||
|
||||
GUI.skin.label.alignment = orig;
|
||||
}
|
||||
|
||||
public void TurnPage(Turn direction)
|
||||
{
|
||||
var _ = Vector2.zero;
|
||||
TurnPage(direction, ref _);
|
||||
}
|
||||
|
||||
public void TurnPage(Turn direction, ref Vector2 scroll)
|
||||
{
|
||||
if (direction == Turn.Left)
|
||||
{
|
||||
if (PageOffset > 0)
|
||||
{
|
||||
PageOffset--;
|
||||
scroll = Vector2.zero;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PageOffset < MaxOffset)
|
||||
{
|
||||
PageOffset++;
|
||||
scroll = Vector2.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int CalculateOffsetIndex()
|
||||
{
|
||||
int offset = PageOffset * PageLimit;
|
||||
|
||||
if (offset >= Count)
|
||||
{
|
||||
offset = 0;
|
||||
PageOffset = 0;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void DrawLimitInputArea()
|
||||
{
|
||||
GUILayout.Label("Limit: ", new GUILayoutOption[] { GUILayout.Width(50) });
|
||||
var limit = this.PageLimit.ToString();
|
||||
limit = GUILayout.TextField(limit, new GUILayoutOption[] { GUILayout.Width(50) });
|
||||
if (limit != PageLimit.ToString() && int.TryParse(limit, out int i))
|
||||
{
|
||||
PageLimit = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
@ -79,9 +81,17 @@ namespace Explorer
|
||||
|
||||
public static bool IsList(Type t)
|
||||
{
|
||||
return t.IsGenericType
|
||||
&& t.GetGenericTypeDefinition() is Type typeDef
|
||||
&& (typeDef.IsAssignableFrom(typeof(List<>)) || typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>)));
|
||||
if (t.IsGenericType)
|
||||
{
|
||||
var generic = t.GetGenericTypeDefinition();
|
||||
|
||||
return generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))
|
||||
|| generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.IList<>));
|
||||
}
|
||||
else
|
||||
{
|
||||
return t.IsAssignableFrom(typeof(Il2CppSystem.Collections.IList));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsDictionary(Type t)
|
||||
@ -108,51 +118,36 @@ 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();
|
||||
return Type.GetType(iltype.AssemblyQualifiedName);
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_object.GetType();
|
||||
var ilTypeName = ilObject.GetIl2CppType().AssemblyQualifiedName;
|
||||
|
||||
if (Type.GetType(ilTypeName) is Type t && !t.FullName.Contains("System.RuntimeType"))
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
return ilObject.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);
|
||||
var type = GetActualType(obj);
|
||||
list.Add(type);
|
||||
|
||||
while (ilType.BaseType != null)
|
||||
{
|
||||
ilType = ilType.BaseType;
|
||||
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilBaseTypeToManaged)
|
||||
{
|
||||
list.Add(ilBaseTypeToManaged);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
while (type.BaseType != null)
|
||||
{
|
||||
var type = m_object.GetType();
|
||||
type = type.BaseType;
|
||||
list.Add(type);
|
||||
while (type.BaseType != null)
|
||||
{
|
||||
type = type.BaseType;
|
||||
list.Add(type);
|
||||
}
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
|
@ -10,6 +10,35 @@ namespace Explorer
|
||||
{
|
||||
public class UIHelpers
|
||||
{
|
||||
private static bool ScrollUnstrippingFailed = false;
|
||||
|
||||
public static Vector2 BeginScrollView(Vector2 scroll) => BeginScrollView(scroll, null);
|
||||
|
||||
public static Vector2 BeginScrollView(Vector2 scroll, GUIStyle style, params GUILayoutOption[] layoutOptions)
|
||||
{
|
||||
if (ScrollUnstrippingFailed) return scroll;
|
||||
|
||||
try
|
||||
{
|
||||
if (style != null)
|
||||
return GUILayout.BeginScrollView(scroll, style, layoutOptions);
|
||||
else
|
||||
return GUILayout.BeginScrollView(scroll, layoutOptions);
|
||||
}
|
||||
catch
|
||||
{
|
||||
ScrollUnstrippingFailed = true;
|
||||
return scroll;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EndScrollView()
|
||||
{
|
||||
if (ScrollUnstrippingFailed) return;
|
||||
|
||||
GUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
// helper for "Instantiate" button on UnityEngine.Objects
|
||||
public static void InstantiateButton(Object obj, float width = 100)
|
||||
{
|
||||
@ -22,8 +51,10 @@ namespace Explorer
|
||||
}
|
||||
|
||||
// helper for drawing a styled button for a GameObject or Transform
|
||||
public static void GameobjButton(GameObject obj, Action<Transform> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
|
||||
public static void GameobjButton(object _obj, Action<Transform> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
|
||||
{
|
||||
var obj = (_obj as GameObject) ?? (_obj as Transform).gameObject;
|
||||
|
||||
bool children = obj.transform.childCount > 0;
|
||||
|
||||
string label = children ? "[" + obj.transform.childCount + " children] " : "";
|
||||
@ -49,11 +80,13 @@ namespace Explorer
|
||||
color = Color.red;
|
||||
}
|
||||
|
||||
FastGameobjButton(obj, color, label, obj.activeSelf, specialInspectMethod, showSmallInspectBtn, width);
|
||||
FastGameobjButton(_obj, color, label, obj.activeSelf, specialInspectMethod, showSmallInspectBtn, width);
|
||||
}
|
||||
|
||||
public static void FastGameobjButton(GameObject obj, Color activeColor, string label, bool enabled, Action<Transform> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
|
||||
public static void FastGameobjButton(object _obj, Color activeColor, string label, bool enabled, Action<Transform> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
|
||||
{
|
||||
var obj = _obj as GameObject ?? (_obj as Transform).gameObject;
|
||||
|
||||
if (!obj)
|
||||
{
|
||||
GUILayout.Label("<i><color=red>null</color></i>", null);
|
||||
@ -83,7 +116,7 @@ namespace Explorer
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowManager.InspectObject(obj, out bool _);
|
||||
WindowManager.InspectObject(_obj, out bool _);
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +127,7 @@ namespace Explorer
|
||||
|
||||
if (showSmallInspectBtn)
|
||||
{
|
||||
SmallInspectButton(obj);
|
||||
SmallInspectButton(_obj);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -51,15 +51,12 @@ namespace Explorer
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
if (CppExplorer.ShowMenu)
|
||||
{
|
||||
var origSkin = GUI.skin;
|
||||
GUI.skin = UIStyles.WindowSkin;
|
||||
var origSkin = GUI.skin;
|
||||
GUI.skin = UIStyles.WindowSkin;
|
||||
|
||||
MainRect = GUI.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, CppExplorer.NAME);
|
||||
MainRect = GUI.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, CppExplorer.NAME);
|
||||
|
||||
GUI.skin = origSkin;
|
||||
}
|
||||
GUI.skin = origSkin;
|
||||
}
|
||||
|
||||
private void MainWindow(int id)
|
||||
@ -77,9 +74,12 @@ namespace Explorer
|
||||
MainHeader();
|
||||
|
||||
var page = Pages[m_currentPage];
|
||||
page.scroll = GUILayout.BeginScrollView(page.scroll, GUI.skin.scrollView);
|
||||
|
||||
page.scroll = UIHelpers.BeginScrollView(page.scroll);
|
||||
|
||||
page.DrawWindow();
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
UIHelpers.EndScrollView();
|
||||
|
||||
MainRect = ResizeDrag.ResizeWindow(MainRect, MainWindowID);
|
||||
|
||||
|
@ -14,9 +14,7 @@ namespace Explorer
|
||||
|
||||
public override string Name { get => "Scene Explorer"; set => base.Name = value; }
|
||||
|
||||
private int m_pageOffset = 0;
|
||||
private int m_limit = 20;
|
||||
private int m_currentTotalCount = 0;
|
||||
public PageHelper Pages = new PageHelper();
|
||||
|
||||
private float m_timeOfLastUpdate = -1f;
|
||||
|
||||
@ -46,14 +44,14 @@ namespace Explorer
|
||||
SetTransformTarget(null);
|
||||
}
|
||||
|
||||
public void CheckOffset(ref int offset, int childCount)
|
||||
{
|
||||
if (offset >= childCount)
|
||||
{
|
||||
offset = 0;
|
||||
m_pageOffset = 0;
|
||||
}
|
||||
}
|
||||
//public void CheckOffset(ref int offset, int childCount)
|
||||
//{
|
||||
// if (offset >= childCount)
|
||||
// {
|
||||
// offset = 0;
|
||||
// m_pageOffset = 0;
|
||||
// }
|
||||
//}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
@ -63,7 +61,6 @@ namespace Explorer
|
||||
m_timeOfLastUpdate = Time.time;
|
||||
|
||||
m_objectList = new List<GameObjectCache>();
|
||||
int offset = m_pageOffset * m_limit;
|
||||
|
||||
var allTransforms = new List<Transform>();
|
||||
|
||||
@ -86,15 +83,14 @@ namespace Explorer
|
||||
}
|
||||
}
|
||||
|
||||
m_currentTotalCount = allTransforms.Count;
|
||||
Pages.Count = allTransforms.Count;
|
||||
|
||||
// make sure offset doesn't exceed count
|
||||
CheckOffset(ref offset, m_currentTotalCount);
|
||||
int offset = Pages.CalculateOffsetIndex();
|
||||
|
||||
// sort by childcount
|
||||
allTransforms.Sort((a, b) => b.childCount.CompareTo(a.childCount));
|
||||
|
||||
for (int i = offset; i < offset + m_limit && i < m_currentTotalCount; i++)
|
||||
for (int i = offset; i < offset + Pages.PageLimit && i < Pages.Count; i++)
|
||||
{
|
||||
var child = allTransforms[i];
|
||||
m_objectList.Add(new GameObjectCache(child.gameObject));
|
||||
@ -128,7 +124,7 @@ namespace Explorer
|
||||
{
|
||||
m_searchResults = SearchSceneObjects(m_searchInput);
|
||||
m_searching = true;
|
||||
m_currentTotalCount = m_searchResults.Count;
|
||||
Pages.Count = m_searchResults.Count;
|
||||
}
|
||||
|
||||
public void CancelSearch()
|
||||
@ -242,33 +238,21 @@ namespace Explorer
|
||||
{
|
||||
GUILayout.BeginHorizontal(null);
|
||||
|
||||
GUILayout.Label("Limit per page: ", new GUILayoutOption[] { GUILayout.Width(100) });
|
||||
var limit = m_limit.ToString();
|
||||
limit = GUILayout.TextField(limit, new GUILayoutOption[] { GUILayout.Width(30) });
|
||||
if (int.TryParse(limit, out int lim))
|
||||
{
|
||||
m_limit = lim;
|
||||
}
|
||||
Pages.DrawLimitInputArea();
|
||||
|
||||
// prev/next page buttons
|
||||
if (m_currentTotalCount > m_limit)
|
||||
if (Pages.Count > Pages.PageLimit)
|
||||
{
|
||||
int count = m_currentTotalCount;
|
||||
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)m_limit)) - 1;
|
||||
if (GUILayout.Button("< Prev", null))
|
||||
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||
{
|
||||
if (m_pageOffset > 0) m_pageOffset--;
|
||||
m_timeOfLastUpdate = -1f;
|
||||
Pages.TurnPage(Turn.Left, ref this.scroll);
|
||||
Update();
|
||||
}
|
||||
|
||||
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
||||
GUILayout.Label($"Page {m_pageOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
|
||||
Pages.CurrentPageLabel();
|
||||
|
||||
if (GUILayout.Button("Next >", null))
|
||||
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||
{
|
||||
if (m_pageOffset < maxOffset) m_pageOffset++;
|
||||
m_timeOfLastUpdate = -1f;
|
||||
Pages.TurnPage(Turn.Right, ref this.scroll);
|
||||
Update();
|
||||
}
|
||||
}
|
||||
@ -342,19 +326,26 @@ namespace Explorer
|
||||
|
||||
if (m_searchResults.Count > 0)
|
||||
{
|
||||
int offset = m_pageOffset * m_limit;
|
||||
int offset = Pages.CalculateOffsetIndex();
|
||||
|
||||
if (offset >= m_searchResults.Count)
|
||||
{
|
||||
offset = 0;
|
||||
m_pageOffset = 0;
|
||||
}
|
||||
|
||||
for (int i = offset; i < offset + m_limit && offset < m_searchResults.Count; i++)
|
||||
for (int i = offset; i < offset + Pages.PageLimit && i < m_searchResults.Count; i++)
|
||||
{
|
||||
var obj = m_searchResults[i];
|
||||
|
||||
UIHelpers.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
|
||||
if (obj.RefGameObject)
|
||||
{
|
||||
UIHelpers.FastGameobjButton(obj.RefGameObject,
|
||||
obj.EnabledColor,
|
||||
obj.Label,
|
||||
obj.RefGameObject.activeSelf,
|
||||
SetTransformTarget,
|
||||
true,
|
||||
MainMenu.MainRect.width - 170);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label("<i><color=red>Null or destroyed!</color></i>", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -5,11 +5,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using System.Reflection;
|
||||
using UnityEngine.SceneManagement;
|
||||
using Object = UnityEngine.Object;
|
||||
using UnhollowerRuntimeLib;
|
||||
using MelonLoader;
|
||||
using UnhollowerBaseLib;
|
||||
|
||||
namespace Explorer
|
||||
{
|
||||
@ -21,11 +17,11 @@ namespace Explorer
|
||||
|
||||
private string m_searchInput = "";
|
||||
private string m_typeInput = "";
|
||||
private int m_limit = 20;
|
||||
private int m_pageOffset = 0;
|
||||
//private List<object> m_searchResults = new List<object>();
|
||||
|
||||
private Vector2 resultsScroll = Vector2.zero;
|
||||
|
||||
public PageHelper Pages = new PageHelper();
|
||||
|
||||
private List<CacheObjectBase> m_searchResults = new List<CacheObjectBase>();
|
||||
|
||||
public SceneFilter SceneMode = SceneFilter.Any;
|
||||
@ -55,7 +51,7 @@ namespace Explorer
|
||||
public void OnSceneChange()
|
||||
{
|
||||
m_searchResults.Clear();
|
||||
m_pageOffset = 0;
|
||||
Pages.PageOffset = 0;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
@ -78,6 +74,9 @@ namespace Explorer
|
||||
var cache = CacheObjectBase.GetCacheObject(toCache);
|
||||
m_searchResults.Add(cache);
|
||||
}
|
||||
|
||||
Pages.Count = m_searchResults.Count;
|
||||
Pages.PageOffset = 0;
|
||||
}
|
||||
|
||||
public override void DrawWindow()
|
||||
@ -90,8 +89,7 @@ namespace Explorer
|
||||
if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) }))
|
||||
{
|
||||
//m_searchResults = GetInstanceClassScanner().ToList();
|
||||
CacheResults(GetInstanceClassScanner());
|
||||
m_pageOffset = 0;
|
||||
CacheResults(GetInstanceClassScanner());
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
@ -106,36 +104,45 @@ namespace Explorer
|
||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||
|
||||
int count = m_searchResults.Count;
|
||||
Pages.CalculateMaxOffset();
|
||||
|
||||
if (count > this.m_limit)
|
||||
GUILayout.BeginHorizontal(null);
|
||||
|
||||
Pages.DrawLimitInputArea();
|
||||
|
||||
if (count > Pages.PageLimit)
|
||||
{
|
||||
// prev/next page buttons
|
||||
GUILayout.BeginHorizontal(null);
|
||||
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)m_limit)) - 1;
|
||||
if (GUILayout.Button("< Prev", null))
|
||||
{
|
||||
if (m_pageOffset > 0) m_pageOffset--;
|
||||
}
|
||||
|
||||
GUILayout.Label($"Page {m_pageOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
|
||||
|
||||
if (GUILayout.Button("Next >", null))
|
||||
if (Pages.Count > Pages.PageLimit)
|
||||
{
|
||||
if (m_pageOffset < maxOffset) m_pageOffset++;
|
||||
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||
{
|
||||
Pages.TurnPage(Turn.Left, ref this.resultsScroll);
|
||||
}
|
||||
|
||||
Pages.CurrentPageLabel();
|
||||
|
||||
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||
{
|
||||
Pages.TurnPage(Turn.Right, ref this.resultsScroll);
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
resultsScroll = GUILayout.BeginScrollView(resultsScroll, GUI.skin.scrollView);
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
resultsScroll = UIHelpers.BeginScrollView(resultsScroll);
|
||||
|
||||
var _temprect = new Rect(MainMenu.MainRect.x, MainMenu.MainRect.y, MainMenu.MainRect.width + 160, MainMenu.MainRect.height);
|
||||
|
||||
if (m_searchResults.Count > 0)
|
||||
{
|
||||
int offset = m_pageOffset * this.m_limit;
|
||||
if (offset >= count) m_pageOffset = 0;
|
||||
//int offset = m_pageOffset * this.m_limit;
|
||||
//if (offset >= count) m_pageOffset = 0;
|
||||
int offset = Pages.CalculateOffsetIndex();
|
||||
|
||||
for (int i = offset; i < offset + m_limit && i < count; i++)
|
||||
for (int i = offset; i < offset + Pages.PageLimit && i < count; i++)
|
||||
{
|
||||
m_searchResults[i].Draw(MainMenu.MainRect, 0f);
|
||||
//m_searchResults[i].DrawValue(MainMenu.MainRect);
|
||||
@ -146,7 +153,7 @@ namespace Explorer
|
||||
GUILayout.Label("<color=red><i>No results found!</i></color>", null);
|
||||
}
|
||||
|
||||
GUILayout.EndScrollView();
|
||||
UIHelpers.EndScrollView();
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
catch
|
||||
@ -169,15 +176,15 @@ namespace Explorer
|
||||
GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) });
|
||||
m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) });
|
||||
|
||||
GUI.skin.label.alignment = TextAnchor.MiddleRight;
|
||||
GUILayout.Label("Results per page:", new GUILayoutOption[] { GUILayout.Width(120) });
|
||||
var resultinput = m_limit.ToString();
|
||||
resultinput = GUILayout.TextField(resultinput, new GUILayoutOption[] { GUILayout.Width(55) });
|
||||
if (int.TryParse(resultinput, out int _i) && _i > 0)
|
||||
{
|
||||
m_limit = _i;
|
||||
}
|
||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||
//GUI.skin.label.alignment = TextAnchor.MiddleRight;
|
||||
//GUILayout.Label("Results per page:", new GUILayoutOption[] { GUILayout.Width(120) });
|
||||
//var resultinput = m_limit.ToString();
|
||||
//resultinput = GUILayout.TextField(resultinput, new GUILayoutOption[] { GUILayout.Width(55) });
|
||||
//if (int.TryParse(resultinput, out int _i) && _i > 0)
|
||||
//{
|
||||
// m_limit = _i;
|
||||
//}
|
||||
//GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
@ -256,7 +263,7 @@ namespace Explorer
|
||||
|
||||
private void Search()
|
||||
{
|
||||
m_pageOffset = 0;
|
||||
Pages.PageOffset = 0;
|
||||
CacheResults(FindAllObjectsOfType(m_searchInput, m_typeInput));
|
||||
}
|
||||
|
||||
|
@ -21,11 +21,13 @@ namespace Explorer
|
||||
private string m_name;
|
||||
private string m_scene;
|
||||
|
||||
private Vector2 m_transformScroll = Vector2.zero;
|
||||
private Transform[] m_children;
|
||||
private Component[] m_components;
|
||||
private Vector2 m_transformScroll = Vector2.zero;
|
||||
private PageHelper ChildPages = new PageHelper();
|
||||
|
||||
private Component[] m_components;
|
||||
private Vector2 m_compScroll = Vector2.zero;
|
||||
private PageHelper CompPages = new PageHelper();
|
||||
|
||||
private float m_translateAmount = 0.3f;
|
||||
private float m_rotateAmount = 50f;
|
||||
@ -71,15 +73,10 @@ namespace Explorer
|
||||
|
||||
m_name = m_object.name;
|
||||
m_scene = string.IsNullOrEmpty(m_object.scene.name)
|
||||
? "None"
|
||||
: m_object.scene.name;
|
||||
? "None"
|
||||
: m_object.scene.name;
|
||||
|
||||
var list = new List<Transform>();
|
||||
for (int i = 0; i < m_object.transform.childCount; i++)
|
||||
{
|
||||
list.Add(m_object.transform.GetChild(i));
|
||||
}
|
||||
m_children = list.ToArray();
|
||||
Update();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
@ -91,12 +88,30 @@ namespace Explorer
|
||||
throw new Exception("Object is null!");
|
||||
}
|
||||
|
||||
var list = new List<Component>();
|
||||
var list = new List<Transform>();
|
||||
for (int i = 0; i < m_object.transform.childCount; i++)
|
||||
{
|
||||
list.Add(m_object.transform.GetChild(i));
|
||||
}
|
||||
list.Sort((a, b) => b.childCount.CompareTo(a.childCount));
|
||||
m_children = list.ToArray();
|
||||
|
||||
ChildPages.Count = m_children.Length;
|
||||
|
||||
var list2 = new List<Component>();
|
||||
foreach (var comp in m_object.GetComponents(ReflectionHelpers.ComponentType))
|
||||
{
|
||||
list.Add(comp);
|
||||
var ilType = comp.GetIl2CppType();
|
||||
if (ilType == ReflectionHelpers.TransformType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
list2.Add(comp);
|
||||
}
|
||||
m_components = list.ToArray();
|
||||
m_components = list2.ToArray();
|
||||
|
||||
CompPages.Count = m_components.Length;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -106,7 +121,7 @@ namespace Explorer
|
||||
|
||||
private void DestroyOnException(Exception e)
|
||||
{
|
||||
MelonLogger.Log($"{e.GetType()}, {e.Message}");
|
||||
MelonLogger.Log($"Exception drawing GameObject Window: {e.GetType()}, {e.Message}");
|
||||
DestroyWindow();
|
||||
}
|
||||
|
||||
@ -154,18 +169,22 @@ namespace Explorer
|
||||
GUILayout.BeginArea(new Rect(5, 25, rect.width - 10, rect.height - 35), GUI.skin.box);
|
||||
}
|
||||
|
||||
scroll = GUILayout.BeginScrollView(scroll, GUI.skin.scrollView);
|
||||
scroll = UIHelpers.BeginScrollView(scroll);
|
||||
|
||||
GUILayout.BeginHorizontal(null);
|
||||
GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", null);
|
||||
if (m_scene == UnityHelpers.ActiveSceneName)
|
||||
{
|
||||
if (GUILayout.Button("<color=#00FF00>< View in Scene Explorer</color>", new GUILayoutOption[] { GUILayout.Width(230) }))
|
||||
if (GUILayout.Button("<color=#00FF00>Send to Scene View</color>", new GUILayoutOption[] { GUILayout.Width(150) }))
|
||||
{
|
||||
ScenePage.Instance.SetTransformTarget(m_object.transform);
|
||||
MainMenu.SetCurrentPage(0);
|
||||
}
|
||||
}
|
||||
if (GUILayout.Button("Reflection Inspect", new GUILayoutOption[] { GUILayout.Width(150) }))
|
||||
{
|
||||
WindowManager.InspectObject(Target, out _, true);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal(null);
|
||||
@ -201,7 +220,7 @@ namespace Explorer
|
||||
|
||||
GameObjectControls();
|
||||
|
||||
GUILayout.EndScrollView();
|
||||
UIHelpers.EndScrollView();
|
||||
|
||||
if (!WindowManager.TabView)
|
||||
{
|
||||
@ -218,29 +237,49 @@ namespace Explorer
|
||||
|
||||
private void TransformList(Rect m_rect)
|
||||
{
|
||||
GUILayout.BeginVertical(GUI.skin.box, null); // new GUILayoutOption[] { GUILayout.Height(250) });
|
||||
m_transformScroll = GUILayout.BeginScrollView(m_transformScroll, GUI.skin.scrollView);
|
||||
GUILayout.BeginVertical(GUI.skin.box, null);
|
||||
m_transformScroll = UIHelpers.BeginScrollView(m_transformScroll);
|
||||
|
||||
GUILayout.Label("<b><size=15>Children</size></b>", null);
|
||||
|
||||
GUILayout.BeginHorizontal(null);
|
||||
ChildPages.DrawLimitInputArea();
|
||||
|
||||
if (ChildPages.Count > ChildPages.PageLimit)
|
||||
{
|
||||
ChildPages.CalculateMaxOffset();
|
||||
|
||||
ChildPages.CurrentPageLabel();
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.BeginHorizontal(null);
|
||||
|
||||
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||
{
|
||||
ChildPages.TurnPage(Turn.Left, ref this.m_transformScroll);
|
||||
}
|
||||
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||
{
|
||||
ChildPages.TurnPage(Turn.Right, ref this.m_transformScroll);
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.Label("<b>Children:</b>", null);
|
||||
if (m_children != null && m_children.Length > 0)
|
||||
{
|
||||
foreach (var obj in m_children.Where(x => x.childCount > 0))
|
||||
int start = ChildPages.CalculateOffsetIndex();
|
||||
|
||||
for (int j = start; (j < start + ChildPages.PageLimit && j < ChildPages.Count); j++)
|
||||
{
|
||||
var obj = m_children[j];
|
||||
|
||||
if (!obj)
|
||||
{
|
||||
GUILayout.Label("null", null);
|
||||
continue;
|
||||
}
|
||||
UIHelpers.GameobjButton(obj.gameObject, InspectGameObject, false, m_rect.width / 2 - 60);
|
||||
}
|
||||
foreach (var obj in m_children.Where(x => x.childCount == 0))
|
||||
{
|
||||
if (!obj)
|
||||
{
|
||||
GUILayout.Label("null", null);
|
||||
continue;
|
||||
}
|
||||
UIHelpers.GameobjButton(obj.gameObject, InspectGameObject, false, m_rect.width / 2 - 60);
|
||||
|
||||
UIHelpers.GameobjButton(obj.gameObject, InspectGameObject, false, m_rect.width / 2 - 80);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -248,17 +287,39 @@ namespace Explorer
|
||||
GUILayout.Label("<i>None</i>", null);
|
||||
}
|
||||
|
||||
GUILayout.EndScrollView();
|
||||
UIHelpers.EndScrollView();
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
|
||||
|
||||
private void ComponentList(Rect m_rect)
|
||||
{
|
||||
GUILayout.BeginVertical(GUI.skin.box, null); // new GUILayoutOption[] { GUILayout.Height(250) });
|
||||
m_compScroll = GUILayout.BeginScrollView(m_compScroll, GUI.skin.scrollView);
|
||||
GUILayout.BeginVertical(GUI.skin.box, null);
|
||||
m_compScroll = UIHelpers.BeginScrollView(m_compScroll);
|
||||
GUILayout.Label("<b><size=15>Components</size></b>", null);
|
||||
|
||||
GUILayout.BeginHorizontal(null);
|
||||
CompPages.DrawLimitInputArea();
|
||||
|
||||
if (CompPages.Count > CompPages.PageLimit)
|
||||
{
|
||||
CompPages.CalculateMaxOffset();
|
||||
|
||||
CompPages.CurrentPageLabel();
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.BeginHorizontal(null);
|
||||
|
||||
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||
{
|
||||
CompPages.TurnPage(Turn.Left, ref this.m_compScroll);
|
||||
}
|
||||
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||
{
|
||||
CompPages.TurnPage(Turn.Right, ref this.m_compScroll);
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
||||
if (m_cachedDestroyList.Count > 0)
|
||||
{
|
||||
@ -267,15 +328,15 @@ namespace Explorer
|
||||
|
||||
if (m_components != null)
|
||||
{
|
||||
foreach (var component in m_components)
|
||||
int start = CompPages.CalculateOffsetIndex();
|
||||
|
||||
for (int j = start; (j < start + CompPages.PageLimit && j < CompPages.Count); j++)
|
||||
{
|
||||
var component = m_components[j];
|
||||
|
||||
if (!component) continue;
|
||||
|
||||
var ilType = component.GetIl2CppType();
|
||||
if (ilType == ReflectionHelpers.TransformType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GUILayout.BeginHorizontal(null);
|
||||
if (ReflectionHelpers.BehaviourType.IsAssignableFrom(ilType))
|
||||
@ -286,7 +347,7 @@ namespace Explorer
|
||||
{
|
||||
GUILayout.Space(26);
|
||||
}
|
||||
if (GUILayout.Button("<color=cyan>" + ilType.Name + "</color>", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 90) }))
|
||||
if (GUILayout.Button("<color=cyan>" + ilType.Name + "</color>", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 100) }))
|
||||
{
|
||||
ReflectObject(component);
|
||||
}
|
||||
@ -308,7 +369,7 @@ namespace Explorer
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.EndScrollView();
|
||||
UIHelpers.EndScrollView();
|
||||
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
|
@ -13,15 +13,17 @@ 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;
|
||||
private int m_pageOffset;
|
||||
private int m_limitPerPage = 20;
|
||||
|
||||
public PageHelper Pages = new PageHelper();
|
||||
//private int m_pageOffset;
|
||||
//private int m_limitPerPage = 20;
|
||||
|
||||
private bool m_autoUpdate = false;
|
||||
private string m_search = "";
|
||||
@ -35,16 +37,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().Name}, ToString: {Target?.ToString()}");
|
||||
DestroyWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectType = type;
|
||||
TargetType = type;
|
||||
|
||||
var types = ReflectionHelpers.GetAllBaseTypes(Target);
|
||||
|
||||
CacheMembers(types);
|
||||
|
||||
if (Target is Il2CppSystem.Object ilObject)
|
||||
@ -90,15 +87,15 @@ 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;
|
||||
|
||||
return holder.FullName
|
||||
.ToLower()
|
||||
.Contains(m_search.ToLower());
|
||||
var name = holder.MemInfo.DeclaringType.Name + "." + holder.MemInfo.Name;
|
||||
|
||||
return name.ToLower().Contains(m_search.ToLower());
|
||||
}
|
||||
|
||||
private void CacheMembers(Type[] types)
|
||||
@ -118,7 +115,7 @@ namespace Explorer
|
||||
}
|
||||
catch
|
||||
{
|
||||
MelonLogger.Log("Exception getting members for type: " + declaringType.Name);
|
||||
MelonLogger.Log($"Exception getting members for type: {declaringType.FullName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -140,25 +137,41 @@ namespace Explorer
|
||||
{
|
||||
if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Method)
|
||||
{
|
||||
if (member.Name.Contains("Il2CppType") || member.Name.StartsWith("get_") || member.Name.StartsWith("set_"))
|
||||
var name = $"{member.DeclaringType.Name}.{member.Name}";
|
||||
|
||||
// blacklist (should probably make a proper implementation)
|
||||
if (name == "Type.DeclaringMethod" || member.Name.StartsWith("get_") || member.Name.StartsWith("set_")) //|| member.Name.Contains("Il2CppType")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (member is MethodInfo mi)
|
||||
{
|
||||
name += " (";
|
||||
foreach (var param in mi.GetParameters())
|
||||
{
|
||||
name += param.ParameterType.Name + ", ";
|
||||
}
|
||||
name += ")";
|
||||
}
|
||||
if (names.Contains(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var name = member.DeclaringType.Name + "." + member.Name;
|
||||
if (names.Contains(name)) continue;
|
||||
names.Add(name);
|
||||
|
||||
var cached = CacheObjectBase.GetCacheObject(null, member, target);
|
||||
var cached = CacheObjectBase.GetCacheObject(member, target);
|
||||
if (cached != null)
|
||||
{
|
||||
names.Add(name);
|
||||
list.Add(cached);
|
||||
cached.ReflectionException = exception;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MelonLogger.LogWarning($"Exception caching member {declaringType.Name}.{member.Name}!");
|
||||
MelonLogger.LogWarning($"Exception caching member {name}!");
|
||||
MelonLogger.Log(e.ToString());
|
||||
}
|
||||
}
|
||||
@ -185,7 +198,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);
|
||||
@ -243,41 +256,34 @@ namespace Explorer
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
Pages.Count = m_cachedMembersFiltered.Length;
|
||||
|
||||
// prev/next page buttons
|
||||
GUILayout.BeginHorizontal(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 lim))
|
||||
{
|
||||
m_limitPerPage = lim;
|
||||
}
|
||||
|
||||
int count = m_cachedMembersFiltered.Length;
|
||||
if (count > m_limitPerPage)
|
||||
Pages.DrawLimitInputArea();
|
||||
|
||||
if (Pages.Count > Pages.PageLimit)
|
||||
{
|
||||
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)m_limitPerPage)) - 1;
|
||||
Pages.CalculateMaxOffset();
|
||||
|
||||
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||
{
|
||||
if (m_pageOffset > 0) m_pageOffset--;
|
||||
scroll = Vector2.zero;
|
||||
Pages.TurnPage(Turn.Left, ref this.scroll);
|
||||
}
|
||||
|
||||
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
||||
GUILayout.Label($"Page {m_pageOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
|
||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||
Pages.CurrentPageLabel();
|
||||
|
||||
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||
{
|
||||
if (m_pageOffset < maxOffset) m_pageOffset++;
|
||||
scroll = Vector2.zero;
|
||||
Pages.TurnPage(Turn.Right, ref this.scroll);
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
// ====== BODY ======
|
||||
|
||||
scroll = GUILayout.BeginScrollView(scroll, GUI.skin.scrollView);
|
||||
scroll = UIHelpers.BeginScrollView(scroll);
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
@ -285,38 +291,32 @@ 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 = Pages.CalculateOffsetIndex();
|
||||
|
||||
if (offsetIndex >= count)
|
||||
{
|
||||
int maxOffset = (int)Mathf.Ceil((float)(m_cachedMembersFiltered.Length / (decimal)m_limitPerPage)) - 1;
|
||||
if (m_pageOffset > maxOffset)
|
||||
{
|
||||
m_pageOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = offsetIndex; (j < offsetIndex + m_limitPerPage && j < members.Length); j++)
|
||||
for (int j = start; (j < start + Pages.PageLimit && j < members.Length); j++)
|
||||
{
|
||||
var holder = members[j];
|
||||
|
||||
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
holder.Draw(rect, 180f);
|
||||
}
|
||||
catch
|
||||
{
|
||||
GUILayout.EndHorizontal();
|
||||
continue;
|
||||
}
|
||||
catch { }
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
index++;
|
||||
// if not last element
|
||||
if (!(j == (start + Pages.PageLimit - 1) || j == (members.Length - 1)))
|
||||
UIStyles.HorizontalLine(new Color(0.07f, 0.07f, 0.07f), true);
|
||||
}
|
||||
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.EndScrollView();
|
||||
UIHelpers.EndScrollView();
|
||||
|
||||
if (!WindowManager.TabView)
|
||||
{
|
||||
@ -353,7 +353,8 @@ namespace Explorer
|
||||
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) }))
|
||||
{
|
||||
m_filter = mode;
|
||||
m_pageOffset = 0;
|
||||
Pages.PageOffset = 0;
|
||||
scroll = Vector2.zero;
|
||||
}
|
||||
GUI.color = Color.white;
|
||||
}
|
||||
|
@ -86,6 +86,90 @@ namespace Explorer
|
||||
|
||||
// ========= Public Helpers =========
|
||||
|
||||
public static UIWindow InspectObject(object obj, out bool createdNew, bool forceReflection = false)
|
||||
{
|
||||
createdNew = false;
|
||||
|
||||
if (Input.GetKey(KeyCode.LeftShift))
|
||||
{
|
||||
forceReflection = true;
|
||||
}
|
||||
|
||||
Il2CppSystem.Object iObj = null;
|
||||
if (obj is Il2CppSystem.Object isObj)
|
||||
{
|
||||
iObj = isObj;
|
||||
}
|
||||
|
||||
if (!forceReflection)
|
||||
{
|
||||
foreach (var window in Windows)
|
||||
{
|
||||
bool equals = ReferenceEquals(obj, window.Target);
|
||||
|
||||
if (!equals && iObj is Il2CppSystem.Object iCurrent && window.Target is Il2CppSystem.Object iTarget)
|
||||
{
|
||||
if (iCurrent.GetIl2CppType() != iTarget.GetIl2CppType())
|
||||
{
|
||||
if (iCurrent is Transform transform)
|
||||
{
|
||||
iCurrent = transform.gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
equals = iCurrent.Pointer == iTarget.Pointer;
|
||||
}
|
||||
|
||||
if (equals)
|
||||
{
|
||||
FocusWindow(window);
|
||||
return window;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createdNew = true;
|
||||
if (!forceReflection && (obj is GameObject || obj is Transform))
|
||||
{
|
||||
return InspectGameObject(obj as GameObject ?? (obj as Transform).gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
return InspectReflection(obj);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FocusWindow(UIWindow window)
|
||||
{
|
||||
if (!TabView)
|
||||
{
|
||||
GUI.BringWindowToFront(window.windowID);
|
||||
GUI.FocusWindow(window.windowID);
|
||||
}
|
||||
else
|
||||
{
|
||||
TabViewWindow.Instance.TargetTabID = Windows.IndexOf(window);
|
||||
}
|
||||
}
|
||||
|
||||
private static UIWindow InspectGameObject(GameObject obj)
|
||||
{
|
||||
var new_window = UIWindow.CreateWindow<GameObjectWindow>(obj);
|
||||
FocusWindow(new_window);
|
||||
|
||||
return new_window;
|
||||
}
|
||||
|
||||
private static UIWindow InspectReflection(object obj)
|
||||
{
|
||||
var new_window = UIWindow.CreateWindow<ReflectionWindow>(obj);
|
||||
FocusWindow(new_window);
|
||||
|
||||
return new_window;
|
||||
}
|
||||
|
||||
// === Misc Helpers ===
|
||||
|
||||
public static bool IsMouseInWindow
|
||||
{
|
||||
get
|
||||
@ -140,71 +224,5 @@ namespace Explorer
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
public static UIWindow InspectObject(object obj, out bool createdNew)
|
||||
{
|
||||
createdNew = false;
|
||||
|
||||
UnityEngine.Object uObj = null;
|
||||
if (obj is UnityEngine.Object)
|
||||
{
|
||||
uObj = obj as UnityEngine.Object;
|
||||
}
|
||||
|
||||
foreach (var window in Windows)
|
||||
{
|
||||
bool equals = ReferenceEquals(obj, window.Target);
|
||||
|
||||
if (!equals && uObj != null && window.Target is UnityEngine.Object uTarget)
|
||||
{
|
||||
equals = uObj.m_CachedPtr == uTarget.m_CachedPtr;
|
||||
}
|
||||
|
||||
if (equals)
|
||||
{
|
||||
FocusWindow(window);
|
||||
return window;
|
||||
}
|
||||
}
|
||||
|
||||
createdNew = true;
|
||||
if (obj is GameObject || obj is Transform)
|
||||
{
|
||||
return InspectGameObject(obj as GameObject ?? (obj as Transform).gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
return InspectReflection(obj);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FocusWindow(UIWindow window)
|
||||
{
|
||||
if (!TabView)
|
||||
{
|
||||
GUI.BringWindowToFront(window.windowID);
|
||||
GUI.FocusWindow(window.windowID);
|
||||
}
|
||||
else
|
||||
{
|
||||
TabViewWindow.Instance.TargetTabID = Windows.IndexOf(window);
|
||||
}
|
||||
}
|
||||
|
||||
private static UIWindow InspectGameObject(GameObject obj)
|
||||
{
|
||||
var new_window = UIWindow.CreateWindow<GameObjectWindow>(obj);
|
||||
FocusWindow(new_window);
|
||||
|
||||
return new_window;
|
||||
}
|
||||
|
||||
public static UIWindow InspectReflection(object obj)
|
||||
{
|
||||
var new_window = UIWindow.CreateWindow<ReflectionWindow>(obj);
|
||||
FocusWindow(new_window);
|
||||
|
||||
return new_window;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user