- various performance improvements
- by default, lists and arrays are now collapsed, use the "v" button to expand them.
- added error handling for a TypeLoadException which can happen with some generic types.
This commit is contained in:
sinaioutlander 2020-08-12 18:25:33 +10:00
parent e8b17d3583
commit 7144b6a44c
19 changed files with 606 additions and 461 deletions

View File

@ -16,7 +16,7 @@ namespace Explorer
public const string ID = "com.sinai.cppexplorer"; public const string ID = "com.sinai.cppexplorer";
public const string NAME = "IL2CPP Runtime Explorer"; public const string NAME = "IL2CPP Runtime Explorer";
public const string VERSION = "1.3.0"; public const string VERSION = "1.3.1";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
// fields // fields

View File

@ -112,7 +112,6 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ILBehaviour.cs" />
<Compile Include="CppExplorer.cs" /> <Compile Include="CppExplorer.cs" />
<Compile Include="MainMenu\Pages\ConsolePage.cs" /> <Compile Include="MainMenu\Pages\ConsolePage.cs" />
<Compile Include="MainMenu\Pages\Console\REPL.cs" /> <Compile Include="MainMenu\Pages\Console\REPL.cs" />

View File

@ -1,33 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using MelonLoader;
using UnhollowerRuntimeLib;
namespace Explorer
{
//public class ILBehaviour : MonoBehaviour
//{
// public ILBehaviour(IntPtr intPtr) : base(intPtr) { }
// public static T AddToGameObject<T>(GameObject _go) where T : ILBehaviour
// {
// Il2CppSystem.Type ilType = UnhollowerRuntimeLib.Il2CppType.Of<T>();
// if (ilType == null)
// {
// MelonLogger.Log("Error - could not get MB as ilType");
// return null;
// }
// var obj = typeof(T)
// .GetConstructor(new Type[] { typeof(IntPtr) })
// .Invoke(new object[] { _go.AddComponent(UnhollowerRuntimeLib.Il2CppType.Of<T>()).Pointer });
// return (T)obj;
// }
//}
}

View File

