diff --git a/README.md b/README.md index 6dcde5c..50b4625 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,9 @@

✔️ Supports most Unity versions from 5.2 to 2021+ (IL2CPP and Mono).

+

+ ✨ Powered by [UniverseLib](https://github.com/sinai-dev/UniverseLib) +

# Releases [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/total.svg)](../../releases) @@ -47,6 +50,21 @@ The standalone release can be used with any injector or loader of your choice, b 3. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();` 4. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish +# Common issues and solutions + +Although UnityExplorer should work out of the box for most Unity games, in some cases you may need to tweak the settings for it to work properly. + +To adjust the settings, open the config file: +* BepInEx: `BepInEx\config\com.sinai.unityexplorer.cfg` +* MelonLoader: `UserData\MelonPreferences.cfg` +* Standalone: `UnityExplorer\config.ini` + +Try adjusting the following settings and see if it fixes your issues: +* `Startup_Delay_Time` - increase to 5-10 seconds (or more as needed), can fix issues with UnityExplorer being destroyed or corrupted during startup. +* `Disable_EventSystem_Override` - if input is not working properly, try setting this to `true`. + +If these fixes do not work, please create an issue in this repo and I'll do my best to look into it. + # Features

diff --git a/src/CSConsole/CSAutoCompleter.cs b/src/CSConsole/CSAutoCompleter.cs index af0854b..6ab828c 100644 --- a/src/CSConsole/CSAutoCompleter.cs +++ b/src/CSConsole/CSAutoCompleter.cs @@ -6,6 +6,8 @@ using UnityEngine; using UnityExplorer.CSConsole.Lexers; using UnityExplorer.UI; using UnityExplorer.UI.Widgets.AutoComplete; +using UniverseLib; +using UniverseLib.UI; namespace UnityExplorer.CSConsole { diff --git a/src/CSConsole/ConsoleController.cs b/src/CSConsole/ConsoleController.cs index f9c53b1..ff76ad7 100644 --- a/src/CSConsole/ConsoleController.cs +++ b/src/CSConsole/ConsoleController.cs @@ -9,11 +9,13 @@ using System.Text; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; -using UnityExplorer.Core.Input; +using UniverseLib.Input; using UnityExplorer.CSConsole; using UnityExplorer.UI; using UnityExplorer.UI.Panels; using UnityExplorer.UI.Widgets.AutoComplete; +using UniverseLib.UI; +using UniverseLib; namespace UnityExplorer.CSConsole { diff --git a/src/CSConsole/LexerBuilder.cs b/src/CSConsole/LexerBuilder.cs index bbe1daa..4222e38 100644 --- a/src/CSConsole/LexerBuilder.cs +++ b/src/CSConsole/LexerBuilder.cs @@ -6,6 +6,7 @@ using System.Text; using UnityEngine; using UnityEngine.UI; using UnityExplorer.CSConsole.Lexers; +using UniverseLib; namespace UnityExplorer.CSConsole { diff --git a/src/CSConsole/Lexers/Lexer.cs b/src/CSConsole/Lexers/Lexer.cs index 056ebc7..591f63c 100644 --- a/src/CSConsole/Lexers/Lexer.cs +++ b/src/CSConsole/Lexers/Lexer.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; +using UniverseLib; namespace UnityExplorer.CSConsole.Lexers { diff --git a/src/CSConsole/ScriptInteraction.cs b/src/CSConsole/ScriptInteraction.cs index 9688c26..c8b1bda 100644 --- a/src/CSConsole/ScriptInteraction.cs +++ b/src/CSConsole/ScriptInteraction.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text; using UnityEngine; using UnityExplorer.Core.Runtime; +using UniverseLib; namespace UnityExplorer.CSConsole { diff --git a/src/CacheObject/CacheKeyValuePair.cs b/src/CacheObject/CacheKeyValuePair.cs index 3665c58..1a3dd9d 100644 --- a/src/CacheObject/CacheKeyValuePair.cs +++ b/src/CacheObject/CacheKeyValuePair.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using UnityExplorer.CacheObject.IValues; using UnityExplorer.CacheObject.Views; +using UniverseLib; namespace UnityExplorer.CacheObject { diff --git a/src/CacheObject/CacheMember.cs b/src/CacheObject/CacheMember.cs index 4b529b5..20f3241 100644 --- a/src/CacheObject/CacheMember.cs +++ b/src/CacheObject/CacheMember.cs @@ -7,8 +7,10 @@ using UnityEngine; using UnityExplorer.Core.Runtime; using UnityExplorer.CacheObject.Views; using UnityExplorer.Inspectors; -using UnityExplorer.UI.Models; +using UniverseLib.UI.Models; using UnityExplorer.UI; +using UniverseLib; +using UniverseLib.UI; namespace UnityExplorer.CacheObject { @@ -230,7 +232,7 @@ namespace UnityExplorer.CacheObject { try { - if (ReflectionUtility.IsBlacklisted(member)) + if (RuntimeHelper.IsBlacklisted(member)) return; var sig = GetSig(member); diff --git a/src/CacheObject/CacheMethod.cs b/src/CacheObject/CacheMethod.cs index 36556cb..8ffaabe 100644 --- a/src/CacheObject/CacheMethod.cs +++ b/src/CacheObject/CacheMethod.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Text; using UnityExplorer.Inspectors; +using UniverseLib; namespace UnityExplorer.CacheObject { diff --git a/src/CacheObject/CacheObjectBase.cs b/src/CacheObject/CacheObjectBase.cs index 8caa1e3..601a9a8 100644 --- a/src/CacheObject/CacheObjectBase.cs +++ b/src/CacheObject/CacheObjectBase.cs @@ -9,8 +9,10 @@ using UnityEngine.UI; using UnityExplorer.Core.Runtime; using UnityExplorer.CacheObject.IValues; using UnityExplorer.CacheObject.Views; -using UnityExplorer.UI.Models; +using UniverseLib.UI.Models; using UnityExplorer.UI; +using UniverseLib; +using UniverseLib.UI; namespace UnityExplorer.CacheObject { @@ -442,7 +444,7 @@ namespace UnityExplorer.CacheObject { inactiveIValueHolder = new GameObject("Temp_IValue_Holder"); GameObject.DontDestroyOnLoad(inactiveIValueHolder); - inactiveIValueHolder.transform.parent = UIManager.PoolHolder.transform; + inactiveIValueHolder.transform.parent = UniversalUI.PoolHolder.transform; inactiveIValueHolder.SetActive(false); } return inactiveIValueHolder; diff --git a/src/CacheObject/IValues/InteractiveColor.cs b/src/CacheObject/IValues/InteractiveColor.cs index 9cd6fe3..5d5be0d 100644 --- a/src/CacheObject/IValues/InteractiveColor.cs +++ b/src/CacheObject/IValues/InteractiveColor.cs @@ -6,6 +6,7 @@ using UnityEngine; using UnityEngine.UI; using UnityExplorer.CacheObject; using UnityExplorer.UI; +using UniverseLib.UI; namespace UnityExplorer.CacheObject.IValues { diff --git a/src/CacheObject/IValues/InteractiveDictionary.cs b/src/CacheObject/IValues/InteractiveDictionary.cs index 046b50b..82d0916 100644 --- a/src/CacheObject/IValues/InteractiveDictionary.cs +++ b/src/CacheObject/IValues/InteractiveDictionary.cs @@ -11,6 +11,9 @@ using UnityExplorer.Inspectors; using UnityExplorer.UI; using UnityExplorer.UI.Panels; using UnityExplorer.UI.Widgets; +using UniverseLib; +using UniverseLib.UI; +using UniverseLib.UI.Widgets; namespace UnityExplorer.CacheObject.IValues { diff --git a/src/CacheObject/IValues/InteractiveEnum.cs b/src/CacheObject/IValues/InteractiveEnum.cs index 14d1ca2..9a67c5e 100644 --- a/src/CacheObject/IValues/InteractiveEnum.cs +++ b/src/CacheObject/IValues/InteractiveEnum.cs @@ -7,6 +7,7 @@ using UnityEngine; using UnityEngine.UI; using UnityExplorer.CacheObject; using UnityExplorer.UI; +using UniverseLib.UI; namespace UnityExplorer.CacheObject.IValues { diff --git a/src/CacheObject/IValues/InteractiveList.cs b/src/CacheObject/IValues/InteractiveList.cs index 1b69d16..17d3d95 100644 --- a/src/CacheObject/IValues/InteractiveList.cs +++ b/src/CacheObject/IValues/InteractiveList.cs @@ -11,6 +11,9 @@ using UnityExplorer.Inspectors; using UnityExplorer.UI; using UnityExplorer.UI.Panels; using UnityExplorer.UI.Widgets; +using UniverseLib; +using UniverseLib.UI; +using UniverseLib.UI.Widgets; namespace UnityExplorer.CacheObject.IValues { diff --git a/src/CacheObject/IValues/InteractiveString.cs b/src/CacheObject/IValues/InteractiveString.cs index 269909c..90bb003 100644 --- a/src/CacheObject/IValues/InteractiveString.cs +++ b/src/CacheObject/IValues/InteractiveString.cs @@ -9,6 +9,8 @@ using UnityExplorer.Core.Config; using UnityExplorer.CacheObject; using UnityExplorer.UI.Widgets; using UnityExplorer.UI; +using UniverseLib.UI; +using UniverseLib; namespace UnityExplorer.CacheObject.IValues { @@ -38,7 +40,7 @@ namespace UnityExplorer.CacheObject.IValues if (s == null) return false; - return s.Length >= UIManager.MAX_INPUTFIELD_CHARS; + return s.Length >= UniversalUI.MAX_INPUTFIELD_CHARS; } public override void SetValue(object value) diff --git a/src/CacheObject/IValues/InteractiveValue.cs b/src/CacheObject/IValues/InteractiveValue.cs index de88cc6..cc7fd0d 100644 --- a/src/CacheObject/IValues/InteractiveValue.cs +++ b/src/CacheObject/IValues/InteractiveValue.cs @@ -6,7 +6,8 @@ using UnityEngine; using UnityEngine.UI; using UnityExplorer.CacheObject; using UnityExplorer.UI; -using UnityExplorer.UI.Models; +using UniverseLib.UI; +using UniverseLib.UI.Models; namespace UnityExplorer.CacheObject.IValues { diff --git a/src/CacheObject/IValues/InteractiveValueStruct.cs b/src/CacheObject/IValues/InteractiveValueStruct.cs index 6c1cd03..6a3e8a2 100644 --- a/src/CacheObject/IValues/InteractiveValueStruct.cs +++ b/src/CacheObject/IValues/InteractiveValueStruct.cs @@ -7,6 +7,8 @@ using UnityEngine; using UnityEngine.UI; using UnityExplorer.CacheObject; using UnityExplorer.UI; +using UniverseLib; +using UniverseLib.UI; namespace UnityExplorer.CacheObject.IValues { diff --git a/src/CacheObject/Views/CacheConfigCell.cs b/src/CacheObject/Views/CacheConfigCell.cs index bd4b3a1..b9884a9 100644 --- a/src/CacheObject/Views/CacheConfigCell.cs +++ b/src/CacheObject/Views/CacheConfigCell.cs @@ -5,6 +5,8 @@ using System.Text; using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI; +using UniverseLib; +using UniverseLib.UI; namespace UnityExplorer.CacheObject.Views { diff --git a/src/CacheObject/Views/CacheKeyValuePairCell.cs b/src/CacheObject/Views/CacheKeyValuePairCell.cs index cf14957..a356c8e 100644 --- a/src/CacheObject/Views/CacheKeyValuePairCell.cs +++ b/src/CacheObject/Views/CacheKeyValuePairCell.cs @@ -8,6 +8,7 @@ using UnityExplorer.CacheObject.IValues; using UnityExplorer.Inspectors; using UnityExplorer.UI; using UnityExplorer.UI.Widgets; +using UniverseLib.UI; namespace UnityExplorer.CacheObject.Views { diff --git a/src/CacheObject/Views/CacheMemberCell.cs b/src/CacheObject/Views/CacheMemberCell.cs index 7710a85..1a1f695 100644 --- a/src/CacheObject/Views/CacheMemberCell.cs +++ b/src/CacheObject/Views/CacheMemberCell.cs @@ -6,6 +6,7 @@ using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI; using UnityExplorer.UI.Widgets; +using UniverseLib.UI; namespace UnityExplorer.CacheObject.Views { diff --git a/src/CacheObject/Views/CacheObjectCell.cs b/src/CacheObject/Views/CacheObjectCell.cs index 6021b21..75f8fca 100644 --- a/src/CacheObject/Views/CacheObjectCell.cs +++ b/src/CacheObject/Views/CacheObjectCell.cs @@ -8,6 +8,9 @@ using UnityExplorer.CacheObject.IValues; using UnityExplorer.Inspectors; using UnityExplorer.UI; using UnityExplorer.UI.Widgets; +using UniverseLib; +using UniverseLib.UI; +using UniverseLib.UI.Widgets; namespace UnityExplorer.CacheObject.Views { diff --git a/src/CacheObject/Views/EvaluateWidget.cs b/src/CacheObject/Views/EvaluateWidget.cs index 0e08f7d..23cdac6 100644 --- a/src/CacheObject/Views/EvaluateWidget.cs +++ b/src/CacheObject/Views/EvaluateWidget.cs @@ -6,8 +6,10 @@ using System.Text; using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI; -using UnityExplorer.UI.Models; +using UniverseLib.UI.Models; using UnityExplorer.UI.Widgets.AutoComplete; +using UniverseLib.UI; +using UniverseLib; namespace UnityExplorer.CacheObject.Views { diff --git a/src/Core/Config/ConfigManager.cs b/src/Core/Config/ConfigManager.cs index aaa2e7b..bd6b87e 100644 --- a/src/Core/Config/ConfigManager.cs +++ b/src/Core/Config/ConfigManager.cs @@ -89,18 +89,22 @@ namespace UnityExplorer.Core.Config Force_Unlock_Mouse = new ConfigElement("Force Unlock Mouse", "Force the Cursor to be unlocked (visible) when the UnityExplorer menu is open.", true); + Force_Unlock_Mouse.OnValueChanged += (bool value) => + { + UniverseLib.Config.ConfigManager.Force_Unlock_Mouse = value; + }; Force_Unlock_Toggle = new ConfigElement("Force Unlock Toggle Key", "The keybind to toggle the 'Force Unlock Mouse' setting. Only usable when UnityExplorer is open.", KeyCode.None); - Aggressive_Mouse_Unlock = new ConfigElement("Aggressive Mouse Unlock", - "Use WaitForEndOfFrame to aggressively force the Mouse to be unlocked.\nRequires restart to take effect.", - false); - Disable_EventSystem_Override = new ConfigElement("Disable EventSystem override", "If enabled, UnityExplorer will not override the EventSystem from the game.\nMay require restart to take effect.", false); + Disable_EventSystem_Override.OnValueChanged += (bool value) => + { + UniverseLib.Config.ConfigManager.Disable_EventSystem_Override = value; + }; Log_Unity_Debug = new ConfigElement("Log Unity Debug", "Should UnityEngine.Debug.Log messages be printed to UnityExplorer's log?", diff --git a/src/Core/ExplorerBehaviour.cs b/src/Core/ExplorerBehaviour.cs index dc81a1c..8d25c65 100644 --- a/src/Core/ExplorerBehaviour.cs +++ b/src/Core/ExplorerBehaviour.cs @@ -32,53 +32,9 @@ namespace UnityExplorer public ExplorerBehaviour(IntPtr ptr) : base(ptr) { } #endif - private static bool onPostRenderFailed; - - internal void Awake() - { - try - { -#if CPP - Camera.onPostRender = Camera.onPostRender == null - ? new Action(OnPostRender) - : Il2CppSystem.Delegate.Combine(Camera.onPostRender, - (Camera.CameraCallback)new Action(OnPostRender)).Cast(); - - if (Camera.onPostRender == null || Camera.onPostRender.delegates == null) - { - ExplorerCore.LogWarning("Failed to add Camera.onPostRender listener, falling back to LateUpdate instead!"); - onPostRenderFailed = true; - } -#else - Camera.onPostRender += OnPostRender; -#endif - } - catch (Exception ex) - { - ExplorerCore.LogWarning($"Exception adding onPostRender listener: {ex.ReflectionExToString()}\r\nFalling back to LateUpdate!"); - onPostRenderFailed = true; - } - } - internal void Update() { ExplorerCore.Update(); } - - internal void FixedUpdate() - { - ExplorerCore.FixedUpdate(); - } - - internal void LateUpdate() - { - if (onPostRenderFailed) - OnPostRender(null); - } - - internal static void OnPostRender(Camera _) - { - ExplorerCore.OnPostRender(); - } } } diff --git a/src/Core/Input/CursorUnlocker.cs b/src/Core/Input/CursorUnlocker.cs deleted file mode 100644 index 4f7d891..0000000 --- a/src/Core/Input/CursorUnlocker.cs +++ /dev/null @@ -1,281 +0,0 @@ -using HarmonyLib; -using System; -using System.Collections; -using UnityEngine; -using UnityEngine.EventSystems; -using UnityExplorer.Core; -using UnityExplorer.Core.Config; -using UnityExplorer.Core.Input; -using UnityExplorer.UI; - -namespace UnityExplorer.Core.Input -{ - public class CursorUnlocker - { - public static bool Unlock - { - get => m_forceUnlock; - set - { - m_forceUnlock = value; - UpdateCursorControl(); - } - } - private static bool m_forceUnlock; - - public static bool ShouldActuallyUnlock => UIManager.ShowMenu && Unlock; - - private static CursorLockMode lastLockMode; - private static bool lastVisibleState; - - private static bool currentlySettingCursor = false; - - public static void Init() - { - lastLockMode = Cursor.lockState; - lastVisibleState = Cursor.visible; - - SetupPatches(); - UpdateCursorControl(); - - // Hook up config values - - // Force Unlock Mouse - Unlock = ConfigManager.Force_Unlock_Mouse.Value; - ConfigManager.Force_Unlock_Mouse.OnValueChanged += (bool val) => { Unlock = val; }; - - // Aggressive Mouse Unlock - if (ConfigManager.Aggressive_Mouse_Unlock.Value) - SetupAggressiveUnlock(); - } - - public static void SetupAggressiveUnlock() - { - try - { - RuntimeProvider.Instance.StartCoroutine(AggressiveUnlockCoroutine()); - } - catch (Exception ex) - { - ExplorerCore.LogWarning($"Exception setting up Aggressive Mouse Unlock: {ex}"); - } - } - - private static WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame(); - - private static IEnumerator AggressiveUnlockCoroutine() - { - while (true) - { - yield return _waitForEndOfFrame ?? (_waitForEndOfFrame = new WaitForEndOfFrame()); - - if (UIManager.ShowMenu) - UpdateCursorControl(); - } - } - - public static void UpdateCursorControl() - { - try - { - currentlySettingCursor = true; - - if (ShouldActuallyUnlock) - { - Cursor.lockState = CursorLockMode.None; - Cursor.visible = true; - - if (!ConfigManager.Disable_EventSystem_Override.Value && UIManager.EventSys) - SetEventSystem(); - } - else - { - Cursor.lockState = lastLockMode; - Cursor.visible = lastVisibleState; - - if (!ConfigManager.Disable_EventSystem_Override.Value && UIManager.EventSys) - ReleaseEventSystem(); - } - - currentlySettingCursor = false; - } - catch (Exception e) - { - ExplorerCore.Log($"Exception setting Cursor state: {e.GetType()}, {e.Message}"); - } - } - - // Event system overrides - - private static bool settingEventSystem; - private static EventSystem lastEventSystem; - private static BaseInputModule lastInputModule; - - public static void SetEventSystem() - { - if (EventSystem.current && EventSystem.current != UIManager.EventSys) - { - lastEventSystem = EventSystem.current; - lastEventSystem.enabled = false; - } - - // Set to our current system - settingEventSystem = true; - UIManager.EventSys.enabled = true; - EventSystem.current = UIManager.EventSys; - InputManager.ActivateUIModule(); - settingEventSystem = false; - } - - public static void ReleaseEventSystem() - { - if (lastEventSystem && lastEventSystem.gameObject.activeSelf) - { - lastEventSystem.enabled = true; - - settingEventSystem = true; - UIManager.EventSys.enabled = false; - EventSystem.current = lastEventSystem; - lastInputModule?.ActivateModule(); - settingEventSystem = false; - } - } - - // Patches - - public static void SetupPatches() - { - try - { - PrefixPropertySetter(typeof(Cursor), - "lockState", - new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_lockState)))); - - PrefixPropertySetter(typeof(Cursor), - "visible", - new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_visible)))); - - PrefixPropertySetter(typeof(EventSystem), - "current", - new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_EventSystem_set_current)))); - - PrefixMethod(typeof(EventSystem), - "SetSelectedGameObject", - // some games use a modified version of uGUI that includes this extra int argument on this method. - new Type[] { typeof(GameObject), typeof(BaseEventData), typeof(int) }, - new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_EventSystem_SetSelectedGameObject))), - // most games use these arguments, we'll use them as our "backup". - new Type[] { typeof(GameObject), typeof(BaseEventData) }); - - //// Not sure if this one is needed. - //PrefixMethod(typeof(PointerInputModule), - // "ClearSelection", - // new Type[0], - // new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_PointerInputModule_ClearSelection)))); - } - catch (Exception ex) - { - ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}"); - } - } - - private static void PrefixMethod(Type type, string method, Type[] arguments, HarmonyMethod prefix, Type[] backupArgs = null) - { - try - { - var methodInfo = type.GetMethod(method, ReflectionUtility.FLAGS, null, arguments, null); - if (methodInfo == null) - { - if (backupArgs != null) - methodInfo = type.GetMethod(method, ReflectionUtility.FLAGS, null, backupArgs, null); - - if (methodInfo == null) - throw new MissingMethodException($"Could not find method for patching - '{type.FullName}.{method}'!"); - } - - var processor = ExplorerCore.Harmony.CreateProcessor(methodInfo); - processor.AddPrefix(prefix); - processor.Patch(); - } - catch (Exception e) - { - ExplorerCore.LogWarning($"Unable to patch {type.Name}.{method}: {e.Message}"); - } - } - - private static void PrefixPropertySetter(Type type, string property, HarmonyMethod prefix) - { - try - { - var processor = ExplorerCore.Harmony.CreateProcessor(type.GetProperty(property, ReflectionUtility.FLAGS).GetSetMethod()); - processor.AddPrefix(prefix); - processor.Patch(); - } - catch (Exception e) - { - ExplorerCore.Log($"Unable to patch {type.Name}.set_{property}: {e.Message}"); - } - } - - // Prevent setting non-UnityExplorer objects as selected when menu is open - - public static bool Prefix_EventSystem_SetSelectedGameObject(GameObject __0) - { - if (!UIManager.ShowMenu || !UIManager.CanvasRoot) - return true; - - return __0 && __0.transform.root.gameObject.GetInstanceID() == UIManager.CanvasRoot.GetInstanceID(); - } - - //public static bool Prefix_PointerInputModule_ClearSelection() - //{ - // return !(UIManager.ShowMenu && UIManager.CanvasRoot); - //} - - // Force EventSystem.current to be UnityExplorer's when menu is open - - public static void Prefix_EventSystem_set_current(ref EventSystem value) - { - if (!settingEventSystem && value) - { - lastEventSystem = value; - lastInputModule = value.currentInputModule; - } - - if (!UIManager.EventSys) - return; - - if (!settingEventSystem && ShouldActuallyUnlock && !ConfigManager.Disable_EventSystem_Override.Value) - { - value = UIManager.EventSys; - value.enabled = true; - } - } - - // Force mouse to stay unlocked and visible while UnlockMouse and ShowMenu are true. - // Also keep track of when anything else tries to set Cursor state, this will be the - // value that we set back to when we close the menu or disable force-unlock. - - public static void Prefix_set_lockState(ref CursorLockMode value) - { - if (!currentlySettingCursor) - { - lastLockMode = value; - - if (ShouldActuallyUnlock) - value = CursorLockMode.None; - } - } - - public static void Prefix_set_visible(ref bool value) - { - if (!currentlySettingCursor) - { - lastVisibleState = value; - - if (ShouldActuallyUnlock) - value = true; - } - } - } -} \ No newline at end of file diff --git a/src/Core/Input/IHandleInput.cs b/src/Core/Input/IHandleInput.cs deleted file mode 100644 index 3e9b592..0000000 --- a/src/Core/Input/IHandleInput.cs +++ /dev/null @@ -1,22 +0,0 @@ -using UnityEngine; -using UnityEngine.EventSystems; - -namespace UnityExplorer.Core.Input -{ - public interface IHandleInput - { - Vector2 MousePosition { get; } - Vector2 MouseScrollDelta { get; } - - bool GetKeyDown(KeyCode key); - bool GetKey(KeyCode key); - - bool GetMouseButtonDown(int btn); - bool GetMouseButton(int btn); - - BaseInputModule UIInputModule { get; } - - void AddUIInputModule(); - void ActivateModule(); - } -} \ No newline at end of file diff --git a/src/Core/Input/InputManager.cs b/src/Core/Input/InputManager.cs deleted file mode 100644 index 1623244..0000000 --- a/src/Core/Input/InputManager.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using UnityEngine; -using UnityEngine.EventSystems; -using UnityExplorer.UI; - -namespace UnityExplorer.Core.Input -{ - public enum InputType - { - InputSystem, - Legacy, - None - } - - public static class InputManager - { - public static InputType CurrentType { get; private set; } - - private static IHandleInput m_inputHandler; - - public static Vector3 MousePosition => m_inputHandler.MousePosition; - - public static bool GetKeyDown(KeyCode key) - { - if (key == KeyCode.None) - return false; - return m_inputHandler.GetKeyDown(key); - } - - public static bool GetKey(KeyCode key) - { - if (key == KeyCode.None) - return false; - return m_inputHandler.GetKey(key); - } - - public static bool GetMouseButtonDown(int btn) => m_inputHandler.GetMouseButtonDown(btn); - public static bool GetMouseButton(int btn) => m_inputHandler.GetMouseButton(btn); - - public static BaseInputModule UIInput => m_inputHandler.UIInputModule; - - public static Vector2 MouseScrollDelta => m_inputHandler.MouseScrollDelta; - - public static void AddUIModule() - { - m_inputHandler.AddUIInputModule(); - ActivateUIModule(); - } - - public static void ActivateUIModule() - { - UIManager.EventSys.m_CurrentInputModule = UIInput; - m_inputHandler.ActivateModule(); - } - - public static void Init() - { - InitHandler(); - - CursorUnlocker.Init(); - } - - private static void InitHandler() - { - // First, just try to use the legacy input, see if its working. - // The InputSystem package may be present but not actually activated, so we can find out this way. - - if (LegacyInput.TInput != null) - { - try - { - m_inputHandler = new LegacyInput(); - CurrentType = InputType.Legacy; - - // make sure its working - GetKeyDown(KeyCode.F5); - - ExplorerCore.Log("Initialized Legacy Input support"); - return; - } - catch - { - // It's not working, we'll fall back to InputSystem. - } - } - - if (InputSystem.TKeyboard != null) - { - try - { - m_inputHandler = new InputSystem(); - CurrentType = InputType.InputSystem; - ExplorerCore.Log("Initialized new InputSystem support."); - return; - } - catch (Exception ex) - { - ExplorerCore.Log(ex); - } - } - - ExplorerCore.LogWarning("Could not find any Input Module Type!"); - m_inputHandler = new NoInput(); - CurrentType = InputType.None; - } - } -} \ No newline at end of file diff --git a/src/Core/Input/InputSystem.cs b/src/Core/Input/InputSystem.cs deleted file mode 100644 index 85c3cc5..0000000 --- a/src/Core/Input/InputSystem.cs +++ /dev/null @@ -1,280 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using UnityEngine; -using UnityEngine.EventSystems; -using UnityExplorer.UI; - -namespace UnityExplorer.Core.Input -{ - public class InputSystem : IHandleInput - { - public InputSystem() - { - SetupSupportedDevices(); - - m_kbCurrentProp = TKeyboard.GetProperty("current"); - m_kbIndexer = TKeyboard.GetProperty("Item", new Type[] { TKey }); - - var btnControl = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Controls.ButtonControl"); - m_btnIsPressedProp = btnControl.GetProperty("isPressed"); - m_btnWasPressedProp = btnControl.GetProperty("wasPressedThisFrame"); - - m_mouseCurrentProp = TMouse.GetProperty("current"); - m_leftButtonProp = TMouse.GetProperty("leftButton"); - m_rightButtonProp = TMouse.GetProperty("rightButton"); - m_scrollDeltaProp = TMouse.GetProperty("scroll"); - - m_positionProp = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Pointer") - .GetProperty("position"); - - ReadV2ControlMethod = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputControl`1") - .MakeGenericType(typeof(Vector2)) - .GetMethod("ReadValue"); - } - - internal static void SetupSupportedDevices() - { - try - { - // typeof(InputSystem) - Type TInputSystem = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputSystem"); - // InputSystem.settings - var settings = TInputSystem.GetProperty("settings", BindingFlags.Public | BindingFlags.Static).GetValue(null, null); - // typeof(InputSettings) - Type TSettings = settings.GetActualType(); - // InputSettings.supportedDevices - PropertyInfo supportedProp = TSettings.GetProperty("supportedDevices", BindingFlags.Public | BindingFlags.Instance); - var supportedDevices = supportedProp.GetValue(settings, null); - // An empty supportedDevices list means all devices are supported. -#if CPP - // weird hack for il2cpp, use the implicit operator and cast Il2CppStringArray to ReadOnlyArray - var args = new object[] { new UnhollowerBaseLib.Il2CppStringArray(0) }; - var method = supportedDevices.GetActualType().GetMethod("op_Implicit", BindingFlags.Static | BindingFlags.Public); - supportedProp.SetValue(settings, method.Invoke(null, args), null); -#else - supportedProp.SetValue(settings, Activator.CreateInstance(supportedDevices.GetActualType(), new object[] { new string[0] }), null); -#endif - } - catch (Exception ex) - { - ExplorerCore.LogWarning($"Exception setting up InputSystem.settings.supportedDevices list!"); - ExplorerCore.Log(ex); - } - } - -#region reflection cache - - 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 = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Mouse")); - private static Type m_tMouse; - - public static Type TKey => m_tKey ?? (m_tKey = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Key")); - private static Type m_tKey; - - private static PropertyInfo m_btnIsPressedProp; - private static PropertyInfo m_btnWasPressedProp; - - private static object CurrentKeyboard => m_currentKeyboard ?? (m_currentKeyboard = m_kbCurrentProp.GetValue(null, null)); - private static object m_currentKeyboard; - private static PropertyInfo m_kbCurrentProp; - private static PropertyInfo m_kbIndexer; - - private static object CurrentMouse => m_currentMouse ?? (m_currentMouse = m_mouseCurrentProp.GetValue(null, null)); - private static object m_currentMouse; - private static PropertyInfo m_mouseCurrentProp; - - private static object LeftMouseButton => m_lmb ?? (m_lmb = m_leftButtonProp.GetValue(CurrentMouse, null)); - private static object m_lmb; - private static PropertyInfo m_leftButtonProp; - - private static object RightMouseButton => m_rmb ?? (m_rmb = m_rightButtonProp.GetValue(CurrentMouse, null)); - private static object m_rmb; - private static PropertyInfo m_rightButtonProp; - - private static MethodInfo ReadV2ControlMethod; - - private static object MousePositionInfo => m_pos ?? (m_pos = m_positionProp.GetValue(CurrentMouse, null)); - private static object m_pos; - private static PropertyInfo m_positionProp; - - private static object MouseScrollInfo => m_scrollInfo ?? (m_scrollInfo = m_scrollDeltaProp.GetValue(CurrentMouse, null)); - private static object m_scrollInfo; - private static PropertyInfo m_scrollDeltaProp; - -#endregion - - public Vector2 MousePosition - { - get - { - try - { - return (Vector2)ReadV2ControlMethod.Invoke(MousePositionInfo, ArgumentUtility.EmptyArgs); - } - catch { return Vector2.zero; } - } - } - - public Vector2 MouseScrollDelta - { - get - { - try - { - return (Vector2)ReadV2ControlMethod.Invoke(MouseScrollInfo, ArgumentUtility.EmptyArgs); - } - catch { return Vector2.zero; } - } - } - - internal static Dictionary ActualKeyDict = new Dictionary(); - internal static Dictionary enumNameFixes = new Dictionary - { - { "Control", "Ctrl" }, - { "Return", "Enter" }, - { "Alpha", "Digit" }, - { "Keypad", "Numpad" }, - { "Numlock", "NumLock" }, - { "Print", "PrintScreen" }, - { "BackQuote", "Backquote" } - }; - - internal object GetActualKey(KeyCode key) - { - if (!ActualKeyDict.ContainsKey(key)) - { - var s = key.ToString(); - try - { - if (enumNameFixes.First(it => s.Contains(it.Key)) is KeyValuePair entry) - s = s.Replace(entry.Key, entry.Value); - } - catch { } - - var parsed = Enum.Parse(TKey, s); - var actualKey = m_kbIndexer.GetValue(CurrentKeyboard, new object[] { parsed }); - - ActualKeyDict.Add(key, actualKey); - } - - return ActualKeyDict[key]; - } - - public bool GetKeyDown(KeyCode key) => (bool)m_btnWasPressedProp.GetValue(GetActualKey(key), null); - - public bool GetKey(KeyCode key) => (bool)m_btnIsPressedProp.GetValue(GetActualKey(key), null); - - public bool GetMouseButtonDown(int btn) - { - if (CurrentMouse == null) - return false; - switch (btn) - { - case 0: return (bool)m_btnWasPressedProp.GetValue(LeftMouseButton, null); - case 1: return (bool)m_btnWasPressedProp.GetValue(RightMouseButton, null); - // case 2: return (bool)_btnWasPressedProp.GetValue(MiddleMouseButton, null); - default: throw new NotImplementedException(); - } - } - - public bool GetMouseButton(int btn) - { - if (CurrentMouse == null) - return false; - switch (btn) - { - case 0: return (bool)m_btnIsPressedProp.GetValue(LeftMouseButton, null); - case 1: return (bool)m_btnIsPressedProp.GetValue(RightMouseButton, null); - // case 2: return (bool)_btnIsPressedProp.GetValue(MiddleMouseButton, null); - default: throw new NotImplementedException(); - } - } - - // UI Input - - public Type TInputSystemUIInputModule - => m_tUIInputModule - ?? (m_tUIInputModule = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.UI.InputSystemUIInputModule")); - internal Type m_tUIInputModule; - - public BaseInputModule UIInputModule => m_newInputModule; - internal BaseInputModule m_newInputModule; - - public void AddUIInputModule() - { - if (TInputSystemUIInputModule == null) - { - ExplorerCore.LogWarning("Unable to find UI Input Module Type, Input will not work!"); - return; - } - - var assetType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionAsset"); - m_newInputModule = RuntimeProvider.Instance.AddComponent(UIManager.CanvasRoot, TInputSystemUIInputModule); - var asset = RuntimeProvider.Instance.CreateScriptable(assetType) - .TryCast(assetType); - - inputExtensions = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionSetupExtensions"); - - var addMap = inputExtensions.GetMethod("AddActionMap", new Type[] { assetType, typeof(string) }); - var map = addMap.Invoke(null, new object[] { asset, "UI" }) - .TryCast(ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionMap")); - - CreateAction(map, "point", new[] { "/position" }, "point"); - CreateAction(map, "click", new[] { "/leftButton" }, "leftClick"); - CreateAction(map, "rightClick", new[] { "/rightButton" }, "rightClick"); - CreateAction(map, "scrollWheel", new[] { "/scroll" }, "scrollWheel"); - - UI_Enable = map.GetType().GetMethod("Enable"); - UI_Enable.Invoke(map, ArgumentUtility.EmptyArgs); - UI_ActionMap = map; - } - - private Type inputExtensions; - private object UI_ActionMap; - private MethodInfo UI_Enable; - - private void CreateAction(object map, string actionName, string[] bindings, string propertyName) - { - var disable = map.GetType().GetMethod("Disable"); - disable.Invoke(map, ArgumentUtility.EmptyArgs); - - var inputActionType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputAction"); - var addAction = inputExtensions.GetMethod("AddAction"); - var action = addAction.Invoke(null, new object[] { map, actionName, default, null, null, null, null, null }) - .TryCast(inputActionType); - - var addBinding = inputExtensions.GetMethod("AddBinding", - new Type[] { inputActionType, typeof(string), typeof(string), typeof(string), typeof(string) }); - - foreach (string binding in bindings) - addBinding.Invoke(null, new object[] { action.TryCast(inputActionType), binding, null, null, null }); - - var refType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionReference"); - var inputRef = refType.GetMethod("Create") - .Invoke(null, new object[] { action }) - .TryCast(refType); - - TInputSystemUIInputModule - .GetProperty(propertyName) - .SetValue(m_newInputModule.TryCast(TInputSystemUIInputModule), inputRef, null); - } - - public void ActivateModule() - { - try - { - m_newInputModule.m_EventSystem = UIManager.EventSys; - m_newInputModule.ActivateModule(); - UI_Enable.Invoke(UI_ActionMap, ArgumentUtility.EmptyArgs); - } - catch (Exception ex) - { - ExplorerCore.LogWarning("Exception enabling InputSystem UI Input Module: " + ex); - } - } - } -} \ No newline at end of file diff --git a/src/Core/Input/LegacyInput.cs b/src/Core/Input/LegacyInput.cs deleted file mode 100644 index 909e802..0000000 --- a/src/Core/Input/LegacyInput.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Reflection; -using UnityEngine; -using UnityEngine.EventSystems; -using UnityExplorer.UI; - -namespace UnityExplorer.Core.Input -{ - public class LegacyInput : IHandleInput - { - public LegacyInput() - { - m_mousePositionProp = TInput.GetProperty("mousePosition"); - m_mouseDeltaProp = TInput.GetProperty("mouseScrollDelta"); - m_getKeyMethod = TInput.GetMethod("GetKey", new Type[] { typeof(KeyCode) }); - m_getKeyDownMethod = TInput.GetMethod("GetKeyDown", new Type[] { typeof(KeyCode) }); - m_getMouseButtonMethod = TInput.GetMethod("GetMouseButton", new Type[] { typeof(int) }); - m_getMouseButtonDownMethod = TInput.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) }); - } - - public static Type TInput => m_tInput ?? (m_tInput = ReflectionUtility.GetTypeByName("UnityEngine.Input")); - private static Type m_tInput; - - private static PropertyInfo m_mousePositionProp; - private static PropertyInfo m_mouseDeltaProp; - private static MethodInfo m_getKeyMethod; - private static MethodInfo m_getKeyDownMethod; - private static MethodInfo m_getMouseButtonMethod; - private static MethodInfo m_getMouseButtonDownMethod; - - public Vector2 MousePosition => (Vector3)m_mousePositionProp.GetValue(null, null); - - public Vector2 MouseScrollDelta => (Vector2)m_mouseDeltaProp.GetValue(null, null); - - public bool GetKey(KeyCode key) => (bool)m_getKeyMethod.Invoke(null, new object[] { key }); - - public bool GetKeyDown(KeyCode key) => (bool)m_getKeyDownMethod.Invoke(null, new object[] { key }); - - public bool GetMouseButton(int btn) => (bool)m_getMouseButtonMethod.Invoke(null, new object[] { btn }); - - public bool GetMouseButtonDown(int btn) => (bool)m_getMouseButtonDownMethod.Invoke(null, new object[] { btn }); - - // UI Input module - - public BaseInputModule UIInputModule => m_inputModule; - internal StandaloneInputModule m_inputModule; - - public void AddUIInputModule() - { - m_inputModule = UIManager.CanvasRoot.gameObject.AddComponent(); - m_inputModule.m_EventSystem = UIManager.EventSys; - } - - public void ActivateModule() - { - try - { - m_inputModule.ActivateModule(); - } - catch (Exception ex) - { - ExplorerCore.LogWarning($"Exception enabling StandaloneInputModule: {ex}"); - } - } - } -} \ No newline at end of file diff --git a/src/Core/Input/NoInput.cs b/src/Core/Input/NoInput.cs deleted file mode 100644 index c156cc3..0000000 --- a/src/Core/Input/NoInput.cs +++ /dev/null @@ -1,23 +0,0 @@ -using UnityEngine; -using UnityEngine.EventSystems; - -namespace UnityExplorer.Core.Input -{ - // Just a stub for games where no Input module was able to load at all. - - public class NoInput : IHandleInput - { - public Vector2 MousePosition => Vector2.zero; - public Vector2 MouseScrollDelta => Vector2.zero; - - public bool GetKey(KeyCode key) => false; - public bool GetKeyDown(KeyCode key) => false; - - public bool GetMouseButton(int btn) => false; - public bool GetMouseButtonDown(int btn) => false; - - public BaseInputModule UIInputModule => null; - public void ActivateModule() { } - public void AddUIInputModule() { } - } -} \ No newline at end of file diff --git a/src/Core/Reflection/Extensions.cs b/src/Core/Reflection/Extensions.cs deleted file mode 100644 index 212c61f..0000000 --- a/src/Core/Reflection/Extensions.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace UnityExplorer -{ - public static class ReflectionExtensions - { - // ReflectionUtility extensions - - public static Type GetActualType(this object obj) - => ReflectionUtility.Instance.Internal_GetActualType(obj); - - public static object TryCast(this object obj) - => ReflectionUtility.Instance.Internal_TryCast(obj, ReflectionUtility.Instance.Internal_GetActualType(obj)); - - public static object TryCast(this object obj, Type castTo) - => ReflectionUtility.Instance.Internal_TryCast(obj, castTo); - - public static T TryCast(this object obj) - { - try - { - return (T)ReflectionUtility.Instance.Internal_TryCast(obj, typeof(T)); - } - catch - { - return default; - } - } - - // ------- Misc extensions -------- - - ///

- /// Safely try to get all Types inside an Assembly. - /// - 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(); - } - } - - - /// - /// Check if the two objects are reference-equal, including checking for UnityEngine.Object-equality and Il2CppSystem.Object-equality. - /// - public static bool ReferenceEqual(this object objA, object objB) - { - if (object.ReferenceEquals(objA, objB)) - return true; - - if (objA is UnityEngine.Object unityA && objB is UnityEngine.Object unityB) - { - if (unityA && unityB && unityA.m_CachedPtr == unityB.m_CachedPtr) - return true; - } - -#if CPP - if (objA is Il2CppSystem.Object cppA && objB is Il2CppSystem.Object cppB - && cppA.Pointer == cppB.Pointer) - return true; -#endif - - return false; - } - - /// - /// Helper to display a simple "{ExceptionType}: {Message}" of the exception, and optionally use the inner-most exception. - /// - public static string ReflectionExToString(this Exception e, bool innerMost = true) - { - if (innerMost) - e = e.GetInnerMostException(); - - return $"{e.GetType()}: {e.Message}"; - } - - public static Exception GetInnerMostException(this Exception e) - { - while (e != null) - { - if (e.InnerException == null) - break; -#if CPP - if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException) - break; -#endif - e = e.InnerException; - } - - return e; - } - } -} diff --git a/src/Core/Reflection/Il2CppReflection.cs b/src/Core/Reflection/Il2CppReflection.cs deleted file mode 100644 index 012885a..0000000 --- a/src/Core/Reflection/Il2CppReflection.cs +++ /dev/null @@ -1,956 +0,0 @@ -#if CPP -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using UnhollowerBaseLib; -using UnhollowerRuntimeLib; -using System.Runtime.InteropServices; -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; -using UnhollowerBaseLib.Attributes; -using UnityEngine; - -namespace UnityExplorer -{ - public class Il2CppReflection : ReflectionUtility - { - protected override void Initialize() - { - base.Initialize(); - - float start = Time.realtimeSinceStartup; - TryLoadGameModules(); - ExplorerCore.Log($"Loaded Unhollowed modules in {Time.realtimeSinceStartup - start} seconds"); - - start = Time.realtimeSinceStartup; - BuildDeobfuscationCache(); - OnTypeLoaded += TryCacheDeobfuscatedType; - ExplorerCore.Log($"Setup IL2CPP reflection in {Time.realtimeSinceStartup - start} seconds, " + - $"deobfuscated types count: {DeobfuscatedTypes.Count}"); - } - -#region IL2CPP Extern and pointers - - // Extern C++ methods - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern bool il2cpp_class_is_assignable_from(IntPtr klass, IntPtr oklass); - - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_object_get_class(IntPtr obj); - - public static bool Il2CppTypeNotNull(Type type) => Il2CppTypeNotNull(type, out _); - - public static bool Il2CppTypeNotNull(Type type, out IntPtr il2cppPtr) - { - if (!cppClassPointers.TryGetValue(type.AssemblyQualifiedName, out il2cppPtr)) - { - il2cppPtr = (IntPtr)typeof(Il2CppClassPointerStore<>) - .MakeGenericType(new Type[] { type }) - .GetField("NativeClassPtr", BF.Public | BF.Static) - .GetValue(null); - - cppClassPointers.Add(type.AssemblyQualifiedName, il2cppPtr); - } - - return il2cppPtr != IntPtr.Zero; - } - -#endregion - - -#region Deobfuscation cache - - private static readonly Dictionary DeobfuscatedTypes = new Dictionary(); - private static readonly Dictionary reverseDeobCache = new Dictionary(); - - private static void BuildDeobfuscationCache() - { - foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) - { - foreach (var type in asm.TryGetTypes()) - TryCacheDeobfuscatedType(type); - } - } - - private static void TryCacheDeobfuscatedType(Type type) - { - try - { - if (!type.CustomAttributes.Any()) - return; - - foreach (var att in type.CustomAttributes) - { - // Thanks to Slaynash for this - - if (att.AttributeType == typeof(ObfuscatedNameAttribute)) - { - string obfuscatedName = att.ConstructorArguments[0].Value.ToString(); - - DeobfuscatedTypes.Add(obfuscatedName, type); - reverseDeobCache.Add(type.FullName, obfuscatedName); - } - } - } - catch { } - } - - internal override string Internal_ProcessTypeInString(string theString, Type type) - { - if (reverseDeobCache.TryGetValue(type.FullName, out string obName)) - return theString.Replace(obName, type.FullName); - - return theString; - } - -#endregion - - - // Get type by name - - internal override Type Internal_GetTypeByName(string fullName) - { - if (DeobfuscatedTypes.TryGetValue(fullName, out Type deob)) - return deob; - - return base.Internal_GetTypeByName(fullName); - } - -#region Get actual type - - internal override Type Internal_GetActualType(object obj) - { - if (obj == null) - return null; - - var type = obj.GetType(); - try - { - if (type.IsGenericType) - return type; - - if (IsString(obj)) - return typeof(string); - - if (IsIl2CppPrimitive(type)) - return il2cppPrimitivesToMono[type.FullName]; - - if (obj is Il2CppSystem.Object cppObject) - { - var cppType = cppObject.GetIl2CppType(); - - // check if type is injected - IntPtr classPtr = il2cpp_object_get_class(cppObject.Pointer); - if (RuntimeSpecificsStore.IsInjected(classPtr)) - { - // Note: This will fail on injected subclasses. - // - {Namespace}.{Class}.{Subclass} would be {Namespace}.{Subclass} when injected. - // Not sure on solution yet. - return GetTypeByName(cppType.FullName) ?? type; - } - - if (AllTypes.TryGetValue(cppType.FullName, out Type primitive) && primitive.IsPrimitive) - return primitive; - - return GetUnhollowedType(cppType) ?? type; - } - } - catch (Exception ex) - { - ExplorerCore.LogWarning("Exception in IL2CPP GetActualType: " + ex); - } - - return type; - } - - public static Type GetUnhollowedType(CppType cppType) - { - var fullname = cppType.FullName; - - if (DeobfuscatedTypes.TryGetValue(fullname, out Type deob)) - return deob; - - if (fullname.StartsWith("System.")) - fullname = $"Il2Cpp{fullname}"; - - if (!AllTypes.TryGetValue(fullname, out Type monoType)) - ExplorerCore.LogWarning($"Failed to get type by name '{fullname}'!"); - return monoType; - } - -#endregion - - -#region Casting - - private static readonly Dictionary cppClassPointers = new Dictionary(); - - internal override object Internal_TryCast(object obj, Type castTo) - { - if (obj == null) - return null; - - var type = obj.GetType(); - - if (type == castTo) - return obj; - - // from structs - if (type.IsValueType) - { - // from il2cpp primitive to system primitive - if (IsIl2CppPrimitive(type) && castTo.IsPrimitive) - { - return MakeMonoPrimitive(obj); - } - // from system primitive to il2cpp primitive - else if (IsIl2CppPrimitive(castTo)) - { - return MakeIl2CppPrimitive(castTo, obj); - } - // from other structs to il2cpp object - else if (typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) - { - return BoxIl2CppObject(obj).TryCast(castTo); - } - else - return obj; - } - - // from string to il2cpp.Object / il2cpp.String - if (obj is string && typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) - { - return BoxStringToType(obj, castTo); - } - - // from il2cpp objects... - - if (!(obj is Il2CppObjectBase cppObj)) - return obj; - - // from Il2CppSystem.Object to a struct - if (castTo.IsValueType) - return UnboxCppObject(cppObj, castTo); - // or to system string - else if (castTo == typeof(string)) - return UnboxString(obj); - - if (!Il2CppTypeNotNull(castTo, out IntPtr castToPtr)) - return obj; - - // Casting from il2cpp object to il2cpp object... - - IntPtr castFromPtr = il2cpp_object_get_class(cppObj.Pointer); - - if (!il2cpp_class_is_assignable_from(castToPtr, castFromPtr)) - return null; - - if (RuntimeSpecificsStore.IsInjected(castToPtr)) - { - var injectedObj = UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(cppObj.Pointer); - return injectedObj ?? obj; - } - - try - { - return Activator.CreateInstance(castTo, cppObj.Pointer); - } - catch - { - return obj; - } - } - - //private static bool IsAssignableFrom(Type thisType, Type fromType) - //{ - // if (!Il2CppTypeNotNull(fromType, out IntPtr fromTypePtr) - // || !Il2CppTypeNotNull(thisType, out IntPtr thisTypePtr)) - // { - // // one or both of the types are not Il2Cpp types, use normal check - // return thisType.IsAssignableFrom(fromType); - // } - // - // return il2cpp_class_is_assignable_from(thisTypePtr, fromTypePtr); - //} - -#endregion - - -#region Boxing and unboxing ValueTypes - - // cached il2cpp unbox methods - internal static readonly Dictionary unboxMethods = new Dictionary(); - - // Unbox an il2cpp object to a struct or System primitive. - public object UnboxCppObject(Il2CppObjectBase cppObj, Type toType) - { - if (!toType.IsValueType) - return null; - - try - { - if (toType.IsEnum) - { - // Check for nullable enums - var type = cppObj.GetType(); - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Il2CppSystem.Nullable<>)) - { - var nullable = cppObj.TryCast(type); - var nullableHasValueProperty = type.GetProperty("HasValue"); - if ((bool)nullableHasValueProperty.GetValue(nullable, null)) - { - // nullable has a value. - var nullableValueProperty = type.GetProperty("Value"); - return Enum.Parse(toType, nullableValueProperty.GetValue(nullable, null).ToString()); - } - // nullable and no current value. - return cppObj; - } - - return Enum.Parse(toType, cppObj.ToString()); - } - - // Not enum, unbox with Il2CppObjectBase.Unbox - - var name = toType.AssemblyQualifiedName; - - if (!unboxMethods.ContainsKey(name)) - { - unboxMethods.Add(name, typeof(Il2CppObjectBase) - .GetMethod("Unbox") - .MakeGenericMethod(toType)); - } - - return unboxMethods[name].Invoke(cppObj, ArgumentUtility.EmptyArgs); - } - catch (Exception ex) - { - ExplorerCore.LogWarning("Exception Unboxing Il2Cpp object to struct: " + ex); - return null; - } - } - - private static Il2CppSystem.Object BoxIl2CppObject(object cppStruct, Type structType) - { - return GetMethodInfo(structType, "BoxIl2CppObject", ArgumentUtility.EmptyTypes) - .Invoke(cppStruct, ArgumentUtility.EmptyArgs) - as Il2CppSystem.Object; - } - - public Il2CppSystem.Object BoxIl2CppObject(object value) - { - if (value == null) - return null; - - try - { - var type = value.GetType(); - if (!type.IsValueType) - return null; - - if (type.IsEnum) - return Il2CppSystem.Enum.Parse(Il2CppType.From(type), value.ToString()); - - if (type.IsPrimitive && AllTypes.TryGetValue($"Il2Cpp{type.FullName}", out Type cppType)) - return BoxIl2CppObject(MakeIl2CppPrimitive(cppType, value), cppType); - - return BoxIl2CppObject(value, type); - } - catch (Exception ex) - { - ExplorerCore.LogWarning("Exception in BoxIl2CppObject: " + ex); - return null; - } - } - - // Helpers for Il2Cpp primitive <-> Mono - - internal static readonly Dictionary il2cppPrimitivesToMono = new Dictionary - { - { "Il2CppSystem.Boolean", typeof(bool) }, - { "Il2CppSystem.Byte", typeof(byte) }, - { "Il2CppSystem.SByte", typeof(sbyte) }, - { "Il2CppSystem.Char", typeof(char) }, - { "Il2CppSystem.Double", typeof(double) }, - { "Il2CppSystem.Single", typeof(float) }, - { "Il2CppSystem.Int32", typeof(int) }, - { "Il2CppSystem.UInt32", typeof(uint) }, - { "Il2CppSystem.Int64", typeof(long) }, - { "Il2CppSystem.UInt64", typeof(ulong) }, - { "Il2CppSystem.Int16", typeof(short) }, - { "Il2CppSystem.UInt16", typeof(ushort) }, - { "Il2CppSystem.IntPtr", typeof(IntPtr) }, - { "Il2CppSystem.UIntPtr", typeof(UIntPtr) } - }; - - public static bool IsIl2CppPrimitive(object obj) => IsIl2CppPrimitive(obj.GetType()); - - public static bool IsIl2CppPrimitive(Type type) => il2cppPrimitivesToMono.ContainsKey(type.FullName); - - public object MakeMonoPrimitive(object cppPrimitive) - { - return GetFieldInfo(cppPrimitive.GetType(), "m_value").GetValue(cppPrimitive); - } - - public object MakeIl2CppPrimitive(Type cppType, object monoValue) - { - var cppStruct = Activator.CreateInstance(cppType); - GetFieldInfo(cppType, "m_value").SetValue(cppStruct, monoValue); - return cppStruct; - } - -#endregion - - -#region String boxing/unboxing - - private const string IL2CPP_STRING_FULLNAME = "Il2CppSystem.String"; - private const string STRING_FULLNAME = "System.String"; - - public bool IsString(object obj) - { - if (obj is string || obj is Il2CppSystem.String) - return true; - - if (obj is Il2CppSystem.Object cppObj) - { - var type = cppObj.GetIl2CppType(); - return type.FullName == IL2CPP_STRING_FULLNAME || type.FullName == STRING_FULLNAME; - } - - return false; - } - - public object BoxStringToType(object value, Type castTo) - { - if (castTo == typeof(Il2CppSystem.String)) - return (Il2CppSystem.String)(value as string); - else - return (Il2CppSystem.Object)(value as string); - } - - public string UnboxString(object value) - { - if (value is string s) - return s; - - s = null; - if (value is Il2CppSystem.Object cppObject) - s = cppObject.ToString(); - else if (value is Il2CppSystem.String cppString) - s = cppString; - - return s; - } - -#endregion - - -#region Singleton finder - - internal override void Internal_FindSingleton(string[] possibleNames, Type type, BF flags, List instances) - { - PropertyInfo pi; - foreach (var name in possibleNames) - { - pi = type.GetProperty(name, flags); - if (pi != null) - { - var instance = pi.GetValue(null, null); - if (instance != null) - { - instances.Add(instance); - return; - } - } - } - - base.Internal_FindSingleton(possibleNames, type, flags, instances); - } - -#endregion - - -#region Force-loading game modules - - // Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded. - - // Force loading all il2cpp modules - - internal void TryLoadGameModules() - { - var dir = ExplorerCore.Loader.UnhollowedModulesFolder; - if (Directory.Exists(dir)) - { - foreach (var filePath in Directory.GetFiles(dir, "*.dll")) - DoLoadModule(filePath); - } - else - ExplorerCore.LogWarning($"Expected Unhollowed folder path does not exist: '{dir}'. " + - $"If you are using the standalone release, you can specify the Unhollowed modules path when you call CreateInstance()."); - } - - internal bool DoLoadModule(string fullPath) - { - if (string.IsNullOrEmpty(fullPath) || !File.Exists(fullPath)) - return false; - - try - { - Assembly.LoadFile(fullPath); - return true; - } - catch //(Exception e) - { - //ExplorerCore.LogWarning($"Failed loading module '{Path.GetFileName(fullPath)}'! {e.ReflectionExToString()}"); - return false; - } - } - -#endregion - - -#region Il2cpp reflection blacklist - - public override string[] DefaultReflectionBlacklist => defaultIl2CppBlacklist.ToArray(); - - // These methods currently cause a crash in most il2cpp games, - // even from doing "GetParameters()" on the MemberInfo. - // Blacklisting until the issue is fixed in Unhollower. - public static HashSet defaultIl2CppBlacklist = new HashSet - { - // These were deprecated a long time ago, still show up in some IL2CPP games for some reason - "UnityEngine.MonoBehaviour.allowPrefabModeInPlayMode", - "UnityEngine.MonoBehaviour.runInEditMode", - "UnityEngine.Component.animation", - "UnityEngine.Component.audio", - "UnityEngine.Component.camera", - "UnityEngine.Component.collider", - "UnityEngine.Component.collider2D", - "UnityEngine.Component.constantForce", - "UnityEngine.Component.hingeJoint", - "UnityEngine.Component.light", - "UnityEngine.Component.networkView", - "UnityEngine.Component.particleSystem", - "UnityEngine.Component.renderer", - "UnityEngine.Component.rigidbody", - "UnityEngine.Component.rigidbody2D", - "UnityEngine.Light.flare", - // These can cause a crash in IL2CPP - "Il2CppSystem.Type.DeclaringMethod", - "Il2CppSystem.RuntimeType.DeclaringMethod", - "Unity.Jobs.LowLevel.Unsafe.JobsUtility.CreateJobReflectionData", - "Unity.Profiling.ProfilerRecorder.CopyTo", - "Unity.Profiling.ProfilerRecorder.StartNew", - "UnityEngine.Analytics.Analytics.RegisterEvent", - "UnityEngine.Analytics.Analytics.SendEvent", - "UnityEngine.Analytics.ContinuousEvent+ConfigureEventDelegate.Invoke", - "UnityEngine.Analytics.ContinuousEvent.ConfigureEvent", - "UnityEngine.Animations.AnimationLayerMixerPlayable.Create", - "UnityEngine.Animations.AnimationLayerMixerPlayable.CreateHandle", - "UnityEngine.Animations.AnimationMixerPlayable.Create", - "UnityEngine.Animations.AnimationMixerPlayable.CreateHandle", - "UnityEngine.AssetBundle.RecompressAssetBundleAsync", - "UnityEngine.Audio.AudioMixerPlayable.Create", - "UnityEngine.BoxcastCommand.ScheduleBatch", - "UnityEngine.Camera.CalculateProjectionMatrixFromPhysicalProperties", - "UnityEngine.Canvas.renderingDisplaySize", - "UnityEngine.CapsulecastCommand.ScheduleBatch", - "UnityEngine.Collider2D.Cast", - "UnityEngine.Collider2D.Raycast", - "UnityEngine.ComputeBuffer+BeginBufferWriteDelegate.Invoke", - "UnityEngine.ComputeBuffer+EndBufferWriteDelegate.Invoke", - "UnityEngine.ComputeBuffer.BeginBufferWrite", - "UnityEngine.ComputeBuffer.EndBufferWrite", - "UnityEngine.Cubemap+SetPixelDataImplArrayDelegate.Invoke", - "UnityEngine.Cubemap+SetPixelDataImplDelegate.Invoke", - "UnityEngine.Cubemap.SetPixelDataImpl", - "UnityEngine.Cubemap.SetPixelDataImplArray", - "UnityEngine.CubemapArray+SetPixelDataImplArrayDelegate.Invoke", - "UnityEngine.CubemapArray+SetPixelDataImplDelegate.Invoke", - "UnityEngine.CubemapArray.SetPixelDataImpl", - "UnityEngine.CubemapArray.SetPixelDataImplArray", - "UnityEngine.Experimental.Playables.MaterialEffectPlayable.Create", - "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure+AddInstanceDelegate.Invoke", - "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure+AddInstance_Procedural_InjectedDelegate.Invoke", - "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance", - "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance_Procedural", - "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance_Procedural_Injected", - "UnityEngine.Experimental.Rendering.RayTracingShader+DispatchDelegate.Invoke", - "UnityEngine.Experimental.Rendering.RayTracingShader.Dispatch", - "UnityEngine.Experimental.Rendering.RenderPassAttachment.Clear", - "UnityEngine.GUI.DoButtonGrid", - "UnityEngine.GUI.Slider", - "UnityEngine.GUI.Toolbar", - "UnityEngine.Graphics.DrawMeshInstancedIndirect", - "UnityEngine.Graphics.DrawMeshInstancedProcedural", - "UnityEngine.Graphics.DrawProcedural", - "UnityEngine.Graphics.DrawProceduralIndirect", - "UnityEngine.Graphics.DrawProceduralIndirectNow", - "UnityEngine.Graphics.DrawProceduralNow", - "UnityEngine.LineRenderer+BakeMeshDelegate.Invoke", - "UnityEngine.LineRenderer.BakeMesh", - "UnityEngine.Mesh.GetIndices", - "UnityEngine.Mesh.GetTriangles", - "UnityEngine.Mesh.SetIndices", - "UnityEngine.Mesh.SetTriangles", - "UnityEngine.Physics2D.BoxCast", - "UnityEngine.Physics2D.CapsuleCast", - "UnityEngine.Physics2D.CircleCast", - "UnityEngine.PhysicsScene.BoxCast", - "UnityEngine.PhysicsScene.CapsuleCast", - "UnityEngine.PhysicsScene.OverlapBox", - "UnityEngine.PhysicsScene.OverlapCapsule", - "UnityEngine.PhysicsScene.SphereCast", - "UnityEngine.PhysicsScene2D.BoxCast", - "UnityEngine.PhysicsScene2D.CapsuleCast", - "UnityEngine.PhysicsScene2D.CircleCast", - "UnityEngine.PhysicsScene2D.GetRayIntersection", - "UnityEngine.PhysicsScene2D.Linecast", - "UnityEngine.PhysicsScene2D.OverlapArea", - "UnityEngine.PhysicsScene2D.OverlapBox", - "UnityEngine.PhysicsScene2D.OverlapCapsule", - "UnityEngine.PhysicsScene2D.OverlapCircle", - "UnityEngine.PhysicsScene2D.OverlapCollider", - "UnityEngine.PhysicsScene2D.OverlapPoint", - "UnityEngine.PhysicsScene2D.Raycast", - "UnityEngine.Playables.Playable.Create", - "UnityEngine.Profiling.CustomSampler.Create", - "UnityEngine.RaycastCommand.ScheduleBatch", - "UnityEngine.RemoteConfigSettings+QueueConfigDelegate.Invoke", - "UnityEngine.RemoteConfigSettings.QueueConfig", - "UnityEngine.RenderTexture.GetTemporaryImpl", - "UnityEngine.Rendering.AsyncGPUReadback.Request", - "UnityEngine.Rendering.AttachmentDescriptor.ConfigureClear", - "UnityEngine.Rendering.BatchRendererGroup+AddBatch_InjectedDelegate.Invoke", - "UnityEngine.Rendering.BatchRendererGroup.AddBatch", - "UnityEngine.Rendering.BatchRendererGroup.AddBatch_Injected", - "UnityEngine.Rendering.CommandBuffer+Internal_DispatchRaysDelegate.Invoke", - "UnityEngine.Rendering.CommandBuffer.DispatchRays", - "UnityEngine.Rendering.CommandBuffer.DrawMeshInstancedProcedural", - "UnityEngine.Rendering.CommandBuffer.Internal_DispatchRays", - "UnityEngine.Rendering.CommandBuffer.ResolveAntiAliasedSurface", - "UnityEngine.Rendering.ScriptableRenderContext.BeginRenderPass", - "UnityEngine.Rendering.ScriptableRenderContext.BeginScopedRenderPass", - "UnityEngine.Rendering.ScriptableRenderContext.BeginScopedSubPass", - "UnityEngine.Rendering.ScriptableRenderContext.BeginSubPass", - "UnityEngine.Rendering.ScriptableRenderContext.SetupCameraProperties", - "UnityEngine.Rigidbody2D.Cast", - "UnityEngine.Scripting.GarbageCollector+CollectIncrementalDelegate.Invoke", - "UnityEngine.Scripting.GarbageCollector.CollectIncremental", - "UnityEngine.SpherecastCommand.ScheduleBatch", - "UnityEngine.Texture.GetPixelDataSize", - "UnityEngine.Texture.GetPixelDataOffset", - "UnityEngine.Texture.GetPixelDataOffset", - "UnityEngine.Texture2D+SetPixelDataImplArrayDelegate.Invoke", - "UnityEngine.Texture2D+SetPixelDataImplDelegate.Invoke", - "UnityEngine.Texture2D.SetPixelDataImpl", - "UnityEngine.Texture2D.SetPixelDataImplArray", - "UnityEngine.Texture2DArray+SetPixelDataImplArrayDelegate.Invoke", - "UnityEngine.Texture2DArray+SetPixelDataImplDelegate.Invoke", - "UnityEngine.Texture2DArray.SetPixelDataImpl", - "UnityEngine.Texture2DArray.SetPixelDataImplArray", - "UnityEngine.Texture3D+SetPixelDataImplArrayDelegate.Invoke", - "UnityEngine.Texture3D+SetPixelDataImplDelegate.Invoke", - "UnityEngine.Texture3D.SetPixelDataImpl", - "UnityEngine.Texture3D.SetPixelDataImplArray", - "UnityEngine.TrailRenderer+BakeMeshDelegate.Invoke", - "UnityEngine.TrailRenderer.BakeMesh", - "UnityEngine.WWW.LoadFromCacheOrDownload", - "UnityEngine.XR.InputDevice.SendHapticImpulse", - }; - -#endregion - - -#region IL2CPP IEnumerable and IDictionary - - protected override bool Internal_TryGetEntryType(Type enumerableType, out Type type) - { - // Check for system types (not unhollowed) - if (base.Internal_TryGetEntryType(enumerableType, out type)) - return true; - - // Type is either an IL2CPP enumerable, or its not generic. - - if (type.IsGenericType) - { - // Temporary naive solution until IL2CPP interface support improves. - // This will work fine for most cases, but there are edge cases which would not work. - type = type.GetGenericArguments()[0]; - return true; - } - - // Unable to determine entry type - type = typeof(object); - return false; - } - - protected override bool Internal_TryGetEntryTypes(Type type, out Type keys, out Type values) - { - if (base.Internal_TryGetEntryTypes(type, out keys, out values)) - return true; - - // Type is either an IL2CPP dictionary, or its not generic. - if (type.IsGenericType) - { - // Naive solution until IL2CPP interfaces improve. - var args = type.GetGenericArguments(); - if (args.Length == 2) - { - keys = args[0]; - values = args[1]; - return true; - } - } - - keys = typeof(object); - values = typeof(object); - return false; - } - - // Temp fix until Unhollower interface support improves - - internal static readonly Dictionary getEnumeratorMethods = new Dictionary(); - internal static readonly Dictionary enumeratorInfos = new Dictionary(); - internal static readonly HashSet notSupportedTypes = new HashSet(); - - // IEnumerables - - internal static IntPtr cppIEnumerablePointer; - - protected override bool Internal_IsEnumerable(Type type) - { - if (base.Internal_IsEnumerable(type)) - return true; - - try - { - if (cppIEnumerablePointer == IntPtr.Zero) - Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IEnumerable), out cppIEnumerablePointer); - - if (cppIEnumerablePointer != IntPtr.Zero - && Il2CppTypeNotNull(type, out IntPtr assignFromPtr) - && il2cpp_class_is_assignable_from(cppIEnumerablePointer, assignFromPtr)) - { - return true; - } - } - catch { } - - return false; - } - - internal class EnumeratorInfo - { - internal MethodInfo moveNext; - internal PropertyInfo current; - } - - protected override bool Internal_TryGetEnumerator(object list, out IEnumerator enumerator) - { - if (list is IEnumerable) - return base.Internal_TryGetEnumerator(list, out enumerator); - - try - { - PrepareCppEnumerator(list, out object cppEnumerator, out EnumeratorInfo info); - enumerator = EnumerateCppList(info, cppEnumerator); - return true; - } - catch //(Exception ex) - { - //ExplorerCore.LogWarning($"Exception enumerating IEnumerable: {ex.ReflectionExToString()}"); - enumerator = null; - return false; - } - } - - private static void PrepareCppEnumerator(object list, out object cppEnumerator, out EnumeratorInfo info) - { - info = null; - cppEnumerator = null; - if (list == null) - throw new ArgumentNullException("list"); - - // Some ugly reflection to use the il2cpp interface for the instance type - - var type = list.GetActualType(); - var key = type.AssemblyQualifiedName; - - if (!getEnumeratorMethods.ContainsKey(key)) - { - var method = type.GetMethod("GetEnumerator") - ?? type.GetMethod("System_Collections_IEnumerable_GetEnumerator", FLAGS); - getEnumeratorMethods.Add(key, method); - - // ensure the enumerator type is supported - try - { - var test = getEnumeratorMethods[key].Invoke(list, null); - test.GetActualType().GetMethod("MoveNext").Invoke(test, null); - } - catch (Exception ex) - { - ExplorerCore.Log($"IEnumerable failed to enumerate: {ex}"); - notSupportedTypes.Add(key); - } - } - - if (notSupportedTypes.Contains(key)) - throw new NotSupportedException($"The IEnumerable type '{type.FullName}' does not support MoveNext."); - - cppEnumerator = getEnumeratorMethods[key].Invoke(list, null); - var enumeratorType = cppEnumerator.GetActualType(); - - var enumInfoKey = enumeratorType.AssemblyQualifiedName; - - if (!enumeratorInfos.ContainsKey(enumInfoKey)) - { - enumeratorInfos.Add(enumInfoKey, new EnumeratorInfo - { - current = enumeratorType.GetProperty("Current"), - moveNext = enumeratorType.GetMethod("MoveNext"), - }); - } - - info = enumeratorInfos[enumInfoKey]; - } - - internal static IEnumerator EnumerateCppList(EnumeratorInfo info, object enumerator) - { - // Yield and return the actual entries - while ((bool)info.moveNext.Invoke(enumerator, null)) - yield return info.current.GetValue(enumerator); - } - - // IDictionary - - internal static IntPtr cppIDictionaryPointer; - - protected override bool Internal_IsDictionary(Type type) - { - if (base.Internal_IsDictionary(type)) - return true; - - try - { - if (cppIDictionaryPointer == IntPtr.Zero) - if (!Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IDictionary), out cppIDictionaryPointer)) - return false; - - if (Il2CppTypeNotNull(type, out IntPtr classPtr) - && il2cpp_class_is_assignable_from(cppIDictionaryPointer, classPtr)) - return true; - } - catch { } - - return false; - } - - protected override bool Internal_TryGetDictEnumerator(object dictionary, out IEnumerator dictEnumerator) - { - if (dictionary is IDictionary) - return base.Internal_TryGetDictEnumerator(dictionary, out dictEnumerator); - - try - { - var type = dictionary.GetActualType(); - - if (typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(type)) - { - dictEnumerator = EnumerateCppHashTable(dictionary.TryCast()); - return true; - } - - var keys = type.GetProperty("Keys").GetValue(dictionary, null); - - var keyCollType = keys.GetActualType(); - var cacheKey = keyCollType.AssemblyQualifiedName; - if (!getEnumeratorMethods.ContainsKey(cacheKey)) - { - var method = keyCollType.GetMethod("GetEnumerator") - ?? keyCollType.GetMethod("System_Collections_IDictionary_GetEnumerator", FLAGS); - getEnumeratorMethods.Add(cacheKey, method); - - // test support - try - { - var test = getEnumeratorMethods[cacheKey].Invoke(keys, null); - test.GetActualType().GetMethod("MoveNext").Invoke(test, null); - } - catch (Exception ex) - { - ExplorerCore.Log($"IDictionary failed to enumerate: {ex}"); - notSupportedTypes.Add(cacheKey); - } - } - - if (notSupportedTypes.Contains(cacheKey)) - throw new Exception($"The IDictionary type '{type.FullName}' does not support MoveNext."); - - var keyEnumerator = getEnumeratorMethods[cacheKey].Invoke(keys, null); - var keyInfo = new EnumeratorInfo - { - current = keyEnumerator.GetActualType().GetProperty("Current"), - moveNext = keyEnumerator.GetActualType().GetMethod("MoveNext"), - }; - - var values = type.GetProperty("Values").GetValue(dictionary, null); - var valueEnumerator = values.GetActualType().GetMethod("GetEnumerator").Invoke(values, null); - var valueInfo = new EnumeratorInfo - { - current = valueEnumerator.GetActualType().GetProperty("Current"), - moveNext = valueEnumerator.GetActualType().GetMethod("MoveNext"), - }; - - dictEnumerator = EnumerateCppDict(keyInfo, keyEnumerator, valueInfo, valueEnumerator); - return true; - } - catch //(Exception ex) - { - //ExplorerCore.LogWarning($"Exception enumerating IDictionary: {ex.ReflectionExToString()}"); - dictEnumerator = null; - return false; - } - } - - internal static IEnumerator EnumerateCppDict(EnumeratorInfo keyInfo, object keyEnumerator, - EnumeratorInfo valueInfo, object valueEnumerator) - { - while ((bool)keyInfo.moveNext.Invoke(keyEnumerator, null)) - { - valueInfo.moveNext.Invoke(valueEnumerator, null); - - var key = keyInfo.current.GetValue(keyEnumerator, null); - var value = valueInfo.current.GetValue(valueEnumerator, null); - - yield return new DictionaryEntry(key, value); - } - } - - internal static IEnumerator EnumerateCppHashTable(Il2CppSystem.Collections.Hashtable hashtable) - { - for (int i = 0; i < hashtable.buckets.Count; i++) - { - var bucket = hashtable.buckets[i]; - if (bucket == null || bucket.key == null) - continue; - - yield return new DictionaryEntry(bucket.key, bucket.val); - } - } - -#endregion - - } -} - -#endif \ No newline at end of file diff --git a/src/Core/Reflection/Patches.cs b/src/Core/Reflection/Patches.cs deleted file mode 100644 index 658008e..0000000 --- a/src/Core/Reflection/Patches.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using HarmonyLib; - -namespace UnityExplorer -{ - public static class ReflectionPatches - { - public static void Init() - { - try - { - var method = typeof(Assembly).GetMethod(nameof(Assembly.GetTypes), new Type[0]); - var processor = ExplorerCore.Harmony.CreateProcessor(method); - processor.AddFinalizer(typeof(ReflectionPatches).GetMethod(nameof(Assembly_GetTypes))); - processor.Patch(); - } - catch (Exception ex) - { - ExplorerCore.LogWarning($"Exception setting up Reflection patch: {ex}"); - } - } - - private static readonly Type[] emptyTypes = new Type[0]; - - public static Exception Assembly_GetTypes(Assembly __instance, Exception __exception, ref Type[] __result) - { - if (__exception != null) - { - try - { - __result = __instance.GetExportedTypes(); - } - catch (ReflectionTypeLoadException e) - { - try - { - __result = e.Types.Where(it => it != null).ToArray(); - } - catch - { - __result = emptyTypes; - } - } - catch - { - __result = emptyTypes; - } - } - - return null; - } - } -} diff --git a/src/Core/Reflection/ReflectionUtility.cs b/src/Core/Reflection/ReflectionUtility.cs deleted file mode 100644 index a63764e..0000000 --- a/src/Core/Reflection/ReflectionUtility.cs +++ /dev/null @@ -1,597 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using UnityEngine; -using UnityExplorer.Core.Config; -using UnityExplorer.Core.Runtime; -using BF = System.Reflection.BindingFlags; - -namespace UnityExplorer -{ - public class ReflectionUtility - { - public const BF FLAGS = BF.Public | BF.Instance | BF.NonPublic | BF.Static; - - internal static ReflectionUtility Instance; - - public static void Init() - { - Instance = -#if CPP - new Il2CppReflection(); -#else - new ReflectionUtility(); -#endif - Instance.Initialize(); - - ReflectionPatches.Init(); - } - - protected virtual void Initialize() - { - SetupTypeCache(); - - LoadBlacklistString(ConfigManager.Reflection_Signature_Blacklist.Value); - ConfigManager.Reflection_Signature_Blacklist.OnValueChanged += LoadBlacklistString; - } - - #region Type cache - - public static Action OnTypeLoaded; - - /// Key: Type.FullName - protected static readonly SortedDictionary AllTypes = new SortedDictionary(StringComparer.OrdinalIgnoreCase); - - public static readonly List AllNamespaces = new List(); - private static readonly HashSet uniqueNamespaces = new HashSet(); - - private static string[] allTypesArray; - public static string[] GetTypeNameArray() - { - if (allTypesArray == null || allTypesArray.Length != AllTypes.Count) - { - allTypesArray = new string[AllTypes.Count]; - int i = 0; - foreach (var name in AllTypes.Keys) - { - allTypesArray[i] = name; - i++; - } - } - return allTypesArray; - } - - private static void SetupTypeCache() - { - float start = Time.realtimeSinceStartup; - - foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) - CacheTypes(asm); - - AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded; - - ExplorerCore.Log($"Cached AppDomain assemblies in {Time.realtimeSinceStartup - start} seconds"); - } - - private static void AssemblyLoaded(object sender, AssemblyLoadEventArgs args) - { - if (args.LoadedAssembly == null || args.LoadedAssembly.GetName().Name == "completions") - return; - - CacheTypes(args.LoadedAssembly); - } - - private static void CacheTypes(Assembly asm) - { - foreach (var type in asm.TryGetTypes()) - { - // Cache namespace if there is one - if (!string.IsNullOrEmpty(type.Namespace) && !uniqueNamespaces.Contains(type.Namespace)) - { - uniqueNamespaces.Add(type.Namespace); - int i = 0; - while (i < AllNamespaces.Count) - { - if (type.Namespace.CompareTo(AllNamespaces[i]) < 0) - break; - i++; - } - AllNamespaces.Insert(i, type.Namespace); - } - - // Cache the type. Overwrite type if one exists with the full name - if (AllTypes.ContainsKey(type.FullName)) - AllTypes[type.FullName] = type; - else - AllTypes.Add(type.FullName, type); - - // Invoke listener - OnTypeLoaded?.Invoke(type); - - // Check type inheritance cache, add this to any lists it should be in - foreach (var key in typeInheritance.Keys) - { - try - { - var baseType = AllTypes[key]; - if (baseType.IsAssignableFrom(type) && !typeInheritance[key].Contains(type)) - typeInheritance[key].Add(type); - } - catch { } - } - } - } - - #endregion - - /// - /// 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) - => Instance.Internal_GetTypeByName(fullName); - - internal virtual Type Internal_GetTypeByName(string fullName) - { - AllTypes.TryGetValue(fullName, out Type type); - return type; - } - - // Getting the actual type of an object - internal virtual Type Internal_GetActualType(object obj) - => obj?.GetType(); - - // Force-casting an object to a type - internal virtual object Internal_TryCast(object obj, Type castTo) - => obj; - - // Processing deobfuscated type names in strings - public static string ProcessTypeInString(Type type, string theString) - => Instance.Internal_ProcessTypeInString(theString, type); - - internal virtual string Internal_ProcessTypeInString(string theString, Type type) - => theString; - - // Singleton finder - - public static void FindSingleton(string[] possibleNames, Type type, BindingFlags flags, List instances) - => Instance.Internal_FindSingleton(possibleNames, type, flags, instances); - - internal virtual void Internal_FindSingleton(string[] possibleNames, Type type, BindingFlags flags, List instances) - { - // Look for a typical Instance backing field. - FieldInfo fi; - foreach (var name in possibleNames) - { - fi = type.GetField(name, flags); - if (fi != null) - { - var instance = fi.GetValue(null); - if (instance != null) - { - instances.Add(instance); - return; - } - } - } - } - - // Universal helpers - - #region Type inheritance cache - - // cache for GetBaseTypes - internal static readonly Dictionary baseTypes = new Dictionary(); - - /// - /// Get all base types of the provided Type, including itself. - /// - public static Type[] GetAllBaseTypes(object obj) => GetAllBaseTypes(obj?.GetActualType()); - - /// - /// Get all base types of the provided Type, including itself. - /// - public static Type[] GetAllBaseTypes(Type type) - { - if (type == null) - throw new ArgumentNullException("type"); - - var name = type.AssemblyQualifiedName; - - if (baseTypes.TryGetValue(name, out Type[] ret)) - return ret; - - List list = new List(); - - while (type != null) - { - list.Add(type); - type = type.BaseType; - } - - ret = list.ToArray(); - - baseTypes.Add(name, ret); - - return ret; - } - - #endregion - - - #region Type and Generic Parameter implementation cache - - // cache for GetImplementationsOf - internal static readonly Dictionary> typeInheritance = new Dictionary>(); - internal static readonly Dictionary> genericParameterInheritance = new Dictionary>(); - - public static string GetImplementationKey(Type type) - { - if (!type.IsGenericParameter) - return type.FullName; - else - { - var sb = new StringBuilder(); - sb.Append(type.GenericParameterAttributes) - .Append('|'); - foreach (var c in type.GetGenericParameterConstraints()) - sb.Append(c.FullName).Append(','); - return sb.ToString(); - } - } - - /// - /// Get all non-abstract implementations of the provided type (include itself, if not abstract) in the current AppDomain. - /// Also works for generic parameters by analyzing the constraints. - /// - /// The base type, which can optionally be abstract / interface. - /// All implementations of the type in the current AppDomain. - public static HashSet GetImplementationsOf(Type baseType, bool allowAbstract, bool allowGeneric, bool allowEnum, bool allowRecursive = true) - { - var key = GetImplementationKey(baseType); - - int count = AllTypes.Count; - HashSet ret; - if (!baseType.IsGenericParameter) - ret = GetImplementations(key, baseType, allowAbstract, allowGeneric, allowEnum); - else - ret = GetGenericParameterImplementations(key, baseType, allowAbstract, allowGeneric); - - // types were resolved during the parse, do it again if we're not already rebuilding. - if (allowRecursive && AllTypes.Count != count) - ret = GetImplementationsOf(baseType, allowAbstract, allowGeneric, false); - - return ret; - } - - private static HashSet GetImplementations(string key, Type baseType, bool allowAbstract, bool allowGeneric, bool allowEnum) - { - if (!typeInheritance.ContainsKey(key)) - { - var set = new HashSet(); - var names = GetTypeNameArray(); - for (int i = 0; i < names.Length; i++) - { - var name = names[i]; - try - { - var type = AllTypes[name]; - - if (set.Contains(type) - || (type.IsAbstract && type.IsSealed) // ignore static classes - || (!allowAbstract && type.IsAbstract) - || (!allowGeneric && (type.IsGenericType || type.IsGenericTypeDefinition)) - || (!allowEnum && type.IsEnum)) - continue; - - if (type.FullName.Contains("PrivateImplementationDetails") - || type.FullName.Contains("DisplayClass") - || type.FullName.Contains('<')) - continue; - - if (baseType.IsAssignableFrom(type) && !set.Contains(type)) - set.Add(type); - } - catch { } - } - - //set. - - typeInheritance.Add(key, set); - } - - return typeInheritance[key]; - } - - private static HashSet GetGenericParameterImplementations(string key, Type baseType, bool allowAbstract, bool allowGeneric) - { - if (!genericParameterInheritance.ContainsKey(key)) - { - var set = new HashSet(); - - var names = GetTypeNameArray(); - for (int i = 0; i < names.Length; i++) - { - var name = names[i]; - try - { - var type = AllTypes[name]; - - if (set.Contains(type) - || (type.IsAbstract && type.IsSealed) // ignore static classes - || (!allowAbstract && type.IsAbstract) - || (!allowGeneric && (type.IsGenericType || type.IsGenericTypeDefinition))) - continue; - - if (type.FullName.Contains("PrivateImplementationDetails") - || type.FullName.Contains("DisplayClass") - || type.FullName.Contains('<')) - continue; - - if (baseType.GenericParameterAttributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint) - && type.IsClass) - continue; - - if (baseType.GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint) - && type.IsValueType) - continue; - - if (baseType.GetGenericParameterConstraints().Any(it => !it.IsAssignableFrom(type))) - continue; - - set.Add(type); - } - catch { } - } - - genericParameterInheritance.Add(key, set); - } - - return genericParameterInheritance[key]; - } - - #endregion - - - #region Internal MemberInfo Cache - - internal static Dictionary> fieldInfos = new Dictionary>(); - - public static FieldInfo GetFieldInfo(Type type, string fieldName) - { - if (!fieldInfos.ContainsKey(type)) - fieldInfos.Add(type, new Dictionary()); - - if (!fieldInfos[type].ContainsKey(fieldName)) - fieldInfos[type].Add(fieldName, type.GetField(fieldName, FLAGS)); - - return fieldInfos[type][fieldName]; - } - - internal static Dictionary> propertyInfos = new Dictionary>(); - - public static PropertyInfo GetPropertyInfo(Type type, string propertyName) - { - if (!propertyInfos.ContainsKey(type)) - propertyInfos.Add(type, new Dictionary()); - - if (!propertyInfos[type].ContainsKey(propertyName)) - propertyInfos[type].Add(propertyName, type.GetProperty(propertyName, FLAGS)); - - return propertyInfos[type][propertyName]; - } - - internal static Dictionary> methodInfos = new Dictionary>(); - - public static MethodInfo GetMethodInfo(Type type, string methodName) - => GetMethodInfo(type, methodName, ArgumentUtility.EmptyTypes, false); - - public static MethodInfo GetMethodInfo(Type type, string methodName, Type[] argumentTypes, bool cacheAmbiguous = false) - { - if (!methodInfos.ContainsKey(type)) - methodInfos.Add(type, new Dictionary()); - - if (cacheAmbiguous) - { - methodName += "|"; - foreach (var arg in argumentTypes) - methodName += arg.FullName + ","; - } - - try - { - if (!methodInfos[type].ContainsKey(methodName)) - { - if (argumentTypes != null) - methodInfos[type].Add(methodName, type.GetMethod(methodName, FLAGS, null, argumentTypes, null)); - else - methodInfos[type].Add(methodName, type.GetMethod(methodName, FLAGS)); - } - - return methodInfos[type][methodName]; - } - catch (AmbiguousMatchException) - { - ExplorerCore.LogWarning($"AmbiguousMatchException trying to get method '{methodName}'"); - return null; - } - catch (Exception e) - { - ExplorerCore.LogWarning($"{e.GetType()} trying to get method '{methodName}': {e.Message}\r\n{e.StackTrace}"); - return null; - } - } - - #endregion - - - #region Reflection Blacklist - - public virtual string[] DefaultReflectionBlacklist => new string[0]; - - public static void LoadBlacklistString(string blacklist) - { - try - { - if (string.IsNullOrEmpty(blacklist) && !Instance.DefaultReflectionBlacklist.Any()) - return; - - try - { - var sigs = blacklist.Split(';'); - foreach (var sig in sigs) - { - var s = sig.Trim(); - if (string.IsNullOrEmpty(s)) - continue; - if (!currentBlacklist.Contains(s)) - currentBlacklist.Add(s); - } - } - catch (Exception ex) - { - ExplorerCore.LogWarning($"Exception parsing blacklist string: {ex.ReflectionExToString()}"); - } - - foreach (var sig in Instance.DefaultReflectionBlacklist) - { - if (!currentBlacklist.Contains(sig)) - currentBlacklist.Add(sig); - } - - Mono.CSharp.IL2CPP.Blacklist.SignatureBlacklist = currentBlacklist; - } - catch (Exception ex) - { - ExplorerCore.LogWarning($"Exception setting up reflection blacklist: {ex.ReflectionExToString()}"); - } - } - - public static bool IsBlacklisted(MemberInfo member) - { - if (string.IsNullOrEmpty(member.DeclaringType?.Namespace)) - return false; - - var sig = $"{member.DeclaringType.FullName}.{member.Name}"; - - return currentBlacklist.Contains(sig); - } - - private static readonly HashSet currentBlacklist = new HashSet(); - - #endregion - - - // Temp fix for IL2CPP until interface support improves - - // IsEnumerable - - public static bool IsEnumerable(Type type) => Instance.Internal_IsEnumerable(type); - - protected virtual bool Internal_IsEnumerable(Type type) - { - return typeof(IEnumerable).IsAssignableFrom(type); - } - - // TryGetEnumerator (list) - - public static bool TryGetEnumerator(object list, out IEnumerator enumerator) - => Instance.Internal_TryGetEnumerator(list, out enumerator); - - protected virtual bool Internal_TryGetEnumerator(object list, out IEnumerator enumerator) - { - enumerator = (list as IEnumerable).GetEnumerator(); - return true; - } - - // TryGetEntryType - - public static bool TryGetEntryType(Type enumerableType, out Type type) - => Instance.Internal_TryGetEntryType(enumerableType, out type); - - protected virtual bool Internal_TryGetEntryType(Type enumerableType, out Type type) - { - // Check for arrays - if (enumerableType.IsArray) - { - type = enumerableType.GetElementType(); - return true; - } - - // Check for implementation of IEnumerable, IList or ICollection - foreach (var t in enumerableType.GetInterfaces()) - { - if (t.IsGenericType) - { - var typeDef = t.GetGenericTypeDefinition(); - if (typeDef == typeof(IEnumerable<>) || typeDef == typeof(IList<>) || typeDef == typeof(ICollection<>)) - { - type = t.GetGenericArguments()[0]; - return true; - } - } - } - - // Unable to determine any generic element type, just use object. - type = typeof(object); - return false; - } - - // IsDictionary - - public static bool IsDictionary(Type type) => Instance.Internal_IsDictionary(type); - - protected virtual bool Internal_IsDictionary(Type type) - { - return typeof(IDictionary).IsAssignableFrom(type); - } - - // TryGetEnumerator (dictionary) - - public static bool TryGetDictEnumerator(object dictionary, out IEnumerator dictEnumerator) - => Instance.Internal_TryGetDictEnumerator(dictionary, out dictEnumerator); - - protected virtual bool Internal_TryGetDictEnumerator(object dictionary, out IEnumerator dictEnumerator) - { - dictEnumerator = EnumerateDictionary((IDictionary)dictionary); - return true; - } - - private IEnumerator EnumerateDictionary(IDictionary dict) - { - var enumerator = dict.GetEnumerator(); - while (enumerator.MoveNext()) - { - yield return new DictionaryEntry(enumerator.Key, enumerator.Value); - } - } - - // TryGetEntryTypes - - public static bool TryGetEntryTypes(Type dictionaryType, out Type keys, out Type values) - => Instance.Internal_TryGetEntryTypes(dictionaryType, out keys, out values); - - protected virtual bool Internal_TryGetEntryTypes(Type dictionaryType, out Type keys, out Type values) - { - foreach (var t in dictionaryType.GetInterfaces()) - { - if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IDictionary<,>)) - { - var args = t.GetGenericArguments(); - keys = args[0]; - values = args[1]; - return true; - } - } - - keys = typeof(object); - values = typeof(object); - return false; - } - } -} diff --git a/src/Core/Runtime/Il2Cpp/AssetBundle.cs b/src/Core/Runtime/Il2Cpp/AssetBundle.cs deleted file mode 100644 index c660bef..0000000 --- a/src/Core/Runtime/Il2Cpp/AssetBundle.cs +++ /dev/null @@ -1,106 +0,0 @@ -#if CPP -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnhollowerBaseLib; -using UnhollowerBaseLib.Attributes; -using UnhollowerRuntimeLib; -using UnityEngine; -using UnityExplorer.Core.Runtime.Il2Cpp; - -namespace UnityExplorer -{ - public class AssetBundle : UnityEngine.Object - { - static AssetBundle() - { - ClassInjector.RegisterTypeInIl2Cpp(); - } - - // ~~~~~~~~~~~~ Static ~~~~~~~~~~~~ - - // AssetBundle.LoadFromFile(string path) - - internal delegate IntPtr d_LoadFromFile(IntPtr path, uint crc, ulong offset); - - [HideFromIl2Cpp] - public static AssetBundle LoadFromFile(string path) - { - IntPtr ptr = ICallManager.GetICall("UnityEngine.AssetBundle::LoadFromFile_Internal") - .Invoke(IL2CPP.ManagedStringToIl2Cpp(path), 0u, 0UL); - - return ptr != IntPtr.Zero ? new AssetBundle(ptr) : null; - } - - // AssetBundle.LoadFromMemory(byte[] binary) - - private delegate IntPtr d_LoadFromMemory(IntPtr binary, uint crc); - - [HideFromIl2Cpp] - public static AssetBundle LoadFromMemory(byte[] binary, uint crc = 0) - { - IntPtr ptr = ICallManager.GetICall("UnityEngine.AssetBundle::LoadFromMemory_Internal") - .Invoke(((Il2CppStructArray)binary).Pointer, crc); - - return ptr != IntPtr.Zero ? new AssetBundle(ptr) : null; - } - - // AssetBundle.GetAllLoadedAssetBundles() - - public delegate IntPtr d_GetAllLoadedAssetBundles_Native(); - - [HideFromIl2Cpp] - public static AssetBundle[] GetAllLoadedAssetBundles() - { - IntPtr ptr = ICallManager.GetICall("UnityEngine.AssetBundle::GetAllLoadedAssetBundles_Native") - .Invoke(); - - return ptr != IntPtr.Zero ? (AssetBundle[])new Il2CppReferenceArray(ptr) : null; - } - - // ~~~~~~~~~~~~ Instance ~~~~~~~~~~~~ - - public readonly IntPtr m_bundlePtr = IntPtr.Zero; - - public AssetBundle(IntPtr ptr) : base(ptr) { m_bundlePtr = ptr; } - - // LoadAllAssets() - - internal delegate IntPtr d_LoadAssetWithSubAssets_Internal(IntPtr _this, IntPtr name, IntPtr type); - - [HideFromIl2Cpp] - public UnityEngine.Object[] LoadAllAssets() - { - IntPtr ptr = ICallManager.GetICall("UnityEngine.AssetBundle::LoadAssetWithSubAssets_Internal") - .Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(""), UnhollowerRuntimeLib.Il2CppType.Of().Pointer); - - return ptr != IntPtr.Zero ? (UnityEngine.Object[])new Il2CppReferenceArray(ptr) : new UnityEngine.Object[0]; - } - - // LoadAsset(string name, Type type) - - internal delegate IntPtr d_LoadAsset_Internal(IntPtr _this, IntPtr name, IntPtr type); - - [HideFromIl2Cpp] - public T LoadAsset(string name) where T : UnityEngine.Object - { - IntPtr ptr = ICallManager.GetICall("UnityEngine.AssetBundle::LoadAsset_Internal") - .Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(name), UnhollowerRuntimeLib.Il2CppType.Of().Pointer); - - return ptr != IntPtr.Zero ? new UnityEngine.Object(ptr).TryCast() : null; - } - - // Unload(bool unloadAllLoadedObjects); - - internal delegate void d_Unload(IntPtr _this, bool unloadAllLoadedObjects); - - [HideFromIl2Cpp] - public void Unload(bool unloadAllLoadedObjects) - { - ICallManager.GetICall("UnityEngine.AssetBundle::Unload") - .Invoke(this.m_bundlePtr, unloadAllLoadedObjects); - } - } -} -#endif \ No newline at end of file diff --git a/src/Core/Runtime/Il2Cpp/ICallManager.cs b/src/Core/Runtime/Il2Cpp/ICallManager.cs deleted file mode 100644 index 3a5ac60..0000000 --- a/src/Core/Runtime/Il2Cpp/ICallManager.cs +++ /dev/null @@ -1,71 +0,0 @@ -#if CPP -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.InteropServices; - -namespace UnityExplorer.Core.Runtime.Il2Cpp -{ - public static class ICallManager - { - [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr il2cpp_resolve_icall([MarshalAs(UnmanagedType.LPStr)] string name); - - private static readonly Dictionary iCallCache = new Dictionary(); - - /// - /// Helper to get and cache an iCall by providing the signature (eg. "UnityEngine.Resources::FindObjectsOfTypeAll"). - /// - /// The Type of Delegate to provide for the iCall. - /// The signature of the iCall you want to get. - /// The delegate if successful. - /// If the iCall could not be found. - public static T GetICall(string signature) where T : Delegate - { - if (iCallCache.ContainsKey(signature)) - return (T)iCallCache[signature]; - - IntPtr ptr = il2cpp_resolve_icall(signature); - - if (ptr == IntPtr.Zero) - throw new MissingMethodException($"Could not find any iCall with the signature '{signature}'!"); - - Delegate iCall = Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)); - iCallCache.Add(signature, iCall); - - return (T)iCall; - } - - private static readonly Dictionary s_unreliableCache = new Dictionary(); - - /// - /// Get an iCall which may be one of multiple different signatures (ie, it changed in different Unity versions). - /// Each possible signature must have the same Type pattern, it can only vary by name. - /// - public static T GetICallUnreliable(IEnumerable possibleSignatures) where T : Delegate - { - // use the first possible signature as the 'key'. - string key = possibleSignatures.First(); - - if (s_unreliableCache.ContainsKey(key)) - return (T)s_unreliableCache[key]; - - T iCall; - IntPtr ptr; - foreach (var sig in possibleSignatures) - { - ptr = il2cpp_resolve_icall(sig); - if (ptr != IntPtr.Zero) - { - iCall = (T)Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)); - s_unreliableCache.Add(key, iCall); - return iCall; - } - } - - throw new MissingMethodException($"Could not find any iCall from list of provided signatures starting with '{key}'!"); - } - } -} -#endif \ No newline at end of file diff --git a/src/Core/Runtime/Il2Cpp/Il2CppCoroutine.cs b/src/Core/Runtime/Il2Cpp/Il2CppCoroutine.cs deleted file mode 100644 index 9601990..0000000 --- a/src/Core/Runtime/Il2Cpp/Il2CppCoroutine.cs +++ /dev/null @@ -1,159 +0,0 @@ -#if CPP -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using UnhollowerBaseLib; -using UnityEngine; - -// Credit to HerpDerpenstine and knah -// https://github.com/LavaGang/MelonLoader/blob/master/SM_Il2Cpp/Coroutines.cs - -namespace UnityExplorer.Core.Runtime.Il2Cpp -{ - public static class Il2CppCoroutine - { - private struct CoroTuple - { - public object WaitCondition; - public IEnumerator Coroutine; - } - private static readonly List ourCoroutinesStore = new List(); - private static readonly List ourNextFrameCoroutines = new List(); - private static readonly List ourWaitForFixedUpdateCoroutines = new List(); - private static readonly List ourWaitForEndOfFrameCoroutines = new List(); - - private static readonly List tempList = new List(); - - internal static object Start(IEnumerator routine) - { - if (routine != null) ProcessNextOfCoroutine(routine); - return routine; - } - - internal static void Stop(IEnumerator enumerator) - { - if (ourNextFrameCoroutines.Contains(enumerator)) // the coroutine is running itself - ourNextFrameCoroutines.Remove(enumerator); - else - { - int coroTupleIndex = ourCoroutinesStore.FindIndex(c => c.Coroutine == enumerator); - if (coroTupleIndex != -1) // the coroutine is waiting for a subroutine - { - object waitCondition = ourCoroutinesStore[coroTupleIndex].WaitCondition; - if (waitCondition is IEnumerator waitEnumerator) - Stop(waitEnumerator); - - ourCoroutinesStore.RemoveAt(coroTupleIndex); - } - } - } - - private static void ProcessCoroList(List target) - { - if (target.Count == 0) return; - - // use a temp list to make sure waits made during processing are not handled by same processing invocation - // additionally, a temp list reduces allocations compared to an array - tempList.AddRange(target); - target.Clear(); - foreach (var enumerator in tempList) ProcessNextOfCoroutine(enumerator); - tempList.Clear(); - } - - internal static void Process() - { - for (var i = ourCoroutinesStore.Count - 1; i >= 0; i--) - { - var tuple = ourCoroutinesStore[i]; - if (tuple.WaitCondition is WaitForSeconds waitForSeconds) - { - if ((waitForSeconds.m_Seconds -= Time.deltaTime) <= 0) - { - ourCoroutinesStore.RemoveAt(i); - ProcessNextOfCoroutine(tuple.Coroutine); - } - } - } - - ProcessCoroList(ourNextFrameCoroutines); - } - - internal static void ProcessWaitForFixedUpdate() => ProcessCoroList(ourWaitForFixedUpdateCoroutines); - - internal static void ProcessWaitForEndOfFrame() => ProcessCoroList(ourWaitForEndOfFrameCoroutines); - - private static void ProcessNextOfCoroutine(IEnumerator enumerator) - { - try - { - if (!enumerator.MoveNext()) // Run the next step of the coroutine. If it's done, restore the parent routine - { - var indices = ourCoroutinesStore.Select((it, idx) => (idx, it)).Where(it => it.it.WaitCondition == enumerator).Select(it => it.idx).ToList(); - for (var i = indices.Count - 1; i >= 0; i--) - { - var index = indices[i]; - ourNextFrameCoroutines.Add(ourCoroutinesStore[index].Coroutine); - ourCoroutinesStore.RemoveAt(index); - } - return; - } - } - catch (Exception e) - { - ExplorerCore.LogError(e.ToString()); - Stop(FindOriginalCoro(enumerator)); // We want the entire coroutine hierachy to stop when an error happen - } - - var next = enumerator.Current; - switch (next) - { - case null: - ourNextFrameCoroutines.Add(enumerator); - return; - case WaitForFixedUpdate _: - ourWaitForFixedUpdateCoroutines.Add(enumerator); - return; - case WaitForEndOfFrame _: - ourWaitForEndOfFrameCoroutines.Add(enumerator); - return; - case WaitForSeconds _: - break; // do nothing, this one is supported in Process - case Il2CppObjectBase il2CppObjectBase: - var nextAsEnumerator = il2CppObjectBase.TryCast(); - if (nextAsEnumerator != null) // il2cpp IEnumerator also handles CustomYieldInstruction - next = new Il2CppEnumeratorWrapper(nextAsEnumerator); - else - ExplorerCore.LogWarning($"Unknown coroutine yield object of type '{il2CppObjectBase}' for coroutine '{enumerator}'"); - return; - default: - ExplorerCore.LogWarning($"Unknown coroutine yield object of type '{next}' for coroutine '{enumerator}'"); - return; - } - - ourCoroutinesStore.Add(new CoroTuple { WaitCondition = next, Coroutine = enumerator }); - - if (next is IEnumerator nextCoro) - ProcessNextOfCoroutine(nextCoro); - } - - private static IEnumerator FindOriginalCoro(IEnumerator enumerator) - { - int index = ourCoroutinesStore.FindIndex(ct => ct.WaitCondition == enumerator); - if (index == -1) - return enumerator; - return FindOriginalCoro(ourCoroutinesStore[index].Coroutine); - } - - private class Il2CppEnumeratorWrapper : IEnumerator - { - private readonly Il2CppSystem.Collections.IEnumerator il2cppEnumerator; - - public Il2CppEnumeratorWrapper(Il2CppSystem.Collections.IEnumerator il2CppEnumerator) => il2cppEnumerator = il2CppEnumerator; - public bool MoveNext() => il2cppEnumerator.MoveNext(); - public void Reset() => il2cppEnumerator.Reset(); - public object Current => il2cppEnumerator.Current; - } - } -} -#endif \ No newline at end of file diff --git a/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs b/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs deleted file mode 100644 index 64091d9..0000000 --- a/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs +++ /dev/null @@ -1,268 +0,0 @@ -#if CPP -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using BF = System.Reflection.BindingFlags; -using System.Text; -using UnhollowerBaseLib; -using UnhollowerRuntimeLib; -using UnityEngine; -using UnityEngine.Events; -using UnityEngine.SceneManagement; -using System.Collections; -using UnityEngine.UI; -using UnityExplorer.Core.Input; -using UnityEngine.EventSystems; - -namespace UnityExplorer.Core.Runtime.Il2Cpp -{ - public class Il2CppProvider : RuntimeProvider - { - public override void Initialize() - { - ExplorerCore.Context = RuntimeContext.IL2CPP; - TextureUtil = new Il2CppTextureUtil(); - } - - public override void SetupEvents() - { - try - { - Application.add_logMessageReceived(new Action(Application_logMessageReceived)); - } - catch (Exception ex) - { - ExplorerCore.LogWarning("Exception setting up Unity log listener, make sure Unity libraries have been unstripped!"); - ExplorerCore.Log(ex); - } - } - - private void Application_logMessageReceived(string condition, string stackTrace, LogType type) - { - ExplorerCore.LogUnity(condition, type); - } - - public override void Update() - { - Il2CppCoroutine.Process(); - } - - internal override void ProcessOnPostRender() - { - Il2CppCoroutine.ProcessWaitForEndOfFrame(); - } - - internal override void ProcessFixedUpdate() - { - Il2CppCoroutine.ProcessWaitForFixedUpdate(); - } - - public override void StartCoroutine(IEnumerator routine) - { - Il2CppCoroutine.Start(routine); - } - - public override T AddComponent(GameObject obj, Type type) - { - return obj.AddComponent(Il2CppType.From(type)).TryCast(); - } - - public override ScriptableObject CreateScriptable(Type type) - { - return ScriptableObject.CreateInstance(Il2CppType.From(type)); - } - - // Pretty disgusting but couldn't figure out a cleaner way yet unfortunately - public override void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List list) - { - var il2cppList = new Il2CppSystem.Collections.Generic.List(); - - raycaster.Raycast(data, il2cppList); - - if (il2cppList.Count > 0) - list.AddRange(il2cppList.ToArray()); - } - - // LayerMask.LayerToName - - 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)); - } - - // Resources.FindObjectsOfTypeAll - - internal delegate IntPtr d_FindObjectsOfTypeAll(IntPtr type); - - internal static readonly string[] findObjectsOfTypeAllSignatures = new[] - { - "UnityEngine.Resources::FindObjectsOfTypeAll", - "UnityEngine.ResourcesAPIInternal::FindObjectsOfTypeAll" // Unity 2020+ updated to this - }; - - public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type) - { - return new Il2CppReferenceArray( - ICallManager.GetICallUnreliable(findObjectsOfTypeAllSignatures) - .Invoke(Il2CppType.From(type).Pointer)); - } - - // Scene.GetRootGameObjects(); - - internal delegate void d_GetRootGameObjects(int handle, IntPtr list); - - public override GameObject[] GetRootGameObjects(Scene scene) - { - if (!scene.isLoaded) - return new GameObject[0]; - - if (scene.handle == -1) - return new GameObject[0]; - - int count = GetRootCount(scene.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(scene.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); - } - - internal static bool triedToGetColorBlockProps; - internal static PropertyInfo _normalColorProp; - internal static PropertyInfo _highlightColorProp; - internal static PropertyInfo _pressedColorProp; - internal static PropertyInfo _disabledColorProp; - - public override void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null, - Color? disabled = null) - { - var colors = selectable.colors; - - colors.colorMultiplier = 1; - - object boxed = (object)colors; - - if (!triedToGetColorBlockProps) - { - triedToGetColorBlockProps = true; - - if (ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "normalColor") is PropertyInfo norm && norm.CanWrite) - _normalColorProp = norm; - if (ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "highlightedColor") is PropertyInfo high && high.CanWrite) - _highlightColorProp = high; - if (ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "pressedColor") is PropertyInfo pres && pres.CanWrite) - _pressedColorProp = pres; - if (ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "disabledColor") is PropertyInfo disa && disa.CanWrite) - _disabledColorProp = disa; - } - - try - { - if (normal != null) - { - if (_normalColorProp != null) - _normalColorProp.SetValue(boxed, (Color)normal); - else if (ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_NormalColor") is FieldInfo fi) - fi.SetValue(boxed, (Color)normal); - } - - if (highlighted != null) - { - if (_highlightColorProp != null) - _highlightColorProp.SetValue(boxed, (Color)highlighted); - else if (ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_HighlightedColor") is FieldInfo fi) - fi.SetValue(boxed, (Color)highlighted); - } - - if (pressed != null) - { - if (_pressedColorProp != null) - _pressedColorProp.SetValue(boxed, (Color)pressed); - else if (ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_PressedColor") is FieldInfo fi) - fi.SetValue(boxed, (Color)pressed); - } - - if (disabled != null) - { - if (_disabledColorProp != null) - _disabledColorProp.SetValue(boxed, (Color)disabled); - else if (ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_DisabledColor") is FieldInfo fi) - fi.SetValue(boxed, (Color)disabled); - } - } - catch (Exception ex) - { - ExplorerCore.Log(ex); - } - - colors = (ColorBlock)boxed; - - SetColorBlock(selectable, colors); - } - - public override void SetColorBlock(Selectable selectable, ColorBlock _colorBlock) - { - try - { - selectable = selectable.TryCast(); - - ReflectionUtility.GetPropertyInfo(typeof(Selectable), "m_Colors") - .SetValue(selectable, _colorBlock, null); - - ReflectionUtility.GetMethodInfo(typeof(Selectable), "OnSetProperty") - .Invoke(selectable, ArgumentUtility.EmptyArgs); - } - catch (Exception ex) - { - ExplorerCore.Log(ex); - } - } - } -} - -public static class Il2CppExtensions -{ - public static void AddListener(this UnityEvent action, Action listener) - { - action.AddListener(listener); - } - - public static void AddListener(this UnityEvent action, Action listener) - { - action.AddListener(listener); - } - - public static void RemoveListener(this UnityEvent action, Action listener) - { - action.RemoveListener(listener); - } - - public static void RemoveListener(this UnityEvent action, Action listener) - { - action.RemoveListener(listener); - } - - public static void SetChildControlHeight(this HorizontalOrVerticalLayoutGroup group, bool value) => group.childControlHeight = value; - public static void SetChildControlWidth(this HorizontalOrVerticalLayoutGroup group, bool value) => group.childControlWidth = value; -} - -#endif \ No newline at end of file diff --git a/src/Core/Runtime/Il2Cpp/Il2CppTextureUtil.cs b/src/Core/Runtime/Il2Cpp/Il2CppTextureUtil.cs deleted file mode 100644 index eb811a7..0000000 --- a/src/Core/Runtime/Il2Cpp/Il2CppTextureUtil.cs +++ /dev/null @@ -1,64 +0,0 @@ -#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); - } - - // 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/Il2CppProvider.cs b/src/Core/Runtime/Il2CppProvider.cs new file mode 100644 index 0000000..feca0a8 --- /dev/null +++ b/src/Core/Runtime/Il2CppProvider.cs @@ -0,0 +1,189 @@ +#if CPP +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using BF = System.Reflection.BindingFlags; +using System.Text; +using UnhollowerBaseLib; +using UnhollowerRuntimeLib; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.SceneManagement; +using System.Collections; +using UnityEngine.UI; +using UnityEngine.EventSystems; + +namespace UnityExplorer.Core.Runtime +{ + public class Il2CppProvider : RuntimeHelper + { + public override void SetupEvents() + { + try + { + Application.add_logMessageReceived(new Action(Application_logMessageReceived)); + } + catch (Exception ex) + { + ExplorerCore.LogWarning("Exception setting up Unity log listener, make sure Unity libraries have been unstripped!"); + ExplorerCore.Log(ex); + } + } + + private void Application_logMessageReceived(string condition, string stackTrace, LogType type) + { + ExplorerCore.LogUnity(condition, type); + } + + public override string[] DefaultReflectionBlacklist => defaultIl2CppBlacklist.ToArray(); + + // These methods currently cause a crash in most il2cpp games, + // even from doing "GetParameters()" on the MemberInfo. + // Blacklisting until the issue is fixed in Unhollower. + public static HashSet defaultIl2CppBlacklist = new HashSet + { + // These were deprecated a long time ago, still show up in some IL2CPP games for some reason + "UnityEngine.MonoBehaviour.allowPrefabModeInPlayMode", + "UnityEngine.MonoBehaviour.runInEditMode", + "UnityEngine.Component.animation", + "UnityEngine.Component.audio", + "UnityEngine.Component.camera", + "UnityEngine.Component.collider", + "UnityEngine.Component.collider2D", + "UnityEngine.Component.constantForce", + "UnityEngine.Component.hingeJoint", + "UnityEngine.Component.light", + "UnityEngine.Component.networkView", + "UnityEngine.Component.particleSystem", + "UnityEngine.Component.renderer", + "UnityEngine.Component.rigidbody", + "UnityEngine.Component.rigidbody2D", + "UnityEngine.Light.flare", + // These can cause a crash in IL2CPP + "Il2CppSystem.Type.DeclaringMethod", + "Il2CppSystem.RuntimeType.DeclaringMethod", + "Unity.Jobs.LowLevel.Unsafe.JobsUtility.CreateJobReflectionData", + "Unity.Profiling.ProfilerRecorder.CopyTo", + "Unity.Profiling.ProfilerRecorder.StartNew", + "UnityEngine.Analytics.Analytics.RegisterEvent", + "UnityEngine.Analytics.Analytics.SendEvent", + "UnityEngine.Analytics.ContinuousEvent+ConfigureEventDelegate.Invoke", + "UnityEngine.Analytics.ContinuousEvent.ConfigureEvent", + "UnityEngine.Animations.AnimationLayerMixerPlayable.Create", + "UnityEngine.Animations.AnimationLayerMixerPlayable.CreateHandle", + "UnityEngine.Animations.AnimationMixerPlayable.Create", + "UnityEngine.Animations.AnimationMixerPlayable.CreateHandle", + "UnityEngine.AssetBundle.RecompressAssetBundleAsync", + "UnityEngine.Audio.AudioMixerPlayable.Create", + "UnityEngine.BoxcastCommand.ScheduleBatch", + "UnityEngine.Camera.CalculateProjectionMatrixFromPhysicalProperties", + "UnityEngine.Canvas.renderingDisplaySize", + "UnityEngine.CapsulecastCommand.ScheduleBatch", + "UnityEngine.Collider2D.Cast", + "UnityEngine.Collider2D.Raycast", + "UnityEngine.ComputeBuffer+BeginBufferWriteDelegate.Invoke", + "UnityEngine.ComputeBuffer+EndBufferWriteDelegate.Invoke", + "UnityEngine.ComputeBuffer.BeginBufferWrite", + "UnityEngine.ComputeBuffer.EndBufferWrite", + "UnityEngine.Cubemap+SetPixelDataImplArrayDelegate.Invoke", + "UnityEngine.Cubemap+SetPixelDataImplDelegate.Invoke", + "UnityEngine.Cubemap.SetPixelDataImpl", + "UnityEngine.Cubemap.SetPixelDataImplArray", + "UnityEngine.CubemapArray+SetPixelDataImplArrayDelegate.Invoke", + "UnityEngine.CubemapArray+SetPixelDataImplDelegate.Invoke", + "UnityEngine.CubemapArray.SetPixelDataImpl", + "UnityEngine.CubemapArray.SetPixelDataImplArray", + "UnityEngine.Experimental.Playables.MaterialEffectPlayable.Create", + "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure+AddInstanceDelegate.Invoke", + "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure+AddInstance_Procedural_InjectedDelegate.Invoke", + "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance", + "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance_Procedural", + "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance_Procedural_Injected", + "UnityEngine.Experimental.Rendering.RayTracingShader+DispatchDelegate.Invoke", + "UnityEngine.Experimental.Rendering.RayTracingShader.Dispatch", + "UnityEngine.Experimental.Rendering.RenderPassAttachment.Clear", + "UnityEngine.GUI.DoButtonGrid", + "UnityEngine.GUI.Slider", + "UnityEngine.GUI.Toolbar", + "UnityEngine.Graphics.DrawMeshInstancedIndirect", + "UnityEngine.Graphics.DrawMeshInstancedProcedural", + "UnityEngine.Graphics.DrawProcedural", + "UnityEngine.Graphics.DrawProceduralIndirect", + "UnityEngine.Graphics.DrawProceduralIndirectNow", + "UnityEngine.Graphics.DrawProceduralNow", + "UnityEngine.LineRenderer+BakeMeshDelegate.Invoke", + "UnityEngine.LineRenderer.BakeMesh", + "UnityEngine.Mesh.GetIndices", + "UnityEngine.Mesh.GetTriangles", + "UnityEngine.Mesh.SetIndices", + "UnityEngine.Mesh.SetTriangles", + "UnityEngine.Physics2D.BoxCast", + "UnityEngine.Physics2D.CapsuleCast", + "UnityEngine.Physics2D.CircleCast", + "UnityEngine.PhysicsScene.BoxCast", + "UnityEngine.PhysicsScene.CapsuleCast", + "UnityEngine.PhysicsScene.OverlapBox", + "UnityEngine.PhysicsScene.OverlapCapsule", + "UnityEngine.PhysicsScene.SphereCast", + "UnityEngine.PhysicsScene2D.BoxCast", + "UnityEngine.PhysicsScene2D.CapsuleCast", + "UnityEngine.PhysicsScene2D.CircleCast", + "UnityEngine.PhysicsScene2D.GetRayIntersection", + "UnityEngine.PhysicsScene2D.Linecast", + "UnityEngine.PhysicsScene2D.OverlapArea", + "UnityEngine.PhysicsScene2D.OverlapBox", + "UnityEngine.PhysicsScene2D.OverlapCapsule", + "UnityEngine.PhysicsScene2D.OverlapCircle", + "UnityEngine.PhysicsScene2D.OverlapCollider", + "UnityEngine.PhysicsScene2D.OverlapPoint", + "UnityEngine.PhysicsScene2D.Raycast", + "UnityEngine.Playables.Playable.Create", + "UnityEngine.Profiling.CustomSampler.Create", + "UnityEngine.RaycastCommand.ScheduleBatch", + "UnityEngine.RemoteConfigSettings+QueueConfigDelegate.Invoke", + "UnityEngine.RemoteConfigSettings.QueueConfig", + "UnityEngine.RenderTexture.GetTemporaryImpl", + "UnityEngine.Rendering.AsyncGPUReadback.Request", + "UnityEngine.Rendering.AttachmentDescriptor.ConfigureClear", + "UnityEngine.Rendering.BatchRendererGroup+AddBatch_InjectedDelegate.Invoke", + "UnityEngine.Rendering.BatchRendererGroup.AddBatch", + "UnityEngine.Rendering.BatchRendererGroup.AddBatch_Injected", + "UnityEngine.Rendering.CommandBuffer+Internal_DispatchRaysDelegate.Invoke", + "UnityEngine.Rendering.CommandBuffer.DispatchRays", + "UnityEngine.Rendering.CommandBuffer.DrawMeshInstancedProcedural", + "UnityEngine.Rendering.CommandBuffer.Internal_DispatchRays", + "UnityEngine.Rendering.CommandBuffer.ResolveAntiAliasedSurface", + "UnityEngine.Rendering.ScriptableRenderContext.BeginRenderPass", + "UnityEngine.Rendering.ScriptableRenderContext.BeginScopedRenderPass", + "UnityEngine.Rendering.ScriptableRenderContext.BeginScopedSubPass", + "UnityEngine.Rendering.ScriptableRenderContext.BeginSubPass", + "UnityEngine.Rendering.ScriptableRenderContext.SetupCameraProperties", + "UnityEngine.Rigidbody2D.Cast", + "UnityEngine.Scripting.GarbageCollector+CollectIncrementalDelegate.Invoke", + "UnityEngine.Scripting.GarbageCollector.CollectIncremental", + "UnityEngine.SpherecastCommand.ScheduleBatch", + "UnityEngine.Texture.GetPixelDataSize", + "UnityEngine.Texture.GetPixelDataOffset", + "UnityEngine.Texture.GetPixelDataOffset", + "UnityEngine.Texture2D+SetPixelDataImplArrayDelegate.Invoke", + "UnityEngine.Texture2D+SetPixelDataImplDelegate.Invoke", + "UnityEngine.Texture2D.SetPixelDataImpl", + "UnityEngine.Texture2D.SetPixelDataImplArray", + "UnityEngine.Texture2DArray+SetPixelDataImplArrayDelegate.Invoke", + "UnityEngine.Texture2DArray+SetPixelDataImplDelegate.Invoke", + "UnityEngine.Texture2DArray.SetPixelDataImpl", + "UnityEngine.Texture2DArray.SetPixelDataImplArray", + "UnityEngine.Texture3D+SetPixelDataImplArrayDelegate.Invoke", + "UnityEngine.Texture3D+SetPixelDataImplDelegate.Invoke", + "UnityEngine.Texture3D.SetPixelDataImpl", + "UnityEngine.Texture3D.SetPixelDataImplArray", + "UnityEngine.TrailRenderer+BakeMeshDelegate.Invoke", + "UnityEngine.TrailRenderer.BakeMesh", + "UnityEngine.WWW.LoadFromCacheOrDownload", + "UnityEngine.XR.InputDevice.SendHapticImpulse", + }; + } +} + +#endif \ No newline at end of file diff --git a/src/Core/Runtime/Mono/MonoProvider.cs b/src/Core/Runtime/Mono/MonoProvider.cs deleted file mode 100644 index 4d0b5f1..0000000 --- a/src/Core/Runtime/Mono/MonoProvider.cs +++ /dev/null @@ -1,119 +0,0 @@ -#if MONO -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using UnityEngine; -using UnityEngine.Events; -using UnityEngine.EventSystems; -using UnityEngine.SceneManagement; -using UnityEngine.UI; -using UnityExplorer; - -namespace UnityExplorer.Core.Runtime.Mono -{ - public class MonoProvider : RuntimeProvider - { - public override void Initialize() - { - ExplorerCore.Context = RuntimeContext.Mono; - TextureUtil = new MonoTextureUtil(); - } - - public override void SetupEvents() - { - Application.logMessageReceived += Application_logMessageReceived; - } - - private void Application_logMessageReceived(string condition, string stackTrace, LogType type) - => ExplorerCore.LogUnity(condition, type); - - public override void StartCoroutine(IEnumerator routine) - => ExplorerBehaviour.Instance.StartCoroutine(routine); - - public override void Update() - { - } - - public override T AddComponent(GameObject obj, Type type) - => (T)obj.AddComponent(type); - - public override ScriptableObject CreateScriptable(Type type) - => ScriptableObject.CreateInstance(type); - - public override void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List list) - => raycaster.Raycast(data, list); - - public override string LayerToName(int layer) - => LayerMask.LayerToName(layer); - - public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type) - => Resources.FindObjectsOfTypeAll(type); - - public override GameObject[] GetRootGameObjects(Scene scene) - => scene.isLoaded ? scene.GetRootGameObjects() : new GameObject[0]; - - public override int GetRootCount(Scene scene) - => scene.rootCount; - - public override void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null, - Color? disabled = null) - { - var colors = selectable.colors; - - if (normal != null) - colors.normalColor = (Color)normal; - - if (highlighted != null) - colors.highlightedColor = (Color)highlighted; - - if (pressed != null) - colors.pressedColor = (Color)pressed; - - if (disabled != null) - colors.disabledColor = (Color)disabled; - - SetColorBlock(selectable, colors); - } - - public override void SetColorBlock(Selectable selectable, ColorBlock colors) - => selectable.colors = colors; - } -} - -public static class MonoExtensions -{ - // Helpers to use the same style of AddListener that IL2CPP uses. - - public static void AddListener(this UnityEvent _event, Action listener) - => _event.AddListener(new UnityAction(listener)); - - public static void AddListener(this UnityEvent _event, Action listener) - => _event.AddListener(new UnityAction(listener)); - - public static void RemoveListener(this UnityEvent _event, Action listener) - => _event.RemoveListener(new UnityAction(listener)); - - public static void RemoveListener(this UnityEvent _event, Action listener) - => _event.RemoveListener(new UnityAction(listener)); - - // Doesn't exist in NET 3.5 - - public static void Clear(this StringBuilder sb) - => sb.Remove(0, sb.Length); - - // These properties don't exist in some earlier games, so null check before trying to set them. - - public static void SetChildControlHeight(this HorizontalOrVerticalLayoutGroup group, bool value) - => ReflectionUtility.GetPropertyInfo(typeof(HorizontalOrVerticalLayoutGroup), "childControlHeight") - ?.SetValue(group, value, null); - - - public static void SetChildControlWidth(this HorizontalOrVerticalLayoutGroup group, bool value) - => ReflectionUtility.GetPropertyInfo(typeof(HorizontalOrVerticalLayoutGroup), "childControlWidth") - ?.SetValue(group, value, null); -} - -#endif \ No newline at end of file diff --git a/src/Core/Runtime/Mono/MonoTextureUtil.cs b/src/Core/Runtime/Mono/MonoTextureUtil.cs deleted file mode 100644 index 331b308..0000000 --- a/src/Core/Runtime/Mono/MonoTextureUtil.cs +++ /dev/null @@ -1,53 +0,0 @@ -#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) - => Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero); - - //public override bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable) - // => tex.LoadImage(data, markNonReadable); - - public override Texture2D NewTexture2D(int width, int height) - => new Texture2D(width, height); - - public override byte[] EncodeToPNG(Texture2D tex) - => EncodeToPNGSafe(tex); - - private static MethodInfo EncodeToPNGMethod => m_encodeToPNGMethod ?? GetEncodeToPNGMethod(); - private static MethodInfo m_encodeToPNGMethod; - - public static byte[] EncodeToPNGSafe(Texture2D tex) - { - return EncodeToPNGMethod.IsStatic - ? (byte[])EncodeToPNGMethod.Invoke(null, new object[] { tex }) - : (byte[])EncodeToPNGMethod.Invoke(tex, ArgumentUtility.EmptyArgs); - } - - private static MethodInfo GetEncodeToPNGMethod() - { - if (ReflectionUtility.GetTypeByName("UnityEngine.ImageConversion") is Type imageConversion) - return m_encodeToPNGMethod = imageConversion.GetMethod("EncodeToPNG", ReflectionUtility.FLAGS); - - var method = typeof(Texture2D).GetMethod("EncodeToPNG", ReflectionUtility.FLAGS); - 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/MonoProvider.cs b/src/Core/Runtime/MonoProvider.cs new file mode 100644 index 0000000..ed1562b --- /dev/null +++ b/src/Core/Runtime/MonoProvider.cs @@ -0,0 +1,29 @@ +#if MONO +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.EventSystems; +using UnityEngine.SceneManagement; +using UnityEngine.UI; +using UnityExplorer; + +namespace UnityExplorer.Core.Runtime +{ + public class MonoProvider : RuntimeHelper + { + public override void SetupEvents() + { + Application.logMessageReceived += Application_logMessageReceived; + } + + private void Application_logMessageReceived(string condition, string stackTrace, LogType type) + => ExplorerCore.LogUnity(condition, type); + } +} + +#endif \ No newline at end of file diff --git a/src/Core/Runtime/RuntimeContext.cs b/src/Core/Runtime/RuntimeContext.cs deleted file mode 100644 index 1dbd7fe..0000000 --- a/src/Core/Runtime/RuntimeContext.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace UnityExplorer.Core.Runtime -{ - public enum RuntimeContext - { - Mono, - IL2CPP - } -} diff --git a/src/Core/Runtime/RuntimeHelper.cs b/src/Core/Runtime/RuntimeHelper.cs new file mode 100644 index 0000000..d6166d9 --- /dev/null +++ b/src/Core/Runtime/RuntimeHelper.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.SceneManagement; +using UnityEngine.UI; +using UnityExplorer.Core.Config; +using UniverseLib; + +namespace UnityExplorer.Core.Runtime +{ + public abstract class RuntimeHelper + { + public static RuntimeHelper Instance; + + public static void Init() + { +#if CPP + Instance = new Il2CppProvider(); +#else + Instance = new MonoProvider(); +#endif + Instance.SetupEvents(); + + LoadBlacklistString(ConfigManager.Reflection_Signature_Blacklist.Value); + ConfigManager.Reflection_Signature_Blacklist.OnValueChanged += (string val) => + { + LoadBlacklistString(val); + }; + } + + public abstract void SetupEvents(); + + #region Reflection Blacklist + + private static readonly HashSet currentBlacklist = new HashSet(); + + public virtual string[] DefaultReflectionBlacklist => new string[0]; + + public static void LoadBlacklistString(string blacklist) + { + try + { + if (string.IsNullOrEmpty(blacklist) && !Instance.DefaultReflectionBlacklist.Any()) + return; + + try + { + var sigs = blacklist.Split(';'); + foreach (var sig in sigs) + { + var s = sig.Trim(); + if (string.IsNullOrEmpty(s)) + continue; + if (!currentBlacklist.Contains(s)) + currentBlacklist.Add(s); + } + } + catch (Exception ex) + { + ExplorerCore.LogWarning($"Exception parsing blacklist string: {ex.ReflectionExToString()}"); + } + + foreach (var sig in Instance.DefaultReflectionBlacklist) + { + if (!currentBlacklist.Contains(sig)) + currentBlacklist.Add(sig); + } + + Mono.CSharp.IL2CPP.Blacklist.SignatureBlacklist = currentBlacklist; + } + catch (Exception ex) + { + ExplorerCore.LogWarning($"Exception setting up reflection blacklist: {ex.ReflectionExToString()}"); + } + } + + public static bool IsBlacklisted(MemberInfo member) + { + if (string.IsNullOrEmpty(member.DeclaringType?.Namespace)) + return false; + + var sig = $"{member.DeclaringType.FullName}.{member.Name}"; + + return currentBlacklist.Contains(sig); + } + + #endregion + } +} diff --git a/src/Core/Runtime/RuntimeProvider.cs b/src/Core/Runtime/RuntimeProvider.cs deleted file mode 100644 index d27336e..0000000 --- a/src/Core/Runtime/RuntimeProvider.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine; -using UnityEngine.EventSystems; -using UnityEngine.SceneManagement; -using UnityEngine.UI; -using UnityExplorer.Core.Runtime; - -// Intentionally project-wide namespace so that its always easily accessible. -namespace UnityExplorer -{ - public abstract class RuntimeProvider - { - public static RuntimeProvider Instance; - - public TextureUtilProvider TextureUtil; - - public RuntimeProvider() - { - Initialize(); - - SetupEvents(); - } - - public static void Init() => -#if CPP - Instance = new Core.Runtime.Il2Cpp.Il2CppProvider(); -#else - Instance = new Core.Runtime.Mono.MonoProvider(); -#endif - - public abstract void Initialize(); - - public abstract void SetupEvents(); - - public abstract void StartCoroutine(IEnumerator routine); - - public abstract void Update(); - - // Unity API handlers - - public abstract T AddComponent(GameObject obj, Type type) where T : Component; - - public abstract ScriptableObject CreateScriptable(Type type); - - public abstract string LayerToName(int layer); - - public abstract UnityEngine.Object[] FindObjectsOfTypeAll(Type type); - - public abstract void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List list); - - public abstract GameObject[] GetRootGameObjects(Scene scene); - - public abstract int GetRootCount(Scene scene); - - public void SetColorBlockAuto(Selectable selectable, Color baseColor) - => SetColorBlock(selectable, baseColor, baseColor * 1.2f, baseColor * 0.8f); - - public abstract void SetColorBlock(Selectable selectable, ColorBlock colors); - - public abstract void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null, - Color? disabled = null); - - internal virtual void ProcessOnPostRender() - { - } - - internal virtual void ProcessFixedUpdate() - { - } - } -} diff --git a/src/Core/Runtime/TextureUtilProvider.cs b/src/Core/Runtime/TextureUtilProvider.cs deleted file mode 100644 index 3cecfc2..0000000 --- a/src/Core/Runtime/TextureUtilProvider.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using UnityEngine; - -namespace UnityExplorer.Core.Runtime -{ - public abstract class TextureUtilProvider - { - public static TextureUtilProvider Instance; - - public TextureUtilProvider() - { - Instance = this; - } - - public abstract byte[] EncodeToPNG(Texture2D tex); - - public abstract Texture2D NewTexture2D(int width, int height); - - public abstract void Blit(Texture2D tex, RenderTexture rt); - - //public abstract bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable); - - public abstract Sprite CreateSprite(Texture2D texture); - - public static bool IsReadable(Texture2D tex) - { - try - { - // This will cause an exception if it's not readable. - // Reason for doing it this way is not all Unity versions - // ship with the 'Texture.isReadable' property. - - tex.GetPixel(0, 0); - return true; - } - catch - { - return false; - } - } - - //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) - { - if (!IsReadable(orig)) - orig = ForceReadTexture(orig); - - Color[] pixels = orig.GetPixels((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); - Texture2D newTex = Instance.NewTexture2D((int)rect.width, (int)rect.height); - newTex.SetPixels(pixels); - return newTex; - } - - public static Texture2D ForceReadTexture(Texture2D tex) - { - try - { - FilterMode origFilter = tex.filterMode; - tex.filterMode = FilterMode.Point; - - var rt = RenderTexture.GetTemporary(tex.width, tex.height, 0, RenderTextureFormat.ARGB32); - rt.filterMode = FilterMode.Point; - RenderTexture.active = rt; - - Instance.Blit(tex, rt); - - var _newTex = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false); - - _newTex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0); - _newTex.Apply(false, false); - - RenderTexture.active = null; - tex.filterMode = origFilter; - - return _newTex; - } - catch (Exception e) - { - ExplorerCore.Log($"Exception on ForceReadTexture: {e.ToString()}"); - return default; - } - } - - public static void SaveTextureAsPNG(Texture2D tex, string dir, string name, bool isDTXnmNormal = false) - { - if (!Directory.Exists(dir)) - Directory.CreateDirectory(dir); - - byte[] data; - string savepath = $@"{dir}\{name}.png"; - - // Make sure we can EncodeToPNG it. - if (tex.format != TextureFormat.ARGB32 || !IsReadable(tex)) - tex = ForceReadTexture(tex); - - if (isDTXnmNormal) - { - tex = DTXnmToRGBA(tex); - tex.Apply(false, false); - } - - data = Instance.EncodeToPNG(tex); - - if (data == null || !data.Any()) - ExplorerCore.LogWarning("Couldn't get any data for the texture!"); - else - File.WriteAllBytes(savepath, data); - } - - // Converts DTXnm-format Normal Map to RGBA-format Normal Map. - public static Texture2D DTXnmToRGBA(Texture2D tex) - { - Color[] colors = tex.GetPixels(); - - for (int i = 0; i < colors.Length; i++) - { - var c = colors[i]; - - c.r = c.a * 2 - 1; // red <- alpha - c.g = c.g * 2 - 1; // green is always the same - - var rg = new Vector2(c.r, c.g); //this is the red-green vector - c.b = Mathf.Sqrt(1 - Mathf.Clamp01(Vector2.Dot(rg, rg))); //recalculate the blue channel - - colors[i] = new Color( - (c.r * 0.5f) + 0.5f, - (c.g * 0.5f) + 0.25f, - (c.b * 0.5f) + 0.5f - ); - } - - var newtex = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false); - newtex.SetPixels(colors); - - return newtex; - } - } -} diff --git a/src/Core/Utility/ArgumentUtility.cs b/src/Core/Utility/ArgumentUtility.cs deleted file mode 100644 index c7b1634..0000000 --- a/src/Core/Utility/ArgumentUtility.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace UnityExplorer -{ - public static class ArgumentUtility - { - public static readonly Type[] EmptyTypes = new Type[0]; - public static readonly object[] EmptyArgs = new object[0]; - - public static readonly Type[] ParseArgs = new Type[] { typeof(string) }; - } -} diff --git a/src/Core/Utility/IOUtility.cs b/src/Core/Utility/IOUtility.cs deleted file mode 100644 index d7975d9..0000000 --- a/src/Core/Utility/IOUtility.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace UnityExplorer -{ - public static class IOUtility - { - private static readonly char[] invalidDirectoryCharacters = Path.GetInvalidPathChars(); - private static readonly char[] invalidFilenameCharacters = Path.GetInvalidFileNameChars(); - - public static string EnsureValidFilePath(string fullPathWithFile) - { - // Remove invalid path characters - fullPathWithFile = string.Concat(fullPathWithFile.Split(invalidDirectoryCharacters)); - - // Create directory (does nothing if it exists) - Directory.CreateDirectory(Path.GetDirectoryName(fullPathWithFile)); - - return fullPathWithFile; - } - - public static string EnsureValidFilename(string filename) - { - return string.Concat(filename.Split(invalidFilenameCharacters)); - } - } -} diff --git a/src/Core/Utility/MiscUtility.cs b/src/Core/Utility/MiscUtility.cs deleted file mode 100644 index ba93678..0000000 --- a/src/Core/Utility/MiscUtility.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace UnityExplorer -{ - public static class MiscUtility - { - /// - /// Check if a string contains another string, case-insensitive. - /// - public static bool ContainsIgnoreCase(this string _this, string s) - { - return CultureInfo.CurrentCulture.CompareInfo.IndexOf(_this, s, CompareOptions.IgnoreCase) >= 0; - } - - /// - /// Just to allow Enum to do .HasFlag() in NET 3.5 - /// - public static bool HasFlag(this Enum flags, Enum value) - { - ulong flag = Convert.ToUInt64(value); - return (Convert.ToUInt64(flags) & flag) == flag; - } - } -} diff --git a/src/Core/Utility/ParseUtility.cs b/src/Core/Utility/ParseUtility.cs deleted file mode 100644 index 7086db5..0000000 --- a/src/Core/Utility/ParseUtility.cs +++ /dev/null @@ -1,414 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; -using System.Text; -using UnityEngine; - -namespace UnityExplorer -{ - public static class ParseUtility - { - private static readonly HashSet nonPrimitiveTypes = new HashSet - { - typeof(string), - typeof(decimal), - typeof(DateTime), - }; - - // Helper for formatting float/double/decimal numbers to maximum of 4 decimal points. - // And also for formatting a sequence of those numbers, ie a Vector3, Color etc - - public static readonly string NumberFormatString = $"0.####"; - private static readonly Dictionary numSequenceStrings = new Dictionary(); - - public static string FormatDecimalSequence(params object[] numbers) - { - if (numbers.Length <= 0) - return null; - - return string.Format(CultureInfo.CurrentCulture, GetSequenceFormatString(numbers.Length), numbers); - } - - public static string GetSequenceFormatString(int count) - { - if (count <= 0) - return null; - - if (numSequenceStrings.ContainsKey(count)) - return numSequenceStrings[count]; - - string[] strings = new string[count]; - - for (int i = 0; i < count; i++) - strings[i] = $"{{{i}:{NumberFormatString}}}"; - - string ret = string.Join(" ", strings); - numSequenceStrings.Add(count, ret); - return ret; - } - - // Main parsing API - - public static bool CanParse(Type type) - { - return !string.IsNullOrEmpty(type?.FullName) - && (type.IsPrimitive || type.IsEnum || nonPrimitiveTypes.Contains(type) || customTypes.ContainsKey(type.FullName)); - } - - public static bool TryParse(string input, Type type, out object obj, out Exception parseException) - { - obj = null; - parseException = null; - - if (type == null) - return false; - - if (type == typeof(string)) - { - obj = input; - return true; - } - - if (type.IsEnum) - { - try - { - obj = Enum.Parse(type, input); - return true; - } - catch (Exception ex) - { - parseException = ex.GetInnerMostException(); - return false; - } - } - - try - { - if (customTypes.ContainsKey(type.FullName)) - { - obj = customTypes[type.FullName].Invoke(input); - } - else - { - obj = ReflectionUtility.GetMethodInfo(type, "Parse", ArgumentUtility.ParseArgs) - .Invoke(null, new object[] { input }); - } - - return true; - } - catch (Exception ex) - { - ex = ex.GetInnerMostException(); - parseException = ex; - } - - return false; - } - - private static readonly HashSet formattedTypes = new HashSet - { - typeof(float), - typeof(double), - typeof(decimal) - }; - - public static string ToStringForInput(object obj, Type type) - { - if (type == null || obj == null) - return null; - - if (type == typeof(string)) - return obj as string; - - if (type.IsEnum) - { - return Enum.IsDefined(type, obj) - ? Enum.GetName(type, obj) - : obj.ToString(); - } - - try - { - if (customTypes.ContainsKey(type.FullName)) - { - return customTypesToString[type.FullName].Invoke(obj); - } - else if (formattedTypes.Contains(type)) - { - return ReflectionUtility.GetMethodInfo(type, "ToString", new Type[] { typeof(string), typeof(IFormatProvider) }) - .Invoke(obj, new object[] { NumberFormatString, CultureInfo.CurrentCulture }) - as string; - } - else - return obj.ToString(); - - } - catch (Exception ex) - { - ExplorerCore.LogWarning($"Exception formatting object for input: {ex}"); - return null; - } - } - - private static readonly Dictionary typeInputExamples = new Dictionary(); - - public static string GetExampleInput(Type type) - { - if (!typeInputExamples.ContainsKey(type.AssemblyQualifiedName)) - { - try - { - if (type.IsEnum) - typeInputExamples.Add(type.AssemblyQualifiedName, Enum.GetNames(type).First()); - else - { - var instance = Activator.CreateInstance(type); - typeInputExamples.Add(type.AssemblyQualifiedName, ToStringForInput(instance, type)); - } - } - catch (Exception ex) - { - ExplorerCore.LogWarning("Exception generating default instance for example input for '" + type.FullName + "'"); - ExplorerCore.Log(ex); - return ""; - } - } - - return typeInputExamples[type.AssemblyQualifiedName]; - } - - #region Custom parse methods - - internal delegate object ParseMethod(string input); - - private static readonly Dictionary customTypes = new Dictionary - { - { typeof(Vector2).FullName, TryParseVector2 }, - { typeof(Vector3).FullName, TryParseVector3 }, - { typeof(Vector4).FullName, TryParseVector4 }, - { typeof(Quaternion).FullName, TryParseQuaternion }, - { typeof(Rect).FullName, TryParseRect }, - { typeof(Color).FullName, TryParseColor }, - { typeof(Color32).FullName, TryParseColor32 }, - { typeof(LayerMask).FullName, TryParseLayerMask }, - }; - - internal delegate string ToStringMethod(object obj); - - private static readonly Dictionary customTypesToString = new Dictionary - { - { typeof(Vector2).FullName, Vector2ToString }, - { typeof(Vector3).FullName, Vector3ToString }, - { typeof(Vector4).FullName, Vector4ToString }, - { typeof(Quaternion).FullName, QuaternionToString }, - { typeof(Rect).FullName, RectToString }, - { typeof(Color).FullName, ColorToString }, - { typeof(Color32).FullName, Color32ToString }, - { typeof(LayerMask).FullName, LayerMaskToString }, - }; - - // Vector2 - - public static object TryParseVector2(string input) - { - Vector2 vector = default; - - var split = input.Split(' '); - - vector.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture); - vector.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture); - - return vector; - } - - public static string Vector2ToString(object obj) - { - if (!(obj is Vector2 vector)) - return null; - - return FormatDecimalSequence(vector.x, vector.y); - } - - // Vector3 - - public static object TryParseVector3(string input) - { - Vector3 vector = default; - - var split = input.Split(' '); - - vector.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture); - vector.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture); - vector.z = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture); - - return vector; - } - - public static string Vector3ToString(object obj) - { - if (!(obj is Vector3 vector)) - return null; - - return FormatDecimalSequence(vector.x, vector.y, vector.z); - } - - // Vector4 - - public static object TryParseVector4(string input) - { - Vector4 vector = default; - - var split = input.Split(' '); - - vector.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture); - vector.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture); - vector.z = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture); - vector.w = float.Parse(split[3].Trim(), CultureInfo.CurrentCulture); - - return vector; - } - - public static string Vector4ToString(object obj) - { - if (!(obj is Vector4 vector)) - return null; - - return FormatDecimalSequence(vector.x, vector.y, vector.z, vector.w); - } - - // Quaternion - - public static object TryParseQuaternion(string input) - { - Vector3 vector = default; - - var split = input.Split(' '); - - if (split.Length == 4) - { - Quaternion quat = default; - quat.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture); - quat.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture); - quat.z = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture); - quat.w = float.Parse(split[3].Trim(), CultureInfo.CurrentCulture); - return quat; - } - else - { - vector.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture); - vector.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture); - vector.z = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture); - return Quaternion.Euler(vector); - } - } - - public static string QuaternionToString(object obj) - { - if (!(obj is Quaternion quaternion)) - return null; - - Vector3 vector = quaternion.eulerAngles; - - return FormatDecimalSequence(vector.x, vector.y, vector.z); - } - - // Rect - - public static object TryParseRect(string input) - { - Rect rect = default; - - var split = input.Split(' '); - - rect.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture); - rect.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture); - rect.width = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture); - rect.height = float.Parse(split[3].Trim(), CultureInfo.CurrentCulture); - - return rect; - } - - public static string RectToString(object obj) - { - if (!(obj is Rect rect)) - return null; - - return FormatDecimalSequence(rect.x, rect.y, rect.width, rect.height); - } - - // Color - - public static object TryParseColor(string input) - { - Color color = default; - - var split = input.Split(' '); - - color.r = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture); - color.g = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture); - color.b = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture); - if (split.Length > 3) - color.a = float.Parse(split[3].Trim(), CultureInfo.CurrentCulture); - else - color.a = 1; - - return color; - } - - public static string ColorToString(object obj) - { - if (!(obj is Color color)) - return null; - - return FormatDecimalSequence(color.r, color.g, color.b, color.a); - } - - // Color32 - - public static object TryParseColor32(string input) - { - Color32 color = default; - - var split = input.Split(' '); - - color.r = byte.Parse(split[0].Trim(), CultureInfo.CurrentCulture); - color.g = byte.Parse(split[1].Trim(), CultureInfo.CurrentCulture); - color.b = byte.Parse(split[2].Trim(), CultureInfo.CurrentCulture); - if (split.Length > 3) - color.a = byte.Parse(split[3].Trim(), CultureInfo.CurrentCulture); - else - color.a = 255; - - return color; - } - - public static string Color32ToString(object obj) - { - if (!(obj is Color32 color)) - return null; - - // ints, this is fine - return $"{color.r} {color.g} {color.b} {color.a}"; - } - - // Layermask (Int32) - - public static object TryParseLayerMask(string input) - { - return (LayerMask)int.Parse(input); - } - - public static string LayerMaskToString(object obj) - { - if (!(obj is LayerMask mask)) - return null; - - return mask.value.ToString(); - } - - #endregion - } -} diff --git a/src/Core/Utility/SignatureHighlighter.cs b/src/Core/Utility/SignatureHighlighter.cs deleted file mode 100644 index 0b789a6..0000000 --- a/src/Core/Utility/SignatureHighlighter.cs +++ /dev/null @@ -1,294 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using UnityEngine; -using UnityExplorer.Core.Runtime; - -namespace UnityExplorer -{ - /// - /// Syntax-highlights a member's signature, by either the Type name or a Type and Member together. - /// - public static class SignatureHighlighter - { - public const string NAMESPACE = "#a8a8a8"; - - public const string CONST = "#92c470"; - - public const string CLASS_STATIC = "#3a8d71"; - public const string CLASS_INSTANCE = "#2df7b2"; - - public const string STRUCT = "#0fba3a"; - public const string INTERFACE = "#9b9b82"; - - public const string FIELD_STATIC = "#8d8dc6"; - public const string FIELD_INSTANCE = "#c266ff"; - - public const string METHOD_STATIC = "#b55b02"; - public const string METHOD_INSTANCE = "#ff8000"; - - public const string PROP_STATIC = "#588075"; - public const string PROP_INSTANCE = "#55a38e"; - - public const string LOCAL_ARG = "#a6e9e9"; - - internal const string ARRAY_TOKEN = "[]"; - internal const string OPEN_COLOR = "').Append(ns).Append(CLOSE_COLOR).Append('.'); - - // Declaring type - - var declaring = type.DeclaringType; - while (declaring != null) - { - syntaxBuilder.Append(HighlightType(declaring)); - syntaxBuilder.Append('.'); - declaring = declaring.DeclaringType; - } - } - - // Highlight the type name - - syntaxBuilder.Append(HighlightType(type)); - - // If memberInfo, highlight the member info - - if (memberInfo != null) - { - syntaxBuilder.Append('.'); - - int start = syntaxBuilder.Length - 1; - syntaxBuilder.Append(OPEN_COLOR) - .Append(GetMemberInfoColor(memberInfo, out bool isStatic)) - .Append('>') - .Append(memberInfo.Name) - .Append(CLOSE_COLOR); - - if (isStatic) - { - syntaxBuilder.Insert(start, OPEN_ITALIC); - syntaxBuilder.Append(CLOSE_ITALIC); - } - - if (memberInfo is MethodInfo method) - { - var args = method.GetGenericArguments(); - if (args.Length > 0) - syntaxBuilder.Append('<').Append(ParseGenericArgs(args, true)).Append('>'); - } - } - - return syntaxBuilder.ToString(); - } - - private static readonly Dictionary typeToRichType = new Dictionary(); - - private static bool EndsWith(this StringBuilder sb, string _string) - { - int len = _string.Length; - - if (sb.Length < len) - return false; - - int stringpos = 0; - for (int i = sb.Length - len; i < sb.Length; i++, stringpos++) - { - if (sb[i] != _string[stringpos]) - return false; - } - return true; - } - - private static string HighlightType(Type type) - { - string key = type.ToString(); - - if (typeToRichType.ContainsKey(key)) - return typeToRichType[key]; - - var sb = new StringBuilder(type.Name); - - bool isArray = false; - if (sb.EndsWith(ARRAY_TOKEN)) - { - isArray = true; - sb.Remove(sb.Length - 2, 2); - type = type.GetElementType(); - } - - if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter)) - { - sb.Insert(0, $""); - sb.Append(CLOSE_COLOR); - } - else - { - var args = type.GetGenericArguments(); - - if (args.Length > 0) - { - // remove the `N from the end of the type name - // this could actually be >9 in some cases, so get the length of the length string and use that. - // eg, if it was "List`15", we would remove the ending 3 chars - - int suffixLen = 1 + args.Length.ToString().Length; - - // make sure the typename actually has expected "`N" format. - if (sb[sb.Length - suffixLen] == '`') - sb.Remove(sb.Length - suffixLen, suffixLen); - } - - // highlight the base name itself - // do this after removing the `N suffix, so only the name itself is in the color tags. - sb.Insert(0, $"{OPEN_COLOR}{GetClassColor(type)}>"); - sb.Append(CLOSE_COLOR); - - // parse the generic args, if any - if (args.Length > 0) - { - sb.Append('<').Append(ParseGenericArgs(args)).Append('>'); - } - } - - if (isArray) - sb.Append('[').Append(']'); - - var ret = sb.ToString(); - typeToRichType.Add(key, ret); - - return ret; - } - - public static string ParseGenericArgs(Type[] args, bool isGenericParams = false) - { - if (args.Length < 1) - return string.Empty; - - var sb = new StringBuilder(); - - for (int i = 0; i < args.Length; i++) - { - if (i > 0) - sb.Append(',').Append(' '); - - if (isGenericParams) - { - sb.Append(OPEN_COLOR).Append(CONST).Append('>').Append(args[i].Name).Append(CLOSE_COLOR); - continue; - } - - sb.Append(HighlightType(args[i])); - } - - return sb.ToString(); - } - - public static string GetMemberInfoColor(MemberTypes type) - { - switch (type) - { - case MemberTypes.Method: return METHOD_INSTANCE; - case MemberTypes.Property: return PROP_INSTANCE; - case MemberTypes.Field: return FIELD_INSTANCE; - default: return null; - } - } - - - public static string GetMemberInfoColor(MemberInfo memberInfo, out bool isStatic) - { - isStatic = false; - if (memberInfo is FieldInfo fi) - { - if (fi.IsStatic) - { - isStatic = true; - return FIELD_STATIC; - } - - return FIELD_INSTANCE; - } - else if (memberInfo is MethodInfo mi) - { - if (mi.IsStatic) - { - isStatic = true; - return METHOD_STATIC; - } - - return METHOD_INSTANCE; - } - else if (memberInfo is PropertyInfo pi) - { - if (pi.GetAccessors(true)[0].IsStatic) - { - isStatic = true; - return PROP_STATIC; - } - - return PROP_INSTANCE; - } - //else if (memberInfo is EventInfo ei) - //{ - // if (ei.GetAddMethod().IsStatic) - // { - // isStatic = true; - // return EVENT_STATIC; - // } - - // return EVENT_INSTANCE; - //} - - throw new NotImplementedException(memberInfo.GetType().Name + " is not supported"); - } - } -} diff --git a/src/Core/Utility/ToStringUtility.cs b/src/Core/Utility/ToStringUtility.cs deleted file mode 100644 index 86df425..0000000 --- a/src/Core/Utility/ToStringUtility.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using UnityEngine; -using UnityEngine.EventSystems; -using UnityExplorer.Core.Runtime; - -namespace UnityExplorer -{ - public static class ToStringUtility - { - internal static Dictionary toStringMethods = new Dictionary(); - - private const string nullString = "null"; - private const string nullUnknown = nullString + " (?)"; - private const string destroyedString = "Destroyed"; - private const string untitledString = "untitled"; - - private const string eventSystemNamespace = "UnityEngine.EventSystem"; - - public static string PruneString(string s, int chars = 200, int lines = 5) - { - if (string.IsNullOrEmpty(s)) - return s; - - var sb = new StringBuilder(Math.Max(chars, s.Length)); - int newlines = 0; - for (int i = 0; i < s.Length; i++) - { - if (newlines >= lines || i >= chars) - { - sb.Append("..."); - break; - } - char c = s[i]; - if (c == '\r' || c == '\n') - newlines++; - sb.Append(c); - } - - return sb.ToString(); - } - - public static string ToStringWithType(object value, Type fallbackType, bool includeNamespace = true) - { - if (value.IsNullOrDestroyed() && fallbackType == null) - return nullUnknown; - - Type type = value?.GetActualType() ?? fallbackType; - - string richType = SignatureHighlighter.Parse(type, includeNamespace); - - var sb = new StringBuilder(); - - if (value.IsNullOrDestroyed()) - { - if (value == null) - { - sb.Append(nullString); - AppendRichType(sb, richType); - return sb.ToString(); - } - else // destroyed unity object - { - sb.Append(destroyedString); - AppendRichType(sb, richType); - return sb.ToString(); - } - } - - if (value is UnityEngine.Object obj) - { - if (string.IsNullOrEmpty(obj.name)) - sb.Append(untitledString); - else - { - sb.Append('"'); - sb.Append(PruneString(obj.name, 50, 1)); - sb.Append('"'); - } - - AppendRichType(sb, richType); - } - else if (type.FullName.StartsWith(eventSystemNamespace)) - { - // UnityEngine.EventSystem classes can have some obnoxious ToString results with rich text. - sb.Append(richType); - } - else - { - var toString = ToString(value); - - if (type.IsGenericType - || toString == type.FullName - || toString == $"{type.FullName} {type.FullName}" - || toString == $"Il2Cpp{type.FullName}" || type.FullName == $"Il2Cpp{toString}") - { - sb.Append(richType); - } - else // the ToString contains some actual implementation, use that value. - { - sb.Append(PruneString(toString, 200, 5)); - - AppendRichType(sb, richType); - } - } - - return sb.ToString(); - } - - private static void AppendRichType(StringBuilder sb, string richType) - { - sb.Append(' '); - sb.Append('('); - sb.Append(richType); - sb.Append(')'); - } - - private static string ToString(object value) - { - if (value.IsNullOrDestroyed()) - { - if (value == null) - return nullString; - else // destroyed unity object - return destroyedString; - } - - var type = value.GetActualType(); - - // Find and cache the ToString method for this Type, if haven't already. - - if (!toStringMethods.ContainsKey(type.AssemblyQualifiedName)) - { - var toStringMethod = type.GetMethod("ToString", ArgumentUtility.EmptyTypes); - toStringMethods.Add(type.AssemblyQualifiedName, toStringMethod); - } - - // Invoke the ToString method on the object - - value = value.TryCast(type); - - string toString; - try - { - toString = (string)toStringMethods[type.AssemblyQualifiedName].Invoke(value, ArgumentUtility.EmptyArgs); - } - catch (Exception ex) - { - toString = ex.ReflectionExToString(); - } - - toString = ReflectionUtility.ProcessTypeInString(type, toString); - -#if CPP - if (value is Il2CppSystem.Type cppType) - { - var monoType = Il2CppReflection.GetUnhollowedType(cppType); - if (monoType != null) - toString = ReflectionUtility.ProcessTypeInString(monoType, toString); - } -#endif - - return toString; - } - } -} diff --git a/src/Core/Utility/UnityHelpers.cs b/src/Core/Utility/UnityHelpers.cs deleted file mode 100644 index 90aab08..0000000 --- a/src/Core/Utility/UnityHelpers.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; -using System.Text; -using UnityEngine; -using UnityEngine.Events; -using UnityEngine.UI; -using Object = UnityEngine.Object; - -namespace UnityExplorer -{ - public static class UnityHelpers - { - // Time helpers, can't use Time.time since timeScale will affect it. - - // default 10ms (one frame at 100fps) - public static bool OccuredEarlierThanDefault(this float time) - { - return Time.realtimeSinceStartup - 0.01f >= time; - } - - public static bool OccuredEarlierThan(this float time, float secondsAgo) - { - return Time.realtimeSinceStartup - secondsAgo >= time; - } - - /// - /// Check if an object is null, and if it's a UnityEngine.Object then also check if it was destroyed. - /// - public static bool IsNullOrDestroyed(this object obj, bool suppressWarning = true) - { - try - { - if (obj == null) - { - if (!suppressWarning) - ExplorerCore.LogWarning("The target instance is null!"); - - return true; - } - else if (obj is Object unityObj && !unityObj) - { - if (!suppressWarning) - ExplorerCore.LogWarning("The target UnityEngine.Object was destroyed!"); - - return true; - } - return false; - } - catch - { - return true; - } - } - - /// - /// Get the full Transform heirarchy path for this provided Transform. - /// - public static string GetTransformPath(this Transform transform, bool includeSelf = false) - { - var sb = new StringBuilder(); - if (includeSelf) - sb.Append(transform.name); - - while (transform.parent) - { - transform = transform.parent; - sb.Insert(0, '/'); - sb.Insert(0, transform.name); - } - - return sb.ToString(); - } - - /// - /// Converts Color to 6-digit RGB hex code (without # symbol). Eg, RGBA(1,0,0,1) -> FF0000 - /// - public static string ToHex(this Color color) - { - byte r = (byte)Mathf.Clamp(Mathf.RoundToInt(color.r * 255f), 0, 255); - byte g = (byte)Mathf.Clamp(Mathf.RoundToInt(color.g * 255f), 0, 255); - byte b = (byte)Mathf.Clamp(Mathf.RoundToInt(color.b * 255f), 0, 255); - - return $"{r:X2}{g:X2}{b:X2}"; - } - - /// - /// Assumes the string is a 6-digit RGB Hex color code (with optional leading #) which it will parse into a UnityEngine.Color. - /// Eg, FF0000 -> RGBA(1,0,0,1) - /// - public static Color ToColor(this string _string) - { - _string = _string.Replace("#", ""); - - if (_string.Length != 6) - return Color.magenta; - - var r = byte.Parse(_string.Substring(0, 2), NumberStyles.HexNumber); - var g = byte.Parse(_string.Substring(2, 2), NumberStyles.HexNumber); - var b = byte.Parse(_string.Substring(4, 2), NumberStyles.HexNumber); - - var color = new Color - { - r = (float)(r / (decimal)255), - g = (float)(g / (decimal)255), - b = (float)(b / (decimal)255), - a = 1 - }; - - return color; - } - - private static PropertyInfo onEndEdit; - - public static UnityEvent GetOnEndEdit(this InputField _this) - { - if (onEndEdit == null) - onEndEdit = typeof(InputField).GetProperty("onEndEdit") - ?? throw new Exception("Could not get InputField.onEndEdit property!"); - - return onEndEdit.GetValue(_this, null).TryCast>(); - } - } -} diff --git a/src/ExplorerCore.cs b/src/ExplorerCore.cs index 40b051c..17f0a90 100644 --- a/src/ExplorerCore.cs +++ b/src/ExplorerCore.cs @@ -1,19 +1,15 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using UnityEngine; using UnityEngine.UI; -using UnityExplorer.Core; using UnityExplorer.Core.Config; -using UnityExplorer.Core.Input; -using UnityExplorer.Core.Runtime; -using UnityExplorer.Tests; using UnityExplorer.UI; -using UnityExplorer.Inspectors; using UnityExplorer.ObjectExplorer; using UnityExplorer.UI.Panels; +using UnityExplorer.Core.Runtime; +using UniverseLib.Input; namespace UnityExplorer { @@ -25,7 +21,6 @@ namespace UnityExplorer public const string GUID = "com.sinai.unityexplorer"; public static IExplorerLoader Loader { get; private set; } - public static RuntimeContext Context { get; internal set; } public static HarmonyLib.Harmony Harmony { get; } = new HarmonyLib.Harmony(GUID); @@ -43,38 +38,31 @@ namespace UnityExplorer Log($"{NAME} {VERSION} initializing..."); - ExplorerBehaviour.Setup(); - if (!Directory.Exists(Loader.ExplorerFolder)) Directory.CreateDirectory(Loader.ExplorerFolder); ConfigManager.Init(Loader.ConfigHandler); - ReflectionUtility.Init(); - RuntimeProvider.Init(); + RuntimeHelper.Init(); + ExplorerBehaviour.Setup(); + + UniverseLib.Universe.Init(ConfigManager.Startup_Delay_Time.Value, LateInit, Log, new UniverseLib.Config.UUConfig + { + Disable_EventSystem_Override = ConfigManager.Disable_EventSystem_Override.Value, + Force_Unlock_Mouse = ConfigManager.Force_Unlock_Mouse.Value, + Unhollowed_Modules_Folder = loader.UnhollowedModulesFolder + }); - SceneHandler.Init(); - InputManager.Init(); - - RuntimeProvider.Instance.StartCoroutine(SetupCoroutine()); - - Log($"Finished core setup, waiting for UI setup..."); + Log($"Finished core setup, waiting for late setup..."); } // Do a delayed setup so that objects aren't destroyed instantly. // This can happen for a multitude of reasons. // Default delay is 1 second which is usually enough. - private static IEnumerator SetupCoroutine() + private static void LateInit() { - yield return null; - float prevRealTime = Time.realtimeSinceStartup; - float delay = ConfigManager.Startup_Delay_Time.Value; - while (delay > 0) - { - float diff = Math.Max(Time.deltaTime, Time.realtimeSinceStartup - prevRealTime); - delay -= diff; - prevRealTime = Time.realtimeSinceStartup; - yield return null; - } + Log($"Setting up late core features..."); + + SceneHandler.Init(); Log($"Creating UI..."); @@ -88,19 +76,11 @@ namespace UnityExplorer /// public static void Update() { - RuntimeProvider.Instance.Update(); - UIManager.Update(); - } - public static void FixedUpdate() - { - RuntimeProvider.Instance.ProcessFixedUpdate(); - } - - public static void OnPostRender() - { - RuntimeProvider.Instance.ProcessOnPostRender(); + // check master toggle + if (InputManager.GetKeyDown(ConfigManager.Master_Toggle.Value)) + UIManager.ShowMenu = !UIManager.ShowMenu; } #region LOGGING diff --git a/src/Hooks/AddHookCell.cs b/src/Hooks/AddHookCell.cs index 09e3325..38635d0 100644 --- a/src/Hooks/AddHookCell.cs +++ b/src/Hooks/AddHookCell.cs @@ -6,6 +6,8 @@ using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI; using UnityExplorer.UI.Widgets; +using UniverseLib.UI; +using UniverseLib.UI.Widgets; namespace UnityExplorer.Hooks { diff --git a/src/Hooks/HookCell.cs b/src/Hooks/HookCell.cs index 4fab2b0..06881d7 100644 --- a/src/Hooks/HookCell.cs +++ b/src/Hooks/HookCell.cs @@ -4,8 +4,8 @@ using System.Linq; using System.Text; using UnityEngine; using UnityEngine.UI; -using UnityExplorer.UI; -using UnityExplorer.UI.Widgets; +using UniverseLib.UI; +using UniverseLib.UI.Widgets; namespace UnityExplorer.Hooks { diff --git a/src/Hooks/HookInstance.cs b/src/Hooks/HookInstance.cs index 970929f..6d6aeb9 100644 --- a/src/Hooks/HookInstance.cs +++ b/src/Hooks/HookInstance.cs @@ -7,6 +7,7 @@ using System.Text; using HarmonyLib; using Mono.CSharp; using UnityExplorer.CSConsole; +using UniverseLib; namespace UnityExplorer.Hooks { diff --git a/src/Hooks/HookManager.cs b/src/Hooks/HookManager.cs index 9eb058d..4c7886b 100644 --- a/src/Hooks/HookManager.cs +++ b/src/Hooks/HookManager.cs @@ -6,10 +6,13 @@ using System.Reflection; using System.Text; using HarmonyLib; using UnityEngine; +using UnityExplorer.Core.Runtime; using UnityExplorer.CSConsole; using UnityExplorer.UI; using UnityExplorer.UI.Panels; using UnityExplorer.UI.Widgets; +using UniverseLib; +using UniverseLib.UI.Widgets; namespace UnityExplorer.Hooks { @@ -106,7 +109,7 @@ namespace UnityExplorer.Hooks currentAddEligableMethods.Clear(); foreach (var method in type.GetMethods(ReflectionUtility.FLAGS)) { - if (method.IsGenericMethod /* || method.IsAbstract */ || ReflectionUtility.IsBlacklisted(method)) + if (method.IsGenericMethod /* || method.IsAbstract */ || RuntimeHelper.IsBlacklisted(method)) continue; currentAddEligableMethods.Add(method); filteredEligableMethods.Add(method); diff --git a/src/Inspectors/GameObjectInspector.cs b/src/Inspectors/GameObjectInspector.cs index e12677f..7851ad3 100644 --- a/src/Inspectors/GameObjectInspector.cs +++ b/src/Inspectors/GameObjectInspector.cs @@ -5,12 +5,15 @@ using System.Linq; using System.Text; using UnityEngine; using UnityEngine.UI; -using UnityExplorer.Core.Input; +using UniverseLib.Input; using UnityExplorer.UI; -using UnityExplorer.UI.Models; +using UniverseLib.UI.Models; using UnityExplorer.UI.Panels; using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets.AutoComplete; +using UniverseLib.UI.Widgets; +using UniverseLib.UI; +using UniverseLib; namespace UnityExplorer.Inspectors { diff --git a/src/Inspectors/GameObjectWidgets/ComponentCell.cs b/src/Inspectors/GameObjectWidgets/ComponentCell.cs index d0f774a..4637dbf 100644 --- a/src/Inspectors/GameObjectWidgets/ComponentCell.cs +++ b/src/Inspectors/GameObjectWidgets/ComponentCell.cs @@ -6,6 +6,8 @@ using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI; using UnityExplorer.UI.Widgets; +using UniverseLib.UI; +using UniverseLib.UI.Widgets; namespace UnityExplorer.Inspectors { diff --git a/src/Inspectors/GameObjectWidgets/ComponentList.cs b/src/Inspectors/GameObjectWidgets/ComponentList.cs index 473bd9c..b881122 100644 --- a/src/Inspectors/GameObjectWidgets/ComponentList.cs +++ b/src/Inspectors/GameObjectWidgets/ComponentList.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Text; using UnityEngine; using UnityExplorer.UI.Widgets; +using UniverseLib; +using UniverseLib.UI.Widgets; namespace UnityExplorer.Inspectors { diff --git a/src/Inspectors/GameObjectWidgets/GameObjectControls.cs b/src/Inspectors/GameObjectWidgets/GameObjectControls.cs index 2b0c184..797db0d 100644 --- a/src/Inspectors/GameObjectWidgets/GameObjectControls.cs +++ b/src/Inspectors/GameObjectWidgets/GameObjectControls.cs @@ -4,8 +4,10 @@ using System.Linq; using System.Text; using UnityEngine; using UnityEngine.UI; -using UnityExplorer.Core.Input; +using UniverseLib.Input; using UnityExplorer.UI; +using UniverseLib.UI; +using UniverseLib; namespace UnityExplorer.Inspectors { diff --git a/src/Inspectors/InspectUnderMouse.cs b/src/Inspectors/InspectUnderMouse.cs index 921d3a8..00a3d97 100644 --- a/src/Inspectors/InspectUnderMouse.cs +++ b/src/Inspectors/InspectUnderMouse.cs @@ -6,11 +6,13 @@ using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using UnityExplorer.Core; -using UnityExplorer.Core.Input; +using UniverseLib.Input; using UnityExplorer.Core.Runtime; using UnityExplorer.Inspectors.MouseInspectors; using UnityExplorer.UI; using UnityExplorer.UI.Panels; +using UniverseLib; +using UniverseLib.UI; namespace UnityExplorer.Inspectors { @@ -163,7 +165,7 @@ namespace UnityExplorer.Inspectors mousePos.y -= 10; // calculate and set our UI position - var inversePos = UIManager.CanvasRoot.transform.InverseTransformPoint(mousePos); + var inversePos = UIManager.UIRoot.transform.InverseTransformPoint(mousePos); UIRoot.transform.localPosition = new Vector3(inversePos.x, inversePos.y, 0); } @@ -181,7 +183,7 @@ namespace UnityExplorer.Inspectors { // hide title bar this.titleBar.SetActive(false); - this.UIRoot.transform.SetParent(UIManager.CanvasRoot.transform, false); + this.UIRoot.transform.SetParent(UIManager.UIRoot.transform, false); var inspectContent = UIFactory.CreateVerticalGroup(this.content, "InspectContent", true, true, true, true, 3, new Vector4(2, 2, 2, 2)); UIFactory.SetLayoutElement(inspectContent, flexibleWidth: 9999, flexibleHeight: 9999); diff --git a/src/Inspectors/InspectorBase.cs b/src/Inspectors/InspectorBase.cs index 5d59ff5..f4836cf 100644 --- a/src/Inspectors/InspectorBase.cs +++ b/src/Inspectors/InspectorBase.cs @@ -5,8 +5,9 @@ using System.Text; using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI; -using UnityExplorer.UI.Models; +using UniverseLib.UI.Models; using UnityExplorer.UI.Panels; +using UniverseLib.UI; namespace UnityExplorer.Inspectors { diff --git a/src/Inspectors/InspectorManager.cs b/src/Inspectors/InspectorManager.cs index 4198d29..04337f0 100644 --- a/src/Inspectors/InspectorManager.cs +++ b/src/Inspectors/InspectorManager.cs @@ -7,8 +7,10 @@ using UnityEngine.UI; using UnityExplorer.UI; using UnityExplorer.CacheObject; using UnityExplorer.Inspectors; -using UnityExplorer.UI.Models; +using UniverseLib.UI.Models; using UnityExplorer.UI.Panels; +using UniverseLib; +using UniverseLib.UI; namespace UnityExplorer { diff --git a/src/Inspectors/InspectorTab.cs b/src/Inspectors/InspectorTab.cs index e900a04..a7ea0fb 100644 --- a/src/Inspectors/InspectorTab.cs +++ b/src/Inspectors/InspectorTab.cs @@ -5,8 +5,10 @@ using System.Text; using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI; -using UnityExplorer.UI.Models; +using UniverseLib.UI.Models; using UnityExplorer.UI.Widgets; +using UniverseLib.UI; +using UniverseLib; namespace UnityExplorer.Inspectors { diff --git a/src/Inspectors/MouseInspectors/UiInspector.cs b/src/Inspectors/MouseInspectors/UiInspector.cs index 7b865f8..dc78d94 100644 --- a/src/Inspectors/MouseInspectors/UiInspector.cs +++ b/src/Inspectors/MouseInspectors/UiInspector.cs @@ -7,6 +7,7 @@ using UnityEngine.EventSystems; using UnityEngine.UI; using UnityExplorer.UI; using UnityExplorer.UI.Panels; +using UniverseLib; namespace UnityExplorer.Inspectors.MouseInspectors { diff --git a/src/Inspectors/MouseInspectors/WorldInspector.cs b/src/Inspectors/MouseInspectors/WorldInspector.cs index ad41684..e955023 100644 --- a/src/Inspectors/MouseInspectors/WorldInspector.cs +++ b/src/Inspectors/MouseInspectors/WorldInspector.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; +using UniverseLib; namespace UnityExplorer.Inspectors.MouseInspectors { diff --git a/src/Inspectors/ReflectionInspector.cs b/src/Inspectors/ReflectionInspector.cs index aeec236..9ca73c8 100644 --- a/src/Inspectors/ReflectionInspector.cs +++ b/src/Inspectors/ReflectionInspector.cs @@ -15,6 +15,10 @@ using UnityExplorer.CacheObject.Views; using UnityExplorer.UI.Panels; using UnityExplorer.UI.Widgets; using UnityExplorer.UI; +using UniverseLib.UI.Widgets; +using UniverseLib.UI; +using UniverseLib; +using UniverseLib.Runtime; namespace UnityExplorer.Inspectors { diff --git a/src/Loader/BepInEx/ExplorerBepInPlugin.cs b/src/Loader/BepInEx/ExplorerBepInPlugin.cs index 18dd77e..88e0468 100644 --- a/src/Loader/BepInEx/ExplorerBepInPlugin.cs +++ b/src/Loader/BepInEx/ExplorerBepInPlugin.cs @@ -11,7 +11,7 @@ using UnityEngine; using UnityEngine.EventSystems; using UnityExplorer.Core; using UnityExplorer.Core.Config; -using UnityExplorer.Core.Input; +using UniverseLib.Input; using UnityExplorer.Loader.BIE; #if CPP using BepInEx.IL2CPP; diff --git a/src/Loader/Standalone/ExplorerStandalone.cs b/src/Loader/Standalone/ExplorerStandalone.cs index cf5d787..cbefb96 100644 --- a/src/Loader/Standalone/ExplorerStandalone.cs +++ b/src/Loader/Standalone/ExplorerStandalone.cs @@ -7,7 +7,7 @@ using UnityEngine; using UnityExplorer.Core.Config; using UnityExplorer.Loader.STANDALONE; using UnityEngine.EventSystems; -using UnityExplorer.Core.Input; +using UniverseLib.Input; using UnityExplorer.Core; #if CPP using UnhollowerRuntimeLib; diff --git a/src/ObjectExplorer/ObjectSearch.cs b/src/ObjectExplorer/ObjectSearch.cs index ddbb0df..cc5255c 100644 --- a/src/ObjectExplorer/ObjectSearch.cs +++ b/src/ObjectExplorer/ObjectSearch.cs @@ -5,10 +5,13 @@ using System.Text; using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI; -using UnityExplorer.UI.Models; +using UniverseLib.UI.Models; using UnityExplorer.UI.Panels; using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets.AutoComplete; +using UniverseLib.UI.Widgets; +using UniverseLib.UI; +using UniverseLib; namespace UnityExplorer.ObjectExplorer { diff --git a/src/ObjectExplorer/SceneExplorer.cs b/src/ObjectExplorer/SceneExplorer.cs index cd53bb4..7502af9 100644 --- a/src/ObjectExplorer/SceneExplorer.cs +++ b/src/ObjectExplorer/SceneExplorer.cs @@ -9,9 +9,11 @@ using UnityEngine.SceneManagement; using UnityEngine.UI; using UnityExplorer.Core; using UnityExplorer.UI; -using UnityExplorer.UI.Models; +using UniverseLib.UI.Models; using UnityExplorer.UI.Panels; using UnityExplorer.UI.Widgets; +using UniverseLib.UI; +using UniverseLib; namespace UnityExplorer.ObjectExplorer { diff --git a/src/ObjectExplorer/SceneHandler.cs b/src/ObjectExplorer/SceneHandler.cs index bdaffa7..9261145 100644 --- a/src/ObjectExplorer/SceneHandler.cs +++ b/src/ObjectExplorer/SceneHandler.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using UnityEngine; using UnityEngine.SceneManagement; +using UniverseLib; namespace UnityExplorer.ObjectExplorer { diff --git a/src/ObjectExplorer/SearchProvider.cs b/src/ObjectExplorer/SearchProvider.cs index 2b7936f..1e2e185 100644 --- a/src/ObjectExplorer/SearchProvider.cs +++ b/src/ObjectExplorer/SearchProvider.cs @@ -7,6 +7,7 @@ using UnityEngine; using UnityEngine.SceneManagement; using UnityExplorer.Core; using UnityExplorer.Core.Runtime; +using UniverseLib; namespace UnityExplorer.ObjectExplorer { @@ -101,7 +102,7 @@ namespace UnityExplorer.ObjectExplorer if (go) { // hide unityexplorer objects - if (go.transform.root.name == "ExplorerCanvas") + if (go.transform.root.name == "UniverseLibCanvas") continue; if (shouldFilterGOs) diff --git a/src/Resources/legacy.5.6.bundle b/src/Resources/legacy.5.6.bundle deleted file mode 100644 index c68a7be..0000000 Binary files a/src/Resources/legacy.5.6.bundle and /dev/null differ diff --git a/src/Resources/legacy.bundle b/src/Resources/legacy.bundle deleted file mode 100644 index d918836..0000000 Binary files a/src/Resources/legacy.bundle and /dev/null differ diff --git a/src/Resources/modern.bundle b/src/Resources/modern.bundle deleted file mode 100644 index b40c19f..0000000 Binary files a/src/Resources/modern.bundle and /dev/null differ diff --git a/src/UI/Models/ButtonRef.cs b/src/UI/Models/ButtonRef.cs deleted file mode 100644 index 40b409c..0000000 --- a/src/UI/Models/ButtonRef.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine.UI; -using UnityExplorer.UI.Models; - -namespace UnityExplorer.UI -{ - // A simple helper class to handle a button's OnClick more effectively. - - public class ButtonRef - { - public Action OnClick; - - public Button Component { get; } - public Text ButtonText { get; } - - public ButtonRef(Button button) - { - this.Component = button; - this.ButtonText = button.GetComponentInChildren(); - - button.onClick.AddListener(() => { OnClick?.Invoke(); }); - } - } -} diff --git a/src/UI/Models/InputFieldRef.cs b/src/UI/Models/InputFieldRef.cs deleted file mode 100644 index 2564f22..0000000 --- a/src/UI/Models/InputFieldRef.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine; -using UnityEngine.UI; -using UnityExplorer.UI.Models; - -namespace UnityExplorer.UI -{ - public class InputFieldRef : UIModel - { - public static readonly HashSet inputsPendingUpdate = new HashSet(); - - public static void UpdateInstances() - { - if (inputsPendingUpdate.Any()) - { - var array = inputsPendingUpdate.ToArray(); - - for (int i = array.Length - 1; i >= 0; i--) - { - var entry = array[i]; - LayoutRebuilder.MarkLayoutForRebuild(entry.Rect); - entry.OnValueChanged?.Invoke(entry.Component.text); - } - - inputsPendingUpdate.Clear(); - } - } - - public InputFieldRef(InputField component) - { - this.Component = component; - Rect = component.GetComponent(); - PlaceholderText = component.placeholder.TryCast(); - component.onValueChanged.AddListener(OnInputChanged); - } - - public event Action OnValueChanged; - - public InputField Component; - public Text PlaceholderText; - public RectTransform Rect; - - public string Text - { - get => Component.text; - set => Component.text = value; - } - - public TextGenerator TextGenerator => Component.cachedInputTextGenerator; - - public bool ReachedMaxVerts => TextGenerator.vertexCount >= UIManager.MAX_TEXT_VERTS; - - private void OnInputChanged(string value) - { - if (!inputsPendingUpdate.Contains(this)) - inputsPendingUpdate.Add(this); - } - - public override GameObject UIRoot => Component.gameObject; - - public override void ConstructUI(GameObject parent) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/UI/Models/UIBehaviourModel.cs b/src/UI/Models/UIBehaviourModel.cs deleted file mode 100644 index cfa6398..0000000 --- a/src/UI/Models/UIBehaviourModel.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine; - -namespace UnityExplorer.UI.Models -{ - public abstract class UIBehaviourModel : UIModel - { - private static readonly List Instances = new List(); - - public static void UpdateInstances() - { - if (!Instances.Any()) - return; - - try - { - for (int i = Instances.Count - 1; i >= 0; i--) - { - var instance = Instances[i]; - if (instance == null || !instance.UIRoot) - { - Instances.RemoveAt(i); - continue; - } - if (instance.Enabled) - instance.Update(); - } - } - catch (Exception ex) - { - ExplorerCore.Log(ex); - } - } - - public UIBehaviourModel() - { - Instances.Add(this); - } - - /// - /// Default empty method, override and implement if NeedsUpdateTick is true. - /// - public virtual void Update() - { - } - - public override void Destroy() - { - if (Instances.Contains(this)) - Instances.Remove(this); - - base.Destroy(); - } - } -} diff --git a/src/UI/Models/UIModel.cs b/src/UI/Models/UIModel.cs deleted file mode 100644 index bb1c885..0000000 --- a/src/UI/Models/UIModel.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine; - -namespace UnityExplorer.UI.Models -{ - public abstract class UIModel - { - public abstract GameObject UIRoot { get; } - - public bool Enabled - { - get => UIRoot && UIRoot.activeInHierarchy; - set - { - if (!UIRoot || Enabled == value) - return; - UIRoot.SetActive(value); - OnToggleEnabled?.Invoke(value); - } - } - - public event Action OnToggleEnabled; - - public abstract void ConstructUI(GameObject parent); - - public virtual void Toggle() => SetActive(!Enabled); - - public virtual void SetActive(bool active) - { - UIRoot?.SetActive(active); - } - - public virtual void Destroy() - { - if (UIRoot) - GameObject.Destroy(UIRoot); - } - } -} diff --git a/src/UI/Panels/CSConsolePanel.cs b/src/UI/Panels/CSConsolePanel.cs index 51d98d1..c45e109 100644 --- a/src/UI/Panels/CSConsolePanel.cs +++ b/src/UI/Panels/CSConsolePanel.cs @@ -9,6 +9,9 @@ using UnityEngine.UI; using UnityExplorer.Core.Config; using UnityExplorer.CSConsole; using UnityExplorer.UI.Widgets; +using UniverseLib; +using UniverseLib.UI; +using UniverseLib.UI.Widgets; namespace UnityExplorer.UI.Panels { @@ -39,8 +42,8 @@ namespace UnityExplorer.UI.Panels private void InvokeOnValueChanged(string value) { - if (value.Length == UIManager.MAX_INPUTFIELD_CHARS) - ExplorerCore.LogWarning($"Reached maximum InputField character length! ({UIManager.MAX_INPUTFIELD_CHARS})"); + if (value.Length == UniversalUI.MAX_INPUTFIELD_CHARS) + ExplorerCore.LogWarning($"Reached maximum InputField character length! ({UniversalUI.MAX_INPUTFIELD_CHARS})"); OnInputChanged?.Invoke(value); } @@ -148,7 +151,7 @@ namespace UnityExplorer.UI.Panels UIFactory.SetLayoutGroup(linesHolder, true, true, true, true); LineNumberText = UIFactory.CreateLabel(linesHolder, "LineNumbers", "1", TextAnchor.UpperCenter, Color.grey, fontSize: 16); - LineNumberText.font = UIManager.ConsoleFont; + LineNumberText.font = UniversalUI.ConsoleFont; // input field @@ -192,9 +195,9 @@ namespace UnityExplorer.UI.Panels HighlightText.fontSize = fontSize; // Set fonts - InputText.font = UIManager.ConsoleFont; - Input.PlaceholderText.font = UIManager.ConsoleFont; - HighlightText.font = UIManager.ConsoleFont; + InputText.font = UniversalUI.ConsoleFont; + Input.PlaceholderText.font = UniversalUI.ConsoleFont; + HighlightText.font = UniversalUI.ConsoleFont; RuntimeProvider.Instance.StartCoroutine(DelayedLayoutSetup()); } diff --git a/src/UI/Panels/HookManagerPanel.cs b/src/UI/Panels/HookManagerPanel.cs index d2160d5..6ee8f10 100644 --- a/src/UI/Panels/HookManagerPanel.cs +++ b/src/UI/Panels/HookManagerPanel.cs @@ -8,6 +8,8 @@ using UnityExplorer.Core.Config; using UnityExplorer.Hooks; using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets.AutoComplete; +using UniverseLib.UI; +using UniverseLib.UI.Widgets; namespace UnityExplorer.UI.Panels { @@ -185,9 +187,9 @@ namespace UnityExplorer.UI.Panels EditorHighlightText.fontSize = fontSize; // Set fonts - EditorInputText.font = UIManager.ConsoleFont; - EditorInput.PlaceholderText.font = UIManager.ConsoleFont; - EditorHighlightText.font = UIManager.ConsoleFont; + EditorInputText.font = UniversalUI.ConsoleFont; + EditorInput.PlaceholderText.font = UniversalUI.ConsoleFont; + EditorHighlightText.font = UniversalUI.ConsoleFont; editorPanel.SetActive(false); } diff --git a/src/UI/Panels/InspectorPanel.cs b/src/UI/Panels/InspectorPanel.cs index 4cfca70..e095b25 100644 --- a/src/UI/Panels/InspectorPanel.cs +++ b/src/UI/Panels/InspectorPanel.cs @@ -7,6 +7,7 @@ using UnityEngine; using UnityEngine.UI; using UnityExplorer.Core.Config; using UnityExplorer.Inspectors; +using UniverseLib.UI; namespace UnityExplorer.UI.Panels { diff --git a/src/UI/Panels/LogPanel.cs b/src/UI/Panels/LogPanel.cs index ee7b88c..04970f6 100644 --- a/src/UI/Panels/LogPanel.cs +++ b/src/UI/Panels/LogPanel.cs @@ -8,6 +8,9 @@ using UnityEngine; using UnityEngine.UI; using UnityExplorer.Core.Config; using UnityExplorer.UI.Widgets; +using UniverseLib; +using UniverseLib.UI; +using UniverseLib.UI.Widgets; namespace UnityExplorer.UI.Panels { @@ -233,8 +236,8 @@ namespace UnityExplorer.UI.Panels Input.Component.readOnly = true; Input.Component.textComponent.supportRichText = true; Input.Component.lineType = InputField.LineType.MultiLineNewline; - Input.Component.textComponent.font = UIManager.ConsoleFont; - Input.PlaceholderText.font = UIManager.ConsoleFont; + Input.Component.textComponent.font = UniversalUI.ConsoleFont; + Input.PlaceholderText.font = UniversalUI.ConsoleFont; return UIRoot; } diff --git a/src/UI/Panels/ObjectExplorerPanel.cs b/src/UI/Panels/ObjectExplorerPanel.cs index be3142a..f2eb508 100644 --- a/src/UI/Panels/ObjectExplorerPanel.cs +++ b/src/UI/Panels/ObjectExplorerPanel.cs @@ -10,9 +10,11 @@ using UnityEngine.SceneManagement; using UnityEngine.UI; using UnityExplorer.Core; using UnityExplorer.Core.Config; -using UnityExplorer.UI.Models; +using UniverseLib.UI.Models; using UnityExplorer.ObjectExplorer; using UnityExplorer.UI.Widgets; +using UniverseLib.UI; +using UniverseLib; namespace UnityExplorer.UI.Panels { @@ -42,7 +44,7 @@ namespace UnityExplorer.UI.Panels content.SetActive(true); var button = tabButtons[tabIndex]; - RuntimeProvider.Instance.SetColorBlock(button.Component, UIManager.enabledButtonColor, UIManager.enabledButtonColor * 1.2f); + RuntimeProvider.Instance.SetColorBlock(button.Component, UniversalUI.enabledButtonColor, UniversalUI.enabledButtonColor * 1.2f); SelectedTab = tabIndex; SaveToConfigManager(); @@ -51,7 +53,7 @@ namespace UnityExplorer.UI.Panels private void DisableTab(int tabIndex) { tabPages[tabIndex].SetActive(false); - RuntimeProvider.Instance.SetColorBlock(tabButtons[tabIndex].Component, UIManager.disabledButtonColor, UIManager.disabledButtonColor * 1.2f); + RuntimeProvider.Instance.SetColorBlock(tabButtons[tabIndex].Component, UniversalUI.disabledButtonColor, UniversalUI.disabledButtonColor * 1.2f); } public override void Update() diff --git a/src/UI/Panels/OptionsPanel.cs b/src/UI/Panels/OptionsPanel.cs index d4e0253..c60fa3a 100644 --- a/src/UI/Panels/OptionsPanel.cs +++ b/src/UI/Panels/OptionsPanel.cs @@ -8,6 +8,8 @@ using UnityExplorer.Core.Config; using UnityExplorer.CacheObject; using UnityExplorer.CacheObject.Views; using UnityExplorer.UI.Widgets; +using UniverseLib.UI.Widgets; +using UniverseLib.UI; namespace UnityExplorer.UI.Panels { diff --git a/src/UI/Panels/PanelDragger.cs b/src/UI/Panels/PanelDragger.cs index 0692546..0f7f013 100644 --- a/src/UI/Panels/PanelDragger.cs +++ b/src/UI/Panels/PanelDragger.cs @@ -5,9 +5,11 @@ using System.IO; using System.Linq; using UnityEngine; using UnityEngine.UI; -using UnityExplorer.Core.Input; -using UnityExplorer.UI.Models; +using UniverseLib.Input; +using UniverseLib.UI.Models; using UnityExplorer.UI.Widgets.AutoComplete; +using UniverseLib.UI; +using UniverseLib; namespace UnityExplorer.UI.Panels { @@ -472,7 +474,7 @@ namespace UnityExplorer.UI.Panels { try { - var text = UIFactory.CreateLabel(UIManager.CanvasRoot, "ResizeCursor", "↔", TextAnchor.MiddleCenter, Color.white, true, 35); + var text = UIFactory.CreateLabel(UIManager.UIRoot, "ResizeCursor", "↔", TextAnchor.MiddleCenter, Color.white, true, 35); s_resizeCursorObj = text.gameObject; RectTransform rect = s_resizeCursorObj.GetComponent(); diff --git a/src/UI/Panels/UIPanel.cs b/src/UI/Panels/UIPanel.cs index e89e682..525830e 100644 --- a/src/UI/Panels/UIPanel.cs +++ b/src/UI/Panels/UIPanel.cs @@ -6,9 +6,11 @@ using System.Text; using UnityEngine; using UnityEngine.UI; using UnityExplorer.Core.Config; -using UnityExplorer.Core.Input; -using UnityExplorer.UI.Models; +using UniverseLib.Input; using UnityExplorer.UI.Widgets; +using UniverseLib.UI.Models; +using UniverseLib.UI; +using UniverseLib; namespace UnityExplorer.UI.Panels { @@ -35,6 +37,7 @@ namespace UnityExplorer.UI.Panels int count = UIManager.PanelHolder.transform.childCount; var mousePos = InputManager.MousePosition; bool clickedInAny = false; + for (int i = count - 1; i >= 0; i--) { // make sure this is a real recognized panel @@ -106,7 +109,7 @@ namespace UnityExplorer.UI.Panels public override void SetActive(bool active) { - if (this.Enabled.Equals(active)) + if (this.Enabled == active) return; base.SetActive(active); @@ -116,7 +119,7 @@ namespace UnityExplorer.UI.Panels if (NavButtonWanted) { - var color = active ? UIManager.enabledButtonColor : UIManager.disabledButtonColor; + var color = active ? UniversalUI.enabledButtonColor : UniversalUI.disabledButtonColor; RuntimeProvider.Instance.SetColorBlock(NavButton.Component, color, color * 1.2f); } @@ -232,7 +235,7 @@ namespace UnityExplorer.UI.Panels UIFactory.SetLayoutGroup(navBtn, false, true, true, true, 0, 0, 0, 5, 5, TextAnchor.MiddleCenter); UIFactory.SetLayoutElement(navBtn, minWidth: 80); - RuntimeProvider.Instance.SetColorBlock(NavButton.Component, UIManager.disabledButtonColor, UIManager.disabledButtonColor * 1.2f); + RuntimeProvider.Instance.SetColorBlock(NavButton.Component, UniversalUI.disabledButtonColor, UniversalUI.disabledButtonColor * 1.2f); NavButton.OnClick += () => { UIManager.TogglePanel(PanelType); }; var txtObj = navBtn.transform.Find("Text").gameObject; @@ -240,7 +243,7 @@ namespace UnityExplorer.UI.Panels } // create core canvas - uiRoot = UIFactory.CreatePanel(Name, out GameObject panelContent); + uiRoot = UIFactory.CreatePanel(Name, UIManager.PanelHolder, out GameObject panelContent); Rect = this.uiRoot.GetComponent(); UIFactory.SetLayoutGroup(this.uiRoot, false, false, true, true, 0, 2, 2, 2, 2, TextAnchor.UpperLeft); diff --git a/src/UI/Panels/UiInspectorResultsPanel.cs b/src/UI/Panels/UiInspectorResultsPanel.cs index b4c38cd..18edf25 100644 --- a/src/UI/Panels/UiInspectorResultsPanel.cs +++ b/src/UI/Panels/UiInspectorResultsPanel.cs @@ -5,6 +5,9 @@ using System.Text; using UnityEngine; using UnityExplorer.Inspectors.MouseInspectors; using UnityExplorer.UI.Widgets; +using UniverseLib; +using UniverseLib.UI; +using UniverseLib.UI.Widgets; namespace UnityExplorer.UI.Panels { diff --git a/src/UI/Pool.cs b/src/UI/Pool.cs deleted file mode 100644 index 74edc98..0000000 --- a/src/UI/Pool.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine; - -namespace UnityExplorer.UI -{ - public interface IPooledObject - { - GameObject UIRoot { get; set; } - float DefaultHeight { get; } - - GameObject CreateContent(GameObject parent); - } - - public abstract class Pool - { - protected static readonly Dictionary pools = new Dictionary(); - - public static Pool GetPool(Type type) - { - if (!pools.TryGetValue(type, out Pool pool)) - pool = CreatePool(type); - return pool; - } - - protected static Pool CreatePool(Type type) - { - Pool pool = (Pool)Activator.CreateInstance(typeof(Pool<>).MakeGenericType(new[] { type })); - pools.Add(type, pool); - return pool; - } - - public static IPooledObject Borrow(Type type) - { - return GetPool(type).TryBorrow(); - } - - public static void Return(Type type, IPooledObject obj) - { - GetPool(type).TryReturn(obj); - } - - protected abstract IPooledObject TryBorrow(); - protected abstract void TryReturn(IPooledObject obj); - } - - public class Pool : Pool where T : IPooledObject - { - public static Pool GetPool() => (Pool)GetPool(typeof(T)); - - public static T Borrow() - { - return GetPool().BorrowObject(); - } - - public static void Return(T obj) - { - GetPool().ReturnObject(obj); - } - - // Instance - - public static Pool Instance - { - get => s_instance ?? (Pool)CreatePool(typeof(T)); - } - private static Pool s_instance; - - public Pool() - { - s_instance = this; - - //ExplorerCore.LogWarning("Creating Pool<" + typeof(T).Name + ">"); - - InactiveHolder = new GameObject($"PoolHolder_{typeof(T).Name}"); - InactiveHolder.transform.parent = UIManager.PoolHolder.transform; - InactiveHolder.hideFlags |= HideFlags.HideAndDontSave; - InactiveHolder.SetActive(false); - - // Create an instance (not content) to grab the default height - var obj = (T)Activator.CreateInstance(typeof(T)); - DefaultHeight = obj.DefaultHeight; - } - - public GameObject InactiveHolder { get; } - public float DefaultHeight { get; } - - private readonly HashSet available = new HashSet(); - private readonly HashSet borrowed = new HashSet(); - - public int AvailableCount => available.Count; - - private void IncrementPool() - { - var obj = (T)Activator.CreateInstance(typeof(T)); - obj.CreateContent(InactiveHolder); - available.Add(obj); - } - - public T BorrowObject() - { - if (available.Count <= 0) - IncrementPool(); - - var obj = available.First(); - available.Remove(obj); - borrowed.Add(obj); - - return obj; - } - - public void ReturnObject(T obj) - { - if (!borrowed.Contains(obj)) - ExplorerCore.LogWarning($"Returning an item to object pool ({typeof(T).Name}) but the item didn't exist in the borrowed list?"); - else - borrowed.Remove(obj); - - available.Add(obj); - obj.UIRoot.transform.SetParent(InactiveHolder.transform, false); - } - - protected override IPooledObject TryBorrow() => Borrow(); - - protected override void TryReturn(IPooledObject obj) => Return((T)obj); - } -} diff --git a/src/UI/UIFactory.cs b/src/UI/UIFactory.cs deleted file mode 100644 index 8b889c1..0000000 --- a/src/UI/UIFactory.cs +++ /dev/null @@ -1,970 +0,0 @@ -using System; -using UnityEngine; -using UnityEngine.Events; -using UnityEngine.UI; -using UnityExplorer.Core.Config; -using UnityExplorer.Core.Runtime; -using UnityExplorer.UI.Models; -using UnityExplorer.UI.Widgets; - -namespace UnityExplorer.UI -{ - public static class UIFactory - { - #region Init, Core - - internal static Vector2 _largeElementSize = new Vector2(100, 30); - internal static Vector2 _smallElementSize = new Vector2(25, 25); - internal static Color _defaultTextColor = Color.white; - - public static GameObject CreateUIObject(string name, GameObject parent, Vector2 size = default) - { - if (!parent) - { - ExplorerCore.LogWarning($"Warning: Creating {name} but parent is null"); - ExplorerCore.Log(Environment.StackTrace); - } - - var obj = new GameObject(name) - { - layer = 5, - hideFlags = HideFlags.HideAndDontSave, - }; - - if (parent) - obj.transform.SetParent(parent.transform, false); - - RectTransform rect = obj.AddComponent(); - rect.sizeDelta = size; - return obj; - } - - internal static void SetDefaultTextValues(Text text) - { - text.color = _defaultTextColor; - text.font = UIManager.DefaultFont; - text.fontSize = 14; - } - - internal static void SetDefaultSelectableColors(Selectable selectable) - { - RuntimeProvider.Instance.SetColorBlock(selectable, new Color(0.2f, 0.2f, 0.2f), - new Color(0.3f, 0.3f, 0.3f), new Color(0.15f, 0.15f, 0.15f)); - } - - #endregion - - - #region Default Layout Components - - /// - /// Get and/or Add a LayoutElement component to the GameObject, and set any of the values on it. - /// - public static LayoutElement SetLayoutElement(GameObject gameObject, int? minWidth = null, int? minHeight = null, - int? flexibleWidth = null, int? flexibleHeight = null, int? preferredWidth = null, int? preferredHeight = null, - bool? ignoreLayout = null) - { - var layout = gameObject.GetComponent(); - if (!layout) - layout = gameObject.AddComponent(); - - if (minWidth != null) - layout.minWidth = (int)minWidth; - - if (minHeight != null) - layout.minHeight = (int)minHeight; - - if (flexibleWidth != null) - layout.flexibleWidth = (int)flexibleWidth; - - if (flexibleHeight != null) - layout.flexibleHeight = (int)flexibleHeight; - - if (preferredWidth != null) - layout.preferredWidth = (int)preferredWidth; - - if (preferredHeight != null) - layout.preferredHeight = (int)preferredHeight; - - if (ignoreLayout != null) - layout.ignoreLayout = (bool)ignoreLayout; - - return layout; - } - - /// - /// Get and/or Add a HorizontalOrVerticalLayoutGroup (must pick one) to the GameObject, and set the values on it. - /// - public static T SetLayoutGroup(GameObject gameObject, bool? forceWidth = null, bool? forceHeight = null, - bool? childControlWidth = null, bool? childControlHeight = null, int? spacing = null, int? padTop = null, - int? padBottom = null, int? padLeft = null, int? padRight = null, TextAnchor? childAlignment = null) - where T : HorizontalOrVerticalLayoutGroup - { - var group = gameObject.GetComponent(); - if (!group) - group = gameObject.AddComponent(); - - return SetLayoutGroup(group, forceWidth, forceHeight, childControlWidth, childControlHeight, spacing, padTop, - padBottom, padLeft, padRight, childAlignment); - } - - /// - /// Set the values on a HorizontalOrVerticalLayoutGroup. - /// - public static T SetLayoutGroup(T group, bool? forceWidth = null, bool? forceHeight = null, - bool? childControlWidth = null, bool? childControlHeight = null, int? spacing = null, int? padTop = null, - int? padBottom = null, int? padLeft = null, int? padRight = null, TextAnchor? childAlignment = null) - where T : HorizontalOrVerticalLayoutGroup - { - if (forceWidth != null) - group.childForceExpandWidth = (bool)forceWidth; - if (forceHeight != null) - group.childForceExpandHeight = (bool)forceHeight; - if (childControlWidth != null) - group.SetChildControlWidth((bool)childControlWidth); - if (childControlHeight != null) - group.SetChildControlHeight((bool)childControlHeight); - if (spacing != null) - group.spacing = (int)spacing; - if (padTop != null) - group.padding.top = (int)padTop; - if (padBottom != null) - group.padding.bottom = (int)padBottom; - if (padLeft != null) - group.padding.left = (int)padLeft; - if (padRight != null) - group.padding.right = (int)padRight; - if (childAlignment != null) - group.childAlignment = (TextAnchor)childAlignment; - - return group; - } - - /// - /// Create a Panel on the UI Canvas. - /// - public static GameObject CreatePanel(string name, out GameObject contentHolder, Color? bgColor = null) - { - var panelObj = CreateUIObject(name, UIManager.PanelHolder); - SetLayoutGroup(panelObj, true, true, true, true); - - var rect = panelObj.GetComponent(); - rect.anchorMin = Vector2.zero; - rect.anchorMax = Vector2.one; - rect.anchoredPosition = Vector2.zero; - rect.sizeDelta = Vector2.zero; - - var maskImg = panelObj.AddComponent(); - maskImg.color = Color.black; - panelObj.AddComponent().showMaskGraphic = true; - - contentHolder = CreateUIObject("Content", panelObj); - - Image bgImage = contentHolder.AddComponent(); - bgImage.type = Image.Type.Filled; - if (bgColor == null) - bgImage.color = new Color(0.07f, 0.07f, 0.07f); - else - bgImage.color = (Color)bgColor; - - SetLayoutGroup(contentHolder, true, true, true, true, 3, 3, 3, 3, 3); - - return panelObj; - } - - /// - /// Create a VerticalLayoutGroup object. - /// - public static GameObject CreateVerticalGroup(GameObject parent, string name, bool forceWidth, bool forceHeight, - bool childControlWidth, bool childControlHeight, int spacing = 0, Vector4 padding = default, Color bgColor = default, - TextAnchor? childAlignment = null) - { - GameObject groupObj = CreateUIObject(name, parent); - - SetLayoutGroup(groupObj, forceWidth, forceHeight, childControlWidth, childControlHeight, - spacing, (int)padding.x, (int)padding.y, (int)padding.z, (int)padding.w, childAlignment); - - Image image = groupObj.AddComponent(); - image.color = bgColor == default - ? new Color(0.17f, 0.17f, 0.17f) - : bgColor; - - return groupObj; - } - - /// - /// Create a HorizontalLayoutGroup object. - /// - public static GameObject CreateHorizontalGroup(GameObject parent, string name, bool forceExpandWidth, bool forceExpandHeight, - bool childControlWidth, bool childControlHeight, int spacing = 0, Vector4 padding = default, Color bgColor = default, - TextAnchor? childAlignment = null) - { - GameObject groupObj = CreateUIObject(name, parent); - - SetLayoutGroup(groupObj, forceExpandWidth, forceExpandHeight, childControlWidth, childControlHeight, - spacing, (int)padding.x, (int)padding.y, (int)padding.z, (int)padding.w, childAlignment); - - Image image = groupObj.AddComponent(); - image.color = bgColor == default - ? new Color(0.17f, 0.17f, 0.17f) - : bgColor; - - return groupObj; - } - - /// - /// Create a GridLayoutGroup object. - /// - public static GameObject CreateGridGroup(GameObject parent, string name, Vector2 cellSize, Vector2 spacing, Color bgColor = default) - { - var groupObj = CreateUIObject(name, parent); - - GridLayoutGroup gridGroup = groupObj.AddComponent(); - gridGroup.childAlignment = TextAnchor.UpperLeft; - gridGroup.cellSize = cellSize; - gridGroup.spacing = spacing; - - Image image = groupObj.AddComponent(); - - image.color = bgColor == default - ? new Color(0.17f, 0.17f, 0.17f) - : bgColor; - - return groupObj; - } - - #endregion - - - #region Default Control Elements - - /// - /// Create a Label object. - /// - public static Text CreateLabel(GameObject parent, string name, string text, TextAnchor alignment, - Color color = default, bool supportRichText = true, int fontSize = 14) - { - var obj = CreateUIObject(name, parent); - var textComp = obj.AddComponent(); - - SetDefaultTextValues(textComp); - - textComp.text = text; - textComp.color = color == default ? _defaultTextColor : color; - textComp.supportRichText = supportRichText; - textComp.alignment = alignment; - textComp.fontSize = fontSize; - - return textComp; - } - - public static ButtonRef CreateButton(GameObject parent, string name, string text, Color? normalColor = null) - { - var colors = new ColorBlock(); - normalColor = normalColor ?? new Color(0.25f, 0.25f, 0.25f); - - var btn = CreateButton(parent, name, text, colors); - - RuntimeProvider.Instance.SetColorBlock(btn.Component, normalColor, normalColor * 1.2f, normalColor * 0.7f); - - return btn; - } - - public static ButtonRef CreateButton(GameObject parent, string name, string text, ColorBlock colors) - { - GameObject buttonObj = CreateUIObject(name, parent, _smallElementSize); - - var textObj = CreateUIObject("Text", buttonObj); - - Image image = buttonObj.AddComponent(); - image.type = Image.Type.Sliced; - image.color = new Color(1, 1, 1, 1); - - var button = buttonObj.AddComponent