✔️ Supports most Unity versions from 5.2 to 2021+ (IL2CPP and Mono).
+
+ ✨ Powered by [UniverseLib](https://github.com/sinai-dev/UniverseLib)
+
# Releases [](../../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