diff --git a/src/CppExplorer.cs b/src/CppExplorer.cs index 1f9b293..8bf2a45 100644 --- a/src/CppExplorer.cs +++ b/src/CppExplorer.cs @@ -16,7 +16,7 @@ namespace Explorer public const string ID = "com.sinai.cppexplorer"; public const string NAME = "IL2CPP Runtime Explorer"; - public const string VERSION = "1.3.0"; + public const string VERSION = "1.3.1"; public const string AUTHOR = "Sinai"; // fields @@ -28,7 +28,7 @@ namespace Explorer // props public static bool ShowMenu { get; set; } = false; - public static int ArrayLimit { get; set; } = 100; + public static int ArrayLimit { get; set; } = 20; public bool MouseInspect { get; set; } = false; public static string ActiveSceneName diff --git a/src/CppExplorer.csproj b/src/CppExplorer.csproj index 6c1be1c..e5837b4 100644 --- a/src/CppExplorer.csproj +++ b/src/CppExplorer.csproj @@ -7,7 +7,7 @@ {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D} Library Properties - CppExplorer + Explorer CppExplorer v4.7.2 512 @@ -113,9 +113,14 @@ + + + + + diff --git a/src/Inspectors/GameObjectWindow.cs b/src/Inspectors/GameObjectWindow.cs index 16d265e..c3d8f66 100644 --- a/src/Inspectors/GameObjectWindow.cs +++ b/src/Inspectors/GameObjectWindow.cs @@ -9,9 +9,9 @@ using UnityEngine.SceneManagement; namespace Explorer { - public class GameObjectWindow : WindowManager.UIWindow + public class GameObjectWindow : UIWindow { - public override Il2CppSystem.String Name { get => "GameObject Inspector"; set => Name = value; } + public override string Name { get => "GameObject Inspector"; set => Name = value; } public GameObject m_object; diff --git a/src/Inspectors/Reflection/FieldInfoHolder.cs b/src/Inspectors/Reflection/FieldInfoHolder.cs new file mode 100644 index 0000000..4963a45 --- /dev/null +++ b/src/Inspectors/Reflection/FieldInfoHolder.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using MelonLoader; +using UnhollowerBaseLib; + +namespace Explorer +{ + public class FieldInfoHolder : MemberInfoHolder + { + public FieldInfo fieldInfo; + public object m_value; + + public FieldInfoHolder(Type _type, FieldInfo _fieldInfo) + { + classType = _type; + fieldInfo = _fieldInfo; + } + + public override void UpdateValue(object obj) + { + m_value = fieldInfo.GetValue(fieldInfo.IsStatic ? null : obj); + } + + public override void Draw(ReflectionWindow window) + { + UIStyles.DrawMember(ref m_value, ref this.IsExpanded, ref this.arrayOffset, this.fieldInfo, window.m_rect, window.m_object, SetValue); + } + + public override void SetValue(object obj) + { + if (fieldInfo.FieldType.IsEnum) + { + if (System.Enum.Parse(fieldInfo.FieldType, m_value.ToString()) is object enumValue && enumValue != null) + { + m_value = enumValue; + } + } + else if (fieldInfo.FieldType.IsPrimitive) + { + if (fieldInfo.FieldType == typeof(float)) + { + if (float.TryParse(m_value.ToString(), out float f)) + { + m_value = f; + } + else + { + MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!"); + } + } + else if (fieldInfo.FieldType == typeof(double)) + { + if (double.TryParse(m_value.ToString(), out double d)) + { + m_value = d; + } + else + { + MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!"); + } + } + else if (fieldInfo.FieldType != typeof(bool)) + { + if (int.TryParse(m_value.ToString(), out int i)) + { + m_value = i; + } + else + { + MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + fieldInfo.FieldType); + } + } + } + + fieldInfo.SetValue(fieldInfo.IsStatic ? null : obj, m_value); + } + } +} diff --git a/src/Inspectors/Reflection/MemberInfoHolder.cs b/src/Inspectors/Reflection/MemberInfoHolder.cs new file mode 100644 index 0000000..8df7f83 --- /dev/null +++ b/src/Inspectors/Reflection/MemberInfoHolder.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Explorer +{ + public abstract class MemberInfoHolder + { + public Type classType; + public bool IsExpanded = false; + public int arrayOffset = 0; + + public abstract void Draw(ReflectionWindow window); + public abstract void UpdateValue(object obj); + public abstract void SetValue(object obj); + } +} diff --git a/src/Inspectors/Reflection/PropertyInfoHolder.cs b/src/Inspectors/Reflection/PropertyInfoHolder.cs new file mode 100644 index 0000000..41f2a16 --- /dev/null +++ b/src/Inspectors/Reflection/PropertyInfoHolder.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using MelonLoader; +using UnhollowerBaseLib; + +namespace Explorer +{ + public class PropertyInfoHolder : MemberInfoHolder + { + public PropertyInfo propInfo; + public object m_value; + + public PropertyInfoHolder(Type _type, PropertyInfo _propInfo) + { + classType = _type; + propInfo = _propInfo; + } + + public override void Draw(ReflectionWindow window) + { + UIStyles.DrawMember(ref m_value, ref this.IsExpanded, ref this.arrayOffset, this.propInfo, window.m_rect, window.m_object, SetValue); + } + + public override void UpdateValue(object obj) + { + try + { + if (obj is Il2CppSystem.Object ilObject) + { + var declaringType = this.propInfo.DeclaringType; + if (declaringType == typeof(Il2CppObjectBase)) + { + m_value = ilObject.Pointer; + } + else + { + var cast = CppExplorer.Il2CppCast(obj, declaringType); + m_value = this.propInfo.GetValue(cast, null); + } + } + else + { + m_value = this.propInfo.GetValue(obj, null); + } + } + catch (Exception e) + { + MelonLogger.Log("Exception on PropertyInfoHolder.UpdateValue, Name: " + this.propInfo.Name); + MelonLogger.Log(e.GetType() + ", " + e.Message); + + var inner = e.InnerException; + while (inner != null) + { + MelonLogger.Log("inner: " + inner.GetType() + ", " + inner.Message); + inner = inner.InnerException; + } + + m_value = null; + } + } + + public override void SetValue(object obj) + { + try + { + if (propInfo.PropertyType.IsEnum) + { + if (System.Enum.Parse(propInfo.PropertyType, m_value.ToString()) is object enumValue && enumValue != null) + { + m_value = enumValue; + } + } + else if (propInfo.PropertyType.IsPrimitive) + { + if (propInfo.PropertyType == typeof(float)) + { + if (float.TryParse(m_value.ToString(), out float f)) + { + m_value = f; + } + else + { + MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!"); + } + } + else if (propInfo.PropertyType == typeof(double)) + { + if (double.TryParse(m_value.ToString(), out double d)) + { + m_value = d; + } + else + { + MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!"); + } + } + else if (propInfo.PropertyType != typeof(bool)) + { + if (int.TryParse(m_value.ToString(), out int i)) + { + m_value = i; + } + else + { + MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + propInfo.PropertyType); + } + } + } + + var declaring = propInfo.DeclaringType; + var cast = CppExplorer.Il2CppCast(obj, declaring); + + propInfo.SetValue(propInfo.GetAccessors()[0].IsStatic ? null : cast, m_value, null); + } + catch + { + //MelonLogger.Log("Exception trying to set property " + this.propInfo.Name); + } + } + } +} diff --git a/src/Inspectors/ReflectionWindow.cs b/src/Inspectors/ReflectionWindow.cs index aafb519..b1a4405 100644 --- a/src/Inspectors/ReflectionWindow.cs +++ b/src/Inspectors/ReflectionWindow.cs @@ -10,9 +10,9 @@ using UnityEngine; namespace Explorer { - public class ReflectionWindow : WindowManager.UIWindow + public class ReflectionWindow : UIWindow { - public override Il2CppSystem.String Name { get => "Object Reflection"; set => Name = value; } + public override string Name { get => "Object Reflection"; set => Name = value; } public Type m_objectType; public object m_object; @@ -351,204 +351,5 @@ namespace Explorer } } } - - - /* ********************* - * PROPERTYINFO HOLDER - */ - - public class PropertyInfoHolder - { - public Type classType; - public PropertyInfo propInfo; - public object m_value; - public bool IsExpanded; - - public PropertyInfoHolder(Type _type, PropertyInfo _propInfo) - { - classType = _type; - propInfo = _propInfo; - } - - public void Draw(ReflectionWindow window) - { - UIStyles.DrawMember(ref m_value, ref this.IsExpanded, this.propInfo, window.m_rect, window.m_object, SetValue); - } - - public void UpdateValue(object obj) - { - try - { - if (obj is Il2CppSystem.Object ilObject) - { - var declaringType = this.propInfo.DeclaringType; - if (declaringType == typeof(Il2CppObjectBase)) - { - m_value = ilObject.Pointer; - } - else - { - var cast = CppExplorer.Il2CppCast(obj, declaringType); - m_value = this.propInfo.GetValue(cast, null); - } - } - else - { - m_value = this.propInfo.GetValue(obj, null); - } - } - catch (Exception e) - { - MelonLogger.Log("Exception on PropertyInfoHolder.UpdateValue, Name: " + this.propInfo.Name); - MelonLogger.Log(e.GetType() + ", " + e.Message); - - var inner = e.InnerException; - while (inner != null) - { - MelonLogger.Log("inner: " + inner.GetType() + ", " + inner.Message); - inner = inner.InnerException; - } - - m_value = null; - } - } - - public void SetValue(object obj) - { - try - { - if (propInfo.PropertyType.IsEnum) - { - if (System.Enum.Parse(propInfo.PropertyType, m_value.ToString()) is object enumValue && enumValue != null) - { - m_value = enumValue; - } - } - else if (propInfo.PropertyType.IsPrimitive) - { - if (propInfo.PropertyType == typeof(float)) - { - if (float.TryParse(m_value.ToString(), out float f)) - { - m_value = f; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!"); - } - } - else if (propInfo.PropertyType == typeof(double)) - { - if (double.TryParse(m_value.ToString(), out double d)) - { - m_value = d; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!"); - } - } - else if (propInfo.PropertyType != typeof(bool)) - { - if (int.TryParse(m_value.ToString(), out int i)) - { - m_value = i; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + propInfo.PropertyType); - } - } - } - - var declaring = propInfo.DeclaringType; - var cast = CppExplorer.Il2CppCast(obj, declaring); - - propInfo.SetValue(propInfo.GetAccessors()[0].IsStatic ? null : cast, m_value, null); - } - catch - { - //MelonLogger.Log("Exception trying to set property " + this.propInfo.Name); - } - } - } - - - /* ********************* - * FIELDINFO HOLDER - */ - - public class FieldInfoHolder - { - public Type classType; - public FieldInfo fieldInfo; - public object m_value; - public bool IsExpanded; - - public FieldInfoHolder(Type _type, FieldInfo _fieldInfo) - { - classType = _type; - fieldInfo = _fieldInfo; - } - - public void UpdateValue(object obj) - { - m_value = fieldInfo.GetValue(fieldInfo.IsStatic ? null : obj); - } - - public void Draw(ReflectionWindow window) - { - UIStyles.DrawMember(ref m_value, ref this.IsExpanded, this.fieldInfo, window.m_rect, window.m_object, SetValue); - } - - public void SetValue(object obj) - { - if (fieldInfo.FieldType.IsEnum) - { - if (System.Enum.Parse(fieldInfo.FieldType, m_value.ToString()) is object enumValue && enumValue != null) - { - m_value = enumValue; - } - } - else if (fieldInfo.FieldType.IsPrimitive) - { - if (fieldInfo.FieldType == typeof(float)) - { - if (float.TryParse(m_value.ToString(), out float f)) - { - m_value = f; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!"); - } - } - else if (fieldInfo.FieldType == typeof(double)) - { - if (double.TryParse(m_value.ToString(), out double d)) - { - m_value = d; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!"); - } - } - else if (fieldInfo.FieldType != typeof(bool)) - { - if (int.TryParse(m_value.ToString(), out int i)) - { - m_value = i; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + fieldInfo.FieldType); - } - } - } - - fieldInfo.SetValue(fieldInfo.IsStatic ? null : obj, m_value); - } - } } } diff --git a/src/Inspectors/UIWindow.cs b/src/Inspectors/UIWindow.cs new file mode 100644 index 0000000..7831d8f --- /dev/null +++ b/src/Inspectors/UIWindow.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Harmony; +using MelonLoader; +using UnhollowerBaseLib; +using UnhollowerRuntimeLib; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.EventSystems; + +namespace Explorer +{ + public abstract class UIWindow + { + public abstract string Name { get; set; } + + public object Target; + + public int windowID; + public Rect m_rect = new Rect(0, 0, 550, 700); + + public Vector2 scroll = Vector2.zero; + + public static UIWindow CreateWindow(object target) where T : UIWindow + { + //var component = (UIWindow)AddToGameObject(Instance.gameObject); + var window = Activator.CreateInstance(); + + window.Target = target; + window.windowID = WindowManager.NextWindowID(); + window.m_rect = WindowManager.GetNewWindowRect(); + + WindowManager.Windows.Add(window); + + window.Init(); + + return window; + } + + public void DestroyWindow() + { + try + { + WindowManager.Windows.Remove(this); + } + catch (Exception e) + { + MelonLogger.Log("Exception removing Window from WindowManager.Windows list!"); + MelonLogger.Log($"{e.GetType()} : {e.Message}\r\n{e.StackTrace}"); + } + //Destroy(this); + } + + public abstract void Init(); + public abstract void WindowFunction(int windowID); + public abstract void Update(); + + public void OnGUI() + { + if (CppExplorer.ShowMenu) + { + var origSkin = GUI.skin; + + GUI.skin = UIStyles.WindowSkin; + m_rect = GUI.Window(windowID, m_rect, (GUI.WindowFunction)WindowFunction, Name); + + GUI.skin = origSkin; + } + } + + public void Header() + { + GUI.DragWindow(new Rect(0, 0, m_rect.width - 90, 20)); + + if (GUI.Button(new Rect(m_rect.width - 90, 2, 80, 20), "X")) + { + DestroyWindow(); + return; + } + } + } +} diff --git a/src/MainMenu/MainMenu.cs b/src/MainMenu/MainMenu.cs index 7488cef..ca708d2 100644 --- a/src/MainMenu/MainMenu.cs +++ b/src/MainMenu/MainMenu.cs @@ -119,18 +119,5 @@ namespace Explorer GUILayout.Space(10); GUI.color = Color.white; } - - public abstract class WindowPage - { - public virtual string Name { get; set; } - - public Vector2 scroll = Vector2.zero; - - public abstract void Init(); - - public abstract void DrawWindow(); - - public abstract void Update(); - } } } diff --git a/src/MainMenu/Pages/ConsolePage.cs b/src/MainMenu/Pages/ConsolePage.cs index ac583ed..cee0eef 100644 --- a/src/MainMenu/Pages/ConsolePage.cs +++ b/src/MainMenu/Pages/ConsolePage.cs @@ -12,9 +12,9 @@ using MelonLoader; namespace Explorer { - public class ConsolePage : MainMenu.WindowPage + public class ConsolePage : WindowPage { - public override string Name { get => "Console"; set => base.Name = value; } + public override string Name { get => "C# Console"; set => base.Name = value; } private ScriptEvaluator _evaluator; private readonly StringBuilder _sb = new StringBuilder(); @@ -41,13 +41,18 @@ namespace Explorer try { - MethodInput = @"// This is a basic REPL console used to execute a method. -// Some common directives are added by default, you can add more below. + MethodInput = @"// This is a basic C# REPL console. +// Some common using directives are added by default, you can add more below. // If you want to return some output, MelonLogger.Log() it. MelonLogger.Log(""hello world"");"; ResetConsole(); + + foreach (var use in m_defaultUsing) + { + AddUsing(use); + } } catch (Exception e) { @@ -65,11 +70,6 @@ MelonLogger.Log(""hello world"");"; _evaluator = new ScriptEvaluator(new StringWriter(_sb)) { InteractiveBaseClass = typeof(REPL) }; UsingDirectives = new List(); - UsingDirectives.AddRange(m_defaultUsing); - foreach (string asm in UsingDirectives) - { - Evaluate(AsmToUsing(asm)); - } } public string AsmToUsing(string asm, bool richtext = false) @@ -111,7 +111,7 @@ MelonLogger.Log(""hello world"");"; public override void DrawWindow() { - GUILayout.Label("REPL Console", null); + GUILayout.Label("C# REPL Console", null); GUILayout.Label("Method:", null); MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.Height(300) }); diff --git a/src/MainMenu/Pages/ScenePage.cs b/src/MainMenu/Pages/ScenePage.cs index af615b1..ae256af 100644 --- a/src/MainMenu/Pages/ScenePage.cs +++ b/src/MainMenu/Pages/ScenePage.cs @@ -8,7 +8,7 @@ using UnityEngine.SceneManagement; namespace Explorer { - public class ScenePage : MainMenu.WindowPage + public class ScenePage : WindowPage { public static ScenePage Instance; @@ -45,7 +45,7 @@ namespace Explorer public override void Update() { - if (Time.time - m_timeOfLastUpdate < 1f) + if (Time.time - m_timeOfLastUpdate < 0.2f) { return; } diff --git a/src/MainMenu/Pages/SearchPage.cs b/src/MainMenu/Pages/SearchPage.cs index c4df37d..6976647 100644 --- a/src/MainMenu/Pages/SearchPage.cs +++ b/src/MainMenu/Pages/SearchPage.cs @@ -12,15 +12,16 @@ using UnhollowerBaseLib; namespace Explorer { - public class SearchPage : MainMenu.WindowPage + public class SearchPage : WindowPage { public static SearchPage Instance; - public override string Name { get => "Advanced Search"; set => base.Name = value; } + public override string Name { get => "Object Search"; set => base.Name = value; } private string m_searchInput = ""; private string m_typeInput = ""; private int m_limit = 100; + private int m_pageOffset = 0; public SceneFilter SceneMode = SceneFilter.Any; public TypeFilter TypeMode = TypeFilter.Object; @@ -52,6 +53,7 @@ namespace Explorer public void OnSceneChange() { m_searchResults.Clear(); + m_pageOffset = 0; } public override void Update() @@ -68,6 +70,7 @@ namespace Explorer if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) })) { m_searchResults = GetInstanceClassScanner().ToList(); + m_pageOffset = 0; } GUILayout.EndHorizontal(); @@ -78,21 +81,59 @@ namespace Explorer GUILayout.BeginVertical(GUI.skin.box, null); GUI.skin.label.alignment = TextAnchor.MiddleCenter; - GUILayout.Label("Results", null); + GUILayout.Label("Results " + " (" + m_searchResults.Count + ")", null); GUI.skin.label.alignment = TextAnchor.UpperLeft; + int count = m_searchResults.Count; + + if (count > CppExplorer.ArrayLimit) + { + // prev/next page buttons + GUILayout.BeginHorizontal(null); + int maxOffset = (int)Mathf.Ceil(count / CppExplorer.ArrayLimit); + if (GUILayout.Button("< Prev", null)) + { + if (m_pageOffset > 0) m_pageOffset--; + } + + GUILayout.Label($"Page {m_pageOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) }); + + if (GUILayout.Button("Next >", null)) + { + if (m_pageOffset < maxOffset) m_pageOffset++; + } + GUILayout.EndHorizontal(); + } + resultsScroll = GUILayout.BeginScrollView(resultsScroll, GUI.skin.scrollView); var _temprect = new Rect(MainMenu.MainRect.x, MainMenu.MainRect.y, MainMenu.MainRect.width + 160, MainMenu.MainRect.height); if (m_searchResults.Count > 0) { + int offset = m_pageOffset * CppExplorer.ArrayLimit; + int preiterated = 0; + + if (offset >= count) offset = 0; + for (int i = 0; i < m_searchResults.Count; i++) { + if (offset > 0 && preiterated < offset) + { + preiterated++; + continue; + } + + if (i - offset > CppExplorer.ArrayLimit - 1) + { + break; + } + var obj = m_searchResults[i]; bool _ = false; - UIStyles.DrawValue(ref obj, _temprect, ref _); + int __ = 0; + UIStyles.DrawValue(ref obj, ref _, ref __, _temprect); } } else @@ -125,7 +166,7 @@ namespace Explorer m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) }); GUI.skin.label.alignment = TextAnchor.MiddleRight; - GUILayout.Label("Result limit:", new GUILayoutOption[] { GUILayout.Width(100) }); + GUILayout.Label("Results per page:", new GUILayoutOption[] { GUILayout.Width(120) }); var resultinput = m_limit.ToString(); resultinput = GUILayout.TextField(resultinput, new GUILayoutOption[] { GUILayout.Width(55) }); if (int.TryParse(resultinput, out int _i) && _i > 0) @@ -289,26 +330,9 @@ namespace Explorer } var matches = new List(); - int added = 0; - - //MelonLogger.Log("Trying to get IL Type. ASM name: " + type.Assembly.GetName().Name + ", Namespace: " + type.Namespace + ", name: " + type.Name); - - //var asmName = type.Assembly.GetName().Name; - //if (asmName.Contains("UnityEngine")) - //{ - // asmName = "UnityEngine"; - //} - - //var intPtr = IL2CPP.GetIl2CppClass(asmName, type.Namespace, type.Name); - //var ilType = Il2CppType.TypeFromPointer(intPtr); foreach (var obj in Resources.FindObjectsOfTypeAll(type)) { - if (added == m_limit) - { - break; - } - if (_search != "" && !obj.name.ToLower().Contains(_search.ToLower())) { continue; @@ -360,7 +384,6 @@ namespace Explorer if (!matches.Contains(obj)) { matches.Add(obj); - added++; } } diff --git a/src/MainMenu/Pages/WindowPage.cs b/src/MainMenu/Pages/WindowPage.cs new file mode 100644 index 0000000..b4c056b --- /dev/null +++ b/src/MainMenu/Pages/WindowPage.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace Explorer +{ + public abstract class WindowPage + { + public virtual string Name { get; set; } + + public Vector2 scroll = Vector2.zero; + + public abstract void Init(); + + public abstract void DrawWindow(); + + public abstract void Update(); + } +} diff --git a/src/UIStyles.cs b/src/UIStyles.cs index d21224d..9f1795f 100644 --- a/src/UIStyles.cs +++ b/src/UIStyles.cs @@ -196,7 +196,7 @@ namespace Explorer GUILayout.EndHorizontal(); } - public static void DrawMember(ref object value, ref bool isExpanded, MemberInfo memberInfo, Rect rect, object setTarget = null, Action setAction = null, float labelWidth = 180, bool autoSet = false) + public static void DrawMember(ref object value, ref bool isExpanded, ref int arrayOffset, MemberInfo memberInfo, Rect rect, object setTarget = null, Action setAction = null, float labelWidth = 180, bool autoSet = false) { GUILayout.Label("" + memberInfo.Name + ":", new GUILayoutOption[] { GUILayout.Width(labelWidth) }); @@ -213,10 +213,10 @@ namespace Explorer canWrite = pi.CanWrite; } - DrawValue(ref value, rect, ref isExpanded, valueType, (canWrite ? setTarget : null), (canWrite ? setAction : null), autoSet); + DrawValue(ref value, ref isExpanded, ref arrayOffset, rect, valueType, (canWrite ? setTarget : null), (canWrite ? setAction : null), autoSet); } - public static void DrawValue(ref object value, Rect rect, ref bool isExpanded, string nullValueType = null, object setTarget = null, Action setAction = null, bool autoSet = false) + public static void DrawValue(ref object value, ref bool isExpanded, ref int arrayOffset, Rect rect, string nullValueType = null, object setTarget = null, Action setAction = null, bool autoSet = false) { if (value == null) { @@ -304,12 +304,41 @@ namespace Explorer if (isExpanded) { + if (count > CppExplorer.ArrayLimit) + { + GUILayout.EndHorizontal(); + GUILayout.BeginHorizontal(null); + GUILayout.Space(190); + int maxOffset = (int)Mathf.Ceil(count / CppExplorer.ArrayLimit); + GUILayout.Label($"Page {arrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) }); + // prev/next page buttons + if (GUILayout.Button("< Prev", null)) + { + if (arrayOffset > 0) arrayOffset--; + } + if (GUILayout.Button("Next >", null)) + { + if (arrayOffset < maxOffset) arrayOffset++; + } + } + + int offset = arrayOffset * CppExplorer.ArrayLimit; + + if (offset >= count) offset = 0; + var enumerator = enumerable.GetEnumerator(); if (enumerator != null) { int i = 0; + int preiterated = 0; while (enumerator.MoveNext()) { + if (offset > 0 && preiterated < offset) + { + preiterated++; + continue; + } + var obj = enumerator.Current; //collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry @@ -317,9 +346,9 @@ namespace Explorer GUILayout.BeginHorizontal(null); GUILayout.Space(190); - if (i > CppExplorer.ArrayLimit) + if (i > CppExplorer.ArrayLimit - 1) { - GUILayout.Label($"{count - CppExplorer.ArrayLimit} results omitted, array is too long!", null); + //GUILayout.Label($"{count - CppExplorer.ArrayLimit} results omitted, array is too long!", null); break; } @@ -330,7 +359,8 @@ namespace Explorer else { var type = obj.GetType(); - var lbl = i + ": " + obj.ToString() + ""; + + var lbl = (i + offset) + ": " + obj.ToString() + ""; if (type.IsPrimitive || typeof(string).IsAssignableFrom(type)) { diff --git a/src/WindowManager.cs b/src/WindowManager.cs index 6fd8bec..49d3fd8 100644 --- a/src/WindowManager.cs +++ b/src/WindowManager.cs @@ -1,5 +1,4 @@ using System; -using System.CodeDom; using System.Collections.Generic; using System.Linq; using System.Text; @@ -142,117 +141,49 @@ namespace Explorer // ============= Resize Window Helper ============ - static readonly GUIContent gcDrag = new GUIContent("<->", "drag to resize"); - + // static readonly GUIContent gcDrag = new GUIContent("<->", "drag to resize"); + private static readonly GUIContent gcDrag = new GUIContent("<->"); private static bool isResizing = false; private static Rect m_currentResize; private static int m_currentWindow; public static Rect ResizeWindow(Rect _rect, int ID) { - GUILayout.BeginHorizontal(null); - GUILayout.Space(_rect.width - 35); - - GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Width(25), GUILayout.Height(25) }); - - var r = GUILayoutUtility.GetLastRect(); - - Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)); - - if (r.Contains(mouse) && Input.GetMouseButtonDown(0)) + try { - isResizing = true; - m_currentWindow = ID; - m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height); - } - else if (!Input.GetMouseButton(0)) - { - isResizing = false; - } + GUILayout.BeginHorizontal(null); + GUILayout.Space(_rect.width - 35); - if (isResizing && ID == m_currentWindow) - { - _rect.width = Mathf.Max(100, m_currentResize.width + (mouse.x - m_currentResize.x)); - _rect.height = Mathf.Max(100, m_currentResize.height + (mouse.y - m_currentResize.y)); - _rect.xMax = Mathf.Min(Screen.width, _rect.xMax); // modifying xMax affects width, not x - _rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y - } + GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Width(25), GUILayout.Height(25) }); - GUILayout.EndHorizontal(); + var r = GUILayoutUtility.GetLastRect(); + + Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)); + + if (r.Contains(mouse) && Input.GetMouseButtonDown(0)) + { + isResizing = true; + m_currentWindow = ID; + m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height); + } + else if (!Input.GetMouseButton(0)) + { + isResizing = false; + } + + if (isResizing && ID == m_currentWindow) + { + _rect.width = Mathf.Max(100, m_currentResize.width + (mouse.x - m_currentResize.x)); + _rect.height = Mathf.Max(100, m_currentResize.height + (mouse.y - m_currentResize.y)); + _rect.xMax = Mathf.Min(Screen.width, _rect.xMax); // modifying xMax affects width, not x + _rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y + } + + GUILayout.EndHorizontal(); + } + catch { } return _rect; } - - // ============ GENERATED WINDOW HOLDER ============ - - public abstract class UIWindow - { - public abstract Il2CppSystem.String Name { get; set; } - - public object Target; - - public int windowID; - public Rect m_rect = new Rect(0, 0, 550, 700); - - public Vector2 scroll = Vector2.zero; - - public static UIWindow CreateWindow(object target) where T: UIWindow - { - //var component = (UIWindow)AddToGameObject(Instance.gameObject); - var component = Activator.CreateInstance(); - - component.Target = target; - component.windowID = NextWindowID(); - component.m_rect = GetNewWindowRect(); - - Windows.Add(component); - - component.Init(); - - return component; - } - - public void DestroyWindow() - { - try - { - Windows.Remove(this); - } - catch (Exception e) - { - MelonLogger.Log("Exception removing Window from WindowManager.Windows list!"); - MelonLogger.Log($"{e.GetType()} : {e.Message}\r\n{e.StackTrace}"); - } - //Destroy(this); - } - - public abstract void Init(); - public abstract void WindowFunction(int windowID); - public abstract void Update(); - - public void OnGUI() - { - if (CppExplorer.ShowMenu) - { - var origSkin = GUI.skin; - - GUI.skin = UIStyles.WindowSkin; - m_rect = GUI.Window(windowID, m_rect, (GUI.WindowFunction)WindowFunction, Name); - - GUI.skin = origSkin; - } - } - - public void Header() - { - GUI.DragWindow(new Rect(0, 0, m_rect.width - 90, 20)); - - if (GUI.Button(new Rect(m_rect.width - 90, 2, 80, 20), "X")) - { - DestroyWindow(); - return; - } - } - } } } diff --git a/src_2018/CppExplorer.cs b/src_2018/CppExplorer.cs index 1f9b293..4ca579c 100644 --- a/src_2018/CppExplorer.cs +++ b/src_2018/CppExplorer.cs @@ -15,8 +15,8 @@ namespace Explorer // consts public const string ID = "com.sinai.cppexplorer"; - public const string NAME = "IL2CPP Runtime Explorer"; - public const string VERSION = "1.3.0"; + public const string NAME = "IL2CPP Runtime Explorer (Unity 2018)"; + public const string VERSION = "1.3.1"; public const string AUTHOR = "Sinai"; // fields @@ -28,7 +28,7 @@ namespace Explorer // props public static bool ShowMenu { get; set; } = false; - public static int ArrayLimit { get; set; } = 100; + public static int ArrayLimit { get; set; } = 20; public bool MouseInspect { get; set; } = false; public static string ActiveSceneName diff --git a/src_2018/CppExplorer.csproj b/src_2018/CppExplorer.csproj index 1657b75..01acd15 100644 --- a/src_2018/CppExplorer.csproj +++ b/src_2018/CppExplorer.csproj @@ -7,8 +7,8 @@ {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D} Library Properties - CppExplorer - CppExplorer_2018 + Explorer + CppExplorer_Unity2018 v4.7.2 512 true @@ -105,9 +105,14 @@ + + + + + diff --git a/src_2018/Inspectors/GameObjectWindow.cs b/src_2018/Inspectors/GameObjectWindow.cs index 16d265e..c3d8f66 100644 --- a/src_2018/Inspectors/GameObjectWindow.cs +++ b/src_2018/Inspectors/GameObjectWindow.cs @@ -9,9 +9,9 @@ using UnityEngine.SceneManagement; namespace Explorer { - public class GameObjectWindow : WindowManager.UIWindow + public class GameObjectWindow : UIWindow { - public override Il2CppSystem.String Name { get => "GameObject Inspector"; set => Name = value; } + public override string Name { get => "GameObject Inspector"; set => Name = value; } public GameObject m_object; diff --git a/src_2018/Inspectors/Reflection/FieldInfoHolder.cs b/src_2018/Inspectors/Reflection/FieldInfoHolder.cs new file mode 100644 index 0000000..4963a45 --- /dev/null +++ b/src_2018/Inspectors/Reflection/FieldInfoHolder.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using MelonLoader; +using UnhollowerBaseLib; + +namespace Explorer +{ + public class FieldInfoHolder : MemberInfoHolder + { + public FieldInfo fieldInfo; + public object m_value; + + public FieldInfoHolder(Type _type, FieldInfo _fieldInfo) + { + classType = _type; + fieldInfo = _fieldInfo; + } + + public override void UpdateValue(object obj) + { + m_value = fieldInfo.GetValue(fieldInfo.IsStatic ? null : obj); + } + + public override void Draw(ReflectionWindow window) + { + UIStyles.DrawMember(ref m_value, ref this.IsExpanded, ref this.arrayOffset, this.fieldInfo, window.m_rect, window.m_object, SetValue); + } + + public override void SetValue(object obj) + { + if (fieldInfo.FieldType.IsEnum) + { + if (System.Enum.Parse(fieldInfo.FieldType, m_value.ToString()) is object enumValue && enumValue != null) + { + m_value = enumValue; + } + } + else if (fieldInfo.FieldType.IsPrimitive) + { + if (fieldInfo.FieldType == typeof(float)) + { + if (float.TryParse(m_value.ToString(), out float f)) + { + m_value = f; + } + else + { + MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!"); + } + } + else if (fieldInfo.FieldType == typeof(double)) + { + if (double.TryParse(m_value.ToString(), out double d)) + { + m_value = d; + } + else + { + MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!"); + } + } + else if (fieldInfo.FieldType != typeof(bool)) + { + if (int.TryParse(m_value.ToString(), out int i)) + { + m_value = i; + } + else + { + MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + fieldInfo.FieldType); + } + } + } + + fieldInfo.SetValue(fieldInfo.IsStatic ? null : obj, m_value); + } + } +} diff --git a/src_2018/Inspectors/Reflection/MemberInfoHolder.cs b/src_2018/Inspectors/Reflection/MemberInfoHolder.cs new file mode 100644 index 0000000..8df7f83 --- /dev/null +++ b/src_2018/Inspectors/Reflection/MemberInfoHolder.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Explorer +{ + public abstract class MemberInfoHolder + { + public Type classType; + public bool IsExpanded = false; + public int arrayOffset = 0; + + public abstract void Draw(ReflectionWindow window); + public abstract void UpdateValue(object obj); + public abstract void SetValue(object obj); + } +} diff --git a/src_2018/Inspectors/Reflection/PropertyInfoHolder.cs b/src_2018/Inspectors/Reflection/PropertyInfoHolder.cs new file mode 100644 index 0000000..41f2a16 --- /dev/null +++ b/src_2018/Inspectors/Reflection/PropertyInfoHolder.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using MelonLoader; +using UnhollowerBaseLib; + +namespace Explorer +{ + public class PropertyInfoHolder : MemberInfoHolder + { + public PropertyInfo propInfo; + public object m_value; + + public PropertyInfoHolder(Type _type, PropertyInfo _propInfo) + { + classType = _type; + propInfo = _propInfo; + } + + public override void Draw(ReflectionWindow window) + { + UIStyles.DrawMember(ref m_value, ref this.IsExpanded, ref this.arrayOffset, this.propInfo, window.m_rect, window.m_object, SetValue); + } + + public override void UpdateValue(object obj) + { + try + { + if (obj is Il2CppSystem.Object ilObject) + { + var declaringType = this.propInfo.DeclaringType; + if (declaringType == typeof(Il2CppObjectBase)) + { + m_value = ilObject.Pointer; + } + else + { + var cast = CppExplorer.Il2CppCast(obj, declaringType); + m_value = this.propInfo.GetValue(cast, null); + } + } + else + { + m_value = this.propInfo.GetValue(obj, null); + } + } + catch (Exception e) + { + MelonLogger.Log("Exception on PropertyInfoHolder.UpdateValue, Name: " + this.propInfo.Name); + MelonLogger.Log(e.GetType() + ", " + e.Message); + + var inner = e.InnerException; + while (inner != null) + { + MelonLogger.Log("inner: " + inner.GetType() + ", " + inner.Message); + inner = inner.InnerException; + } + + m_value = null; + } + } + + public override void SetValue(object obj) + { + try + { + if (propInfo.PropertyType.IsEnum) + { + if (System.Enum.Parse(propInfo.PropertyType, m_value.ToString()) is object enumValue && enumValue != null) + { + m_value = enumValue; + } + } + else if (propInfo.PropertyType.IsPrimitive) + { + if (propInfo.PropertyType == typeof(float)) + { + if (float.TryParse(m_value.ToString(), out float f)) + { + m_value = f; + } + else + { + MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!"); + } + } + else if (propInfo.PropertyType == typeof(double)) + { + if (double.TryParse(m_value.ToString(), out double d)) + { + m_value = d; + } + else + { + MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!"); + } + } + else if (propInfo.PropertyType != typeof(bool)) + { + if (int.TryParse(m_value.ToString(), out int i)) + { + m_value = i; + } + else + { + MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + propInfo.PropertyType); + } + } + } + + var declaring = propInfo.DeclaringType; + var cast = CppExplorer.Il2CppCast(obj, declaring); + + propInfo.SetValue(propInfo.GetAccessors()[0].IsStatic ? null : cast, m_value, null); + } + catch + { + //MelonLogger.Log("Exception trying to set property " + this.propInfo.Name); + } + } + } +} diff --git a/src_2018/Inspectors/ReflectionWindow.cs b/src_2018/Inspectors/ReflectionWindow.cs index aafb519..b1a4405 100644 --- a/src_2018/Inspectors/ReflectionWindow.cs +++ b/src_2018/Inspectors/ReflectionWindow.cs @@ -10,9 +10,9 @@ using UnityEngine; namespace Explorer { - public class ReflectionWindow : WindowManager.UIWindow + public class ReflectionWindow : UIWindow { - public override Il2CppSystem.String Name { get => "Object Reflection"; set => Name = value; } + public override string Name { get => "Object Reflection"; set => Name = value; } public Type m_objectType; public object m_object; @@ -351,204 +351,5 @@ namespace Explorer } } } - - - /* ********************* - * PROPERTYINFO HOLDER - */ - - public class PropertyInfoHolder - { - public Type classType; - public PropertyInfo propInfo; - public object m_value; - public bool IsExpanded; - - public PropertyInfoHolder(Type _type, PropertyInfo _propInfo) - { - classType = _type; - propInfo = _propInfo; - } - - public void Draw(ReflectionWindow window) - { - UIStyles.DrawMember(ref m_value, ref this.IsExpanded, this.propInfo, window.m_rect, window.m_object, SetValue); - } - - public void UpdateValue(object obj) - { - try - { - if (obj is Il2CppSystem.Object ilObject) - { - var declaringType = this.propInfo.DeclaringType; - if (declaringType == typeof(Il2CppObjectBase)) - { - m_value = ilObject.Pointer; - } - else - { - var cast = CppExplorer.Il2CppCast(obj, declaringType); - m_value = this.propInfo.GetValue(cast, null); - } - } - else - { - m_value = this.propInfo.GetValue(obj, null); - } - } - catch (Exception e) - { - MelonLogger.Log("Exception on PropertyInfoHolder.UpdateValue, Name: " + this.propInfo.Name); - MelonLogger.Log(e.GetType() + ", " + e.Message); - - var inner = e.InnerException; - while (inner != null) - { - MelonLogger.Log("inner: " + inner.GetType() + ", " + inner.Message); - inner = inner.InnerException; - } - - m_value = null; - } - } - - public void SetValue(object obj) - { - try - { - if (propInfo.PropertyType.IsEnum) - { - if (System.Enum.Parse(propInfo.PropertyType, m_value.ToString()) is object enumValue && enumValue != null) - { - m_value = enumValue; - } - } - else if (propInfo.PropertyType.IsPrimitive) - { - if (propInfo.PropertyType == typeof(float)) - { - if (float.TryParse(m_value.ToString(), out float f)) - { - m_value = f; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!"); - } - } - else if (propInfo.PropertyType == typeof(double)) - { - if (double.TryParse(m_value.ToString(), out double d)) - { - m_value = d; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!"); - } - } - else if (propInfo.PropertyType != typeof(bool)) - { - if (int.TryParse(m_value.ToString(), out int i)) - { - m_value = i; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + propInfo.PropertyType); - } - } - } - - var declaring = propInfo.DeclaringType; - var cast = CppExplorer.Il2CppCast(obj, declaring); - - propInfo.SetValue(propInfo.GetAccessors()[0].IsStatic ? null : cast, m_value, null); - } - catch - { - //MelonLogger.Log("Exception trying to set property " + this.propInfo.Name); - } - } - } - - - /* ********************* - * FIELDINFO HOLDER - */ - - public class FieldInfoHolder - { - public Type classType; - public FieldInfo fieldInfo; - public object m_value; - public bool IsExpanded; - - public FieldInfoHolder(Type _type, FieldInfo _fieldInfo) - { - classType = _type; - fieldInfo = _fieldInfo; - } - - public void UpdateValue(object obj) - { - m_value = fieldInfo.GetValue(fieldInfo.IsStatic ? null : obj); - } - - public void Draw(ReflectionWindow window) - { - UIStyles.DrawMember(ref m_value, ref this.IsExpanded, this.fieldInfo, window.m_rect, window.m_object, SetValue); - } - - public void SetValue(object obj) - { - if (fieldInfo.FieldType.IsEnum) - { - if (System.Enum.Parse(fieldInfo.FieldType, m_value.ToString()) is object enumValue && enumValue != null) - { - m_value = enumValue; - } - } - else if (fieldInfo.FieldType.IsPrimitive) - { - if (fieldInfo.FieldType == typeof(float)) - { - if (float.TryParse(m_value.ToString(), out float f)) - { - m_value = f; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!"); - } - } - else if (fieldInfo.FieldType == typeof(double)) - { - if (double.TryParse(m_value.ToString(), out double d)) - { - m_value = d; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!"); - } - } - else if (fieldInfo.FieldType != typeof(bool)) - { - if (int.TryParse(m_value.ToString(), out int i)) - { - m_value = i; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + fieldInfo.FieldType); - } - } - } - - fieldInfo.SetValue(fieldInfo.IsStatic ? null : obj, m_value); - } - } } } diff --git a/src_2018/Inspectors/UIWindow.cs b/src_2018/Inspectors/UIWindow.cs new file mode 100644 index 0000000..7831d8f --- /dev/null +++ b/src_2018/Inspectors/UIWindow.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Harmony; +using MelonLoader; +using UnhollowerBaseLib; +using UnhollowerRuntimeLib; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.EventSystems; + +namespace Explorer +{ + public abstract class UIWindow + { + public abstract string Name { get; set; } + + public object Target; + + public int windowID; + public Rect m_rect = new Rect(0, 0, 550, 700); + + public Vector2 scroll = Vector2.zero; + + public static UIWindow CreateWindow(object target) where T : UIWindow + { + //var component = (UIWindow)AddToGameObject(Instance.gameObject); + var window = Activator.CreateInstance(); + + window.Target = target; + window.windowID = WindowManager.NextWindowID(); + window.m_rect = WindowManager.GetNewWindowRect(); + + WindowManager.Windows.Add(window); + + window.Init(); + + return window; + } + + public void DestroyWindow() + { + try + { + WindowManager.Windows.Remove(this); + } + catch (Exception e) + { + MelonLogger.Log("Exception removing Window from WindowManager.Windows list!"); + MelonLogger.Log($"{e.GetType()} : {e.Message}\r\n{e.StackTrace}"); + } + //Destroy(this); + } + + public abstract void Init(); + public abstract void WindowFunction(int windowID); + public abstract void Update(); + + public void OnGUI() + { + if (CppExplorer.ShowMenu) + { + var origSkin = GUI.skin; + + GUI.skin = UIStyles.WindowSkin; + m_rect = GUI.Window(windowID, m_rect, (GUI.WindowFunction)WindowFunction, Name); + + GUI.skin = origSkin; + } + } + + public void Header() + { + GUI.DragWindow(new Rect(0, 0, m_rect.width - 90, 20)); + + if (GUI.Button(new Rect(m_rect.width - 90, 2, 80, 20), "X")) + { + DestroyWindow(); + return; + } + } + } +} diff --git a/src_2018/MainMenu/MainMenu.cs b/src_2018/MainMenu/MainMenu.cs index 7488cef..ca708d2 100644 --- a/src_2018/MainMenu/MainMenu.cs +++ b/src_2018/MainMenu/MainMenu.cs @@ -119,18 +119,5 @@ namespace Explorer GUILayout.Space(10); GUI.color = Color.white; } - - public abstract class WindowPage - { - public virtual string Name { get; set; } - - public Vector2 scroll = Vector2.zero; - - public abstract void Init(); - - public abstract void DrawWindow(); - - public abstract void Update(); - } } } diff --git a/src_2018/MainMenu/Pages/ConsolePage.cs b/src_2018/MainMenu/Pages/ConsolePage.cs index ac583ed..cee0eef 100644 --- a/src_2018/MainMenu/Pages/ConsolePage.cs +++ b/src_2018/MainMenu/Pages/ConsolePage.cs @@ -12,9 +12,9 @@ using MelonLoader; namespace Explorer { - public class ConsolePage : MainMenu.WindowPage + public class ConsolePage : WindowPage { - public override string Name { get => "Console"; set => base.Name = value; } + public override string Name { get => "C# Console"; set => base.Name = value; } private ScriptEvaluator _evaluator; private readonly StringBuilder _sb = new StringBuilder(); @@ -41,13 +41,18 @@ namespace Explorer try { - MethodInput = @"// This is a basic REPL console used to execute a method. -// Some common directives are added by default, you can add more below. + MethodInput = @"// This is a basic C# REPL console. +// Some common using directives are added by default, you can add more below. // If you want to return some output, MelonLogger.Log() it. MelonLogger.Log(""hello world"");"; ResetConsole(); + + foreach (var use in m_defaultUsing) + { + AddUsing(use); + } } catch (Exception e) { @@ -65,11 +70,6 @@ MelonLogger.Log(""hello world"");"; _evaluator = new ScriptEvaluator(new StringWriter(_sb)) { InteractiveBaseClass = typeof(REPL) }; UsingDirectives = new List(); - UsingDirectives.AddRange(m_defaultUsing); - foreach (string asm in UsingDirectives) - { - Evaluate(AsmToUsing(asm)); - } } public string AsmToUsing(string asm, bool richtext = false) @@ -111,7 +111,7 @@ MelonLogger.Log(""hello world"");"; public override void DrawWindow() { - GUILayout.Label("REPL Console", null); + GUILayout.Label("C# REPL Console", null); GUILayout.Label("Method:", null); MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.Height(300) }); diff --git a/src_2018/MainMenu/Pages/ScenePage.cs b/src_2018/MainMenu/Pages/ScenePage.cs index 431b03c..d53f6b7 100644 --- a/src_2018/MainMenu/Pages/ScenePage.cs +++ b/src_2018/MainMenu/Pages/ScenePage.cs @@ -8,7 +8,7 @@ using UnityEngine.SceneManagement; namespace Explorer { - public class ScenePage : MainMenu.WindowPage + public class ScenePage : WindowPage { public static ScenePage Instance; @@ -45,7 +45,7 @@ namespace Explorer public override void Update() { - if (Time.time - m_timeOfLastUpdate < 1f) + if (Time.time - m_timeOfLastUpdate < 0.2f) { return; } diff --git a/src_2018/MainMenu/Pages/SearchPage.cs b/src_2018/MainMenu/Pages/SearchPage.cs index 3bbd0a1..9a632ca 100644 --- a/src_2018/MainMenu/Pages/SearchPage.cs +++ b/src_2018/MainMenu/Pages/SearchPage.cs @@ -12,15 +12,16 @@ using UnhollowerBaseLib; namespace Explorer { - public class SearchPage : MainMenu.WindowPage + public class SearchPage : WindowPage { public static SearchPage Instance; - public override string Name { get => "Advanced Search"; set => base.Name = value; } + public override string Name { get => "Object Search"; set => base.Name = value; } private string m_searchInput = ""; private string m_typeInput = ""; private int m_limit = 100; + private int m_pageOffset = 0; public SceneFilter SceneMode = SceneFilter.Any; public TypeFilter TypeMode = TypeFilter.Object; @@ -52,6 +53,7 @@ namespace Explorer public void OnSceneChange() { m_searchResults.Clear(); + m_pageOffset = 0; } public override void Update() @@ -68,6 +70,7 @@ namespace Explorer if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) })) { m_searchResults = GetInstanceClassScanner().ToList(); + m_pageOffset = 0; } GUILayout.EndHorizontal(); @@ -78,21 +81,59 @@ namespace Explorer GUILayout.BeginVertical(GUI.skin.box, null); GUI.skin.label.alignment = TextAnchor.MiddleCenter; - GUILayout.Label("Results", null); + GUILayout.Label("Results " + " (" + m_searchResults.Count + ")", null); GUI.skin.label.alignment = TextAnchor.UpperLeft; + int count = m_searchResults.Count; + + if (count > CppExplorer.ArrayLimit) + { + // prev/next page buttons + GUILayout.BeginHorizontal(null); + int maxOffset = (int)Mathf.Ceil(count / CppExplorer.ArrayLimit); + if (GUILayout.Button("< Prev", null)) + { + if (m_pageOffset > 0) m_pageOffset--; + } + + GUILayout.Label($"Page {m_pageOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) }); + + if (GUILayout.Button("Next >", null)) + { + if (m_pageOffset < maxOffset) m_pageOffset++; + } + GUILayout.EndHorizontal(); + } + resultsScroll = GUILayout.BeginScrollView(resultsScroll, GUI.skin.scrollView); var _temprect = new Rect(MainMenu.MainRect.x, MainMenu.MainRect.y, MainMenu.MainRect.width + 160, MainMenu.MainRect.height); if (m_searchResults.Count > 0) { + int offset = m_pageOffset * CppExplorer.ArrayLimit; + int preiterated = 0; + + if (offset >= count) offset = 0; + for (int i = 0; i < m_searchResults.Count; i++) { + if (offset > 0 && preiterated < offset) + { + preiterated++; + continue; + } + + if (i - offset > CppExplorer.ArrayLimit - 1) + { + break; + } + var obj = m_searchResults[i]; bool _ = false; - UIStyles.DrawValue(ref obj, _temprect, ref _); + int __ = 0; + UIStyles.DrawValue(ref obj, ref _, ref __, _temprect); } } else @@ -125,7 +166,7 @@ namespace Explorer m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) }); GUI.skin.label.alignment = TextAnchor.MiddleRight; - GUILayout.Label("Result limit:", new GUILayoutOption[] { GUILayout.Width(100) }); + GUILayout.Label("Results per page:", new GUILayoutOption[] { GUILayout.Width(120) }); var resultinput = m_limit.ToString(); resultinput = GUILayout.TextField(resultinput, new GUILayoutOption[] { GUILayout.Width(55) }); if (int.TryParse(resultinput, out int _i) && _i > 0) @@ -289,26 +330,9 @@ namespace Explorer } var matches = new List(); - int added = 0; - - //MelonLogger.Log("Trying to get IL Type. ASM name: " + type.Assembly.GetName().Name + ", Namespace: " + type.Namespace + ", name: " + type.Name); - - //var asmName = type.Assembly.GetName().Name; - //if (asmName.Contains("UnityEngine")) - //{ - // asmName = "UnityEngine"; - //} - - //var intPtr = IL2CPP.GetIl2CppClass(asmName, type.Namespace, type.Name); - //var ilType = Il2CppType.TypeFromPointer(intPtr); foreach (var obj in Resources.FindObjectsOfTypeAll(type)) { - if (added == m_limit) - { - break; - } - if (_search != "" && !obj.name.ToLower().Contains(_search.ToLower())) { continue; @@ -360,7 +384,6 @@ namespace Explorer if (!matches.Contains(obj)) { matches.Add(obj); - added++; } } diff --git a/src_2018/MainMenu/Pages/WindowPage.cs b/src_2018/MainMenu/Pages/WindowPage.cs new file mode 100644 index 0000000..b4c056b --- /dev/null +++ b/src_2018/MainMenu/Pages/WindowPage.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace Explorer +{ + public abstract class WindowPage + { + public virtual string Name { get; set; } + + public Vector2 scroll = Vector2.zero; + + public abstract void Init(); + + public abstract void DrawWindow(); + + public abstract void Update(); + } +} diff --git a/src_2018/UIStyles.cs b/src_2018/UIStyles.cs index b7b1821..8c2ebef 100644 --- a/src_2018/UIStyles.cs +++ b/src_2018/UIStyles.cs @@ -196,7 +196,7 @@ namespace Explorer GUILayout.EndHorizontal(); } - public static void DrawMember(ref object value, ref bool isExpanded, MemberInfo memberInfo, Rect rect, object setTarget = null, Action setAction = null, float labelWidth = 180, bool autoSet = false) + public static void DrawMember(ref object value, ref bool isExpanded, ref int arrayOffset, MemberInfo memberInfo, Rect rect, object setTarget = null, Action setAction = null, float labelWidth = 180, bool autoSet = false) { GUILayout.Label("" + memberInfo.Name + ":", new GUILayoutOption[] { GUILayout.Width(labelWidth) }); @@ -213,10 +213,10 @@ namespace Explorer canWrite = pi.CanWrite; } - DrawValue(ref value, rect, ref isExpanded, valueType, (canWrite ? setTarget : null), (canWrite ? setAction : null), autoSet); + DrawValue(ref value, ref isExpanded, ref arrayOffset, rect, valueType, (canWrite ? setTarget : null), (canWrite ? setAction : null), autoSet); } - public static void DrawValue(ref object value, Rect rect, ref bool isExpanded, string nullValueType = null, object setTarget = null, Action setAction = null, bool autoSet = false) + public static void DrawValue(ref object value, ref bool isExpanded, ref int arrayOffset, Rect rect, string nullValueType = null, object setTarget = null, Action setAction = null, bool autoSet = false) { if (value == null) { @@ -304,12 +304,41 @@ namespace Explorer if (isExpanded) { + if (count > CppExplorer.ArrayLimit) + { + GUILayout.EndHorizontal(); + GUILayout.BeginHorizontal(null); + GUILayout.Space(190); + int maxOffset = (int)Mathf.Ceil(count / CppExplorer.ArrayLimit); + GUILayout.Label($"Page {arrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) }); + // prev/next page buttons + if (GUILayout.Button("< Prev", null)) + { + if (arrayOffset > 0) arrayOffset--; + } + if (GUILayout.Button("Next >", null)) + { + if (arrayOffset < maxOffset) arrayOffset++; + } + } + + int offset = arrayOffset * CppExplorer.ArrayLimit; + + if (offset >= count) offset = 0; + var enumerator = enumerable.GetEnumerator(); if (enumerator != null) { int i = 0; + int preiterated = 0; while (enumerator.MoveNext()) { + if (offset > 0 && preiterated < offset) + { + preiterated++; + continue; + } + var obj = enumerator.Current; //collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry @@ -317,9 +346,9 @@ namespace Explorer GUILayout.BeginHorizontal(null); GUILayout.Space(190); - if (i > CppExplorer.ArrayLimit) + if (i > CppExplorer.ArrayLimit - 1) { - GUILayout.Label($"{count - CppExplorer.ArrayLimit} results omitted, array is too long!", null); + //GUILayout.Label($"{count - CppExplorer.ArrayLimit} results omitted, array is too long!", null); break; } @@ -330,7 +359,8 @@ namespace Explorer else { var type = obj.GetType(); - var lbl = i + ": " + obj.ToString() + ""; + + var lbl = (i + offset) + ": " + obj.ToString() + ""; if (type.IsPrimitive || typeof(string).IsAssignableFrom(type)) { diff --git a/src_2018/WindowManager.cs b/src_2018/WindowManager.cs index 547b44a..49d3fd8 100644 --- a/src_2018/WindowManager.cs +++ b/src_2018/WindowManager.cs @@ -1,5 +1,4 @@ using System; -using System.CodeDom; using System.Collections.Generic; using System.Linq; using System.Text; @@ -142,117 +141,49 @@ namespace Explorer // ============= Resize Window Helper ============ - //static readonly GUIContent gcDrag = new GUIContent("<->"); - - //private static bool isResizing = false; - //private static Rect m_currentResize; - //private static int m_currentWindow; + // static readonly GUIContent gcDrag = new GUIContent("<->", "drag to resize"); + private static readonly GUIContent gcDrag = new GUIContent("<->"); + private static bool isResizing = false; + private static Rect m_currentResize; + private static int m_currentWindow; public static Rect ResizeWindow(Rect _rect, int ID) { - //GUILayout.BeginHorizontal(null); - //GUILayout.Space(_rect.width - 35); + try + { + GUILayout.BeginHorizontal(null); + GUILayout.Space(_rect.width - 35); - //GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Width(25), GUILayout.Height(25) }); + GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Width(25), GUILayout.Height(25) }); - //var r = GUILayoutUtility.GetLastRect(); + var r = GUILayoutUtility.GetLastRect(); - //Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)); + Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)); - //if (r.Contains(mouse) && Input.GetMouseButtonDown(0)) - //{ - // isResizing = true; - // m_currentWindow = ID; - // m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height); - //} - //else if (!Input.GetMouseButton(0)) - //{ - // isResizing = false; - //} + if (r.Contains(mouse) && Input.GetMouseButtonDown(0)) + { + isResizing = true; + m_currentWindow = ID; + m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height); + } + else if (!Input.GetMouseButton(0)) + { + isResizing = false; + } - //if (isResizing && ID == m_currentWindow) - //{ - // _rect.width = Mathf.Max(100, m_currentResize.width + (mouse.x - m_currentResize.x)); - // _rect.height = Mathf.Max(100, m_currentResize.height + (mouse.y - m_currentResize.y)); - // _rect.xMax = Mathf.Min(Screen.width, _rect.xMax); // modifying xMax affects width, not x - // _rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y - //} + if (isResizing && ID == m_currentWindow) + { + _rect.width = Mathf.Max(100, m_currentResize.width + (mouse.x - m_currentResize.x)); + _rect.height = Mathf.Max(100, m_currentResize.height + (mouse.y - m_currentResize.y)); + _rect.xMax = Mathf.Min(Screen.width, _rect.xMax); // modifying xMax affects width, not x + _rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y + } - //GUILayout.EndHorizontal(); + GUILayout.EndHorizontal(); + } + catch { } return _rect; } - - // ============ GENERATED WINDOW HOLDER ============ - - public abstract class UIWindow - { - public abstract Il2CppSystem.String Name { get; set; } - - public object Target; - - public int windowID; - public Rect m_rect = new Rect(0, 0, 550, 700); - - public Vector2 scroll = Vector2.zero; - - public static UIWindow CreateWindow(object target) where T: UIWindow - { - //var component = (UIWindow)AddToGameObject(Instance.gameObject); - var component = Activator.CreateInstance(); - - component.Target = target; - component.windowID = NextWindowID(); - component.m_rect = GetNewWindowRect(); - - Windows.Add(component); - - component.Init(); - - return component; - } - - public void DestroyWindow() - { - try - { - Windows.Remove(this); - } - catch (Exception e) - { - MelonLogger.Log("Exception removing Window from WindowManager.Windows list!"); - MelonLogger.Log($"{e.GetType()} : {e.Message}\r\n{e.StackTrace}"); - } - //Destroy(this); - } - - public abstract void Init(); - public abstract void WindowFunction(int windowID); - public abstract void Update(); - - public void OnGUI() - { - if (CppExplorer.ShowMenu) - { - var origSkin = GUI.skin; - - GUI.skin = UIStyles.WindowSkin; - m_rect = GUI.Window(windowID, m_rect, (GUI.WindowFunction)WindowFunction, Name); - - GUI.skin = origSkin; - } - } - - public void Header() - { - GUI.DragWindow(new Rect(0, 0, m_rect.width - 90, 20)); - - if (GUI.Button(new Rect(m_rect.width - 90, 2, 80, 20), "X")) - { - DestroyWindow(); - return; - } - } - } } }