diff --git a/README.md b/README.md index 00378d3..2006762 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,6 @@ Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be ins CppExplorer can force the mouse to be visible and unlocked when the menu is open, if you have enabled "Force Unlock Mouse" (Left-Alt toggle). However, you may also want to prevent the mouse clicking-through onto the game behind CppExplorer, this is possible but it requires specific patches for that game. * For VRChat, use [VRCExplorerMouseControl](https://github.com/sinaioutlander/VRCExplorerMouseControl) -* For Hellpoint, use [HPExplorerMouseControl](https://github.com/sinaioutlander/Hellpoint-Mods/tree/master/HPExplorerMouseControl/HPExplorerMouseControl) * You can create your own mini-plugin using one of the two plugins above as an example. Usually only 1 or 2 simple Harmony patches are needed to fix the problem (if you want to submit that here, feel free to make a PR to this Readme). ## Images diff --git a/src/CachedObjects/CacheDictionary.cs b/src/CachedObjects/CacheDictionary.cs new file mode 100644 index 0000000..6454bcc --- /dev/null +++ b/src/CachedObjects/CacheDictionary.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MelonLoader; +using UnityEngine; + +namespace Explorer +{ + public class CacheDictionary : CacheObjectBase + { + + + public override void Init() + { + //base.Init(); + + Value = "Unsupported"; + } + + public override void UpdateValue() + { + //base.UpdateValue(); + + + } + + public override void DrawValue(Rect window, float width) + { + GUILayout.Label("Dictionary (unsupported)", null); + } + } +} diff --git a/src/CachedObjects/CacheEnum.cs b/src/CachedObjects/CacheEnum.cs index d8a182b..00f3d09 100644 --- a/src/CachedObjects/CacheEnum.cs +++ b/src/CachedObjects/CacheEnum.cs @@ -8,7 +8,7 @@ using UnityEngine; namespace Explorer { - public class CacheEnum : CacheObject + public class CacheEnum : CacheObjectBase { public Type EnumType; public string[] EnumNames; diff --git a/src/CachedObjects/CacheGameObject.cs b/src/CachedObjects/CacheGameObject.cs index 417093b..34bf0af 100644 --- a/src/CachedObjects/CacheGameObject.cs +++ b/src/CachedObjects/CacheGameObject.cs @@ -8,7 +8,7 @@ using UnityEngine; namespace Explorer { - public class CacheGameObject : CacheObject + public class CacheGameObject : CacheObjectBase { private GameObject GameObj { diff --git a/src/CachedObjects/CacheList.cs b/src/CachedObjects/CacheList.cs index dfb9270..7bdd720 100644 --- a/src/CachedObjects/CacheList.cs +++ b/src/CachedObjects/CacheList.cs @@ -3,34 +3,40 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using MelonLoader; -using Mono.CSharp; using UnityEngine; namespace Explorer { - public partial class CacheList : CacheObject + public partial class CacheList : CacheObjectBase { public bool IsExpanded { get; set; } public int ArrayOffset { get; set; } public int ArrayLimit { get; set; } = 20; - + + public float WhiteSpace = 215f; + public float ButtonWidthOffset = 290f; + public Type EntryType { get { if (m_entryType == null) { - switch (this.MemberInfoType) + if (this.MemberInfo != null) { - case ReflectionWindow.MemberInfoType.Field: - m_entryType = (MemberInfo as FieldInfo).FieldType.GetGenericArguments()[0]; - break; - case ReflectionWindow.MemberInfoType.Property: - m_entryType = (MemberInfo as PropertyInfo).PropertyType.GetGenericArguments()[0]; - break; + switch (this.MemberInfoType) + { + 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; @@ -55,7 +61,7 @@ namespace Explorer } private IEnumerable m_enumerable; - private CacheObject[] m_cachedEntries; + private CacheObjectBase[] m_cachedEntries; public MethodInfo GenericToArrayMethod { @@ -97,7 +103,7 @@ namespace Explorer GUI.skin.button.alignment = TextAnchor.MiddleLeft; string btnLabel = "[" + count + "] " + EntryType + ""; - if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - 260) })) + if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - ButtonWidthOffset) })) { WindowManager.InspectObject(Value, out bool _); } @@ -107,8 +113,12 @@ namespace Explorer if (IsExpanded) { - float whitespace = 215; - ClampLabelWidth(window, ref whitespace); + float whitespace = WhiteSpace; + + if (whitespace > 0) + { + ClampLabelWidth(window, ref whitespace); + } if (count > ArrayLimit) { @@ -120,11 +130,11 @@ namespace Explorer 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)) + if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(60) })) { if (ArrayOffset > 0) ArrayOffset--; } - if (GUILayout.Button("Next >", null)) + if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(60) })) { if (ArrayOffset < maxOffset) ArrayOffset++; } @@ -186,7 +196,7 @@ namespace Explorer if (enumerator == null) return; - var list = new List(); + var list = new List(); while (enumerator.MoveNext()) { list.Add(GetCacheObject(enumerator.Current, null, null, this.EntryType)); diff --git a/src/CachedObjects/CacheMethod.cs b/src/CachedObjects/CacheMethod.cs new file mode 100644 index 0000000..30d4518 --- /dev/null +++ b/src/CachedObjects/CacheMethod.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Reflection; +using UnityEngine; +using MelonLoader; + +namespace Explorer +{ + public class CacheMethod : CacheObjectBase + { + private bool m_evaluated = false; + private CacheObjectBase m_cachedReturnValue; + + public static bool CanEvaluate(MethodInfo mi) + { + if (mi.GetParameters().Length > 0 || mi.GetGenericArguments().Length > 0) + { + // Currently methods with arguments are not supported (no good way to input them). + return false; + } + + return true; + } + + public override void Init() + { + base.Init(); + } + + public override void UpdateValue() + { + base.UpdateValue(); + } + + private void Evaluate() + { + m_evaluated = true; + + var mi = MemberInfo as MethodInfo; + var ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, new object[0]); + + if (ret != null) + { + m_cachedReturnValue = GetCacheObject(ret); + if (m_cachedReturnValue is CacheList cacheList) + { + cacheList.WhiteSpace = 0f; + cacheList.ButtonWidthOffset += 70f; + } + m_cachedReturnValue.UpdateValue(); + } + else + { + m_cachedReturnValue = null; + } + } + + public override void DrawValue(Rect window, float width) + { + GUILayout.BeginVertical(null); + + GUILayout.BeginHorizontal(null); + if (GUILayout.Button("Evaluate", new GUILayoutOption[] { GUILayout.Width(70) })) + { + Evaluate(); + } + GUI.skin.label.wordWrap = false; + GUILayout.Label($"{ValueType}", null); + GUI.skin.label.wordWrap = true; + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(null); + if (m_evaluated) + { + if (m_cachedReturnValue != null) + { + try + { + m_cachedReturnValue.DrawValue(window, width); + } + catch (Exception e) + { + MelonLogger.Log("Exception drawing m_cachedReturnValue!"); + MelonLogger.Log(e.ToString()); + } + } + else + { + GUILayout.Label($"null", null); + } + } + else + { + GUILayout.Label($"Not yet evaluated", null); + } + GUILayout.EndHorizontal(); + + GUILayout.EndVertical(); + } + } +} diff --git a/src/CachedObjects/CacheObject.cs b/src/CachedObjects/CacheObjectBase.cs similarity index 74% rename from src/CachedObjects/CacheObject.cs rename to src/CachedObjects/CacheObjectBase.cs index 54cb20d..2bdd936 100644 --- a/src/CachedObjects/CacheObject.cs +++ b/src/CachedObjects/CacheObjectBase.cs @@ -10,14 +10,13 @@ using UnityEngine; namespace Explorer { - public abstract class CacheObject + public abstract class CacheObjectBase { public object Value; public string ValueType; // Reflection window only public MemberInfo MemberInfo { 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}"; @@ -42,14 +41,14 @@ namespace Explorer } } - public ReflectionWindow.MemberInfoType MemberInfoType + public MemberTypes 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; + if (MemberInfo is FieldInfo) return MemberTypes.Field; + if (MemberInfo is PropertyInfo) return MemberTypes.Property; + if (MemberInfo is MethodInfo) return MemberTypes.Method; + return MemberTypes.All; } } @@ -57,7 +56,7 @@ namespace Explorer public virtual void Init() { } public abstract void DrawValue(Rect window, float width); - public static CacheObject GetCacheObject(object obj) + public static CacheObjectBase GetCacheObject(object obj) { return GetCacheObject(obj, null, null); } @@ -69,13 +68,39 @@ namespace Explorer /// The MemberInfo (can be null if obj is not null) /// If MemberInfo is not null, the declaring class instance. Can be null if static. /// - public static CacheObject 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; + //var type = ReflectionHelpers.GetActualType(obj) ?? (memberInfo as FieldInfo)?.FieldType ?? (memberInfo as PropertyInfo)?.PropertyType; + + Type type = null; + + if (obj != null) + { + type = ReflectionHelpers.GetActualType(obj); + } + else if (memberInfo != null) + { + if (memberInfo is FieldInfo fi) + { + type = fi.FieldType; + } + else if (memberInfo is PropertyInfo pi) + { + type = pi.PropertyType; + } + else if (memberInfo is MethodInfo mi) + { + type = mi.ReturnType; + } + } if (type == null) { MelonLogger.Log("Could not get type for object or memberinfo!"); + if (memberInfo is MethodInfo) + { + MelonLogger.Log("is it void?"); + } return null; } @@ -90,12 +115,22 @@ namespace Explorer /// 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 valueType) + public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType) { - CacheObject holder; + CacheObjectBase holder; - if ((obj is Il2CppSystem.Object || typeof(Il2CppSystem.Object).IsAssignableFrom(valueType)) - && (valueType.FullName.Contains("UnityEngine.GameObject") || valueType.FullName.Contains("UnityEngine.Transform"))) + if (memberInfo is MethodInfo mi) + { + if (CacheMethod.CanEvaluate(mi)) + { + holder = new CacheMethod(); + } + else + { + return null; + } + } + else if (valueType == typeof(GameObject) || valueType == typeof(Transform)) { holder = new CacheGameObject(); } @@ -107,10 +142,14 @@ namespace Explorer { holder = new CacheEnum(); } - else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(valueType) || ReflectionHelpers.IsList(valueType)) + else if (ReflectionHelpers.IsArray(valueType) || ReflectionHelpers.IsList(valueType)) { holder = new CacheList(); } + else if (ReflectionHelpers.IsDictionary(valueType)) + { + holder = new CacheDictionary(); + } else { holder = new CacheOther(); @@ -163,7 +202,7 @@ namespace Explorer { GUILayout.Label("Reflection failed! (" + ReflectionException + ")", null); } - else if (Value == null) + else if (Value == null && MemberInfoType != MemberTypes.Method) { GUILayout.Label("null (" + ValueType + ")", null); } diff --git a/src/CachedObjects/CacheOther.cs b/src/CachedObjects/CacheOther.cs index f101b96..a4d5044 100644 --- a/src/CachedObjects/CacheOther.cs +++ b/src/CachedObjects/CacheOther.cs @@ -9,7 +9,7 @@ using UnityEngine; namespace Explorer { - public class CacheOther : CacheObject + public class CacheOther : CacheObjectBase { private MethodInfo m_toStringMethod; private bool m_triedToGetMethod; diff --git a/src/CachedObjects/CachePrimitive.cs b/src/CachedObjects/CachePrimitive.cs index 4d274cf..7b3190a 100644 --- a/src/CachedObjects/CachePrimitive.cs +++ b/src/CachedObjects/CachePrimitive.cs @@ -1,11 +1,12 @@ using System; +using System.Collections.Generic; using System.Reflection; using MelonLoader; using UnityEngine; namespace Explorer { - public class CachePrimitive : CacheObject + public class CachePrimitive : CacheObjectBase { public enum PrimitiveTypes { @@ -13,7 +14,8 @@ namespace Explorer Double, Float, Int, - String + String, + Char } private string m_valueToString; @@ -37,10 +39,10 @@ namespace Explorer t = typeof(float); break; case PrimitiveTypes.Int: t = typeof(int); break; - case PrimitiveTypes.String: - t = typeof(string); break; + case PrimitiveTypes.Char: + t = typeof(char); break; } - m_parseMethod = t.GetMethod("Parse", new Type[] { typeof(string) }); + m_parseMethod = t?.GetMethod("Parse", new Type[] { typeof(string) }); } return m_parseMethod; } @@ -52,7 +54,7 @@ namespace Explorer { if (Value == null) { - // this must mean it is a string? no other primitive type should be nullable + // this must mean it is a string. No other primitive type should be nullable. PrimitiveType = PrimitiveTypes.String; return; } @@ -72,21 +74,39 @@ namespace Explorer { PrimitiveType = PrimitiveTypes.Float; } - else if (type == typeof(int) || type == typeof(long) || type == typeof(uint) || type == typeof(ulong) || type == typeof(IntPtr)) + else if (IsInteger(type)) { PrimitiveType = PrimitiveTypes.Int; } + else if (type == typeof(char)) + { + PrimitiveType = PrimitiveTypes.Char; + } else { - if (type != typeof(string)) - { - MelonLogger.Log("Unsupported primitive: " + type); - } - PrimitiveType = PrimitiveTypes.String; } } + private static bool IsInteger(Type type) + { + // For our purposes, all types of int can be treated the same, including IntPtr. + return _integerTypes.Contains(type); + } + + private static readonly HashSet _integerTypes = new HashSet + { + typeof(int), + typeof(uint), + typeof(short), + typeof(ushort), + typeof(long), + typeof(ulong), + typeof(byte), + typeof(sbyte), + typeof(IntPtr) + }; + public override void UpdateValue() { base.UpdateValue(); @@ -99,21 +119,26 @@ namespace Explorer if (PrimitiveType == PrimitiveTypes.Bool) { var b = (bool)Value; - var color = "" : "red>"); - b = GUILayout.Toggle(b, color + b.ToString() + "", null); + var color = $"" : "red>")}"; + var label = $"{color}{b}"; - if (b != (bool)Value) + if (CanWrite) { - SetValue(m_valueToString); + b = GUILayout.Toggle(b, label, null); + if (b != (bool)Value) + { + SetValue(m_valueToString); + } + } + else + { + GUILayout.Label(label, null); } } else { GUILayout.Label("" + PrimitiveType + "", new GUILayoutOption[] { GUILayout.Width(50) }); - //var content = new GUIContent(m_valueToString); - //var contentSize = GUI.skin.textField.CalcSize(content); - int dynSize = 25 + (m_valueToString.Length * 15); var maxwidth = window.width - 300f; if (CanWrite) maxwidth -= 60; diff --git a/src/CppExplorer.cs b/src/CppExplorer.cs index 69a71d2..bd66eb3 100644 --- a/src/CppExplorer.cs +++ b/src/CppExplorer.cs @@ -170,32 +170,34 @@ namespace Explorer } } - // Make it appear as though UnlockMouse is disabled to the rest of the application. + // Temporarily disabled this because I don't think it's actually useful, and may in fact cause problems instead - [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; - } - } - } + //// Make it appear as though UnlockMouse is disabled to the rest of the application. - [HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Getter)] - public class Cursor_get_lockState - { - [HarmonyPostfix] - public static void Postfix(ref CursorLockMode __result) - { - if (ShouldForceMouse) - { - __result = m_lastLockMode; - } - } - } + //[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Getter)] + //public class Cursor_get_visible + //{ + // [HarmonyPostfix] + // public static void Postfix(ref bool __result) + // { + // if (ShouldForceMouse) + // { + // __result = m_lastVisibleState; + // } + // } + //} + + //[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Getter)] + //public class Cursor_get_lockState + //{ + // [HarmonyPostfix] + // public static void Postfix(ref CursorLockMode __result) + // { + // if (ShouldForceMouse) + // { + // __result = m_lastLockMode; + // } + // } + //} } } diff --git a/src/CppExplorer.csproj b/src/CppExplorer.csproj index fdde414..4b446cd 100644 --- a/src/CppExplorer.csproj +++ b/src/CppExplorer.csproj @@ -120,11 +120,13 @@ + + @@ -132,7 +134,7 @@ - + diff --git a/src/Helpers/ReflectionHelpers.cs b/src/Helpers/ReflectionHelpers.cs index 2b416f9..b0912a1 100644 --- a/src/Helpers/ReflectionHelpers.cs +++ b/src/Helpers/ReflectionHelpers.cs @@ -72,6 +72,11 @@ namespace Explorer return false; } + public static bool IsArray(Type t) + { + return typeof(System.Collections.IEnumerable).IsAssignableFrom(t); + } + public static bool IsList(Type t) { return t.IsGenericType @@ -79,6 +84,13 @@ namespace Explorer && (typeDef.IsAssignableFrom(typeof(List<>)) || typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))); } + public static bool IsDictionary(Type t) + { + return t.IsGenericType + && t.GetGenericTypeDefinition() is Type typeDef + && typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.Dictionary<,>)); + } + public static Type GetTypeByName(string typeName) { foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) diff --git a/src/MainMenu/Pages/ConsolePage.cs b/src/MainMenu/Pages/ConsolePage.cs index 7c5993b..122778e 100644 --- a/src/MainMenu/Pages/ConsolePage.cs +++ b/src/MainMenu/Pages/ConsolePage.cs @@ -121,7 +121,9 @@ MelonLogger.Log(""hello world"");"; { GUILayout.Label("C# REPL Console", null); - GUILayout.Label("Method:", null); + GUI.skin.label.alignment = TextAnchor.UpperLeft; + + GUILayout.Label("Enter code here as though it is a method body:", null); MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.Height(250) }); if (GUILayout.Button("Execute", null)) @@ -147,10 +149,7 @@ MelonLogger.Log(""hello world"");"; } GUILayout.Label("Using directives:", null); - foreach (var asm in UsingDirectives) - { - GUILayout.Label(AsmToUsing(asm, true), null); - } + GUILayout.BeginHorizontal(null); GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(105) }); UsingInput = GUILayout.TextField(UsingInput, new GUILayoutOption[] { GUILayout.Width(150) }); @@ -163,6 +162,11 @@ MelonLogger.Log(""hello world"");"; ResetConsole(); } GUILayout.EndHorizontal(); + + foreach (var asm in UsingDirectives) + { + GUILayout.Label(AsmToUsing(asm, true), null); + } } public override void Update() { } diff --git a/src/MainMenu/Pages/SearchPage.cs b/src/MainMenu/Pages/SearchPage.cs index e978a20..7011131 100644 --- a/src/MainMenu/Pages/SearchPage.cs +++ b/src/MainMenu/Pages/SearchPage.cs @@ -26,7 +26,7 @@ namespace Explorer //private List m_searchResults = new List(); private Vector2 resultsScroll = Vector2.zero; - private List m_searchResults = new List(); + private List m_searchResults = new List(); public SceneFilter SceneMode = SceneFilter.Any; public TypeFilter TypeMode = TypeFilter.Object; @@ -64,7 +64,7 @@ namespace Explorer private void CacheResults(IEnumerable results) { - m_searchResults = new List(); + m_searchResults = new List(); foreach (var obj in results) { @@ -75,7 +75,7 @@ namespace Explorer toCache = ilObject.TryCast() ?? ilObject.TryCast()?.gameObject ?? ilObject; } - var cache = CacheObject.GetCacheObject(toCache); + var cache = CacheObjectBase.GetCacheObject(toCache); m_searchResults.Add(cache); } } diff --git a/src/Windows/GameObjectWindow.cs b/src/Windows/GameObjectWindow.cs index 280cf89..b70f8c9 100644 --- a/src/Windows/GameObjectWindow.cs +++ b/src/Windows/GameObjectWindow.cs @@ -6,7 +6,6 @@ using MelonLoader; using UnhollowerRuntimeLib; using UnityEngine; using UnityEngine.SceneManagement; -using ComponentList = Il2CppSystem.Collections.Generic.List; namespace Explorer { @@ -22,7 +21,7 @@ namespace Explorer private Vector2 m_transformScroll = Vector2.zero; private Transform[] m_children; - private ComponentList m_components; + private Component[] m_components; private Vector2 m_compScroll = Vector2.zero; @@ -69,11 +68,9 @@ namespace Explorer } m_name = m_object.name; - m_scene = m_object.scene == null ? "null" : m_object.scene.name; - - //var listComps = new Il2CppSystem.Collections.Generic.List(); - //m_object.GetComponents(listComps); - //m_components = listComps.ToArray(); + m_scene = string.IsNullOrEmpty(m_object.scene.name) + ? "None" + : m_object.scene.name; var list = new List(); for (int i = 0; i < m_object.transform.childCount; i++) @@ -92,8 +89,12 @@ namespace Explorer throw new Exception("Object is null!"); } - m_components = new Il2CppSystem.Collections.Generic.List(); - m_object.GetComponentsInternal(ReflectionHelpers.ComponentType, false, false, true, false, m_components); + var list = new List(); + foreach (var comp in m_object.GetComponents(ReflectionHelpers.ComponentType)) + { + list.Add(comp); + } + m_components = list.ToArray(); } catch (Exception e) { @@ -255,34 +256,37 @@ namespace Explorer m_cachedDestroyList.Clear(); } - foreach (var component in m_components) + if (m_components != null) { - if (!component) continue; + foreach (var component in m_components) + { + if (!component) continue; - var ilType = component.GetIl2CppType(); - if (ilType == ReflectionHelpers.TransformType) - { - continue; - } + var ilType = component.GetIl2CppType(); + if (ilType == ReflectionHelpers.TransformType) + { + continue; + } - GUILayout.BeginHorizontal(null); - if (ReflectionHelpers.BehaviourType.IsAssignableFrom(ilType)) - { - BehaviourEnabledBtn(component.TryCast()); + GUILayout.BeginHorizontal(null); + if (ReflectionHelpers.BehaviourType.IsAssignableFrom(ilType)) + { + BehaviourEnabledBtn(component.TryCast()); + } + else + { + GUILayout.Space(26); + } + if (GUILayout.Button("" + ilType.Name + "", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 90) })) + { + ReflectObject(component); + } + if (GUILayout.Button("-", new GUILayoutOption[] { GUILayout.Width(20) })) + { + m_cachedDestroyList.Add(component); + } + GUILayout.EndHorizontal(); } - else - { - GUILayout.Space(26); - } - if (GUILayout.Button("" + ilType.Name + "", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 90) })) - { - ReflectObject(component); - } - if (GUILayout.Button("-", new GUILayoutOption[] { GUILayout.Width(20) })) - { - m_cachedDestroyList.Add(component); - } - GUILayout.EndHorizontal(); } GUI.skin.button.alignment = TextAnchor.MiddleCenter; diff --git a/src/Windows/ReflectionWindow.cs b/src/Windows/ReflectionWindow.cs index 3b57ab3..76f9c33 100644 --- a/src/Windows/ReflectionWindow.cs +++ b/src/Windows/ReflectionWindow.cs @@ -16,30 +16,27 @@ namespace Explorer public Type ObjectType; - private CacheObject[] m_cachedMembers; - private CacheObject[] m_cachedMemberFiltered; + private CacheObjectBase[] m_cachedMembers; + private CacheObjectBase[] m_cachedMemberFiltered; private int m_pageOffset; private int m_limitPerPage = 20; private bool m_autoUpdate = false; private string m_search = ""; - public MemberInfoType m_filter = MemberInfoType.Property; + public MemberTypes m_filter = MemberTypes.Property; private bool m_hideFailedReflection = false; - public enum MemberInfoType - { - Field, - Property, - Method, - All - } + // some extra caching + private UnityEngine.Object m_uObj; + private Component m_component; public override void Init() { var type = ReflectionHelpers.GetActualType(Target); if (type == null) { - MelonLogger.Log("Could not get underlying type for object. ToString(): " + Target.ToString()); + MelonLogger.Log($"Could not get underlying type for object..? Type: {Target?.GetType().Name}, ToString: {Target?.ToString()}"); + DestroyWindow(); return; } @@ -48,10 +45,27 @@ namespace Explorer var types = ReflectionHelpers.GetAllBaseTypes(Target); CacheMembers(types); - m_filter = MemberInfoType.All; - m_cachedMemberFiltered = m_cachedMembers.Where(x => ShouldProcessMember(x)).ToArray(); - UpdateValues(); - m_filter = MemberInfoType.Property; + if (Target is Il2CppSystem.Object ilObject) + { + var unityObj = ilObject.TryCast(); + if (unityObj) + { + m_uObj = unityObj; + + var component = ilObject.TryCast(); + if (component) + { + m_component = component; + } + } + } + + m_filter = MemberTypes.All; + m_autoUpdate = true; + Update(); + + m_autoUpdate = false; + m_filter = MemberTypes.Property; } public override void Update() @@ -65,11 +79,6 @@ namespace Explorer } private void UpdateValues() - { - UpdateMembers(); - } - - private void UpdateMembers() { foreach (var member in m_cachedMemberFiltered) { @@ -77,9 +86,9 @@ namespace Explorer } } - private bool ShouldProcessMember(CacheObject holder) + private bool ShouldProcessMember(CacheObjectBase holder) { - if (m_filter != MemberInfoType.All && m_filter != holder.MemberInfoType) return false; + if (m_filter != MemberTypes.All && m_filter != holder.MemberInfoType) return false; if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) return false; @@ -92,16 +101,13 @@ namespace Explorer private void CacheMembers(Type[] types) { - var list = new List(); + var list = new List(); var names = new List(); foreach (var declaringType in types) { - if (declaringType == typeof(Il2CppObjectBase)) continue; - MemberInfo[] infos; - string exception = null; try @@ -114,8 +120,8 @@ namespace Explorer continue; } - //object value = null; object target = Target; + if (target is Il2CppSystem.Object ilObject) { try @@ -130,9 +136,10 @@ namespace Explorer foreach (var member in infos) { - if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property) + if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Method) { - if (member.Name == "Il2CppType") continue; + if (member.Name.Contains("Il2CppType") || member.Name.StartsWith("get_")) + continue; try { @@ -140,7 +147,7 @@ namespace Explorer if (names.Contains(name)) continue; names.Add(name); - var cached = CacheObject.GetCacheObject(null, member, target); + var cached = CacheObjectBase.GetCacheObject(null, member, target); if (cached != null) { list.Add(cached); @@ -149,9 +156,8 @@ namespace Explorer } catch (Exception e) { - MelonLogger.Log("Exception caching member!"); - MelonLogger.Log(e.GetType() + ", " + e.Message); - MelonLogger.Log(e.StackTrace); + MelonLogger.LogWarning($"Exception caching member {declaringType.Name}.{member.Name}!"); + MelonLogger.Log(e.ToString()); } } } @@ -172,26 +178,18 @@ namespace Explorer GUILayout.BeginHorizontal(null); GUILayout.Label("Type: " + ObjectType.FullName + "", null); - - bool unityObj = Target is UnityEngine.Object; - - if (unityObj) + if (m_uObj) { - GUILayout.Label("Name: " + (Target as UnityEngine.Object).name, null); + GUILayout.Label("Name: " + m_uObj.name, null); } GUILayout.EndHorizontal(); - if (unityObj) + if (m_uObj) { GUILayout.BeginHorizontal(null); - GUILayout.Label("Tools:", new GUILayoutOption[] { GUILayout.Width(80) }); - - UIHelpers.InstantiateButton((UnityEngine.Object)Target); - - var comp = (Target as Il2CppSystem.Object).TryCast(); - - if (comp && comp.gameObject is GameObject obj) + UIHelpers.InstantiateButton(m_uObj); + if (m_component && m_component.gameObject is GameObject obj) { GUI.skin.label.alignment = TextAnchor.MiddleRight; GUILayout.Label("GameObject:", null); @@ -201,7 +199,6 @@ namespace Explorer } GUI.skin.label.alignment = TextAnchor.UpperLeft; } - GUILayout.EndHorizontal(); } @@ -221,9 +218,10 @@ namespace Explorer GUILayout.BeginHorizontal(null); GUILayout.Label("Filter:", new GUILayoutOption[] { GUILayout.Width(75) }); - FilterToggle(MemberInfoType.All, "All"); - FilterToggle(MemberInfoType.Property, "Properties"); - FilterToggle(MemberInfoType.Field, "Fields"); + FilterToggle(MemberTypes.All, "All"); + FilterToggle(MemberTypes.Property, "Properties"); + FilterToggle(MemberTypes.Field, "Fields"); + FilterToggle(MemberTypes.Method, "Methods"); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(null); @@ -284,18 +282,19 @@ namespace Explorer } } - private void DrawMembers(CacheObject[] members) + private void DrawMembers(CacheObjectBase[] members) { // todo pre-cache list based on current search, otherwise this doesnt work. int i = 0; - DrawMembersInternal("Properties", MemberInfoType.Property, members, ref i); - DrawMembersInternal("Fields", MemberInfoType.Field, members, ref i); + DrawMembersInternal("Properties", MemberTypes.Property, members, ref i); + DrawMembersInternal("Fields", MemberTypes.Field, members, ref i); + DrawMembersInternal("Methods", MemberTypes.Method, members, ref i); } - private void DrawMembersInternal(string title, MemberInfoType filter, CacheObject[] members, ref int index) + private void DrawMembersInternal(string title, MemberTypes filter, CacheObjectBase[] members, ref int index) { - if (m_filter != filter && m_filter != MemberInfoType.All) + if (m_filter != filter && m_filter != MemberTypes.All) { return; } @@ -336,7 +335,7 @@ namespace Explorer } } - private void FilterToggle(MemberInfoType mode, string label) + private void FilterToggle(MemberTypes mode, string label) { if (m_filter == mode) { diff --git a/src/Windows/ResizeDrag.cs b/src/Windows/ResizeDrag.cs index 9a53e96..7b8e3d0 100644 --- a/src/Windows/ResizeDrag.cs +++ b/src/Windows/ResizeDrag.cs @@ -59,6 +59,8 @@ namespace Explorer MelonLogger.Log("Exception on GuiResize: " + e.GetType() + ", " + e.Message); } + GUI.skin.label.alignment = TextAnchor.UpperLeft; + return _rect; } }