@ -93,9 +93,13 @@ namespace Explorer
return; return;
} }
m_objectType = type; try
GetFields(m_object); {
GetProperties(m_object); m_objectType = type;
GetFields(m_object);
GetProperties(m_object);
}
catch { }
UpdateValues(); UpdateValues();
} }
@ -276,7 +280,7 @@ namespace Explorer
public static bool IsList(Type t) public static bool IsList(Type t)
{ {
return t.IsGenericType return t.IsGenericType
&& t.GetGenericTypeDefinition() is Type typeDef && t.GetGenericTypeDefinition() is Type typeDef
&& (typeDef.IsAssignableFrom(typeof(List<>)) || typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))); && (typeDef.IsAssignableFrom(typeof(List<>)) || typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>)));
} }
@ -292,8 +296,20 @@ namespace Explorer
foreach (var type in types) foreach (var type in types)
{ {
foreach (var pi in type.GetProperties(At.flags)) PropertyInfo[] propInfos = new PropertyInfo[0];
try
{ {
propInfos = type.GetProperties(At.flags);
}
catch (TypeLoadException)
{
MelonLogger.Log($"Couldn't get Properties for Type '{type.Name}', it may not support Il2Cpp Reflection at the moment.");
}
foreach (var pi in propInfos)
{
// this member causes a crash when inspected, so just skipping it for now.
if (pi.Name == "Il2CppType") if (pi.Name == "Il2CppType")
{ {
continue; continue;
@ -335,7 +351,7 @@ namespace Explorer
} }
} }
} }
/* ********************* /* *********************
* PROPERTYINFO HOLDER * PROPERTYINFO HOLDER
@ -346,6 +362,7 @@ namespace Explorer
public Type classType; public Type classType;
public PropertyInfo propInfo; public PropertyInfo propInfo;
public object m_value; public object m_value;
public bool IsExpanded;
public PropertyInfoHolder(Type _type, PropertyInfo _propInfo) public PropertyInfoHolder(Type _type, PropertyInfo _propInfo)
{ {
@ -355,14 +372,7 @@ namespace Explorer
public void Draw(ReflectionWindow window) public void Draw(ReflectionWindow window)
{ {
if (propInfo.CanWrite) UIStyles.DrawMember(ref m_value, ref this.IsExpanded, this.propInfo, window.m_rect, window.m_object, SetValue);
{
UIStyles.DrawMember(ref m_value, propInfo.PropertyType.Name, propInfo.Name, window.m_rect, window.m_object, SetValue);
}
else
{
UIStyles.DrawMember(ref m_value, propInfo.PropertyType.Name, propInfo.Name, window.m_rect, window.m_object);
}
} }
public void UpdateValue(object obj) public void UpdateValue(object obj)
@ -389,15 +399,15 @@ namespace Explorer
} }
catch (Exception e) catch (Exception e)
{ {
//MelonLogger.Log("Exception on PropertyInfoHolder.UpdateValue, Name: " + this.propInfo.Name); MelonLogger.Log("Exception on PropertyInfoHolder.UpdateValue, Name: " + this.propInfo.Name);
//MelonLogger.Log(e.GetType() + ", " + e.Message); MelonLogger.Log(e.GetType() + ", " + e.Message);
//var inner = e.InnerException; var inner = e.InnerException;
//while (inner != null) while (inner != null)
//{ {
// MelonLogger.Log("inner: " + inner.GetType() + ", " + inner.Message); MelonLogger.Log("inner: " + inner.GetType() + ", " + inner.Message);
// inner = inner.InnerException; inner = inner.InnerException;
//} }
m_value = null; m_value = null;
} }
@ -473,6 +483,7 @@ namespace Explorer
public Type classType; public Type classType;
public FieldInfo fieldInfo; public FieldInfo fieldInfo;
public object m_value; public object m_value;
public bool IsExpanded;
public FieldInfoHolder(Type _type, FieldInfo _fieldInfo) public FieldInfoHolder(Type _type, FieldInfo _fieldInfo)
{ {
@ -487,16 +498,7 @@ namespace Explorer
public void Draw(ReflectionWindow window) public void Draw(ReflectionWindow window)
{ {
bool canSet = !(fieldInfo.IsLiteral && !fieldInfo.IsInitOnly); UIStyles.DrawMember(ref m_value, ref this.IsExpanded, this.fieldInfo, window.m_rect, window.m_object, SetValue);
if (canSet)
{
UIStyles.DrawMember(ref m_value, fieldInfo.FieldType.Name, fieldInfo.Name, window.m_rect, window.m_object, SetValue);
}
else
{
UIStyles.DrawMember(ref m_value, fieldInfo.FieldType.Name, fieldInfo.Name, window.m_rect, window.m_object);
}
} }
public void SetValue(object obj) public void SetValue(object obj)

View File

@ -36,17 +36,17 @@ namespace Explorer
return MB.FindAll<T>(); return MB.FindAll<T>();
} }
[Documentation("runCoroutine(enumerator) - runs an IEnumerator as a Unity coroutine.")] //[Documentation("runCoroutine(enumerator) - runs an IEnumerator as a Unity coroutine.")]
public static object runCoroutine(IEnumerator i) //public static object runCoroutine(IEnumerator i)
{ //{
return MB.RunCoroutine(i); // return MB.RunCoroutine(i);
} //}
[Documentation("endCoroutine(co) - ends a Unity coroutine.")] //[Documentation("endCoroutine(co) - ends a Unity coroutine.")]
public static void endCoroutine(Coroutine c) //public static void endCoroutine(Coroutine c)
{ //{
MB.EndCoroutine(c); // MB.EndCoroutine(c);
} //}
////[Documentation("type<T>() - obtain type info about a type T. Provides some Reflection helpers.")] ////[Documentation("type<T>() - obtain type info about a type T. Provides some Reflection helpers.")]
////public static TypeHelper type<T>() ////public static TypeHelper type<T>()

View File

@ -21,14 +21,14 @@ namespace Explorer
return FindObjectsOfType<T>(); return FindObjectsOfType<T>();
} }
public object RunCoroutine(IEnumerator enumerator) //public object RunCoroutine(IEnumerator enumerator)
{ //{
return MelonCoroutines.Start(enumerator); // return MelonCoroutines.Start(enumerator);
} //}
public void EndCoroutine(Coroutine c) //public void EndCoroutine(Coroutine c)
{ //{
StopCoroutine(c); // StopCoroutine(c);
} //}
} }
} }

View File

@ -20,12 +20,13 @@ namespace Explorer
// gameobject list // gameobject list
private Transform m_currentTransform; private Transform m_currentTransform;
private List<GameObject> m_objectList = new List<GameObject>(); private List<GameObjectCache> m_objectList = new List<GameObjectCache>();
private float m_timeOfLastUpdate = -1f;
// search bar // search bar
private bool m_searching = false; private bool m_searching = false;
private string m_searchInput = ""; private string m_searchInput = "";
private List<GameObject> m_searchResults = new List<GameObject>(); private List<GameObjectCache> m_searchResults = new List<GameObjectCache>();
// ------------ Init and Update ------------ // // ------------ Init and Update ------------ //
@ -40,17 +41,24 @@ namespace Explorer
m_currentTransform = null; m_currentTransform = null;
CancelSearch(); CancelSearch();
} }
public override void Update() public override void Update()
{ {
if (Time.time - m_timeOfLastUpdate < 1f)
{
return;
}
m_timeOfLastUpdate = Time.time;
var start = Time.realtimeSinceStartup;
if (!m_searching) if (!m_searching)
{ {
m_objectList = new List<GameObject>(); m_objectList = new List<GameObjectCache>();
if (m_currentTransform) if (m_currentTransform)
{ {
var noChildren = new List<GameObject>(); var endAppend = new List<GameObjectCache>();
for (int i = 0; i < m_currentTransform.childCount; i++) for (int i = 0; i < m_currentTransform.childCount; i++)
{ {
var child = m_currentTransform.GetChild(i); var child = m_currentTransform.GetChild(i);
@ -58,13 +66,13 @@ namespace Explorer
if (child) if (child)
{ {
if (child.childCount > 0) if (child.childCount > 0)
m_objectList.Add(child.gameObject); m_objectList.Add(new GameObjectCache(child.gameObject));
else else
noChildren.Add(child.gameObject); endAppend.Add(new GameObjectCache(child.gameObject));
} }
} }
m_objectList.AddRange(noChildren); m_objectList.AddRange(endAppend);
noChildren = null; endAppend = null;
} }
else else
{ {
@ -74,11 +82,11 @@ namespace Explorer
// add objects with children first // add objects with children first
foreach (var obj in rootObjects.Where(x => x.transform.childCount > 0)) foreach (var obj in rootObjects.Where(x => x.transform.childCount > 0))
{ {
m_objectList.Add(obj); m_objectList.Add(new GameObjectCache(obj));
} }
foreach (var obj in rootObjects.Where(x => x.transform.childCount == 0)) foreach (var obj in rootObjects.Where(x => x.transform.childCount == 0))
{ {
m_objectList.Add(obj); m_objectList.Add(new GameObjectCache(obj));
} }
} }
} }
@ -161,10 +169,13 @@ namespace Explorer
if (m_objectList.Count > 0) if (m_objectList.Count > 0)
{ {
var start = Time.realtimeSinceStartup;
foreach (var obj in m_objectList) foreach (var obj in m_objectList)
{ {
UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170); //UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
UIStyles.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
} }
var diff = Time.realtimeSinceStartup - start;
} }
else else
{ {
@ -184,7 +195,8 @@ namespace Explorer
{ {
foreach (var obj in m_searchResults) foreach (var obj in m_searchResults)
{ {
UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170); //UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
UIStyles.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
} }
} }
else else
@ -232,19 +244,54 @@ namespace Explorer
m_searching = false; m_searching = false;
} }
public List<GameObject> SearchSceneObjects(string _search) public List<GameObjectCache> SearchSceneObjects(string _search)
{ {
var matches = new List<GameObject>(); var matches = new List<GameObjectCache>();
foreach (var obj in Resources.FindObjectsOfTypeAll<GameObject>()) foreach (var obj in Resources.FindObjectsOfTypeAll<GameObject>())
{ {
if (obj.name.ToLower().Contains(_search.ToLower()) && obj.scene.name == m_currentScene) if (obj.name.ToLower().Contains(_search.ToLower()) && obj.scene.name == m_currentScene)
{ {
matches.Add(obj); matches.Add(new GameObjectCache(obj));
} }
} }
return matches; return matches;
} }
public class GameObjectCache
{
public GameObject RefGameObject;
public string Label;
public Color EnabledColor;
public int ChildCount;
public GameObjectCache(GameObject obj)
{
RefGameObject = obj;
ChildCount = obj.transform.childCount;
Label = (ChildCount > 0) ? "[" + obj.transform.childCount + " children] " : "";
Label += obj.name;
bool enabled = obj.activeSelf;
int childCount = obj.transform.childCount;
if (enabled)
{
if (childCount > 0)
{
EnabledColor = Color.green;
}
else
{
EnabledColor = UIStyles.LightGreen;
}
}
else
{
EnabledColor = Color.red;
}
}
}
} }
} }

View File

@ -91,7 +91,8 @@ namespace Explorer
{ {
var obj = m_searchResults[i]; var obj = m_searchResults[i];
UIStyles.DrawValue(ref obj, _temprect); bool _ = false;
UIStyles.DrawValue(ref obj, _temprect, ref _);
} }
} }
else else
@ -262,7 +263,6 @@ namespace Explorer
{ {
var findType = CppExplorer.GetType(_type); var findType = CppExplorer.GetType(_type);
type = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName); type = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName);
MelonLogger.Log("Got type: " + type.AssemblyQualifiedName);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -5,7 +5,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using Il2CppSystem.Collections; using Il2CppSystem.Collections;
using Il2CppSystem.Reflection; //using Il2CppSystem.Reflection;
using MelonLoader; using MelonLoader;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using UnityEngine; using UnityEngine;
@ -15,6 +15,8 @@ namespace Explorer
{ {
public class UIStyles public class UIStyles
{ {
public static Color LightGreen = new Color(Color.green.r - 0.3f, Color.green.g - 0.3f, Color.green.b - 0.3f);
public static GUISkin WindowSkin public static GUISkin WindowSkin
{ {
get get
@ -115,42 +117,48 @@ 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<GameObject> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380) public static void GameobjButton(GameObject obj, Action<GameObject> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
{ {
if (obj == null) bool children = obj.transform.childCount > 0;
string label = children ? "[" + obj.transform.childCount + " children] " : "";
label += obj.name;
bool enabled = obj.activeSelf;
int childCount = obj.transform.childCount;
Color color;
if (enabled)
{
if (childCount > 0)
{
color = Color.green;
}
else
{
color = LightGreen;
}
}
else
{
color = Color.red;
}
FastGameobjButton(obj, color, label, obj.activeSelf, specialInspectMethod, showSmallInspectBtn, width);
}
public static void FastGameobjButton(GameObject obj, Color activeColor, string label, bool enabled, Action<GameObject> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
{
if (!obj)
{ {
GUILayout.Label("<i><color=red>null</color></i>", null); GUILayout.Label("<i><color=red>null</color></i>", null);
return; return;
} }
bool enabled = obj.activeSelf; // ------ toggle active button ------
bool children = obj.transform.childCount > 0;
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUI.skin.button.alignment = TextAnchor.UpperLeft; GUI.skin.button.alignment = TextAnchor.UpperLeft;
// ------ build name ------ GUI.color = activeColor;
string label = children ? "[" + obj.transform.childCount + " children] " : "";
label += obj.name;
// ------ Color -------
if (enabled)
{
if (children)
{
GUI.color = Color.green;
}
else
{
GUI.color = new Color(Color.green.r - 0.3f, Color.green.g - 0.3f, Color.green.b - 0.3f);
}
}
else
{
GUI.color = Color.red;
}
// ------ toggle active button ------
enabled = GUILayout.Toggle(enabled, "", new GUILayoutOption[] { GUILayout.Width(18) }); enabled = GUILayout.Toggle(enabled, "", new GUILayoutOption[] { GUILayout.Width(18) });
if (obj.activeSelf != enabled) if (obj.activeSelf != enabled)
@ -188,84 +196,114 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
public static void DrawMember(ref object value, string valueType, string memberName, Rect rect, object setTarget = null, Action<object> setAction = null, float labelWidth = 180, bool autoSet = false) public static void DrawMember(ref object value, ref bool isExpanded, MemberInfo memberInfo, Rect rect, object setTarget = null, Action<object> setAction = null, float labelWidth = 180, bool autoSet = false)
{ {
GUILayout.Label("<color=cyan>" + memberName + ":</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) }); GUILayout.Label("<color=cyan>" + memberInfo.Name + ":</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });
DrawValue(ref value, rect, valueType, memberName, setTarget, setAction, autoSet); string valueType = "";
bool canWrite = true;
if (memberInfo is FieldInfo fi)
{
valueType = fi.FieldType.Name;
canWrite = !(fi.IsLiteral && !fi.IsInitOnly);
}
else if (memberInfo is PropertyInfo pi)
{
valueType = pi.PropertyType.Name;
canWrite = pi.CanWrite;
}
DrawValue(ref value, rect, ref isExpanded, valueType, (canWrite ? setTarget : null), (canWrite ? setAction : null), autoSet);
} }
public static void DrawValue(ref object value, Rect rect, string nullValueType = null, string memberName = null, object setTarget = null, Action<object> setAction = null, bool autoSet = false) public static void DrawValue(ref object value, Rect rect, ref bool isExpanded, string nullValueType = null, object setTarget = null, Action<object> setAction = null, bool autoSet = false)
{ {
if (value == null) if (value == null)
{ {
GUILayout.Label("<i>null (" + nullValueType + ")</i>", null); GUILayout.Label("<i>null (" + nullValueType + ")</i>", null);
return;
} }
else
var valueType = value.GetType();
if (valueType.IsPrimitive || value.GetType() == typeof(string))
{ {
var valueType = value.GetType(); DrawPrimitive(ref value, rect, setTarget, setAction);
if (valueType.IsPrimitive || value.GetType() == typeof(string)) }
else if (valueType == typeof(GameObject) || valueType == typeof(Transform))
{
GameObject go;
if (value.GetType() == typeof(Transform))
{ {
DrawPrimitive(ref value, rect, setTarget, setAction); go = (value as Transform).gameObject;
} }
else if (valueType == typeof(GameObject) || valueType == typeof(Transform)) else
{ {
GameObject go; go = (value as GameObject);
if (value.GetType() == typeof(Transform))
{
go = (value as Transform).gameObject;
}
else
{
go = (value as GameObject);
}
UIStyles.GameobjButton(go, null, false, rect.width - 250);
} }
else if (valueType.IsEnum)
{
if (setAction != null)
{
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) }))
{
SetEnum(ref value, -1);
setAction.Invoke(setTarget);
}
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) }))
{
SetEnum(ref value, 1);
setAction.Invoke(setTarget);
}
}
GUILayout.Label(value.ToString(), null); GameobjButton(go, null, false, rect.width - 250);
}
else if (valueType.IsEnum)
{
if (setAction != null)
{
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) }))
{
SetEnum(ref value, -1);
setAction.Invoke(setTarget);
}
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) }))
{
SetEnum(ref value, 1);
setAction.Invoke(setTarget);
}
} }
else if (value is System.Collections.IEnumerable || ReflectionWindow.IsList(valueType))
GUILayout.Label(value.ToString(), null);
}
else if (value is System.Collections.IEnumerable || ReflectionWindow.IsList(valueType))
{
System.Collections.IEnumerable enumerable;
if (value is System.Collections.IEnumerable isEnumerable)
{ {
System.Collections.IEnumerable enumerable; enumerable = isEnumerable;
}
else
{
var listValueType = value.GetType().GetGenericArguments()[0];
var listType = typeof(Il2CppSystem.Collections.Generic.List<>).MakeGenericType(new Type[] { listValueType });
var method = listType.GetMethod("ToArray");
enumerable = (System.Collections.IEnumerable)method.Invoke(value, new object[0]);
}
if (value is System.Collections.IEnumerable isEnumerable) int count = enumerable.Cast<object>().Count();
if (!isExpanded)
{
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
{ {
enumerable = isEnumerable; isExpanded = true;
} }
else }
else
{
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{ {
var listValueType = value.GetType().GetGenericArguments()[0]; isExpanded = false;
var listType = typeof(Il2CppSystem.Collections.Generic.List<>).MakeGenericType(new Type[] { listValueType });
var method = listType.GetMethod("ToArray");
enumerable = (System.Collections.IEnumerable)method.Invoke(value, new object[0]);
} }
}
int count = enumerable.Cast<object>().Count(); GUI.skin.button.alignment = TextAnchor.MiddleLeft;
string btnLabel = "<color=yellow>[" + count + "] " + valueType + "</color>";
GUI.skin.button.alignment = TextAnchor.MiddleLeft; if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 260) }))
string btnLabel = "<color=yellow>[" + count + "] " + valueType + "</color>"; {
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 230) })) WindowManager.InspectObject(value, out bool _);
{ }
WindowManager.InspectObject(value, out bool _); GUI.skin.button.alignment = TextAnchor.MiddleCenter;
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
if (isExpanded)
{
var enumerator = enumerable.GetEnumerator(); var enumerator = enumerable.GetEnumerator();
if (enumerator != null) if (enumerator != null)
{ {
@ -315,49 +353,68 @@ namespace Explorer
} }
} }
} }
else }
else
{
var label = value.ToString();
if (valueType == typeof(Object))
{ {
var label = value.ToString(); label = (value as Object).name;
if (valueType == typeof(Object))
{
label = (value as Object).name;
}
else if (value is Vector4 vec4)
{
label = vec4.ToString();
}
else if (value is Vector3 vec3)
{
label = vec3.ToString();
}
else if (value is Vector2 vec2)
{
label = vec2.ToString();
}
else if (value is Rect rec)
{
label = rec.ToString();
}
else if (value is Matrix4x4 matrix)
{
label = matrix.ToString();
}
else if (value is Color col)
{
label = col.ToString();
}
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 230) }))
{
WindowManager.InspectObject(value, out bool _);
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
} }
else if (value is Vector4 vec4)
{
label = vec4.ToString();
}
else if (value is Vector3 vec3)
{
label = vec3.ToString();
}
else if (value is Vector2 vec2)
{
label = vec2.ToString();
}
else if (value is Rect rec)
{
label = rec.ToString();
}
else if (value is Matrix4x4 matrix)
{
label = matrix.ToString();
}
else if (value is Color col)
{
label = col.ToString();
}
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 230) }))
{
WindowManager.InspectObject(value, out bool _);
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
} }
} }
//public static void DrawMember(ref object value, string valueType, string memberName, Rect rect, object setTarget = null, Action<object> setAction = null, float labelWidth = 180, bool autoSet = false)
//{
// GUILayout.Label("<color=cyan>" + memberName + ":</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });
// DrawValue(ref value, rect, valueType, memberName, setTarget, setAction, autoSet);
//}
//public static void DrawValue(ref object value, Rect rect, string nullValueType = null, string memberName = null, object setTarget = null, Action<object> setAction = null, bool autoSet = false)
//{
// if (value == null)
// {
// GUILayout.Label("<i>null (" + nullValueType + ")</i>", null);
// }
// else
// {
// }
//}
// Helper for drawing primitive values (with Apply button) // Helper for drawing primitive values (with Apply button)
public static void DrawPrimitive(ref object value, Rect m_rect, object setTarget = null, Action<object> setAction = null, bool autoSet = false) public static void DrawPrimitive(ref object value, Rect m_rect, object setTarget = null, Action<object> setAction = null, bool autoSet = false)
@ -387,10 +444,10 @@ namespace Explorer
} }
else else
{ {
value = GUILayout.TextField(value.ToString(), new GUILayoutOption[] { GUILayout.MaxWidth(m_rect.width - 260) }); value = GUILayout.TextField(value.ToString(), new GUILayoutOption[] { GUILayout.MaxWidth(m_rect.width - 260) });
} }
if (autoSet || (allowSet && GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))) if (autoSet || (allowSet && GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) })))
{ {
setAction.Invoke(setTarget); setAction.Invoke(setTarget);
} }

View File

@ -16,7 +16,7 @@ namespace Explorer
public const string ID = "com.sinai.cppexplorer"; public const string ID = "com.sinai.cppexplorer";
public const string NAME = "IL2CPP Runtime Explorer"; public const string NAME = "IL2CPP Runtime Explorer";
public const string VERSION = "1.3.0"; public const string VERSION = "1.3.1";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
// fields // fields

View File

@ -104,7 +104,6 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ILBehaviour.cs" />
<Compile Include="CppExplorer.cs" /> <Compile Include="CppExplorer.cs" />
<Compile Include="MainMenu\Pages\ConsolePage.cs" /> <Compile Include="MainMenu\Pages\ConsolePage.cs" />
<Compile Include="MainMenu\Pages\Console\REPL.cs" /> <Compile Include="MainMenu\Pages\Console\REPL.cs" />

View File

@ -1,33 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using MelonLoader;
using UnhollowerRuntimeLib;
namespace Explorer
{
//public class ILBehaviour : MonoBehaviour
//{
// public ILBehaviour(IntPtr intPtr) : base(intPtr) { }
// public static T AddToGameObject<T>(GameObject _go) where T : ILBehaviour
// {
// Il2CppSystem.Type ilType = UnhollowerRuntimeLib.Il2CppType.Of<T>();
// if (ilType == null)
// {
// MelonLogger.Log("Error - could not get MB as ilType");
// return null;
// }
// var obj = typeof(T)
// .GetConstructor(new Type[] { typeof(IntPtr) })
// .Invoke(new object[] { _go.AddComponent(UnhollowerRuntimeLib.Il2CppType.Of<T>()).Pointer });
// return (T)obj;
// }
//}
}

View File

@ -93,9 +93,13 @@ namespace Explorer
return; return;
} }
m_objectType = type; try
GetFields(m_object); {
GetProperties(m_object); m_objectType = type;
GetFields(m_object);
GetProperties(m_object);
}
catch { }
UpdateValues(); UpdateValues();
} }
@ -276,7 +280,7 @@ namespace Explorer
public static bool IsList(Type t) public static bool IsList(Type t)
{ {
return t.IsGenericType return t.IsGenericType
&& t.GetGenericTypeDefinition() is Type typeDef && t.GetGenericTypeDefinition() is Type typeDef
&& (typeDef.IsAssignableFrom(typeof(List<>)) || typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))); && (typeDef.IsAssignableFrom(typeof(List<>)) || typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>)));
} }
@ -292,8 +296,20 @@ namespace Explorer
foreach (var type in types) foreach (var type in types)
{ {
foreach (var pi in type.GetProperties(At.flags)) PropertyInfo[] propInfos = new PropertyInfo[0];
try
{ {
propInfos = type.GetProperties(At.flags);
}
catch (TypeLoadException)
{
MelonLogger.Log($"Couldn't get Properties for Type '{type.Name}', it may not support Il2Cpp Reflection at the moment.");
}
foreach (var pi in propInfos)
{
// this member causes a crash when inspected, so just skipping it for now.
if (pi.Name == "Il2CppType") if (pi.Name == "Il2CppType")
{ {
continue; continue;
@ -335,7 +351,7 @@ namespace Explorer
} }
} }
} }
/* ********************* /* *********************
* PROPERTYINFO HOLDER * PROPERTYINFO HOLDER
@ -346,6 +362,7 @@ namespace Explorer
public Type classType; public Type classType;
public PropertyInfo propInfo; public PropertyInfo propInfo;
public object m_value; public object m_value;
public bool IsExpanded;
public PropertyInfoHolder(Type _type, PropertyInfo _propInfo) public PropertyInfoHolder(Type _type, PropertyInfo _propInfo)
{ {
@ -355,14 +372,7 @@ namespace Explorer
public void Draw(ReflectionWindow window) public void Draw(ReflectionWindow window)
{ {
if (propInfo.CanWrite) UIStyles.DrawMember(ref m_value, ref this.IsExpanded, this.propInfo, window.m_rect, window.m_object, SetValue);
{
UIStyles.DrawMember(ref m_value, propInfo.PropertyType.Name, propInfo.Name, window.m_rect, window.m_object, SetValue);
}
else
{
UIStyles.DrawMember(ref m_value, propInfo.PropertyType.Name, propInfo.Name, window.m_rect, window.m_object);
}
} }
public void UpdateValue(object obj) public void UpdateValue(object obj)
@ -389,15 +399,15 @@ namespace Explorer
} }
catch (Exception e) catch (Exception e)
{ {
//MelonLogger.Log("Exception on PropertyInfoHolder.UpdateValue, Name: " + this.propInfo.Name); MelonLogger.Log("Exception on PropertyInfoHolder.UpdateValue, Name: " + this.propInfo.Name);
//MelonLogger.Log(e.GetType() + ", " + e.Message); MelonLogger.Log(e.GetType() + ", " + e.Message);
//var inner = e.InnerException; var inner = e.InnerException;
//while (inner != null) while (inner != null)
//{ {
// MelonLogger.Log("inner: " + inner.GetType() + ", " + inner.Message); MelonLogger.Log("inner: " + inner.GetType() + ", " + inner.Message);
// inner = inner.InnerException; inner = inner.InnerException;
//} }
m_value = null; m_value = null;
} }
@ -473,6 +483,7 @@ namespace Explorer
public Type classType; public Type classType;
public FieldInfo fieldInfo; public FieldInfo fieldInfo;
public object m_value; public object m_value;
public bool IsExpanded;
public FieldInfoHolder(Type _type, FieldInfo _fieldInfo) public FieldInfoHolder(Type _type, FieldInfo _fieldInfo)
{ {
@ -487,16 +498,7 @@ namespace Explorer
public void Draw(ReflectionWindow window) public void Draw(ReflectionWindow window)
{ {
bool canSet = !(fieldInfo.IsLiteral && !fieldInfo.IsInitOnly); UIStyles.DrawMember(ref m_value, ref this.IsExpanded, this.fieldInfo, window.m_rect, window.m_object, SetValue);
if (canSet)
{
UIStyles.DrawMember(ref m_value, fieldInfo.FieldType.Name, fieldInfo.Name, window.m_rect, window.m_object, SetValue);
}
else
{
UIStyles.DrawMember(ref m_value, fieldInfo.FieldType.Name, fieldInfo.Name, window.m_rect, window.m_object);
}
} }
public void SetValue(object obj) public void SetValue(object obj)

View File

@ -36,17 +36,17 @@ namespace Explorer
return MB.FindAll<T>(); return MB.FindAll<T>();
} }
[Documentation("runCoroutine(enumerator) - runs an IEnumerator as a Unity coroutine.")] //[Documentation("runCoroutine(enumerator) - runs an IEnumerator as a Unity coroutine.")]
public static object runCoroutine(IEnumerator i) //public static object runCoroutine(IEnumerator i)
{ //{
return MB.RunCoroutine(i); // return MB.RunCoroutine(i);
} //}
[Documentation("endCoroutine(co) - ends a Unity coroutine.")] //[Documentation("endCoroutine(co) - ends a Unity coroutine.")]
public static void endCoroutine(Coroutine c) //public static void endCoroutine(Coroutine c)
{ //{
MB.EndCoroutine(c); // MB.EndCoroutine(c);
} //}
////[Documentation("type<T>() - obtain type info about a type T. Provides some Reflection helpers.")] ////[Documentation("type<T>() - obtain type info about a type T. Provides some Reflection helpers.")]
////public static TypeHelper type<T>() ////public static TypeHelper type<T>()

View File

@ -21,14 +21,14 @@ namespace Explorer
return FindObjectsOfType<T>(); return FindObjectsOfType<T>();
} }
public object RunCoroutine(IEnumerator enumerator) //public object RunCoroutine(IEnumerator enumerator)
{ //{
return MelonCoroutines.Start(enumerator); // return MelonCoroutines.Start(enumerator);
} //}
public void EndCoroutine(Coroutine c) //public void EndCoroutine(Coroutine c)
{ //{
StopCoroutine(c); // StopCoroutine(c);
} //}
} }
} }

View File

@ -20,12 +20,13 @@ namespace Explorer
// gameobject list // gameobject list
private Transform m_currentTransform; private Transform m_currentTransform;
private List<GameObject> m_objectList = new List<GameObject>(); private List<GameObjectCache> m_objectList = new List<GameObjectCache>();
private float m_timeOfLastUpdate = -1f;
// search bar // search bar
private bool m_searching = false; private bool m_searching = false;
private string m_searchInput = ""; private string m_searchInput = "";
private List<GameObject> m_searchResults = new List<GameObject>(); private List<GameObjectCache> m_searchResults = new List<GameObjectCache>();
// ------------ Init and Update ------------ // // ------------ Init and Update ------------ //
@ -40,17 +41,24 @@ namespace Explorer
m_currentTransform = null; m_currentTransform = null;
CancelSearch(); CancelSearch();
} }
public override void Update() public override void Update()
{ {
if (Time.time - m_timeOfLastUpdate < 1f)
{
return;
}
m_timeOfLastUpdate = Time.time;
var start = Time.realtimeSinceStartup;
if (!m_searching) if (!m_searching)
{ {
m_objectList = new List<GameObject>(); m_objectList = new List<GameObjectCache>();
if (m_currentTransform) if (m_currentTransform)
{ {
var noChildren = new List<GameObject>(); var endAppend = new List<GameObjectCache>();
for (int i = 0; i < m_currentTransform.childCount; i++) for (int i = 0; i < m_currentTransform.childCount; i++)
{ {
var child = m_currentTransform.GetChild(i); var child = m_currentTransform.GetChild(i);
@ -58,13 +66,13 @@ namespace Explorer
if (child) if (child)
{ {
if (child.childCount > 0) if (child.childCount > 0)
m_objectList.Add(child.gameObject); m_objectList.Add(new GameObjectCache(child.gameObject));
else else
noChildren.Add(child.gameObject); endAppend.Add(new GameObjectCache(child.gameObject));
} }
} }
m_objectList.AddRange(noChildren); m_objectList.AddRange(endAppend);
noChildren = null; endAppend = null;
} }
else else
{ {
@ -74,11 +82,11 @@ namespace Explorer
// add objects with children first // add objects with children first
foreach (var obj in rootObjects.Where(x => x.transform.childCount > 0)) foreach (var obj in rootObjects.Where(x => x.transform.childCount > 0))
{ {
m_objectList.Add(obj); m_objectList.Add(new GameObjectCache(obj));
} }
foreach (var obj in rootObjects.Where(x => x.transform.childCount == 0)) foreach (var obj in rootObjects.Where(x => x.transform.childCount == 0))
{ {
m_objectList.Add(obj); m_objectList.Add(new GameObjectCache(obj));
} }
} }
} }
@ -120,7 +128,8 @@ namespace Explorer
m_currentScene = scenes[index].name; m_currentScene = scenes[index].name;
} }
} }
GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", null); GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", null); //new GUILayoutOption[] { GUILayout.Width(250) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
// ----- GameObject Search ----- // ----- GameObject Search -----
@ -160,10 +169,13 @@ namespace Explorer
if (m_objectList.Count > 0) if (m_objectList.Count > 0)
{ {
var start = Time.realtimeSinceStartup;
foreach (var obj in m_objectList) foreach (var obj in m_objectList)
{ {
UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170); //UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
UIStyles.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
} }
var diff = Time.realtimeSinceStartup - start;
} }
else else
{ {
@ -183,7 +195,8 @@ namespace Explorer
{ {
foreach (var obj in m_searchResults) foreach (var obj in m_searchResults)
{ {
UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170); //UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
UIStyles.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
} }
} }
else else
@ -231,19 +244,54 @@ namespace Explorer
m_searching = false; m_searching = false;
} }
public List<GameObject> SearchSceneObjects(string _search) public List<GameObjectCache> SearchSceneObjects(string _search)
{ {
var matches = new List<GameObject>(); var matches = new List<GameObjectCache>();
foreach (var obj in Resources.FindObjectsOfTypeAll<GameObject>()) foreach (var obj in Resources.FindObjectsOfTypeAll<GameObject>())
{ {
if (obj.name.ToLower().Contains(_search.ToLower()) && obj.scene.name == m_currentScene) if (obj.name.ToLower().Contains(_search.ToLower()) && obj.scene.name == m_currentScene)
{ {
matches.Add(obj); matches.Add(new GameObjectCache(obj));
} }
} }
return matches; return matches;
} }
public class GameObjectCache
{
public GameObject RefGameObject;
public string Label;
public Color EnabledColor;
public int ChildCount;
public GameObjectCache(GameObject obj)
{
RefGameObject = obj;
ChildCount = obj.transform.childCount;
Label = (ChildCount > 0) ? "[" + obj.transform.childCount + " children] " : "";
Label += obj.name;
bool enabled = obj.activeSelf;
int childCount = obj.transform.childCount;
if (enabled)
{
if (childCount > 0)
{
EnabledColor = Color.green;
}
else
{
EnabledColor = UIStyles.LightGreen;
}
}
else
{
EnabledColor = Color.red;
}
}
}
} }
} }

View File

@ -44,7 +44,7 @@ namespace Explorer
private List<object> m_searchResults = new List<object>(); private List<object> m_searchResults = new List<object>();
private Vector2 resultsScroll = Vector2.zero; private Vector2 resultsScroll = Vector2.zero;
public override void Init() public override void Init()
{ {
Instance = this; Instance = this;
} }
@ -54,7 +54,7 @@ namespace Explorer
m_searchResults.Clear(); m_searchResults.Clear();
} }
public override void Update() public override void Update()
{ {
} }
@ -91,7 +91,8 @@ namespace Explorer
{ {
var obj = m_searchResults[i]; var obj = m_searchResults[i];
UIStyles.DrawValue(ref obj, _temprect); bool _ = false;
UIStyles.DrawValue(ref obj, _temprect, ref _);
} }
} }
else else
@ -147,14 +148,14 @@ namespace Explorer
{ {
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUI.skin.label.alignment = TextAnchor.MiddleRight; GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("Custom Class:", new GUILayoutOption[] { GUILayout.Width(250) }); GUILayout.Label("Custom Class:", new GUILayoutOption[] { GUILayout.Width(250) });
GUI.skin.label.alignment = TextAnchor.UpperLeft; GUI.skin.label.alignment = TextAnchor.UpperLeft;
m_typeInput = GUILayout.TextField(m_typeInput, new GUILayoutOption[] { GUILayout.Width(250) }); m_typeInput = GUILayout.TextField(m_typeInput, new GUILayoutOption[] { GUILayout.Width(250) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Label("Scene Filter:", new GUILayoutOption[] { GUILayout.Width(100) }); GUILayout.Label("Scene Filter:", new GUILayoutOption[] { GUILayout.Width(100) });
SceneFilterToggle(SceneFilter.Any, "Any", 60); SceneFilterToggle(SceneFilter.Any, "Any", 60);
SceneFilterToggle(SceneFilter.This, "This Scene", 100); SceneFilterToggle(SceneFilter.This, "This Scene", 100);
SceneFilterToggle(SceneFilter.DontDestroy, "DontDestroyOnLoad", 140); SceneFilterToggle(SceneFilter.DontDestroy, "DontDestroyOnLoad", 140);
@ -262,7 +263,6 @@ namespace Explorer
{ {
var findType = CppExplorer.GetType(_type); var findType = CppExplorer.GetType(_type);
type = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName); type = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName);
MelonLogger.Log("Got type: " + type.AssemblyQualifiedName);
} }
catch (Exception e) catch (Exception e)
{ {
@ -372,7 +372,7 @@ namespace Explorer
if (type == typeof(GameObject) || typeof(Component).IsAssignableFrom(type)) if (type == typeof(GameObject) || typeof(Component).IsAssignableFrom(type))
{ {
var go = obj as GameObject ?? (obj as Component).gameObject; var go = obj as GameObject ?? (obj as Component).gameObject;
if (go != null && go.scene.name == CppExplorer.ActiveSceneName && go.scene.name != "DontDestroyOnLoad") if (go != null && go.scene.name == CppExplorer.ActiveSceneName && go.scene.name != "DontDestroyOnLoad")
{ {
return true; return true;

View File

@ -5,7 +5,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using Il2CppSystem.Collections; using Il2CppSystem.Collections;
using Il2CppSystem.Reflection; //using Il2CppSystem.Reflection;
using MelonLoader; using MelonLoader;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using UnityEngine; using UnityEngine;
@ -15,6 +15,8 @@ namespace Explorer
{ {
public class UIStyles public class UIStyles
{ {
public static Color LightGreen = new Color(Color.green.r - 0.3f, Color.green.g - 0.3f, Color.green.b - 0.3f);
public static GUISkin WindowSkin public static GUISkin WindowSkin
{ {
get get
@ -115,42 +117,48 @@ 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<GameObject> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380) public static void GameobjButton(GameObject obj, Action<GameObject> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
{ {
if (obj == null) bool children = obj.transform.childCount > 0;
string label = children ? "[" + obj.transform.childCount + " children] " : "";
label += obj.name;
bool enabled = obj.activeSelf;
int childCount = obj.transform.childCount;
Color color;
if (enabled)
{
if (childCount > 0)
{
color = Color.green;
}
else
{
color = LightGreen;
}
}
else
{
color = Color.red;
}
FastGameobjButton(obj, color, label, obj.activeSelf, specialInspectMethod, showSmallInspectBtn, width);
}
public static void FastGameobjButton(GameObject obj, Color activeColor, string label, bool enabled, Action<GameObject> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
{
if (!obj)
{ {
GUILayout.Label("<i><color=red>null</color></i>", null); GUILayout.Label("<i><color=red>null</color></i>", null);
return; return;
} }
bool enabled = obj.activeSelf; // ------ toggle active button ------
bool children = obj.transform.childCount > 0;
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUI.skin.button.alignment = TextAnchor.UpperLeft; GUI.skin.button.alignment = TextAnchor.UpperLeft;
// ------ build name ------ GUI.color = activeColor;
string label = children ? "[" + obj.transform.childCount + " children] " : "";
label += obj.name;
// ------ Color -------
if (enabled)
{
if (children)
{
GUI.color = Color.green;
}
else
{
GUI.color = new Color(Color.green.r - 0.3f, Color.green.g - 0.3f, Color.green.b - 0.3f);
}
}
else
{
GUI.color = Color.red;
}
// ------ toggle active button ------
enabled = GUILayout.Toggle(enabled, "", new GUILayoutOption[] { GUILayout.Width(18) }); enabled = GUILayout.Toggle(enabled, "", new GUILayoutOption[] { GUILayout.Width(18) });
if (obj.activeSelf != enabled) if (obj.activeSelf != enabled)
@ -188,84 +196,114 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
public static void DrawMember(ref object value, string valueType, string memberName, Rect rect, object setTarget = null, Action<object> setAction = null, float labelWidth = 180, bool autoSet = false) public static void DrawMember(ref object value, ref bool isExpanded, MemberInfo memberInfo, Rect rect, object setTarget = null, Action<object> setAction = null, float labelWidth = 180, bool autoSet = false)
{ {
GUILayout.Label("<color=cyan>" + memberName + ":</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) }); GUILayout.Label("<color=cyan>" + memberInfo.Name + ":</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });
DrawValue(ref value, rect, valueType, memberName, setTarget, setAction, autoSet); string valueType = "";
bool canWrite = true;
if (memberInfo is FieldInfo fi)
{
valueType = fi.FieldType.Name;
canWrite = !(fi.IsLiteral && !fi.IsInitOnly);
}
else if (memberInfo is PropertyInfo pi)
{
valueType = pi.PropertyType.Name;
canWrite = pi.CanWrite;
}
DrawValue(ref value, rect, ref isExpanded, valueType, (canWrite ? setTarget : null), (canWrite ? setAction : null), autoSet);
} }
public static void DrawValue(ref object value, Rect rect, string nullValueType = null, string memberName = null, object setTarget = null, Action<object> setAction = null, bool autoSet = false) public static void DrawValue(ref object value, Rect rect, ref bool isExpanded, string nullValueType = null, object setTarget = null, Action<object> setAction = null, bool autoSet = false)
{ {
if (value == null) if (value == null)
{ {
GUILayout.Label("<i>null (" + nullValueType + ")</i>", null); GUILayout.Label("<i>null (" + nullValueType + ")</i>", null);
return;
} }
else
var valueType = value.GetType();
if (valueType.IsPrimitive || value.GetType() == typeof(string))
{ {
var valueType = value.GetType(); DrawPrimitive(ref value, rect, setTarget, setAction);
if (valueType.IsPrimitive || value.GetType() == typeof(string)) }
else if (valueType == typeof(GameObject) || valueType == typeof(Transform))
{
GameObject go;
if (value.GetType() == typeof(Transform))
{ {
DrawPrimitive(ref value, rect, setTarget, setAction); go = (value as Transform).gameObject;
} }
else if (valueType == typeof(GameObject) || valueType == typeof(Transform)) else
{ {
GameObject go; go = (value as GameObject);
if (value.GetType() == typeof(Transform))
{
go = (value as Transform).gameObject;
}
else
{
go = (value as GameObject);
}
UIStyles.GameobjButton(go, null, false, rect.width - 250);
} }
else if (valueType.IsEnum)
{
if (setAction != null)
{
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) }))
{
SetEnum(ref value, -1);
setAction.Invoke(setTarget);
}
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) }))
{
SetEnum(ref value, 1);
setAction.Invoke(setTarget);
}
}
GUILayout.Label(value.ToString(), null); GameobjButton(go, null, false, rect.width - 250);
}
else if (valueType.IsEnum)
{
if (setAction != null)
{
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) }))
{
SetEnum(ref value, -1);
setAction.Invoke(setTarget);
}
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) }))
{
SetEnum(ref value, 1);
setAction.Invoke(setTarget);
}
} }
else if (value is System.Collections.IEnumerable || ReflectionWindow.IsList(valueType))
GUILayout.Label(value.ToString(), null);
}
else if (value is System.Collections.IEnumerable || ReflectionWindow.IsList(valueType))
{
System.Collections.IEnumerable enumerable;
if (value is System.Collections.IEnumerable isEnumerable)
{ {
System.Collections.IEnumerable enumerable; enumerable = isEnumerable;
}
else
{
var listValueType = value.GetType().GetGenericArguments()[0];
var listType = typeof(Il2CppSystem.Collections.Generic.List<>).MakeGenericType(new Type[] { listValueType });
var method = listType.GetMethod("ToArray");
enumerable = (System.Collections.IEnumerable)method.Invoke(value, new object[0]);
}
if (value is System.Collections.IEnumerable isEnumerable) int count = enumerable.Cast<object>().Count();
if (!isExpanded)
{
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
{ {
enumerable = isEnumerable; isExpanded = true;
} }
else }
else
{
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{ {
var listValueType = value.GetType().GetGenericArguments()[0]; isExpanded = false;
var listType = typeof(Il2CppSystem.Collections.Generic.List<>).MakeGenericType(new Type[] { listValueType });
var method = listType.GetMethod("ToArray");
enumerable = (System.Collections.IEnumerable)method.Invoke(value, new object[0]);
} }
}
int count = enumerable.Cast<object>().Count(); GUI.skin.button.alignment = TextAnchor.MiddleLeft;
string btnLabel = "<color=yellow>[" + count + "] " + valueType + "</color>";
GUI.skin.button.alignment = TextAnchor.MiddleLeft; if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 260) }))
string btnLabel = "<color=yellow>[" + count + "] " + valueType + "</color>"; {
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 230) })) WindowManager.InspectObject(value, out bool _);
{ }
WindowManager.InspectObject(value, out bool _); GUI.skin.button.alignment = TextAnchor.MiddleCenter;
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
if (isExpanded)
{
var enumerator = enumerable.GetEnumerator(); var enumerator = enumerable.GetEnumerator();
if (enumerator != null) if (enumerator != null)
{ {
@ -315,49 +353,68 @@ namespace Explorer
} }
} }
} }
else }
else
{
var label = value.ToString();
if (valueType == typeof(Object))
{ {
var label = value.ToString(); label = (value as Object).name;
if (valueType == typeof(Object))
{
label = (value as Object).name;
}
else if (value is Vector4 vec4)
{
label = vec4.ToString();
}
else if (value is Vector3 vec3)
{
label = vec3.ToString();
}
else if (value is Vector2 vec2)
{
label = vec2.ToString();
}
else if (value is Rect rec)
{
label = rec.ToString();
}
else if (value is Matrix4x4 matrix)
{
label = matrix.ToString();
}
else if (value is Color col)
{
label = col.ToString();
}
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 230) }))
{
WindowManager.InspectObject(value, out bool _);
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
} }
else if (value is Vector4 vec4)
{
label = vec4.ToString();
}
else if (value is Vector3 vec3)
{
label = vec3.ToString();
}
else if (value is Vector2 vec2)
{
label = vec2.ToString();
}
else if (value is Rect rec)
{
label = rec.ToString();
}
else if (value is Matrix4x4 matrix)
{
label = matrix.ToString();
}
else if (value is Color col)
{
label = col.ToString();
}
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 230) }))
{
WindowManager.InspectObject(value, out bool _);
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
} }
} }
//public static void DrawMember(ref object value, string valueType, string memberName, Rect rect, object setTarget = null, Action<object> setAction = null, float labelWidth = 180, bool autoSet = false)
//{
// GUILayout.Label("<color=cyan>" + memberName + ":</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });
// DrawValue(ref value, rect, valueType, memberName, setTarget, setAction, autoSet);
//}
//public static void DrawValue(ref object value, Rect rect, string nullValueType = null, string memberName = null, object setTarget = null, Action<object> setAction = null, bool autoSet = false)
//{
// if (value == null)
// {
// GUILayout.Label("<i>null (" + nullValueType + ")</i>", null);
// }
// else
// {
// }
//}
// Helper for drawing primitive values (with Apply button) // Helper for drawing primitive values (with Apply button)
public static void DrawPrimitive(ref object value, Rect m_rect, object setTarget = null, Action<object> setAction = null, bool autoSet = false) public static void DrawPrimitive(ref object value, Rect m_rect, object setTarget = null, Action<object> setAction = null, bool autoSet = false)
@ -387,10 +444,10 @@ namespace Explorer
} }
else else
{ {
value = GUILayout.TextField(value.ToString(), new GUILayoutOption[] { GUILayout.MaxWidth(m_rect.width - 260) }); value = GUILayout.TextField(value.ToString(), new GUILayoutOption[] { GUILayout.MaxWidth(m_rect.width - 260) });
} }
if (autoSet || (allowSet && GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))) if (autoSet || (allowSet && GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) })))
{ {
setAction.Invoke(setTarget); setAction.Invoke(setTarget);
} }

View File

@ -142,11 +142,11 @@ namespace Explorer
// ============= Resize Window Helper ============ // ============= Resize Window Helper ============
static readonly GUIContent gcDrag = new GUIContent("<->"); //static readonly GUIContent gcDrag = new GUIContent("<->");
private static bool isResizing = false; //private static bool isResizing = false;
private static Rect m_currentResize; //private static Rect m_currentResize;
private static int m_currentWindow; //private static int m_currentWindow;
public static Rect ResizeWindow(Rect _rect, int ID) public static Rect ResizeWindow(Rect _rect, int ID)
{ {