diff --git a/src/CachedObjects/CacheEnum.cs b/src/CachedObjects/CacheEnum.cs index 90fdae3..d8a182b 100644 --- a/src/CachedObjects/CacheEnum.cs +++ b/src/CachedObjects/CacheEnum.cs @@ -10,21 +10,18 @@ namespace Explorer { public class CacheEnum : CacheObject { - private readonly Type m_enumType; - private readonly string[] m_names; + public Type EnumType; + public string[] EnumNames; - public CacheEnum(object obj) + public override void Init() { - if (obj != null) - { - m_enumType = obj.GetType(); - m_names = Enum.GetNames(m_enumType); - } + EnumType = Value.GetType(); + EnumNames = Enum.GetNames(EnumType); } public override void DrawValue(Rect window, float width) { - if (MemberInfo != null) + if (CanWrite) { if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) })) { @@ -38,34 +35,18 @@ namespace Explorer } } - GUILayout.Label(Value.ToString(), null); - } - - public override void SetValue() - { - if (MemberInfo == null) - { - MelonLogger.Log("Trying to SetValue but the MemberInfo is null!"); - return; - } - - if (Enum.Parse(m_enumType, Value.ToString()) is object enumValue && enumValue != null) - { - Value = enumValue; - } - - SetValue(Value, MemberInfo, DeclaringInstance); + GUILayout.Label(Value.ToString(), null);// + " (" + ValueType + ")", null); } public void SetEnum(ref object value, int change) { - var names = m_names.ToList(); + var names = EnumNames.ToList(); int newindex = names.IndexOf(value.ToString()) + change; if ((change < 0 && newindex >= 0) || (change > 0 && newindex < names.Count)) { - value = Enum.Parse(m_enumType, names[newindex]); + value = Enum.Parse(EnumType, names[newindex]); } } } diff --git a/src/CachedObjects/CacheGameObject.cs b/src/CachedObjects/CacheGameObject.cs index 7025531..417093b 100644 --- a/src/CachedObjects/CacheGameObject.cs +++ b/src/CachedObjects/CacheGameObject.cs @@ -10,44 +10,37 @@ namespace Explorer { public class CacheGameObject : CacheObject { - private GameObject m_gameObject; - - public CacheGameObject(object obj) + private GameObject GameObj { - if (obj != null) - m_gameObject = GetGameObject(obj); - } - - private GameObject GetGameObject(object obj) - { - if (obj is Il2CppSystem.Object ilObj) + get { - var ilType = ilObj.GetIl2CppType(); - - if (ilType == ReflectionHelpers.GameObjectType || ilType == ReflectionHelpers.TransformType) + if (m_gameObject == null) { - return ilObj.TryCast() ?? ilObj.TryCast()?.gameObject; - } - } + if (Value is Il2CppSystem.Object ilObj) + { + var ilType = ilObj.GetIl2CppType(); - return null; + if (ilType == ReflectionHelpers.GameObjectType || ilType == ReflectionHelpers.TransformType) + { + m_gameObject = ilObj.TryCast() ?? ilObj.TryCast()?.gameObject; + } + } + } + + return m_gameObject; + } } + private GameObject m_gameObject; + public override void DrawValue(Rect window, float width) { - UIHelpers.GameobjButton(m_gameObject, null, false, width); - } - - public override void SetValue() - { - throw new NotImplementedException("TODO"); + UIHelpers.GameobjButton(GameObj, null, false, width); } public override void UpdateValue() { base.UpdateValue(); - - m_gameObject = GetGameObject(Value); } } } diff --git a/src/CachedObjects/CacheList.cs b/src/CachedObjects/CacheList.cs index 5a0f82a..7dfd644 100644 --- a/src/CachedObjects/CacheList.cs +++ b/src/CachedObjects/CacheList.cs @@ -14,6 +14,7 @@ namespace Explorer { public bool IsExpanded { get; set; } public int ArrayOffset { get; set; } + public int ArrayLimit { get; set; } = 20; public Type EntryType { @@ -47,15 +48,6 @@ namespace Explorer private IEnumerable m_enumerable; private CacheObject[] m_cachedEntries; - public CacheList(object obj) - { - if (obj != null) - { - Value = obj; - EntryType = obj.GetType().GetGenericArguments()[0]; - } - } - private IEnumerable CppListToEnumerable(object list) { if (EntryType == null) return null; @@ -95,12 +87,12 @@ namespace Explorer if (IsExpanded) { - if (count > CppExplorer.ArrayLimit) + if (count > ArrayLimit) { GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(null); GUILayout.Space(190); - int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)CppExplorer.ArrayLimit)) - 1; + int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)ArrayLimit)) - 1; GUILayout.Label($"Page {ArrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) }); // prev/next page buttons if (GUILayout.Button("< Prev", null)) @@ -111,13 +103,20 @@ namespace Explorer { if (ArrayOffset < maxOffset) ArrayOffset++; } + GUILayout.Label("Limit: ", new GUILayoutOption[] { GUILayout.Width(50) }); + var limit = this.ArrayLimit.ToString(); + limit = GUILayout.TextField(limit, new GUILayoutOption[] { GUILayout.Width(50) }); + if (limit != ArrayLimit.ToString() && int.TryParse(limit, out int i)) + { + ArrayLimit = i; + } } - int offset = ArrayOffset * CppExplorer.ArrayLimit; + int offset = ArrayOffset * ArrayLimit; if (offset >= count) offset = 0; - for (int i = offset; i < offset + CppExplorer.ArrayLimit && i < count; i++) + for (int i = offset; i < offset + ArrayLimit && i < count; i++) { var entry = m_cachedEntries[i]; @@ -140,11 +139,6 @@ namespace Explorer } } - public override void SetValue() - { - throw new NotImplementedException("TODO"); - } - /// /// Called when the user presses the "Update" button, or if AutoUpdate is on. /// @@ -161,7 +155,7 @@ namespace Explorer var list = new List(); while (enumerator.MoveNext()) { - list.Add(GetCacheObject(enumerator.Current)); + list.Add(GetCacheObject(enumerator.Current, null, null, this.EntryType)); } m_cachedEntries = list.ToArray(); diff --git a/src/CachedObjects/CacheObject.cs b/src/CachedObjects/CacheObject.cs index 505a0c2..2240025 100644 --- a/src/CachedObjects/CacheObject.cs +++ b/src/CachedObjects/CacheObject.cs @@ -17,15 +17,45 @@ namespace Explorer // Reflection window only public MemberInfo MemberInfo { get; set; } - public ReflectionWindow.MemberInfoType MemberInfoType { get; set; } + // public ReflectionWindow.MemberInfoType MemberInfoType { get; set; } public Type DeclaringType { get; set; } public object DeclaringInstance { get; set; } public string FullName => $"{MemberInfo.DeclaringType.Name}.{MemberInfo.Name}"; public string ReflectionException; + public bool CanWrite + { + get + { + if (MemberInfo is FieldInfo fi) + { + return !(fi.IsLiteral && !fi.IsInitOnly); + } + else if (MemberInfo is PropertyInfo pi) + { + return pi.CanWrite; + } + else + { + return false; + } + } + } + + public ReflectionWindow.MemberInfoType MemberInfoType + { + get + { + if (MemberInfo is FieldInfo) return ReflectionWindow.MemberInfoType.Field; + if (MemberInfo is PropertyInfo) return ReflectionWindow.MemberInfoType.Property; + if (MemberInfo is MethodInfo) return ReflectionWindow.MemberInfoType.Method; + return ReflectionWindow.MemberInfoType.All; + } + } + // methods + public virtual void Init() { } public abstract void DrawValue(Rect window, float width); - public abstract void SetValue(); public static CacheObject GetCacheObject(object obj) { @@ -41,63 +71,72 @@ namespace Explorer /// public static CacheObject GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance) { - CacheObject holder; - var type = ReflectionHelpers.GetActualType(obj) ?? (memberInfo as FieldInfo)?.FieldType ?? (memberInfo as PropertyInfo)?.PropertyType; + if (type == null) + { + MelonLogger.Log("Could not get type for object or memberinfo!"); + return null; + } + + return GetCacheObject(obj, memberInfo, declaringInstance, type); + } + + /// + /// Gets the CacheObject subclass for an object or MemberInfo + /// + /// The current value (can be null if memberInfo is not null) + /// The MemberInfo (can be null if obj is not null) + /// If MemberInfo is not null, the declaring class instance. Can be null if static. + /// The type of the object or MemberInfo value. + /// + public static CacheObject GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type type) + { + CacheObject holder; + if ((obj is Il2CppSystem.Object || typeof(Il2CppSystem.Object).IsAssignableFrom(type)) && (type.FullName.Contains("UnityEngine.GameObject") || type.FullName.Contains("UnityEngine.Transform"))) { - holder = new CacheGameObject(obj); + holder = new CacheGameObject(); } else if (type.IsPrimitive || type == typeof(string)) { - holder = new CachePrimitive(obj); + holder = new CachePrimitive(); } else if (type.IsEnum) { - holder = new CacheEnum(obj); + holder = new CacheEnum(); } else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type) || ReflectionHelpers.IsList(type)) { - holder = new CacheList(obj); + holder = new CacheList(); } else { holder = new CacheOther(); } - if (memberInfo != null) - { - holder.MemberInfo = memberInfo; - holder.DeclaringType = memberInfo.DeclaringType; - holder.DeclaringInstance = declaringInstance; - - if (memberInfo.MemberType == MemberTypes.Field) - { - holder.MemberInfoType = ReflectionWindow.MemberInfoType.Field; - } - else if (memberInfo.MemberType == MemberTypes.Property) - { - holder.MemberInfoType = ReflectionWindow.MemberInfoType.Property; - } - else if (memberInfo.MemberType == MemberTypes.Method) - { - holder.MemberInfoType = ReflectionWindow.MemberInfoType.Method; - } - } - holder.Value = obj; holder.ValueType = type.FullName; + if (memberInfo != null) + { + holder.MemberInfo = memberInfo; + holder.DeclaringType = memberInfo.DeclaringType; + holder.DeclaringInstance = declaringInstance; + } + + holder.UpdateValue(); + holder.Init(); + return holder; } - public void Draw(Rect window, float labelWidth = 180f) + public void Draw(Rect window, float labelWidth = 215f) { if (MemberInfo != null) { - GUILayout.Label("" + FullName + ":", new GUILayoutOption[] { GUILayout.Width(labelWidth) }); + GUILayout.Label("" + FullName + "", new GUILayoutOption[] { GUILayout.Width(labelWidth) }); } else { @@ -110,7 +149,7 @@ namespace Explorer } else if (Value == null) { - GUILayout.Label("null (" + this.ValueType + ")", null); + GUILayout.Label("null (" + ValueType + ")", null); } else { @@ -147,25 +186,19 @@ namespace Explorer } } - public void SetValue(object value, MemberInfo memberInfo, object declaringInstance) + public void SetValue() { try { - if (memberInfo.MemberType == MemberTypes.Field) + if (MemberInfo.MemberType == MemberTypes.Field) { - var fi = memberInfo as FieldInfo; - if (!(fi.IsLiteral && !fi.IsInitOnly)) - { - fi.SetValue(fi.IsStatic ? null : declaringInstance, value); - } + var fi = MemberInfo as FieldInfo; + fi.SetValue(fi.IsStatic ? null : DeclaringInstance, Value); } - else if (memberInfo.MemberType == MemberTypes.Property) + else if (MemberInfo.MemberType == MemberTypes.Property) { - var pi = memberInfo as PropertyInfo; - if (pi.CanWrite) - { - pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : declaringInstance, value); - } + var pi = MemberInfo as PropertyInfo; + pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value); } } catch (Exception e) diff --git a/src/CachedObjects/CacheOther.cs b/src/CachedObjects/CacheOther.cs index d2adf85..f101b96 100644 --- a/src/CachedObjects/CacheOther.cs +++ b/src/CachedObjects/CacheOther.cs @@ -58,21 +58,11 @@ namespace Explorer } GUI.skin.button.alignment = TextAnchor.MiddleLeft; - if (GUILayout.Button("" + label + "", new GUILayoutOption[] { GUILayout.MaxWidth(width) })) + if (GUILayout.Button("" + label + "", new GUILayoutOption[] { GUILayout.MaxWidth(width + 40) })) { WindowManager.InspectObject(Value, out bool _); } GUI.skin.button.alignment = TextAnchor.MiddleCenter; } - - public override void SetValue() - { - throw new NotImplementedException("TODO"); - } - - //public override void UpdateValue(object obj) - //{ - - //} } } diff --git a/src/CachedObjects/CachePrimitive.cs b/src/CachedObjects/CachePrimitive.cs index 26be983..0c7fbfa 100644 --- a/src/CachedObjects/CachePrimitive.cs +++ b/src/CachedObjects/CachePrimitive.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Reflection; using MelonLoader; using UnityEngine; @@ -10,7 +7,7 @@ namespace Explorer { public class CachePrimitive : CacheObject { - public enum PrimitiveType + public enum PrimitiveTypes { Bool, Double, @@ -19,69 +16,122 @@ namespace Explorer String } - private readonly PrimitiveType m_primitiveType; + private string m_valueToString; - public CachePrimitive(object obj) + public PrimitiveTypes PrimitiveType; + + public MethodInfo ParseMethod { - if (obj == null) return; + get + { + if (m_parseMethod == null) + { + Type t = null; + switch (PrimitiveType) + { + case PrimitiveTypes.Bool: + t = typeof(bool); break; + case PrimitiveTypes.Double: + t = typeof(double); break; + case PrimitiveTypes.Float: + t = typeof(float); break; + case PrimitiveTypes.Int: + t = typeof(int); break; + case PrimitiveTypes.String: + t = typeof(string); break; + } + m_parseMethod = t.GetMethod("Parse", new Type[] { typeof(string) }); + } + return m_parseMethod; + } + } - if (obj is bool) + private MethodInfo m_parseMethod; + + public override void Init() + { + if (Value == null) { - m_primitiveType = PrimitiveType.Bool; + // this must mean it is a string? no other primitive type should be nullable + PrimitiveType = PrimitiveTypes.String; + return; } - else if (obj is double) + + m_valueToString = Value.ToString(); + var type = Value.GetType(); + + if (type == typeof(bool)) { - m_primitiveType = PrimitiveType.Double; + PrimitiveType = PrimitiveTypes.Bool; } - else if (obj is float) + else if (type == typeof(double)) { - m_primitiveType = PrimitiveType.Float; + PrimitiveType = PrimitiveTypes.Double; } - else if (obj is int || obj is IntPtr || obj is uint) + else if (type == typeof(float)) { - m_primitiveType = PrimitiveType.Int; + PrimitiveType = PrimitiveTypes.Float; } - else if (obj is string) + else if (type == typeof(int) || type == typeof(long) || type == typeof(uint) || type == typeof(ulong) || type == typeof(IntPtr)) { - m_primitiveType = PrimitiveType.String; + PrimitiveType = PrimitiveTypes.Int; } + else + { + if (type != typeof(string)) + { + MelonLogger.Log("Unsupported primitive: " + type); + } + + PrimitiveType = PrimitiveTypes.String; + } + } + + public override void UpdateValue() + { + base.UpdateValue(); + + m_valueToString = Value?.ToString(); } public override void DrawValue(Rect window, float width) { - if (m_primitiveType == PrimitiveType.Bool && Value is bool b) + if (PrimitiveType == PrimitiveTypes.Bool) { + var b = (bool)Value; var color = "" : "red>"); - Value = GUILayout.Toggle((bool)Value, color + Value.ToString() + "", null); + b = GUILayout.Toggle(b, color + b.ToString() + "", null); if (b != (bool)Value) { - SetValue(); + SetValue(m_valueToString); } } else { - var toString = Value.ToString(); - if (toString.Length > 37) + GUILayout.Label("" + PrimitiveType + "", new GUILayoutOption[] { GUILayout.Width(50) }); + + var _width = window.width - 200; + if (m_valueToString.Length > 37) { - Value = GUILayout.TextArea(toString, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - 260) }); + m_valueToString = GUILayout.TextArea(m_valueToString, new GUILayoutOption[] { GUILayout.MaxWidth(_width) }); } else { - Value = GUILayout.TextField(toString, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - 260) }); + m_valueToString = GUILayout.TextField(m_valueToString, new GUILayoutOption[] { GUILayout.MaxWidth(_width) }); } - - if (MemberInfo != null) + + if (CanWrite) { if (GUILayout.Button("Apply", new GUILayoutOption[] { GUILayout.Width(60) })) { - SetValue(); + SetValue(m_valueToString); } } } } - public override void SetValue() + public void SetValue(string value) { if (MemberInfo == null) { @@ -89,19 +139,24 @@ namespace Explorer return; } - switch (m_primitiveType) + if (PrimitiveType == PrimitiveTypes.String) { - case PrimitiveType.Bool: - SetValue(bool.Parse(Value.ToString()), MemberInfo, DeclaringInstance); return; - case PrimitiveType.Double: - SetValue(double.Parse(Value.ToString()), MemberInfo, DeclaringInstance); return; - case PrimitiveType.Float: - SetValue(float.Parse(Value.ToString()), MemberInfo, DeclaringInstance); return; - case PrimitiveType.Int: - SetValue(int.Parse(Value.ToString()), MemberInfo, DeclaringInstance); return; - case PrimitiveType.String: - SetValue(Value.ToString(), MemberInfo, DeclaringInstance); return; + Value = value; } + else + { + try + { + var val = ParseMethod.Invoke(null, new object[] { value }); + Value = val; + } + catch (Exception e) + { + MelonLogger.Log("Exception parsing value: " + e.GetType() + ", " + e.Message); + } + } + + SetValue(); } } } diff --git a/src/CppExplorer.cs b/src/CppExplorer.cs index afcb79d..b99b2ad 100644 --- a/src/CppExplorer.cs +++ b/src/CppExplorer.cs @@ -13,7 +13,7 @@ namespace Explorer // consts public const string ID = "com.sinai.cppexplorer"; - public const string VERSION = "1.4.1"; + public const string VERSION = "1.4.2"; public const string AUTHOR = "Sinai"; public const string NAME = "CppExplorer" @@ -29,7 +29,6 @@ namespace Explorer // props public static bool ShowMenu { get; set; } = false; - public static int ArrayLimit { get; set; } = 20; // methods diff --git a/src/Helpers/ReflectionHelpers.cs b/src/Helpers/ReflectionHelpers.cs index 0868a20..25e5e8e 100644 --- a/src/Helpers/ReflectionHelpers.cs +++ b/src/Helpers/ReflectionHelpers.cs @@ -22,6 +22,7 @@ namespace Explorer public static Il2CppSystem.Type TransformType => Il2CppType.Of(); public static Il2CppSystem.Type ObjectType => Il2CppType.Of(); public static Il2CppSystem.Type ComponentType => Il2CppType.Of(); + public static Il2CppSystem.Type BehaviourType => Il2CppType.Of(); private static readonly MethodInfo m_tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast"); diff --git a/src/Helpers/UIHelpers.cs b/src/Helpers/UIHelpers.cs index d72d479..cbf4bd9 100644 --- a/src/Helpers/UIHelpers.cs +++ b/src/Helpers/UIHelpers.cs @@ -22,7 +22,7 @@ namespace Explorer } // helper for drawing a styled button for a GameObject or Transform - public static void GameobjButton(GameObject obj, Action specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380) + public static void GameobjButton(GameObject obj, Action specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380) { bool children = obj.transform.childCount > 0; @@ -52,7 +52,7 @@ namespace Explorer FastGameobjButton(obj, color, label, obj.activeSelf, specialInspectMethod, showSmallInspectBtn, width); } - public static void FastGameobjButton(GameObject obj, Color activeColor, string label, bool enabled, Action specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380) + public static void FastGameobjButton(GameObject obj, Color activeColor, string label, bool enabled, Action specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380) { if (!obj) { @@ -79,7 +79,7 @@ namespace Explorer { if (specialInspectMethod != null) { - specialInspectMethod(obj); + specialInspectMethod(obj.transform); } else { @@ -94,13 +94,18 @@ namespace Explorer if (showSmallInspectBtn) { - if (GUILayout.Button("Inspect", null)) - { - WindowManager.InspectObject(obj, out bool _); - } + SmallInspectButton(obj); } GUILayout.EndHorizontal(); } + + public static void SmallInspectButton(object obj) + { + if (GUILayout.Button("Inspect", null)) + { + WindowManager.InspectObject(obj, out bool _); + } + } } } diff --git a/src/MainMenu/MainMenu.cs b/src/MainMenu/MainMenu.cs index 640a7a3..512fca8 100644 --- a/src/MainMenu/MainMenu.cs +++ b/src/MainMenu/MainMenu.cs @@ -88,19 +88,6 @@ namespace Explorer private void MainHeader() { - GUILayout.BeginHorizontal(null); - GUILayout.Label("Options:", new GUILayoutOption[] { GUILayout.Width(70) }); - GUI.skin.label.alignment = TextAnchor.MiddleRight; - GUILayout.Label("Array Limit:", new GUILayoutOption[] { GUILayout.Width(70) }); - GUI.skin.label.alignment = TextAnchor.UpperLeft; - var _input = GUILayout.TextField(CppExplorer.ArrayLimit.ToString(), new GUILayoutOption[] { GUILayout.Width(60) }); - if (int.TryParse(_input, out int _lim)) - { - CppExplorer.ArrayLimit = _lim; - } - InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", null); - GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(null); for (int i = 0; i < Pages.Count; i++) { @@ -116,6 +103,9 @@ namespace Explorer } GUILayout.EndHorizontal(); + GUI.color = Color.white; + InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", null); + GUILayout.Space(10); GUI.color = Color.white; } diff --git a/src/MainMenu/Pages/ScenePage.cs b/src/MainMenu/Pages/ScenePage.cs index c5e61da..8166b4c 100644 --- a/src/MainMenu/Pages/ScenePage.cs +++ b/src/MainMenu/Pages/ScenePage.cs @@ -14,9 +14,15 @@ namespace Explorer public override string Name { get => "Scene Explorer"; set => base.Name = value; } - // ----- Holders for GUI elements ----- // + private int m_pageOffset = 0; + private int m_limit = 20; + private int m_currentTotalCount = 0; - private string m_currentScene = ""; + private float m_timeOfLastUpdate = -1f; + + // ----- Holders for GUI elements ----- // + + private string m_currentScene = ""; // gameobject list private Transform m_currentTransform; @@ -37,190 +43,84 @@ namespace Explorer public void OnSceneChange() { m_currentScene = UnityHelpers.ActiveSceneName; + SetTransformTarget(null); + } - m_currentTransform = null; - CancelSearch(); + public void CheckOffset(ref int offset, int childCount) + { + if (offset >= childCount) + { + offset = 0; + m_pageOffset = 0; + } } public override void Update() { - if (!m_searching) + if (m_searching) return; + + if (Time.time - m_timeOfLastUpdate < 1f) return; + m_timeOfLastUpdate = Time.time; + + m_objectList = new List(); + int offset = m_pageOffset * m_limit; + + var allTransforms = new List(); + + // get current list of all transforms (either scene root or our current transform children) + if (m_currentTransform) { - m_objectList = new List(); - if (m_currentTransform) + for (int i = 0; i < m_currentTransform.childCount; i++) { - var endAppend = new List(); - for (int i = 0; i < m_currentTransform.childCount; i++) - { - var child = m_currentTransform.GetChild(i); - - if (child) - { - if (child.childCount > 0) - m_objectList.Add(new GameObjectCache(child.gameObject)); - else - endAppend.Add(new GameObjectCache(child.gameObject)); - } - } - m_objectList.AddRange(endAppend); - endAppend = null; + allTransforms.Add(m_currentTransform.GetChild(i)); } - else + } + else + { + var scene = SceneManager.GetSceneByName(m_currentScene); + var rootObjects = scene.GetRootGameObjects(); + + foreach (var obj in rootObjects) { - var scene = SceneManager.GetSceneByName(m_currentScene); - var rootObjects = scene.GetRootGameObjects(); - - // add objects with children first - foreach (var obj in rootObjects.Where(x => x.transform.childCount > 0)) - { - m_objectList.Add(new GameObjectCache(obj)); - } - foreach (var obj in rootObjects.Where(x => x.transform.childCount == 0)) - { - m_objectList.Add(new GameObjectCache(obj)); - } + allTransforms.Add(obj.transform); } } + + m_currentTotalCount = allTransforms.Count; + + // make sure offset doesn't exceed count + CheckOffset(ref offset, m_currentTotalCount); + + // sort by childcount + allTransforms.Sort((a, b) => b.childCount.CompareTo(a.childCount)); + + for (int i = offset; i < offset + m_limit && i < m_currentTotalCount; i++) + { + var child = allTransforms[i]; + m_objectList.Add(new GameObjectCache(child.gameObject)); + } } - // --------- GUI Draw Functions --------- // - - public override void DrawWindow() + public void SetTransformTarget(Transform t) { - try - { - GUILayout.BeginHorizontal(null); - // Current Scene label - GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) }); - try - { - // Need to do 'ToList()' so the object isn't cleaned up by Il2Cpp GC. - var scenes = SceneManager.GetAllScenes().ToList(); + m_currentTransform = t; - if (scenes.Count > 1) - { - int changeWanted = 0; - if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(30) })) - { - changeWanted = -1; - } - if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(30) })) - { - changeWanted = 1; - } - if (changeWanted != 0) - { - int index = scenes.IndexOf(SceneManager.GetSceneByName(m_currentScene)); - index += changeWanted; - if (index > scenes.Count - 1) - { - index = 0; - } - else if (index < 0) - { - index = scenes.Count - 1; - } - m_currentScene = scenes[index].name; - } - } - } - catch { } - GUILayout.Label("" + m_currentScene + "", null); //new GUILayoutOption[] { GUILayout.Width(250) }); + if (m_searching) + CancelSearch(); - GUILayout.EndHorizontal(); - - // ----- GameObject Search ----- - GUILayout.BeginHorizontal(GUI.skin.box, null); - GUILayout.Label("Search Scene:", new GUILayoutOption[] { GUILayout.Width(100) }); - m_searchInput = GUILayout.TextField(m_searchInput, null); - if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) })) - { - Search(); - } - GUILayout.EndHorizontal(); - - GUILayout.Space(15); - - // ************** GameObject list *************** - - // ----- main explorer ------ - if (!m_searching) - { - if (m_currentTransform != null) - { - GUILayout.BeginHorizontal(null); - if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) })) - { - TraverseUp(); - } - else - { - GUILayout.Label(m_currentTransform.GetGameObjectPath(), null); - } - GUILayout.EndHorizontal(); - } - else - { - GUILayout.Label("Scene Root GameObjects:", null); - } - - if (m_objectList.Count > 0) - { - foreach (var obj in m_objectList) - { - //UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170); - UIHelpers.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170); - } - } - } - else // ------ Scene Search results ------ - { - if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) })) - { - CancelSearch(); - } - - GUILayout.Label("Search Results:", null); - - if (m_searchResults.Count > 0) - { - foreach (var obj in m_searchResults) - { - //UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170); - UIHelpers.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170); - } - } - else - { - GUILayout.Label("No results found!", null); - } - } - } - catch - { - m_currentTransform = null; - } - } - - - - // -------- Actual Methods (not drawing GUI) ---------- // - - public void SetTransformTarget(GameObject obj) - { - m_currentTransform = obj.transform; - CancelSearch(); + m_timeOfLastUpdate = -1f; + Update(); } public void TraverseUp() { if (m_currentTransform.parent != null) { - m_currentTransform = m_currentTransform.parent; + SetTransformTarget(m_currentTransform.parent); } else { - m_currentTransform = null; + SetTransformTarget(null); } } @@ -228,6 +128,7 @@ namespace Explorer { m_searchResults = SearchSceneObjects(m_searchInput); m_searching = true; + m_currentTotalCount = m_searchResults.Count; } public void CancelSearch() @@ -249,6 +150,220 @@ namespace Explorer return matches; } + + // --------- GUI Draw Function --------- // + + public override void DrawWindow() + { + try + { + DrawHeaderArea(); + + GUILayout.BeginVertical(GUI.skin.box, null); + + DrawPageButtons(); + + if (!m_searching) + { + DrawGameObjectList(); + } + else + { + DrawSearchResultsList(); + } + + GUILayout.EndVertical(); + } + catch (Exception e) + { + MelonLogger.Log("Exception drawing ScenePage! " + e.GetType() + ", " + e.Message); + MelonLogger.Log(e.StackTrace); + m_currentTransform = null; + } + } + + private void DrawHeaderArea() + { + GUILayout.BeginHorizontal(null); + + // Current Scene label + GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) }); + try + { + // Need to do 'ToList()' so the object isn't cleaned up by Il2Cpp GC. + var scenes = SceneManager.GetAllScenes().ToList(); + + if (scenes.Count > 1) + { + int changeWanted = 0; + if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(30) })) + { + changeWanted = -1; + } + if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(30) })) + { + changeWanted = 1; + } + if (changeWanted != 0) + { + int index = scenes.IndexOf(SceneManager.GetSceneByName(m_currentScene)); + index += changeWanted; + if (index > scenes.Count - 1) + { + index = 0; + } + else if (index < 0) + { + index = scenes.Count - 1; + } + m_currentScene = scenes[index].name; + } + } + } + catch { } + GUILayout.Label("" + m_currentScene + "", null); //new GUILayoutOption[] { GUILayout.Width(250) }); + + GUILayout.EndHorizontal(); + + // ----- GameObject Search ----- + GUILayout.BeginHorizontal(GUI.skin.box, null); + GUILayout.Label("Search Scene:", new GUILayoutOption[] { GUILayout.Width(100) }); + m_searchInput = GUILayout.TextField(m_searchInput, null); + if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) })) + { + Search(); + } + GUILayout.EndHorizontal(); + + GUILayout.Space(5); + } + + private void DrawPageButtons() + { + GUILayout.BeginHorizontal(null); + + GUILayout.Label("Limit per page: ", new GUILayoutOption[] { GUILayout.Width(100) }); + var limit = m_limit.ToString(); + limit = GUILayout.TextField(limit, new GUILayoutOption[] { GUILayout.Width(30) }); + if (int.TryParse(limit, out int lim)) + { + m_limit = lim; + } + + // prev/next page buttons + if (m_currentTotalCount > m_limit) + { + int count = m_currentTotalCount; + int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)m_limit)) - 1; + if (GUILayout.Button("< Prev", null)) + { + if (m_pageOffset > 0) m_pageOffset--; + m_timeOfLastUpdate = -1f; + Update(); + } + + GUI.skin.label.alignment = TextAnchor.MiddleCenter; + GUILayout.Label($"Page {m_pageOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) }); + + if (GUILayout.Button("Next >", null)) + { + if (m_pageOffset < maxOffset) m_pageOffset++; + m_timeOfLastUpdate = -1f; + Update(); + } + } + + GUILayout.EndHorizontal(); + GUI.skin.label.alignment = TextAnchor.UpperLeft; + } + + private void DrawGameObjectList() + { + if (m_currentTransform != null) + { + GUILayout.BeginHorizontal(null); + if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) })) + { + TraverseUp(); + } + else + { + GUILayout.Label("" + m_currentTransform.GetGameObjectPath() + "", + new GUILayoutOption[] { GUILayout.Width(MainMenu.MainRect.width - 187f) }); + } + + UIHelpers.SmallInspectButton(m_currentTransform); + + GUILayout.EndHorizontal(); + } + else + { + GUILayout.Label("Scene Root GameObjects:", null); + } + + if (m_objectList.Count > 0) + { + foreach (var obj in m_objectList) + { + if (!obj.RefGameObject) + { + string label = "null"; + + if (obj.RefGameObject != null) + { + label += " (Destroyed)"; + } + + label += ""; + GUILayout.Label(label, null); + } + else + { + UIHelpers.FastGameobjButton(obj.RefGameObject, + obj.EnabledColor, + obj.Label, + obj.RefGameObject.activeSelf, + SetTransformTarget, + true, + MainMenu.MainRect.width - 170); + } + } + } + } + + private void DrawSearchResultsList() + { + if (GUILayout.Button("<- Cancel Search", new GUILayoutOption[] { GUILayout.Width(150) })) + { + CancelSearch(); + } + + GUILayout.Label("Search Results:", null); + + if (m_searchResults.Count > 0) + { + int offset = m_pageOffset * m_limit; + + if (offset >= m_searchResults.Count) + { + offset = 0; + m_pageOffset = 0; + } + + for (int i = offset; i < offset + m_limit && offset < m_searchResults.Count; i++) + { + var obj = m_searchResults[i]; + + UIHelpers.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170); + } + } + else + { + GUILayout.Label("No results found!", null); + } + } + + // -------- Mini GameObjectCache class ---------- // public class GameObjectCache { diff --git a/src/MainMenu/Pages/SearchPage.cs b/src/MainMenu/Pages/SearchPage.cs index 4de7292..e978a20 100644 --- a/src/MainMenu/Pages/SearchPage.cs +++ b/src/MainMenu/Pages/SearchPage.cs @@ -135,7 +135,7 @@ namespace Explorer int offset = m_pageOffset * this.m_limit; if (offset >= count) m_pageOffset = 0; - for (int i = offset; i < offset + CppExplorer.ArrayLimit && i < count; i++) + for (int i = offset; i < offset + m_limit && i < count; i++) { m_searchResults[i].Draw(MainMenu.MainRect, 0f); //m_searchResults[i].DrawValue(MainMenu.MainRect); diff --git a/src/Windows/GameObjectWindow.cs b/src/Windows/GameObjectWindow.cs index c66fb30..bcf5ba4 100644 --- a/src/Windows/GameObjectWindow.cs +++ b/src/Windows/GameObjectWindow.cs @@ -29,7 +29,7 @@ namespace Explorer private float m_rotateAmount = 50f; private float m_scaleAmount = 0.1f; - List m_cachedDestroyList = new List(); + private List m_cachedDestroyList = new List(); //private string m_addComponentInput = ""; private string m_setParentInput = ""; @@ -91,7 +91,7 @@ namespace Explorer } } - private void InspectGameObject(GameObject obj) + private void InspectGameObject(Transform obj) { var window = WindowManager.InspectObject(obj, out bool created); @@ -137,7 +137,7 @@ namespace Explorer { if (GUILayout.Button("< View in Scene Explorer", new GUILayoutOption[] { GUILayout.Width(230) })) { - ScenePage.Instance.SetTransformTarget(m_object); + ScenePage.Instance.SetTransformTarget(m_object.transform); MainMenu.SetCurrentPage(0); } } @@ -150,7 +150,7 @@ namespace Explorer { if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) })) { - InspectGameObject(m_object.transform.parent.gameObject); + InspectGameObject(m_object.transform.parent); } } GUILayout.TextArea(pathlabel, null); @@ -235,18 +235,16 @@ namespace Explorer var m_components = new Il2CppSystem.Collections.Generic.List(); m_object.GetComponentsInternal(Il2CppType.Of(), false, false, true, false, m_components); - var ilTypeOfTransform = Il2CppType.Of(); - var ilTypeOfBehaviour = Il2CppType.Of(); foreach (var component in m_components) { var ilType = component.GetIl2CppType(); - if (ilType == ilTypeOfTransform) + if (ilType == ReflectionHelpers.TransformType) { continue; } GUILayout.BeginHorizontal(null); - if (ilTypeOfBehaviour.IsAssignableFrom(ilType)) + if (ReflectionHelpers.BehaviourType.IsAssignableFrom(ilType)) { BehaviourEnabledBtn(component.TryCast()); } @@ -274,24 +272,6 @@ namespace Explorer GUILayout.EndScrollView(); - //GUILayout.BeginHorizontal(null); - //m_addComponentInput = GUILayout.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 150) }); - //if (GUILayout.Button("Add Component", new GUILayoutOption[] { GUILayout.Width(120) })) - //{ - // if (HPExplorer.GetType(m_addComponentInput) is Type type && typeof(Component).IsAssignableFrom(type)) - // { - // var comp = m_object.AddComponent(type); - // var list = m_components.ToList(); - // list.Add(comp); - // m_components = list.ToArray(); - // } - // else - // { - // MelonLogger.LogWarning($"Could not get type '{m_addComponentInput}'. If it's not a typo, try the fully qualified name."); - // } - //} - //GUILayout.EndHorizontal(); - GUILayout.EndVertical(); } diff --git a/src/Windows/ReflectionWindow.cs b/src/Windows/ReflectionWindow.cs index ad734b5..1b1a649 100644 --- a/src/Windows/ReflectionWindow.cs +++ b/src/Windows/ReflectionWindow.cs @@ -25,7 +25,7 @@ namespace Explorer private bool m_autoUpdate = false; private string m_search = ""; public MemberInfoType m_filter = MemberInfoType.Property; - private bool m_hideFailedReflection = true; + private bool m_hideFailedReflection = false; public enum MemberInfoType { @@ -135,13 +135,25 @@ namespace Explorer { if (member.Name == "Il2CppType") continue; - var name = member.DeclaringType.Name + "." + member.Name; - if (names.Contains(name)) continue; - names.Add(name); + try + { + var name = member.DeclaringType.Name + "." + member.Name; + if (names.Contains(name)) continue; + names.Add(name); - var cached = CacheObject.GetCacheObject(null, member, target); - list.Add(cached); - cached.ReflectionException = exception; + var cached = CacheObject.GetCacheObject(null, member, target); + if (cached != null) + { + list.Add(cached); + cached.ReflectionException = exception; + } + } + catch (Exception e) + { + MelonLogger.Log("Exception caching member!"); + MelonLogger.Log(e.GetType() + ", " + e.Message); + MelonLogger.Log(e.StackTrace); + } } } } @@ -160,7 +172,7 @@ namespace Explorer GUILayout.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box); GUILayout.BeginHorizontal(null); - GUILayout.Label("Type: " + ObjectType.Name + "", null); + GUILayout.Label("Type: " + ObjectType.FullName + "", null); bool unityObj = Target is UnityEngine.Object; @@ -238,6 +250,7 @@ namespace Explorer if (GUILayout.Button("< Prev", null)) { if (m_pageOffset > 0) m_pageOffset--; + scroll = Vector2.zero; } GUILayout.Label($"Page {m_pageOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) }); @@ -245,6 +258,7 @@ namespace Explorer if (GUILayout.Button("Next >", null)) { if (m_pageOffset < maxOffset) m_pageOffset++; + scroll = Vector2.zero; } GUILayout.EndHorizontal(); } diff --git a/src/Windows/WindowManager.cs b/src/Windows/WindowManager.cs index 49d3fd8..02a4352 100644 --- a/src/Windows/WindowManager.cs +++ b/src/Windows/WindowManager.cs @@ -104,7 +104,7 @@ namespace Explorer foreach (var window in Windows) { - if (obj == window.Target) + if (ReferenceEquals(obj, window.Target)) { GUI.BringWindowToFront(window.windowID); GUI.FocusWindow(window.windowID);