diff --git a/README.md b/README.md index fbfcdf4..f43780b 100644 --- a/README.md +++ b/README.md @@ -12,16 +12,11 @@

-

- -

- - [Releases](#releases) -- [How to install](#how-to-install) -- [How to use](#how-to-use) - - [Mod Config](#mod-config) - [Features](#features) - - [Mouse Control](#mouse-control) +- [How to install](#how-to-install) +- [Mod Config](#mod-config) +- [Mouse Control](#mouse-control) - [Building](#building) - [Credits](#credits) @@ -33,10 +28,23 @@ | [BepInEx](https://github.com/BepInEx/BepInEx) | ❔ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.BepInEx.Il2Cpp.zip) | ✔️ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.BepInEx.Mono.zip) | Il2Cpp Issues: -* Some methods may still fail with a `MissingMethodException`, please let me know if you experience this (with full MelonLoader log please). +* Some methods may still fail with a `MissingMethodException`, please let me know if you experience this (with full debug log please). * Reflection may fail with certain types, see [here](https://github.com/knah/Il2CppAssemblyUnhollower#known-issues) for more details. * Scrolling with mouse wheel in the Explorer menu may not work on all games at the moment. +## Features + +

+ +

+ +* Scene Explorer: Simple menu to traverse the Transform heirarchy of the scene. +* GameObject Inspector: Various helpful tools to see and manipulate the GameObject, similar to what you can do in the Editor. +* Reflection Inspector: Inspect Properties and Fields. Can also set primitive values and evaluate primitive methods. +* Search: Search for UnityEngine.Objects with various filters, or use the helpers for static Instances and Classes. +* C# Console: Interactive console for evaluating C# methods on the fly, with some basic helpers. +* Inspect-under-mouse: Hover over an object with a collider and inspect it by clicking on it. + ## How to install ### MelonLoader @@ -53,73 +61,26 @@ Requires [BepInEx](https://github.com/BepInEx/BepInEx) to be installed for your 2. Unzip the file into the `BepInEx\plugins\` folder in your game's installation directory, created by BepInEx. 3. Make sure it's not in a sub-folder, `Explorer.dll` should be directly in the `plugins\` folder. -## How to use - -* Press F7 to show or hide the menu. -* Use the Scene Explorer or the Object Search to start Exploring, or the C# Console to test some code. -* See below for more specific details. - -### Mod Config +## Mod Config There is a simple Mod Config for the Explorer. You can access the settings via the "Options" page of the main menu. -`Main Menu Toggle` (KeyCode) -* Sets the keybinding for the Main Menu toggle (show/hide all Explorer windows) +`Main Menu Toggle` (KeyCode) | Default: `F7` * See [this article](https://docs.unity3d.com/ScriptReference/KeyCode.html) for a full list of all accepted KeyCodes. -* Default: `F7` -`Default Window Size` (Vector2) +`Default Window Size` (Vector2) | Default: `x: 550, y: 700` * Sets the default width and height for all Explorer windows when created. -* `x` is width, `y` is height. -* Default: `550 700` -`Default Items per Page` (Int) +`Default Items per Page` (int) | Default: `20` * Sets the default items per page when viewing lists or search results. -* Default: `20` -## Features +`Enable Bitwise Editing` (bool) | Default: `false` +* Whether or not to show the Bitwise Editing helper when inspecting integers -### Scene Explorer +`Enable Tab View` (bool) | Default: `true` +* Whether or not all inspector windows a grouped into a single window with tabs. -* A simple menu which allows you to traverse the Transform heirarchy of the scene. -* Click on a GameObject to set it as the current path, or Inspect it to send it to an Inspector Window. - -### Inspectors - -Explorer has two main inspector modes: GameObject Inspector, and Reflection Inspector. - -Tips: -* When in Tab View, GameObjects are denoted by a [G] prefix, and Reflection objects are denoted by a [R] prefix. -* Hold Left Shift when you click the Inspect button to force Reflection mode for GameObjects and Transforms. - -### GameObject Inspector - -* Allows you to see the children and components on a GameObject. -* Can use some basic GameObject Controls such as translating and rotating the object, destroy it, clone it, etc. - -### Reflection Inspector - -* The Reflection Inspector is used for all other supported objects. -* Allows you to inspect Properties, Fields and basic Methods, as well as set primitive values and evaluate primitive methods. -* Can search and filter members for the ones you are interested in. - -### Object Search - -* You can search for an `UnityEngine.Object` with the Object Search feature. -* Filter by name, type, etc. -* For GameObjects and Transforms you can filter which scene they are found in too. - -### C# console - -* A simple C# console, allows you to execute a method body on the fly. - -### Inspect-under-mouse - -* Press Shift+RMB (Right Mouse Button) while the Explorer menu is open to begin Inspect-Under-Mouse. -* Hover over your desired object, if you see the name appear then you can click on it to inspect it. -* Only objects with Colliders are supported. - -### Mouse Control +## Mouse Control Explorer 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 Explorer, this is possible but it requires specific patches for that game. diff --git a/src/CacheObject/CacheEnumerated.cs b/src/CacheObject/CacheEnumerated.cs new file mode 100644 index 0000000..3f7d172 --- /dev/null +++ b/src/CacheObject/CacheEnumerated.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Explorer.UI; + +namespace Explorer.CacheObject +{ + public class CacheEnumerated : CacheObjectBase + { + public int Index { get; set; } + public IList RefIList { get; set; } + public InteractiveEnumerable ParentEnumeration { get; set; } + + public override bool CanWrite => RefIList != null && ParentEnumeration.OwnerCacheObject.CanWrite; + + public override void SetValue() + { + RefIList[Index] = IValue.Value; + ParentEnumeration.Value = RefIList; + + ParentEnumeration.OwnerCacheObject.SetValue(); + } + } +} diff --git a/src/CacheObject/CacheFactory.cs b/src/CacheObject/CacheFactory.cs new file mode 100644 index 0000000..4c1bfaa --- /dev/null +++ b/src/CacheObject/CacheFactory.cs @@ -0,0 +1,75 @@ +using System; +using System.Reflection; +using Explorer.CacheObject; +using UnityEngine; + +namespace Explorer +{ + public static class CacheFactory + { + public static CacheObjectBase GetCacheObject(object obj) + { + if (obj == null) return null; + + return GetCacheObject(obj, ReflectionHelpers.GetActualType(obj)); + } + + public static CacheObjectBase GetCacheObject(object obj, Type type) + { + var ret = new CacheObjectBase(); + ret.Init(obj, type); + return ret; + } + + public static CacheMember GetCacheObject(MemberInfo member, object declaringInstance) + { + CacheMember ret; + + if (member is MethodInfo mi && CanProcessArgs(mi.GetParameters())) + { + ret = new CacheMethod(); + ret.InitMember(mi, declaringInstance); + } + else if (member is PropertyInfo pi && CanProcessArgs(pi.GetIndexParameters())) + { + ret = new CacheProperty(); + ret.InitMember(pi, declaringInstance); + } + else if (member is FieldInfo fi) + { + ret = new CacheField(); + ret.InitMember(fi, declaringInstance); + } + else + { + return null; + } + + return ret; + } + + public static bool CanProcessArgs(ParameterInfo[] parameters) + { + foreach (var param in parameters) + { + var pType = param.ParameterType; + + if (pType.IsByRef && pType.HasElementType) + { + pType = pType.GetElementType(); + } + + if (pType.IsPrimitive || pType == typeof(string)) + { + continue; + } + else + { + return false; + } + } + + return true; + } + } +} diff --git a/src/CacheObject/CacheField.cs b/src/CacheObject/CacheField.cs new file mode 100644 index 0000000..6495225 --- /dev/null +++ b/src/CacheObject/CacheField.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; + +namespace Explorer.CacheObject +{ + public class CacheField : CacheMember + { + public override bool IsStatic => (MemInfo as FieldInfo).IsStatic; + + public override void InitMember(MemberInfo member, object declaringInstance) + { + base.InitMember(member, declaringInstance); + + base.Init(null, (member as FieldInfo).FieldType); + + UpdateValue(); + } + + public override void UpdateValue() + { + try + { + var fi = MemInfo as FieldInfo; + IValue.Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance); + + base.UpdateValue(); + } + catch (Exception e) + { + ReflectionException = ReflectionHelpers.ExceptionToString(e); + } + } + + public override void SetValue() + { + var fi = MemInfo as FieldInfo; + fi.SetValue(fi.IsStatic ? null : DeclaringInstance, IValue.Value); + + base.SetValue(); + } + } +} diff --git a/src/CacheObject/CacheMember.cs b/src/CacheObject/CacheMember.cs new file mode 100644 index 0000000..bd55929 --- /dev/null +++ b/src/CacheObject/CacheMember.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; +using Explorer.UI; +using Explorer.UI.Shared; + +namespace Explorer.CacheObject +{ + public class CacheMember : CacheObjectBase + { + public MemberInfo MemInfo { get; set; } + public Type DeclaringType { get; set; } + public object DeclaringInstance { get; set; } + + public virtual bool IsStatic { get; private set; } + + public override bool HasParameters => m_arguments != null && m_arguments.Length > 0; + public override bool IsMember => true; + + public string RichTextName => m_richTextName ?? GetRichTextName(); + private string m_richTextName; + + public override bool CanWrite => m_canWrite ?? GetCanWrite(); + private bool? m_canWrite; + + public string ReflectionException { get; set; } + + public bool m_evaluated = false; + public bool m_isEvaluating; + public ParameterInfo[] m_arguments = new ParameterInfo[0]; + public string[] m_argumentInput = new string[0]; + + public virtual void InitMember(MemberInfo member, object declaringInstance) + { + MemInfo = member; + DeclaringInstance = declaringInstance; + DeclaringType = member.DeclaringType; + } + + public override void UpdateValue() + { + base.UpdateValue(); + } + + public override void SetValue() + { + // ... + } + + public object[] ParseArguments() + { + if (m_arguments.Length < 1) + { + return null; + } + + var parsedArgs = new List(); + for (int i = 0; i < m_arguments.Length; i++) + { + var input = m_argumentInput[i]; + var type = m_arguments[i].ParameterType; + + if (type.IsByRef) + { + type = type.GetElementType(); + } + + if (!string.IsNullOrEmpty(input)) + { + if (type == typeof(string)) + { + parsedArgs.Add(input); + continue; + } + else + { + try + { + var arg = type.GetMethod("Parse", new Type[] { typeof(string) }) + .Invoke(null, new object[] { input }); + + parsedArgs.Add(arg); + continue; + } + catch + { + ExplorerCore.Log($"Argument #{i} '{m_arguments[i].Name}' ({type.Name}), could not parse input '{input}'."); + } + } + } + + // No input, see if there is a default value. + if (HasDefaultValue(m_arguments[i])) + { + parsedArgs.Add(m_arguments[i].DefaultValue); + continue; + } + + // Try add a null arg I guess + parsedArgs.Add(null); + } + + return parsedArgs.ToArray(); + } + + public static bool HasDefaultValue(ParameterInfo arg) => arg.DefaultValue != DBNull.Value; + + public void DrawArgsInput() + { + GUILayout.Label($"Arguments:", new GUILayoutOption[0]); + for (int i = 0; i < this.m_arguments.Length; i++) + { + var name = this.m_arguments[i].Name; + var input = this.m_argumentInput[i]; + var type = this.m_arguments[i].ParameterType.Name; + + var label = $"{type} "; + label += $"{name}"; + if (HasDefaultValue(this.m_arguments[i])) + { + label = $"[{label} = {this.m_arguments[i].DefaultValue ?? "null"}]"; + } + + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + + GUI.skin.label.alignment = TextAnchor.MiddleCenter; + GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(15) }); + this.m_argumentInput[i] = GUIUnstrip.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) }); + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + GUILayout.Label(label, new GUILayoutOption[0]); + + GUILayout.EndHorizontal(); + } + } + + private bool GetCanWrite() + { + if (MemInfo is FieldInfo fi) + m_canWrite = !(fi.IsLiteral && !fi.IsInitOnly); + else if (MemInfo is PropertyInfo pi) + m_canWrite = pi.CanWrite; + else + m_canWrite = false; + + return (bool)m_canWrite; + } + + private string GetRichTextName() + { + string memberColor = ""; + bool isStatic = false; + + if (MemInfo is FieldInfo fi) + { + if (fi.IsStatic) + { + isStatic = true; + memberColor = Syntax.Field_Static; + } + else + memberColor = Syntax.Field_Instance; + } + else if (MemInfo is MethodInfo mi) + { + if (mi.IsStatic) + { + isStatic = true; + memberColor = Syntax.Method_Static; + } + else + memberColor = Syntax.Method_Instance; + } + else if (MemInfo is PropertyInfo pi) + { + if (pi.GetAccessors()[0].IsStatic) + { + isStatic = true; + memberColor = Syntax.Prop_Static; + } + else + memberColor = Syntax.Prop_Instance; + } + + string classColor; + if (MemInfo.DeclaringType.IsValueType) + { + classColor = Syntax.StructGreen; + } + else if (MemInfo.DeclaringType.IsAbstract && MemInfo.DeclaringType.IsSealed) + { + classColor = Syntax.Class_Static; + } + else + { + classColor = Syntax.Class_Instance; + } + + m_richTextName = $"{MemInfo.DeclaringType.Name}."; + if (isStatic) m_richTextName += ""; + m_richTextName += $"{MemInfo.Name}"; + if (isStatic) m_richTextName += ""; + + // generic method args + if (this is CacheMethod cm && cm.GenericArgs.Length > 0) + { + m_richTextName += "<"; + + var args = ""; + for (int i = 0; i < cm.GenericArgs.Length; i++) + { + if (args != "") args += ", "; + args += $"{cm.GenericArgs[i].Name}"; + } + m_richTextName += args; + + m_richTextName += ">"; + } + + return m_richTextName; + } + } +} diff --git a/src/CachedObjects/Other/CacheMethod.cs b/src/CacheObject/CacheMethod.cs similarity index 56% rename from src/CachedObjects/Other/CacheMethod.cs rename to src/CacheObject/CacheMethod.cs index 1435d86..1b3e012 100644 --- a/src/CachedObjects/Other/CacheMethod.cs +++ b/src/CacheObject/CacheMethod.cs @@ -3,23 +3,28 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEngine; +using Explorer.UI.Shared; -namespace Explorer +namespace Explorer.CacheObject { - public class CacheMethod : CacheObjectBase + public class CacheMethod : CacheMember { private CacheObjectBase m_cachedReturnValue; public override bool HasParameters => base.HasParameters || GenericArgs.Length > 0; + public override bool IsStatic => (MemInfo as MethodInfo).IsStatic; + public Type[] GenericArgs { get; private set; } public Type[][] GenericConstraints { get; private set; } public string[] GenericArgInput = new string[0]; - public override void Init() + public override void InitMember(MemberInfo member, object declaringInstance) { - var mi = (MemInfo as MethodInfo); + base.InitMember(member, declaringInstance); + + var mi = MemInfo as MethodInfo; GenericArgs = mi.GetGenericArguments(); GenericConstraints = GenericArgs.Select(x => x.GetGenericParameterConstraints()) @@ -27,12 +32,15 @@ namespace Explorer GenericArgInput = new string[GenericArgs.Length]; - ValueType = mi.ReturnType; + m_arguments = mi.GetParameters(); + m_argumentInput = new string[m_arguments.Length]; + + base.Init(null, mi.ReturnType); } public override void UpdateValue() { - //base.UpdateValue(); + // CacheMethod cannot UpdateValue directly. Need to Evaluate. } public void Evaluate() @@ -64,7 +72,8 @@ namespace Explorer if (ret != null) { - m_cachedReturnValue = CacheFactory.GetTypeAndCacheObject(ret); + //m_cachedReturnValue = CacheFactory.GetTypeAndCacheObject(ret); + m_cachedReturnValue = CacheFactory.GetCacheObject(ret, IValue.ValueType); m_cachedReturnValue.UpdateValue(); } else @@ -91,11 +100,11 @@ namespace Explorer { foreach (var constraint in GenericConstraints[i].Where(x => x != null)) { - if (!constraint.IsAssignableFrom(t)) - { + if (!constraint.IsAssignableFrom(t)) + { ExplorerCore.LogWarning($"Generic argument #{i}, '{input}' is not assignable from the constraint '{constraint}'!"); return null; - } + } } list.Add(t); @@ -117,15 +126,20 @@ namespace Explorer // ==== GUI DRAW ==== - public override void DrawValue(Rect window, float width) + //public override void Draw(Rect window, float width) + //{ + // base.Draw(window, width); + //} + + public void DrawValue(Rect window, float width) { - string typeLabel = $"{ValueType.FullName}"; + string typeLabel = $"{IValue.ValueType.FullName}"; if (m_evaluated) { if (m_cachedReturnValue != null) { - m_cachedReturnValue.DrawValue(window, width); + m_cachedReturnValue.IValue.DrawValue(window, width); } else { @@ -137,5 +151,49 @@ namespace Explorer GUILayout.Label($"Not yet evaluated ({typeLabel})", new GUILayoutOption[0]); } } + + public void DrawGenericArgsInput() + { + GUILayout.Label($"Generic Arguments:", new GUILayoutOption[0]); + + for (int i = 0; i < this.GenericArgs.Length; i++) + { + string types = ""; + if (this.GenericConstraints[i].Length > 0) + { + foreach (var constraint in this.GenericConstraints[i]) + { + if (types != "") types += ", "; + + string type; + + if (constraint == null) + type = "Any"; + else + type = constraint.ToString(); + + types += $"{type}"; + } + } + else + { + types = $"Any"; + } + var input = this.GenericArgInput[i]; + + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + + GUI.skin.label.alignment = TextAnchor.MiddleCenter; + GUILayout.Label( + $"{this.GenericArgs[i].Name}", + new GUILayoutOption[] { GUILayout.Width(15) } + ); + this.GenericArgInput[i] = GUIUnstrip.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) }); + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + GUILayout.Label(types, new GUILayoutOption[0]); + + GUILayout.EndHorizontal(); + } + } } } diff --git a/src/CacheObject/CacheObjectBase.cs b/src/CacheObject/CacheObjectBase.cs new file mode 100644 index 0000000..270529d --- /dev/null +++ b/src/CacheObject/CacheObjectBase.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; +using Explorer.UI; +using Explorer.UI.Shared; + +namespace Explorer.CacheObject +{ + public class CacheObjectBase + { + public InteractiveValue IValue; + + public virtual bool CanWrite => false; + public virtual bool HasParameters => false; + public virtual bool IsMember => false; + + public bool IsStaticClassSearchResult { get; set; } + + public virtual void Init(object obj, Type valueType) + { + if (valueType == null && obj == null) + { + return; + } + + InteractiveValue interactive; + + if (valueType == typeof(GameObject) || valueType == typeof(Transform)) + { + interactive = new InteractiveGameObject(); + } + else if (valueType.IsPrimitive || valueType == typeof(string)) + { + interactive = new InteractivePrimitive(); + } + else if (valueType.IsEnum) + { + if (valueType.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] attributes && attributes.Length > 0) + { + interactive = new InteractiveFlags(); + } + else + { + interactive = new InteractiveEnum(); + } + } + else if (valueType == typeof(Vector2) || valueType == typeof(Vector3) || valueType == typeof(Vector4)) + { + interactive = new InteractiveVector(); + } + else if (valueType == typeof(Quaternion)) + { + interactive = new InteractiveQuaternion(); + } + else if (valueType == typeof(Color)) + { + interactive = new InteractiveColor(); + } + else if (valueType == typeof(Rect)) + { + interactive = new InteractiveRect(); + } + // must check this before IsEnumerable + else if (ReflectionHelpers.IsDictionary(valueType)) + { + interactive = new InteractiveDictionary(); + } + else if (ReflectionHelpers.IsEnumerable(valueType)) + { + interactive = new InteractiveEnumerable(); + } + else + { + interactive = new InteractiveValue(); + } + + interactive.Value = obj; + interactive.ValueType = valueType; + + this.IValue = interactive; + this.IValue.OwnerCacheObject = this; + + UpdateValue(); + + this.IValue.Init(); + } + + public virtual void Draw(Rect window, float width) + { + IValue.Draw(window, width); + } + + public virtual void UpdateValue() + { + IValue.UpdateValue(); + } + + public virtual void SetValue() => throw new NotImplementedException(); + } +} diff --git a/src/CacheObject/CacheProperty.cs b/src/CacheObject/CacheProperty.cs new file mode 100644 index 0000000..5959f44 --- /dev/null +++ b/src/CacheObject/CacheProperty.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; + +namespace Explorer.CacheObject +{ + public class CacheProperty : CacheMember + { + public override bool IsStatic => (MemInfo as PropertyInfo).GetAccessors()[0].IsStatic; + + public override void InitMember(MemberInfo member, object declaringInstance) + { + base.InitMember(member, declaringInstance); + + var pi = member as PropertyInfo; + + this.m_arguments = pi.GetIndexParameters(); + this.m_argumentInput = new string[m_arguments.Length]; + + base.Init(null, pi.PropertyType); + + UpdateValue(); + } + + public override void UpdateValue() + { + if (HasParameters && !m_isEvaluating) + { + // Need to enter parameters first. + return; + } + + try + { + var pi = MemInfo as PropertyInfo; + var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance; + + IValue.Value = pi.GetValue(target, ParseArguments()); + + base.UpdateValue(); + } + catch (Exception e) + { + ReflectionException = ReflectionHelpers.ExceptionToString(e); + } + } + + public override void SetValue() + { + var pi = MemInfo as PropertyInfo; + var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance; + + pi.SetValue(target, IValue.Value, ParseArguments()); + + base.SetValue(); + } + } +} diff --git a/src/CachedObjects/CacheFactory.cs b/src/CachedObjects/CacheFactory.cs deleted file mode 100644 index 6faf977..0000000 --- a/src/CachedObjects/CacheFactory.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System; -using System.Reflection; -using UnityEngine; - -namespace Explorer -{ - public static class CacheFactory - { - public static CacheObjectBase GetTypeAndCacheObject(object obj) - => GetTypeAndCacheObject(obj, null, null); - - public static CacheObjectBase GetTypeAndCacheObject(MemberInfo memberInfo, object declarer) - => GetTypeAndCacheObject(null, memberInfo, declarer); - - public static CacheObjectBase GetTypeAndCacheObject(object obj, MemberInfo memberInfo, object declarer) - { - Type type = null; - - 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; - } - } - else if (obj != null) - { - type = ReflectionHelpers.GetActualType(obj); - } - - if (type == null) - { - return null; - } - - return GetCacheObject(obj, memberInfo, declarer, type); - } - - public static CacheObjectBase GetCacheObject(object obj, Type valueType) - => GetCacheObject(obj, null, null, valueType); - - private static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType) - { - CacheObjectBase cached; - - var pi = memberInfo as PropertyInfo; - var mi = memberInfo as MethodInfo; - - // Check if can process args - if ((pi != null && !CanProcessArgs(pi.GetIndexParameters())) - || (mi != null && !CanProcessArgs(mi.GetParameters()))) - { - return null; - } - - if (mi != null) - { - cached = new CacheMethod(); - } - else if (valueType == typeof(GameObject) || valueType == typeof(Transform)) - { - cached = new CacheGameObject(); - } - else if (valueType.IsPrimitive || valueType == typeof(string)) - { - cached = new CachePrimitive(); - } - else if (valueType.IsEnum) - { - if (valueType.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] attributes && attributes.Length > 0) - { - cached = new CacheEnumFlags(); - } - else - { - cached = new CacheEnum(); - } - } - else if (valueType == typeof(Vector2) || valueType == typeof(Vector3) || valueType == typeof(Vector4)) - { - cached = new CacheVector(); - } - else if (valueType == typeof(Quaternion)) - { - cached = new CacheQuaternion(); - } - else if (valueType == typeof(Color)) - { - cached = new CacheColor(); - } - else if (valueType == typeof(Rect)) - { - cached = new CacheRect(); - } - // must check this before IsEnumerable - else if (ReflectionHelpers.IsDictionary(valueType)) - { - cached = new CacheDictionary(); - } - else if (ReflectionHelpers.IsEnumerable(valueType)) - { - cached = new CacheList(); - } - else - { - cached = new CacheOther(); - } - - cached.Value = obj; - cached.ValueType = valueType; - - if (memberInfo != null) - { - cached.MemInfo = memberInfo; - cached.DeclaringType = memberInfo.DeclaringType; - cached.DeclaringInstance = declaringInstance; - } - - if (pi != null) - { - cached.m_arguments = pi.GetIndexParameters(); - } - else if (mi != null) - { - cached.m_arguments = mi.GetParameters(); - } - - cached.m_argumentInput = new string[cached.m_arguments.Length]; - - cached.UpdateValue(); - - cached.Init(); - - return cached; - } - - public static bool CanProcessArgs(ParameterInfo[] parameters) - { - foreach (var param in parameters) - { - var pType = param.ParameterType; - - if (pType.IsByRef && pType.HasElementType) - { - pType = pType.GetElementType(); - } - - if (pType.IsPrimitive || pType == typeof(string)) - { - continue; - } - else - { - return false; - } - } - - return true; - } - } -} diff --git a/src/CachedObjects/CacheObjectBase.cs b/src/CachedObjects/CacheObjectBase.cs deleted file mode 100644 index 9b8e3d7..0000000 --- a/src/CachedObjects/CacheObjectBase.cs +++ /dev/null @@ -1,430 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using UnityEngine; - -namespace Explorer -{ - public abstract class CacheObjectBase - { - public object Value; - public Type ValueType; - - public MemberInfo MemInfo { get; set; } - public Type DeclaringType { get; set; } - public object DeclaringInstance { get; set; } - - public virtual bool HasParameters => m_arguments != null && m_arguments.Length > 0; - - public bool m_evaluated = false; - public bool m_isEvaluating; - public ParameterInfo[] m_arguments = new ParameterInfo[0]; - public string[] m_argumentInput = new string[0]; - - public string ReflectionException { get; set; } - - public string RichTextName => m_richTextName ?? GetRichTextName(); - private string m_richTextName; - - public bool CanWrite => m_canWrite ?? GetCanWrite(); - private bool? m_canWrite; - - public virtual void Init() { } - - public abstract void DrawValue(Rect window, float width); - - public virtual void UpdateValue() - { - if (MemInfo == null) - { - return; - } - - if (HasParameters && !m_isEvaluating) - { - // Need to enter parameters first - return; - } - - try - { - if (MemInfo.MemberType == MemberTypes.Field) - { - var fi = MemInfo as FieldInfo; - Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance); - } - else if (MemInfo.MemberType == MemberTypes.Property) - { - var pi = MemInfo as PropertyInfo; - var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance; - - Value = pi.GetValue(target, ParseArguments()); - } - - ReflectionException = null; - m_evaluated = true; - m_isEvaluating = false; - } - catch (Exception e) - { - ReflectionException = ReflectionHelpers.ExceptionToString(e); - } - } - - public void SetValue() - { - try - { - if (MemInfo.MemberType == MemberTypes.Field) - { - var fi = MemInfo as FieldInfo; - fi.SetValue(fi.IsStatic ? null : DeclaringInstance, Value); - } - else if (MemInfo.MemberType == MemberTypes.Property) - { - var pi = MemInfo as PropertyInfo; - - if (HasParameters) - { - pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value, ParseArguments()); - } - else - { - pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value, null); - } - } - } - catch (Exception e) - { - ExplorerCore.LogWarning($"Error setting value: {e.GetType()}, {e.Message}"); - } - } - - public object[] ParseArguments() - { - var parsedArgs = new List(); - for (int i = 0; i < m_arguments.Length; i++) - { - var input = m_argumentInput[i]; - var type = m_arguments[i].ParameterType; - - if (type.IsByRef) - { - type = type.GetElementType(); - } - - if (!string.IsNullOrEmpty(input)) - { - if (type == typeof(string)) - { - parsedArgs.Add(input); - continue; - } - else - { - try - { - var arg = type.GetMethod("Parse", new Type[] { typeof(string) }) - .Invoke(null, new object[] { input }); - - parsedArgs.Add(arg); - continue; - } - catch - { - ExplorerCore.Log($"Argument #{i} '{m_arguments[i].Name}' ({type.Name}), could not parse input '{input}'."); - } - } - } - - // No input, see if there is a default value. - if (HasDefaultValue(m_arguments[i])) - { - parsedArgs.Add(m_arguments[i].DefaultValue); - continue; - } - - // Try add a null arg I guess - parsedArgs.Add(null); - } - - return parsedArgs.ToArray(); - } - - public static bool HasDefaultValue(ParameterInfo arg) => arg.DefaultValue != DBNull.Value; - - // ========= Gui Draw ========== - - public const float MAX_LABEL_WIDTH = 400f; - public const string EVALUATE_LABEL = "Evaluate"; - - public float CalcWhitespace(Rect window) - { - if (!(this is IExpandHeight)) return 0f; - - float whitespace = (this as IExpandHeight).WhiteSpace; - if (whitespace > 0) - { - ClampLabelWidth(window, ref whitespace); - } - - return whitespace; - } - - public static void ClampLabelWidth(Rect window, ref float labelWidth) - { - float min = window.width * 0.37f; - if (min > MAX_LABEL_WIDTH) min = MAX_LABEL_WIDTH; - - labelWidth = Mathf.Clamp(labelWidth, min, MAX_LABEL_WIDTH); - } - - public void Draw(Rect window, float labelWidth = 215f) - { - if (labelWidth > 0) - { - ClampLabelWidth(window, ref labelWidth); - } - - if (MemInfo != null) - { - GUILayout.Label(RichTextName, new GUILayoutOption[] { GUILayout.Width(labelWidth) }); - } - else - { - GUIUnstrip.Space(labelWidth); - } - - var cm = this as CacheMethod; - - if (HasParameters) - { - GUILayout.BeginVertical(new GUILayoutOption[0]); - - if (m_isEvaluating) - { - if (cm != null && cm.GenericArgs.Length > 0) - { - GUILayout.Label($"Generic Arguments:", new GUILayoutOption[0]); - - for (int i = 0; i < cm.GenericArgs.Length; i++) - { - string types = ""; - if (cm.GenericConstraints[i].Length > 0) - { - foreach (var constraint in cm.GenericConstraints[i]) - { - if (types != "") types += ", "; - - string type; - - if (constraint == null) - type = "Any"; - else - type = constraint.ToString(); - - types += $"{type}"; - } - } - else - { - types = $"Any"; - } - var input = cm.GenericArgInput[i]; - - GUILayout.BeginHorizontal(new GUILayoutOption[0]); - - GUI.skin.label.alignment = TextAnchor.MiddleCenter; - GUILayout.Label( - $"{cm.GenericArgs[i].Name}", - new GUILayoutOption[] { GUILayout.Width(15) } - ); - cm.GenericArgInput[i] = GUIUnstrip.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) }); - GUI.skin.label.alignment = TextAnchor.MiddleLeft; - GUILayout.Label(types, new GUILayoutOption[0]); - - GUILayout.EndHorizontal(); - } - } - - if (m_arguments.Length > 0) - { - GUILayout.Label($"Arguments:", new GUILayoutOption[0]); - for (int i = 0; i < m_arguments.Length; i++) - { - var name = m_arguments[i].Name; - var input = m_argumentInput[i]; - var type = m_arguments[i].ParameterType.Name; - - var label = $"{type} "; - label += $"{name}"; - if (HasDefaultValue(m_arguments[i])) - { - label = $"[{label} = {m_arguments[i].DefaultValue ?? "null"}]"; - } - - GUILayout.BeginHorizontal(new GUILayoutOption[0]); - - GUI.skin.label.alignment = TextAnchor.MiddleCenter; - GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(15) }); - m_argumentInput[i] = GUIUnstrip.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) }); - GUI.skin.label.alignment = TextAnchor.MiddleLeft; - GUILayout.Label(label, new GUILayoutOption[0]); - - GUILayout.EndHorizontal(); - } - } - - GUILayout.BeginHorizontal(new GUILayoutOption[0]); - if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) })) - { - if (cm != null) - cm.Evaluate(); - else - UpdateValue(); - } - if (GUILayout.Button("Cancel", new GUILayoutOption[] { GUILayout.Width(70) })) - { - m_isEvaluating = false; - } - GUILayout.EndHorizontal(); - } - else - { - var lbl = $"Evaluate ("; - int len = m_arguments.Length; - if (cm != null) len += cm.GenericArgs.Length; - lbl += len + " params)"; - - if (GUILayout.Button(lbl, new GUILayoutOption[] { GUILayout.Width(150) })) - { - m_isEvaluating = true; - } - } - - GUILayout.EndVertical(); - - GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); - GUIUnstrip.Space(labelWidth); - } - else if (cm != null) - { - if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) })) - { - cm.Evaluate(); - } - - GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); - GUIUnstrip.Space(labelWidth); - } - - string typeName = $"{ValueType.FullName}"; - - if (!string.IsNullOrEmpty(ReflectionException)) - { - GUILayout.Label("Reflection failed! (" + ReflectionException + ")", new GUILayoutOption[0]); - } - else if ((HasParameters || this is CacheMethod) && !m_evaluated) - { - GUILayout.Label($"Not yet evaluated ({typeName})", new GUILayoutOption[0]); - } - else if (Value == null && !(this is CacheMethod)) - { - GUILayout.Label($"null ({typeName})", new GUILayoutOption[0]); - } - else - { - DrawValue(window, window.width - labelWidth - 90); - } - } - - private bool GetCanWrite() - { - if (MemInfo is FieldInfo fi) - m_canWrite = !(fi.IsLiteral && !fi.IsInitOnly); - else if (MemInfo is PropertyInfo pi) - m_canWrite = pi.CanWrite; - else - m_canWrite = false; - - return (bool)m_canWrite; - } - - private string GetRichTextName() - { - string memberColor = ""; - bool isStatic = false; - - if (MemInfo is FieldInfo fi) - { - if (fi.IsStatic) - { - isStatic = true; - memberColor = UIStyles.Syntax.Field_Static; - } - else - memberColor = UIStyles.Syntax.Field_Instance; - } - else if (MemInfo is MethodInfo mi) - { - if (mi.IsStatic) - { - isStatic = true; - memberColor = UIStyles.Syntax.Method_Static; - } - else - memberColor = UIStyles.Syntax.Method_Instance; - } - else if (MemInfo is PropertyInfo pi) - { - if (pi.GetAccessors()[0].IsStatic) - { - isStatic = true; - memberColor = UIStyles.Syntax.Prop_Static; - } - else - memberColor = UIStyles.Syntax.Prop_Instance; - } - - string classColor; - if (MemInfo.DeclaringType.IsValueType) - { - classColor = UIStyles.Syntax.StructGreen; - } - else if (MemInfo.DeclaringType.IsAbstract && MemInfo.DeclaringType.IsSealed) - { - classColor = UIStyles.Syntax.Class_Static; - } - else - { - classColor = UIStyles.Syntax.Class_Instance; - } - - m_richTextName = $"{MemInfo.DeclaringType.Name}."; - if (isStatic) m_richTextName += ""; - m_richTextName += $"{MemInfo.Name}"; - if (isStatic) m_richTextName += ""; - - // generic method args - if (this is CacheMethod cm && cm.GenericArgs.Length > 0) - { - m_richTextName += "<"; - - var args = ""; - for (int i = 0; i < cm.GenericArgs.Length; i++) - { - if (args != "") args += ", "; - args += $"{cm.GenericArgs[i].Name}"; - } - m_richTextName += args; - - m_richTextName += ">"; - } - - return m_richTextName; - } - } -} diff --git a/src/CachedObjects/Object/CacheGameObject.cs b/src/CachedObjects/Object/CacheGameObject.cs deleted file mode 100644 index baba7e5..0000000 --- a/src/CachedObjects/Object/CacheGameObject.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UnityEngine; - -namespace Explorer -{ - public class CacheGameObject : CacheObjectBase - { - public override void DrawValue(Rect window, float width) - { - UIHelpers.GOButton(Value, null, false, width); - } - - public override void UpdateValue() - { - base.UpdateValue(); - } - } -} diff --git a/src/CachedObjects/Other/CacheOther.cs b/src/CachedObjects/Other/CacheOther.cs deleted file mode 100644 index 7916aa4..0000000 --- a/src/CachedObjects/Other/CacheOther.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Reflection; -using UnityEngine; - -namespace Explorer -{ - public class CacheOther : CacheObjectBase - { - public string ButtonLabel => m_btnLabel ?? GetButtonLabel(); - private string m_btnLabel; - - public MethodInfo ToStringMethod => m_toStringMethod ?? GetToStringMethod(); - private MethodInfo m_toStringMethod; - - public override void UpdateValue() - { - base.UpdateValue(); - - GetButtonLabel(); - } - - public override void DrawValue(Rect window, float width) - { - GUI.skin.button.alignment = TextAnchor.MiddleLeft; - if (GUILayout.Button(ButtonLabel, new GUILayoutOption[] { GUILayout.Width(width - 15) })) - { - WindowManager.InspectObject(Value, out bool _); - } - GUI.skin.button.alignment = TextAnchor.MiddleCenter; - } - - private MethodInfo GetToStringMethod() - { - try - { - m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0]) - ?? typeof(object).GetMethod("ToString", new Type[0]); - - // test invoke - m_toStringMethod.Invoke(Value, null); - } - catch - { - m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]); - } - return m_toStringMethod; - } - - private string GetButtonLabel() - { - if (Value == null) return null; - - string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString(); - - var classColor = ValueType.IsAbstract && ValueType.IsSealed - ? UIStyles.Syntax.Class_Static - : UIStyles.Syntax.Class_Instance; - - string typeLabel = $"{ValueType.FullName}"; - - if (Value is UnityEngine.Object) - { - label = label.Replace($"({ValueType.FullName})", $"({typeLabel})"); - } - else - { - if (!label.Contains(ValueType.FullName)) - { - label += $" ({typeLabel})"; - } - else - { - label = label.Replace(ValueType.FullName, typeLabel); - } - } - - return m_btnLabel = label; - } - } -} diff --git a/src/Config/ModConfig.cs b/src/Config/ModConfig.cs index df2d9f8..b7ffd2e 100644 --- a/src/Config/ModConfig.cs +++ b/src/Config/ModConfig.cs @@ -2,7 +2,7 @@ using System.Xml.Serialization; using UnityEngine; -namespace Explorer +namespace Explorer.Config { public class ModConfig { @@ -13,9 +13,13 @@ namespace Explorer [XmlIgnore] public static ModConfig Instance; - public KeyCode Main_Menu_Toggle = KeyCode.F7; + // Actual configs + public KeyCode Main_Menu_Toggle = KeyCode.F7; public Vector2 Default_Window_Size = new Vector2(550, 700); - public int Default_Page_Limit = 20; + public int Default_Page_Limit = 20; + public bool Bitwise_Support = false; + public bool Tab_View = true; + //public bool Main_Toggle_Global = true; public static void OnLoad() { @@ -43,7 +47,7 @@ namespace Explorer Instance = (ModConfig)Serializer.Deserialize(file); } } - catch + catch { return false; } diff --git a/src/Explorer.csproj b/src/Explorer.csproj index 52a2762..45bcfc7 100644 --- a/src/Explorer.csproj +++ b/src/Explorer.csproj @@ -197,58 +197,67 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - @@ -257,6 +266,7 @@ + diff --git a/src/ExplorerCore.cs b/src/ExplorerCore.cs index f1828f7..28b0b90 100644 --- a/src/ExplorerCore.cs +++ b/src/ExplorerCore.cs @@ -1,11 +1,16 @@ -using UnityEngine; +using Explorer.Config; +using Explorer.UI; +using Explorer.UI.Inspectors; +using Explorer.UI.Main; +using Explorer.UI.Shared; +using UnityEngine; namespace Explorer { public class ExplorerCore { - public const string NAME = "Explorer (" + PLATFORM + ", " + MODLOADER + ")"; - public const string VERSION = "1.8.3.1"; + public const string NAME = "Explorer " + VERSION + " (" + PLATFORM + ", " + MODLOADER + ")"; + public const string VERSION = "2.0.0"; public const string AUTHOR = "Sinai"; public const string GUID = "com.sinai.explorer"; @@ -34,9 +39,11 @@ namespace Explorer new WindowManager(); InputManager.Init(); - CursorControl.Init(); + ForceUnlockCursor.Init(); - Log($"{NAME} {VERSION} initialized."); + ShowMenu = true; + + Log($"{NAME} initialized."); } public static bool ShowMenu @@ -49,7 +56,7 @@ namespace Explorer private static void SetShowMenu(bool show) { m_showMenu = show; - CursorControl.UpdateCursorControl(); + ForceUnlockCursor.UpdateCursorControl(); } public static void Update() @@ -61,7 +68,7 @@ namespace Explorer if (ShowMenu) { - CursorControl.Update(); + //CursorControl.Update(); InspectUnderMouse.Update(); MainMenu.Instance.Update(); @@ -89,30 +96,30 @@ namespace Explorer SearchPage.Instance?.OnSceneChange(); } - public static void Log(string message) + public static void Log(object message) { #if ML - MelonLoader.MelonLogger.Log(message); + MelonLoader.MelonLogger.Log(message.ToString()); #else - ExplorerBepInPlugin.Logging?.LogMessage(message); + ExplorerBepInPlugin.Logging?.LogMessage(message.ToString()); #endif } - public static void LogWarning(string message) + public static void LogWarning(object message) { #if ML - MelonLoader.MelonLogger.LogWarning(message); + MelonLoader.MelonLogger.LogWarning(message.ToString()); #else - ExplorerBepInPlugin.Logging?.LogWarning(message); + ExplorerBepInPlugin.Logging?.LogWarning(message.ToString()); #endif } - public static void LogError(string message) + public static void LogError(object message) { #if ML - MelonLoader.MelonLogger.LogError(message); + MelonLoader.MelonLogger.LogError(message.ToString()); #else - ExplorerBepInPlugin.Logging?.LogError(message); + ExplorerBepInPlugin.Logging?.LogError(message.ToString()); #endif } } diff --git a/src/Helpers/ReflectionHelpers.cs b/src/Helpers/ReflectionHelpers.cs index 8a507c4..74f0c4d 100644 --- a/src/Helpers/ReflectionHelpers.cs +++ b/src/Helpers/ReflectionHelpers.cs @@ -19,11 +19,11 @@ namespace Explorer public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static; #if CPP - public static ILType GameObjectType => Il2CppType.Of(); - public static ILType TransformType => Il2CppType.Of(); - public static ILType ObjectType => Il2CppType.Of(); - public static ILType ComponentType => Il2CppType.Of(); - public static ILType BehaviourType => Il2CppType.Of(); + public static ILType GameObjectType => Il2CppType.Of(); + public static ILType TransformType => Il2CppType.Of(); + public static ILType ObjectType => Il2CppType.Of(); + public static ILType ComponentType => Il2CppType.Of(); + public static ILType BehaviourType => Il2CppType.Of(); private static readonly MethodInfo tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast"); private static readonly Dictionary cachedTryCastMethods = new Dictionary(); @@ -131,12 +131,12 @@ namespace Explorer return obj.GetType(); } - public static Type[] GetAllBaseTypes(object obj) + public static Type[] GetAllBaseTypes(object obj) => GetAllBaseTypes(GetActualType(obj)); + + public static Type[] GetAllBaseTypes(Type type) { var list = new List(); - var type = GetActualType(obj); - while (type != null) { list.Add(type); diff --git a/src/Input/InputManager.cs b/src/Input/InputManager.cs index 6885cc0..eb3dbf0 100644 --- a/src/Input/InputManager.cs +++ b/src/Input/InputManager.cs @@ -19,7 +19,7 @@ namespace Explorer { inputModule = new LegacyInput(); } - + if (inputModule == null) { ExplorerCore.LogWarning("Could not find any Input module!"); diff --git a/src/Menu/MainMenu/Pages/Console/REPL.cs b/src/Menu/MainMenu/Pages/Console/REPL.cs deleted file mode 100644 index 6fc97ce..0000000 --- a/src/Menu/MainMenu/Pages/Console/REPL.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using Mono.CSharp; -using UnityEngine; -using Attribute = System.Attribute; -using Object = UnityEngine.Object; - -namespace Explorer -{ - public class REPL : InteractiveBase - { - static REPL() - { - var go = new GameObject("UnityREPL"); - GameObject.DontDestroyOnLoad(go); - //go.transform.parent = HPExplorer.Instance.transform; - MB = go.AddComponent(); - } - - [Documentation("MB - A dummy MonoBehaviour for accessing Unity.")] - public static ReplHelper MB { get; } - - [Documentation("find() - find a UnityEngine.Object of type T.")] - public static T find() where T : Object - { - return MB.Find(); - } - - [Documentation("findAll() - find all UnityEngine.Object of type T.")] - public static T[] findAll() where T : Object - { - return MB.FindAll(); - } - - //[Documentation("runCoroutine(enumerator) - runs an IEnumerator as a Unity coroutine.")] - //public static object runCoroutine(IEnumerator i) - //{ - // return MB.RunCoroutine(i); - //} - - //[Documentation("endCoroutine(co) - ends a Unity coroutine.")] - //public static void endCoroutine(Coroutine c) - //{ - // MB.EndCoroutine(c); - //} - - ////[Documentation("type() - obtain type info about a type T. Provides some Reflection helpers.")] - ////public static TypeHelper type() - ////{ - //// return new TypeHelper(typeof(T)); - ////} - - ////[Documentation("type(obj) - obtain type info about object obj. Provides some Reflection helpers.")] - ////public static TypeHelper type(object instance) - ////{ - //// return new TypeHelper(instance); - ////} - - //[Documentation("dir(obj) - lists all available methods and fiels of a given obj.")] - //public static string dir(object instance) - //{ - // return type(instance).info(); - //} - - //[Documentation("dir() - lists all available methods and fields of type T.")] - //public static string dir() - //{ - // return type().info(); - //} - - //[Documentation("findrefs(obj) - find references to the object in currently loaded components.")] - //public static Component[] findrefs(object obj) - //{ - // if (obj == null) throw new ArgumentNullException(nameof(obj)); - - // var results = new List(); - // foreach (var component in Object.FindObjectsOfType()) - // { - // var type = component.GetType(); - - // var nameBlacklist = new[] { "parent", "parentInternal", "root", "transform", "gameObject" }; - // var typeBlacklist = new[] { typeof(bool) }; - - // foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - // .Where(x => x.CanRead && !nameBlacklist.Contains(x.Name) && !typeBlacklist.Contains(x.PropertyType))) - // { - // try - // { - // if (Equals(prop.GetValue(component, null), obj)) - // { - // results.Add(component); - // goto finish; - // } - // } - // catch { } - // } - // foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - // .Where(x => !nameBlacklist.Contains(x.Name) && !typeBlacklist.Contains(x.FieldType))) - // { - // try - // { - // if (Equals(field.GetValue(component), obj)) - // { - // results.Add(component); - // goto finish; - // } - // } - // catch { } - // } - // finish:; - // } - - // return results.ToArray(); - //} - - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)] - private class DocumentationAttribute : Attribute - { - public DocumentationAttribute(string doc) - { - Docs = doc; - } - - public string Docs { get; } - } - } -} \ No newline at end of file diff --git a/src/Menu/MainMenu/Pages/Console/REPLHelper.cs b/src/Menu/MainMenu/Pages/Console/REPLHelper.cs deleted file mode 100644 index eb95555..0000000 --- a/src/Menu/MainMenu/Pages/Console/REPLHelper.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace Explorer -{ - public class ReplHelper : MonoBehaviour - { -#if CPP - public ReplHelper(IntPtr intPtr) : base(intPtr) { } -#endif - - public T Find() where T : Object - { - return FindObjectOfType(); - } - - public T[] FindAll() where T : Object - { - return FindObjectsOfType(); - } - - //public object RunCoroutine(IEnumerator enumerator) - //{ - // return MelonCoroutines.Start(enumerator); - //} - - //public void EndCoroutine(Coroutine c) - //{ - // StopCoroutine(c); - //} - } -} \ No newline at end of file diff --git a/src/Menu/MainMenu/Pages/ConsolePage.cs b/src/Menu/MainMenu/Pages/ConsolePage.cs deleted file mode 100644 index db9d4cd..0000000 --- a/src/Menu/MainMenu/Pages/ConsolePage.cs +++ /dev/null @@ -1,253 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text; -using Mono.CSharp; -using UnityEngine; - -namespace Explorer -{ - public class ConsolePage : WindowPage - { - public override string Name { get => "C# Console"; } - - private ScriptEvaluator _evaluator; - private readonly StringBuilder _sb = new StringBuilder(); - - private Vector2 inputAreaScroll; - - private string MethodInput = ""; - private string UsingInput = ""; - - public static List UsingDirectives; - - private static readonly string[] m_defaultUsing = new string[] - { - "System", - "UnityEngine", - "System.Linq", - "System.Collections", - "System.Collections.Generic", - "System.Reflection", -#if ML - "MelonLoader" -#endif - }; - - public override void Init() - { -#if CPP - UnhollowerRuntimeLib.ClassInjector.RegisterTypeInIl2Cpp(); -#endif - try - { - MethodInput = @"// This is a basic C# console. -// Some common using directives are added by default, you can add more below. -// If you want to return some output, Debug.Log() or MelonLogger.Log() it. - -" -#if ML -+ @"MelonLogger.Log(""hello world"");"; -#else -+ @"Debug.Log(""hello world"");"; -#endif - ; - - ResetConsole(); - - foreach (var use in m_defaultUsing) - { - AddUsing(use); - } - } - catch (Exception e) - { - ExplorerCore.Log($"Error setting up console!\r\nMessage: {e.Message}"); - MainMenu.SetCurrentPage(0); - MainMenu.Pages.Remove(this); - } - } - - public void ResetConsole() - { - if (_evaluator != null) - { - _evaluator.Dispose(); - } - - _evaluator = new ScriptEvaluator(new StringWriter(_sb)) { InteractiveBaseClass = typeof(REPL) }; - - UsingDirectives = new List(); - } - - public string AsmToUsing(string asm, bool richtext = false) - { - if (richtext) - { - return $"using {asm};"; - } - return $"using {asm};"; - } - - public void AddUsing(string asm) - { - if (!UsingDirectives.Contains(asm)) - { - UsingDirectives.Add(asm); - Evaluate(AsmToUsing(asm), true); - } - } - - public object Evaluate(string str, bool suppressWarning = false) - { - object ret = VoidType.Value; - - _evaluator.Compile(str, out var compiled); - - try - { - if (compiled == null) - { - throw new Exception("Mono.Csharp Service was unable to compile the code provided."); - } - - compiled.Invoke(ref ret); - } - catch (Exception e) - { - if (!suppressWarning) - { - ExplorerCore.LogWarning(e.GetType() + ", " + e.Message); - } - } - - return ret; - } - - - public override void DrawWindow() - { - GUILayout.Label("C# Console", new GUILayoutOption[0]); - - GUI.skin.label.alignment = TextAnchor.UpperLeft; - - GUILayout.Label("Enter code here as though it is a method body:", new GUILayoutOption[0]); - - inputAreaScroll = GUIUnstrip.BeginScrollView(inputAreaScroll, new GUILayoutOption[] { GUILayout.Height(250) }); - - MethodInput = GUIUnstrip.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.ExpandHeight(true) }); - - GUIUnstrip.EndScrollView(); - - if (GUILayout.Button("Execute", new GUILayoutOption[0])) - { - try - { - MethodInput = MethodInput.Trim(); - - if (!string.IsNullOrEmpty(MethodInput)) - { - var result = Evaluate(MethodInput); - - if (result != null && !Equals(result, VoidType.Value)) - { - ExplorerCore.Log("[Console Output]\r\n" + result.ToString()); - } - } - } - catch (Exception e) - { - ExplorerCore.LogError("Exception compiling!\r\nMessage: " + e.Message + "\r\nStack: " + e.StackTrace); - } - } - - GUILayout.Label("Using directives:", new GUILayoutOption[0]); - - GUILayout.BeginHorizontal(new GUILayoutOption[0]); - GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(105) }); - UsingInput = GUIUnstrip.TextField(UsingInput, new GUILayoutOption[] { GUILayout.Width(150) }); - if (GUILayout.Button("Add", new GUILayoutOption[] { GUILayout.Width(120) })) - { - AddUsing(UsingInput); - } - if (GUILayout.Button("Clear All", new GUILayoutOption[] { GUILayout.Width(120) })) - { - ResetConsole(); - } - GUILayout.EndHorizontal(); - - foreach (var asm in UsingDirectives) - { - GUILayout.Label(AsmToUsing(asm, true), new GUILayoutOption[0]); - } - } - - public override void Update() { } - - private class VoidType - { - public static readonly VoidType Value = new VoidType(); - private VoidType() { } - } - - } - - internal class ScriptEvaluator : Evaluator, IDisposable - { - private static readonly HashSet StdLib = - new HashSet(StringComparer.InvariantCultureIgnoreCase) { "mscorlib", "System.Core", "System", "System.Xml" }; - - private readonly TextWriter _logger; - - public ScriptEvaluator(TextWriter logger) : base(BuildContext(logger)) - { - _logger = logger; - - ImportAppdomainAssemblies(ReferenceAssembly); - AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad; - } - - public void Dispose() - { - AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad; - _logger.Dispose(); - } - - private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args) - { - string name = args.LoadedAssembly.GetName().Name; - if (StdLib.Contains(name)) - return; - ReferenceAssembly(args.LoadedAssembly); - } - - private static CompilerContext BuildContext(TextWriter tw) - { - var reporter = new StreamReportPrinter(tw); - - var settings = new CompilerSettings - { - Version = LanguageVersion.Experimental, - GenerateDebugInfo = false, - StdLib = true, - Target = Target.Library, - WarningLevel = 0, - EnhancedWarnings = false - }; - - return new CompilerContext(settings, reporter); - } - - private static void ImportAppdomainAssemblies(Action import) - { - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) - { - string name = assembly.GetName().Name; - if (StdLib.Contains(name)) - continue; - import(assembly); - } - } - } -} diff --git a/src/Menu/MainMenu/Pages/OptionsPage.cs b/src/Menu/MainMenu/Pages/OptionsPage.cs deleted file mode 100644 index 50291dc..0000000 --- a/src/Menu/MainMenu/Pages/OptionsPage.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Explorer.Tests; -using UnityEngine; - -namespace Explorer -{ - public class OptionsPage : WindowPage - { - public override string Name => "Options"; - - public string toggleKeyInputString = ""; - public Vector2 defaultSizeInputVector; - public int defaultPageLimit; - - private CacheObjectBase toggleKeyInput; - private CacheObjectBase defaultSizeInput; - private CacheObjectBase defaultPageLimitInput; - - public override void Init() - { - toggleKeyInputString = ModConfig.Instance.Main_Menu_Toggle.ToString(); - toggleKeyInput = CacheFactory.GetTypeAndCacheObject(typeof(OptionsPage).GetField("toggleKeyInputString"), this); - - defaultSizeInputVector = ModConfig.Instance.Default_Window_Size; - defaultSizeInput = CacheFactory.GetTypeAndCacheObject(typeof(OptionsPage).GetField("defaultSizeInputVector"), this); - - defaultPageLimit = ModConfig.Instance.Default_Page_Limit; - defaultPageLimitInput = CacheFactory.GetTypeAndCacheObject(typeof(OptionsPage).GetField("defaultPageLimit"), this); - } - - public override void Update() { } - - public override void DrawWindow() - { - GUI.skin.label.alignment = TextAnchor.MiddleCenter; - GUILayout.Label("Settings", new GUILayoutOption[0]); - GUI.skin.label.alignment = TextAnchor.MiddleLeft; - - GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[0]); - - GUILayout.BeginHorizontal(new GUILayoutOption[0]); - GUILayout.Label($"Menu Toggle Key:", new GUILayoutOption[] { GUILayout.Width(215f) }); - toggleKeyInput.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f); - GUILayout.EndHorizontal(); - - GUILayout.BeginHorizontal(new GUILayoutOption[0]); - GUILayout.Label($"Default Window Size:", new GUILayoutOption[] { GUILayout.Width(215f) }); - defaultSizeInput.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f); - GUILayout.EndHorizontal(); - - GUILayout.BeginHorizontal(new GUILayoutOption[0]); - GUILayout.Label($"Default Items per Page:", new GUILayoutOption[] { GUILayout.Width(215f) }); - defaultPageLimitInput.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f); - GUILayout.EndHorizontal(); - - if (GUILayout.Button("Apply and Save", new GUILayoutOption[0])) - { - ApplyAndSave(); - } - - GUILayout.EndVertical(); - - //GUIUnstrip.Space(10f); - - //GUI.skin.label.alignment = TextAnchor.MiddleCenter; - //GUILayout.Label("Other", new GUILayoutOption[0]); - //GUI.skin.label.alignment = TextAnchor.MiddleLeft; - - //GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[0]); - - //if (GUILayout.Button("Inspect Test Class", new GUILayoutOption[0])) - //{ - // WindowManager.InspectObject(TestClass.Instance, out bool _); - //} - - //GUILayout.EndVertical(); - } - - private void ApplyAndSave() - { - if (Enum.Parse(typeof(KeyCode), toggleKeyInputString) is KeyCode key) - { - ModConfig.Instance.Main_Menu_Toggle = key; - } - else - { - ExplorerCore.LogWarning($"Could not parse '{toggleKeyInputString}' to KeyCode!"); - } - - ModConfig.Instance.Default_Window_Size = defaultSizeInputVector; - ModConfig.Instance.Default_Page_Limit = defaultPageLimit; - - ModConfig.SaveSettings(); - } - } -} diff --git a/src/Tests/TestClass.cs b/src/Tests/TestClass.cs index 512cec2..ff57f2b 100644 --- a/src/Tests/TestClass.cs +++ b/src/Tests/TestClass.cs @@ -3,46 +3,35 @@ using System.Collections.Generic; using System; using UnityEngine; using System.Reflection; -using System.Collections.Specialized; - -// used to test multiple generic constraints -public class TestGeneric : IComparable -{ - public TestGeneric() { } - - public int CompareTo(string other) => throw new NotImplementedException(); -} - -[Flags] -public enum TestFlags -{ - Red, - Green, - Blue -} - -// test non-flags weird enum -public enum WeirdEnum -{ - First = 1, - Second, - Third = 2, - Fourth, - Fifth -} namespace Explorer.Tests { + public static class StaticTestClass + { + public static int StaticProperty => 5; + + public static int StaticField = 69; + + public static List StaticList = new List + { + "one", + "two", + "three", + }; + + public static void StaticMethod() { } + + } + public class TestClass { - public static TestFlags testFlags = TestFlags.Blue | TestFlags.Green; - public static WeirdEnum testWeird = WeirdEnum.First; - - public static int testBitmask; - public static TestClass Instance => m_instance ?? (m_instance = new TestClass()); private static TestClass m_instance; + public static int StaticProperty => 5; + public static int StaticField = 5; + public int NonStaticField; + #if CPP public static Il2CppSystem.Collections.Generic.HashSet ILHashSetTest; #endif @@ -55,17 +44,6 @@ namespace Explorer.Tests ILHashSetTest.Add("2"); ILHashSetTest.Add("3"); #endif - - testBitmask = 1 | 2; - } - - public static int StaticProperty => 5; - public static int StaticField = 5; - public int NonStaticField; - - public static string TestGeneric(string arg0) where C : Component where T : TestGeneric, IComparable - { - return $"C: '{typeof(C).FullName}', T: '{typeof(T).FullName}', arg0: '{arg0}'"; } public static string TestRefInOutGeneric(ref string arg0, in int arg1, out string arg2) @@ -75,12 +53,6 @@ namespace Explorer.Tests return $"T: '{typeof(T).FullName}', ref arg0: '{arg0}', in arg1: '{arg1}', out arg2: '{arg2}'"; } - //// this type of generic is not supported, due to requiring a non-primitive argument. - //public static T TestDifferentGeneric(T obj) where T : Component - //{ - // return obj; - //} - // test a non-generic dictionary public Hashtable TestNonGenericDict() @@ -93,18 +65,6 @@ namespace Explorer.Tests }; } - // IL2CPP HASHTABLE NOT SUPPORTED! Cannot assign Il2CppSystem.Object from primitive struct / string. - // Technically they are "supported" but if they contain System types they will not work. - - //public Il2CppSystem.Collections.Hashtable TestIl2CppNonGenericDict() - //{ - // var table = new Il2CppSystem.Collections.Hashtable(); - // table.Add("One", 1); - // table.Add("One", 2); - // table.Add("One", 3); - // return table; - //} - // test HashSets public static HashSet HashSetTest = new HashSet diff --git a/src/Menu/CursorControl.cs b/src/UI/ForceUnlockCursor.cs similarity index 81% rename from src/Menu/CursorControl.cs rename to src/UI/ForceUnlockCursor.cs index dc1bdff..a6ec1bf 100644 --- a/src/Menu/CursorControl.cs +++ b/src/UI/ForceUnlockCursor.cs @@ -6,21 +6,22 @@ using Harmony; using HarmonyLib; #endif -namespace Explorer +namespace Explorer.UI { - public class CursorControl + public class ForceUnlockCursor { - public static bool ForceUnlockMouse + public static bool Unlock { get => m_forceUnlock; set => SetForceUnlock(value); } private static bool m_forceUnlock; + private static CursorLockMode m_lastLockMode; private static bool m_lastVisibleState; private static bool m_currentlySettingCursor = false; - public static bool ShouldForceMouse => ExplorerCore.ShowMenu && ForceUnlockMouse; + public static bool ShouldForceMouse => ExplorerCore.ShowMenu && Unlock; private static Type CursorType => m_cursorType ?? (m_cursorType = ReflectionHelpers.GetTypeByName("UnityEngine.Cursor")); private static Type m_cursorType; @@ -49,29 +50,24 @@ namespace Explorer m_lastVisibleState = Cursor.visible; // Setup Harmony Patches - TryPatch("lockState", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Prefix_set_lockState))), true); - TryPatch("lockState", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Postfix_get_lockState))), false); + TryPatch("lockState", new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_lockState))), true); + TryPatch("lockState", new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Postfix_get_lockState))), false); - TryPatch("visible", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Prefix_set_visible))), true); - TryPatch("visible", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Postfix_get_visible))), false); + TryPatch("visible", new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_visible))), true); + TryPatch("visible", new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Postfix_get_visible))), false); } catch (Exception e) { ExplorerCore.Log($"Exception on CursorControl.Init! {e.GetType()}, {e.Message}"); } - // Enable ShowMenu and ForceUnlockMouse - // (set m_showMenu directly to not call UpdateCursorState twice) - ExplorerCore.m_showMenu = true; - ForceUnlockMouse = true; + Unlock = true; } private static void TryPatch(string property, HarmonyMethod patch, bool setter) { try { - // var harmony = ExplorerCore.Instance.harmonyInstance; - var harmony = #if ML ExplorerMelonMod.Instance.harmonyInstance; @@ -85,12 +81,12 @@ namespace Explorer if (setter) { // setter is prefix - harmony.Patch(prop.GetSetMethod(), patch); + harmony.Patch(prop.GetSetMethod(), prefix: patch); } else { // getter is postfix - harmony.Patch(prop.GetGetMethod(), null, patch); + harmony.Patch(prop.GetGetMethod(), postfix: patch); } } catch (Exception e) @@ -110,7 +106,7 @@ namespace Explorer // Check Force-Unlock input if (InputManager.GetKeyDown(KeyCode.LeftAlt)) { - ForceUnlockMouse = !ForceUnlockMouse; + Unlock = !Unlock; } } diff --git a/src/Menu/Windows/GameObjectWindow.cs b/src/UI/Inspectors/GameObjectInspector.cs similarity index 89% rename from src/Menu/Windows/GameObjectWindow.cs rename to src/UI/Inspectors/GameObjectInspector.cs index e10d79e..cb70a34 100644 --- a/src/Menu/Windows/GameObjectWindow.cs +++ b/src/UI/Inspectors/GameObjectInspector.cs @@ -1,18 +1,19 @@ using System; using System.Collections.Generic; using UnityEngine; - +using Explorer.UI.Shared; +using Explorer.UI.Main; #if CPP using UnhollowerRuntimeLib; #endif -namespace Explorer +namespace Explorer.UI.Inspectors { - public class GameObjectWindow : UIWindow + public class GameObjectInspector : WindowBase { public override string Title => WindowManager.TabView - ? $"[G] {TargetGO.name}" - : $"GameObject Inspector ({TargetGO.name})"; + ? $"[G] {TargetGO.name}" + : $"GameObject Inspector ({TargetGO.name})"; public GameObject TargetGO; @@ -49,7 +50,7 @@ namespace Explorer public bool GetObjectAsGameObject() { - var targetType = Target.GetType(); + var targetType = Target?.GetType(); if (targetType == typeof(GameObject)) { @@ -75,8 +76,8 @@ namespace Explorer } m_name = TargetGO.name; - m_scene = string.IsNullOrEmpty(TargetGO.scene.name) - ? "None (Asset/Resource)" + m_scene = string.IsNullOrEmpty(TargetGO.scene.name) + ? "None (Asset/Resource)" : TargetGO.scene.name; CacheTransformValues(); @@ -109,19 +110,11 @@ namespace Explorer DestroyWindow(); return; } - if (Target is UnityEngine.Object uObj) - { - if (!uObj) - { - ExplorerCore.Log("Target was destroyed!"); - DestroyWindow(); - return; - } - } - if (!TargetGO && !GetObjectAsGameObject()) { - throw new Exception("Object is null!"); + ExplorerCore.Log("Target was destroyed!"); + DestroyWindow(); + return; } if (m_freeze) @@ -176,7 +169,7 @@ namespace Explorer private void InspectGameObject(Transform obj) { var window = WindowManager.InspectObject(obj, out bool created); - + if (created) { window.m_rect = new Rect(this.m_rect.x, this.m_rect.y, this.m_rect.width, this.m_rect.height); @@ -189,7 +182,7 @@ namespace Explorer #else private void ReflectObject(object obj) #endif - { + { var window = WindowManager.InspectObject(obj, out bool created, true); if (created) @@ -223,7 +216,7 @@ namespace Explorer scroll = GUIUnstrip.BeginScrollView(scroll); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUILayout.Label("Scene: " + (m_scene == "" ? "n/a" : m_scene) + "", new GUILayoutOption[0]); if (m_scene == UnityHelpers.ActiveSceneName) { @@ -239,7 +232,7 @@ namespace Explorer } GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) }); string pathlabel = TargetGO.transform.GetGameObjectPath(); if (TargetGO.transform.parent != null) @@ -252,19 +245,19 @@ namespace Explorer GUIUnstrip.TextArea(pathlabel, new GUILayoutOption[0]); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUILayout.Label("Name:", new GUILayoutOption[] { GUILayout.Width(50) }); GUIUnstrip.TextArea(m_name, new GUILayoutOption[0]); GUILayout.EndHorizontal(); // --- Horizontal Columns section --- - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); - GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) }); + GUIUnstrip.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) }); TransformList(rect); GUILayout.EndVertical(); - GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) }); + GUIUnstrip.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) }); ComponentList(rect); GUILayout.EndVertical(); @@ -289,12 +282,12 @@ namespace Explorer private void TransformList(Rect m_rect) { - GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); + GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null); m_transformScroll = GUIUnstrip.BeginScrollView(m_transformScroll); GUILayout.Label("Children", new GUILayoutOption[0]); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); ChildPages.DrawLimitInputArea(); if (ChildPages.ItemCount > ChildPages.ItemsPerPage) @@ -302,7 +295,7 @@ namespace Explorer ChildPages.CurrentPageLabel(); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) })) { @@ -329,7 +322,7 @@ namespace Explorer continue; } - UIHelpers.GOButton(obj.gameObject, InspectGameObject, false, m_rect.width / 2 - 80); + Buttons.GameObjectButton(obj.gameObject, InspectGameObject, false, m_rect.width / 2 - 80); } } else @@ -343,11 +336,11 @@ namespace Explorer private void ComponentList(Rect m_rect) { - GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); + GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null); m_compScroll = GUIUnstrip.BeginScrollView(m_compScroll); GUILayout.Label("Components", new GUILayoutOption[0]); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); CompPages.DrawLimitInputArea(); if (CompPages.ItemCount > CompPages.ItemsPerPage) @@ -355,7 +348,7 @@ namespace Explorer CompPages.CurrentPageLabel(); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) })) { @@ -368,7 +361,7 @@ namespace Explorer } GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); var width = m_rect.width / 2 - 135f; m_addComponentInput = GUIUnstrip.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(width) }); if (GUILayout.Button("Add Comp", new GUILayoutOption[0])) @@ -417,7 +410,7 @@ namespace Explorer #else component.GetType(); #endif - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); if (ReflectionHelpers.BehaviourType.IsAssignableFrom(type)) { #if CPP @@ -484,7 +477,7 @@ namespace Explorer { if (m_hideControls) { - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUILayout.Label("GameObject Controls", new GUILayoutOption[] { GUILayout.Width(200) }); if (GUILayout.Button("^ Show ^", new GUILayoutOption[] { GUILayout.Width(75) })) { @@ -495,9 +488,9 @@ namespace Explorer return; } - GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(520) }); + GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(520) }); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUILayout.Label("GameObject Controls", new GUILayoutOption[] { GUILayout.Width(200) }); if (GUILayout.Button("v Hide v", new GUILayoutOption[] { GUILayout.Width(75) })) { @@ -505,13 +498,13 @@ namespace Explorer } GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); bool m_active = TargetGO.activeSelf; m_active = GUILayout.Toggle(m_active, (m_active ? "Enabled " : "Disabled") + "", new GUILayoutOption[] { GUILayout.Width(80) }); if (TargetGO.activeSelf != m_active) { TargetGO.SetActive(m_active); } - UIHelpers.InstantiateButton(TargetGO, 100); + Buttons.InstantiateButton(TargetGO, 100); if (GUILayout.Button("Set DontDestroyOnLoad", new GUILayoutOption[] { GUILayout.Width(170) })) { @@ -530,7 +523,7 @@ namespace Explorer } GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); m_setParentInput = GUIUnstrip.TextField(m_setParentInput, new GUILayoutOption[0]); if (GUILayout.Button("Set Parent", new GUILayoutOption[] { GUILayout.Width(80) })) @@ -551,13 +544,13 @@ namespace Explorer } GUILayout.EndHorizontal(); - GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); + GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null); - m_cachedInput[0] = TranslateControl(TranslateType.Position, ref m_translateAmount, false); - m_cachedInput[1] = TranslateControl(TranslateType.Rotation, ref m_rotateAmount, true); - m_cachedInput[2] = TranslateControl(TranslateType.Scale, ref m_scaleAmount, false); + m_cachedInput[0] = TranslateControl(TranslateType.Position, ref m_translateAmount, false); + m_cachedInput[1] = TranslateControl(TranslateType.Rotation, ref m_rotateAmount, true); + m_cachedInput[2] = TranslateControl(TranslateType.Scale, ref m_scaleAmount, false); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); if (GUILayout.Button("Apply to Transform", new GUILayoutOption[0]) || m_autoApplyTransform) { if (m_localContext) @@ -583,7 +576,7 @@ namespace Explorer } GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); BoolToggle(ref m_autoApplyTransform, "Auto-apply to Transform?"); BoolToggle(ref m_autoUpdateTransform, "Auto-update from transform?"); GUILayout.EndHorizontal(); @@ -645,8 +638,8 @@ namespace Explorer private Vector3 TranslateControl(TranslateType mode, ref float amount, bool multByTime) { - GUILayout.BeginHorizontal(new GUILayoutOption[0]); - GUILayout.Label($"{(m_localContext ? "Local " : "")}{mode}:", + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + GUILayout.Label($"{(m_localContext ? "Local " : "")}{mode}:", new GUILayoutOption[] { GUILayout.Width(m_localContext ? 110 : 65) }); var transform = TargetGO.transform; @@ -668,7 +661,7 @@ namespace Explorer Vector3 input = m_cachedInput[(int)mode]; - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUI.skin.label.alignment = TextAnchor.MiddleRight; GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(20) }); @@ -678,7 +671,7 @@ namespace Explorer PlusMinusFloat(ref input.y, amount, multByTime); GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(20) }); - PlusMinusFloat(ref input.z, amount, multByTime); + PlusMinusFloat(ref input.z, amount, multByTime); GUILayout.Label("+/-:", new GUILayoutOption[] { GUILayout.Width(30) }); var amountInput = amount.ToString("F3"); diff --git a/src/Menu/InspectUnderMouse.cs b/src/UI/Inspectors/InspectUnderMouse.cs similarity index 96% rename from src/Menu/InspectUnderMouse.cs rename to src/UI/Inspectors/InspectUnderMouse.cs index 85d390e..96fc08d 100644 --- a/src/Menu/InspectUnderMouse.cs +++ b/src/UI/Inspectors/InspectUnderMouse.cs @@ -1,12 +1,12 @@ using UnityEngine; -namespace Explorer +namespace Explorer.UI.Inspectors { public class InspectUnderMouse { public static bool EnableInspect { get; set; } = false; - private static string m_objUnderMouseName = ""; + private static string m_objUnderMouseName = ""; public static void Update() { diff --git a/src/UI/Inspectors/Reflection/InstanceInspector.cs b/src/UI/Inspectors/Reflection/InstanceInspector.cs new file mode 100644 index 0000000..172ad01 --- /dev/null +++ b/src/UI/Inspectors/Reflection/InstanceInspector.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; +using Explorer.UI; +using Explorer.UI.Shared; +using Explorer.CacheObject; +#if CPP +using UnhollowerBaseLib; +#endif + +namespace Explorer.UI.Inspectors +{ + public class InstanceInspector : ReflectionInspector + { + public override bool IsStaticInspector => false; + + // some extra cast-caching + public UnityEngine.Object m_uObj; + private Component m_component; + + public override void Init() + { + // cache the extra cast-caching +#if CPP + if (!IsStaticInspector && Target is Il2CppSystem.Object ilObject) + { + var unityObj = ilObject.TryCast(); + if (unityObj) + { + m_uObj = unityObj; + + var component = ilObject.TryCast(); + if (component) + { + m_component = component; + } + } + } +#else + if (!IsStaticInspector) + { + m_uObj = Target as UnityEngine.Object; + m_component = Target as Component; + } +#endif + + base.Init(); + } + + public override void Update() + { + if (Target == null) + { + ExplorerCore.Log("Target is null!"); + DestroyWindow(); + return; + } + if (Target is UnityEngine.Object uObj) + { + if (!uObj) + { + ExplorerCore.Log("Target was destroyed!"); + DestroyWindow(); + return; + } + } + + base.Update(); + } + + public void DrawInstanceControls(Rect rect) + { + if (m_uObj) + { + GUILayout.Label("Name: " + m_uObj.name, new GUILayoutOption[0]); + } + GUILayout.EndHorizontal(); + + if (m_uObj) + { + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + GUILayout.Label("Tools:", new GUILayoutOption[] { GUILayout.Width(80) }); + Buttons.InstantiateButton(m_uObj); + if (m_component && m_component.gameObject is GameObject obj) + { + GUI.skin.label.alignment = TextAnchor.MiddleRight; + GUILayout.Label("GameObject:", new GUILayoutOption[] { GUILayout.Width(135) }); + var charWidth = obj.name.Length * 15; + var maxWidth = rect.width - 350; + var labelWidth = charWidth < maxWidth ? charWidth : maxWidth; + if (GUILayout.Button("" + obj.name + "", new GUILayoutOption[] { GUILayout.Width(labelWidth) })) + { + WindowManager.InspectObject(obj, out bool _); + } + GUI.skin.label.alignment = TextAnchor.UpperLeft; + } + GUILayout.EndHorizontal(); + } + } + } +} diff --git a/src/UI/Inspectors/Reflection/StaticInspector.cs b/src/UI/Inspectors/Reflection/StaticInspector.cs new file mode 100644 index 0000000..c3cc84e --- /dev/null +++ b/src/UI/Inspectors/Reflection/StaticInspector.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Explorer.CacheObject; + +namespace Explorer.UI.Inspectors +{ + public class StaticInspector : ReflectionInspector + { + public override bool IsStaticInspector => true; + + public override void Init() + { + base.Init(); + } + + public override bool ShouldProcessMember(CacheMember holder) + { + return base.ShouldProcessMember(holder); + } + + public override void WindowFunction(int windowID) + { + base.WindowFunction(windowID); + } + } +} diff --git a/src/Menu/Windows/ReflectionWindow.cs b/src/UI/Inspectors/ReflectionInspector.cs similarity index 55% rename from src/Menu/Windows/ReflectionWindow.cs rename to src/UI/Inspectors/ReflectionInspector.cs index 8e45db9..2fcbfc7 100644 --- a/src/Menu/Windows/ReflectionWindow.cs +++ b/src/UI/Inspectors/ReflectionInspector.cs @@ -3,33 +3,43 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEngine; +using Explorer.UI; +using Explorer.UI.Shared; +using Explorer.CacheObject; +using Explorer.UI.Inspectors; #if CPP using UnhollowerBaseLib; #endif -namespace Explorer +namespace Explorer.UI.Inspectors { - public class ReflectionWindow : UIWindow + public abstract class ReflectionInspector : WindowBase { public override string Title => WindowManager.TabView - ? $"[R] {TargetType.Name}" + ? $"[R] {TargetType.Name}" : $"Reflection Inspector ({TargetType.Name})"; + public virtual bool IsStaticInspector { get; } + public Type TargetType; - private CacheObjectBase[] m_allCachedMembers; - private CacheObjectBase[] m_cachedMembersFiltered; + private CacheMember[] m_allCachedMembers; + private CacheMember[] m_cachedMembersFiltered; public PageHelper Pages = new PageHelper(); private bool m_autoUpdate = false; private string m_search = ""; - public MemberTypes m_filter = MemberTypes.Property; + public MemberTypes m_typeFilter = MemberTypes.Property; private bool m_hideFailedReflection = false; - // some extra cast-caching - private UnityEngine.Object m_uObj; - private Component m_component; + private MemberScopes m_scopeFilter; + private enum MemberScopes + { + Both, + Instance, + Static + } private static readonly HashSet _typeAndMemberBlacklist = new HashSet { @@ -48,49 +58,23 @@ namespace Explorer public override void Init() { - TargetType = ReflectionHelpers.GetActualType(Target); - - CacheMembers(ReflectionHelpers.GetAllBaseTypes(Target)); - - // cache the extra cast-caching -#if CPP - if (Target is Il2CppSystem.Object ilObject) + if (!IsStaticInspector) { - var unityObj = ilObject.TryCast(); - if (unityObj) - { - m_uObj = unityObj; - - var component = ilObject.TryCast(); - if (component) - { - m_component = component; - } - } + TargetType = ReflectionHelpers.GetActualType(Target); + CacheMembers(ReflectionHelpers.GetAllBaseTypes(Target)); + } + else + { + CacheMembers(new Type[] { TargetType }); } -#else - m_uObj = Target as UnityEngine.Object; - m_component = Target as Component; -#endif } public override void Update() { - if (Target == null) + if (m_allCachedMembers == null) { - ExplorerCore.Log("Target is null!"); - DestroyWindow(); return; } - if (Target is UnityEngine.Object uObj) - { - if (!uObj) - { - ExplorerCore.Log("Target was destroyed!"); - DestroyWindow(); - return; - } - } m_cachedMembersFiltered = m_allCachedMembers.Where(x => ShouldProcessMember(x)).ToArray(); @@ -108,18 +92,28 @@ namespace Explorer } } - private bool ShouldProcessMember(CacheObjectBase holder) + public virtual bool ShouldProcessMember(CacheMember holder) { // check MemberTypes filter - if (m_filter != MemberTypes.All && m_filter != holder.MemInfo?.MemberType) + if (m_typeFilter != MemberTypes.All && m_typeFilter != holder.MemInfo?.MemberType) return false; + // check scope filter + if (m_scopeFilter == MemberScopes.Instance) + { + return !holder.IsStatic; + } + else if (m_scopeFilter == MemberScopes.Static) + { + return holder.IsStatic; + } + // hide failed reflection - if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) + if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) return false; // see if we should do name search - if (m_search == "" || holder.MemInfo == null) + if (m_search == "" || holder.MemInfo == null) return true; // ok do name search @@ -130,7 +124,7 @@ namespace Explorer private void CacheMembers(Type[] types) { - var list = new List(); + var list = new List(); var cachedSigs = new List(); foreach (var declaringType in types) @@ -140,7 +134,7 @@ namespace Explorer { infos = declaringType.GetMembers(ReflectionHelpers.CommonFlags); } - catch + catch { ExplorerCore.Log($"Exception getting members for type: {declaringType.FullName}"); continue; @@ -150,7 +144,7 @@ namespace Explorer string exception = null; #if CPP - if (target is Il2CppSystem.Object ilObject) + if (!IsStaticInspector && target is Il2CppSystem.Object ilObject) { try { @@ -162,61 +156,79 @@ namespace Explorer } } #endif - foreach (var member in infos) { - // make sure member type is Field, Method of Property (4 / 8 / 16) - int m = (int)member.MemberType; - if (m < 4 || m > 16) - continue; - - // check blacklisted members - var sig = $"{member.DeclaringType.Name}.{member.Name}"; - if (_typeAndMemberBlacklist.Any(it => it == sig)) - continue; - - if (_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it))) - continue; - - if (member is MethodInfo mi) - { - AppendParams(mi.GetParameters()); - } - else if (member is PropertyInfo pi) - { - AppendParams(pi.GetIndexParameters()); - } - - void AppendParams(ParameterInfo[] _args) - { - sig += " ("; - foreach (var param in _args) - { - sig += $"{param.ParameterType.Name} {param.Name}, "; - } - sig += ")"; - } - - if (cachedSigs.Contains(sig)) - { - continue; - } - - //ExplorerCore.Log($"Trying to cache member {sig}..."); - try { - var cached = CacheFactory.GetTypeAndCacheObject(member, target); - if (cached != null) + // make sure member type is Field, Method of Property (4 / 8 / 16) + int m = (int)member.MemberType; + if (m < 4 || m > 16) + continue; + + var fi = member as FieldInfo; + var pi = member as PropertyInfo; + var mi = member as MethodInfo; + + if (IsStaticInspector) { - cachedSigs.Add(sig); - list.Add(cached); - cached.ReflectionException = exception; + if (fi != null && !fi.IsStatic) continue; + else if (pi != null && !pi.GetAccessors()[0].IsStatic) continue; + else if (mi != null && !mi.IsStatic) continue; } - } + + // check blacklisted members + var sig = $"{member.DeclaringType.Name}.{member.Name}"; + if (_typeAndMemberBlacklist.Any(it => it == sig)) + continue; + + if (_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it))) + continue; + + if (mi != null) + { + AppendParams(mi.GetParameters()); + } + else if (pi != null) + { + AppendParams(pi.GetIndexParameters()); + } + + void AppendParams(ParameterInfo[] _args) + { + sig += " ("; + foreach (var param in _args) + { + sig += $"{param.ParameterType.Name} {param.Name}, "; + } + sig += ")"; + } + + if (cachedSigs.Contains(sig)) + { + continue; + } + + //ExplorerCore.Log($"Trying to cache member {sig}..."); + + try + { + var cached = CacheFactory.GetCacheObject(member, target); + if (cached != null) + { + cachedSigs.Add(sig); + list.Add(cached); + cached.ReflectionException = exception; + } + } + catch (Exception e) + { + ExplorerCore.LogWarning($"Exception caching member {sig}!"); + ExplorerCore.Log(e.ToString()); + } + } catch (Exception e) { - ExplorerCore.LogWarning($"Exception caching member {sig}!"); + ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!"); ExplorerCore.Log(e.ToString()); } } @@ -241,51 +253,49 @@ namespace Explorer GUIUnstrip.BeginArea(new Rect(5, 25, rect.width - 10, rect.height - 35), GUI.skin.box); } - GUILayout.BeginHorizontal(new GUILayoutOption[0]); - GUILayout.Label("Type: " + TargetType.FullName + "", new GUILayoutOption[] { GUILayout.Width(245f) }); - if (m_uObj) - { - GUILayout.Label("Name: " + m_uObj.name, new GUILayoutOption[0]); - } - GUILayout.EndHorizontal(); + var asInstance = this as InstanceInspector; - if (m_uObj) + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + var labelWidth = (asInstance != null && asInstance.m_uObj) + ? new GUILayoutOption[] { GUILayout.Width(245f) } + : new GUILayoutOption[0]; + GUILayout.Label("Type: " + TargetType.FullName + "", labelWidth); + + if (asInstance != null) + { + asInstance.DrawInstanceControls(rect); + } + else { - GUILayout.BeginHorizontal(new GUILayoutOption[0]); - GUILayout.Label("Tools:", new GUILayoutOption[] { GUILayout.Width(80) }); - UIHelpers.InstantiateButton(m_uObj); - if (m_component && m_component.gameObject is GameObject obj) - { - GUI.skin.label.alignment = TextAnchor.MiddleRight; - GUILayout.Label("GameObject:", new GUILayoutOption[] { GUILayout.Width(135) }); - var charWidth = obj.name.Length * 15; - var maxWidth = rect.width - 350; - var labelWidth = charWidth < maxWidth ? charWidth : maxWidth; - if (GUILayout.Button("" + obj.name + "", new GUILayoutOption[] { GUILayout.Width(labelWidth) })) - { - WindowManager.InspectObject(obj, out bool _); - } - GUI.skin.label.alignment = TextAnchor.UpperLeft; - } GUILayout.EndHorizontal(); } UIStyles.HorizontalLine(Color.grey); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUILayout.Label("Search:", new GUILayoutOption[] { GUILayout.Width(75) }); - m_search = GUIUnstrip.TextField(m_search, new GUILayoutOption[0]); + m_search = GUIUnstrip.TextField(m_search, new GUILayoutOption[0]); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUILayout.Label("Filter:", new GUILayoutOption[] { GUILayout.Width(75) }); - FilterToggle(MemberTypes.All, "All"); - FilterToggle(MemberTypes.Property, "Properties"); - FilterToggle(MemberTypes.Field, "Fields"); - FilterToggle(MemberTypes.Method, "Methods"); + FilterTypeToggle(MemberTypes.All, "All"); + FilterTypeToggle(MemberTypes.Property, "Properties"); + FilterTypeToggle(MemberTypes.Field, "Fields"); + FilterTypeToggle(MemberTypes.Method, "Methods"); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + if (this is InstanceInspector) + { + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + GUILayout.Label("Scope:", new GUILayoutOption[] { GUILayout.Width(75) }); + FilterScopeToggle(MemberScopes.Both, "Both"); + FilterScopeToggle(MemberScopes.Instance, "Instance"); + FilterScopeToggle(MemberScopes.Static, "Static"); + GUILayout.EndHorizontal(); + } + + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUILayout.Label("Values:", new GUILayoutOption[] { GUILayout.Width(75) }); if (GUILayout.Button("Update", new GUILayoutOption[] { GUILayout.Width(100) })) { @@ -303,7 +313,7 @@ namespace Explorer Pages.ItemCount = m_cachedMembersFiltered.Length; // prev/next page buttons - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); Pages.DrawLimitInputArea(); @@ -331,7 +341,7 @@ namespace Explorer UIStyles.HorizontalLine(Color.grey); - GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); + GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null); var members = this.m_cachedMembersFiltered; int start = Pages.CalculateOffsetIndex(); @@ -340,12 +350,12 @@ namespace Explorer { var holder = members[j]; - GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) }); - try - { + GUIUnstrip.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) }); + try + { holder.Draw(rect, 180f); - } - catch + } + catch { GUILayout.EndHorizontal(); continue; @@ -379,9 +389,9 @@ namespace Explorer } } - private void FilterToggle(MemberTypes mode, string label) + private void FilterTypeToggle(MemberTypes mode, string label) { - if (m_filter == mode) + if (m_typeFilter == mode) { GUI.color = Color.green; } @@ -391,7 +401,26 @@ namespace Explorer } if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) })) { - m_filter = mode; + m_typeFilter = mode; + Pages.PageOffset = 0; + scroll = Vector2.zero; + } + GUI.color = Color.white; + } + + private void FilterScopeToggle(MemberScopes mode, string label) + { + if (m_scopeFilter == mode) + { + GUI.color = Color.green; + } + else + { + GUI.color = Color.white; + } + if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) })) + { + m_scopeFilter = mode; Pages.PageOffset = 0; scroll = Vector2.zero; } diff --git a/src/UI/InteractiveValue/InteractiveValue.cs b/src/UI/InteractiveValue/InteractiveValue.cs new file mode 100644 index 0000000..ad9e2be --- /dev/null +++ b/src/UI/InteractiveValue/InteractiveValue.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; +using Explorer.UI.Shared; +using Explorer.CacheObject; + +namespace Explorer.UI +{ + public class InteractiveValue + { + public const float MAX_LABEL_WIDTH = 400f; + public const string EVALUATE_LABEL = "Evaluate"; + + public CacheObjectBase OwnerCacheObject; + + public object Value { get; set; } + public Type ValueType; + + public string ButtonLabel => m_btnLabel ?? GetButtonLabel(); + private string m_btnLabel; + + public MethodInfo ToStringMethod => m_toStringMethod ?? GetToStringMethod(); + private MethodInfo m_toStringMethod; + + public virtual void Init() + { + UpdateValue(); + } + + public virtual void UpdateValue() + { + GetButtonLabel(); + } + + public float CalcWhitespace(Rect window) + { + if (!(this is IExpandHeight)) return 0f; + + float whitespace = (this as IExpandHeight).WhiteSpace; + if (whitespace > 0) + { + ClampLabelWidth(window, ref whitespace); + } + + return whitespace; + } + + public static void ClampLabelWidth(Rect window, ref float labelWidth) + { + float min = window.width * 0.37f; + if (min > MAX_LABEL_WIDTH) min = MAX_LABEL_WIDTH; + + labelWidth = Mathf.Clamp(labelWidth, min, MAX_LABEL_WIDTH); + } + + public void Draw(Rect window, float labelWidth = 215f) + { + if (labelWidth > 0) + { + ClampLabelWidth(window, ref labelWidth); + } + + var cacheMember = OwnerCacheObject as CacheMember; + + if (cacheMember != null && cacheMember.MemInfo != null) + { + GUILayout.Label(cacheMember.RichTextName, new GUILayoutOption[] { GUILayout.Width(labelWidth) }); + } + else + { + GUIUnstrip.Space(labelWidth); + } + + var cacheMethod = OwnerCacheObject as CacheMethod; + + if (cacheMember != null && cacheMember.HasParameters) + { + GUIUnstrip.BeginVertical(new GUILayoutOption[] { GUILayout.ExpandHeight(true) } ); + + if (cacheMember.m_isEvaluating) + { + if (cacheMethod != null && cacheMethod.GenericArgs.Length > 0) + { + cacheMethod.DrawGenericArgsInput(); + } + + if (cacheMember.m_arguments.Length > 0) + { + cacheMember.DrawArgsInput(); + } + + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) })) + { + if (cacheMethod != null) + cacheMethod.Evaluate(); + else + cacheMember.UpdateValue(); + } + if (GUILayout.Button("Cancel", new GUILayoutOption[] { GUILayout.Width(70) })) + { + cacheMember.m_isEvaluating = false; + } + GUILayout.EndHorizontal(); + } + else + { + var lbl = $"Evaluate ("; + int len = cacheMember.m_arguments.Length; + if (cacheMethod != null) len += cacheMethod.GenericArgs.Length; + lbl += len + " params)"; + + if (GUILayout.Button(lbl, new GUILayoutOption[] { GUILayout.Width(150) })) + { + cacheMember.m_isEvaluating = true; + } + } + + GUILayout.EndVertical(); + + GUILayout.EndHorizontal(); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.Space(labelWidth); + } + else if (cacheMethod != null) + { + if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) })) + { + cacheMethod.Evaluate(); + } + + GUILayout.EndHorizontal(); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.Space(labelWidth); + } + + string typeName = $"{ValueType.FullName}"; + + if (cacheMember != null && !string.IsNullOrEmpty(cacheMember.ReflectionException)) + { + GUILayout.Label("Reflection failed! (" + cacheMember.ReflectionException + ")", new GUILayoutOption[0]); + } + else if (cacheMember != null && (cacheMember.HasParameters || cacheMember is CacheMethod) && !cacheMember.m_evaluated) + { + GUILayout.Label($"Not yet evaluated ({typeName})", new GUILayoutOption[0]); + } + else if (Value == null && !(cacheMember is CacheMethod)) + { + GUILayout.Label($"null ({typeName})", new GUILayoutOption[0]); + } + else + { + float _width = window.width - labelWidth - 90; + if (OwnerCacheObject is CacheMethod cm) + { + cm.DrawValue(window, _width); + } + else + { + DrawValue(window, _width); + } + } + } + + public virtual void DrawValue(Rect window, float width) + { + GUI.skin.button.alignment = TextAnchor.MiddleLeft; + if (GUILayout.Button(ButtonLabel, new GUILayoutOption[] { GUILayout.Width(width - 15) })) + { + if (OwnerCacheObject.IsStaticClassSearchResult) + { + WindowManager.InspectStaticReflection(Value as Type); + } + else + { + WindowManager.InspectObject(Value, out bool _); + } + } + GUI.skin.button.alignment = TextAnchor.MiddleCenter; + } + + private MethodInfo GetToStringMethod() + { + try + { + m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0]) + ?? typeof(object).GetMethod("ToString", new Type[0]); + + // test invoke + m_toStringMethod.Invoke(Value, null); + } + catch + { + m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]); + } + return m_toStringMethod; + } + + private string GetButtonLabel() + { + if (Value == null) return null; + + string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString(); + + var classColor = ValueType.IsAbstract && ValueType.IsSealed + ? Syntax.Class_Static + : Syntax.Class_Instance; + + string typeLabel = $"{ValueType.FullName}"; + + if (Value is UnityEngine.Object) + { + label = label.Replace($"({ValueType.FullName})", $"({typeLabel})"); + } + else + { + if (!label.Contains(ValueType.FullName)) + { + label += $" ({typeLabel})"; + } + else + { + label = label.Replace(ValueType.FullName, typeLabel); + } + } + + return m_btnLabel = label; + } + } +} diff --git a/src/CachedObjects/Object/CacheDictionary.cs b/src/UI/InteractiveValue/Object/InteractiveDictionary.cs similarity index 89% rename from src/CachedObjects/Object/CacheDictionary.cs rename to src/UI/InteractiveValue/Object/InteractiveDictionary.cs index a84ce68..850a211 100644 --- a/src/CachedObjects/Object/CacheDictionary.cs +++ b/src/UI/InteractiveValue/Object/InteractiveDictionary.cs @@ -1,15 +1,16 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Threading; using UnityEngine; #if CPP using UnhollowerBaseLib; #endif +using Explorer.UI.Shared; +using Explorer.CacheObject; -namespace Explorer +namespace Explorer.UI { - public class CacheDictionary : CacheObjectBase, IExpandHeight + public class InteractiveDictionary : InteractiveValue, IExpandHeight { public bool IsExpanded { get; set; } public float WhiteSpace { get; set; } = 215f; @@ -54,11 +55,11 @@ namespace Explorer // note: "ValueType" is the Dictionary itself, TypeOfValues is the 'Dictionary.Values' type. // get keys and values - var keys = ValueType.GetProperty("Keys") .GetValue(Value, null); + var keys = ValueType.GetProperty("Keys").GetValue(Value, null); var values = ValueType.GetProperty("Values").GetValue(Value, null); // create lists to hold them - var keyList = new List(); + var keyList = new List(); var valueList = new List(); // store entries with reflection @@ -98,8 +99,9 @@ namespace Explorer { if (ValueType.IsGenericType) { - m_keysType = ValueType.GetGenericArguments()[0]; - m_valuesType = ValueType.GetGenericArguments()[1]; + var generics = ValueType.GetGenericArguments(); + m_keysType = generics[0]; + m_valuesType = generics[1]; } else { @@ -114,7 +116,10 @@ namespace Explorer // first make sure we won't run into a TypeInitializationException. if (!EnsureDictionaryIsSupported()) { - ReflectionException = "Dictionary Type not supported with Reflection!"; + if (OwnerCacheObject is CacheMember cacheMember) + { + cacheMember.ReflectionException = "Dictionary Type not supported with Reflection!"; + } return; } @@ -175,9 +180,9 @@ namespace Explorer return Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(ptr)) is Il2CppSystem.Type; } } - catch - { - return false; + catch + { + return false; } #else return false; @@ -232,7 +237,7 @@ namespace Explorer if (count > Pages.ItemsPerPage) { GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); @@ -262,7 +267,7 @@ namespace Explorer //collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); //GUIUnstrip.Space(whitespace); @@ -278,13 +283,13 @@ namespace Explorer GUI.skin.label.alignment = TextAnchor.MiddleLeft; GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) }); if (key != null) - key.DrawValue(window, (window.width / 2) - 80f); + key.IValue.DrawValue(window, (window.width / 2) - 80f); else GUILayout.Label("null", new GUILayoutOption[0]); GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) }); - if (Value != null) - val.DrawValue(window, (window.width / 2) - 80f); + if (val != null) + val.IValue.DrawValue(window, (window.width / 2) - 80f); else GUILayout.Label("null", new GUILayoutOption[0]); } diff --git a/src/CachedObjects/Object/CacheList.cs b/src/UI/InteractiveValue/Object/InteractiveEnumerable.cs similarity index 86% rename from src/CachedObjects/Object/CacheList.cs rename to src/UI/InteractiveValue/Object/InteractiveEnumerable.cs index be4e11c..e863390 100644 --- a/src/CachedObjects/Object/CacheList.cs +++ b/src/UI/InteractiveValue/Object/InteractiveEnumerable.cs @@ -4,20 +4,22 @@ using System.Collections.Generic; using System.Threading; using System.Reflection; using UnityEngine; +using Explorer.UI.Shared; +using Explorer.CacheObject; -namespace Explorer +namespace Explorer.UI { - public class CacheList : CacheObjectBase, IExpandHeight + public class InteractiveEnumerable : InteractiveValue, IExpandHeight { public bool IsExpanded { get; set; } public float WhiteSpace { get; set; } = 215f; public PageHelper Pages = new PageHelper(); - private CacheObjectBase[] m_cachedEntries = new CacheObjectBase[0]; + private CacheEnumerated[] m_cachedEntries = new CacheEnumerated[0]; // Type of Entries in the Array - public Type EntryType + public Type EntryType { get => GetEntryType(); set => m_entryType = value; @@ -173,16 +175,16 @@ namespace Explorer { if (m_entryType == null) { - if (this.MemInfo != null) + if (OwnerCacheObject is CacheMember cacheMember && cacheMember.MemInfo != null) { Type memberType = null; - switch (this.MemInfo.MemberType) + switch (cacheMember.MemInfo.MemberType) { case MemberTypes.Field: - memberType = (MemInfo as FieldInfo).FieldType; + memberType = (cacheMember.MemInfo as FieldInfo).FieldType; break; case MemberTypes.Property: - memberType = (MemInfo as PropertyInfo).PropertyType; + memberType = (cacheMember.MemInfo as PropertyInfo).PropertyType; break; } @@ -201,7 +203,7 @@ namespace Explorer } } - // IList probably won't be able to get any EntryType. + // use System.Object for non-generic. if (m_entryType == null) { m_entryType = typeof(object); @@ -230,7 +232,8 @@ namespace Explorer return; } - var list = new List(); + var list = new List(); + int index = 0; while (enumerator.MoveNext()) { var obj = enumerator.Current; @@ -252,19 +255,25 @@ namespace Explorer } #endif - if (CacheFactory.GetCacheObject(obj, t) is CacheObjectBase cached) - { - list.Add(cached); - } - else - { - list.Add(null); - } + var cached = new CacheEnumerated() { Index = index, RefIList = Value as IList, ParentEnumeration = this }; + cached.Init(obj, EntryType); + list.Add(cached); + + //if (CacheFactory.GetCacheObject(obj, t) is CacheObjectBase cached) + //{ + // list.Add(cached); + //} + //else + //{ + // list.Add(null); + //} } else { list.Add(null); } + + index++; } m_cachedEntries = list.ToArray(); @@ -318,10 +327,10 @@ namespace Explorer if (count > Pages.ItemsPerPage) { GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); - + Pages.CurrentPageLabel(); // prev/next page buttons @@ -347,11 +356,11 @@ namespace Explorer //collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); - if (entry == null || entry.Value == null) + if (entry == null || entry.IValue == null) { GUILayout.Label($"[{i}] (null)", new GUILayoutOption[0]); } @@ -361,9 +370,9 @@ namespace Explorer GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) }); GUI.skin.label.alignment = TextAnchor.MiddleLeft; - entry.DrawValue(window, window.width - (whitespace + 85)); + entry.IValue.DrawValue(window, window.width - (whitespace + 85)); } - + } GUI.skin.label.alignment = TextAnchor.UpperLeft; diff --git a/src/UI/InteractiveValue/Object/InteractiveGameObject.cs b/src/UI/InteractiveValue/Object/InteractiveGameObject.cs new file mode 100644 index 0000000..851468d --- /dev/null +++ b/src/UI/InteractiveValue/Object/InteractiveGameObject.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using Explorer.UI.Shared; +using Explorer.CacheObject; + +namespace Explorer.UI +{ + public class InteractiveGameObject : InteractiveValue + { + + + public override void DrawValue(Rect window, float width) + { + Buttons.GameObjectButton(Value, null, false, width); + } + + public override void UpdateValue() + { + base.UpdateValue(); + } + } +} diff --git a/src/CachedObjects/Struct/CacheColor.cs b/src/UI/InteractiveValue/Struct/InteractiveColor.cs similarity index 82% rename from src/CachedObjects/Struct/CacheColor.cs rename to src/UI/InteractiveValue/Struct/InteractiveColor.cs index 2ce1191..5de25b1 100644 --- a/src/CachedObjects/Struct/CacheColor.cs +++ b/src/UI/InteractiveValue/Struct/InteractiveColor.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; +using Explorer.UI.Shared; +using Explorer.CacheObject; -namespace Explorer +namespace Explorer.UI { - public class CacheColor : CacheObjectBase, IExpandHeight + public class InteractiveColor : InteractiveValue, IExpandHeight { private string r = "0"; private string g = "0"; @@ -32,7 +34,7 @@ namespace Explorer public override void DrawValue(Rect window, float width) { - if (CanWrite) + if (OwnerCacheObject.CanWrite) { if (!IsExpanded) { @@ -55,38 +57,38 @@ namespace Explorer GUILayout.Label($"Color: {((Color)Value).ToString()}", new GUILayoutOption[0]); //GUI.color = Color.white; - if (CanWrite && IsExpanded) + if (OwnerCacheObject.CanWrite && IsExpanded) { GUILayout.EndHorizontal(); var whitespace = CalcWhitespace(window); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("R:", new GUILayoutOption[] { GUILayout.Width(30) }); r = GUIUnstrip.TextField(r, new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("G:", new GUILayoutOption[] { GUILayout.Width(30) }); g = GUIUnstrip.TextField(g, new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("B:", new GUILayoutOption[] { GUILayout.Width(30) }); b = GUIUnstrip.TextField(b, new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("A:", new GUILayoutOption[] { GUILayout.Width(30) }); a = GUIUnstrip.TextField(a, new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.EndHorizontal(); // draw set value button - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); if (GUILayout.Button("Apply", new GUILayoutOption[] { GUILayout.Width(155) })) { @@ -94,7 +96,7 @@ namespace Explorer } GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); } } @@ -106,7 +108,7 @@ namespace Explorer && float.TryParse(a, out float fA)) { Value = new Color(fR, fG, fB, fA); - SetValue(); + OwnerCacheObject.SetValue(); } } } diff --git a/src/CachedObjects/Struct/CacheEnum.cs b/src/UI/InteractiveValue/Struct/InteractiveEnum.cs similarity index 78% rename from src/CachedObjects/Struct/CacheEnum.cs rename to src/UI/InteractiveValue/Struct/InteractiveEnum.cs index 184c733..9899bdf 100644 --- a/src/CachedObjects/Struct/CacheEnum.cs +++ b/src/UI/InteractiveValue/Struct/InteractiveEnum.cs @@ -4,10 +4,12 @@ using System.Linq; using System.Text; using System.Reflection; using UnityEngine; +using Explorer.UI.Shared; +using Explorer.CacheObject; -namespace Explorer +namespace Explorer.UI { - public class CacheEnum : CacheObjectBase + public class InteractiveEnum : InteractiveValue { internal static Dictionary EnumNamesInternalCache = new Dictionary(); @@ -27,7 +29,10 @@ namespace Explorer } else { - ReflectionException = "Unknown, could not get Enum names."; + if (OwnerCacheObject is CacheMember cacheMember) + { + cacheMember.ReflectionException = "Unknown, could not get Enum names."; + } } } @@ -54,21 +59,21 @@ namespace Explorer public override void DrawValue(Rect window, float width) { - if (CanWrite) + if (OwnerCacheObject.CanWrite) { if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) })) { SetEnum(-1); - SetValue(); + OwnerCacheObject.SetValue(); } if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) })) { SetEnum(1); - SetValue(); + OwnerCacheObject.SetValue(); } } - GUILayout.Label(Value.ToString() + $" ({ValueType})", new GUILayoutOption[0]); + GUILayout.Label(Value.ToString() + $" ({ValueType})", new GUILayoutOption[0]); } public void SetEnum(int change) diff --git a/src/CachedObjects/Struct/CacheEnumFlags.cs b/src/UI/InteractiveValue/Struct/InteractiveFlags.cs similarity index 86% rename from src/CachedObjects/Struct/CacheEnumFlags.cs rename to src/UI/InteractiveValue/Struct/InteractiveFlags.cs index de1316d..e6a1141 100644 --- a/src/CachedObjects/Struct/CacheEnumFlags.cs +++ b/src/UI/InteractiveValue/Struct/InteractiveFlags.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; +using Explorer.UI.Shared; +using Explorer.CacheObject; -namespace Explorer +namespace Explorer.UI { - public class CacheEnumFlags : CacheEnum, IExpandHeight + public class InteractiveFlags : InteractiveEnum, IExpandHeight { public bool[] m_enabledFlags = new bool[0]; @@ -45,7 +47,7 @@ namespace Explorer public override void DrawValue(Rect window, float width) { - if (CanWrite) + if (OwnerCacheObject.CanWrite) { if (!IsExpanded) { @@ -73,7 +75,7 @@ namespace Explorer for (int i = 0; i < EnumNames.Length; i++) { - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); m_enabledFlags[i] = GUILayout.Toggle(m_enabledFlags[i], EnumNames[i], new GUILayoutOption[0]); @@ -81,7 +83,7 @@ namespace Explorer GUILayout.EndHorizontal(); } - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); if (GUILayout.Button("Apply", new GUILayoutOption[] { GUILayout.Width(155) })) { @@ -89,7 +91,7 @@ namespace Explorer } GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); } } @@ -105,7 +107,7 @@ namespace Explorer } } Value = Enum.Parse(ValueType, val); - SetValue(); + OwnerCacheObject.SetValue(); } } } diff --git a/src/CachedObjects/Struct/CachePrimitive.cs b/src/UI/InteractiveValue/Struct/InteractivePrimitive.cs similarity index 88% rename from src/CachedObjects/Struct/CachePrimitive.cs rename to src/UI/InteractiveValue/Struct/InteractivePrimitive.cs index 07ec5f9..5c00b05 100644 --- a/src/CachedObjects/Struct/CachePrimitive.cs +++ b/src/UI/InteractiveValue/Struct/InteractivePrimitive.cs @@ -6,10 +6,13 @@ using UnityEngine; #if CPP using UnhollowerRuntimeLib; #endif +using Explorer.UI.Shared; +using Explorer.CacheObject; +using Explorer.Config; -namespace Explorer +namespace Explorer.UI { - public class CachePrimitive : CacheObjectBase + public class InteractivePrimitive : InteractiveValue { private string m_valueToString; private bool m_isBool; @@ -61,7 +64,7 @@ namespace Explorer { m_valueToString = Value?.ToString(); - if (m_canBitwiseOperate) + if (m_canBitwiseOperate && Value != null) { var _int = (int)Value; m_binaryInput = Convert.ToString(_int, toBase: 2); @@ -75,13 +78,13 @@ namespace Explorer var b = (bool)Value; var label = $"{b}"; - if (CanWrite) + if (OwnerCacheObject.CanWrite) { b = GUILayout.Toggle(b, label, new GUILayoutOption[0]); if (b != (bool)Value) { Value = b; - SetValue(); + OwnerCacheObject.SetValue(); } } else @@ -94,14 +97,14 @@ namespace Explorer // all other non-bool values use TextField - GUILayout.BeginVertical(new GUILayoutOption[0]); + GUIUnstrip.BeginVertical(new GUILayoutOption[0]); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUILayout.Label("" + ValueType.Name + "", new GUILayoutOption[] { GUILayout.Width(50) }); m_valueToString = GUIUnstrip.TextArea(m_valueToString, new GUILayoutOption[] { GUILayout.ExpandWidth(true) }); - if (CanWrite) + if (OwnerCacheObject.CanWrite) { if (GUILayout.Button("Apply", new GUILayoutOption[] { GUILayout.Width(60) })) { @@ -109,7 +112,7 @@ namespace Explorer } } - if (m_canBitwiseOperate) + if (ModConfig.Instance.Bitwise_Support && m_canBitwiseOperate) { m_inBitwiseMode = GUILayout.Toggle(m_inBitwiseMode, "Bitwise?", new GUILayoutOption[0]); } @@ -118,7 +121,7 @@ namespace Explorer GUILayout.EndHorizontal(); - if (m_inBitwiseMode) + if (ModConfig.Instance.Bitwise_Support && m_inBitwiseMode) { DrawBitwise(); } @@ -128,9 +131,9 @@ namespace Explorer private void DrawBitwise() { - if (CanWrite) + if (OwnerCacheObject.CanWrite) { - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUI.skin.label.alignment = TextAnchor.MiddleRight; GUILayout.Label("RHS:", new GUILayoutOption[] { GUILayout.Width(35) }); @@ -191,10 +194,10 @@ namespace Explorer GUILayout.EndHorizontal(); } - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUILayout.Label($"Binary:", new GUILayoutOption[] { GUILayout.Width(60) }); m_binaryInput = GUIUnstrip.TextField(m_binaryInput, new GUILayoutOption[0]); - if (CanWrite) + if (OwnerCacheObject.CanWrite) { if (GUILayout.Button("Apply", new GUILayoutOption[0])) { @@ -206,12 +209,6 @@ namespace Explorer public void SetValueFromInput() { - if (MemInfo == null) - { - ExplorerCore.Log("Trying to SetValue but the MemberInfo is null!"); - return; - } - if (m_isString) { Value = m_valueToString; @@ -228,7 +225,7 @@ namespace Explorer } } - SetValue(); + OwnerCacheObject.SetValue(); RefreshToString(); } @@ -239,7 +236,7 @@ namespace Explorer var method = typeof(Convert).GetMethod($"To{ValueType.Name}", new Type[] { typeof(string), typeof(int) }); Value = method.Invoke(null, new object[] { m_binaryInput, 2 }); - SetValue(); + OwnerCacheObject.SetValue(); RefreshToString(); } catch (Exception e) diff --git a/src/CachedObjects/Struct/CacheQuaternion.cs b/src/UI/InteractiveValue/Struct/InteractiveQuaternion.cs similarity index 82% rename from src/CachedObjects/Struct/CacheQuaternion.cs rename to src/UI/InteractiveValue/Struct/InteractiveQuaternion.cs index 6b02684..cd00cc0 100644 --- a/src/CachedObjects/Struct/CacheQuaternion.cs +++ b/src/UI/InteractiveValue/Struct/InteractiveQuaternion.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; +using Explorer.UI.Shared; +using Explorer.CacheObject; -namespace Explorer +namespace Explorer.UI { - public class CacheQuaternion : CacheObjectBase, IExpandHeight + public class InteractiveQuaternion : InteractiveValue, IExpandHeight { private string x = "0"; private string y = "0"; @@ -30,7 +32,7 @@ namespace Explorer public override void DrawValue(Rect window, float width) { - if (CanWrite) + if (OwnerCacheObject.CanWrite) { if (!IsExpanded) { @@ -50,32 +52,32 @@ namespace Explorer GUILayout.Label($"Quaternion: {((Quaternion)Value).eulerAngles.ToString()}", new GUILayoutOption[0]); - if (CanWrite && IsExpanded) + if (OwnerCacheObject.CanWrite && IsExpanded) { GUILayout.EndHorizontal(); var whitespace = CalcWhitespace(window); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) }); x = GUIUnstrip.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) }); y = GUIUnstrip.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(30) }); z = GUIUnstrip.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.EndHorizontal(); // draw set value button - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); if (GUILayout.Button("Apply", new GUILayoutOption[] { GUILayout.Width(155) })) { @@ -83,7 +85,7 @@ namespace Explorer } GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); } } @@ -94,7 +96,7 @@ namespace Explorer && float.TryParse(z, out float fZ)) { Value = Quaternion.Euler(new Vector3(fX, fY, fZ)); - SetValue(); + OwnerCacheObject.SetValue(); } } } diff --git a/src/CachedObjects/Struct/CacheRect.cs b/src/UI/InteractiveValue/Struct/InteractiveRect.cs similarity index 82% rename from src/CachedObjects/Struct/CacheRect.cs rename to src/UI/InteractiveValue/Struct/InteractiveRect.cs index 4a69089..5814f52 100644 --- a/src/CachedObjects/Struct/CacheRect.cs +++ b/src/UI/InteractiveValue/Struct/InteractiveRect.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; +using Explorer.UI.Shared; +using Explorer.CacheObject; -namespace Explorer +namespace Explorer.UI { - public class CacheRect : CacheObjectBase, IExpandHeight + public class InteractiveRect : InteractiveValue, IExpandHeight { private string x = "0"; private string y = "0"; @@ -32,7 +34,7 @@ namespace Explorer public override void DrawValue(Rect window, float width) { - if (CanWrite) + if (OwnerCacheObject.CanWrite) { if (!IsExpanded) { @@ -52,38 +54,38 @@ namespace Explorer GUILayout.Label($"Rect: {((Rect)Value).ToString()}", new GUILayoutOption[0]); - if (CanWrite && IsExpanded) + if (OwnerCacheObject.CanWrite && IsExpanded) { GUILayout.EndHorizontal(); var whitespace = CalcWhitespace(window); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) }); x = GUIUnstrip.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) }); y = GUIUnstrip.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("W:", new GUILayoutOption[] { GUILayout.Width(30) }); w = GUIUnstrip.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("H:", new GUILayoutOption[] { GUILayout.Width(30) }); h = GUIUnstrip.TextField(h, new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.EndHorizontal(); // draw set value button - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); if (GUILayout.Button("Apply", new GUILayoutOption[] { GUILayout.Width(155) })) { @@ -91,7 +93,7 @@ namespace Explorer } GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); } } @@ -103,7 +105,7 @@ namespace Explorer && float.TryParse(h, out float fH)) { Value = new Rect(fX, fY, fW, fH); - SetValue(); + OwnerCacheObject.SetValue(); } } } diff --git a/src/CachedObjects/Struct/CacheVector.cs b/src/UI/InteractiveValue/Struct/InteractiveVector.cs similarity index 81% rename from src/CachedObjects/Struct/CacheVector.cs rename to src/UI/InteractiveValue/Struct/InteractiveVector.cs index 3f857b7..5306610 100644 --- a/src/CachedObjects/Struct/CacheVector.cs +++ b/src/UI/InteractiveValue/Struct/InteractiveVector.cs @@ -5,9 +5,9 @@ using System.Text; using System.Reflection; using UnityEngine; -namespace Explorer +namespace Explorer.UI { - public class CacheVector : CacheObjectBase, IExpandHeight + public class InteractiveVector : InteractiveValue, IExpandHeight { public int VectorSize = 2; @@ -16,7 +16,7 @@ namespace Explorer private string z = "0"; private string w = "0"; - private MethodInfo m_toStringMethod; + //private MethodInfo m_toStringMethod; public bool IsExpanded { get; set; } public float WhiteSpace { get; set; } = 215f; @@ -31,18 +31,20 @@ namespace Explorer if (ValueType == typeof(Vector2)) { VectorSize = 2; - m_toStringMethod = typeof(Vector2).GetMethod("ToString", new Type[0]); + //m_toStringMethod = typeof(Vector2).GetMethod("ToString", new Type[0]); } else if (ValueType == typeof(Vector3)) { VectorSize = 3; - m_toStringMethod = typeof(Vector3).GetMethod("ToString", new Type[0]); + //m_toStringMethod = typeof(Vector3).GetMethod("ToString", new Type[0]); } else { VectorSize = 4; - m_toStringMethod = typeof(Vector4).GetMethod("ToString", new Type[0]); + //m_toStringMethod = typeof(Vector4).GetMethod("ToString", new Type[0]); } + + base.Init(); } public override void UpdateValue() @@ -71,7 +73,7 @@ namespace Explorer public override void DrawValue(Rect window, float width) { - if (CanWrite) + if (OwnerCacheObject.CanWrite) { if (!IsExpanded) { @@ -89,22 +91,22 @@ namespace Explorer } } - GUILayout.Label($"Vector{VectorSize}: {(string)m_toStringMethod.Invoke(Value, new object[0])}", new GUILayoutOption[0]); + GUILayout.Label($"Vector{VectorSize}: {(string)ToStringMethod.Invoke(Value, new object[0])}", new GUILayoutOption[0]); - if (CanWrite && IsExpanded) + if (OwnerCacheObject.CanWrite && IsExpanded) { GUILayout.EndHorizontal(); var whitespace = CalcWhitespace(window); // always draw x and y - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) }); x = GUIUnstrip.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) }); y = GUIUnstrip.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) }); @@ -113,7 +115,7 @@ namespace Explorer if (VectorSize > 2) { // draw z - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(30) }); z = GUIUnstrip.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) }); @@ -122,7 +124,7 @@ namespace Explorer if (VectorSize > 3) { // draw w - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); GUILayout.Label("W:", new GUILayoutOption[] { GUILayout.Width(30) }); w = GUIUnstrip.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) }); @@ -130,7 +132,7 @@ namespace Explorer } // draw set value button - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.Space(whitespace); if (GUILayout.Button("Apply", new GUILayoutOption[] { GUILayout.Width(155) })) { @@ -138,7 +140,7 @@ namespace Explorer } GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); } } @@ -161,7 +163,7 @@ namespace Explorer if (vector != null) { Value = vector; - SetValue(); + OwnerCacheObject.SetValue(); } } } diff --git a/src/Menu/MainMenu/Pages/WindowPage.cs b/src/UI/Main/BaseMainMenuPage.cs similarity index 78% rename from src/Menu/MainMenu/Pages/WindowPage.cs rename to src/UI/Main/BaseMainMenuPage.cs index 5a3a344..00bf59d 100644 --- a/src/Menu/MainMenu/Pages/WindowPage.cs +++ b/src/UI/Main/BaseMainMenuPage.cs @@ -1,8 +1,8 @@ using UnityEngine; -namespace Explorer +namespace Explorer.UI.Main { - public abstract class WindowPage + public abstract class BaseMainMenuPage { public virtual string Name { get; } diff --git a/src/UI/Main/Console/AutoComplete.cs b/src/UI/Main/Console/AutoComplete.cs new file mode 100644 index 0000000..9d2e3b3 --- /dev/null +++ b/src/UI/Main/Console/AutoComplete.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using UnityEngine; + +// Thanks to ManlyMarco for this + +namespace Explorer.UI.Main +{ + public struct AutoComplete + { + public string Full => Prefix + Addition; + + public readonly string Prefix; + public readonly string Addition; + public readonly Contexts Context; + + public Color TextColor => Context == Contexts.Namespace + ? Color.gray + : Color.white; + + public AutoComplete(string addition, string prefix, Contexts type) + { + Addition = addition; + Prefix = prefix; + Context = type; + } + + public enum Contexts + { + Namespace, + Other + } + } + + public static class AutoCompleteHelpers + { + public static HashSet Namespaces => _namespaces ?? GetNamespaces(); + private static HashSet _namespaces; + + private static HashSet GetNamespaces() + { + var set = new HashSet( + AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(GetTypes) + .Where(x => x.IsPublic && !string.IsNullOrEmpty(x.Namespace)) + .Select(x => x.Namespace)); + + return _namespaces = set; + + IEnumerable GetTypes(Assembly asm) => asm.TryGetTypes(); + } + } +} diff --git a/src/UI/Main/Console/ScriptEvaluator.cs b/src/UI/Main/Console/ScriptEvaluator.cs new file mode 100644 index 0000000..eb827a6 --- /dev/null +++ b/src/UI/Main/Console/ScriptEvaluator.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Mono.CSharp; + +// Thanks to ManlyMarco for this + +namespace Explorer.UI.Main +{ + internal class ScriptEvaluator : Evaluator, IDisposable + { + private static readonly HashSet StdLib = new HashSet(StringComparer.InvariantCultureIgnoreCase) + { + "mscorlib", "System.Core", "System", "System.Xml" + }; + + private readonly TextWriter _logger; + + public ScriptEvaluator(TextWriter logger) : base(BuildContext(logger)) + { + _logger = logger; + + ImportAppdomainAssemblies(ReferenceAssembly); + AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad; + } + + public void Dispose() + { + AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad; + _logger.Dispose(); + } + + private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args) + { + string name = args.LoadedAssembly.GetName().Name; + if (StdLib.Contains(name)) + return; + ReferenceAssembly(args.LoadedAssembly); + } + + private static CompilerContext BuildContext(TextWriter tw) + { + var reporter = new StreamReportPrinter(tw); + + var settings = new CompilerSettings + { + Version = LanguageVersion.Experimental, + GenerateDebugInfo = false, + StdLib = true, + Target = Target.Library, + WarningLevel = 0, + EnhancedWarnings = false + }; + + return new CompilerContext(settings, reporter); + } + + private static void ImportAppdomainAssemblies(Action import) + { + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + string name = assembly.GetName().Name; + if (StdLib.Contains(name)) + continue; + import(assembly); + } + } + } +} diff --git a/src/UI/Main/Console/ScriptInteraction.cs b/src/UI/Main/Console/ScriptInteraction.cs new file mode 100644 index 0000000..5515669 --- /dev/null +++ b/src/UI/Main/Console/ScriptInteraction.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Explorer.UI.Inspectors; +using Mono.CSharp; +using UnityEngine; + +namespace Explorer.UI.Main +{ + public class ScriptInteraction : InteractiveBase + { + public static void Log(object message) + { + ExplorerCore.Log(message); + } + + public static object CurrentTarget() + { + if (!WindowManager.TabView) + { + ExplorerCore.Log("CurrentTarget() is only a valid method when in Tab View mode!"); + return null; + } + + return WindowManager.Windows.ElementAt(TabViewWindow.Instance.TargetTabID).Target; + } + + public static object[] AllTargets() + { + var list = new List(); + foreach (var window in WindowManager.Windows) + { + if (window.Target != null) + { + list.Add(window.Target); + } + } + return list.ToArray(); + } + + public static void Inspect(object obj) + { + WindowManager.InspectObject(obj, out bool _); + } + + public static void Inspect(Type type) + { + WindowManager.InspectStaticReflection(type); + } + + public static void Help() + { + ExplorerCore.Log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + ExplorerCore.Log(" C# Console Help "); + ExplorerCore.Log(""); + ExplorerCore.Log("The following helper methods are available:"); + ExplorerCore.Log(""); + ExplorerCore.Log("void Log(object message)"); + ExplorerCore.Log(" prints a message to the console window and debug log"); + ExplorerCore.Log(" usage: Log(\"hello world\");"); + ExplorerCore.Log(""); + ExplorerCore.Log("object CurrentTarget()"); + ExplorerCore.Log(" returns the target object of the current tab (in tab view mode only)"); + ExplorerCore.Log(" usage: var target = CurrentTarget();"); + ExplorerCore.Log(""); + ExplorerCore.Log("object[] AllTargets()"); + ExplorerCore.Log(" returns an object[] array containing all currently inspected objects"); + ExplorerCore.Log(" usage: var targets = AllTargets();"); + ExplorerCore.Log(""); + ExplorerCore.Log("void Inspect(object obj)"); + ExplorerCore.Log(" inspects the provided object in a new window."); + ExplorerCore.Log(" usage: Inspect(Camera.main);"); + ExplorerCore.Log(""); + ExplorerCore.Log("void Inspect(Type type)"); + ExplorerCore.Log(" attempts to inspect the provided type with static-only reflection."); + ExplorerCore.Log(" usage: Inspect(typeof(Camera));"); + } + } +} \ No newline at end of file diff --git a/src/UI/Main/ConsolePage.cs b/src/UI/Main/ConsolePage.cs new file mode 100644 index 0000000..8d03a38 --- /dev/null +++ b/src/UI/Main/ConsolePage.cs @@ -0,0 +1,366 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using Mono.CSharp; +using System.Reflection; +using System.IO; +#if CPP +using UnhollowerRuntimeLib; +#endif + +namespace Explorer.UI.Main +{ + public class ConsolePage : BaseMainMenuPage + { + public static ConsolePage Instance { get; private set; } + + public override string Name { get => "C# Console"; } + + private ScriptEvaluator m_evaluator; + + public const string INPUT_CONTROL_NAME = "consoleInput"; + private string m_input = ""; + private string m_prevInput = ""; + private string m_usingInput = ""; + + public static List AutoCompletes = new List(); + public static List UsingDirectives; + + private Vector2 inputAreaScroll; + private Vector2 autocompleteScroll; + public static TextEditor textEditor; + private bool shouldRefocus; + + public static GUIStyle AutocompleteStyle => autocompleteStyle ?? GetCompletionStyle(); + private static GUIStyle autocompleteStyle; + + public static readonly string[] DefaultUsing = new string[] + { + "System", + "UnityEngine", + "System.Linq", + "System.Collections", + "System.Collections.Generic", + "System.Reflection" + }; + + public override void Init() + { + Instance = this; + + try + { + m_input = @"// For a list of helper methods, execute the 'Help();' method. +// Enable the Console Window with your Mod Loader to see log output. + +Help();"; + ResetConsole(); + + foreach (var use in DefaultUsing) + { + AddUsing(use); + } + } + catch (Exception e) + { + ExplorerCore.LogWarning($"Error setting up console!\r\nMessage: {e.Message}"); + MainMenu.SetCurrentPage(0); + MainMenu.Pages.Remove(this); + } + } + + public override void Update() { } + + public string AsmToUsing(string asm, bool richtext = false) + { + if (richtext) + { + return $"using {asm};"; + } + return $"using {asm};"; + } + + public void AddUsing(string asm) + { + if (!UsingDirectives.Contains(asm)) + { + UsingDirectives.Add(asm); + Evaluate(AsmToUsing(asm), true); + } + } + + public object Evaluate(string str, bool suppressWarning = false) + { + object ret = VoidType.Value; + + m_evaluator.Compile(str, out var compiled); + + try + { + if (compiled == null) + { + throw new Exception("Mono.Csharp Service was unable to compile the code provided."); + } + + compiled.Invoke(ref ret); + } + catch (Exception e) + { + if (!suppressWarning) + { + ExplorerCore.LogWarning(e.GetType() + ", " + e.Message); + } + } + + return ret; + } + + public void ResetConsole() + { + if (m_evaluator != null) + { + m_evaluator.Dispose(); + } + + m_evaluator = new ScriptEvaluator(new StringWriter(new StringBuilder())) { InteractiveBaseClass = typeof(ScriptInteraction) }; + + UsingDirectives = new List(); + } + + + public override void DrawWindow() + { + GUILayout.Label("C# Console", new GUILayoutOption[0]); + + GUI.skin.label.alignment = TextAnchor.UpperLeft; + + // SCRIPT INPUT + + GUILayout.Label("Enter code here as though it is a method body:", new GUILayoutOption[0]); + + inputAreaScroll = GUIUnstrip.BeginScrollView( + inputAreaScroll, + new GUILayoutOption[] { GUILayout.Height(250), GUILayout.ExpandHeight(true) } + ); + + GUI.SetNextControlName(INPUT_CONTROL_NAME); + m_input = GUIUnstrip.TextArea(m_input, new GUILayoutOption[] { GUILayout.ExpandHeight(true) }); + + GUIUnstrip.EndScrollView(); + + // EXECUTE BUTTON + + if (GUILayout.Button("Execute", new GUILayoutOption[0])) + { + try + { + m_input = m_input.Trim(); + + if (!string.IsNullOrEmpty(m_input)) + { + Evaluate(m_input); + + //var result = Evaluate(m_input); + + //if (result != null && !Equals(result, VoidType.Value)) + //{ + // ExplorerCore.Log("[Console Output]\r\n" + result.ToString()); + //} + } + } + catch (Exception e) + { + ExplorerCore.LogError("Exception compiling!\r\nMessage: " + e.Message + "\r\nStack: " + e.StackTrace); + } + } + + // SUGGESTIONS + if (AutoCompletes.Count > 0) + { + autocompleteScroll = GUIUnstrip.BeginScrollView(autocompleteScroll, new GUILayoutOption[] { GUILayout.Height(150) }); + + var origSkin = GUI.skin.button; + GUI.skin.button = AutocompleteStyle; + + foreach (var autocomplete in AutoCompletes) + { + AutocompleteStyle.normal.textColor = autocomplete.TextColor; + if (GUILayout.Button(autocomplete.Full, new GUILayoutOption[] { GUILayout.Width(MainMenu.MainRect.width - 50) })) + { + UseAutocomplete(autocomplete.Addition); + break; + } + } + + GUI.skin.button = origSkin; + + GUIUnstrip.EndScrollView(); + } + + if (shouldRefocus) + { + GUI.FocusControl(INPUT_CONTROL_NAME); + shouldRefocus = false; + } + + // USING DIRECTIVES + + GUILayout.Label("Using directives:", new GUILayoutOption[0]); + + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(105) }); + m_usingInput = GUIUnstrip.TextField(m_usingInput, new GUILayoutOption[] { GUILayout.Width(150) }); + if (GUILayout.Button("Add", new GUILayoutOption[] { GUILayout.Width(120) })) + { + AddUsing(m_usingInput); + } + if (GUILayout.Button("Clear All", new GUILayoutOption[] { GUILayout.Width(120) })) + { + ResetConsole(); + } + GUILayout.EndHorizontal(); + + foreach (var asm in UsingDirectives) + { + GUILayout.Label(AsmToUsing(asm, true), new GUILayoutOption[0]); + } + + CheckAutocomplete(); + } + + private void CheckAutocomplete() + { + // Temporary disabling this check in BepInEx Il2Cpp. +#if BIE +#if CPP +#else + if (GUI.GetNameOfFocusedControl() != INPUT_CONTROL_NAME) + return; +#endif +#else + if (GUI.GetNameOfFocusedControl() != INPUT_CONTROL_NAME) + return; +#endif + +#if CPP + textEditor = GUIUtility.GetStateObject(Il2CppType.Of(), GUIUtility.keyboardControl).TryCast(); +#else + textEditor = (TextEditor)GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl); +#endif + + var input = m_input; + + if (!string.IsNullOrEmpty(input)) + { + try + { + var splitChars = new[] { ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&' }; + + // Credit ManlyMarco + // Separate input into parts, grab only the part with cursor in it + var cursorIndex = textEditor.cursorIndex; + var start = cursorIndex <= 0 ? 0 : input.LastIndexOfAny(splitChars, cursorIndex - 1) + 1; + var end = cursorIndex <= 0 ? input.Length : input.IndexOfAny(splitChars, cursorIndex - 1); + if (end < 0 || end < start) end = input.Length; + input = input.Substring(start, end - start); + } + catch (ArgumentException) { } + + if (!string.IsNullOrEmpty(input) && input != m_prevInput) + { + GetAutocompletes(input); + } + } + else + { + ClearAutocompletes(); + } + + m_prevInput = input; + } + + private void ClearAutocompletes() + { + if (AutoCompletes.Any()) + { + AutoCompletes.Clear(); + shouldRefocus = true; + } + } + + private void UseAutocomplete(string suggestion) + { + int cursorIndex = textEditor.cursorIndex; + m_input = m_input.Insert(cursorIndex, suggestion); + + ClearAutocompletes(); + shouldRefocus = true; + } + + private void GetAutocompletes(string input) + { + try + { + //ExplorerCore.Log("Fetching suggestions for input " + input); + + // Credit ManylMarco + AutoCompletes.Clear(); + var completions = m_evaluator.GetCompletions(input, out string prefix); + if (completions != null) + { + if (prefix == null) + prefix = input; + + AutoCompletes.AddRange(completions + .Where(x => !string.IsNullOrEmpty(x)) + .Select(x => new AutoComplete(x, prefix, AutoComplete.Contexts.Other)) + ); + } + + var trimmed = input.Trim(); + if (trimmed.StartsWith("using")) + trimmed = trimmed.Remove(0, 5).Trim(); + + var namespaces = AutoCompleteHelpers.Namespaces + .Where(x => x.StartsWith(trimmed) && x.Length > trimmed.Length) + .Select(x => new AutoComplete( + x.Substring(trimmed.Length), + x.Substring(0, trimmed.Length), + AutoComplete.Contexts.Namespace)); + + AutoCompletes.AddRange(namespaces); + + shouldRefocus = true; + } + catch (Exception ex) + { + ExplorerCore.Log("C# Console error:\r\n" + ex); + ClearAutocompletes(); + } + } + + // Credit ManlyMarco + private static GUIStyle GetCompletionStyle() + { + return autocompleteStyle = new GUIStyle(GUI.skin.button) + { + border = new RectOffset(0, 0, 0, 0), + margin = new RectOffset(0, 0, 0, 0), + padding = new RectOffset(0, 0, 0, 0), + hover = { background = Texture2D.whiteTexture, textColor = Color.black }, + normal = { background = null }, + focused = { background = Texture2D.whiteTexture, textColor = Color.black }, + active = { background = Texture2D.whiteTexture, textColor = Color.black }, + alignment = TextAnchor.MiddleLeft, + }; + } + + private class VoidType + { + public static readonly VoidType Value = new VoidType(); + private VoidType() { } + } + } +} diff --git a/src/UI/Main/OptionsPage.cs b/src/UI/Main/OptionsPage.cs new file mode 100644 index 0000000..e3e28d2 --- /dev/null +++ b/src/UI/Main/OptionsPage.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using Explorer.UI.Shared; +using Explorer.Config; +using Explorer.CacheObject; + +namespace Explorer.UI.Main +{ + public class OptionsPage : BaseMainMenuPage + { + public override string Name => "Options"; + + public string toggleKeyInputString = ""; + public Vector2 defaultSizeInputVector; + public int defaultPageLimit; + public bool bitwiseSupport; + public bool tabView; + + private CacheObjectBase toggleKeyInput; + private CacheObjectBase defaultSizeInput; + private CacheObjectBase defaultPageLimitInput; + private CacheObjectBase bitwiseSupportInput; + private CacheObjectBase tabViewInput; + + public override void Init() + { + toggleKeyInputString = ModConfig.Instance.Main_Menu_Toggle.ToString(); + toggleKeyInput = CacheFactory.GetCacheObject(typeof(OptionsPage).GetField("toggleKeyInputString"), this); + + defaultSizeInputVector = ModConfig.Instance.Default_Window_Size; + defaultSizeInput = CacheFactory.GetCacheObject(typeof(OptionsPage).GetField("defaultSizeInputVector"), this); + + defaultPageLimit = ModConfig.Instance.Default_Page_Limit; + defaultPageLimitInput = CacheFactory.GetCacheObject(typeof(OptionsPage).GetField("defaultPageLimit"), this); + + bitwiseSupport = ModConfig.Instance.Bitwise_Support; + bitwiseSupportInput = CacheFactory.GetCacheObject(typeof(OptionsPage).GetField("bitwiseSupport"), this); + + tabView = ModConfig.Instance.Tab_View; + tabViewInput = CacheFactory.GetCacheObject(typeof(OptionsPage).GetField("tabView"), this); + } + + public override void Update() { } + + public override void DrawWindow() + { + GUI.skin.label.alignment = TextAnchor.MiddleCenter; + GUILayout.Label("Options", new GUILayoutOption[0]); + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + + GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[0]); + + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + GUILayout.Label($"Menu Toggle Key:", new GUILayoutOption[] { GUILayout.Width(215f) }); + toggleKeyInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f); + GUILayout.EndHorizontal(); + + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + GUILayout.Label($"Default Window Size:", new GUILayoutOption[] { GUILayout.Width(215f) }); + defaultSizeInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f); + GUILayout.EndHorizontal(); + + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + GUILayout.Label($"Default Items per Page:", new GUILayoutOption[] { GUILayout.Width(215f) }); + defaultPageLimitInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f); + GUILayout.EndHorizontal(); + + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + GUILayout.Label($"Enable Bitwise Editing:", new GUILayoutOption[] { GUILayout.Width(215f) }); + bitwiseSupportInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f); + GUILayout.EndHorizontal(); + + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + GUILayout.Label($"Enable Tab View:", new GUILayoutOption[] { GUILayout.Width(215f) }); + tabViewInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f); + GUILayout.EndHorizontal(); + + if (GUILayout.Button("Apply and Save", new GUILayoutOption[0])) + { + ApplyAndSave(); + } + + GUILayout.EndVertical(); + + GUIUnstrip.Space(10f); + + GUI.skin.label.alignment = TextAnchor.MiddleCenter; + GUILayout.Label("Other", new GUILayoutOption[0]); + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + + GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[0]); + + if (GUILayout.Button("Inspect Test Class", new GUILayoutOption[0])) + { + WindowManager.InspectObject(Tests.TestClass.Instance, out bool _); + } + + GUILayout.EndVertical(); + } + + private void ApplyAndSave() + { + if (Enum.Parse(typeof(KeyCode), toggleKeyInputString) is KeyCode key) + { + ModConfig.Instance.Main_Menu_Toggle = key; + } + else + { + ExplorerCore.LogWarning($"Could not parse '{toggleKeyInputString}' to KeyCode!"); + } + + ModConfig.Instance.Default_Window_Size = defaultSizeInputVector; + ModConfig.Instance.Default_Page_Limit = defaultPageLimit; + ModConfig.Instance.Bitwise_Support = bitwiseSupport; + + ModConfig.Instance.Tab_View = tabView; + WindowManager.TabView = tabView; + + ModConfig.SaveSettings(); + } + } +} diff --git a/src/Menu/MainMenu/Pages/ScenePage.cs b/src/UI/Main/ScenePage.cs similarity index 78% rename from src/Menu/MainMenu/Pages/ScenePage.cs rename to src/UI/Main/ScenePage.cs index 025d2f6..f9a0fdf 100644 --- a/src/Menu/MainMenu/Pages/ScenePage.cs +++ b/src/UI/Main/ScenePage.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.SceneManagement; +using Explorer.UI.Shared; +using Explorer.CacheObject; -namespace Explorer +namespace Explorer.UI.Main { - public class ScenePage : WindowPage + public class ScenePage : BaseMainMenuPage { public static ScenePage Instance; @@ -23,12 +25,12 @@ namespace Explorer // gameobject list private Transform m_currentTransform; - private readonly List m_objectList = new List(); + private readonly List m_objectList = new List(); // search bar private bool m_searching = false; private string m_searchInput = ""; - private List m_searchResults = new List(); + private List m_searchResults = new List(); public override void Init() { @@ -80,9 +82,9 @@ namespace Explorer } } - public List SearchSceneObjects(string _search) + public List SearchSceneObjects(string _search) { - var matches = new List(); + var matches = new List(); foreach (var obj in Resources.FindObjectsOfTypeAll(ReflectionHelpers.GameObjectType)) { @@ -93,7 +95,7 @@ namespace Explorer #endif if (go.name.ToLower().Contains(_search.ToLower()) && go.scene.name == m_currentScene) { - matches.Add(new GameObjectCache(go)); + matches.Add(CacheFactory.GetCacheObject(go)); } } @@ -172,7 +174,7 @@ namespace Explorer for (int i = offset; i < offset + Pages.ItemsPerPage && i < Pages.ItemCount; i++) { var child = allTransforms[i]; - m_objectList.Add(new GameObjectCache(child.gameObject)); + m_objectList.Add(CacheFactory.GetCacheObject(child)); } } @@ -199,8 +201,8 @@ namespace Explorer } catch (Exception e) { - ExplorerCore.Log("Exception getting root scene objects (manual): " - + e.GetType() + ", " + e.Message + "\r\n" + ExplorerCore.Log("Exception getting root scene objects (manual): " + + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace); return new Transform[0]; } @@ -214,7 +216,7 @@ namespace Explorer { DrawHeaderArea(); - GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); + GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null); DrawPageButtons(); @@ -240,7 +242,7 @@ namespace Explorer private void DrawHeaderArea() { - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); // Current Scene label GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) }); @@ -250,7 +252,7 @@ namespace Explorer GUILayout.EndHorizontal(); // ----- GameObject Search ----- - GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null); + GUIUnstrip.BeginHorizontal(GUIContent.none, GUI.skin.box, null); GUILayout.Label("Search Scene:", new GUILayoutOption[] { GUILayout.Width(100) }); m_searchInput = GUIUnstrip.TextField(m_searchInput, new GUILayoutOption[0]); @@ -305,7 +307,7 @@ namespace Explorer private void DrawPageButtons() { - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); Pages.DrawLimitInputArea(); @@ -336,7 +338,7 @@ namespace Explorer { if (m_currentTransform != null) { - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) })) { TraverseUp(); @@ -347,8 +349,8 @@ namespace Explorer new GUILayoutOption[] { GUILayout.Width(MainMenu.MainRect.width - 187f) }); } - UIHelpers.SmallInspectButton(m_currentTransform); - + Buttons.InspectButton(m_currentTransform); + GUILayout.EndHorizontal(); } else @@ -372,28 +374,28 @@ namespace Explorer if (obj == null) continue; - if (!obj.RefGameObject) + try { - string label = "null"; + var go = obj.IValue.Value as GameObject ?? (obj.IValue.Value as Transform)?.gameObject; - if (obj.RefGameObject != null) + if (!go) { - label += " (Destroyed)"; - } + string label = "null"; - label += ""; - GUILayout.Label(label, new GUILayoutOption[0]); - } - else - { - UIHelpers.GOButton_Impl(obj.RefGameObject, - obj.EnabledColor, - obj.Label, - obj.RefGameObject.activeSelf, - SetTransformTarget, - true, - MainMenu.MainRect.width - 170); + if (go != null) + { + label += " (Destroyed)"; + } + + label += ""; + GUILayout.Label(label, new GUILayoutOption[0]); + } + else + { + Buttons.GameObjectButton(go, SetTransformTarget, true, MainMenu.MainRect.width - 170f); + } } + catch { } } } } @@ -413,17 +415,11 @@ namespace Explorer for (int i = offset; i < offset + Pages.ItemsPerPage && i < m_searchResults.Count; i++) { - var obj = m_searchResults[i]; + var obj = m_searchResults[i].IValue.Value as GameObject; - if (obj.RefGameObject) + if (obj) { - UIHelpers.GOButton_Impl(obj.RefGameObject, - obj.EnabledColor, - obj.Label, - obj.RefGameObject.activeSelf, - SetTransformTarget, - true, - MainMenu.MainRect.width - 170); + Buttons.GameObjectButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170); } else { @@ -436,42 +432,5 @@ namespace Explorer GUILayout.Label("No results found!", new GUILayoutOption[0]); } } - - // -------- Mini GameObjectCache class ---------- // - - 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; - } - } - } } } diff --git a/src/Menu/MainMenu/Pages/SearchPage.cs b/src/UI/Main/SearchPage.cs similarity index 83% rename from src/Menu/MainMenu/Pages/SearchPage.cs rename to src/UI/Main/SearchPage.cs index 0bc3818..5ca227d 100644 --- a/src/Menu/MainMenu/Pages/SearchPage.cs +++ b/src/UI/Main/SearchPage.cs @@ -4,14 +4,16 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEngine; +using Explorer.UI.Shared; +using Explorer.CacheObject; -namespace Explorer +namespace Explorer.UI.Main { - public class SearchPage : WindowPage + public class SearchPage : BaseMainMenuPage { public static SearchPage Instance; - public override string Name { get => "Object Search"; } + public override string Name { get => "Search"; } private string m_searchInput = ""; private string m_typeInput = ""; @@ -41,7 +43,7 @@ namespace Explorer Custom } - public override void Init() + public override void Init() { Instance = this; } @@ -52,11 +54,9 @@ namespace Explorer Pages.PageOffset = 0; } - public override void Update() - { - } + public override void Update() { } - private void CacheResults(IEnumerable results) + private void CacheResults(IEnumerable results, bool isStaticClasses = false) { m_searchResults = new List(); @@ -76,11 +76,12 @@ namespace Explorer } #endif - var cache = CacheFactory.GetTypeAndCacheObject(toCache); + var cache = CacheFactory.GetCacheObject(toCache); + cache.IsStaticClassSearchResult = isStaticClasses; m_searchResults.Add(cache); } - Pages.ItemCount = m_searchResults.Count; + Pages.ItemCount = m_searchResults.Count; Pages.PageOffset = 0; } @@ -89,12 +90,15 @@ namespace Explorer try { // helpers - GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null); + GUIUnstrip.BeginHorizontal(GUIContent.none, GUI.skin.box, null); GUILayout.Label("Helpers", new GUILayoutOption[] { GUILayout.Width(70) }); if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) })) { - //m_searchResults = GetInstanceClassScanner().ToList(); - CacheResults(GetInstanceClassScanner()); + CacheResults(GetStaticInstances()); + } + if (GUILayout.Button("Find Static Classes", new GUILayoutOption[] { GUILayout.Width(180) })) + { + CacheResults(GetStaticClasses(), true); } GUILayout.EndHorizontal(); @@ -102,7 +106,7 @@ namespace Explorer SearchBox(); // results - GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); + GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null); GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUILayout.Label("Results " + " (" + m_searchResults.Count + ")", new GUILayoutOption[0]); @@ -110,7 +114,7 @@ namespace Explorer int count = m_searchResults.Count; - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); Pages.DrawLimitInputArea(); @@ -165,21 +169,21 @@ namespace Explorer private void SearchBox() { - GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); + GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null); // ----- GameObject Search ----- GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUILayout.Label("Search", new GUILayoutOption[0]); GUI.skin.label.alignment = TextAnchor.UpperLeft; - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) }); m_searchInput = GUIUnstrip.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) }); GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUILayout.Label("Class Filter:", new GUILayoutOption[] { GUILayout.Width(100) }); ClassFilterToggle(TypeFilter.Object, "Object"); @@ -189,16 +193,16 @@ namespace Explorer GUILayout.EndHorizontal(); if (TypeMode == TypeFilter.Custom) { - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); 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; - m_typeInput = GUIUnstrip.TextField(m_typeInput, new GUILayoutOption[] { GUILayout.Width(250) }); + m_typeInput = GUIUnstrip.TextField(m_typeInput, new GUILayoutOption[] { GUILayout.Width(250) }); GUILayout.EndHorizontal(); } - GUILayout.BeginHorizontal(new GUILayoutOption[0]); - GUILayout.Label("Scene Filter:", new GUILayoutOption[] { GUILayout.Width(100) }); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); + GUILayout.Label("Scene Filter:", new GUILayoutOption[] { GUILayout.Width(100) }); SceneFilterToggle(SceneFilter.Any, "Any", 60); SceneFilterToggle(SceneFilter.This, "This Scene", 100); SceneFilterToggle(SceneFilter.DontDestroy, "DontDestroyOnLoad", 140); @@ -360,7 +364,7 @@ namespace Explorer #if CPP if (obj is Il2CppSystem.Object ilObject) { - go = ilObject.TryCast() ?? ilObject.TryCast().gameObject; + go = ilObject.TryCast() ?? ilObject.TryCast().gameObject; } #else if (obj is GameObject || obj is Component) @@ -395,14 +399,14 @@ namespace Explorer private static bool FilterName(string name) { // Don't really want these instances. - return !name.StartsWith("Mono") - && !name.StartsWith("System") - && !name.StartsWith("Il2CppSystem") + return !name.StartsWith("Mono") + && !name.StartsWith("System") + && !name.StartsWith("Il2CppSystem") && !name.StartsWith("Iced"); } // credit: ManlyMarco (RuntimeUnityEditor) - public static IEnumerable GetInstanceClassScanner() + public static IEnumerable GetStaticInstances() { var query = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(t => t.TryGetTypes()) @@ -457,5 +461,41 @@ namespace Explorer } } } + + private IEnumerable GetStaticClasses() + { + var list = new List(); + + var input = m_searchInput?.ToLower() ?? ""; + + foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) + { + try + { + foreach (var type in asm.TryGetTypes()) + { + if (!type.IsAbstract || !type.IsSealed) + { + continue; + } + + if (!string.IsNullOrEmpty(input)) + { + var typename = type.FullName.ToLower(); + + if (!typename.Contains(input)) + { + continue; + } + } + + list.Add(type); + } + } + catch { } + } + + return list; + } } } diff --git a/src/Menu/MainMenu/MainMenu.cs b/src/UI/MainMenu.cs similarity index 84% rename from src/Menu/MainMenu/MainMenu.cs rename to src/UI/MainMenu.cs index 6158459..25ffd32 100644 --- a/src/Menu/MainMenu/MainMenu.cs +++ b/src/UI/MainMenu.cs @@ -3,8 +3,12 @@ using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; +using Explorer.Config; +using Explorer.UI.Main; +using Explorer.UI.Shared; +using Explorer.UI.Inspectors; -namespace Explorer +namespace Explorer.UI { public class MainMenu { @@ -32,7 +36,7 @@ namespace Explorer public const int MainWindowID = 5000; public static Rect MainRect = new Rect(5, 5, ModConfig.Instance.Default_Window_Size.x, ModConfig.Instance.Default_Window_Size.y); - public static readonly List Pages = new List(); + public static readonly List Pages = new List(); private static int m_currentPage = 0; public static void SetCurrentPage(int index) @@ -86,7 +90,7 @@ namespace Explorer private void MainHeader() { - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); for (int i = 0; i < Pages.Count; i++) { if (m_currentPage == i) @@ -101,15 +105,15 @@ namespace Explorer } GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUI.color = Color.white; InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", new GUILayoutOption[0]); - bool mouseState = CursorControl.ForceUnlockMouse; + bool mouseState = ForceUnlockCursor.Unlock; bool setMouse = GUILayout.Toggle(mouseState, "Force Unlock Mouse (Left Alt)", new GUILayoutOption[0]); - if (setMouse != mouseState) CursorControl.ForceUnlockMouse = setMouse; + if (setMouse != mouseState) ForceUnlockCursor.Unlock = setMouse; - WindowManager.TabView = GUILayout.Toggle(WindowManager.TabView, "Tab View", new GUILayoutOption[0]); + //WindowManager.TabView = GUILayout.Toggle(WindowManager.TabView, "Tab View", new GUILayoutOption[0]); GUILayout.EndHorizontal(); //GUIUnstrip.Space(10); diff --git a/src/Menu/UIHelpers.cs b/src/UI/Shared/Buttons.cs similarity index 52% rename from src/Menu/UIHelpers.cs rename to src/UI/Shared/Buttons.cs index a41790f..8d7007d 100644 --- a/src/Menu/UIHelpers.cs +++ b/src/UI/Shared/Buttons.cs @@ -1,34 +1,44 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; using UnityEngine; using Object = UnityEngine.Object; -namespace Explorer +namespace Explorer.UI.Shared { - public class UIHelpers + public class Buttons { - // helper for "Instantiate" button on UnityEngine.Objects public static void InstantiateButton(Object obj, float width = 100) { if (GUILayout.Button("Instantiate", new GUILayoutOption[] { GUILayout.Width(width) })) { var newobj = Object.Instantiate(obj); - WindowManager.InspectObject(newobj, out _); } } - // helper for drawing a styled button for a GameObject or Transform - public static void GOButton(object _obj, Action specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380) + public static void InspectButton(object obj) { - var obj = (_obj as GameObject) ?? (_obj as Transform).gameObject; + if (GUILayout.Button("Inspect", new GUILayoutOption[0])) + { + WindowManager.InspectObject(obj, out bool _); + } + } - bool hasChild = obj.transform.childCount > 0; + public static void GameObjectButton(object _obj, Action inspectOverride = null, bool showSmallInspect = true, float width = 380) + { + var go = (_obj as GameObject) ?? (_obj as Transform).gameObject; - string label = hasChild ? $"[{obj.transform.childCount} children] " : ""; - label += obj.name; + if (!go) return; - bool enabled = obj.activeSelf; - int childCount = obj.transform.childCount; + bool hasChild = go.transform.childCount > 0; + + string label = hasChild ? $"[{go.transform.childCount} children] " : ""; + label += go.name; + + bool enabled = go.activeSelf; + int childCount = go.transform.childCount; Color color; if (enabled) @@ -47,39 +57,26 @@ namespace Explorer color = Color.red; } - GOButton_Impl(_obj, color, label, obj.activeSelf, specialInspectMethod, showSmallInspectBtn, width); - } - - public static void GOButton_Impl(object _obj, Color activeColor, string label, bool enabled, Action specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380) - { - var obj = _obj as GameObject ?? (_obj as Transform).gameObject; - - if (!obj) - { - GUILayout.Label("null", new GUILayoutOption[0]); - return; - } - // ------ toggle active button ------ - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUI.skin.button.alignment = TextAnchor.UpperLeft; - GUI.color = activeColor; + GUI.color = color; enabled = GUILayout.Toggle(enabled, "", new GUILayoutOption[] { GUILayout.Width(18) }); - if (obj.activeSelf != enabled) + if (go.activeSelf != enabled) { - obj.SetActive(enabled); + go.SetActive(enabled); } // ------- actual button --------- if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Height(22), GUILayout.Width(width) })) { - if (specialInspectMethod != null) + if (inspectOverride != null) { - specialInspectMethod(obj.transform); + inspectOverride(go.transform); } else { @@ -92,20 +89,12 @@ namespace Explorer GUI.skin.button.alignment = TextAnchor.MiddleCenter; GUI.color = Color.white; - if (showSmallInspectBtn) + if (showSmallInspect) { - SmallInspectButton(_obj); + InspectButton(_obj); } GUILayout.EndHorizontal(); } - - public static void SmallInspectButton(object obj) - { - if (GUILayout.Button("Inspect", new GUILayoutOption[0])) - { - WindowManager.InspectObject(obj, out bool _); - } - } } } diff --git a/src/CachedObjects/IExpandHeight.cs b/src/UI/Shared/IExpandHeight.cs similarity index 68% rename from src/CachedObjects/IExpandHeight.cs rename to src/UI/Shared/IExpandHeight.cs index f0cf45a..685dbd5 100644 --- a/src/CachedObjects/IExpandHeight.cs +++ b/src/UI/Shared/IExpandHeight.cs @@ -3,6 +3,6 @@ interface IExpandHeight { bool IsExpanded { get; set; } - float WhiteSpace { get; set; } + float WhiteSpace { get; set; } } } diff --git a/src/Helpers/PageHelper.cs b/src/UI/Shared/PageHelper.cs similarity index 93% rename from src/Helpers/PageHelper.cs rename to src/UI/Shared/PageHelper.cs index e7d7250..989cf50 100644 --- a/src/Helpers/PageHelper.cs +++ b/src/UI/Shared/PageHelper.cs @@ -1,6 +1,6 @@ using UnityEngine; -namespace Explorer +namespace Explorer.UI.Shared { public enum Turn { @@ -21,9 +21,9 @@ namespace Explorer CalculateMaxOffset(); } } - private int m_itemsPerPage = ModConfig.Instance.Default_Page_Limit; + private int m_itemsPerPage = Config.ModConfig.Instance.Default_Page_Limit; - public int ItemCount + public int ItemCount { get => m_count; set @@ -61,7 +61,7 @@ namespace Explorer { if (direction == Turn.Left) { - if (PageOffset > 0) + if (PageOffset > 0) { PageOffset--; scroll = Vector2.zero; diff --git a/src/Menu/ResizeDrag.cs b/src/UI/Shared/ResizeDrag.cs similarity index 93% rename from src/Menu/ResizeDrag.cs rename to src/UI/Shared/ResizeDrag.cs index 5843b50..24a649e 100644 --- a/src/Menu/ResizeDrag.cs +++ b/src/UI/Shared/ResizeDrag.cs @@ -4,7 +4,7 @@ using UnhollowerBaseLib; #endif using UnityEngine; -namespace Explorer +namespace Explorer.UI.Shared { public class ResizeDrag { @@ -26,7 +26,7 @@ namespace Explorer try { - GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null); + GUIUnstrip.BeginHorizontal(GUIContent.none, GUI.skin.box, null); GUI.skin.label.alignment = TextAnchor.MiddleCenter; #if ML @@ -35,8 +35,7 @@ namespace Explorer GUILayout.Button("<-- Drag to resize -->", new GUILayoutOption[] { GUILayout.Height(15) }); #endif - //var r = GUILayoutUtility.GetLastRect(); - var r = Internal_LayoutUtility.GetLastRect(); + var r = GUIUnstrip.GetLastRect(); var mousePos = InputManager.MousePosition; @@ -62,7 +61,7 @@ namespace Explorer _rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y } } - catch + catch { // throw safe Managed exception throw new Exception(""); @@ -87,7 +86,7 @@ namespace Explorer } else { - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUILayout.Label("Resize window:", new GUILayoutOption[] { GUILayout.Width(100) }); @@ -117,7 +116,7 @@ namespace Explorer #else // mono - GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null); + GUIUnstrip.BeginHorizontal(GUIContent.none, GUI.skin.box, null); GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) }); diff --git a/src/UI/Shared/Syntax.cs b/src/UI/Shared/Syntax.cs new file mode 100644 index 0000000..ff8de3c --- /dev/null +++ b/src/UI/Shared/Syntax.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Explorer.UI.Shared +{ + public class Syntax + { + public const string Field_Static = "#8d8dc6"; + public const string Field_Instance = "#c266ff"; + + public const string Method_Static = "#b55b02"; + public const string Method_Instance = "#ff8000"; + + public const string Prop_Static = "#588075"; + public const string Prop_Instance = "#55a38e"; + + public const string Class_Static = "#3a8d71"; + public const string Class_Instance = "#2df7b2"; + + public const string Local = "#a6e9e9"; + + public const string StructGreen = "#92c470"; + } +} diff --git a/src/Menu/UIStyles.cs b/src/UI/Shared/UIStyles.cs similarity index 84% rename from src/Menu/UIStyles.cs rename to src/UI/Shared/UIStyles.cs index b66e9fa..7959be1 100644 --- a/src/Menu/UIStyles.cs +++ b/src/UI/Shared/UIStyles.cs @@ -1,29 +1,10 @@ using UnityEngine; using Object = UnityEngine.Object; -namespace Explorer +namespace Explorer.UI.Shared { public class UIStyles { - public class Syntax - { - public const string Field_Static = "#8d8dc6"; - public const string Field_Instance = "#c266ff"; - - public const string Method_Static = "#b55b02"; - public const string Method_Instance = "#ff8000"; - - public const string Prop_Static = "#588075"; - public const string Prop_Instance = "#55a38e"; - - public const string Class_Static = "#3a8d71"; - public const string Class_Instance = "#2df7b2"; - - public const string Local = "#a6e9e9"; - - public const string StructGreen = "#92c470"; - } - 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 diff --git a/src/Menu/Windows/TabViewWindow.cs b/src/UI/TabViewWindow.cs similarity index 80% rename from src/Menu/Windows/TabViewWindow.cs rename to src/UI/TabViewWindow.cs index 95a61d4..ec16055 100644 --- a/src/Menu/Windows/TabViewWindow.cs +++ b/src/UI/TabViewWindow.cs @@ -1,15 +1,17 @@ -using UnityEngine; +using System; +using UnityEngine; +using Explorer.UI.Shared; -namespace Explorer +namespace Explorer.UI { - public class TabViewWindow : UIWindow + public class TabViewWindow : WindowBase { public override string Title => $"Tabs ({WindowManager.Windows.Count})"; public static TabViewWindow Instance => m_instance ?? (m_instance = new TabViewWindow()); private static TabViewWindow m_instance; - private UIWindow m_targetWindow; + private WindowBase m_targetWindow; public int TargetTabID = 0; public override bool IsTabViewWindow => true; @@ -21,7 +23,7 @@ namespace Explorer public override void Init() { } - public override void Update() + public override void Update() { while (TargetTabID >= WindowManager.Windows.Count) { @@ -35,7 +37,7 @@ namespace Explorer if (TargetTabID >= 0) { - m_targetWindow = WindowManager.Windows[TargetTabID]; + m_targetWindow = WindowManager.Windows[TargetTabID]; } else { @@ -61,8 +63,8 @@ namespace Explorer GUIUnstrip.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box); - GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); GUI.skin.button.alignment = TextAnchor.MiddleLeft; int tabPerRow = Mathf.FloorToInt((float)((decimal)m_rect.width / 238)); int rowCount = 0; @@ -72,7 +74,7 @@ namespace Explorer { rowCount = 0; GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(new GUILayoutOption[0]); + GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]); } rowCount++; @@ -97,15 +99,18 @@ namespace Explorer m_targetWindow.WindowFunction(m_targetWindow.windowID); - try - { - m_rect = ResizeDrag.ResizeWindow(m_rect, windowID); - } - catch { } + m_rect = ResizeDrag.ResizeWindow(m_rect, windowID); GUIUnstrip.EndArea(); } - catch { } + catch (Exception e) + { + if (!e.Message.Contains("in a group with only")) + { + ExplorerCore.Log("Exception drawing Tab View window: " + e.GetType() + ", " + e.Message); + ExplorerCore.Log(e.StackTrace); + } + } } } } diff --git a/src/Menu/Windows/UIWindow.cs b/src/UI/WindowBase.cs similarity index 69% rename from src/Menu/Windows/UIWindow.cs rename to src/UI/WindowBase.cs index d387e83..b42136b 100644 --- a/src/Menu/Windows/UIWindow.cs +++ b/src/UI/WindowBase.cs @@ -1,16 +1,18 @@ using System; using UnityEngine; +using Explorer.Config; +using Explorer.UI.Inspectors; -namespace Explorer +namespace Explorer.UI { - public abstract class UIWindow + public abstract class WindowBase { public abstract string Title { get; } public object Target; public int windowID; - public Rect m_rect = new Rect(0,0, ModConfig.Instance.Default_Window_Size.x,ModConfig.Instance.Default_Window_Size.y); + public Rect m_rect = new Rect(0, 0, ModConfig.Instance.Default_Window_Size.x, ModConfig.Instance.Default_Window_Size.y); public Vector2 scroll = Vector2.zero; @@ -20,7 +22,7 @@ namespace Explorer public abstract void WindowFunction(int windowID); public abstract void Update(); - public static UIWindow CreateWindow(object target) where T : UIWindow + public static WindowBase CreateWindow(object target) where T : WindowBase { var window = Activator.CreateInstance(); @@ -35,6 +37,22 @@ namespace Explorer return window; } + public static StaticInspector CreateWindowStatic(Type type) + { + var window = new StaticInspector + { + TargetType = type, + windowID = WindowManager.NextWindowID(), + m_rect = WindowManager.GetNewWindowRect() + }; + + WindowManager.Windows.Add(window); + + window.Init(); + + return window; + } + public void DestroyWindow() { WindowManager.DestroyWindow(this); diff --git a/src/Menu/Windows/WindowManager.cs b/src/UI/WindowManager.cs similarity index 80% rename from src/Menu/Windows/WindowManager.cs rename to src/UI/WindowManager.cs index 58194eb..c07627c 100644 --- a/src/Menu/Windows/WindowManager.cs +++ b/src/UI/WindowManager.cs @@ -1,90 +1,40 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using UnityEngine; +using Explorer.UI.Inspectors; -namespace Explorer +namespace Explorer.UI { public class WindowManager { public static WindowManager Instance; - public static bool TabView = true; + public static bool TabView = Config.ModConfig.Instance.Tab_View; - public static List Windows = new List(); + public static List Windows = new List(); public static int CurrentWindowID { get; set; } = 500000; private static Rect m_lastWindowRect; - private static readonly List m_windowsToDestroy = new List(); + private static readonly List m_windowsToDestroy = new List(); public WindowManager() { Instance = this; } - public static void DestroyWindow(UIWindow window) + public static void DestroyWindow(WindowBase window) { m_windowsToDestroy.Add(window); } - public void Update() - { - if (m_windowsToDestroy.Count > 0) - { - foreach (var window in m_windowsToDestroy) - { - if (Windows.Contains(window)) - { - Windows.Remove(window); - } - } - - m_windowsToDestroy.Clear(); - } - - if (TabView) - { - TabViewWindow.Instance.Update(); - } - else - { - for (int i = 0; i < Windows.Count; i++) - { - var window = Windows[i]; - if (window != null) - { - window.Update(); - } - } - } - } - - public void OnGUI() - { - if (TabView) - { - if (Windows.Count > 0) - { - TabViewWindow.Instance.OnGUI(); - } - } - else - { - foreach (var window in Windows) - { - window.OnGUI(); - } - } - } - - // ========= Public Helpers ========= - - public static UIWindow InspectObject(object obj, out bool createdNew, bool forceReflection = false) + public static WindowBase InspectObject(object obj, out bool createdNew, bool forceReflection = false) { createdNew = false; - if (InputManager.GetKey(KeyCode.LeftShift)) - { - forceReflection = true; - } + //if (InputManager.GetKey(KeyCode.LeftShift)) + //{ + // forceReflection = true; + //} #if CPP Il2CppSystem.Object iObj = null; @@ -136,7 +86,7 @@ namespace Explorer } } - private static void FocusWindow(UIWindow window) + private static void FocusWindow(WindowBase window) { if (!TabView) { @@ -149,23 +99,29 @@ namespace Explorer } } - private static UIWindow InspectGameObject(GameObject obj) + private static WindowBase InspectGameObject(GameObject obj) { - var new_window = UIWindow.CreateWindow(obj); + var new_window = WindowBase.CreateWindow(obj); FocusWindow(new_window); return new_window; } - private static UIWindow InspectReflection(object obj) + private static WindowBase InspectReflection(object obj) { - var new_window = UIWindow.CreateWindow(obj); + var new_window = WindowBase.CreateWindow(obj); FocusWindow(new_window); return new_window; } - // === Misc Helpers === + public static StaticInspector InspectStaticReflection(Type type) + { + var new_window = WindowBase.CreateWindowStatic(type); + FocusWindow(new_window); + + return new_window; + } public static bool IsMouseInWindow { @@ -222,5 +178,57 @@ namespace Explorer return rect; } + + // ============= instance methods =============== + + public void Update() + { + if (m_windowsToDestroy.Count > 0) + { + foreach (var window in m_windowsToDestroy) + { + if (Windows.Contains(window)) + { + Windows.Remove(window); + } + } + + m_windowsToDestroy.Clear(); + } + + if (TabView) + { + TabViewWindow.Instance.Update(); + } + else + { + for (int i = 0; i < Windows.Count; i++) + { + var window = Windows[i]; + if (window != null) + { + window.Update(); + } + } + } + } + + public void OnGUI() + { + if (TabView) + { + if (Windows.Count > 0) + { + TabViewWindow.Instance.OnGUI(); + } + } + else + { + foreach (var window in Windows) + { + window.OnGUI(); + } + } + } } } diff --git a/src/UnstripFixes/GUIUnstrip.cs b/src/UnstripFixes/GUIUnstrip.cs index 9f4407f..d34accc 100644 --- a/src/UnstripFixes/GUIUnstrip.cs +++ b/src/UnstripFixes/GUIUnstrip.cs @@ -4,14 +4,52 @@ using System.Reflection; using UnityEngine; #if CPP using Explorer.UnstripInternals; -using UnityEngineInternal; -using UnhollowerRuntimeLib; #endif namespace Explorer { public class GUIUnstrip { + public static void BeginHorizontal(params GUILayoutOption[] options) + => BeginHorizontal(GUIContent.none, GUIStyle.none, options); + + public static void BeginHorizontal(GUIStyle style, params GUILayoutOption[] options) + => BeginHorizontal(GUIContent.none, style, options); + + public static void BeginHorizontal(GUIContent content, GUIStyle style, params GUILayoutOption[] options) + { +#if CPP + Internal.BeginLayoutDirection(false, content, style, options); +#else + GUILayout.BeginHorizontal(content, style, options); +#endif + } + + public static void BeginVertical(params GUILayoutOption[] options) + => BeginVertical(GUIContent.none, GUIStyle.none, options); + + public static void BeginVertical(GUIStyle style, params GUILayoutOption[] options) + => BeginVertical(GUIContent.none, style, options); + + public static void BeginVertical(GUIContent content, GUIStyle style, params GUILayoutOption[] options) + { +#if CPP + Internal.BeginLayoutDirection(true, content, style, options); +#else + GUILayout.BeginVertical(content, style, options); +#endif + } + + + public static Rect GetLastRect() + { +#if CPP + return Internal_LayoutUtility.GetLastRect(); +#else + return GUILayoutUtility.GetLastRect(); +#endif + } + public static string TextField(string text, GUILayoutOption[] options) { #if CPP diff --git a/src/UnstripFixes/Internal.cs b/src/UnstripFixes/Internal.cs index cf4db8a..c7759ba 100644 --- a/src/UnstripFixes/Internal.cs +++ b/src/UnstripFixes/Internal.cs @@ -107,6 +107,14 @@ namespace Explorer.UnstripInternals #region GUILayout Methods + public static void BeginLayoutDirection(bool vertical, GUIContent content, GUIStyle style, GUILayoutOption[] options) + { + var g = GUILayoutUtility.BeginLayoutGroup(style, options, Il2CppType.Of()); + g.isVertical = vertical; + if (style != GUIStyle.none || content != GUIContent.none) + GUI.Box(g.rect, content, style); + } + public static string TextField(string text, GUILayoutOption[] options) { text = text ?? ""; diff --git a/src/UnstripFixes/Internal_LayoutUtility.cs b/src/UnstripFixes/Internal_LayoutUtility.cs index f7d4583..b491449 100644 --- a/src/UnstripFixes/Internal_LayoutUtility.cs +++ b/src/UnstripFixes/Internal_LayoutUtility.cs @@ -1,8 +1,7 @@ #if CPP using UnityEngine; -using Explorer.UnstripInternals; -namespace Explorer +namespace Explorer.UnstripInternals { public class Internal_LayoutUtility { diff --git a/src/UnstripFixes/Internal_ScrollViewState.cs b/src/UnstripFixes/Internal_ScrollViewState.cs index c1b84b9..64af6fb 100644 --- a/src/UnstripFixes/Internal_ScrollViewState.cs +++ b/src/UnstripFixes/Internal_ScrollViewState.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using UnityEngine; -namespace Explorer +namespace Explorer.UnstripInternals { public class Internal_ScrollViewState { diff --git a/src/UnstripFixes/Internal_SliderHandler.cs b/src/UnstripFixes/Internal_SliderHandler.cs index 9b57f31..1a458e8 100644 --- a/src/UnstripFixes/Internal_SliderHandler.cs +++ b/src/UnstripFixes/Internal_SliderHandler.cs @@ -5,7 +5,7 @@ using UnityEngine; using Explorer.UnstripInternals; using Il2CppSystem.Reflection; -namespace Explorer +namespace Explorer.UnstripInternals { public struct Internal_SliderHandler { diff --git a/src/UnstripFixes/Internal_SliderState.cs b/src/UnstripFixes/Internal_SliderState.cs index 83326a3..4934e4d 100644 --- a/src/UnstripFixes/Internal_SliderState.cs +++ b/src/UnstripFixes/Internal_SliderState.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; -namespace Explorer +namespace Explorer.UnstripInternals { public class Internal_SliderState {