mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-14 07:37:06 +08:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
217b93ef4f | |||
42156e1160 | |||
e7208d0c9d | |||
2f3b779199 | |||
916bdea59b | |||
d8688193d5 | |||
30b48b1f1f | |||
0fd382c1f6 |
@ -1,4 +1,4 @@
|
|||||||
# CppExplorer []()
|
# CppExplorer []()
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img align="center" src="https://sinai-dev.github.io/images/thumbs/02.png">
|
<img align="center" src="https://sinai-dev.github.io/images/thumbs/02.png">
|
||||||
@ -66,7 +66,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.
|
* 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.
|
* 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
|
### Object Search
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Reflection;
|
||||||
using MelonLoader;
|
using MelonLoader;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@ -15,8 +16,23 @@ namespace Explorer
|
|||||||
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
EnumType = Value.GetType();
|
try
|
||||||
EnumNames = Enum.GetNames(EnumType);
|
{
|
||||||
|
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)
|
public override void DrawValue(Rect window, float width)
|
||||||
|
@ -10,32 +10,9 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
public class CacheGameObject : CacheObjectBase
|
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)
|
public override void DrawValue(Rect window, float width)
|
||||||
{
|
{
|
||||||
UIHelpers.GameobjButton(GameObj, null, false, width);
|
UIHelpers.GameobjButton(Value, null, false, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateValue()
|
public override void UpdateValue()
|
||||||
|
@ -3,87 +3,247 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using MelonLoader;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
public partial class CacheList : CacheObjectBase
|
public class CacheList : CacheObjectBase
|
||||||
{
|
{
|
||||||
public bool IsExpanded { get; set; }
|
public bool IsExpanded { get; set; }
|
||||||
public int ArrayOffset { get; set; }
|
public PageHelper Pages = new PageHelper();
|
||||||
public int ArrayLimit { get; set; } = 20;
|
|
||||||
|
|
||||||
public float WhiteSpace = 215f;
|
public float WhiteSpace = 215f;
|
||||||
public float ButtonWidthOffset = 290f;
|
public float ButtonWidthOffset = 290f;
|
||||||
|
|
||||||
|
private CacheObjectBase[] m_cachedEntries;
|
||||||
|
|
||||||
|
// Type of Entries in the Array
|
||||||
public Type EntryType
|
public Type EntryType
|
||||||
{
|
{
|
||||||
get
|
get => GetEntryType();
|
||||||
{
|
set => m_entryType = value;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
private Type m_entryType;
|
private Type m_entryType;
|
||||||
|
|
||||||
|
// Cached IEnumerable object
|
||||||
public IEnumerable Enumerable
|
public IEnumerable Enumerable
|
||||||
{
|
{
|
||||||
get
|
get => GetEnumerable();
|
||||||
{
|
|
||||||
if (m_enumerable == null && Value != null)
|
|
||||||
{
|
|
||||||
m_enumerable = Value as IEnumerable ?? CastValueFromList();
|
|
||||||
}
|
|
||||||
return m_enumerable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable m_enumerable;
|
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
|
public MethodInfo GenericToArrayMethod
|
||||||
{
|
{
|
||||||
get
|
get => GetGenericToArrayMethod();
|
||||||
{
|
|
||||||
if (EntryType == null) return null;
|
|
||||||
|
|
||||||
return m_genericToArray ??
|
|
||||||
(m_genericToArray = typeof(Il2CppSystem.Collections.Generic.List<>)
|
|
||||||
.MakeGenericType(new Type[] { this.EntryType })
|
|
||||||
.GetMethod("ToArray"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
private MethodInfo m_genericToArray;
|
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)
|
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;
|
int count = m_cachedEntries.Length;
|
||||||
|
|
||||||
if (!IsExpanded)
|
if (!IsExpanded)
|
||||||
@ -113,51 +273,51 @@ namespace Explorer
|
|||||||
|
|
||||||
if (IsExpanded)
|
if (IsExpanded)
|
||||||
{
|
{
|
||||||
float whitespace = WhiteSpace;
|
float whitespace = WhiteSpace;
|
||||||
|
|
||||||
if (whitespace > 0)
|
if (whitespace > 0)
|
||||||
{
|
{
|
||||||
ClampLabelWidth(window, ref whitespace);
|
ClampLabelWidth(window, ref whitespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count > ArrayLimit)
|
Pages.Count = count;
|
||||||
|
|
||||||
|
if (count > Pages.PageLimit)
|
||||||
{
|
{
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
|
|
||||||
GUILayout.Space(whitespace);
|
GUILayout.Space(whitespace);
|
||||||
|
|
||||||
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)ArrayLimit)) - 1;
|
//int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)ArrayLimit)) - 1;
|
||||||
GUILayout.Label($"Page {ArrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
|
Pages.CalculateMaxOffset();
|
||||||
|
|
||||||
|
//GUILayout.Label($"Page {PH.ArrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
|
||||||
|
Pages.CurrentPageLabel();
|
||||||
|
|
||||||
// prev/next page buttons
|
// prev/next page buttons
|
||||||
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(60) }))
|
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 (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(60) }))
|
||||||
{
|
{
|
||||||
if (ArrayOffset < maxOffset) ArrayOffset++;
|
Pages.TurnPage(Turn.Right);
|
||||||
}
|
|
||||||
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.DrawLimitInputArea();
|
||||||
|
|
||||||
GUILayout.Space(5);
|
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)
|
for (int i = offset; i < offset + Pages.PageLimit && i < count; i++)
|
||||||
{
|
|
||||||
offset = 0;
|
|
||||||
ArrayOffset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = offset; i < offset + ArrayLimit && i < count; i++)
|
|
||||||
{
|
{
|
||||||
var entry = m_cachedEntries[i];
|
var entry = m_cachedEntries[i];
|
||||||
|
|
||||||
@ -167,42 +327,22 @@ namespace Explorer
|
|||||||
|
|
||||||
GUILayout.Space(whitespace);
|
GUILayout.Space(whitespace);
|
||||||
|
|
||||||
if (entry.Value == null)
|
if (entry == null || entry.Value == null)
|
||||||
{
|
{
|
||||||
GUILayout.Label(i + "<i><color=grey> (null)</color></i>", null);
|
GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
||||||
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
|
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
|
||||||
entry.DrawValue(window, window.width - (whitespace + 85));
|
|
||||||
|
entry.DrawValue(window, window.width - (whitespace + 85));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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)
|
if (m_hasParams == null)
|
||||||
{
|
{
|
||||||
m_hasParams = (MemberInfo as MethodInfo).GetParameters().Length > 0;
|
m_hasParams = (MemInfo as MethodInfo).GetParameters().Length > 0;
|
||||||
}
|
}
|
||||||
return (bool)m_hasParams;
|
return (bool)m_hasParams;
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
|
|
||||||
var mi = MemberInfo as MethodInfo;
|
var mi = MemInfo as MethodInfo;
|
||||||
|
|
||||||
m_arguments = mi.GetParameters();
|
m_arguments = mi.GetParameters();
|
||||||
m_argumentInput = new string[m_arguments.Length];
|
m_argumentInput = new string[m_arguments.Length];
|
||||||
@ -63,7 +63,7 @@ namespace Explorer
|
|||||||
|
|
||||||
public override void UpdateValue()
|
public override void UpdateValue()
|
||||||
{
|
{
|
||||||
base.UpdateValue();
|
//base.UpdateValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
public override void DrawValue(Rect window, float width)
|
||||||
@ -136,12 +136,12 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GUILayout.Label($"null (<color=yellow>{ValueType}</color>)", null);
|
GUILayout.Label($"null (<color=yellow>{ValueTypeName}</color>)", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=yellow>{ValueType}</color>)", null);
|
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=yellow>{ValueTypeName}</color>)", null);
|
||||||
}
|
}
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ namespace Explorer
|
|||||||
|
|
||||||
private void Evaluate()
|
private void Evaluate()
|
||||||
{
|
{
|
||||||
var mi = MemberInfo as MethodInfo;
|
var mi = MemInfo as MethodInfo;
|
||||||
|
|
||||||
object ret = null;
|
object ret = null;
|
||||||
|
|
||||||
|
@ -13,54 +13,70 @@ namespace Explorer
|
|||||||
public abstract class CacheObjectBase
|
public abstract class CacheObjectBase
|
||||||
{
|
{
|
||||||
public object Value;
|
public object Value;
|
||||||
public string ValueType;
|
public string ValueTypeName;
|
||||||
|
|
||||||
// Reflection window only
|
// Reflection Inspector only
|
||||||
public MemberInfo MemberInfo { get; set; }
|
public MemberInfo MemInfo { get; set; }
|
||||||
public Type DeclaringType { get; set; }
|
public Type DeclaringType { get; set; }
|
||||||
public object DeclaringInstance { get; set; }
|
public object DeclaringInstance { get; set; }
|
||||||
public string FullName => $"{MemberInfo.DeclaringType.Name}.{MemberInfo.Name}";
|
public string ReflectionException { get; set; }
|
||||||
public string ReflectionException;
|
|
||||||
|
public int PropertyIndex { get; private set; }
|
||||||
|
private string m_propertyIndexInput = "0";
|
||||||
|
|
||||||
|
public string RichTextName => m_richTextName ?? GetRichTextName();
|
||||||
|
private string m_richTextName;
|
||||||
|
|
||||||
public bool CanWrite
|
public bool CanWrite
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (MemberInfo is FieldInfo fi)
|
if (MemInfo is FieldInfo fi)
|
||||||
{
|
|
||||||
return !(fi.IsLiteral && !fi.IsInitOnly);
|
return !(fi.IsLiteral && !fi.IsInitOnly);
|
||||||
}
|
else if (MemInfo is PropertyInfo pi)
|
||||||
else if (MemberInfo is PropertyInfo pi)
|
|
||||||
{
|
|
||||||
return pi.CanWrite;
|
return pi.CanWrite;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// methods
|
// ===== Abstract/Virtual Methods ===== //
|
||||||
|
|
||||||
public virtual void Init() { }
|
public virtual void Init() { }
|
||||||
|
|
||||||
public abstract void DrawValue(Rect window, float width);
|
public abstract void DrawValue(Rect window, float width);
|
||||||
|
|
||||||
|
// ===== Static Methods ===== //
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get CacheObject from only an object instance
|
||||||
|
/// Calls GetCacheObject(obj, memberInfo, declaringInstance) with (obj, null, null)</summary>
|
||||||
public static CacheObjectBase GetCacheObject(object obj)
|
public static CacheObjectBase GetCacheObject(object obj)
|
||||||
{
|
{
|
||||||
return GetCacheObject(obj, null, null);
|
return GetCacheObject(obj, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the CacheObject subclass for an object or MemberInfo
|
/// Get CacheObject from an object instance and provide the value type
|
||||||
/// </summary>
|
/// Calls GetCacheObjectImpl directly</summary>
|
||||||
/// <param name="obj">The current value (can be null if memberInfo is not null)</param>
|
public static CacheObjectBase GetCacheObject(object obj, Type valueType)
|
||||||
/// <param name="memberInfo">The MemberInfo (can be null if obj is not null)</param>
|
{
|
||||||
/// <param name="declaringInstance">If MemberInfo is not null, the declaring class instance. Can be null if static.</param>
|
return GetCacheObjectImpl(obj, null, null, valueType);
|
||||||
/// <returns></returns>
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get CacheObject from only a MemberInfo and declaring instance
|
||||||
|
/// Calls GetCacheObject(obj, memberInfo, declaringInstance) with (null, memberInfo, declaringInstance)</summary>
|
||||||
|
public static CacheObjectBase GetCacheObject(MemberInfo memberInfo, object declaringInstance)
|
||||||
|
{
|
||||||
|
return GetCacheObject(null, memberInfo, declaringInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get CacheObject from either an object or MemberInfo, and don't provide the type.
|
||||||
|
/// This gets the type and then calls GetCacheObjectImpl</summary>
|
||||||
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance)
|
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance)
|
||||||
{
|
{
|
||||||
//var type = ReflectionHelpers.GetActualType(obj) ?? (memberInfo as FieldInfo)?.FieldType ?? (memberInfo as PropertyInfo)?.PropertyType;
|
|
||||||
|
|
||||||
Type type = null;
|
Type type = null;
|
||||||
|
|
||||||
if (obj != null)
|
if (obj != null)
|
||||||
@ -85,26 +101,16 @@ namespace Explorer
|
|||||||
|
|
||||||
if (type == null)
|
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 null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetCacheObject(obj, memberInfo, declaringInstance, type);
|
return GetCacheObjectImpl(obj, memberInfo, declaringInstance, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the CacheObject subclass for an object or MemberInfo
|
/// Actual GetCacheObject implementation (private)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The current value (can be null if memberInfo is not null)</param>
|
private static CacheObjectBase GetCacheObjectImpl(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
|
||||||
/// <param name="memberInfo">The MemberInfo (can be null if obj is not null)</param>
|
|
||||||
/// <param name="declaringInstance">If MemberInfo is not null, the declaring class instance. Can be null if static.</param>
|
|
||||||
/// <param name="valueType">The type of the object or MemberInfo value.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
|
|
||||||
{
|
{
|
||||||
CacheObjectBase holder;
|
CacheObjectBase holder;
|
||||||
|
|
||||||
@ -145,11 +151,11 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
|
|
||||||
holder.Value = obj;
|
holder.Value = obj;
|
||||||
holder.ValueType = valueType.FullName;
|
holder.ValueTypeName = valueType.FullName;
|
||||||
|
|
||||||
if (memberInfo != null)
|
if (memberInfo != null)
|
||||||
{
|
{
|
||||||
holder.MemberInfo = memberInfo;
|
holder.MemInfo = memberInfo;
|
||||||
holder.DeclaringType = memberInfo.DeclaringType;
|
holder.DeclaringType = memberInfo.DeclaringType;
|
||||||
holder.DeclaringInstance = declaringInstance;
|
holder.DeclaringInstance = declaringInstance;
|
||||||
|
|
||||||
@ -161,6 +167,79 @@ namespace Explorer
|
|||||||
return holder;
|
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 const float MAX_LABEL_WIDTH = 400f;
|
||||||
|
|
||||||
public static void ClampLabelWidth(Rect window, ref float labelWidth)
|
public static void ClampLabelWidth(Rect window, ref float labelWidth)
|
||||||
@ -178,9 +257,15 @@ namespace Explorer
|
|||||||
ClampLabelWidth(window, ref labelWidth);
|
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
|
else
|
||||||
{
|
{
|
||||||
@ -191,64 +276,70 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null);
|
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null);
|
||||||
}
|
}
|
||||||
else if (Value == null && MemberInfo?.MemberType != MemberTypes.Method)
|
else if (Value == null && MemInfo?.MemberType != MemberTypes.Method)
|
||||||
{
|
{
|
||||||
GUILayout.Label("<i>null (" + ValueType + ")</i>", null);
|
GUILayout.Label("<i>null (" + ValueTypeName + ")</i>", null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (MemInfo is PropertyInfo pi && pi.GetIndexParameters().Length > 0)
|
||||||
|
{
|
||||||
|
GUILayout.Label("index:", new GUILayoutOption[] { GUILayout.Width(50) });
|
||||||
|
|
||||||
|
m_propertyIndexInput = GUILayout.TextField(m_propertyIndexInput, new GUILayoutOption[] { GUILayout.Width(100) });
|
||||||
|
if (GUILayout.Button("Set", new GUILayoutOption[] { GUILayout.Width(60) }))
|
||||||
|
{
|
||||||
|
if (int.TryParse(m_propertyIndexInput, out int i))
|
||||||
|
{
|
||||||
|
PropertyIndex = i;
|
||||||
|
UpdateValue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MelonLogger.Log($"Could not parse '{m_propertyIndexInput}' to an int!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// new line and space
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
GUILayout.BeginHorizontal(null);
|
||||||
|
GUILayout.Space(labelWidth);
|
||||||
|
}
|
||||||
|
|
||||||
DrawValue(window, window.width - labelWidth - 90);
|
DrawValue(window, window.width - labelWidth - 90);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
return m_richTextName;
|
||||||
{
|
|
||||||
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}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,33 +12,25 @@ namespace Explorer
|
|||||||
public class CacheOther : CacheObjectBase
|
public class CacheOther : CacheObjectBase
|
||||||
{
|
{
|
||||||
private MethodInfo m_toStringMethod;
|
private MethodInfo m_toStringMethod;
|
||||||
private bool m_triedToGetMethod;
|
|
||||||
|
|
||||||
public MethodInfo ToStringMethod
|
public MethodInfo ToStringMethod
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (m_toStringMethod == null && !m_triedToGetMethod)
|
if (m_toStringMethod == null)
|
||||||
{
|
{
|
||||||
if (Value == null) return null;
|
|
||||||
|
|
||||||
m_triedToGetMethod = true;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var methods = ReflectionHelpers.GetActualType(Value)
|
m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0])
|
||||||
.GetMethods(ReflectionHelpers.CommonFlags)
|
?? typeof(object).GetMethod("ToString", new Type[0]);
|
||||||
.Where(x => x.Name == "ToString")
|
|
||||||
.GetEnumerator();
|
|
||||||
|
|
||||||
while (methods.MoveNext())
|
// test invoke
|
||||||
{
|
m_toStringMethod.Invoke(Value, null);
|
||||||
// just get the first (top-most level) method, then break.
|
}
|
||||||
m_toStringMethod = methods.Current;
|
catch
|
||||||
break;
|
{
|
||||||
}
|
m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]);
|
||||||
}
|
}
|
||||||
catch { }
|
|
||||||
}
|
}
|
||||||
return m_toStringMethod;
|
return m_toStringMethod;
|
||||||
}
|
}
|
||||||
@ -48,9 +40,9 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
|
string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
|
||||||
|
|
||||||
if (!label.Contains(ValueType))
|
if (!label.Contains(ValueTypeName))
|
||||||
{
|
{
|
||||||
label += $" ({ValueType})";
|
label += $" ({ValueTypeName})";
|
||||||
}
|
}
|
||||||
if (Value is UnityEngine.Object unityObj && !label.Contains(unityObj.name))
|
if (Value is UnityEngine.Object unityObj && !label.Contains(unityObj.name))
|
||||||
{
|
{
|
||||||
@ -58,7 +50,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
|
|
||||||
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
||||||
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(width + 40) }))
|
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.Width(width) }))
|
||||||
{
|
{
|
||||||
WindowManager.InspectObject(Value, out bool _);
|
WindowManager.InspectObject(Value, out bool _);
|
||||||
}
|
}
|
||||||
|
@ -28,21 +28,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (m_parseMethod == null)
|
if (m_parseMethod == null)
|
||||||
{
|
{
|
||||||
Type t = null;
|
m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) });
|
||||||
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) });
|
|
||||||
}
|
}
|
||||||
return m_parseMethod;
|
return m_parseMethod;
|
||||||
}
|
}
|
||||||
@ -127,7 +113,7 @@ namespace Explorer
|
|||||||
b = GUILayout.Toggle(b, label, null);
|
b = GUILayout.Toggle(b, label, null);
|
||||||
if (b != (bool)Value)
|
if (b != (bool)Value)
|
||||||
{
|
{
|
||||||
SetValue(m_valueToString);
|
SetValueFromInput(b.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -156,7 +142,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))
|
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!");
|
MelonLogger.Log("Trying to SetValue but the MemberInfo is null!");
|
||||||
return;
|
return;
|
||||||
|
@ -12,7 +12,7 @@ namespace Explorer
|
|||||||
public class CppExplorer : MelonMod
|
public class CppExplorer : MelonMod
|
||||||
{
|
{
|
||||||
public const string GUID = "com.sinai.cppexplorer";
|
public const string GUID = "com.sinai.cppexplorer";
|
||||||
public const string VERSION = "1.4.5";
|
public const string VERSION = "1.5.2";
|
||||||
public const string AUTHOR = "Sinai";
|
public const string AUTHOR = "Sinai";
|
||||||
|
|
||||||
public const string NAME = "CppExplorer"
|
public const string NAME = "CppExplorer"
|
||||||
@ -133,7 +133,7 @@ namespace Explorer
|
|||||||
// value that we set back to when we close the menu or disable force-unlock.
|
// value that we set back to when we close the menu or disable force-unlock.
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Setter)]
|
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Setter)]
|
||||||
public class Cursor_lockState
|
public class Cursor_set_lockState
|
||||||
{
|
{
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
public static void Prefix(ref CursorLockMode value)
|
public static void Prefix(ref CursorLockMode value)
|
||||||
@ -170,19 +170,6 @@ namespace Explorer
|
|||||||
|
|
||||||
// Make it appear as though UnlockMouse is disabled to the rest of the application.
|
// Make it appear as though UnlockMouse is disabled to the rest of the application.
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Getter)]
|
|
||||||
public class Cursor_get_visible
|
|
||||||
{
|
|
||||||
[HarmonyPostfix]
|
|
||||||
public static void Postfix(ref bool __result)
|
|
||||||
{
|
|
||||||
if (ShouldForceMouse)
|
|
||||||
{
|
|
||||||
__result = m_lastVisibleState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Getter)]
|
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Getter)]
|
||||||
public class Cursor_get_lockState
|
public class Cursor_get_lockState
|
||||||
{
|
{
|
||||||
@ -195,5 +182,18 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Getter)]
|
||||||
|
public class Cursor_get_visible
|
||||||
|
{
|
||||||
|
[HarmonyPostfix]
|
||||||
|
public static void Postfix(ref bool __result)
|
||||||
|
{
|
||||||
|
if (ShouldForceMouse)
|
||||||
|
{
|
||||||
|
__result = m_lastVisibleState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,6 +130,7 @@
|
|||||||
<Compile Include="CppExplorer.cs" />
|
<Compile Include="CppExplorer.cs" />
|
||||||
<Compile Include="Extensions\ReflectionExtensions.cs" />
|
<Compile Include="Extensions\ReflectionExtensions.cs" />
|
||||||
<Compile Include="Extensions\UnityExtensions.cs" />
|
<Compile Include="Extensions\UnityExtensions.cs" />
|
||||||
|
<Compile Include="Helpers\PageHelper.cs" />
|
||||||
<Compile Include="Helpers\ReflectionHelpers.cs" />
|
<Compile Include="Helpers\ReflectionHelpers.cs" />
|
||||||
<Compile Include="Helpers\UIHelpers.cs" />
|
<Compile Include="Helpers\UIHelpers.cs" />
|
||||||
<Compile Include="Helpers\UnityHelpers.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 UnhollowerRuntimeLib;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using BF = System.Reflection.BindingFlags;
|
using BF = System.Reflection.BindingFlags;
|
||||||
|
using MelonLoader;
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
@ -27,8 +28,9 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj;
|
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj;
|
||||||
|
|
||||||
var generic = m_tryCastMethodInfo.MakeGenericMethod(castTo);
|
return m_tryCastMethodInfo
|
||||||
return generic.Invoke(obj, null);
|
.MakeGenericMethod(castTo)
|
||||||
|
.Invoke(obj, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ExceptionToString(Exception e)
|
public static string ExceptionToString(Exception e)
|
||||||
@ -79,9 +81,17 @@ namespace Explorer
|
|||||||
|
|
||||||
public static bool IsList(Type t)
|
public static bool IsList(Type t)
|
||||||
{
|
{
|
||||||
return t.IsGenericType
|
if (t.IsGenericType)
|
||||||
&& t.GetGenericTypeDefinition() is Type typeDef
|
{
|
||||||
&& (typeDef.IsAssignableFrom(typeof(List<>)) || typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>)));
|
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)
|
public static bool IsDictionary(Type t)
|
||||||
@ -108,51 +118,36 @@ namespace Explorer
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Type GetActualType(object m_object)
|
public static Type GetActualType(object obj)
|
||||||
{
|
{
|
||||||
if (m_object == null) return null;
|
if (obj == null) return null;
|
||||||
|
|
||||||
if (m_object is Il2CppSystem.Object ilObject)
|
if (obj is Il2CppSystem.Object ilObject)
|
||||||
{
|
{
|
||||||
var iltype = ilObject.GetIl2CppType();
|
var ilTypeName = ilObject.GetIl2CppType().AssemblyQualifiedName;
|
||||||
return Type.GetType(iltype.AssemblyQualifiedName);
|
|
||||||
}
|
if (Type.GetType(ilTypeName) is Type t && !t.FullName.Contains("System.RuntimeType"))
|
||||||
else
|
{
|
||||||
{
|
return t;
|
||||||
return m_object.GetType();
|
}
|
||||||
|
|
||||||
|
return ilObject.GetType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return obj.GetType();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Type[] GetAllBaseTypes(object m_object)
|
public static Type[] GetAllBaseTypes(object obj)
|
||||||
{
|
{
|
||||||
var list = new List<Type>();
|
var list = new List<Type>();
|
||||||
|
|
||||||
if (m_object is Il2CppSystem.Object ilObject)
|
var type = GetActualType(obj);
|
||||||
{
|
list.Add(type);
|
||||||
var ilType = ilObject.GetIl2CppType();
|
|
||||||
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilTypeToManaged)
|
|
||||||
{
|
|
||||||
list.Add(ilTypeToManaged);
|
|
||||||
|
|
||||||
while (ilType.BaseType != null)
|
while (type.BaseType != null)
|
||||||
{
|
|
||||||
ilType = ilType.BaseType;
|
|
||||||
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilBaseTypeToManaged)
|
|
||||||
{
|
|
||||||
list.Add(ilBaseTypeToManaged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
var type = m_object.GetType();
|
type = type.BaseType;
|
||||||
list.Add(type);
|
list.Add(type);
|
||||||
while (type.BaseType != null)
|
|
||||||
{
|
|
||||||
type = type.BaseType;
|
|
||||||
list.Add(type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return list.ToArray();
|
return list.ToArray();
|
||||||
|
@ -22,8 +22,10 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// helper for drawing a styled button for a GameObject or Transform
|
// 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;
|
bool children = obj.transform.childCount > 0;
|
||||||
|
|
||||||
string label = children ? "[" + obj.transform.childCount + " children] " : "";
|
string label = children ? "[" + obj.transform.childCount + " children] " : "";
|
||||||
@ -49,11 +51,13 @@ namespace Explorer
|
|||||||
color = Color.red;
|
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)
|
if (!obj)
|
||||||
{
|
{
|
||||||
GUILayout.Label("<i><color=red>null</color></i>", null);
|
GUILayout.Label("<i><color=red>null</color></i>", null);
|
||||||
@ -83,7 +87,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WindowManager.InspectObject(obj, out bool _);
|
WindowManager.InspectObject(_obj, out bool _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +98,7 @@ namespace Explorer
|
|||||||
|
|
||||||
if (showSmallInspectBtn)
|
if (showSmallInspectBtn)
|
||||||
{
|
{
|
||||||
SmallInspectButton(obj);
|
SmallInspectButton(_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
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;
|
var orig = GUI.color;
|
||||||
GUI.color = color;
|
|
||||||
GUILayout.Box(GUIContent.none, HorizontalBar, null);
|
GUI.color = _color;
|
||||||
GUI.color = c;
|
GUILayout.Box(GUIContent.none, !small ? HorizontalBar : HorizontalBarSmall, null);
|
||||||
|
|
||||||
|
GUI.color = orig;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GUISkin _customSkin;
|
private static GUISkin _customSkin;
|
||||||
@ -46,8 +48,6 @@ namespace Explorer
|
|||||||
public static Texture2D m_nofocusTex;
|
public static Texture2D m_nofocusTex;
|
||||||
public static Texture2D m_focusTex;
|
public static Texture2D m_focusTex;
|
||||||
|
|
||||||
private static GUIStyle _horizBarStyle;
|
|
||||||
|
|
||||||
private static GUIStyle HorizontalBar
|
private static GUIStyle HorizontalBar
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -63,6 +63,24 @@ namespace Explorer
|
|||||||
return _horizBarStyle;
|
return _horizBarStyle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private static GUIStyle _horizBarStyle;
|
||||||
|
|
||||||
|
private static GUIStyle HorizontalBarSmall
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_horizBarSmallStyle == null)
|
||||||
|
{
|
||||||
|
_horizBarSmallStyle = new GUIStyle();
|
||||||
|
_horizBarSmallStyle.normal.background = Texture2D.whiteTexture;
|
||||||
|
_horizBarSmallStyle.margin = new RectOffset(0, 0, 2, 2);
|
||||||
|
_horizBarSmallStyle.fixedHeight = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _horizBarSmallStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static GUIStyle _horizBarSmallStyle;
|
||||||
|
|
||||||
private static GUISkin CreateWindowSkin()
|
private static GUISkin CreateWindowSkin()
|
||||||
{
|
{
|
||||||
|
@ -14,9 +14,7 @@ namespace Explorer
|
|||||||
|
|
||||||
public override string Name { get => "Scene Explorer"; set => base.Name = value; }
|
public override string Name { get => "Scene Explorer"; set => base.Name = value; }
|
||||||
|
|
||||||
private int m_pageOffset = 0;
|
public PageHelper Pages = new PageHelper();
|
||||||
private int m_limit = 20;
|
|
||||||
private int m_currentTotalCount = 0;
|
|
||||||
|
|
||||||
private float m_timeOfLastUpdate = -1f;
|
private float m_timeOfLastUpdate = -1f;
|
||||||
|
|
||||||
@ -46,14 +44,14 @@ namespace Explorer
|
|||||||
SetTransformTarget(null);
|
SetTransformTarget(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckOffset(ref int offset, int childCount)
|
//public void CheckOffset(ref int offset, int childCount)
|
||||||
{
|
//{
|
||||||
if (offset >= childCount)
|
// if (offset >= childCount)
|
||||||
{
|
// {
|
||||||
offset = 0;
|
// offset = 0;
|
||||||
m_pageOffset = 0;
|
// m_pageOffset = 0;
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
@ -63,7 +61,6 @@ namespace Explorer
|
|||||||
m_timeOfLastUpdate = Time.time;
|
m_timeOfLastUpdate = Time.time;
|
||||||
|
|
||||||
m_objectList = new List<GameObjectCache>();
|
m_objectList = new List<GameObjectCache>();
|
||||||
int offset = m_pageOffset * m_limit;
|
|
||||||
|
|
||||||
var allTransforms = new List<Transform>();
|
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
|
int offset = Pages.CalculateOffsetIndex();
|
||||||
CheckOffset(ref offset, m_currentTotalCount);
|
|
||||||
|
|
||||||
// sort by childcount
|
// sort by childcount
|
||||||
allTransforms.Sort((a, b) => b.childCount.CompareTo(a.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];
|
var child = allTransforms[i];
|
||||||
m_objectList.Add(new GameObjectCache(child.gameObject));
|
m_objectList.Add(new GameObjectCache(child.gameObject));
|
||||||
@ -128,7 +124,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
m_searchResults = SearchSceneObjects(m_searchInput);
|
m_searchResults = SearchSceneObjects(m_searchInput);
|
||||||
m_searching = true;
|
m_searching = true;
|
||||||
m_currentTotalCount = m_searchResults.Count;
|
Pages.Count = m_searchResults.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CancelSearch()
|
public void CancelSearch()
|
||||||
@ -242,33 +238,21 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
|
|
||||||
GUILayout.Label("Limit per page: ", new GUILayoutOption[] { GUILayout.Width(100) });
|
Pages.DrawLimitInputArea();
|
||||||
var limit = m_limit.ToString();
|
|
||||||
limit = GUILayout.TextField(limit, new GUILayoutOption[] { GUILayout.Width(30) });
|
|
||||||
if (int.TryParse(limit, out int lim))
|
|
||||||
{
|
|
||||||
m_limit = lim;
|
|
||||||
}
|
|
||||||
|
|
||||||
// prev/next page buttons
|
if (Pages.Count > Pages.PageLimit)
|
||||||
if (m_currentTotalCount > m_limit)
|
|
||||||
{
|
{
|
||||||
int count = m_currentTotalCount;
|
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||||
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)m_limit)) - 1;
|
|
||||||
if (GUILayout.Button("< Prev", null))
|
|
||||||
{
|
{
|
||||||
if (m_pageOffset > 0) m_pageOffset--;
|
Pages.TurnPage(Turn.Left, ref this.scroll);
|
||||||
m_timeOfLastUpdate = -1f;
|
|
||||||
Update();
|
Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
Pages.CurrentPageLabel();
|
||||||
GUILayout.Label($"Page {m_pageOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
|
|
||||||
|
|
||||||
if (GUILayout.Button("Next >", null))
|
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||||
{
|
{
|
||||||
if (m_pageOffset < maxOffset) m_pageOffset++;
|
Pages.TurnPage(Turn.Right, ref this.scroll);
|
||||||
m_timeOfLastUpdate = -1f;
|
|
||||||
Update();
|
Update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -342,19 +326,26 @@ namespace Explorer
|
|||||||
|
|
||||||
if (m_searchResults.Count > 0)
|
if (m_searchResults.Count > 0)
|
||||||
{
|
{
|
||||||
int offset = m_pageOffset * m_limit;
|
int offset = Pages.CalculateOffsetIndex();
|
||||||
|
|
||||||
if (offset >= m_searchResults.Count)
|
for (int i = offset; i < offset + Pages.PageLimit && i < m_searchResults.Count; i++)
|
||||||
{
|
|
||||||
offset = 0;
|
|
||||||
m_pageOffset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = offset; i < offset + m_limit && offset < m_searchResults.Count; i++)
|
|
||||||
{
|
{
|
||||||
var obj = m_searchResults[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
|
else
|
||||||
|
@ -5,11 +5,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
using Object = UnityEngine.Object;
|
|
||||||
using UnhollowerRuntimeLib;
|
|
||||||
using MelonLoader;
|
using MelonLoader;
|
||||||
using UnhollowerBaseLib;
|
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
@ -21,11 +17,11 @@ namespace Explorer
|
|||||||
|
|
||||||
private string m_searchInput = "";
|
private string m_searchInput = "";
|
||||||
private string m_typeInput = "";
|
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;
|
private Vector2 resultsScroll = Vector2.zero;
|
||||||
|
|
||||||
|
public PageHelper Pages = new PageHelper();
|
||||||
|
|
||||||
private List<CacheObjectBase> m_searchResults = new List<CacheObjectBase>();
|
private List<CacheObjectBase> m_searchResults = new List<CacheObjectBase>();
|
||||||
|
|
||||||
public SceneFilter SceneMode = SceneFilter.Any;
|
public SceneFilter SceneMode = SceneFilter.Any;
|
||||||
@ -55,7 +51,7 @@ namespace Explorer
|
|||||||
public void OnSceneChange()
|
public void OnSceneChange()
|
||||||
{
|
{
|
||||||
m_searchResults.Clear();
|
m_searchResults.Clear();
|
||||||
m_pageOffset = 0;
|
Pages.PageOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
@ -78,6 +74,9 @@ namespace Explorer
|
|||||||
var cache = CacheObjectBase.GetCacheObject(toCache);
|
var cache = CacheObjectBase.GetCacheObject(toCache);
|
||||||
m_searchResults.Add(cache);
|
m_searchResults.Add(cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Pages.Count = m_searchResults.Count;
|
||||||
|
Pages.PageOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void DrawWindow()
|
public override void DrawWindow()
|
||||||
@ -90,8 +89,7 @@ namespace Explorer
|
|||||||
if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) }))
|
if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) }))
|
||||||
{
|
{
|
||||||
//m_searchResults = GetInstanceClassScanner().ToList();
|
//m_searchResults = GetInstanceClassScanner().ToList();
|
||||||
CacheResults(GetInstanceClassScanner());
|
CacheResults(GetInstanceClassScanner());
|
||||||
m_pageOffset = 0;
|
|
||||||
}
|
}
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
@ -106,36 +104,45 @@ namespace Explorer
|
|||||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||||
|
|
||||||
int count = m_searchResults.Count;
|
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
|
// 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 (Pages.Count > Pages.PageLimit)
|
||||||
|
|
||||||
if (GUILayout.Button("Next >", null))
|
|
||||||
{
|
{
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
resultsScroll = GUILayout.BeginScrollView(resultsScroll, GUI.skin.scrollView);
|
resultsScroll = GUILayout.BeginScrollView(resultsScroll, GUI.skin.scrollView);
|
||||||
|
|
||||||
var _temprect = new Rect(MainMenu.MainRect.x, MainMenu.MainRect.y, MainMenu.MainRect.width + 160, MainMenu.MainRect.height);
|
var _temprect = new Rect(MainMenu.MainRect.x, MainMenu.MainRect.y, MainMenu.MainRect.width + 160, MainMenu.MainRect.height);
|
||||||
|
|
||||||
if (m_searchResults.Count > 0)
|
if (m_searchResults.Count > 0)
|
||||||
{
|
{
|
||||||
int offset = m_pageOffset * this.m_limit;
|
//int offset = m_pageOffset * this.m_limit;
|
||||||
if (offset >= count) m_pageOffset = 0;
|
//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].Draw(MainMenu.MainRect, 0f);
|
||||||
//m_searchResults[i].DrawValue(MainMenu.MainRect);
|
//m_searchResults[i].DrawValue(MainMenu.MainRect);
|
||||||
@ -169,15 +176,15 @@ namespace Explorer
|
|||||||
GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) });
|
GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) });
|
||||||
m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) });
|
m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) });
|
||||||
|
|
||||||
GUI.skin.label.alignment = TextAnchor.MiddleRight;
|
//GUI.skin.label.alignment = TextAnchor.MiddleRight;
|
||||||
GUILayout.Label("Results per page:", new GUILayoutOption[] { GUILayout.Width(120) });
|
//GUILayout.Label("Results per page:", new GUILayoutOption[] { GUILayout.Width(120) });
|
||||||
var resultinput = m_limit.ToString();
|
//var resultinput = m_limit.ToString();
|
||||||
resultinput = GUILayout.TextField(resultinput, new GUILayoutOption[] { GUILayout.Width(55) });
|
//resultinput = GUILayout.TextField(resultinput, new GUILayoutOption[] { GUILayout.Width(55) });
|
||||||
if (int.TryParse(resultinput, out int _i) && _i > 0)
|
//if (int.TryParse(resultinput, out int _i) && _i > 0)
|
||||||
{
|
//{
|
||||||
m_limit = _i;
|
// m_limit = _i;
|
||||||
}
|
//}
|
||||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
//GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
@ -256,7 +263,7 @@ namespace Explorer
|
|||||||
|
|
||||||
private void Search()
|
private void Search()
|
||||||
{
|
{
|
||||||
m_pageOffset = 0;
|
Pages.PageOffset = 0;
|
||||||
CacheResults(FindAllObjectsOfType(m_searchInput, m_typeInput));
|
CacheResults(FindAllObjectsOfType(m_searchInput, m_typeInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,11 +21,13 @@ namespace Explorer
|
|||||||
private string m_name;
|
private string m_name;
|
||||||
private string m_scene;
|
private string m_scene;
|
||||||
|
|
||||||
private Vector2 m_transformScroll = Vector2.zero;
|
|
||||||
private Transform[] m_children;
|
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 Vector2 m_compScroll = Vector2.zero;
|
||||||
|
private PageHelper CompPages = new PageHelper();
|
||||||
|
|
||||||
private float m_translateAmount = 0.3f;
|
private float m_translateAmount = 0.3f;
|
||||||
private float m_rotateAmount = 50f;
|
private float m_rotateAmount = 50f;
|
||||||
@ -74,12 +76,7 @@ namespace Explorer
|
|||||||
? "None"
|
? "None"
|
||||||
: m_object.scene.name;
|
: m_object.scene.name;
|
||||||
|
|
||||||
var list = new List<Transform>();
|
Update();
|
||||||
for (int i = 0; i < m_object.transform.childCount; i++)
|
|
||||||
{
|
|
||||||
list.Add(m_object.transform.GetChild(i));
|
|
||||||
}
|
|
||||||
m_children = list.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
@ -91,12 +88,30 @@ namespace Explorer
|
|||||||
throw new Exception("Object is null!");
|
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))
|
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)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -160,12 +175,16 @@ namespace Explorer
|
|||||||
GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", null);
|
GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", null);
|
||||||
if (m_scene == UnityHelpers.ActiveSceneName)
|
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);
|
ScenePage.Instance.SetTransformTarget(m_object.transform);
|
||||||
MainMenu.SetCurrentPage(0);
|
MainMenu.SetCurrentPage(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (GUILayout.Button("Reflection Inspect", new GUILayoutOption[] { GUILayout.Width(150) }))
|
||||||
|
{
|
||||||
|
WindowManager.InspectObject(Target, out _, true);
|
||||||
|
}
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
@ -221,26 +240,46 @@ namespace Explorer
|
|||||||
GUILayout.BeginVertical(GUI.skin.box, null); // new GUILayoutOption[] { GUILayout.Height(250) });
|
GUILayout.BeginVertical(GUI.skin.box, null); // new GUILayoutOption[] { GUILayout.Height(250) });
|
||||||
m_transformScroll = GUILayout.BeginScrollView(m_transformScroll, GUI.skin.scrollView);
|
m_transformScroll = GUILayout.BeginScrollView(m_transformScroll, GUI.skin.scrollView);
|
||||||
|
|
||||||
GUILayout.Label("<b>Children:</b>", null);
|
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();
|
||||||
|
|
||||||
if (m_children != null && m_children.Length > 0)
|
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)
|
if (!obj)
|
||||||
{
|
{
|
||||||
GUILayout.Label("null", null);
|
GUILayout.Label("null", null);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
UIHelpers.GameobjButton(obj.gameObject, InspectGameObject, false, m_rect.width / 2 - 60);
|
|
||||||
}
|
UIHelpers.GameobjButton(obj.gameObject, InspectGameObject, false, m_rect.width / 2 - 80);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -252,13 +291,35 @@ namespace Explorer
|
|||||||
GUILayout.EndVertical();
|
GUILayout.EndVertical();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void ComponentList(Rect m_rect)
|
private void ComponentList(Rect m_rect)
|
||||||
{
|
{
|
||||||
GUILayout.BeginVertical(GUI.skin.box, null); // new GUILayoutOption[] { GUILayout.Height(250) });
|
GUILayout.BeginVertical(GUI.skin.box, null); // new GUILayoutOption[] { GUILayout.Height(250) });
|
||||||
m_compScroll = GUILayout.BeginScrollView(m_compScroll, GUI.skin.scrollView);
|
m_compScroll = GUILayout.BeginScrollView(m_compScroll, GUI.skin.scrollView);
|
||||||
GUILayout.Label("<b><size=15>Components</size></b>", null);
|
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;
|
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
||||||
if (m_cachedDestroyList.Count > 0)
|
if (m_cachedDestroyList.Count > 0)
|
||||||
{
|
{
|
||||||
@ -267,15 +328,15 @@ namespace Explorer
|
|||||||
|
|
||||||
if (m_components != null)
|
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;
|
if (!component) continue;
|
||||||
|
|
||||||
var ilType = component.GetIl2CppType();
|
var ilType = component.GetIl2CppType();
|
||||||
if (ilType == ReflectionHelpers.TransformType)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
if (ReflectionHelpers.BehaviourType.IsAssignableFrom(ilType))
|
if (ReflectionHelpers.BehaviourType.IsAssignableFrom(ilType))
|
||||||
@ -286,7 +347,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
GUILayout.Space(26);
|
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);
|
ReflectObject(component);
|
||||||
}
|
}
|
||||||
|
@ -13,15 +13,17 @@ namespace Explorer
|
|||||||
public class ReflectionWindow : UIWindow
|
public class ReflectionWindow : UIWindow
|
||||||
{
|
{
|
||||||
public override string Title => WindowManager.TabView
|
public override string Title => WindowManager.TabView
|
||||||
? $"<color=cyan>[R]</color> {ObjectType.Name}"
|
? $"<color=cyan>[R]</color> {TargetType.Name}"
|
||||||
: $"Reflection Inspector ({ObjectType.Name})";
|
: $"Reflection Inspector ({TargetType.Name})";
|
||||||
|
|
||||||
public Type ObjectType;
|
public Type TargetType;
|
||||||
|
|
||||||
private CacheObjectBase[] m_allCachedMembers;
|
private CacheObjectBase[] m_allCachedMembers;
|
||||||
private CacheObjectBase[] m_cachedMembersFiltered;
|
private CacheObjectBase[] m_cachedMembersFiltered;
|
||||||
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 bool m_autoUpdate = false;
|
||||||
private string m_search = "";
|
private string m_search = "";
|
||||||
@ -35,16 +37,11 @@ namespace Explorer
|
|||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
var type = ReflectionHelpers.GetActualType(Target);
|
var type = ReflectionHelpers.GetActualType(Target);
|
||||||
if (type == null)
|
|
||||||
{
|
|
||||||
MelonLogger.Log($"Could not get underlying type for object..? Type: {Target?.GetType().Name}, ToString: {Target?.ToString()}");
|
|
||||||
DestroyWindow();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectType = type;
|
TargetType = type;
|
||||||
|
|
||||||
var types = ReflectionHelpers.GetAllBaseTypes(Target);
|
var types = ReflectionHelpers.GetAllBaseTypes(Target);
|
||||||
|
|
||||||
CacheMembers(types);
|
CacheMembers(types);
|
||||||
|
|
||||||
if (Target is Il2CppSystem.Object ilObject)
|
if (Target is Il2CppSystem.Object ilObject)
|
||||||
@ -90,15 +87,15 @@ namespace Explorer
|
|||||||
|
|
||||||
private bool ShouldProcessMember(CacheObjectBase holder)
|
private bool ShouldProcessMember(CacheObjectBase holder)
|
||||||
{
|
{
|
||||||
if (m_filter != MemberTypes.All && m_filter != holder.MemberInfo?.MemberType) return false;
|
if (m_filter != MemberTypes.All && m_filter != holder.MemInfo?.MemberType) return false;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) return false;
|
if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) return false;
|
||||||
|
|
||||||
if (m_search == "" || holder.MemberInfo == null) return true;
|
if (m_search == "" || holder.MemInfo == null) return true;
|
||||||
|
|
||||||
return holder.FullName
|
var name = holder.MemInfo.DeclaringType.Name + "." + holder.MemInfo.Name;
|
||||||
.ToLower()
|
|
||||||
.Contains(m_search.ToLower());
|
return name.ToLower().Contains(m_search.ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CacheMembers(Type[] types)
|
private void CacheMembers(Type[] types)
|
||||||
@ -118,7 +115,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
MelonLogger.Log("Exception getting members for type: " + declaringType.Name);
|
MelonLogger.Log($"Exception getting members for type: {declaringType.FullName}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,25 +137,41 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Method)
|
if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Method)
|
||||||
{
|
{
|
||||||
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;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (member is MethodInfo mi)
|
||||||
|
{
|
||||||
|
name += " (";
|
||||||
|
foreach (var param in mi.GetParameters())
|
||||||
|
{
|
||||||
|
name += param.ParameterType.Name + ", ";
|
||||||
|
}
|
||||||
|
name += ")";
|
||||||
|
}
|
||||||
|
if (names.Contains(name))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var name = member.DeclaringType.Name + "." + member.Name;
|
var cached = CacheObjectBase.GetCacheObject(member, target);
|
||||||
if (names.Contains(name)) continue;
|
|
||||||
names.Add(name);
|
|
||||||
|
|
||||||
var cached = CacheObjectBase.GetCacheObject(null, member, target);
|
|
||||||
if (cached != null)
|
if (cached != null)
|
||||||
{
|
{
|
||||||
|
names.Add(name);
|
||||||
list.Add(cached);
|
list.Add(cached);
|
||||||
cached.ReflectionException = exception;
|
cached.ReflectionException = exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
MelonLogger.LogWarning($"Exception caching member {declaringType.Name}.{member.Name}!");
|
MelonLogger.LogWarning($"Exception caching member {name}!");
|
||||||
MelonLogger.Log(e.ToString());
|
MelonLogger.Log(e.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,7 +198,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Label("<b>Type:</b> <color=cyan>" + ObjectType.FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(245f) });
|
GUILayout.Label("<b>Type:</b> <color=cyan>" + TargetType.FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(245f) });
|
||||||
if (m_uObj)
|
if (m_uObj)
|
||||||
{
|
{
|
||||||
GUILayout.Label("Name: " + m_uObj.name, null);
|
GUILayout.Label("Name: " + m_uObj.name, null);
|
||||||
@ -243,34 +256,27 @@ namespace Explorer
|
|||||||
|
|
||||||
GUILayout.Space(10);
|
GUILayout.Space(10);
|
||||||
|
|
||||||
|
Pages.Count = m_cachedMembersFiltered.Length;
|
||||||
|
|
||||||
// prev/next page buttons
|
// prev/next page buttons
|
||||||
GUILayout.BeginHorizontal(null);
|
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;
|
Pages.DrawLimitInputArea();
|
||||||
if (count > m_limitPerPage)
|
|
||||||
|
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 (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||||
{
|
{
|
||||||
if (m_pageOffset > 0) m_pageOffset--;
|
Pages.TurnPage(Turn.Left, ref this.scroll);
|
||||||
scroll = Vector2.zero;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
Pages.CurrentPageLabel();
|
||||||
GUILayout.Label($"Page {m_pageOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
|
|
||||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
|
||||||
|
|
||||||
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(80) }))
|
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||||
{
|
{
|
||||||
if (m_pageOffset < maxOffset) m_pageOffset++;
|
Pages.TurnPage(Turn.Right, ref this.scroll);
|
||||||
scroll = Vector2.zero;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
@ -285,34 +291,28 @@ namespace Explorer
|
|||||||
|
|
||||||
GUILayout.BeginVertical(GUI.skin.box, null);
|
GUILayout.BeginVertical(GUI.skin.box, null);
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
var members = this.m_cachedMembersFiltered;
|
var members = this.m_cachedMembersFiltered;
|
||||||
int offsetIndex = (m_pageOffset * m_limitPerPage) + index;
|
int start = Pages.CalculateOffsetIndex();
|
||||||
|
|
||||||
if (offsetIndex >= count)
|
for (int j = start; (j < start + Pages.PageLimit && j < members.Length); j++)
|
||||||
{
|
|
||||||
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++)
|
|
||||||
{
|
{
|
||||||
var holder = members[j];
|
var holder = members[j];
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
|
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
|
||||||
|
try
|
||||||
try
|
{
|
||||||
{
|
|
||||||
holder.Draw(rect, 180f);
|
holder.Draw(rect, 180f);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
catch { }
|
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
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.EndVertical();
|
||||||
@ -353,7 +353,8 @@ namespace Explorer
|
|||||||
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) }))
|
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) }))
|
||||||
{
|
{
|
||||||
m_filter = mode;
|
m_filter = mode;
|
||||||
m_pageOffset = 0;
|
Pages.PageOffset = 0;
|
||||||
|
scroll = Vector2.zero;
|
||||||
}
|
}
|
||||||
GUI.color = Color.white;
|
GUI.color = Color.white;
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,90 @@ namespace Explorer
|
|||||||
|
|
||||||
// ========= Public Helpers =========
|
// ========= 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
|
public static bool IsMouseInWindow
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -140,71 +224,5 @@ namespace Explorer
|
|||||||
|
|
||||||
return rect;
|
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