diff --git a/src/CSConsole/ScriptEvaluator.cs b/src/Core/CSharp/ScriptEvaluator.cs similarity index 96% rename from src/CSConsole/ScriptEvaluator.cs rename to src/Core/CSharp/ScriptEvaluator.cs index 8a8824a..0127140 100644 --- a/src/CSConsole/ScriptEvaluator.cs +++ b/src/Core/CSharp/ScriptEvaluator.cs @@ -4,9 +4,9 @@ using System.IO; using System.Reflection; using Mono.CSharp; -// Thanks to ManlyMarco for this +// Thanks to ManlyMarco for most of this -namespace UnityExplorer.CSConsole +namespace UnityExplorer.Core.CSharp { public class ScriptEvaluator : Evaluator, IDisposable { diff --git a/src/CSConsole/ScriptInteraction.cs b/src/Core/CSharp/ScriptInteraction.cs similarity index 81% rename from src/CSConsole/ScriptInteraction.cs rename to src/Core/CSharp/ScriptInteraction.cs index 8efa1ee..142eb74 100644 --- a/src/CSConsole/ScriptInteraction.cs +++ b/src/Core/CSharp/ScriptInteraction.cs @@ -1,10 +1,10 @@ using System; using Mono.CSharp; using UnityExplorer.UI; -using UnityExplorer.UI.Modules; -using UnityExplorer.Inspectors; +using UnityExplorer.UI.Main; +using UnityExplorer.Core.Inspectors; -namespace UnityExplorer.CSConsole +namespace UnityExplorer.Core.CSharp { public class ScriptInteraction : InteractiveBase { @@ -15,17 +15,17 @@ namespace UnityExplorer.CSConsole public static void AddUsing(string directive) { - CSConsolePage.Instance.AddUsing(directive); + CSharpConsole.Instance.AddUsing(directive); } public static void GetUsing() { - ExplorerCore.Log(CSConsolePage.Instance.m_evaluator.GetUsing()); + ExplorerCore.Log(CSharpConsole.Instance.m_evaluator.GetUsing()); } public static void Reset() { - CSConsolePage.Instance.ResetConsole(); + CSharpConsole.Instance.ResetConsole(); } public static object CurrentTarget() diff --git a/src/CSConsole/Suggestion.cs b/src/Core/CSharp/Suggestion.cs similarity index 89% rename from src/CSConsole/Suggestion.cs rename to src/Core/CSharp/Suggestion.cs index caa632f..bc27568 100644 --- a/src/CSConsole/Suggestion.cs +++ b/src/Core/CSharp/Suggestion.cs @@ -3,9 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEngine; -using UnityExplorer.Helpers; +using UnityExplorer.Core; +using UnityExplorer.Core.Unity; +using UnityExplorer.UI.CSConsole; -namespace UnityExplorer.CSConsole +namespace UnityExplorer.Core.CSharp { public struct Suggestion { @@ -48,7 +50,7 @@ namespace UnityExplorer.CSConsole public static HashSet Namespaces => m_namspaces ?? GetNamespaces(); private static HashSet m_namspaces; - public static HashSet Keywords => m_keywords ?? (m_keywords = new HashSet(CSharpLexer.validKeywordMatcher.Keywords)); + public static HashSet Keywords => m_keywords ?? (m_keywords = new HashSet(CSLexerHighlighter.validKeywordMatcher.Keywords)); private static HashSet m_keywords; private static readonly Color keywordColor = new Color(80f / 255f, 150f / 255f, 215f / 255f); diff --git a/src/Config/ExplorerConfig.cs b/src/Core/Config/ExplorerConfig.cs similarity index 99% rename from src/Config/ExplorerConfig.cs rename to src/Core/Config/ExplorerConfig.cs index 0784494..c62a999 100644 --- a/src/Config/ExplorerConfig.cs +++ b/src/Core/Config/ExplorerConfig.cs @@ -5,7 +5,7 @@ using IniParser; using IniParser.Parser; using UnityExplorer.UI; -namespace UnityExplorer.Config +namespace UnityExplorer.Core.Config { public class ExplorerConfig { diff --git a/src/Input/IHandleInput.cs b/src/Core/Input/IHandleInput.cs similarity index 92% rename from src/Input/IHandleInput.cs rename to src/Core/Input/IHandleInput.cs index 1422e03..f51eb26 100644 --- a/src/Input/IHandleInput.cs +++ b/src/Core/Input/IHandleInput.cs @@ -1,7 +1,7 @@ using UnityEngine; using UnityEngine.EventSystems; -namespace UnityExplorer.Input +namespace UnityExplorer.Core.Input { public interface IHandleInput { diff --git a/src/Input/InputManager.cs b/src/Core/Input/InputManager.cs similarity index 83% rename from src/Input/InputManager.cs rename to src/Core/Input/InputManager.cs index 563a63f..5a97992 100644 --- a/src/Input/InputManager.cs +++ b/src/Core/Input/InputManager.cs @@ -1,13 +1,9 @@ using System; using UnityEngine; -using UnityExplorer.Helpers; using System.Diagnostics.CodeAnalysis; using UnityEngine.EventSystems; -#if CPP -using UnhollowerBaseLib; -#endif -namespace UnityExplorer.Input +namespace UnityExplorer.Core.Input { public enum InputType { @@ -43,12 +39,12 @@ namespace UnityExplorer.Input public static void Init() { - if (InputSystem.TKeyboard != null || (ReflectionHelpers.LoadModule("Unity.InputSystem") && InputSystem.TKeyboard != null)) + if (InputSystem.TKeyboard != null || (ReflectionUtility.LoadModule("Unity.InputSystem") && InputSystem.TKeyboard != null)) { m_inputModule = new InputSystem(); CurrentType = InputType.InputSystem; } - else if (LegacyInput.TInput != null || (ReflectionHelpers.LoadModule("UnityEngine.InputLegacyModule") && LegacyInput.TInput != null)) + else if (LegacyInput.TInput != null || (ReflectionUtility.LoadModule("UnityEngine.InputLegacyModule") && LegacyInput.TInput != null)) { m_inputModule = new LegacyInput(); CurrentType = InputType.Legacy; diff --git a/src/Input/InputSystem.cs b/src/Core/Input/InputSystem.cs similarity index 93% rename from src/Input/InputSystem.cs rename to src/Core/Input/InputSystem.cs index e32427a..27711ec 100644 --- a/src/Input/InputSystem.cs +++ b/src/Core/Input/InputSystem.cs @@ -1,12 +1,12 @@ using System; using System.Reflection; -using UnityExplorer.Helpers; +using UnityExplorer.Core.Unity; using UnityEngine; using UnityEngine.EventSystems; using UnityExplorer.UI; using System.Collections.Generic; -namespace UnityExplorer.Input +namespace UnityExplorer.Core.Input { public class InputSystem : IHandleInput { @@ -17,7 +17,7 @@ namespace UnityExplorer.Input m_kbCurrentProp = TKeyboard.GetProperty("current"); m_kbIndexer = TKeyboard.GetProperty("Item", new Type[] { TKey }); - var btnControl = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Controls.ButtonControl"); + var btnControl = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Controls.ButtonControl"); m_btnIsPressedProp = btnControl.GetProperty("isPressed"); m_btnWasPressedProp = btnControl.GetProperty("wasPressedThisFrame"); @@ -25,21 +25,21 @@ namespace UnityExplorer.Input m_leftButtonProp = TMouse.GetProperty("leftButton"); m_rightButtonProp = TMouse.GetProperty("rightButton"); - m_positionProp = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Pointer") + m_positionProp = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Pointer") .GetProperty("position"); - m_readVector2InputMethod = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.InputControl`1") + m_readVector2InputMethod = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputControl`1") .MakeGenericType(typeof(Vector2)) .GetMethod("ReadValue"); } - public static Type TKeyboard => m_tKeyboard ?? (m_tKeyboard = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Keyboard")); + public static Type TKeyboard => m_tKeyboard ?? (m_tKeyboard = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Keyboard")); private static Type m_tKeyboard; - public static Type TMouse => m_tMouse ?? (m_tMouse = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Mouse")); + public static Type TMouse => m_tMouse ?? (m_tMouse = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Mouse")); private static Type m_tMouse; - public static Type TKey => m_tKey ?? (m_tKey = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Key")); + public static Type TKey => m_tKey ?? (m_tKey = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Key")); private static Type m_tKey; private static PropertyInfo m_btnIsPressedProp; diff --git a/src/Input/LegacyInput.cs b/src/Core/Input/LegacyInput.cs similarity index 94% rename from src/Input/LegacyInput.cs rename to src/Core/Input/LegacyInput.cs index a868ae1..a3c45eb 100644 --- a/src/Input/LegacyInput.cs +++ b/src/Core/Input/LegacyInput.cs @@ -1,11 +1,11 @@ using System; using System.Reflection; -using UnityExplorer.Helpers; +using UnityExplorer.Core.Unity; using UnityEngine; using UnityEngine.EventSystems; using UnityExplorer.UI; -namespace UnityExplorer.Input +namespace UnityExplorer.Core.Input { public class LegacyInput : IHandleInput { @@ -20,7 +20,7 @@ namespace UnityExplorer.Input m_getMouseButtonDownMethod = TInput.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) }); } - public static Type TInput => m_tInput ?? (m_tInput = ReflectionHelpers.GetTypeByName("UnityEngine.Input")); + public static Type TInput => m_tInput ?? (m_tInput = ReflectionUtility.GetTypeByName("UnityEngine.Input")); private static Type m_tInput; private static PropertyInfo m_mousePositionProp; diff --git a/src/Input/NoInput.cs b/src/Core/Input/NoInput.cs similarity index 94% rename from src/Input/NoInput.cs rename to src/Core/Input/NoInput.cs index 1e70357..0ce659c 100644 --- a/src/Input/NoInput.cs +++ b/src/Core/Input/NoInput.cs @@ -1,7 +1,7 @@ using UnityEngine; using UnityEngine.EventSystems; -namespace UnityExplorer.Input +namespace UnityExplorer.Core.Input { // Just a stub for games where no Input module was able to load at all. diff --git a/src/Core/InspectorManager.cs b/src/Core/InspectorManager.cs new file mode 100644 index 0000000..1d3683c --- /dev/null +++ b/src/Core/InspectorManager.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityExplorer.Core.Unity; +using UnityExplorer.UI; +using UnityExplorer.UI.Main; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.UI; +using UnityExplorer.Core.Inspectors.Reflection; +using UnityExplorer.Core.Runtime; +using UnityExplorer.UI.Main.Home; + +namespace UnityExplorer.Core.Inspectors +{ + public class InspectorManager + { + public static InspectorManager Instance { get; private set; } + + internal static InspectorManagerUI UI; + + public InspectorManager() + { + Instance = this; + + UI = new InspectorManagerUI(); + UI.ConstructInspectorPane(); + } + + public InspectorBase m_activeInspector; + public readonly List m_currentInspectors = new List(); + + public void Update() + { + for (int i = 0; i < m_currentInspectors.Count; i++) + { + if (i >= m_currentInspectors.Count) + break; + + m_currentInspectors[i].Update(); + } + } + + public void Inspect(object obj, CacheObjectBase parentMember = null) + { + obj = ReflectionProvider.Instance.Cast(obj, ReflectionProvider.Instance.GetActualType(obj)); + + UnityEngine.Object unityObj = obj as UnityEngine.Object; + + if (obj.IsNullOrDestroyed(false)) + { + return; + } + + // check if currently inspecting this object + foreach (InspectorBase tab in m_currentInspectors) + { + if (ReferenceEquals(obj, tab.Target)) + { + SetInspectorTab(tab); + return; + } +#if CPP + else if (unityObj && tab.Target is UnityEngine.Object uTabObj) + { + if (unityObj.m_CachedPtr == uTabObj.m_CachedPtr) + { + SetInspectorTab(tab); + return; + } + } +#endif + } + + InspectorBase inspector; + if (obj is GameObject go) + inspector = new GameObjectInspector(go); + else + inspector = new InstanceInspector(obj); + + if (inspector is ReflectionInspector ri) + ri.ParentMember = parentMember; + + m_currentInspectors.Add(inspector); + SetInspectorTab(inspector); + } + + public void Inspect(Type type) + { + if (type == null) + { + ExplorerCore.LogWarning("The provided type was null!"); + return; + } + + foreach (var tab in m_currentInspectors.Where(x => x is StaticInspector)) + { + if (ReferenceEquals(tab.Target as Type, type)) + { + SetInspectorTab(tab); + return; + } + } + + var inspector = new StaticInspector(type); + + m_currentInspectors.Add(inspector); + SetInspectorTab(inspector); + } + + public void SetInspectorTab(InspectorBase inspector) + { + MainMenu.Instance.SetPage(HomePage.Instance); + + if (m_activeInspector == inspector) + return; + + UnsetInspectorTab(); + + m_activeInspector = inspector; + inspector.SetActive(); + + UI.OnSetInspectorTab(inspector); + } + + public void UnsetInspectorTab() + { + if (m_activeInspector == null) + return; + + m_activeInspector.SetInactive(); + + UI.OnUnsetInspectorTab(); + + m_activeInspector = null; + } + } +} diff --git a/src/Core/Inspectors/GameObjects/GameObjectInspector.cs b/src/Core/Inspectors/GameObjects/GameObjectInspector.cs new file mode 100644 index 0000000..179df46 --- /dev/null +++ b/src/Core/Inspectors/GameObjects/GameObjectInspector.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityExplorer.UI; +using UnityEngine; +using UnityEngine.UI; +using UnityExplorer.Core.Runtime; +using UnityExplorer.Core.Unity; +using UnityExplorer.UI.Main.Home.Inspectors; + +namespace UnityExplorer.Core.Inspectors +{ + public class GameObjectInspector : InspectorBase + { + public override string TabLabel => $" [G] {TargetGO?.name}"; + + public static GameObjectInspector ActiveInstance { get; private set; } + + public GameObject TargetGO; + + public GameObjectInspectorUI UIModule; + + // sub modules + internal static ChildList s_childList; + internal static ComponentList s_compList; + internal static GameObjectControls s_controls; + + internal static bool m_UIConstructed; + + public GameObjectInspector(GameObject target) : base(target) + { + ActiveInstance = this; + + TargetGO = target; + + if (!TargetGO) + { + ExplorerCore.LogWarning("Target GameObject is null!"); + return; + } + + // one UI is used for all gameobject inspectors. no point recreating it. + if (!m_UIConstructed) + { + m_UIConstructed = true; + + s_childList = new ChildList(); + s_compList = new ComponentList(); + s_controls = new GameObjectControls(); + + UIModule.ConstructUI(); + } + } + + public override void SetActive() + { + base.SetActive(); + ActiveInstance = this; + } + + public override void SetInactive() + { + base.SetInactive(); + ActiveInstance = null; + } + + internal void ChangeInspectorTarget(GameObject newTarget) + { + if (!newTarget) + return; + + this.Target = this.TargetGO = newTarget; + } + + // Update + + public override void Update() + { + base.Update(); + + if (m_pendingDestroy || !this.IsActive) + return; + + UIModule.RefreshTopInfo(); + + s_childList.RefreshChildObjectList(); + + s_compList.RefreshComponentList(); + + s_controls.RefreshControls(); + + if (GameObjectControls.s_sliderChangedWanted) + GameObjectControls.UpdateSliderControl(); + } + + public override void CreateUIModule() + { + base.BaseUI = UIModule = new GameObjectInspectorUI(); + } + } +} diff --git a/src/Inspectors/MouseInspector.cs b/src/Core/Inspectors/InspectUnderMouse.cs similarity index 55% rename from src/Inspectors/MouseInspector.cs rename to src/Core/Inspectors/InspectUnderMouse.cs index 4c19625..a58e424 100644 --- a/src/Inspectors/MouseInspector.cs +++ b/src/Core/Inspectors/InspectUnderMouse.cs @@ -5,14 +5,17 @@ using System.Text; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; -using UnityExplorer.Helpers; -using UnityExplorer.Input; +using UnityExplorer.Core; +using UnityExplorer.Core.Unity; +using UnityExplorer.Core.Input; +using UnityExplorer.Core.Runtime; using UnityExplorer.UI; -using UnityExplorer.Unstrip; +using UnityExplorer.UI.Main; +using UnityExplorer.UI.Main.Home.Inspectors; -namespace UnityExplorer.Inspectors +namespace UnityExplorer.Core.Inspectors { - public class MouseInspector + public class InspectUnderMouse { public enum MouseInspectMode { @@ -24,31 +27,29 @@ namespace UnityExplorer.Inspectors public static MouseInspectMode Mode { get; set; } - internal static Text s_objNameLabel; - internal static Text s_objPathLabel; - internal static Text s_mousePosLabel; - private static GameObject s_lastHit; private static Vector3 s_lastMousePos; - internal static GameObject s_UIContent; + internal static MouseInspectorUI UI; + + static InspectUnderMouse() + { + UI = new MouseInspectorUI(); + } public static void StartInspect() { Enabled = true; MainMenu.Instance.MainPanel.SetActive(false); - s_UIContent.SetActive(true); + + UI.s_UIContent.SetActive(true); // recache Graphic Raycasters each time we start - var casters = ResourcesUnstrip.FindObjectsOfTypeAll(typeof(GraphicRaycaster)); + var casters = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GraphicRaycaster)); m_gCasters = new GraphicRaycaster[casters.Length]; for (int i = 0; i < casters.Length; i++) { -#if CPP - m_gCasters[i] = casters[i].TryCast(); -#else - m_gCasters[i] = casters[i] as GraphicRaycaster; -#endif + m_gCasters[i] = casters[i].Cast(typeof(GraphicRaycaster)) as GraphicRaycaster; } } @@ -56,7 +57,7 @@ namespace UnityExplorer.Inspectors { Enabled = false; MainMenu.Instance.MainPanel.SetActive(true); - s_UIContent.SetActive(false); + UI.s_UIContent.SetActive(false); ClearHitData(); } @@ -76,7 +77,7 @@ namespace UnityExplorer.Inspectors if (mousePos != s_lastMousePos) UpdatePosition(mousePos); - if (!UnityHelpers.MainCamera) + if (!UnityHelper.MainCamera) return; // actual inspect raycast @@ -95,8 +96,8 @@ namespace UnityExplorer.Inspectors if (obj != s_lastHit) { s_lastHit = obj; - s_objNameLabel.text = $"Click to Inspect: {obj.name}"; - s_objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}"; + UI.s_objNameLabel.text = $"Click to Inspect: {obj.name}"; + UI.s_objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}"; } if (InputManager.GetMouseButtonDown(0)) @@ -108,7 +109,7 @@ namespace UnityExplorer.Inspectors internal static void RaycastWorld(Vector2 mousePos) { - var ray = UnityHelpers.MainCamera.ScreenPointToRay(mousePos); + var ray = UnityHelper.MainCamera.ScreenPointToRay(mousePos); Physics.Raycast(ray, out RaycastHit hit, 1000f); if (hit.transform) @@ -167,64 +168,17 @@ namespace UnityExplorer.Inspectors var inversePos = UIManager.CanvasRoot.transform.InverseTransformPoint(mousePos); - s_mousePosLabel.text = $"Mouse Position: {mousePos.ToString()}"; + UI.s_mousePosLabel.text = $"Mouse Position: {mousePos.ToString()}"; float yFix = mousePos.y < 120 ? 80 : -80; - s_UIContent.transform.localPosition = new Vector3(inversePos.x, inversePos.y + yFix, 0); + UI.s_UIContent.transform.localPosition = new Vector3(inversePos.x, inversePos.y + yFix, 0); } internal static void ClearHitData() { s_lastHit = null; - s_objNameLabel.text = "No hits..."; - s_objPathLabel.text = ""; + UI.s_objNameLabel.text = "No hits..."; + UI.s_objPathLabel.text = ""; } - -#region UI Construction - - internal static void ConstructUI() - { - s_UIContent = UIFactory.CreatePanel(UIManager.CanvasRoot, "MouseInspect", out GameObject content); - - s_UIContent.AddComponent(); - - var baseRect = s_UIContent.GetComponent(); - var half = new Vector2(0.5f, 0.5f); - baseRect.anchorMin = half; - baseRect.anchorMax = half; - baseRect.pivot = half; - baseRect.sizeDelta = new Vector2(700, 150); - - var group = content.GetComponent(); - group.childForceExpandHeight = true; - - // Title text - - var titleObj = UIFactory.CreateLabel(content, TextAnchor.MiddleCenter); - var titleText = titleObj.GetComponent(); - titleText.text = "Mouse Inspector (press ESC to cancel)"; - - var mousePosObj = UIFactory.CreateLabel(content, TextAnchor.MiddleCenter); - s_mousePosLabel = mousePosObj.GetComponent(); - s_mousePosLabel.text = "Mouse Position:"; - - var hitLabelObj = UIFactory.CreateLabel(content, TextAnchor.MiddleLeft); - s_objNameLabel = hitLabelObj.GetComponent(); - s_objNameLabel.text = "No hits..."; - s_objNameLabel.horizontalOverflow = HorizontalWrapMode.Overflow; - - var pathLabelObj = UIFactory.CreateLabel(content, TextAnchor.MiddleLeft); - s_objPathLabel = pathLabelObj.GetComponent(); - s_objPathLabel.fontStyle = FontStyle.Italic; - s_objPathLabel.horizontalOverflow = HorizontalWrapMode.Wrap; - - var pathLayout = pathLabelObj.AddComponent(); - pathLayout.minHeight = 75; - pathLayout.flexibleHeight = 0; - - s_UIContent.SetActive(false); - } - -#endregion } } diff --git a/src/Core/Inspectors/InspectorBase.cs b/src/Core/Inspectors/InspectorBase.cs new file mode 100644 index 0000000..89f765d --- /dev/null +++ b/src/Core/Inspectors/InspectorBase.cs @@ -0,0 +1,92 @@ +using System; +using UnityEngine; +using UnityEngine.UI; +using UnityExplorer.Core.Unity; +using UnityExplorer.UI; +using UnityExplorer.UI.Main.Home.Inspectors; + +namespace UnityExplorer.Core.Inspectors +{ + public abstract class InspectorBase + { + public object Target; + + public InspectorBaseUI BaseUI; + + public abstract string TabLabel { get; } + + public bool IsActive { get; private set; } + + internal bool m_pendingDestroy; + + public InspectorBase(object target) + { + Target = target; + + if (Target.IsNullOrDestroyed(false)) + { + Destroy(); + return; + } + + CreateUIModule(); + + BaseUI.AddInspectorTab(this); + } + + public abstract void CreateUIModule(); + + public virtual void SetActive() + { + this.IsActive = true; + BaseUI.Content?.SetActive(true); + } + + public virtual void SetInactive() + { + this.IsActive = false; + BaseUI.Content?.SetActive(false); + } + + public virtual void Update() + { + if (Target.IsNullOrDestroyed(false)) + { + Destroy(); + return; + } + + BaseUI.tabText.text = TabLabel; + } + + public virtual void Destroy() + { + m_pendingDestroy = true; + + GameObject tabGroup = BaseUI.tabButton?.transform.parent.gameObject; + + if (tabGroup) + { + GameObject.Destroy(tabGroup); + } + + int thisIndex = -1; + if (InspectorManager.Instance.m_currentInspectors.Contains(this)) + { + thisIndex = InspectorManager.Instance.m_currentInspectors.IndexOf(this); + InspectorManager.Instance.m_currentInspectors.Remove(this); + } + + if (ReferenceEquals(InspectorManager.Instance.m_activeInspector, this)) + { + InspectorManager.Instance.UnsetInspectorTab(); + + if (InspectorManager.Instance.m_currentInspectors.Count > 0) + { + var prevTab = InspectorManager.Instance.m_currentInspectors[thisIndex > 0 ? thisIndex - 1 : 0]; + InspectorManager.Instance.SetInspectorTab(prevTab); + } + } + } + } +} diff --git a/src/Inspectors/Reflection/CacheObject/CacheEnumerated.cs b/src/Core/Inspectors/Reflection/CacheObject/CacheEnumerated.cs similarity index 97% rename from src/Inspectors/Reflection/CacheObject/CacheEnumerated.cs rename to src/Core/Inspectors/Reflection/CacheObject/CacheEnumerated.cs index cefb230..4ae12ba 100644 --- a/src/Inspectors/Reflection/CacheObject/CacheEnumerated.cs +++ b/src/Core/Inspectors/Reflection/CacheObject/CacheEnumerated.cs @@ -7,7 +7,7 @@ using UnityExplorer.UI; using UnityEngine; using UnityEngine.UI; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public class CacheEnumerated : CacheObjectBase { diff --git a/src/Inspectors/Reflection/CacheObject/CacheField.cs b/src/Core/Inspectors/Reflection/CacheObject/CacheField.cs similarity index 93% rename from src/Inspectors/Reflection/CacheObject/CacheField.cs rename to src/Core/Inspectors/Reflection/CacheObject/CacheField.cs index e21dd51..c507927 100644 --- a/src/Inspectors/Reflection/CacheObject/CacheField.cs +++ b/src/Core/Inspectors/Reflection/CacheObject/CacheField.cs @@ -4,10 +4,9 @@ using System.Linq; using System.Text; using System.Reflection; using UnityExplorer.UI; -using UnityExplorer.Helpers; using UnityEngine; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public class CacheField : CacheMember { diff --git a/src/Inspectors/Reflection/CacheObject/CacheMember.cs b/src/Core/Inspectors/Reflection/CacheObject/CacheMember.cs similarity index 89% rename from src/Inspectors/Reflection/CacheObject/CacheMember.cs rename to src/Core/Inspectors/Reflection/CacheObject/CacheMember.cs index e10856c..5796069 100644 --- a/src/Inspectors/Reflection/CacheObject/CacheMember.cs +++ b/src/Core/Inspectors/Reflection/CacheObject/CacheMember.cs @@ -5,13 +5,13 @@ using System.Reflection; using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI; -using UnityExplorer.UI.Shared; -using UnityExplorer.Helpers; -#if CPP -using UnhollowerBaseLib; -#endif +using UnityExplorer.UI.Reusable; +using UnityExplorer.Core.Unity; +using UnityExplorer.Core.Runtime; +using UnityExplorer.Core; +using UnityExplorer.UI.Utility; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public abstract class CacheMember : CacheObjectBase { @@ -50,10 +50,8 @@ namespace UnityExplorer.Inspectors.Reflection DeclaringType = memberInfo.DeclaringType; DeclaringInstance = declaringInstance; this.m_parentContent = parentContent; -#if CPP - if (DeclaringInstance != null) - DeclaringInstance = DeclaringInstance.Il2CppCast(DeclaringType); -#endif + + DeclaringInstance = ReflectionProvider.Instance.Cast(declaringInstance, DeclaringType); } public static bool CanProcessArgs(ParameterInfo[] parameters) @@ -87,19 +85,19 @@ namespace UnityExplorer.Inspectors.Reflection { try { -#if CPP - if (!IsReflectionSupported()) - throw new Exception("Type not supported with Reflection"); -#endif + Type baseType = ReflectionUtility.GetType(IValue.Value) ?? FallbackType; + + if (!ReflectionProvider.Instance.IsReflectionSupported(baseType)) + throw new Exception("Type not supported with reflection"); + UpdateReflection(); -#if CPP + if (IValue.Value != null) - IValue.Value = IValue.Value.Il2CppCast(ReflectionHelpers.GetActualType(IValue.Value)); -#endif + IValue.Value = IValue.Value.Cast(ReflectionUtility.GetType(IValue.Value)); } catch (Exception e) { - ReflectionException = ReflectionHelpers.ExceptionToString(e, true); + ReflectionException = e.ReflectionExToString(true); } } @@ -179,46 +177,9 @@ namespace UnityExplorer.Inspectors.Reflection private string GetRichTextName() { - return m_richTextName = UISyntaxHighlight.ParseFullSyntax(MemInfo.DeclaringType, false, MemInfo); + return m_richTextName = SignatureHighlighter.ParseFullSyntax(MemInfo.DeclaringType, false, MemInfo); } -#if CPP - internal bool IsReflectionSupported() - { - try - { - var baseType = ReflectionHelpers.GetActualType(IValue.Value) ?? IValue.FallbackType; - - var gArgs = baseType.GetGenericArguments(); - if (gArgs.Length < 1) - return true; - - foreach (var arg in gArgs) - { - if (!Check(arg)) - return false; - } - - return true; - - bool Check(Type type) - { - if (!typeof(Il2CppSystem.Object).IsAssignableFrom(type)) - return true; - - if (!ReflectionHelpers.Il2CppTypeNotNull(type, out IntPtr ptr)) - return false; - - return Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(ptr)) is Il2CppSystem.Type; - } - } - catch - { - return false; - } - } -#endif - #region UI internal float GetMemberLabelWidth(RectTransform scrollRect) @@ -380,8 +341,8 @@ namespace UnityExplorer.Inspectors.Reflection var argLabelLayout = argLabelObj.AddComponent(); argLabelLayout.minHeight = 25; var argText = argLabelObj.GetComponent(); - var argTypeTxt = UISyntaxHighlight.ParseFullSyntax(arg.ParameterType, false); - argText.text = $"{argTypeTxt} {arg.Name}"; + var argTypeTxt = SignatureHighlighter.ParseFullSyntax(arg.ParameterType, false); + argText.text = $"{argTypeTxt} {arg.Name}"; var argInputObj = UIFactory.CreateInputField(rowObj, 14, (int)TextAnchor.MiddleLeft, 1); var argInputLayout = argInputObj.AddComponent(); diff --git a/src/Inspectors/Reflection/CacheObject/CacheMethod.cs b/src/Core/Inspectors/Reflection/CacheObject/CacheMethod.cs similarity index 92% rename from src/Inspectors/Reflection/CacheObject/CacheMethod.cs rename to src/Core/Inspectors/Reflection/CacheObject/CacheMethod.cs index d0a1823..d50f3f2 100644 --- a/src/Inspectors/Reflection/CacheObject/CacheMethod.cs +++ b/src/Core/Inspectors/Reflection/CacheObject/CacheMethod.cs @@ -5,9 +5,11 @@ using System.Reflection; using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI; -using UnityExplorer.Helpers; +using UnityExplorer.Core.Unity; +using UnityExplorer.Core; +using UnityExplorer.UI.Utility; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public class CacheMethod : CacheMember { @@ -75,7 +77,7 @@ namespace UnityExplorer.Inspectors.Reflection e = e.InnerException; ExplorerCore.LogWarning($"Exception evaluating: {e.GetType()}, {e.Message}"); - ReflectionException = ReflectionHelpers.ExceptionToString(e); + ReflectionException = ReflectionUtility.ReflectionExToString(e); } IValue.Value = ret; @@ -90,7 +92,7 @@ namespace UnityExplorer.Inspectors.Reflection for (int i = 0; i < GenericArgs.Length; i++) { var input = m_genericArgInput[i]; - if (ReflectionHelpers.GetTypeByName(input) is Type t) + if (ReflectionUtility.GetTypeByName(input) is Type t) { if (GenericConstraints[i].Length == 0) { @@ -150,7 +152,7 @@ namespace UnityExplorer.Inspectors.Reflection if (constrainTxt != "") constrainTxt += ", "; - constrainTxt += $"{UISyntaxHighlight.ParseFullSyntax(constraint, false)}"; + constrainTxt += $"{SignatureHighlighter.ParseFullSyntax(constraint, false)}"; } } else @@ -168,7 +170,7 @@ namespace UnityExplorer.Inspectors.Reflection //var argLayout = argLabelObj.AddComponent(); //argLayout.minWidth = 20; var argText = argLabelObj.GetComponent(); - argText.text = $"{constrainTxt} {arg.Name}"; + argText.text = $"{constrainTxt} {arg.Name}"; var argInputObj = UIFactory.CreateInputField(rowObj, 14, (int)TextAnchor.MiddleLeft, 1); var argInputLayout = argInputObj.AddComponent(); diff --git a/src/Inspectors/Reflection/CacheObject/CacheObjectBase.cs b/src/Core/Inspectors/Reflection/CacheObject/CacheObjectBase.cs similarity index 95% rename from src/Inspectors/Reflection/CacheObject/CacheObjectBase.cs rename to src/Core/Inspectors/Reflection/CacheObject/CacheObjectBase.cs index cf644a0..e498361 100644 --- a/src/Inspectors/Reflection/CacheObject/CacheObjectBase.cs +++ b/src/Core/Inspectors/Reflection/CacheObject/CacheObjectBase.cs @@ -4,11 +4,12 @@ using System.Linq; using System.Reflection; using UnityEngine; using UnityExplorer.UI; -using UnityExplorer.UI.Shared; -using UnityExplorer.Helpers; +using UnityExplorer.UI.Reusable; +using UnityExplorer.Core.Unity; using UnityEngine.UI; +using UnityExplorer.Core; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public abstract class CacheObjectBase { @@ -54,7 +55,7 @@ namespace UnityExplorer.Inspectors.Reflection // if the type has changed fundamentally, make a new interactivevalue for it var type = value == null ? FallbackType - : ReflectionHelpers.GetActualType(value); + : ReflectionUtility.GetType(value); var ivalueType = InteractiveValue.GetIValueForType(type); diff --git a/src/Inspectors/Reflection/CacheObject/CachePaired.cs b/src/Core/Inspectors/Reflection/CacheObject/CachePaired.cs similarity index 97% rename from src/Inspectors/Reflection/CacheObject/CachePaired.cs rename to src/Core/Inspectors/Reflection/CacheObject/CachePaired.cs index 080b616..3623176 100644 --- a/src/Inspectors/Reflection/CacheObject/CachePaired.cs +++ b/src/Core/Inspectors/Reflection/CacheObject/CachePaired.cs @@ -8,7 +8,7 @@ using UnityEngine; using UnityEngine.UI; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public enum PairTypes { diff --git a/src/Inspectors/Reflection/CacheObject/CacheProperty.cs b/src/Core/Inspectors/Reflection/CacheObject/CacheProperty.cs similarity index 96% rename from src/Inspectors/Reflection/CacheObject/CacheProperty.cs rename to src/Core/Inspectors/Reflection/CacheObject/CacheProperty.cs index 9634b04..15d0d99 100644 --- a/src/Inspectors/Reflection/CacheObject/CacheProperty.cs +++ b/src/Core/Inspectors/Reflection/CacheObject/CacheProperty.cs @@ -4,10 +4,10 @@ using System.Linq; using System.Text; using System.Reflection; using UnityExplorer.UI; -using UnityExplorer.Helpers; +using UnityExplorer.Core.Unity; using UnityEngine; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public class CacheProperty : CacheMember { diff --git a/src/Core/Inspectors/Reflection/InstanceInspector.cs b/src/Core/Inspectors/Reflection/InstanceInspector.cs new file mode 100644 index 0000000..b283169 --- /dev/null +++ b/src/Core/Inspectors/Reflection/InstanceInspector.cs @@ -0,0 +1,54 @@ +using System; +using System.Reflection; +using UnityEngine; +using UnityExplorer.UI; +using UnityEngine.UI; +using System.IO; +using UnityExplorer.Core.Runtime; +using UnityExplorer.UI.Main.Home.Inspectors; + +namespace UnityExplorer.Core.Inspectors.Reflection +{ + public enum MemberScopes + { + All, + Instance, + Static + } + + public class InstanceInspector : ReflectionInspector + { + public override string TabLabel => $" [R] {base.TabLabel}"; + + internal MemberScopes m_scopeFilter; + internal Button m_lastActiveScopeButton; + + public InstanceInspector(object target) : base(target) { } + + internal InstanceInspectorUI InstanceUI; + public void CreateInstanceUIModule() + { + InstanceUI = new InstanceInspectorUI(this); + } + + internal void OnScopeFilterClicked(MemberScopes type, Button button) + { + if (m_lastActiveScopeButton) + { + var lastColors = m_lastActiveScopeButton.colors; + lastColors.normalColor = new Color(0.2f, 0.2f, 0.2f); + m_lastActiveScopeButton.colors = lastColors; + } + + m_scopeFilter = type; + m_lastActiveScopeButton = button; + + var colors = m_lastActiveScopeButton.colors; + colors.normalColor = new Color(0.2f, 0.6f, 0.2f); + m_lastActiveScopeButton.colors = colors; + + FilterMembers(null, true); + base.ReflectionUI.m_sliderScroller.m_slider.value = 1f; + } + } +} diff --git a/src/Inspectors/Reflection/InteractiveValue/InteractiveBool.cs b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveBool.cs similarity index 97% rename from src/Inspectors/Reflection/InteractiveValue/InteractiveBool.cs rename to src/Core/Inspectors/Reflection/InteractiveValue/InteractiveBool.cs index c4df3b0..de6b76d 100644 --- a/src/Inspectors/Reflection/InteractiveValue/InteractiveBool.cs +++ b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveBool.cs @@ -4,10 +4,10 @@ using System.Linq; using System.Text; using UnityEngine; using UnityEngine.UI; -using UnityExplorer.Helpers; +using UnityExplorer.Core.Unity; using UnityExplorer.UI; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public class InteractiveBool : InteractiveValue { diff --git a/src/Inspectors/Reflection/InteractiveValue/InteractiveDictionary.cs b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveDictionary.cs similarity index 98% rename from src/Inspectors/Reflection/InteractiveValue/InteractiveDictionary.cs rename to src/Core/Inspectors/Reflection/InteractiveValue/InteractiveDictionary.cs index 7dff039..918c816 100644 --- a/src/Inspectors/Reflection/InteractiveValue/InteractiveDictionary.cs +++ b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveDictionary.cs @@ -5,16 +5,16 @@ using System.Linq; using System.Text; using UnityEngine; using UnityEngine.UI; -using UnityExplorer.Config; -using UnityExplorer.Helpers; +using UnityExplorer.Core.Config; +using UnityExplorer.Core.Unity; using UnityExplorer.UI; -using UnityExplorer.UI.Shared; +using UnityExplorer.UI.Reusable; using System.Reflection; #if CPP using CppDictionary = Il2CppSystem.Collections.IDictionary; #endif -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public class InteractiveDictionary : InteractiveValue { diff --git a/src/Inspectors/Reflection/InteractiveValue/InteractiveEnum.cs b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveEnum.cs similarity index 98% rename from src/Inspectors/Reflection/InteractiveValue/InteractiveEnum.cs rename to src/Core/Inspectors/Reflection/InteractiveValue/InteractiveEnum.cs index a2d82af..abd4d0d 100644 --- a/src/Inspectors/Reflection/InteractiveValue/InteractiveEnum.cs +++ b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveEnum.cs @@ -4,10 +4,10 @@ using System.Linq; using System.Text; using UnityEngine; using UnityEngine.UI; -using UnityExplorer.Helpers; +using UnityExplorer.Core.Unity; using UnityExplorer.UI; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public class InteractiveEnum : InteractiveValue { diff --git a/src/Inspectors/Reflection/InteractiveValue/InteractiveEnumerable.cs b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveEnumerable.cs similarity index 98% rename from src/Inspectors/Reflection/InteractiveValue/InteractiveEnumerable.cs rename to src/Core/Inspectors/Reflection/InteractiveValue/InteractiveEnumerable.cs index 89df055..0f770d8 100644 --- a/src/Inspectors/Reflection/InteractiveValue/InteractiveEnumerable.cs +++ b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveEnumerable.cs @@ -6,12 +6,12 @@ using System.Reflection; using System.Text; using UnityEngine; using UnityEngine.UI; -using UnityExplorer.Config; -using UnityExplorer.Helpers; +using UnityExplorer.Core.Config; +using UnityExplorer.Core.Unity; using UnityExplorer.UI; -using UnityExplorer.UI.Shared; +using UnityExplorer.UI.Reusable; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public class InteractiveEnumerable : InteractiveValue { diff --git a/src/Inspectors/Reflection/InteractiveValue/InteractiveFlags.cs b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveFlags.cs similarity index 98% rename from src/Inspectors/Reflection/InteractiveValue/InteractiveFlags.cs rename to src/Core/Inspectors/Reflection/InteractiveValue/InteractiveFlags.cs index d3821e5..5f3a479 100644 --- a/src/Inspectors/Reflection/InteractiveValue/InteractiveFlags.cs +++ b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveFlags.cs @@ -4,10 +4,10 @@ using System.Linq; using System.Text; using UnityEngine; using UnityEngine.UI; -using UnityExplorer.Helpers; +using UnityExplorer.Core.Unity; using UnityExplorer.UI; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public class InteractiveFlags : InteractiveEnum { diff --git a/src/Inspectors/Reflection/InteractiveValue/InteractiveNumber.cs b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveNumber.cs similarity index 93% rename from src/Inspectors/Reflection/InteractiveValue/InteractiveNumber.cs rename to src/Core/Inspectors/Reflection/InteractiveValue/InteractiveNumber.cs index 2c58907..3bb257e 100644 --- a/src/Inspectors/Reflection/InteractiveValue/InteractiveNumber.cs +++ b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveNumber.cs @@ -5,10 +5,12 @@ using System.Text; using System.Reflection; using UnityEngine; using UnityEngine.UI; -using UnityExplorer.Helpers; +using UnityExplorer.Core.Unity; using UnityExplorer.UI; +using UnityExplorer.Core; +using UnityExplorer.UI.Utility; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public class InteractiveNumber : InteractiveValue { @@ -46,7 +48,7 @@ namespace UnityExplorer.Inspectors.Reflection return; } - m_baseLabel.text = UISyntaxHighlight.ParseFullSyntax(FallbackType, false); + m_baseLabel.text = SignatureHighlighter.ParseFullSyntax(FallbackType, false); m_valueInput.text = Value.ToString(); var type = Value.GetType(); @@ -84,7 +86,7 @@ namespace UnityExplorer.Inspectors.Reflection } catch (Exception e) { - ExplorerCore.LogWarning("Could not parse input! " + ReflectionHelpers.ExceptionToString(e, true)); + ExplorerCore.LogWarning("Could not parse input! " + ReflectionUtility.ReflectionExToString(e, true)); } } diff --git a/src/Inspectors/Reflection/InteractiveValue/InteractiveString.cs b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveString.cs similarity index 97% rename from src/Inspectors/Reflection/InteractiveValue/InteractiveString.cs rename to src/Core/Inspectors/Reflection/InteractiveValue/InteractiveString.cs index 7b7e83f..9068448 100644 --- a/src/Inspectors/Reflection/InteractiveValue/InteractiveString.cs +++ b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveString.cs @@ -5,10 +5,11 @@ using System.Text; using System.Reflection; using UnityEngine; using UnityEngine.UI; -using UnityExplorer.Helpers; +using UnityExplorer.Core.Unity; using UnityExplorer.UI; +using UnityExplorer.UI.Utility; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public class InteractiveString : InteractiveValue { @@ -109,7 +110,7 @@ namespace UnityExplorer.Inspectors.Reflection base.ConstructUI(parent, subGroup); GetDefaultLabel(false); - m_richValueType = UISyntaxHighlight.ParseFullSyntax(FallbackType, false); + m_richValueType = SignatureHighlighter.ParseFullSyntax(FallbackType, false); m_labelLayout = m_baseLabel.gameObject.GetComponent(); diff --git a/src/Inspectors/Reflection/InteractiveValue/InteractiveUnityStruct.cs b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveUnityStruct.cs similarity index 99% rename from src/Inspectors/Reflection/InteractiveValue/InteractiveUnityStruct.cs rename to src/Core/Inspectors/Reflection/InteractiveValue/InteractiveUnityStruct.cs index 409cdec..e51f7e1 100644 --- a/src/Inspectors/Reflection/InteractiveValue/InteractiveUnityStruct.cs +++ b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveUnityStruct.cs @@ -4,10 +4,10 @@ using System.Linq; using System.Text; using UnityEngine; using UnityEngine.UI; -using UnityExplorer.Helpers; +using UnityExplorer.Core.Unity; using UnityExplorer.UI; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { #region IStructInfo helper diff --git a/src/Inspectors/Reflection/InteractiveValue/InteractiveValue.cs b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveValue.cs similarity index 94% rename from src/Inspectors/Reflection/InteractiveValue/InteractiveValue.cs rename to src/Core/Inspectors/Reflection/InteractiveValue/InteractiveValue.cs index b51f43e..7b1d0b1 100644 --- a/src/Inspectors/Reflection/InteractiveValue/InteractiveValue.cs +++ b/src/Core/Inspectors/Reflection/InteractiveValue/InteractiveValue.cs @@ -5,10 +5,13 @@ using System.Reflection; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; -using UnityExplorer.Helpers; +using UnityExplorer.Core; +using UnityExplorer.Core.Unity; +using UnityExplorer.Core.Runtime; using UnityExplorer.UI; +using UnityExplorer.UI.Utility; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public class InteractiveValue { @@ -47,10 +50,10 @@ namespace UnityExplorer.Inspectors.Reflection else if (typeof(Transform).IsAssignableFrom(type)) return typeof(InteractiveValue); // check Dictionaries before Enumerables - else if (ReflectionHelpers.IsDictionary(type)) + else if (ReflectionUtility.IsDictionary(type)) return typeof(InteractiveDictionary); // finally check for Enumerables - else if (ReflectionHelpers.IsEnumerable(type)) + else if (ReflectionUtility.IsEnumerable(type)) return typeof(InteractiveEnumerable); // fallback to default else @@ -59,7 +62,7 @@ namespace UnityExplorer.Inspectors.Reflection public static InteractiveValue Create(object value, Type fallbackType) { - var type = ReflectionHelpers.GetActualType(value) ?? fallbackType; + var type = ReflectionUtility.GetType(value) ?? fallbackType; var iType = GetIValueForType(type); return (InteractiveValue)Activator.CreateInstance(iType, new object[] { value, type }); @@ -202,7 +205,7 @@ namespace UnityExplorer.Inspectors.Reflection { var valueType = Value?.GetType() ?? this.FallbackType; if (updateType) - m_richValueType = UISyntaxHighlight.ParseFullSyntax(valueType, true); + m_richValueType = SignatureHighlighter.ParseFullSyntax(valueType, true); if (!Owner.HasEvaluated) return m_defaultLabel = $"Not yet evaluated ({m_richValueType})"; @@ -255,14 +258,8 @@ namespace UnityExplorer.Inspectors.Reflection string typeName = valueType.FullName; if (typeName.StartsWith("Il2CppSystem.")) typeName = typeName.Substring(6, typeName.Length - 6); -#if CPP - var cppType = UnhollowerRuntimeLib.Il2CppType.From(valueType); - if (cppType != null && ReflectionHelpers.UnobfuscatedTypeNames.ContainsKey(cppType.FullName)) - { - typeName = ReflectionHelpers.UnobfuscatedTypeNames[cppType.FullName]; - toString = toString.Replace(cppType.FullName, typeName); - } -#endif + + toString = ReflectionProvider.Instance.ProcessTypeNameInString(valueType, toString, ref typeName); // If the ToString is just the type name, use our syntax highlighted type name instead. if (toString == typeName) diff --git a/src/Core/Inspectors/Reflection/ReflectionInspector.cs b/src/Core/Inspectors/Reflection/ReflectionInspector.cs new file mode 100644 index 0000000..0e9739f --- /dev/null +++ b/src/Core/Inspectors/Reflection/ReflectionInspector.cs @@ -0,0 +1,360 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityExplorer.Core.Unity; +using UnityEngine; +using UnityExplorer.Core.Inspectors.Reflection; +using UnityExplorer.UI.Reusable; +using System.Reflection; +using UnityExplorer.UI; +using UnityEngine.UI; +using UnityExplorer.Core.Config; +using UnityExplorer.Core; +using UnityExplorer.UI.Utility; +using UnityExplorer.UI.Main.Home.Inspectors; + +namespace UnityExplorer.Core.Inspectors +{ + public class ReflectionInspector : InspectorBase + { + #region STATIC + + public static ReflectionInspector ActiveInstance { get; private set; } + + static ReflectionInspector() + { + PanelDragger.OnFinishResize += OnContainerResized; + SceneExplorer.OnToggleShow += OnContainerResized; + } + + private static void OnContainerResized() + { + if (ActiveInstance == null) + return; + + ActiveInstance.ReflectionUI.m_widthUpdateWanted = true; + } + + // Blacklists + private static readonly HashSet bl_typeAndMember = new HashSet + { +#if CPP + // these cause a crash in IL2CPP + "Type.DeclaringMethod", + "Rigidbody2D.Cast", + "Collider2D.Cast", + "Collider2D.Raycast", + "Texture2D.SetPixelDataImpl", + "Camera.CalculateProjectionMatrixFromPhysicalProperties", +#endif + }; + private static readonly HashSet bl_memberNameStartsWith = new HashSet + { + // these are redundant + "get_", + "set_", + }; + + #endregion + + #region INSTANCE + + public override string TabLabel => m_targetTypeShortName; + + internal CacheObjectBase ParentMember { get; set; } + + internal ReflectionInspectorUI ReflectionUI; + + internal readonly Type m_targetType; + internal readonly string m_targetTypeShortName; + + // all cached members of the target + internal CacheMember[] m_allMembers; + // filtered members based on current filters + internal readonly List m_membersFiltered = new List(); + // actual shortlist of displayed members + internal readonly CacheMember[] m_displayedMembers = new CacheMember[ExplorerConfig.Instance.Default_Page_Limit]; + + internal bool m_autoUpdate; + + public ReflectionInspector(object target) : base(target) + { + if (this is StaticInspector) + m_targetType = target as Type; + else + m_targetType = ReflectionUtility.GetType(target); + + m_targetTypeShortName = SignatureHighlighter.ParseFullSyntax(m_targetType, false); + + ReflectionUI.ConstructUI(); + + CacheMembers(m_targetType); + + FilterMembers(); + } + + public override void CreateUIModule() + { + BaseUI = ReflectionUI = new ReflectionInspectorUI(this); + } + + public override void SetActive() + { + base.SetActive(); + ActiveInstance = this; + } + + public override void SetInactive() + { + base.SetInactive(); + ActiveInstance = null; + } + + public override void Destroy() + { + base.Destroy(); + + if (this.BaseUI.Content) + GameObject.Destroy(this.BaseUI.Content); + } + + internal void OnPageTurned() + { + RefreshDisplay(); + } + + internal bool IsBlacklisted(string sig) => bl_typeAndMember.Any(it => sig.Contains(it)); + internal bool IsBlacklisted(MethodInfo method) => bl_memberNameStartsWith.Any(it => method.Name.StartsWith(it)); + + internal string GetSig(MemberInfo member) => $"{member.DeclaringType.Name}.{member.Name}"; + internal string AppendArgsToSig(ParameterInfo[] args) + { + string ret = " ("; + foreach (var param in args) + ret += $"{param.ParameterType.Name} {param.Name}, "; + ret += ")"; + return ret; + } + + public void CacheMembers(Type type) + { + var list = new List(); + var cachedSigs = new HashSet(); + + var types = ReflectionUtility.GetAllBaseTypes(type); + + var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static; + if (this is InstanceInspector) + flags |= BindingFlags.Instance; + + foreach (var declaringType in types) + { + var target = Target; + target = target.Cast(declaringType); + + IEnumerable infos = declaringType.GetMethods(flags); + infos = infos.Concat(declaringType.GetProperties(flags)); + infos = infos.Concat(declaringType.GetFields(flags)); + + foreach (var member in infos) + { + try + { + var sig = GetSig(member); + + //ExplorerCore.Log($"Trying to cache member {sig}..."); + //ExplorerCore.Log(member.DeclaringType.FullName + "." + member.Name); + + var mi = member as MethodInfo; + var pi = member as PropertyInfo; + var fi = member as FieldInfo; + + if (IsBlacklisted(sig) || (mi != null && IsBlacklisted(mi))) + continue; + + var args = mi?.GetParameters() ?? pi?.GetIndexParameters(); + if (args != null) + { + if (!CacheMember.CanProcessArgs(args)) + continue; + + sig += AppendArgsToSig(args); + } + + if (cachedSigs.Contains(sig)) + continue; + + cachedSigs.Add(sig); + + if (mi != null) + list.Add(new CacheMethod(mi, target, ReflectionUI.m_scrollContent)); + else if (pi != null) + list.Add(new CacheProperty(pi, target, ReflectionUI.m_scrollContent)); + else + list.Add(new CacheField(fi, target, ReflectionUI.m_scrollContent)); + + list.Last().ParentInspector = this; + } + catch (Exception e) + { + ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!"); + ExplorerCore.Log(e.ToString()); + } + } + } + + var typeList = types.ToList(); + + var sorted = new List(); + sorted.AddRange(list.Where(it => it is CacheMethod) + .OrderBy(it => typeList.IndexOf(it.DeclaringType)) + .ThenBy(it => it.NameForFiltering)); + sorted.AddRange(list.Where(it => it is CacheProperty) + .OrderBy(it => typeList.IndexOf(it.DeclaringType)) + .ThenBy(it => it.NameForFiltering)); + sorted.AddRange(list.Where(it => it is CacheField) + .OrderBy(it => typeList.IndexOf(it.DeclaringType)) + .ThenBy(it => it.NameForFiltering)); + + m_allMembers = sorted.ToArray(); + } + + public override void Update() + { + base.Update(); + + if (m_autoUpdate) + { + foreach (var member in m_displayedMembers) + { + if (member == null) break; + member.UpdateValue(); + } + } + + if (ReflectionUI.m_widthUpdateWanted) + { + if (!ReflectionUI.m_widthUpdateWaiting) + ReflectionUI.m_widthUpdateWaiting = true; + else + { + UpdateWidths(); + ReflectionUI.m_widthUpdateWaiting = false; + ReflectionUI.m_widthUpdateWanted = false; + } + } + } + + internal void OnMemberFilterClicked(MemberTypes type, Button button) + { + if (ReflectionUI.m_lastActiveMemButton) + { + var lastColors = ReflectionUI.m_lastActiveMemButton.colors; + lastColors.normalColor = new Color(0.2f, 0.2f, 0.2f); + ReflectionUI.m_lastActiveMemButton.colors = lastColors; + } + + ReflectionUI.m_memberFilter = type; + ReflectionUI.m_lastActiveMemButton = button; + + var colors = ReflectionUI.m_lastActiveMemButton.colors; + colors.normalColor = new Color(0.2f, 0.6f, 0.2f); + ReflectionUI.m_lastActiveMemButton.colors = colors; + + FilterMembers(null, true); + ReflectionUI.m_sliderScroller.m_slider.value = 1f; + } + + public void FilterMembers(string nameFilter = null, bool force = false) + { + int lastCount = m_membersFiltered.Count; + m_membersFiltered.Clear(); + + nameFilter = nameFilter?.ToLower() ?? ReflectionUI.m_nameFilterText.text.ToLower(); + + foreach (var mem in m_allMembers) + { + // membertype filter + if (ReflectionUI.m_memberFilter != MemberTypes.All && mem.MemInfo.MemberType != ReflectionUI.m_memberFilter) + continue; + + if (this is InstanceInspector ii && ii.m_scopeFilter != MemberScopes.All) + { + if (mem.IsStatic && ii.m_scopeFilter != MemberScopes.Static) + continue; + else if (!mem.IsStatic && ii.m_scopeFilter != MemberScopes.Instance) + continue; + } + + // name filter + if (!string.IsNullOrEmpty(nameFilter) && !mem.NameForFiltering.Contains(nameFilter)) + continue; + + m_membersFiltered.Add(mem); + } + + if (force || lastCount != m_membersFiltered.Count) + RefreshDisplay(); + } + + public void RefreshDisplay() + { + var members = m_membersFiltered; + ReflectionUI.m_pageHandler.ListCount = members.Count; + + // disable current members + for (int i = 0; i < m_displayedMembers.Length; i++) + { + var mem = m_displayedMembers[i]; + if (mem != null) + mem.Disable(); + else + break; + } + + if (members.Count < 1) + return; + + foreach (var itemIndex in ReflectionUI.m_pageHandler) + { + if (itemIndex >= members.Count) + break; + + CacheMember member = members[itemIndex]; + m_displayedMembers[itemIndex - ReflectionUI.m_pageHandler.StartIndex] = member; + member.Enable(); + } + + ReflectionUI.m_widthUpdateWanted = true; + } + + internal void UpdateWidths() + { + float labelWidth = 125; + + foreach (var cache in m_displayedMembers) + { + if (cache == null) + break; + + var width = cache.GetMemberLabelWidth(ReflectionUI.m_scrollContentRect); + + if (width > labelWidth) + labelWidth = width; + } + + float valueWidth = ReflectionUI.m_scrollContentRect.rect.width - labelWidth - 20; + + foreach (var cache in m_displayedMembers) + { + if (cache == null) + break; + cache.SetWidths(labelWidth, valueWidth); + } + } + + + #endregion // end instance + } +} diff --git a/src/Inspectors/Reflection/StaticInspector.cs b/src/Core/Inspectors/Reflection/StaticInspector.cs similarity index 82% rename from src/Inspectors/Reflection/StaticInspector.cs rename to src/Core/Inspectors/Reflection/StaticInspector.cs index 981e62a..ec433e1 100644 --- a/src/Inspectors/Reflection/StaticInspector.cs +++ b/src/Core/Inspectors/Reflection/StaticInspector.cs @@ -1,6 +1,6 @@ using System; -namespace UnityExplorer.Inspectors.Reflection +namespace UnityExplorer.Core.Inspectors.Reflection { public class StaticInspector : ReflectionInspector { diff --git a/src/Core/ReflectionUtility.cs b/src/Core/ReflectionUtility.cs new file mode 100644 index 0000000..ef127f5 --- /dev/null +++ b/src/Core/ReflectionUtility.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using BF = System.Reflection.BindingFlags; +using UnityExplorer.Core.Runtime; + +namespace UnityExplorer.Core +{ + public static class ReflectionUtility + { + public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static; + + /// + /// Helper for IL2CPP to get the underlying true Type (Unhollowed) of the object. + /// + /// The object to get the true Type for. + /// The most accurate Type of the object which could be identified. + public static Type GetType(this object obj) + { + if (obj == null) + return null; + + return ReflectionProvider.Instance.GetActualType(obj); + } + + /// + /// Cast an object to its underlying Type. + /// + /// The object to cast + /// The object, cast to the underlying Type if possible, otherwise the original object. + public static object Cast(this object obj) + => Cast(obj, GetType(obj)); + + /// + /// Cast an object to a Type, if possible. + /// + /// The object to cast + /// The Type to cast to + /// The object, cast to the Type provided if possible, otherwise the original object. + public static object Cast(this object obj, Type castTo) + => ReflectionProvider.Instance.Cast(obj, castTo); + + /// + /// Check if the provided Type is assignable to IEnumerable. + /// + /// The Type to check + /// True if the Type is assignable to IEnumerable, otherwise false. + public static bool IsEnumerable(this Type t) + => ReflectionProvider.Instance.IsAssignableFrom(typeof(IEnumerable), t); + + /// + /// Check if the provided Type is assignable to IDictionary. + /// + /// The Type to check + /// True if the Type is assignable to IDictionary, otherwise false. + public static bool IsDictionary(this Type t) + => ReflectionProvider.Instance.IsAssignableFrom(typeof(IDictionary), t); + + public static bool LoadModule(string module) + => ReflectionProvider.Instance.LoadModule(module); + + // cache for GetTypeByName + internal static readonly Dictionary s_typesByName = new Dictionary(); + + /// + /// Find a in the current AppDomain whose matches the provided . + /// + /// The you want to search for - case sensitive and full matches only. + /// The Type if found, otherwise null. + public static Type GetTypeByName(string fullName) + { + s_typesByName.TryGetValue(fullName, out Type ret); + + if (ret != null) + return ret; + + foreach (var type in from asm in AppDomain.CurrentDomain.GetAssemblies() + from type in asm.TryGetTypes() + select type) + { + if (type.FullName == fullName) + { + ret = type; + break; + } + } + + if (s_typesByName.ContainsKey(fullName)) + s_typesByName[fullName] = ret; + else + s_typesByName.Add(fullName, ret); + + return ret; + } + + // cache for GetBaseTypes + internal static readonly Dictionary s_cachedTypeInheritance = new Dictionary(); + + /// + /// Get all base types of the provided Type, including itself. + /// + public static Type[] GetAllBaseTypes(this object obj) => GetAllBaseTypes(GetType(obj)); + + /// + /// Get all base types of the provided Type, including itself. + /// + public static Type[] GetAllBaseTypes(this Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + var name = type.AssemblyQualifiedName; + + if (s_cachedTypeInheritance.TryGetValue(name, out Type[] ret)) + return ret; + + List list = new List(); + + while (type != null) + { + list.Add(type); + type = type.BaseType; + } + + ret = list.ToArray(); + + s_cachedTypeInheritance.Add(name, ret); + + return ret; + } + + /// + /// Safely get all valid Types inside an Assembly. + /// + /// The Assembly to find Types in. + /// All possible Types which could be retrieved from the Assembly, or an empty array. + public static IEnumerable TryGetTypes(this Assembly asm) + { + try + { + return asm.GetTypes(); + } + catch (ReflectionTypeLoadException e) + { + try + { + return asm.GetExportedTypes(); + } + catch + { + return e.Types.Where(t => t != null); + } + } + catch + { + return Enumerable.Empty(); + } + } + + /// + /// Helper to display a simple "{ExceptionType}: {Message}" of the exception, and optionally use the inner-most exception. + /// + /// The Exception to convert to string. + /// Should the inner-most Exception of the stack be used? If false, the Exception you provided will be used directly. + /// The exception to string. + public static string ReflectionExToString(this Exception e, bool innerMost = false) + { + if (innerMost) + { + while (e.InnerException != null) + { +#if CPP + if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException) + break; +#endif + e = e.InnerException; + } + } + + return $"{e.GetType()}: {e.Message}"; + } + } +} diff --git a/src/Unstrip/AssetBundleUnstrip.cs b/src/Core/Runtime/Il2Cpp/AssetBundle.cs similarity index 78% rename from src/Unstrip/AssetBundleUnstrip.cs rename to src/Core/Runtime/Il2Cpp/AssetBundle.cs index c212789..9ad4301 100644 --- a/src/Unstrip/AssetBundleUnstrip.cs +++ b/src/Core/Runtime/Il2Cpp/AssetBundle.cs @@ -6,9 +6,9 @@ using System.Text; using UnhollowerBaseLib; using UnhollowerRuntimeLib; using UnityEngine; -using UnityExplorer.Helpers; +using UnityExplorer.Core.Runtime.Il2Cpp; -namespace UnityExplorer.Unstrip +namespace UnityExplorer { public class AssetBundle { @@ -18,7 +18,7 @@ namespace UnityExplorer.Unstrip public static AssetBundle LoadFromFile(string path) { - var iCall = ICallHelper.GetICall("UnityEngine.AssetBundle::LoadFromFile_Internal"); + var iCall = ICallManager.GetICall("UnityEngine.AssetBundle::LoadFromFile_Internal"); var ptr = iCall.Invoke(IL2CPP.ManagedStringToIl2Cpp(path), 0u, 0UL); @@ -29,7 +29,7 @@ namespace UnityExplorer.Unstrip public static AssetBundle LoadFromMemory(byte[] binary, uint crc = 0) { - var iCall = ICallHelper.GetICall("UnityEngine.AssetBundle::LoadFromMemory_Internal"); + var iCall = ICallManager.GetICall("UnityEngine.AssetBundle::LoadFromMemory_Internal"); var ptr = iCall(((Il2CppStructArray) binary).Pointer, crc); @@ -48,7 +48,7 @@ namespace UnityExplorer.Unstrip public UnityEngine.Object[] LoadAllAssets() { - var iCall = ICallHelper.GetICall("UnityEngine.AssetBundle::LoadAssetWithSubAssets_Internal"); + var iCall = ICallManager.GetICall("UnityEngine.AssetBundle::LoadAssetWithSubAssets_Internal"); var ptr = iCall.Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(""), Il2CppType.Of().Pointer); if (ptr == IntPtr.Zero) @@ -63,7 +63,7 @@ namespace UnityExplorer.Unstrip public T LoadAsset(string name) where T : UnityEngine.Object { - var iCall = ICallHelper.GetICall("UnityEngine.AssetBundle::LoadAsset_Internal"); + var iCall = ICallManager.GetICall("UnityEngine.AssetBundle::LoadAsset_Internal"); var ptr = iCall.Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(name), Il2CppType.Of().Pointer); if (ptr == IntPtr.Zero) diff --git a/src/Helpers/ICallHelper.cs b/src/Core/Runtime/Il2Cpp/ICallManager.cs similarity index 95% rename from src/Helpers/ICallHelper.cs rename to src/Core/Runtime/Il2Cpp/ICallManager.cs index bc1f370..cf0de54 100644 --- a/src/Helpers/ICallHelper.cs +++ b/src/Core/Runtime/Il2Cpp/ICallManager.cs @@ -4,10 +4,10 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace UnityExplorer.Helpers +namespace UnityExplorer.Core.Runtime.Il2Cpp { [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "External methods")] - public static class ICallHelper + public static class ICallManager { private static readonly Dictionary iCallCache = new Dictionary(); diff --git a/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs b/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs new file mode 100644 index 0000000..a17619e --- /dev/null +++ b/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs @@ -0,0 +1,107 @@ +#if CPP +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnhollowerBaseLib; +using UnhollowerRuntimeLib; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.SceneManagement; + +namespace UnityExplorer.Core.Runtime.Il2Cpp +{ + public class Il2CppProvider : RuntimeProvider + { + public override void Initialize() + { + Reflection = new Il2CppReflection(); + TextureUtil = new Il2CppTextureUtil(); + } + + public override void SetupEvents() + { + Application.add_logMessageReceived( + new Action(ExplorerCore.Instance.OnUnityLog)); + + //SceneManager.add_sceneLoaded( + // new Action(ExplorerCore.Instance.OnSceneLoaded1)); + + //SceneManager.add_activeSceneChanged( + // new Action(ExplorerCore.Instance.OnSceneLoaded2)); + } + + internal delegate IntPtr d_LayerToName(int layer); + + public override string LayerToName(int layer) + { + var iCall = ICallManager.GetICall("UnityEngine.LayerMask::LayerToName"); + return IL2CPP.Il2CppStringToManaged(iCall.Invoke(layer)); + } + + internal delegate IntPtr d_FindObjectsOfTypeAll(IntPtr type); + + public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type) + { + var iCall = ICallManager.GetICall("UnityEngine.Resources::FindObjectsOfTypeAll"); + var cppType = Il2CppType.From(type); + + return new Il2CppReferenceArray(iCall.Invoke(cppType.Pointer)); + } + + public override int GetSceneHandle(Scene scene) + => scene.handle; + + //Scene.GetRootGameObjects(); + + internal delegate void d_GetRootGameObjects(int handle, IntPtr list); + + public override GameObject[] GetRootGameObjects(Scene scene) => GetRootGameObjects(scene.handle); + + public static GameObject[] GetRootGameObjects(int handle) + { + if (handle == -1) + return new GameObject[0]; + + int count = GetRootCount(handle); + + if (count < 1) + return new GameObject[0]; + + var list = new Il2CppSystem.Collections.Generic.List(count); + + var iCall = ICallManager.GetICall("UnityEngine.SceneManagement.Scene::GetRootGameObjectsInternal"); + + iCall.Invoke(handle, list.Pointer); + + return list.ToArray(); + } + + //Scene.rootCount; + + internal delegate int d_GetRootCountInternal(int handle); + + public override int GetRootCount(Scene scene) => GetRootCount(scene.handle); + + public static int GetRootCount(int handle) + { + return ICallManager.GetICall("UnityEngine.SceneManagement.Scene::GetRootCountInternal") + .Invoke(handle); + } + } +} + +public static class UnityEventExtensions +{ + public static void AddListener(this UnityEvent action, Action listener) + { + action.AddListener(listener); + } + + public static void AddListener(this UnityEvent action, Action listener) + { + action.AddListener(listener); + } +} + +#endif \ No newline at end of file diff --git a/src/Helpers/ReflectionHelpers.cs b/src/Core/Runtime/Il2Cpp/Il2CppReflection.cs similarity index 54% rename from src/Helpers/ReflectionHelpers.cs rename to src/Core/Runtime/Il2Cpp/Il2CppReflection.cs index b54151d..918e73b 100644 --- a/src/Helpers/ReflectionHelpers.cs +++ b/src/Core/Runtime/Il2Cpp/Il2CppReflection.cs @@ -1,110 +1,63 @@ -using System; -using System.Collections; +#if CPP +using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Reflection; -using UnityEngine; -using BF = System.Reflection.BindingFlags; -using System.Diagnostics.CodeAnalysis; -#if CPP -using CppType = Il2CppSystem.Type; +using System.Text; +using System.Threading.Tasks; using UnhollowerBaseLib; +using UnityExplorer.Core.Unity; using UnhollowerRuntimeLib; using System.Runtime.InteropServices; -#endif +using System.Reflection; +using System.Collections; +using System.IO; +using System.Diagnostics.CodeAnalysis; +using UnityExplorer.Core; +using CppType = Il2CppSystem.Type; +using BF = System.Reflection.BindingFlags; -namespace UnityExplorer.Helpers +namespace UnityExplorer.Core.Runtime.Il2Cpp { [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "External methods")] - public static class ReflectionHelpers + public class Il2CppReflection : ReflectionProvider { - public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static; - - // cache for GetTypeByName - internal static readonly Dictionary s_typesByName = new Dictionary(); - - /// - /// Find a in the current AppDomain whose matches the provided . - /// - /// The you want to search for - case sensitive and full matches only. - /// The Type if found, otherwise null. - public static Type GetTypeByName(string fullName) + public Il2CppReflection() : base() { - s_typesByName.TryGetValue(fullName, out Type ret); + Instance = this; - if (ret != null) - return ret; - - foreach (var type in from asm in AppDomain.CurrentDomain.GetAssemblies() - from type in asm.TryGetTypes() - select type) - { - if (type.FullName == fullName) - { - ret = type; - break; - } - } - - if (s_typesByName.ContainsKey(fullName)) - s_typesByName[fullName] = ret; - else - s_typesByName.Add(fullName, ret); - - return ret; + TryLoadGameModules(); } - // cache for GetBaseTypes - internal static readonly Dictionary s_cachedTypeInheritance = new Dictionary(); - - /// - /// Get all base types of the provided Type, including itself. - /// - public static Type[] GetAllBaseTypes(object obj) => GetAllBaseTypes(GetActualType(obj)); - - /// - /// Get all base types of the provided Type, including itself. - /// - public static Type[] GetAllBaseTypes(Type type) + public override object Cast(object obj, Type castTo) { - if (type == null) - throw new ArgumentNullException("type"); - - var name = type.AssemblyQualifiedName; - - if (s_cachedTypeInheritance.TryGetValue(name, out Type[] ret)) - return ret; - - List list = new List(); - - while (type != null) - { - list.Add(type); - type = type.BaseType; - } - - ret = list.ToArray(); - - s_cachedTypeInheritance.Add(name, ret); - - return ret; + return Il2CppCast(obj, castTo); } - /// - /// Helper for IL2CPP to get the underlying true Type (Unhollowed) of the object. - /// - /// The object to get the true Type for. - /// The most accurate Type of the object which could be identified. - public static Type GetActualType(this object obj) + public override string ProcessTypeNameInString(Type type, string theString, ref string typeName) + { + if (!Il2CppTypeNotNull(type)) + return theString; + + var cppType = Il2CppType.From(type); + if (cppType != null && s_deobfuscatedTypeNames.ContainsKey(cppType.FullName)) + { + typeName = s_deobfuscatedTypeNames[cppType.FullName]; + theString = theString.Replace(cppType.FullName, typeName); + } + + return theString; + } + + public override Type GetActualType(object obj) { if (obj == null) return null; var type = obj.GetType(); -#if CPP + if (obj is Il2CppSystem.Object cppObject) { + // weird specific case - if the object is an Il2CppSystem.Type, then return so manually. if (cppObject is CppType) return typeof(CppType); @@ -122,7 +75,7 @@ namespace UnityExplorer.Helpers IntPtr classPtr = il2cpp_object_get_class(cppObject.Pointer); if (RuntimeSpecificsStore.IsInjected(classPtr)) { - var typeByName = GetTypeByName(cppType.FullName); + var typeByName = ReflectionUtility.GetTypeByName(cppType.FullName); if (typeByName != null) return typeByName; } @@ -132,16 +85,15 @@ namespace UnityExplorer.Helpers if (getType != null) return getType; } -#endif + return type; } -#if CPP // caching for GetMonoType private static readonly Dictionary Il2CppToMonoType = new Dictionary(); - // keep unobfuscated type name cache, used to display proper name. - internal static Dictionary UnobfuscatedTypeNames = new Dictionary(); + // keep deobfuscated type name cache, used to display proper name. + internal static Dictionary s_deobfuscatedTypeNames = new Dictionary(); /// /// Try to get the Mono (Unhollowed) Type representation of the provided . @@ -156,7 +108,7 @@ namespace UnityExplorer.Helpers return Il2CppToMonoType[name]; Type ret = Type.GetType(name); - + if (ret == null) { string baseName = cppType.FullName; @@ -164,18 +116,18 @@ namespace UnityExplorer.Helpers ret = AppDomain.CurrentDomain .GetAssemblies() - .FirstOrDefault(a + .FirstOrDefault(a => a.GetName().Name == baseAssembly)? .TryGetTypes() - .FirstOrDefault(t - => t.CustomAttributes.Any(ca + .FirstOrDefault(t + => t.CustomAttributes.Any(ca => ca.AttributeType.Name == "ObfuscatedNameAttribute" && (string)ca.ConstructorArguments[0].Value == baseName)); if (ret != null) { - // unobfuscated type was found, add to cache. - UnobfuscatedTypeNames.Add(cppType.FullName, ret.FullName); + // deobfuscated type was found, add to cache. + s_deobfuscatedTypeNames.Add(cppType.FullName, ret.FullName); } } @@ -185,14 +137,14 @@ namespace UnityExplorer.Helpers } // cached class pointers for Il2CppCast - private static readonly Dictionary CppClassPointers = new Dictionary(); + private static readonly Dictionary s_cppClassPointers = new Dictionary(); /// /// Attempt to cast the object to its underlying type. /// /// The object you want to cast. /// The object, as the underlying type if successful or the input value if not. - public static object Il2CppCast(this object obj) => Il2CppCast(obj, GetActualType(obj)); + public static object Il2CppCast(object obj) => Il2CppCast(obj, Instance.GetActualType(obj)); /// /// Attempt to cast the object to the provided type. @@ -200,7 +152,7 @@ namespace UnityExplorer.Helpers /// The object you want to cast. /// The Type you want to cast to. /// The object, as the type (or a normal C# object) if successful or the input value if not. - public static object Il2CppCast(this object obj, Type castTo) + public static object Il2CppCast(object obj, Type castTo) { if (!(obj is Il2CppSystem.Object ilObj)) return obj; @@ -234,7 +186,7 @@ namespace UnityExplorer.Helpers /// True if successful, false if not. public static bool Il2CppTypeNotNull(Type type, out IntPtr il2cppPtr) { - if (CppClassPointers.TryGetValue(type.AssemblyQualifiedName, out il2cppPtr)) + if (s_cppClassPointers.TryGetValue(type.AssemblyQualifiedName, out il2cppPtr)) return il2cppPtr != IntPtr.Zero; il2cppPtr = (IntPtr)typeof(Il2CppClassPointerStore<>) @@ -242,7 +194,7 @@ namespace UnityExplorer.Helpers .GetField("NativeClassPtr", BF.Public | BF.Static) .GetValue(null); - CppClassPointers.Add(type.AssemblyQualifiedName, il2cppPtr); + s_cppClassPointers.Add(type.AssemblyQualifiedName, il2cppPtr); return il2cppPtr != IntPtr.Zero; } @@ -254,143 +206,92 @@ namespace UnityExplorer.Helpers [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public static extern IntPtr il2cpp_object_get_class(IntPtr obj); - // cached il2cpp unbox methods - internal static readonly Dictionary s_unboxMethods = new Dictionary(); + internal static IntPtr s_cppEnumerableClassPtr; + internal static IntPtr s_cppDictionaryClassPtr; - /// - /// Attempt to unbox the object to the underlying struct type. - /// - /// The object which is a struct underneath. - /// The struct if successful, otherwise null. - public static object Unbox(this object obj) => Unbox(obj, GetActualType(obj)); - - /// - /// Attempt to unbox the object to the struct type. - /// - /// The object which is a struct underneath. - /// The type of the struct you want to unbox to. - /// The struct if successful, otherwise null. - public static object Unbox(this object obj, Type type) + public override bool IsAssignableFrom(Type toAssignTo, Type toAssignFrom) { - if (!type.IsValueType) - return null; + if (toAssignTo.IsAssignableFrom(toAssignFrom)) + return true; - if (!(obj is Il2CppSystem.Object)) - return obj; - - var name = type.AssemblyQualifiedName; - - if (!s_unboxMethods.ContainsKey(name)) - { - s_unboxMethods.Add(name, typeof(Il2CppObjectBase) - .GetMethod("Unbox") - .MakeGenericMethod(type)); - } - - return s_unboxMethods[name].Invoke(obj, new object[0]); - } - -#endif - - public static IEnumerable TryGetTypes(this Assembly asm) - { - try - { - return asm.GetTypes(); - } - catch (ReflectionTypeLoadException e) + if (toAssignTo == typeof(IEnumerable)) { try { - return asm.GetExportedTypes(); + if (s_cppEnumerableClassPtr == IntPtr.Zero) + Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IEnumerable), out s_cppEnumerableClassPtr); + + if (s_cppEnumerableClassPtr != IntPtr.Zero + && Il2CppTypeNotNull(toAssignFrom, out IntPtr assignFromPtr) + && il2cpp_class_is_assignable_from(s_cppEnumerableClassPtr, assignFromPtr)) + { + return true; + } } - catch + catch { } + } + else if (toAssignTo == typeof(IDictionary)) + { + try { - return e.Types.Where(t => t != null); + if (s_cppDictionaryClassPtr == IntPtr.Zero) + if (!Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IDictionary), out s_cppDictionaryClassPtr)) + return false; + + if (Il2CppTypeNotNull(toAssignFrom, out IntPtr classPtr)) + { + if (il2cpp_class_is_assignable_from(s_cppDictionaryClassPtr, classPtr)) + return true; + } + } + catch { } + } + + return false; + } + + public override bool IsReflectionSupported(Type type) + { + try + { + var gArgs = type.GetGenericArguments(); + if (!gArgs.Any()) + return true; + + foreach (var gType in gArgs) + { + if (!Supported(gType)) + return false; + } + + return true; + + bool Supported(Type t) + { + if (!typeof(Il2CppSystem.Object).IsAssignableFrom(t)) + return true; + + if (!Il2CppTypeNotNull(t, out IntPtr ptr)) + return false; + + return CppType.internal_from_handle(IL2CPP.il2cpp_class_get_type(ptr)) is CppType; } } catch { - return Enumerable.Empty(); + return false; } } - // Helper for IL2CPP to check if a Type is assignable to IEnumerable - -#if CPP - internal static IntPtr s_cppEnumerableClassPtr; -#endif - - /// - /// Check if the provided Type is assignable to IEnumerable. - /// - /// The Type to check - /// True if the Type is assignable to IEnumerable, otherwise false. - public static bool IsEnumerable(Type t) - { - if (typeof(IEnumerable).IsAssignableFrom(t)) - return true; -#if CPP - try - { - if (s_cppEnumerableClassPtr == IntPtr.Zero) - Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IEnumerable), out s_cppEnumerableClassPtr); - - if (s_cppEnumerableClassPtr != IntPtr.Zero - && Il2CppTypeNotNull(t, out IntPtr classPtr) - && il2cpp_class_is_assignable_from(s_cppEnumerableClassPtr, classPtr)) - { - return true; - } - } - catch { } -#endif - return false; - } - - // Helper for IL2CPP to check if a Type is assignable to IDictionary - -#if CPP - internal static IntPtr s_cppDictionaryClassPtr; -#endif - - /// - /// Check if the provided Type is assignable to IDictionary. - /// - /// The Type to check - /// True if the Type is assignable to IDictionary, otherwise false. - public static bool IsDictionary(Type t) - { - if (typeof(IDictionary).IsAssignableFrom(t)) - return true; -#if CPP - try - { - if (s_cppDictionaryClassPtr == IntPtr.Zero) - if (!Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IDictionary), out s_cppDictionaryClassPtr)) - return false; - - if (Il2CppTypeNotNull(t, out IntPtr classPtr)) - { - if (il2cpp_class_is_assignable_from(s_cppDictionaryClassPtr, classPtr)) - return true; - } - } - catch { } -#endif - return false; - } - // Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded. -#if CPP internal static void TryLoadGameModules() { - LoadModule("Assembly-CSharp"); - LoadModule("Assembly-CSharp-firstpass"); + Instance.LoadModule("Assembly-CSharp"); + Instance.LoadModule("Assembly-CSharp-firstpass"); } - public static bool LoadModule(string module) + public override bool LoadModule(string module) { #if ML var path = Path.Combine("MelonLoader", "Managed", $"{module}.dll"); @@ -417,32 +318,45 @@ namespace UnityExplorer.Helpers return false; } -#else - // For Mono, just return true and do nothing, Mono will sort it out itself. - public static bool LoadModule(string module) => true; -#endif + + // ~~~~~~~~~~ not used ~~~~~~~~~~~~ + + // cached il2cpp unbox methods + internal static readonly Dictionary s_unboxMethods = new Dictionary(); /// - /// Helper to display a simple "{ExceptionType}: {Message}" of the exception, and optionally use the inner-most exception. + /// Attempt to unbox the object to the underlying struct type. /// - /// The Exception to convert to string. - /// Should the inner-most Exception of the stack be used? If false, the Exception you provided will be used directly. - /// The exception to string. - public static string ExceptionToString(Exception e, bool innerMost = false) + /// The object which is a struct underneath. + /// The struct if successful, otherwise null. + public static object Unbox(object obj) => Unbox(obj, Instance.GetActualType(obj)); + + /// + /// Attempt to unbox the object to the struct type. + /// + /// The object which is a struct underneath. + /// The type of the struct you want to unbox to. + /// The struct if successful, otherwise null. + public static object Unbox(object obj, Type type) { - if (innerMost) + if (!type.IsValueType) + return null; + + if (!(obj is Il2CppSystem.Object)) + return obj; + + var name = type.AssemblyQualifiedName; + + if (!s_unboxMethods.ContainsKey(name)) { - while (e.InnerException != null) - { -#if CPP - if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException) - break; -#endif - e = e.InnerException; - } + s_unboxMethods.Add(name, typeof(Il2CppObjectBase) + .GetMethod("Unbox") + .MakeGenericMethod(type)); } - return $"{e.GetType()}: {e.Message}"; + return s_unboxMethods[name].Invoke(obj, new object[0]); } } } + +#endif \ No newline at end of file diff --git a/src/Core/Runtime/Il2Cpp/Il2CppTextureUtil.cs b/src/Core/Runtime/Il2Cpp/Il2CppTextureUtil.cs new file mode 100644 index 0000000..973ae8d --- /dev/null +++ b/src/Core/Runtime/Il2Cpp/Il2CppTextureUtil.cs @@ -0,0 +1,77 @@ +#if CPP +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnhollowerBaseLib; +using UnityEngine; + +namespace UnityExplorer.Core.Runtime.Il2Cpp +{ + public class Il2CppTextureUtil : TextureUtilProvider + { + public override Texture2D NewTexture2D(int width, int height) + => new Texture2D((int)width, (int)height, TextureFormat.RGBA32, Texture.GenerateAllMips, false, IntPtr.Zero); + + internal delegate void d_Blit2(IntPtr source, IntPtr dest); + + public override void Blit(Texture2D tex, RenderTexture rt) + { + var iCall = ICallManager.GetICall("UnityEngine.Graphics::Blit2"); + iCall.Invoke(tex.Pointer, rt.Pointer); + } + + // byte[] ImageConversion.EncodeToPNG(this Texture2D image); + + internal delegate IntPtr d_EncodeToPNG(IntPtr tex); + + public override byte[] EncodeToPNG(Texture2D tex) + { + var iCall = ICallManager.GetICall("UnityEngine.ImageConversion::EncodeToPNG"); + + IntPtr ptr = iCall.Invoke(tex.Pointer); + + if (ptr == IntPtr.Zero) + return null; + + return new Il2CppStructArray(ptr); + } + + // bool ImageConversion.LoadImage(this Texture2D tex, byte[] data, bool markNonReadable); + + internal delegate bool d_LoadImage(IntPtr tex, IntPtr data, bool markNonReadable); + + public override bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable) + { + var il2cppArray = (Il2CppStructArray)data; + + var iCall = ICallManager.GetICall("UnityEngine.ImageConversion::LoadImage"); + + return iCall.Invoke(tex.Pointer, il2cppArray.Pointer, markNonReadable); + } + + // Sprite Sprite.Create + + public override Sprite CreateSprite(Texture2D texture) + { + return CreateSpriteImpl(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero, 100f, 0u, Vector4.zero); + } + + internal delegate IntPtr d_CreateSprite(IntPtr texture, ref Rect rect, ref Vector2 pivot, float pixelsPerUnit, + uint extrude, int meshType, ref Vector4 border, bool generateFallbackPhysicsShape); + + public static Sprite CreateSpriteImpl(Texture texture, Rect rect, Vector2 pivot, float pixelsPerUnit, uint extrude, Vector4 border) + { + var iCall = ICallManager.GetICall("UnityEngine.Sprite::CreateSprite_Injected"); + + var ptr = iCall.Invoke(texture.Pointer, ref rect, ref pivot, pixelsPerUnit, extrude, 1, ref border, false); + + if (ptr == IntPtr.Zero) + return null; + else + return new Sprite(ptr); + } + } +} +#endif \ No newline at end of file diff --git a/src/Core/Runtime/Mono/MonoProvider.cs b/src/Core/Runtime/Mono/MonoProvider.cs new file mode 100644 index 0000000..11f559f --- /dev/null +++ b/src/Core/Runtime/Mono/MonoProvider.cs @@ -0,0 +1,53 @@ +#if MONO +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityExplorer.Core; + +namespace UnityExplorer.Core.Runtime.Mono +{ + public class MonoProvider : RuntimeProvider + { + public override void Initialize() + { + Reflection = new MonoReflection(); + TextureUtil = new MonoTextureUtil(); + } + + public override void SetupEvents() + { + Application.logMessageReceived += ExplorerCore.Instance.OnUnityLog; + //SceneManager.sceneLoaded += ExplorerCore.Instance.OnSceneLoaded1; + //SceneManager.activeSceneChanged += ExplorerCore.Instance.OnSceneLoaded2; + } + + public override string LayerToName(int layer) + => LayerMask.LayerToName(layer); + + public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type) + => Resources.FindObjectsOfTypeAll(type); + + private static readonly FieldInfo fi_Scene_handle = typeof(Scene).GetField("m_Handle", ReflectionUtility.CommonFlags); + + public override int GetSceneHandle(Scene scene) + { + return (int)fi_Scene_handle.GetValue(scene); + } + + public override GameObject[] GetRootGameObjects(Scene scene) + { + return scene.GetRootGameObjects(); + } + + public override int GetRootCount(Scene scene) + { + return scene.rootCount; + } + } +} + +#endif \ No newline at end of file diff --git a/src/Core/Runtime/Mono/MonoReflection.cs b/src/Core/Runtime/Mono/MonoReflection.cs new file mode 100644 index 0000000..7a96427 --- /dev/null +++ b/src/Core/Runtime/Mono/MonoReflection.cs @@ -0,0 +1,33 @@ +#if MONO +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UnityExplorer.Core.Runtime.Mono +{ + public class MonoReflection : ReflectionProvider + { + // Mono doesn't need to explicitly cast things. + public override object Cast(object obj, Type castTo) + => obj; + + // Vanilla GetType is fine for mono + public override Type GetActualType(object obj) + => obj.GetType(); + + public override bool IsAssignableFrom(Type toAssignTo, Type toAssignFrom) + => toAssignTo.IsAssignableFrom(toAssignFrom); + + public override bool IsReflectionSupported(Type type) + => true; + + public override bool LoadModule(string module) + => true; + + public override string ProcessTypeNameInString(Type type, string theString, ref string typeName) + => theString; + } +} + +#endif \ No newline at end of file diff --git a/src/Core/Runtime/Mono/MonoTextureUtil.cs b/src/Core/Runtime/Mono/MonoTextureUtil.cs new file mode 100644 index 0000000..eef1940 --- /dev/null +++ b/src/Core/Runtime/Mono/MonoTextureUtil.cs @@ -0,0 +1,66 @@ +#if MONO +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using UnityEngine; +using UnityExplorer.Core; + +namespace UnityExplorer.Core.Runtime.Mono +{ + public class MonoTextureUtil : TextureUtilProvider + { + public override void Blit(Texture2D tex, RenderTexture rt) + { + Graphics.Blit(tex, rt); + } + + public override Sprite CreateSprite(Texture2D texture) + { + return Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero); + } + + public override bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable) + { + return tex.LoadImage(data, markNonReadable); + } + + public override Texture2D NewTexture2D(int width, int height) + { + return new Texture2D(width, height); + } + + public override byte[] EncodeToPNG(Texture2D tex) + { + return EncodeToPNGSafe(tex); + } + + private static MethodInfo EncodeToPNGMethod => m_encodeToPNGMethod ?? GetEncodeToPNGMethod(); + private static MethodInfo m_encodeToPNGMethod; + + public static byte[] EncodeToPNGSafe(Texture2D tex) + { + var method = EncodeToPNGMethod; + + if (method.IsStatic) + return (byte[])method.Invoke(null, new object[] { tex }); + else + return (byte[])method.Invoke(tex, new object[0]); + } + + private static MethodInfo GetEncodeToPNGMethod() + { + if (ReflectionUtility.GetTypeByName("UnityEngine.ImageConversion") is Type imageConversion) + return m_encodeToPNGMethod = imageConversion.GetMethod("EncodeToPNG", ReflectionUtility.CommonFlags); + + var method = typeof(Texture2D).GetMethod("EncodeToPNG", ReflectionUtility.CommonFlags); + if (method != null) + return m_encodeToPNGMethod = method; + + ExplorerCore.Log("ERROR: Cannot get any EncodeToPNG method!"); + return null; + } + } +} +#endif \ No newline at end of file diff --git a/src/Core/Runtime/ReflectionProvider.cs b/src/Core/Runtime/ReflectionProvider.cs new file mode 100644 index 0000000..90b874f --- /dev/null +++ b/src/Core/Runtime/ReflectionProvider.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UnityExplorer.Core.Runtime +{ + public abstract class ReflectionProvider + { + public static ReflectionProvider Instance; + + public ReflectionProvider() + { + Instance = this; + } + + public abstract Type GetActualType(object obj); + + public abstract object Cast(object obj, Type castTo); + + public abstract bool IsAssignableFrom(Type toAssignTo, Type toAssignFrom); + + public abstract bool IsReflectionSupported(Type type); + + public abstract string ProcessTypeNameInString(Type type, string theString, ref string typeName); + + public abstract bool LoadModule(string module); + } +} diff --git a/src/Runtime/RuntimeProvider.cs b/src/Core/Runtime/RuntimeProvider.cs similarity index 56% rename from src/Runtime/RuntimeProvider.cs rename to src/Core/Runtime/RuntimeProvider.cs index 157aea4..f28a477 100644 --- a/src/Runtime/RuntimeProvider.cs +++ b/src/Core/Runtime/RuntimeProvider.cs @@ -2,8 +2,10 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using UnityEngine; +using UnityEngine.SceneManagement; -namespace UnityExplorer.Runtime +namespace UnityExplorer.Core.Runtime { // Work in progress, this will be used to replace all the "if CPP / if MONO" // pre-processor directives all over the codebase. @@ -12,6 +14,9 @@ namespace UnityExplorer.Runtime { public static RuntimeProvider Instance; + public ReflectionProvider Reflection; + public TextureUtilProvider TextureUtil; + public RuntimeProvider() { Initialize(); @@ -31,5 +36,16 @@ namespace UnityExplorer.Runtime public abstract void SetupEvents(); + // Unity API handlers + + public abstract string LayerToName(int layer); + + public abstract UnityEngine.Object[] FindObjectsOfTypeAll(Type type); + + public abstract int GetSceneHandle(Scene scene); + + public abstract GameObject[] GetRootGameObjects(Scene scene); + + public abstract int GetRootCount(Scene scene); } } diff --git a/src/Helpers/Texture2DHelpers.cs b/src/Core/Runtime/TextureUtilProvider.cs similarity index 58% rename from src/Helpers/Texture2DHelpers.cs rename to src/Core/Runtime/TextureUtilProvider.cs index fdbcfc3..a3967a5 100644 --- a/src/Helpers/Texture2DHelpers.cs +++ b/src/Core/Runtime/TextureUtilProvider.cs @@ -1,50 +1,32 @@ using System; -using UnityEngine; +using System.Collections.Generic; using System.IO; -using System.Reflection; -#if CPP -using UnityExplorer.Unstrip; -#endif +using System.Linq; +using System.Text; +using UnityEngine; -namespace UnityExplorer.Helpers +namespace UnityExplorer.Core.Runtime { - public static class Texture2DHelpers + public abstract class TextureUtilProvider { -#if MONO - private static bool isNewEncodeMethod = false; - private static MethodInfo EncodeToPNGMethod => m_encodeToPNGMethod ?? GetEncodeToPNGMethod(); - private static MethodInfo m_encodeToPNGMethod; + public static TextureUtilProvider Instance; - public static byte[] EncodeToPNGSafe(this Texture2D tex) + public TextureUtilProvider() { - var method = EncodeToPNGMethod; - - if (method.IsStatic) - return (byte[])method.Invoke(null, new object[] { tex }); - else - return (byte[])method.Invoke(tex, new object[0]); + Instance = this; } - private static MethodInfo GetEncodeToPNGMethod() - { - if (ReflectionHelpers.GetTypeByName("UnityEngine.ImageConversion") is Type imageConversion) - { - isNewEncodeMethod = true; - return m_encodeToPNGMethod = imageConversion.GetMethod("EncodeToPNG", ReflectionHelpers.CommonFlags); - } + public abstract byte[] EncodeToPNG(Texture2D tex); - var method = typeof(Texture2D).GetMethod("EncodeToPNG", ReflectionHelpers.CommonFlags); - if (method != null) - { - return m_encodeToPNGMethod = method; - } + public abstract Texture2D NewTexture2D(int width, int height); - ExplorerCore.Log("ERROR: Cannot get any EncodeToPNG method!"); - return null; - } -#endif + public abstract void Blit(Texture2D tex, RenderTexture rt); - public static bool IsReadable(this Texture2D tex) + public abstract bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable); + + public abstract Sprite CreateSprite(Texture2D texture); + + public static bool IsReadable(Texture2D tex) { try { @@ -61,30 +43,30 @@ namespace UnityExplorer.Helpers } } + public static bool LoadImage(Texture2D tex, string filePath, bool markNonReadable) + { + if (!File.Exists(filePath)) + return false; + + return Instance.LoadImage(tex, File.ReadAllBytes(filePath), markNonReadable); + } + public static Texture2D Copy(Texture2D orig, Rect rect) { Color[] pixels; - if (!orig.IsReadable()) + if (IsReadable(orig)) orig = ForceReadTexture(orig); pixels = orig.GetPixels((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); - // use full constructor for better compatibility -#if CPP - var _newTex = new Texture2D((int)rect.width, (int)rect.height, TextureFormat.RGBA32, Texture.GenerateAllMips, false, IntPtr.Zero); -#else - var _newTex = new Texture2D((int)rect.width, (int)rect.height); -#endif - _newTex.SetPixels(pixels); + Texture2D newTex = Instance.NewTexture2D((int)rect.width, (int)rect.height); - return _newTex; + newTex.SetPixels(pixels); + + return newTex; } -#if CPP - internal delegate void d_Blit2(IntPtr source, IntPtr dest); -#endif - public static Texture2D ForceReadTexture(Texture2D tex) { try @@ -96,12 +78,7 @@ namespace UnityExplorer.Helpers rt.filterMode = FilterMode.Point; RenderTexture.active = rt; -#if MONO - Graphics.Blit(tex, rt); -#else - var iCall = ICallHelper.GetICall("UnityEngine.Graphics::Blit2"); - iCall.Invoke(tex.Pointer, rt.Pointer); -#endif + Instance.Blit(tex, rt); var _newTex = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false); @@ -129,7 +106,7 @@ namespace UnityExplorer.Helpers string savepath = dir + @"\" + name + ".png"; // Make sure we can EncodeToPNG it. - if (tex.format != TextureFormat.ARGB32 || !tex.IsReadable()) + if (tex.format != TextureFormat.ARGB32 || !IsReadable(tex)) { tex = ForceReadTexture(tex); } @@ -140,20 +117,9 @@ namespace UnityExplorer.Helpers tex.Apply(false, false); } -#if CPP - data = tex.EncodeToPNG(); -#else - if (isNewEncodeMethod) - { - data = (byte[])EncodeToPNGMethod.Invoke(null, new object[] { tex }); - } - else - { - data = (byte[])EncodeToPNGMethod.Invoke(tex, new object[0]); - } -#endif + data = Instance.EncodeToPNG(tex); - if (data == null || data.Length < 1) + if (data == null || !data.Any()) { ExplorerCore.LogWarning("Couldn't get any data for the texture!"); } diff --git a/src/Core/SceneExplorer.cs b/src/Core/SceneExplorer.cs new file mode 100644 index 0000000..172a33f --- /dev/null +++ b/src/Core/SceneExplorer.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityExplorer.UI; +using UnityExplorer.UI.Main; +using UnityExplorer.UI.Reusable; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.UI; +using UnityExplorer.Core.Runtime; +using UnityExplorer.UI.Main.Home; + +namespace UnityExplorer.Core.Inspectors +{ + public class SceneExplorer + { + public static SceneExplorer Instance; + + public static SceneExplorerUI UI; + + internal static Action OnToggleShow; + + public SceneExplorer() + { + Instance = this; + + UI = new SceneExplorerUI(); + UI.ConstructScenePane(); + } + + private const float UPDATE_INTERVAL = 1f; + private float m_timeOfLastSceneUpdate; + + // private int m_currentSceneHandle = -1; + public static Scene DontDestroyScene => DontDestroyObject.scene; + internal Scene m_currentScene; + internal Scene[] m_currentScenes = new Scene[0]; + + internal GameObject[] m_allObjects = new GameObject[0]; + + internal GameObject m_selectedSceneObject; + internal int m_lastCount; + + internal static GameObject DontDestroyObject + { + get + { + if (!s_dontDestroyObject) + { + s_dontDestroyObject = new GameObject("DontDestroyMe"); + GameObject.DontDestroyOnLoad(s_dontDestroyObject); + } + return s_dontDestroyObject; + } + } + internal static GameObject s_dontDestroyObject; + + public void Init() + { + RefreshSceneSelector(); + } + + public void Update() + { + if (SceneExplorerUI.Hiding || Time.realtimeSinceStartup - m_timeOfLastSceneUpdate < UPDATE_INTERVAL) + { + return; + } + + RefreshSceneSelector(); + + if (!m_selectedSceneObject) + { + if (m_currentScene != default) + { + var rootObjects = RuntimeProvider.Instance.GetRootGameObjects(m_currentScene); + SetSceneObjectList(rootObjects); + } + } + else + { + RefreshSelectedSceneObject(); + } + } + + private void RefreshSceneSelector() + { + var newNames = new List(); + var newScenes = new List(); + + if (m_currentScenes == null) + m_currentScenes = new Scene[0]; + + bool anyChange = SceneManager.sceneCount != m_currentScenes.Length - 1; + + for (int i = 0; i < SceneManager.sceneCount; i++) + { + Scene scene = SceneManager.GetSceneAt(i); + + if (scene == default) + continue; + + int handle = RuntimeProvider.Instance.GetSceneHandle(scene); + + if (!anyChange && !m_currentScenes.Any(it => handle == RuntimeProvider.Instance.GetSceneHandle(it))) + anyChange = true; + + newScenes.Add(scene); + newNames.Add(scene.name); + } + + if (anyChange) + { + newNames.Add("DontDestroyOnLoad"); + newScenes.Add(DontDestroyScene); + m_currentScenes = newScenes.ToArray(); + + UI.OnActiveScenesChanged(newNames); + + SetTargetScene(newScenes[0]); + + SearchPage.Instance.OnSceneChange(); + } + } + + public void SetTargetScene(int index) + => SetTargetScene(m_currentScenes[index]); + + public void SetTargetScene(Scene scene) + { + if (scene == default) + return; + + m_currentScene = scene; + var rootObjs = RuntimeProvider.Instance.GetRootGameObjects(scene); + SetSceneObjectList(rootObjs); + + m_selectedSceneObject = null; + + UI.OnSceneSelected(); + } + + public void SetSceneObjectParent() + { + if (!m_selectedSceneObject || !m_selectedSceneObject.transform.parent?.gameObject) + { + m_selectedSceneObject = null; + SetTargetScene(m_currentScene); + } + else + { + SetTargetObject(m_selectedSceneObject.transform.parent.gameObject); + } + } + + public void SetTargetObject(GameObject obj) + { + if (!obj) + return; + + UI.OnGameObjectSelected(obj); + + m_selectedSceneObject = obj; + + RefreshSelectedSceneObject(); + } + + private void RefreshSelectedSceneObject() + { + GameObject[] list = new GameObject[m_selectedSceneObject.transform.childCount]; + for (int i = 0; i < m_selectedSceneObject.transform.childCount; i++) + { + list[i] = m_selectedSceneObject.transform.GetChild(i).gameObject; + } + + SetSceneObjectList(list); + } + + private void SetSceneObjectList(GameObject[] objects) + { + m_allObjects = objects; + RefreshSceneObjectList(); + } + + internal void RefreshSceneObjectList() + { + m_timeOfLastSceneUpdate = Time.realtimeSinceStartup; + + UI.RefreshSceneObjectList(m_allObjects, out int newCount); + + m_lastCount = newCount; + } + + internal static void InspectSelectedGameObject() + { + InspectorManager.Instance.Inspect(Instance.m_selectedSceneObject); + } + + internal static void InvokeOnToggleShow() + { + OnToggleShow?.Invoke(); + } + } +} diff --git a/src/Core/Search/ChildFilter.cs b/src/Core/Search/ChildFilter.cs new file mode 100644 index 0000000..33d1bc7 --- /dev/null +++ b/src/Core/Search/ChildFilter.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UnityExplorer.Search +{ + internal enum ChildFilter + { + Any, + RootObject, + HasParent + } +} diff --git a/src/Core/Search/SceneFilter.cs b/src/Core/Search/SceneFilter.cs new file mode 100644 index 0000000..ff6a9a3 --- /dev/null +++ b/src/Core/Search/SceneFilter.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UnityExplorer.Search +{ + internal enum SceneFilter + { + Any, + Asset, + DontDestroyOnLoad, + Explicit, + } +} diff --git a/src/Core/Search/SearchContext.cs b/src/Core/Search/SearchContext.cs new file mode 100644 index 0000000..a09a33e --- /dev/null +++ b/src/Core/Search/SearchContext.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UnityExplorer.Search +{ + internal enum SearchContext + { + UnityObject, + GameObject, + Component, + Custom, + Singleton, + StaticClass + } +} diff --git a/src/Core/Search/SearchProvider.cs b/src/Core/Search/SearchProvider.cs new file mode 100644 index 0000000..7c3bdbf --- /dev/null +++ b/src/Core/Search/SearchProvider.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using UnityEngine; +using UnityExplorer.Core; +using UnityExplorer.Core.Runtime; +using UnityExplorer.UI.Main; + +namespace UnityExplorer.Search +{ + public static class SearchProvider + { + internal static object[] StaticClassSearch(string input) + { + var list = new List(); + + var nameFilter = ""; + if (!string.IsNullOrEmpty(input)) + nameFilter = input.ToLower(); + + foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) + { + foreach (var type in asm.TryGetTypes().Where(it => it.IsSealed && it.IsAbstract)) + { + if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ToLower().Contains(nameFilter)) + continue; + + list.Add(type); + } + } + + return list.ToArray(); + } + + internal static string[] s_instanceNames = new string[] + { + "m_instance", + "m_Instance", + "s_instance", + "s_Instance", + "_instance", + "_Instance", + "instance", + "Instance", + "k__BackingField", + "k__BackingField", + }; + + internal static object[] SingletonSearch(string input) + { + var instances = new List(); + + var nameFilter = ""; + if (!string.IsNullOrEmpty(input)) + nameFilter = input.ToLower(); + + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static; + + foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) + { + // Search all non-static, non-enum classes. + foreach (var type in asm.TryGetTypes().Where(it => !(it.IsSealed && it.IsAbstract) && !it.IsEnum)) + { + try + { + if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ToLower().Contains(nameFilter)) + continue; +#if CPP + // Only look for Properties in IL2CPP, not for Mono. + PropertyInfo pi; + foreach (var name in s_instanceNames) + { + pi = type.GetProperty(name, flags); + if (pi != null) + { + var instance = pi.GetValue(null, null); + if (instance != null) + { + instances.Add(instance); + continue; + } + } + } +#endif + // Look for a typical Instance backing field. + FieldInfo fi; + foreach (var name in s_instanceNames) + { + fi = type.GetField(name, flags); + if (fi != null) + { + var instance = fi.GetValue(null); + if (instance != null) + { + instances.Add(instance); + break; + } + } + } + } + catch { } + } + } + + return instances.ToArray(); + } + + internal static object[] UnityObjectSearch(string input, string customTypeInput, SearchContext context, + ChildFilter childFilter, SceneFilter sceneFilter) + { + Type searchType = null; + switch (context) + { + case SearchContext.GameObject: + searchType = typeof(GameObject); break; + + case SearchContext.Component: + searchType = typeof(Component); break; + + case SearchContext.Custom: + if (string.IsNullOrEmpty(customTypeInput)) + { + ExplorerCore.LogWarning("Custom Type input must not be empty!"); + return null; + } + if (ReflectionUtility.GetTypeByName(customTypeInput) is Type customType) + if (typeof(UnityEngine.Object).IsAssignableFrom(customType)) + searchType = customType; + else + ExplorerCore.LogWarning($"Custom type '{customType.FullName}' is not assignable from UnityEngine.Object!"); + else + ExplorerCore.LogWarning($"Could not find a type by the name '{customTypeInput}'!"); + break; + + default: + searchType = typeof(UnityEngine.Object); break; + } + + if (searchType == null) + return null; + + var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(searchType); + var results = new List(); + + // perform filter comparers + + string nameFilter = null; + if (!string.IsNullOrEmpty(input)) + nameFilter = input.ToLower(); + + bool canGetGameObject = (sceneFilter != SceneFilter.Any || childFilter != ChildFilter.Any) + && (context == SearchContext.GameObject || typeof(Component).IsAssignableFrom(searchType)); + + string sceneFilterString = null; + if (!canGetGameObject) + { + if (context != SearchContext.UnityObject && (sceneFilter != SceneFilter.Any || childFilter != ChildFilter.Any)) + ExplorerCore.LogWarning($"Type '{searchType}' cannot have Scene or Child filters applied to it"); + } + else + { + if (sceneFilter == SceneFilter.DontDestroyOnLoad) + sceneFilterString = "DontDestroyOnLoad"; + else if (sceneFilter == SceneFilter.Explicit) + sceneFilterString = SearchPage.Instance.m_sceneDropdown.options[SearchPage.Instance.m_sceneDropdown.value].text; + } + + foreach (var obj in allObjects) + { + // name check + if (!string.IsNullOrEmpty(nameFilter) && !obj.name.ToLower().Contains(nameFilter)) + continue; + + if (canGetGameObject) + { +#if MONO + var go = context == SearchContext.GameObject + ? obj as GameObject + : (obj as Component).gameObject; +#else + var go = context == SearchContext.GameObject + ? obj.TryCast() + : obj.TryCast().gameObject; +#endif + + // scene check + if (sceneFilter != SceneFilter.Any) + { + if (!go) + continue; + + switch (context) + { + case SearchContext.GameObject: + if (go.scene.name != sceneFilterString) + continue; + break; + case SearchContext.Custom: + case SearchContext.Component: + if (go.scene.name != sceneFilterString) + continue; + break; + } + } + + if (childFilter != ChildFilter.Any) + { + if (!go) + continue; + + // root object check (no parent) + if (childFilter == ChildFilter.HasParent && !go.transform.parent) + continue; + else if (childFilter == ChildFilter.RootObject && go.transform.parent) + continue; + } + } + + results.Add(obj); + } + + return results.ToArray(); + } + } +} diff --git a/src/Core/Tests/Tests.cs b/src/Core/Tests/Tests.cs new file mode 100644 index 0000000..7c38067 --- /dev/null +++ b/src/Core/Tests/Tests.cs @@ -0,0 +1,290 @@ +//using System.Collections; +//using System.Collections.Generic; +//using UnityExplorer.UI; +//using UnityEngine; +//using System; +//using System.Runtime.InteropServices; +//using System.Text; +//using UnityExplorer.Core.Runtime; + +//namespace UnityExplorer.Core.Tests +//{ +// internal enum TestByteEnum : byte +// { +// One, +// Two, +// Three, +// TwoFiftyFive = 255, +// } + +// 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 +// { +// internal static TestByteEnum testingByte = TestByteEnum.One; + +// public string AAALongString = @"1 +//2 +//3 +//4 +//5"; + +// public Vector2 AATestVector2 = new Vector2(1, 2); +// public Vector3 AATestVector3 = new Vector3(1, 2, 3); +// public Vector4 AATestVector4 = new Vector4(1, 2, 3, 4); +// public Rect AATestRect = new Rect(1, 2, 3, 4); +// public Color AATestColor = new Color(0.1f, 0.2f, 0.3f, 0.4f); + +// public bool ATestBoolMethod() => false; + +// public bool this[int index] +// { +// get => index % 2 == 0; +// set => m_thisBool = value; +// } +// internal bool m_thisBool; + +// static int testInt; +// public static List ExceptionList +// { +// get +// { +// testInt++; +// if (testInt % 2 == 0) +// throw new Exception("its even"); +// else +// return new List { "one" }; +// } +// } + +// static bool abool; +// public static bool ATestExceptionBool +// { +// get +// { +// abool = !abool; +// if (!abool) +// throw new Exception("false"); +// else +// return true; +// } +// } + +// public static string ExceptionString => throw new NotImplementedException(); + +// public static string ANullString = null; +// public static float ATestFloat = 420.69f; +// public static int ATestInt = -1; +// public static string ATestString = "hello world"; +// public static uint ATestUInt = 1u; +// public static byte ATestByte = 255; +// public static ulong AReadonlyUlong = 82934UL; + +// public static TestClass Instance => m_instance ?? (m_instance = new TestClass()); +// private static TestClass m_instance; + +// public object AmbigObject; + +// public List>> ANestedNestedList = new List>> +// { +// new List> +// { +// new List +// { +// "one", +// "two", +// }, +// new List +// { +// "three", +// "four" +// } +// }, +// new List> +// { +// new List +// { +// "five", +// "six" +// } +// } +// }; + +// public static bool SetOnlyProperty +// { +// set => m_setOnlyProperty = value; +// } +// private static bool m_setOnlyProperty; +// public static bool ReadSetOnlyProperty => m_setOnlyProperty; + +// public Texture2D TestTexture; +// public static Sprite TestSprite; + +//#if CPP +// public static Il2CppSystem.Collections.Generic.HashSet CppHashSetTest; +// public static Il2CppSystem.Collections.Generic.List CppStringTest; +// public static Il2CppSystem.Collections.IList CppIList; +// //public static Il2CppSystem.Collections.Generic.Dictionary CppDictTest; +// //public static Il2CppSystem.Collections.Generic.Dictionary CppDictTest2; +//#endif + +// public TestClass() +// { +// int a = 0; +// foreach (var list in ANestedNestedList) +// { +// foreach (var list2 in list) +// { +// for (int i = 0; i < 33; i++) +// list2.Add(a++.ToString()); +// } +// } + +//#if CPP +// //TextureSpriteTest(); + +// CppHashSetTest = new Il2CppSystem.Collections.Generic.HashSet(); +// CppHashSetTest.Add("1"); +// CppHashSetTest.Add("2"); +// CppHashSetTest.Add("3"); + +// CppStringTest = new Il2CppSystem.Collections.Generic.List(); +// CppStringTest.Add("1"); +// CppStringTest.Add("2"); + +// //CppDictTest = new Il2CppSystem.Collections.Generic.Dictionary(); +// //CppDictTest.Add("key1", "value1"); +// //CppDictTest.Add("key2", "value2"); +// //CppDictTest.Add("key3", "value3"); + +// //CppDictTest2 = new Il2CppSystem.Collections.Generic.Dictionary(); +// //CppDictTest2.Add(0, 0.5f); +// //CppDictTest2.Add(1, 0.5f); +// //CppDictTest2.Add(2, 0.5f); +//#endif +// } + +// //private void TextureSpriteTest() +// //{ +// // TestTexture = new Texture2D(32, 32, TextureFormat.ARGB32, false) +// // { +// // name = "TestTexture" +// // }; +// // TestSprite = TextureUtilProvider.Instance.CreateSprite(TestTexture); + +// // GameObject.DontDestroyOnLoad(TestTexture); +// // GameObject.DontDestroyOnLoad(TestSprite); + +// // // test loading a tex from file +// // if (System.IO.File.Exists(@"D:\Downloads\test.png")) +// // { +// // var dataToLoad = System.IO.File.ReadAllBytes(@"D:\Downloads\test.png"); +// // ExplorerCore.Log($"Tex load success: {TestTexture.LoadImage(dataToLoad, false)}"); +// // } +// //} + +// //public static string TestRefInOutGeneric(ref string arg0, in int arg1, out string arg2) where T : Component +// //{ +// // arg2 = "this is arg2"; + +// // return $"T: '{typeof(T).FullName}', ref arg0: '{arg0}', in arg1: '{arg1}', out arg2: '{arg2}'"; +// //} + +// // test a non-generic dictionary + +// public Hashtable TestNonGenericDict() +// { +// return new Hashtable +// { +// { "One", 1 }, +// { "Two", 2 }, +// { "Three", 3 }, +// }; +// } + +// // test HashSets + +// public static HashSet HashSetTest = new HashSet +// { +// "One", +// "Two", +// "Three" +// }; + + +// // Test indexed parameter + +// public string this[int arg0, string arg1] +// { +// get +// { +// return $"arg0: {arg0}, arg1: {arg1}"; +// } +// } + +// // Test basic list + +// public static List TestList = new List +// { +// "1", +// "2", +// "3", +// "etc..." +// }; + +// // Test a nested dictionary + +// public static Dictionary> NestedDictionary = new Dictionary> +// { +// { +// 1, +// new Dictionary +// { +// { +// "Sub 1", 123 +// }, +// { +// "Sub 2", 456 +// }, +// } +// }, +// { +// 2, +// new Dictionary +// { +// { +// "Sub 3", 789 +// }, +// { +// "Sub 4", 000 +// }, +// } +// }, +// }; + +// // Test a basic method + +// public static Color TestMethod(float r, float g, float b, float a) +// { +// return new Color(r, g, b, a); +// } + +// // A method with default arguments + +// public static Vector3 TestDefaultArgs(float arg0, float arg1, float arg2 = 5.0f) +// { +// return new Vector3(arg0, arg1, arg2); +// } +// } +//} diff --git a/src/Unstrip/ColorUtilityUnstrip.cs b/src/Core/Unity/ColorHelper.cs similarity index 95% rename from src/Unstrip/ColorUtilityUnstrip.cs rename to src/Core/Unity/ColorHelper.cs index d976be2..cd234c6 100644 --- a/src/Unstrip/ColorUtilityUnstrip.cs +++ b/src/Core/Unity/ColorHelper.cs @@ -1,9 +1,9 @@ using System.Globalization; using UnityEngine; -namespace UnityExplorer.Unstrip +namespace UnityExplorer.Core.Unity { - public static class ColorUtilityUnstrip + public static class ColorHelper { /// /// Converts Color to 6-digit RGB hex code (without # symbol). Eg, RGBA(1,0,0,1) -> FF0000 diff --git a/src/Helpers/UnityHelpers.cs b/src/Core/Unity/UnityHelper.cs similarity index 95% rename from src/Helpers/UnityHelpers.cs rename to src/Core/Unity/UnityHelper.cs index b1a3d8c..f95231d 100644 --- a/src/Helpers/UnityHelpers.cs +++ b/src/Core/Unity/UnityHelper.cs @@ -1,8 +1,8 @@ using UnityEngine; -namespace UnityExplorer.Helpers +namespace UnityExplorer.Core.Unity { - public static class UnityHelpers + public static class UnityHelper { private static Camera m_mainCamera; diff --git a/src/ExplorerCore.cs b/src/ExplorerCore.cs index c67558a..4b5df91 100644 --- a/src/ExplorerCore.cs +++ b/src/ExplorerCore.cs @@ -3,13 +3,14 @@ using System.IO; using System.Reflection; using UnityEngine; using UnityEngine.SceneManagement; -using UnityExplorer.Config; -using UnityExplorer.Helpers; -using UnityExplorer.Input; -using UnityExplorer.Inspectors; -using UnityExplorer.Runtime; +using UnityExplorer.Core.Config; +using UnityExplorer.Core.Unity; +using UnityExplorer.Core.Input; +using UnityExplorer.Core.Inspectors; +using UnityExplorer.Core.Runtime; using UnityExplorer.UI; -using UnityExplorer.UI.Modules; +using UnityExplorer.UI.Main; +using UnityExplorer.UI.Utility; namespace UnityExplorer { @@ -52,7 +53,7 @@ namespace UnityExplorer InputManager.Init(); - ForceUnlockCursor.Init(); + CursorUnlocker.Init(); UIManager.ShowMenu = true; @@ -63,8 +64,8 @@ namespace UnityExplorer { UIManager.CheckUIInit(); - if (MouseInspector.Enabled) - MouseInspector.UpdateInspect(); + if (InspectUnderMouse.Enabled) + InspectUnderMouse.UpdateInspect(); else UIManager.Update(); } diff --git a/src/Inspectors/InspectorBase.cs b/src/Inspectors/InspectorBase.cs deleted file mode 100644 index 8a3fb25..0000000 --- a/src/Inspectors/InspectorBase.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using UnityEngine; -using UnityEngine.UI; -using UnityExplorer.Helpers; -using UnityExplorer.UI; - -namespace UnityExplorer.Inspectors -{ - public abstract class InspectorBase - { - public object Target; - - public abstract string TabLabel { get; } - - public bool IsActive { get; private set; } - public abstract GameObject Content { get; set; } - public Button tabButton; - public Text tabText; - - internal bool m_pendingDestroy; - - public InspectorBase(object target) - { - Target = target; - - if (Target.IsNullOrDestroyed(false)) - { - Destroy(); - return; - } - - AddInspectorTab(); - } - - public virtual void SetActive() - { - this.IsActive = true; - Content?.SetActive(true); - } - - public virtual void SetInactive() - { - this.IsActive = false; - Content?.SetActive(false); - } - - public virtual void Update() - { - if (Target.IsNullOrDestroyed(false)) - { - Destroy(); - return; - } - - tabText.text = TabLabel; - } - - public virtual void Destroy() - { - m_pendingDestroy = true; - - GameObject tabGroup = tabButton?.transform.parent.gameObject; - - if (tabGroup) - { - GameObject.Destroy(tabGroup); - } - - int thisIndex = -1; - if (InspectorManager.Instance.m_currentInspectors.Contains(this)) - { - thisIndex = InspectorManager.Instance.m_currentInspectors.IndexOf(this); - InspectorManager.Instance.m_currentInspectors.Remove(this); - } - - if (ReferenceEquals(InspectorManager.Instance.m_activeInspector, this)) - { - InspectorManager.Instance.UnsetInspectorTab(); - - if (InspectorManager.Instance.m_currentInspectors.Count > 0) - { - var prevTab = InspectorManager.Instance.m_currentInspectors[thisIndex > 0 ? thisIndex - 1 : 0]; - InspectorManager.Instance.SetInspectorTab(prevTab); - } - } - } - - - - #region UI CONSTRUCTION - - public void AddInspectorTab() - { - var tabContent = InspectorManager.Instance.m_tabBarContent; - - var tabGroupObj = UIFactory.CreateHorizontalGroup(tabContent); - var tabGroup = tabGroupObj.GetComponent(); - tabGroup.childForceExpandWidth = true; - tabGroup.childControlWidth = true; - var tabLayout = tabGroupObj.AddComponent(); - tabLayout.minWidth = 185; - tabLayout.flexibleWidth = 0; - tabGroupObj.AddComponent(); - - var targetButtonObj = UIFactory.CreateButton(tabGroupObj); - targetButtonObj.AddComponent(); - var targetButtonLayout = targetButtonObj.AddComponent(); - targetButtonLayout.minWidth = 165; - targetButtonLayout.flexibleWidth = 0; - - tabText = targetButtonObj.GetComponentInChildren(); - tabText.horizontalOverflow = HorizontalWrapMode.Overflow; - tabText.alignment = TextAnchor.MiddleLeft; - - tabButton = targetButtonObj.GetComponent