Compare commits

..

25 Commits

Author SHA1 Message Date
a5a07a0a23 Add RuntimeProvider method for setting Selectable.colors 2021-04-10 18:25:13 +10:00
e0fd682c81 Add MethodInfo helper 2021-04-10 18:24:16 +10:00
7426bd1dd6 Bump version 2021-04-10 17:44:15 +10:00
b39b044f79 Add advanced attributes to BepInEx config 2021-04-10 17:44:09 +10:00
7a2b4aa257 Fix setting color block when partially stripped 2021-04-10 17:43:56 +10:00
3762d14bdb Fix InputSystem for IL2CPP 2021-04-10 17:43:32 +10:00
3628f3db31 Fix event system control 2021-04-09 01:46:26 +10:00
d39fea69c3 Better InputSystem Key enum resolving 2021-04-07 20:54:08 +10:00
95e8b3aa58 fix string unbox 2021-04-07 17:31:06 +10:00
b68145385c Fix issue with float struct check 2021-04-07 17:20:54 +10:00
2310f2f7ce Add "Default Tab" config setting instead of "last active tab" 2021-04-07 17:20:42 +10:00
2cc403ad17 Cleanup runtime-specific 2021-04-07 17:20:09 +10:00
c2d9b9b59e a few small fixes
* Fix InteractiveFlags toggles not being properly updated
* Fix cases where games that don't have Reflection.Emit would still have the C# Console available. Also added a "(disabled)" message to the tab button.
2021-04-06 01:01:46 +10:00
c748be7bcc Rewrite InteractiveUnityStruct, now called InteractiveFloatStruct
InteractiveFloatStruct supports any struct where all the fields are floats.
2021-04-05 20:32:47 +10:00
09dae6f1d3 Add proper support for InputSystem 2021-04-05 16:28:30 +10:00
6ca117b070 Fix strings boxed as Il2CppSystem.Objects 2021-04-04 13:44:58 +10:00
113f2fd922 3.3.5 - fix Il2Cpp Hashtable, boxed strings 2021-04-04 03:41:36 +10:00
7443f6500e Update README.md 2021-04-03 17:24:28 +11:00
92566c2729 Update README.md 2021-04-02 19:56:18 +11:00
713f87f455 Update THIRDPARTY_LICENSES.md 2021-04-02 17:45:50 +11:00
4241e7e207 Include third-party licenses 2021-04-02 17:44:49 +11:00
6d479a6703 3.3.4
* Fixed Harmony patches not working properly for games which use older BepInEx releases (ie. Risk of Rain 2)
* Fixed a couple minor issues with the config settings
2021-04-02 17:06:49 +11:00
6539f818c3 Update README.md 2021-04-01 22:04:58 +11:00
bc5ffcab40 Update README.md 2021-04-01 17:15:50 +11:00
d070ded036 3.3.3
* Fix `Hide on Startup` not working
* Fix for cases when we try to `scene.GetRootGameObjects()` but the scene has not yet fully loaded.
* MelonLoader releases will no longer spam "Preferences Saved!" constantly in the Console log
* Fix mistake with UI Event System setting/releasing
* Fix some UI elements not having correct Color transition values
2021-04-01 17:13:31 +11:00
62 changed files with 2325 additions and 940 deletions

View File

@ -6,7 +6,7 @@
An in-game explorer and a suite of debugging tools for <a href="https://docs.unity3d.com/Manual/IL2CPP.html">IL2CPP</a> and <b>Mono</b> Unity games, to aid with modding development.
</p>
## Releases [![](https://img.shields.io/github/release/sinai-dev/Explorer.svg?label=release%20notes)](../../releases/latest) [![](https://img.shields.io/github/downloads/sinai-dev/Explorer/total.svg)]() [![](https://img.shields.io/github/downloads/sinai-dev/Explorer/latest/total.svg)]()
## Releases [![](https://img.shields.io/github/release/sinai-dev/UnityExplorer.svg?label=release%20notes)](../../releases/latest) [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/total.svg)](../../releases) [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/latest/total.svg)](../../releases/latest)
| Mod Loader | IL2CPP | Mono |
| ----------- | ------ | ---- |
@ -19,24 +19,54 @@
### BepInEx
0. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game. For IL2CPP you should use [BepInEx 6 (Bleeding Edge)](https://builds.bepis.io/projects/bepinex_be), for Mono you should use [BepInEx 5](https://github.com/BepInEx/BepInEx/releases) (until Mono support stabilizes in BepInEx 6).
1. Download the UnityExplorer release for BepInEx IL2CPP or Mono above.
2. Take the `UnityExplorer.BIE.___.dll` file and put it in `[GameFolder]\BepInEx\plugins\`
3. In IL2CPP, you will need to download the [Unity libs](https://github.com/LavaGang/Unity-Runtime-Libraries) for the game's Unity version and put them in the `BepInEx\unity-libs\` folder.
1. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game. For IL2CPP you should use [BepInEx 6 (Bleeding Edge)](https://builds.bepis.io/projects/bepinex_be), for Mono you should use [BepInEx 5](https://github.com/BepInEx/BepInEx/releases) (until Mono support stabilizes in BepInEx 6).
2. Download the UnityExplorer release for BepInEx IL2CPP or Mono above.
3. Take the `UnityExplorer.BIE.___.dll` file and put it in `[GameFolder]\BepInEx\plugins\`
4. In IL2CPP, you will need to download the [Unity libs](https://github.com/LavaGang/Unity-Runtime-Libraries) for the game's Unity version and put them in the `BepInEx\unity-libs\` folder.
### MelonLoader
0. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3+ for your game. Version 0.3 is currently in pre-release, so you must "Enable ALPHA Releases" in your MelonLoader Installer settings to see the option for it.
1. Download the UnityExplorer release for MelonLoader IL2CPP or Mono above.
2. Take the `UnityExplorer.ML.___.dll` file and put it in the `[GameFolder]\Mods\` folder.
1. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3+ for your game. Version 0.3 is currently in pre-release, so you must "Enable ALPHA Releases" in your MelonLoader Installer settings to see the option for it.
2. Download the UnityExplorer release for MelonLoader IL2CPP or Mono above.
3. Take the `UnityExplorer.ML.___.dll` file and put it in the `[GameFolder]\Mods\` folder.
### Standalone
The standalone release is based on the BepInEx build, so it requires Harmony 2.0 (or HarmonyX) to function properly.
The standalone release requires you to also load `0Harmony.dll` (HarmonyX, from the `lib\` folder) to function properly, and the IL2CPP also requires `UnhollowerBaseLib.dll` as well. The Mono release should be fairly easy to use with any loader, but the IL2CPP one may be tricky, I'd recommend just using BepInEx or MelonLoader for IL2CPP.
0. Load the DLL from your mod or inject it. You must also make sure `0Harmony.dll` is loaded, and `UnhollowerBaseLib.dll` for IL2CPP as well.
1. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();`
2. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish.
1. Load the UnityExplorer DLL from your mod or inject it, as well as `0Harmony.dll` and `UnhollowerBaseLib.dll` as required.
2. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();`
3. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish.
## Issues and contributions
Both issue reports and PR contributions are welcome in this repository.
### Issue reporting
To report an issue with UnityExplorer, please use the following template (as well as any other information / images you can provide).
Template:
```
* Game issue occurs on:
* UnityExplorer version: (eg BIE.IL2CPP v3.3.3, etc)
* Description of issue:
* Unity log link:
* Mod Loader log link:
```
Please upload your Unity log file to [Pastebin](https://pastebin.com/) (or equivalent service) and provide a link to the paste.
* The log should be found at `%userprofile%\AppData\LocalLow\[Company]\[Game]\` unless redirected by your Mod Loader.
* The file will be called either `output_log.txt` or `Player.log`
As well as the Unity log, please upload your Mod Loader's log:
* BepInEx: `BepInEx\LogOutput.log`
* MelonLoader: `MelonLoader\Latest.log`
## Features
@ -94,31 +124,6 @@ Depending on the release you are using, the config file will be found at:
* MelonLoader: `UserData\MelonPreferences.cfg`
* Standalone `{DLL_location}\UnityExplorer\config.ini`
`Main Menu Toggle` (KeyCode)
* Default: `F7`
* See [this article](https://docs.unity3d.com/ScriptReference/KeyCode.html) for a full list of all accepted KeyCodes.
`Force Unlock Mouse` (bool)
* Default: `true`
* Forces the cursor to be unlocked and visible while the UnityExplorer menu is open, and prevents anything else taking control.
`Default Page Limit` (int)
* Default: `25`
* Sets the default items per page when viewing lists or search results.
* <b>Requires a restart to take effect</b>, apart from Reflection Inspector tabs.
`Default Output Path` (string)
* Default: `Mods\UnityExplorer`
* Where output is generated to, by default (for Texture PNG saving, etc).
`Log Unity Debug` (bool)
* Default: `false`
* Listens for Unity `Debug.Log` messages and prints them to UnityExplorer's log.
`Hide on Startup` (bool)
* Default: `false`
* If true, UnityExplorer will be hidden when you start the game, you must open it via the keybind.
## Building
Building the project should be straight-forward, the references are all inside the `lib\` folder.
@ -130,10 +135,10 @@ Building the project should be straight-forward, the references are all inside t
## Acknowledgments
* [ManlyMarco](https://github.com/ManlyMarco) for [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor), snippets from the REPL Console were used for UnityExplorer's C# Console.
* [denikson](https://github.com/denikson) (aka Horse) for [mcs-unity](https://github.com/denikson/mcs-unity), used as the `Mono.CSharp` reference for the C# Console.
* [HerpDerpenstine](https://github.com/HerpDerpinstine) for [MelonCoroutines](https://github.com/LavaGang/MelonLoader/blob/master/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs), they were included for standalone IL2CPP coroutine support.
* [InGameCodeEditor](https://assetstore.unity.com/packages/tools/gui/ingame-code-editor-144254) was used as the base for the syntax highlighting for UnityExplorer's C# console (`UnityExplorer.UI.Main.CSConsole.Lexer`).
* [ManlyMarco](https://github.com/ManlyMarco) for [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor) \[[license](THIRDPARTY_LICENSES.md#runtimeunityeditor-license)\], snippets from the REPL Console were used for UnityExplorer's C# Console.
* [denikson](https://github.com/denikson) (aka Horse) for [mcs-unity](https://github.com/denikson/mcs-unity) \[no license\], used as the `Mono.CSharp` reference for the C# Console.
* [HerpDerpenstine](https://github.com/HerpDerpinstine) for [MelonCoroutines](https://github.com/LavaGang/MelonLoader/blob/6cc958ec23b5e2e8453a73bc2e0d5aa353d4f0d1/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs) \[[license](THIRDPARTY_LICENSES.md#melonloader-license)\], they were included for standalone IL2CPP coroutine support.
* [InGameCodeEditor](https://assetstore.unity.com/packages/tools/gui/ingame-code-editor-144254) \[[license](THIRDPARTY_LICENSES.md#ingamecodeeditor-license)\] was used as the base for the syntax highlighting for UnityExplorer's C# console (`UnityExplorer.UI.Main.CSConsole.Lexer`).
### Disclaimer

1067
THIRDPARTY_LICENSES.md Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -7,6 +7,7 @@ using System.Linq;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.Main.CSConsole;
using UnityExplorer.UI.Main.Home;
using UnityExplorer.UI.Inspectors;
namespace UnityExplorer.Core.CSharp
{
@ -19,7 +20,7 @@ namespace UnityExplorer.Core.CSharp
public static void StartCoroutine(IEnumerator ienumerator)
{
RuntimeProvider.Instance.StartConsoleCoroutine(ienumerator);
RuntimeProvider.Instance.StartCoroutine(ienumerator);
}
public static void AddUsing(string directive)

View File

@ -46,7 +46,7 @@ namespace UnityExplorer.Core.Config
private void SetValue(T value)
{
if ((m_value == null && value == null) || m_value.Equals(value))
if ((m_value == null && value == null) || (m_value != null && m_value.Equals(value)))
return;
m_value = value;

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;
using UnityExplorer.UI.Main;
@ -16,18 +17,18 @@ namespace UnityExplorer.Core.Config
// See the UnityExplorer.Loader namespace for the implementations.
public static ConfigHandler Handler { get; private set; }
public static ConfigElement<KeyCode> Main_Menu_Toggle;
public static ConfigElement<bool> Force_Unlock_Mouse;
public static ConfigElement<int> Default_Page_Limit;
public static ConfigElement<string> Default_Output_Path;
public static ConfigElement<bool> Log_Unity_Debug;
public static ConfigElement<bool> Hide_On_Startup;
public static ConfigElement<KeyCode> Main_Menu_Toggle;
public static ConfigElement<bool> Force_Unlock_Mouse;
public static ConfigElement<MenuPages> Default_Tab;
public static ConfigElement<int> Default_Page_Limit;
public static ConfigElement<string> Default_Output_Path;
public static ConfigElement<bool> Log_Unity_Debug;
public static ConfigElement<bool> Hide_On_Startup;
public static ConfigElement<string> Last_Window_Anchors;
public static ConfigElement<string> Last_Window_Position;
public static ConfigElement<int> Last_Active_Tab;
public static ConfigElement<bool> Last_DebugConsole_State;
public static ConfigElement<bool> Last_SceneExplorer_State;
public static ConfigElement<string> Last_Window_Anchors;
public static ConfigElement<string> Last_Window_Position;
public static ConfigElement<bool> Last_DebugConsole_State;
public static ConfigElement<bool> Last_SceneExplorer_State;
internal static readonly Dictionary<string, IConfigElement> ConfigElements = new Dictionary<string, IConfigElement>();
@ -43,8 +44,9 @@ namespace UnityExplorer.Core.Config
SceneExplorer.OnToggleShow += SceneExplorer_OnToggleShow;
PanelDragger.OnFinishResize += PanelDragger_OnFinishResize;
PanelDragger.OnFinishDrag += PanelDragger_OnFinishDrag;
MainMenu.OnActiveTabChanged += MainMenu_OnActiveTabChanged;
DebugConsole.OnToggleShow += DebugConsole_OnToggleShow;
InitConsoleCallback();
}
internal static void RegisterConfigElement<T>(ConfigElement<T> configElement)
@ -63,6 +65,10 @@ namespace UnityExplorer.Core.Config
"Should UnityExplorer be hidden on startup?",
false);
Default_Tab = new ConfigElement<MenuPages>("Default Tab",
"The default menu page when starting the game.",
MenuPages.Home);
Log_Unity_Debug = new ConfigElement<bool>("Log Unity Debug",
"Should UnityEngine.Debug.Log messages be printed to UnityExplorer's log?",
false);
@ -91,11 +97,6 @@ namespace UnityExplorer.Core.Config
DEFAULT_WINDOW_POSITION,
true);
Last_Active_Tab = new ConfigElement<int>("Last_Active_Tab",
"For internal use, the last active tab index.",
0,
true);
Last_DebugConsole_State = new ConfigElement<bool>("Last_DebugConsole_State",
"For internal use, the collapsed state of the Debug Console.",
true,
@ -112,6 +113,7 @@ namespace UnityExplorer.Core.Config
private static void PanelDragger_OnFinishResize(RectTransform rect)
{
Last_Window_Anchors.Value = rect.RectAnchorsToString();
PanelDragger_OnFinishDrag(rect);
}
private static void PanelDragger_OnFinishDrag(RectTransform rect)
@ -119,11 +121,6 @@ namespace UnityExplorer.Core.Config
Last_Window_Position.Value = rect.RectPositionToString();
}
private static void MainMenu_OnActiveTabChanged(int page)
{
Last_Active_Tab.Value = page;
}
private static void DebugConsole_OnToggleShow(bool showing)
{
Last_DebugConsole_State.Value = showing;
@ -134,6 +131,33 @@ namespace UnityExplorer.Core.Config
Last_SceneExplorer_State.Value = showing;
}
#region CONSOLE ONEXIT CALLBACK
internal static void InitConsoleCallback()
{
handler = new ConsoleEventDelegate(ConsoleEventCallback);
SetConsoleCtrlHandler(handler, true);
}
static bool ConsoleEventCallback(int eventType)
{
// 2 is Console Quit
if (eventType == 2)
Handler.SaveConfig();
return false;
}
static ConsoleEventDelegate handler;
private delegate bool ConsoleEventDelegate(int eventType);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
#endregion
#region WINDOW ANCHORS / POSITION HELPERS
// Window Anchors helpers
private const string DEFAULT_WINDOW_ANCHORS = "0.25,0.10,0.78,0.95";
@ -210,5 +234,7 @@ namespace UnityExplorer.Core.Config
//ExplorerCore.LogWarning("Exception setting window position: " + ex);
}
}
#endregion
}
}

View File

@ -46,91 +46,33 @@ namespace UnityExplorer.Core.Input
UpdateCursorControl();
Unlock = true;
Unlock = ConfigManager.Force_Unlock_Mouse.Value;
ConfigManager.Force_Unlock_Mouse.OnValueChanged += (bool val) => { Unlock = val; };
}
private static void SetupPatches()
{
try
{
if (CursorType == null)
{
throw new Exception("Could not find Type 'UnityEngine.Cursor'!");
}
// Get current cursor state and enable cursor
try
{
m_lastLockMode = (CursorLockMode?)typeof(Cursor).GetProperty("lockState", BF.Public | BF.Static)?.GetValue(null, null)
?? CursorLockMode.None;
m_lastVisibleState = (bool?)typeof(Cursor).GetProperty("visible", BF.Public | BF.Static)?.GetValue(null, null)
?? false;
}
catch { }
// Setup Harmony Patches
TryPatch(typeof(Cursor),
"lockState",
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(Prefix_set_lockState))),
true);
TryPatch(typeof(Cursor),
"visible",
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(Prefix_set_visible))),
true);
TryPatch(typeof(EventSystem),
"current",
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(Prefix_EventSystem_set_current))),
true);
}
catch (Exception e)
{
ExplorerCore.Log($"Exception on ForceUnlockCursor.Init! {e.GetType()}, {e.Message}");
}
}
private static void TryPatch(Type type, string property, HarmonyMethod patch, bool setter)
{
try
{
var harmony = ExplorerCore.Loader.HarmonyInstance;
var prop = type.GetProperty(property);
if (setter) // setter is prefix
{
harmony.Patch(prop.GetSetMethod(), prefix: patch);
}
else // getter is postfix
{
harmony.Patch(prop.GetGetMethod(), postfix: patch);
}
}
catch (Exception e)
{
string suf = setter ? "set_" : "get_";
ExplorerCore.Log($"Unable to patch {type.Name}.{suf}{property}: {e.Message}");
}
}
public static void UpdateCursorControl()
{
try
{
m_currentlySettingCursor = true;
if (ShouldActuallyUnlock)
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
if (UIManager.EventSys)
SetEventSystem();
}
else
{
Cursor.lockState = m_lastLockMode;
Cursor.visible = m_lastVisibleState;
if (UIManager.EventSys)
ReleaseEventSystem();
}
m_currentlySettingCursor = false;
}
catch (Exception e)
@ -147,25 +89,19 @@ namespace UnityExplorer.Core.Input
public static void SetEventSystem()
{
// temp disabled for new InputSystem
if (InputManager.CurrentType == InputType.InputSystem)
return;
// Disable current event system object
if (m_lastEventSystem || EventSystem.current)
if (EventSystem.current && EventSystem.current != UIManager.EventSys)
{
if (!m_lastEventSystem)
m_lastEventSystem = EventSystem.current;
//ExplorerCore.Log("Disabling current event system...");
m_lastEventSystem = EventSystem.current;
m_lastEventSystem.enabled = false;
//m_lastEventSystem.gameObject.SetActive(false);
}
// Set to our current system
m_settingEventSystem = true;
EventSystem.current = UIManager.EventSys;
UIManager.EventSys.enabled = true;
EventSystem.current = UIManager.EventSys;
InputManager.ActivateUIModule();
m_settingEventSystem = false;
}
@ -175,10 +111,9 @@ namespace UnityExplorer.Core.Input
if (InputManager.CurrentType == InputType.InputSystem)
return;
if (m_lastEventSystem)
if (m_lastEventSystem && m_lastEventSystem.gameObject.activeSelf)
{
m_lastEventSystem.enabled = true;
//m_lastEventSystem.gameObject.SetActive(true);
m_settingEventSystem = true;
EventSystem.current = m_lastEventSystem;
@ -187,16 +122,42 @@ namespace UnityExplorer.Core.Input
}
}
[HarmonyPrefix]
// Patches
private static void SetupPatches()
{
try
{
if (CursorType == null)
throw new Exception("Could not load Type 'UnityEngine.Cursor'!");
// Get current cursor state and enable cursor
m_lastLockMode = (CursorLockMode?)CursorType.GetProperty("lockState", BF.Public | BF.Static)?.GetValue(null, null)
?? CursorLockMode.None;
m_lastVisibleState = (bool?)CursorType.GetProperty("visible", BF.Public | BF.Static)?.GetValue(null, null)
?? false;
ExplorerCore.Loader.SetupPatches();
}
catch (Exception e)
{
ExplorerCore.Log($"Error on CursorUnlocker.Init! {e.GetType()}, {e.Message}");
}
}
public static void Prefix_EventSystem_set_current(ref EventSystem value)
{
if (!m_settingEventSystem)
if (!m_settingEventSystem && value != UIManager.EventSys)
{
m_lastEventSystem = value;
m_lastInputModule = value?.currentInputModule;
if (ShouldActuallyUnlock)
{
value = UIManager.EventSys;
value.enabled = true;
}
}
}
@ -204,7 +165,6 @@ namespace UnityExplorer.Core.Input
// 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.
[HarmonyPrefix]
public static void Prefix_set_lockState(ref CursorLockMode value)
{
if (!m_currentlySettingCursor)
@ -216,7 +176,6 @@ namespace UnityExplorer.Core.Input
}
}
[HarmonyPrefix]
public static void Prefix_set_visible(ref bool value)
{
if (!m_currentlySettingCursor)
@ -228,4 +187,4 @@ namespace UnityExplorer.Core.Input
}
}
}
}
}

View File

@ -15,9 +15,7 @@ namespace UnityExplorer.Core.Input
BaseInputModule UIModule { get; }
PointerEventData InputPointerEvent { get; }
void AddUIInputModule();
void ActivateModule();
}
}
}

View File

@ -27,7 +27,6 @@ namespace UnityExplorer.Core.Input
public static bool GetMouseButton(int btn) => m_inputModule.GetMouseButton(btn);
public static BaseInputModule UIInput => m_inputModule.UIModule;
public static PointerEventData InputPointerEvent => m_inputModule.InputPointerEvent;
public static void ActivateUIModule() => m_inputModule.ActivateModule();
@ -52,7 +51,7 @@ namespace UnityExplorer.Core.Input
if (m_inputModule == null)
{
ExplorerCore.LogWarning("Could not find any Input module!");
ExplorerCore.LogWarning("Could not find any Input Module Type!");
m_inputModule = new NoInput();
CurrentType = InputType.None;
}

View File

@ -5,6 +5,8 @@ using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer.UI;
using System.Collections.Generic;
using UnityExplorer.UI.Inspectors;
using System.Linq;
namespace UnityExplorer.Core.Input
{
@ -83,16 +85,28 @@ namespace UnityExplorer.Core.Input
}
internal static Dictionary<KeyCode, object> ActualKeyDict = new Dictionary<KeyCode, object>();
internal static Dictionary<string, string> enumNameFixes = new Dictionary<string, string>
{
{ "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();
if (s.Contains("Control"))
s = s.Replace("Control", "Ctrl");
else if (s.Contains("Return"))
s = "Enter";
try
{
if (enumNameFixes.First(it => s.Contains(it.Key)) is KeyValuePair<string, string> entry)
s = s.Replace(entry.Key, entry.Value);
}
catch { }
var parsed = Enum.Parse(TKey, s);
var actualKey = m_kbIndexer.GetValue(CurrentKeyboard, new object[] { parsed });
@ -131,41 +145,74 @@ namespace UnityExplorer.Core.Input
// UI Input
//public Type TInputSystemUIInputModule
// => m_tUIInputModule
// ?? (m_tUIInputModule = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.UI.InputSystemUIInputModule"));
//internal Type m_tUIInputModule;
public Type TInputSystemUIInputModule
=> m_tUIInputModule
?? (m_tUIInputModule = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.UI.InputSystemUIInputModule"));
internal Type m_tUIInputModule;
public BaseInputModule UIModule => null; // m_newInputModule;
//internal BaseInputModule m_newInputModule;
public PointerEventData InputPointerEvent => null;
public BaseInputModule UIModule => m_newInputModule;
internal BaseInputModule m_newInputModule;
public void AddUIInputModule()
{
// if (TInputSystemUIInputModule != null)
// {
//#if CPP
// // m_newInputModule = UIManager.CanvasRoot.AddComponent(Il2CppType.From(TInputSystemUIInputModule)).TryCast<BaseInputModule>();
//#else
// m_newInputModule = (BaseInputModule)UIManager.CanvasRoot.AddComponent(TInputSystemUIInputModule);
//#endif
// }
// else
// {
// ExplorerCore.LogWarning("New input system: Could not find type by name 'UnityEngine.InputSystem.UI.InputSystemUIInputModule'");
// }
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<BaseInputModule>(UIManager.CanvasRoot, TInputSystemUIInputModule);
var asset = RuntimeProvider.Instance.CreateScriptable(assetType)
.Cast(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" })
.Cast(ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionMap"));
CreateAction(map, "point", new[] { "<Mouse>/position" }, "point");
CreateAction(map, "click", new[] { "<Mouse>/leftButton" }, "leftClick");
CreateAction(map, "rightClick", new[] { "<Mouse>/rightButton" }, "rightClick");
CreateAction(map, "scrollWheel", new[] { "<Mouse>/scroll" }, "scrollWheel");
UI_Enable = map.GetType().GetMethod("Enable");
UI_Enable.Invoke(map, new object[0]);
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 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 })
.Cast(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.Cast(inputActionType), binding, null, null, null });
var refType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionReference");
var inputRef = refType.GetMethod("Create")
.Invoke(null, new object[] { action })
.Cast(refType);
TInputSystemUIInputModule
.GetProperty(propertyName)
.SetValue(m_newInputModule.Cast(TInputSystemUIInputModule), inputRef, null);
}
public void ActivateModule()
{
//#if CPP
// // m_newInputModule.ActivateModule();
//#else
// m_newInputModule.ActivateModule();
//#endif
m_newInputModule.ActivateModule();
UI_Enable.Invoke(UI_ActionMap, new object[0]);
}
}
}
}

View File

@ -44,13 +44,6 @@ namespace UnityExplorer.Core.Input
public BaseInputModule UIModule => m_inputModule;
internal StandaloneInputModule m_inputModule;
public PointerEventData InputPointerEvent =>
#if CPP
m_inputModule.m_InputPointerEvent;
#else
null;
#endif
public void AddUIInputModule()
{
m_inputModule = UIManager.CanvasRoot.gameObject.AddComponent<StandaloneInputModule>();
@ -61,4 +54,4 @@ namespace UnityExplorer.Core.Input
m_inputModule.ActivateModule();
}
}
}
}

View File

@ -16,8 +16,7 @@ namespace UnityExplorer.Core.Input
public bool GetMouseButtonDown(int btn) => false;
public BaseInputModule UIModule => null;
public PointerEventData InputPointerEvent => null;
public void ActivateModule() { }
public void AddUIInputModule() { }
}
}
}

View File

@ -7,7 +7,7 @@ using System.Reflection;
using BF = System.Reflection.BindingFlags;
using UnityExplorer.Core.Runtime;
namespace UnityExplorer.Core
namespace UnityExplorer
{
public static class ReflectionUtility
{
@ -18,7 +18,7 @@ namespace UnityExplorer.Core
/// </summary>
/// <param name="obj">The object to get the true Type for.</param>
/// <returns>The most accurate Type of the object which could be identified.</returns>
public static Type GetType(this object obj)
public static Type GetActualType(this object obj)
{
if (obj == null)
return null;
@ -32,7 +32,7 @@ namespace UnityExplorer.Core
/// <param name="obj">The object to cast</param>
/// <returns>The object, cast to the underlying Type if possible, otherwise the original object.</returns>
public static object Cast(this object obj)
=> ReflectionProvider.Instance.Cast(obj, GetType(obj));
=> ReflectionProvider.Instance.Cast(obj, GetActualType(obj));
/// <summary>
/// Cast an object to a Type, if possible.
@ -43,6 +43,9 @@ namespace UnityExplorer.Core
public static object Cast(this object obj, Type castTo)
=> ReflectionProvider.Instance.Cast(obj, castTo);
public static T TryCast<T>(this object obj)
=> ReflectionProvider.Instance.TryCast<T>(obj);
/// <summary>
/// Check if the provided Type is assignable to IEnumerable.
/// </summary>
@ -105,7 +108,7 @@ namespace UnityExplorer.Core
/// <summary>
/// Get all base types of the provided Type, including itself.
/// </summary>
public static Type[] GetAllBaseTypes(this object obj) => GetAllBaseTypes(GetType(obj));
public static Type[] GetAllBaseTypes(this object obj) => GetAllBaseTypes(GetActualType(obj));
/// <summary>
/// Get all base types of the provided Type, including itself.
@ -189,6 +192,51 @@ namespace UnityExplorer.Core
return s_cachedPropInfos[type][propertyName];
}
internal static Dictionary<Type, Dictionary<string, MethodInfo>> s_cachedMethodInfos = new Dictionary<Type, Dictionary<string, MethodInfo>>();
public static MethodInfo GetMethodInfo(Type type, string methodName, Type[] argumentTypes)
{
if (!s_cachedMethodInfos.ContainsKey(type))
s_cachedMethodInfos.Add(type, new Dictionary<string, MethodInfo>());
var sig = methodName;
if (argumentTypes != null)
{
sig += "(";
for (int i = 0; i < argumentTypes.Length; i++)
{
if (i > 0)
sig += ",";
sig += argumentTypes[i].FullName;
}
sig += ")";
}
try
{
if (!s_cachedMethodInfos[type].ContainsKey(sig))
{
if (argumentTypes != null)
s_cachedMethodInfos[type].Add(sig, type.GetMethod(methodName, AllFlags, null, argumentTypes, null));
else
s_cachedMethodInfos[type].Add(sig, type.GetMethod(methodName, AllFlags));
}
return s_cachedMethodInfos[type][sig];
}
catch (AmbiguousMatchException)
{
ExplorerCore.LogWarning($"AmbiguousMatchException trying to get method '{sig}'");
return null;
}
catch (Exception e)
{
ExplorerCore.LogWarning($"{e.GetType()} trying to get method '{sig}': {e.Message}\r\n{e.StackTrace}");
return null;
}
}
/// <summary>
/// Helper to display a simple "{ExceptionType}: {Message}" of the exception, and optionally use the inner-most exception.
/// </summary>
@ -201,10 +249,9 @@ namespace UnityExplorer.Core
{
while (e.InnerException != null)
{
#if CPP
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException)
break;
#endif
e = e.InnerException;
}
}

View File

@ -50,12 +50,47 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
ExplorerCore.Log(condition, type, true);
}
public override void StartConsoleCoroutine(IEnumerator routine)
public override void StartCoroutine(IEnumerator routine)
{
Il2CppCoroutine.Start(routine);
}
// Unity API Handlers
public override void Update()
{
Il2CppCoroutine.Process();
}
public override T AddComponent<T>(GameObject obj, Type type)
{
return obj.AddComponent(Il2CppType.From(type)).TryCast<T>();
}
public override ScriptableObject CreateScriptable(Type type)
{
return ScriptableObject.CreateInstance(Il2CppType.From(type));
}
public override void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List<RaycastResult> list)
{
var il2cppList = new Il2CppSystem.Collections.Generic.List<RaycastResult>();
raycaster.Raycast(data, il2cppList);
if (il2cppList.Count > 0)
list.AddRange(il2cppList.ToArray());
}
public override bool IsReferenceEqual(object a, object b)
{
if (a.TryCast<UnityEngine.Object>() is UnityEngine.Object ua)
{
var ub = b.TryCast<UnityEngine.Object>();
if (ub && ua.m_CachedPtr == ub.m_CachedPtr)
return true;
}
return base.IsReferenceEqual(a, b);
}
// LayerMask.LayerToName
@ -89,10 +124,13 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
internal delegate void d_GetRootGameObjects(int handle, IntPtr list);
public override GameObject[] GetRootGameObjects(Scene scene) => GetRootGameObjects(scene.handle);
public static GameObject[] GetRootGameObjects(int handle)
public override GameObject[] GetRootGameObjects(Scene scene)
{
if (!scene.isLoaded)
return new GameObject[0];
int handle = scene.handle;
if (handle == -1)
return new GameObject[0];
@ -122,42 +160,103 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
.Invoke(handle);
}
internal static bool? s_doPropertiesExist;
internal static bool triedToGetColorBlockProps;
internal static PropertyInfo _normalColorProp;
internal static PropertyInfo _highlightColorProp;
internal static PropertyInfo _pressedColorProp;
public override ColorBlock SetColorBlock(ColorBlock colors, Color? normal = null, Color? highlighted = null, Color? pressed = null)
public override void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null)
{
if (s_doPropertiesExist == null)
{
var prop = ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "normalColor") as PropertyInfo;
s_doPropertiesExist = prop != null && prop.CanWrite;
}
var colors = selectable.colors;
colors.colorMultiplier = 1;
object boxed = (object)colors;
if (s_doPropertiesExist == true)
if (!triedToGetColorBlockProps)
{
if (normal != null)
ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "normalColor").SetValue(boxed, (Color)normal);
if (pressed != null)
ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "pressedColor").SetValue(boxed, (Color)pressed);
if (highlighted != null)
ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "highlightedColor").SetValue(boxed, (Color)highlighted);
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;
}
else if (s_doPropertiesExist == false)
try
{
if (normal != null)
ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_NormalColor").SetValue(boxed, (Color)normal);
if (pressed != null)
ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_PressedColor").SetValue(boxed, (Color)pressed);
{
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)
ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_HighlightedColor").SetValue(boxed, (Color)highlighted);
{
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);
}
}
catch (Exception ex)
{
ExplorerCore.Log(ex);
}
colors = (ColorBlock)boxed;
return colors;
SetColorBlock(selectable, colors);
}
public override void SetColorBlock(Selectable selectable, ColorBlock _colorBlock)
{
try
{
selectable = selectable.TryCast<Selectable>();
ReflectionUtility.GetPropertyInfo(typeof(Selectable), "m_Colors")
.SetValue(selectable, _colorBlock, null);
ReflectionUtility.GetMethodInfo(typeof(Selectable), "OnSetProperty", new Type[0])
.Invoke(selectable, new object[0]);
}
catch (Exception ex)
{
ExplorerCore.Log(ex);
}
}
public override void FindSingleton(string[] possibleNames, Type type, BF flags, List<object> 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.FindSingleton(possibleNames, type, flags, instances);
}
}
}

View File

@ -32,6 +32,43 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
return Il2CppCast(obj, castTo);
}
public override T TryCast<T>(object obj)
{
try
{
return (T)Il2CppCast(obj, typeof(T));
}
catch
{
return default;
}
}
public override void BoxStringToType(ref object value, Type castTo)
{
if (castTo == typeof(Il2CppSystem.String))
value = (Il2CppSystem.String)(value as string);
else
value = (Il2CppSystem.Object)(value as string);
}
public override string UnboxString(object value)
{
if (value is string s)
return s;
s = null;
// strings boxed as Il2CppSystem.Objects can behave weirdly.
// GetActualType will find they are a string, but if its boxed
// then we need to unbox it like this...
if (value is Il2CppSystem.Object cppObject)
s = cppObject.ToString();
else if (value is Il2CppSystem.String cppString)
s = cppString;
return s;
}
public override string ProcessTypeNameInString(Type type, string theString, ref string typeName)
{
if (!Il2CppTypeNotNull(type))
@ -154,21 +191,24 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
/// <returns>The object, as the type (or a normal C# object) if successful or the input value if not.</returns>
public static object Il2CppCast(object obj, Type castTo)
{
if (!(obj is Il2CppSystem.Object ilObj))
if (!(obj is Il2CppSystem.Object cppObj))
return obj;
if (!Il2CppTypeNotNull(castTo, out IntPtr castToPtr))
return obj;
IntPtr castFromPtr = il2cpp_object_get_class(ilObj.Pointer);
IntPtr castFromPtr = il2cpp_object_get_class(cppObj.Pointer);
if (!il2cpp_class_is_assignable_from(castToPtr, castFromPtr))
return obj;
return null;
if (RuntimeSpecificsStore.IsInjected(castToPtr))
return UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(ilObj.Pointer);
return UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(cppObj.Pointer);
return Activator.CreateInstance(castTo, ilObj.Pointer);
if (castTo == typeof(string))
return cppObj.ToString();
return Activator.CreateInstance(castTo, cppObj.Pointer);
}
/// <summary>
@ -319,6 +359,112 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
return false;
}
internal static readonly Dictionary<Type, MethodInfo> s_getEnumeratorMethods = new Dictionary<Type, MethodInfo>();
internal static readonly Dictionary<Type, EnumeratorInfo> s_enumeratorInfos = new Dictionary<Type, EnumeratorInfo>();
internal class EnumeratorInfo
{
internal MethodInfo moveNext;
internal PropertyInfo current;
}
public override IEnumerable EnumerateEnumerable(object value)
{
if (value == null)
return null;
var cppEnumerable = (value as Il2CppSystem.Object)?.TryCast<Il2CppSystem.Collections.IEnumerable>();
if (cppEnumerable != null)
{
var type = value.GetType();
if (!s_getEnumeratorMethods.ContainsKey(type))
s_getEnumeratorMethods.Add(type, type.GetMethod("GetEnumerator"));
var enumerator = s_getEnumeratorMethods[type].Invoke(value, null);
var enumeratorType = enumerator.GetType();
if (!s_enumeratorInfos.ContainsKey(enumeratorType))
{
s_enumeratorInfos.Add(enumeratorType, new EnumeratorInfo
{
current = enumeratorType.GetProperty("Current"),
moveNext = enumeratorType.GetMethod("MoveNext"),
});
}
var info = s_enumeratorInfos[enumeratorType];
// iterate
var list = new List<object>();
while ((bool)info.moveNext.Invoke(enumerator, null))
list.Add(info.current.GetValue(enumerator));
return list;
}
return null;
}
public override IDictionary EnumerateDictionary(object value, Type typeOfKeys, Type typeOfValues)
{
var valueType = ReflectionUtility.GetActualType(value);
var keyList = new List<object>();
var valueList = new List<object>();
var hashtable = value.Cast(typeof(Il2CppSystem.Collections.Hashtable)) as Il2CppSystem.Collections.Hashtable;
if (hashtable != null)
{
EnumerateCppHashtable(hashtable, keyList, valueList);
}
else
{
var keys = valueType.GetProperty("Keys").GetValue(value, null);
var values = valueType.GetProperty("Values").GetValue(value, null);
EnumerateCppIDictionary(keys, keyList);
EnumerateCppIDictionary(values, valueList);
}
var dict = Activator.CreateInstance(typeof(Dictionary<,>)
.MakeGenericType(typeOfKeys, typeOfValues))
as IDictionary;
for (int i = 0; i < keyList.Count; i++)
dict.Add(keyList[i], valueList[i]);
return dict;
}
private void EnumerateCppIDictionary(object collection, List<object> list)
{
// invoke GetEnumerator
var enumerator = collection.GetType().GetMethod("GetEnumerator").Invoke(collection, null);
// get the type of it
var enumeratorType = enumerator.GetType();
// reflect MoveNext and Current
var moveNext = enumeratorType.GetMethod("MoveNext");
var current = enumeratorType.GetProperty("Current");
// iterate
while ((bool)moveNext.Invoke(enumerator, null))
{
list.Add(current.GetValue(enumerator, null));
}
}
private void EnumerateCppHashtable(Il2CppSystem.Collections.Hashtable hashtable, List<object> keys, List<object> values)
{
for (int i = 0; i < hashtable.buckets.Count; i++)
{
var bucket = hashtable.buckets[i];
if (bucket == null || bucket.key == null)
continue;
keys.Add(bucket.key);
values.Add(bucket.val);
}
}
// ~~~~~~~~~~ not used ~~~~~~~~~~~~
// cached il2cpp unbox methods

View File

@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityExplorer.Core.CSharp
namespace UnityExplorer.Core.Runtime.Mono
{
public class DummyBehaviour : MonoBehaviour
{

View File

@ -7,6 +7,7 @@ using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityExplorer.Core;
@ -20,6 +21,8 @@ namespace UnityExplorer.Core.Runtime.Mono
{
Reflection = new MonoReflection();
TextureUtil = new MonoTextureUtil();
DummyBehaviour.Setup();
}
public override void SetupEvents()
@ -32,11 +35,31 @@ namespace UnityExplorer.Core.Runtime.Mono
ExplorerCore.Log(condition, type, true);
}
public override void StartConsoleCoroutine(IEnumerator routine)
public override void StartCoroutine(IEnumerator routine)
{
DummyBehaviour.Instance.StartCoroutine(routine);
}
public override void Update()
{
}
public override T AddComponent<T>(GameObject obj, Type type)
{
return (T)obj.AddComponent(type);
}
public override ScriptableObject CreateScriptable(Type type)
{
return ScriptableObject.CreateInstance(type);
}
public override void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List<RaycastResult> list)
{
raycaster.Raycast(data, list);
}
public override string LayerToName(int layer)
=> LayerMask.LayerToName(layer);
@ -52,6 +75,9 @@ namespace UnityExplorer.Core.Runtime.Mono
public override GameObject[] GetRootGameObjects(Scene scene)
{
if (!scene.isLoaded)
return new GameObject[0];
return scene.GetRootGameObjects();
}
@ -60,8 +86,10 @@ namespace UnityExplorer.Core.Runtime.Mono
return scene.rootCount;
}
public override ColorBlock SetColorBlock(ColorBlock colors, Color? normal = null, Color? highlighted = null, Color? pressed = null)
public override void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null)
{
var colors = selectable.colors;
if (normal != null)
colors.normalColor = (Color)normal;
@ -71,7 +99,12 @@ namespace UnityExplorer.Core.Runtime.Mono
if (pressed != null)
colors.pressedColor = (Color)pressed;
return colors;
SetColorBlock(selectable, colors);
}
public override void SetColorBlock(Selectable selectable, ColorBlock colors)
{
selectable.colors = colors;
}
}
}

View File

@ -12,6 +12,18 @@ namespace UnityExplorer.Core.Runtime.Mono
public override object Cast(object obj, Type castTo)
=> obj;
public override T TryCast<T>(object obj)
{
try
{
return (T)obj;
}
catch
{
return default;
}
}
// Vanilla GetType is fine for mono
public override Type GetActualType(object obj)
=> obj.GetType();
@ -27,6 +39,9 @@ namespace UnityExplorer.Core.Runtime.Mono
public override string ProcessTypeNameInString(Type type, string theString, ref string typeName)
=> theString;
// not necessary
public override void BoxStringToType(ref object _string, Type castTo) { }
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -18,6 +19,8 @@ namespace UnityExplorer.Core.Runtime
public abstract object Cast(object obj, Type castTo);
public abstract T TryCast<T>(object obj);
public abstract bool IsAssignableFrom(Type toAssignTo, Type toAssignFrom);
public abstract bool IsReflectionSupported(Type type);
@ -25,5 +28,15 @@ namespace UnityExplorer.Core.Runtime
public abstract string ProcessTypeNameInString(Type type, string theString, ref string typeName);
public abstract bool LoadModule(string module);
public abstract void BoxStringToType(ref object _string, Type castTo);
public virtual string UnboxString(object value) => (string)value;
public virtual IDictionary EnumerateDictionary(object value, Type typeOfKeys, Type typeOfValues)
=> null;
public virtual IEnumerable EnumerateEnumerable(object value)
=> null;
}
}

View File

@ -2,16 +2,17 @@
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.Runtime;
namespace UnityExplorer.Core.Runtime
// Intentionally project-wide namespace so that its always easily accessible.
namespace UnityExplorer
{
// Work in progress, this will be used to replace all the "if CPP / if MONO"
// pre-processor directives all over the codebase.
public abstract class RuntimeProvider
{
public static RuntimeProvider Instance;
@ -28,30 +29,60 @@ namespace UnityExplorer.Core.Runtime
public static void Init() =>
#if CPP
Instance = new Il2Cpp.Il2CppProvider();
Instance = new Core.Runtime.Il2Cpp.Il2CppProvider();
#else
Instance = new Mono.MonoProvider();
Instance = new Core.Runtime.Mono.MonoProvider();
#endif
public abstract void Initialize();
public abstract void SetupEvents();
public abstract void StartConsoleCoroutine(IEnumerator routine);
public abstract void StartCoroutine(IEnumerator routine);
public abstract void Update();
public virtual bool IsReferenceEqual(object a, object b) => ReferenceEquals(a, b);
// Unity API handlers
public abstract T AddComponent<T>(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<RaycastResult> list);
public abstract int GetSceneHandle(Scene scene);
public abstract GameObject[] GetRootGameObjects(Scene scene);
public abstract int GetRootCount(Scene scene);
public abstract ColorBlock SetColorBlock(ColorBlock colors, Color? normal = null, Color? highlighted = null, Color? pressed = null);
public abstract void SetColorBlock(Selectable selectable, ColorBlock colors);
public abstract void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null);
public virtual void FindSingleton(string[] s_instanceNames, Type type, BindingFlags flags, List<object> instances)
{
// Look for a typical Instance backing field.
FieldInfo fi;
foreach (var name in s_instanceNames)
{
fi = type.GetField(name, flags);
if (fi != null)
{
var instance = fi.GetValue(null);
if (instance != null)
{
instances.Add(instance);
return;
}
}
}
}
}
}

View File

@ -67,38 +67,8 @@ namespace UnityExplorer.Core.Search
{
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ToLower().Contains(nameFilter))
continue;
#if CPP
// Only look for Properties in IL2CPP, not for Mono.
PropertyInfo pi;
foreach (var name in s_instanceNames)
{
pi = type.GetProperty(name, flags);
if (pi != null)
{
var instance = pi.GetValue(null, null);
if (instance != null)
{
instances.Add(instance);
continue;
}
}
}
#endif
// Look for a typical Instance backing field.
FieldInfo fi;
foreach (var name in s_instanceNames)
{
fi = type.GetField(name, flags);
if (fi != null)
{
var instance = fi.GetValue(null);
if (instance != null)
{
instances.Add(instance);
break;
}
}
}
RuntimeProvider.Instance.FindSingleton(s_instanceNames, type, flags, instances);
}
catch { }
}
@ -175,15 +145,9 @@ namespace UnityExplorer.Core.Search
if (canGetGameObject)
{
#if MONO
var go = context == SearchContext.GameObject
? obj as GameObject
: (obj as Component).gameObject;
#else
var go = context == SearchContext.GameObject
? obj.TryCast<GameObject>()
: obj.TryCast<Component>().gameObject;
#endif
// scene check
if (sceneFilter != SceneFilter.Any)

36
src/Core/TestClass.cs Normal file
View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityExplorer
{
public static class TestClass
{
public static UI.Main.PanelDragger.ResizeTypes flags = UI.Main.PanelDragger.ResizeTypes.NONE;
#if CPP
public static string testStringOne = "Test";
public static Il2CppSystem.Object testStringTwo = "string boxed as cpp object";
public static Il2CppSystem.String testStringThree = "string boxed as cpp string";
public static string nullString = null;
public static Il2CppSystem.Collections.Hashtable testHashset;
public static Il2CppSystem.Collections.Generic.List<Il2CppSystem.Object> testList;
static TestClass()
{
testHashset = new Il2CppSystem.Collections.Hashtable();
testHashset.Add("key1", "itemOne");
testHashset.Add("key2", "itemTwo");
testHashset.Add("key3", "itemThree");
testList = new Il2CppSystem.Collections.Generic.List<Il2CppSystem.Object>(3);
testList.Add("One");
testList.Add("Two");
testList.Add("Three");
//testIList = list.TryCast<Il2CppSystem.Collections.IList>();
}
#endif
}
}

View File

@ -5,6 +5,7 @@ using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Main;
namespace UnityExplorer
@ -12,7 +13,7 @@ namespace UnityExplorer
public class ExplorerCore
{
public const string NAME = "UnityExplorer";
public const string VERSION = "3.3.2";
public const string VERSION = "3.3.11";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer";
@ -46,10 +47,14 @@ namespace UnityExplorer
UIManager.Init();
Log($"{NAME} {VERSION} initialized.");
//InspectorManager.Instance.Inspect(typeof(TestClass));
}
public static void Update()
{
RuntimeProvider.Instance.Update();
UIManager.Update();
}

View File

@ -21,7 +21,11 @@ namespace UnityExplorer.Loader.BIE
public override void RegisterConfigElement<T>(ConfigElement<T> config)
{
var entry = Config.Bind(CTG_NAME, config.Name, config.Value, config.Description);
object[] tags = null;
if (config.IsInternal)
tags = new[] { "Advanced" };
var entry = Config.Bind(CTG_NAME, config.Name, config.Value, new ConfigDescription(config.Description, null, tags));
entry.SettingChanged += (object o, EventArgs e) =>
{

View File

@ -10,6 +10,9 @@ using System.Text;
using UnityExplorer.Core.Config;
using UnityExplorer.Loader.BIE;
using UnityEngine;
using UnityExplorer.Core;
using UnityEngine.EventSystems;
using UnityExplorer.Core.Input;
#if CPP
using BepInEx.IL2CPP;
using UnhollowerRuntimeLib;
@ -75,10 +78,8 @@ namespace UnityExplorer
ClassInjector.RegisterTypeInIl2Cpp<ExplorerBehaviour>();
var obj = new GameObject(
"ExplorerBehaviour",
new Il2CppSystem.Type[] { Il2CppType.Of<ExplorerBehaviour>() }
);
var obj = new GameObject("ExplorerBehaviour");
obj.AddComponent<ExplorerBehaviour>();
obj.hideFlags = HideFlags.HideAndDontSave;
GameObject.DontDestroyOnLoad(obj);
@ -101,6 +102,48 @@ namespace UnityExplorer
}
}
#endif
public void SetupPatches()
{
try
{
this.HarmonyInstance.PatchAll();
}
catch (Exception ex)
{
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
}
}
[HarmonyPatch(typeof(EventSystem), "current", MethodType.Setter)]
public class PATCH_EventSystem_current
{
[HarmonyPrefix]
public static void Prefix_EventSystem_set_current(ref EventSystem value)
{
CursorUnlocker.Prefix_EventSystem_set_current(ref value);
}
}
[HarmonyPatch(typeof(Cursor), "lockState", MethodType.Setter)]
public class PATCH_Cursor_lockState
{
[HarmonyPrefix]
public static void Prefix_set_lockState(ref CursorLockMode value)
{
CursorUnlocker.Prefix_set_lockState(ref value);
}
}
[HarmonyPatch(typeof(Cursor), "visible", MethodType.Setter)]
public class PATCH_Cursor_visible
{
[HarmonyPrefix]
public static void Prefix_set_visible(ref bool value)
{
CursorUnlocker.Prefix_set_visible(ref value);
}
}
}
}
#endif

View File

@ -17,10 +17,6 @@ namespace UnityExplorer
Action<object> OnLogWarning { get; }
Action<object> OnLogError { get; }
#if ML
Harmony.HarmonyInstance HarmonyInstance { get; }
#else
HarmonyLib.Harmony HarmonyInstance { get; }
#endif
void SetupPatches();
}
}

View File

@ -1,14 +1,19 @@
#if ML
using System;
using System.IO;
using Harmony;
using MelonLoader;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer;
using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UnityExplorer.Loader.ML;
[assembly: MelonInfo(typeof(ExplorerMelonMod), ExplorerCore.NAME, ExplorerCore.VERSION, ExplorerCore.AUTHOR)]
[assembly: MelonGame(null, null)]
//[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.UNIVERSAL)]
namespace UnityExplorer
{
@ -40,6 +45,41 @@ namespace UnityExplorer
{
ExplorerCore.Update();
}
public void SetupPatches()
{
try
{
PrefixProperty(typeof(Cursor),
"lockState",
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_lockState))));
PrefixProperty(typeof(Cursor),
"visible",
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_visible))));
PrefixProperty(typeof(EventSystem),
"current",
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_EventSystem_set_current))));
}
catch (Exception ex)
{
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
}
}
private void PrefixProperty(Type type, string property, HarmonyMethod prefix)
{
try
{
var prop = type.GetProperty(property);
this.Harmony.Patch(prop.GetSetMethod(), prefix: prefix);
}
catch (Exception e)
{
ExplorerCore.Log($"Unable to patch {type.Name}.set_{property}: {e.Message}");
}
}
}
}
#endif

View File

@ -21,7 +21,13 @@ namespace UnityExplorer.Loader.ML
{
prefCategory = MelonPreferences.CreateCategory(CTG_NAME, $"{CTG_NAME} Settings");
MelonPreferences.Mapper.RegisterMapper(KeycodeReader, KeycodeWriter);
try
{
// TEMPORARY - JUST REQUIRED UNTIL ML 0.3.1 RELEASED
MelonPreferences.Mapper.RegisterMapper(KeycodeReader, KeycodeWriter);
MelonPreferences.Mapper.RegisterMapper(MenuPagesReader, MenuPagesWriter);
}
catch { }
}
public override void LoadConfig()
@ -69,7 +75,6 @@ namespace UnityExplorer.Loader.ML
public override void OnAnyConfigChanged()
{
MelonPreferences.Save();
}
public override void SaveConfig()
@ -77,6 +82,8 @@ namespace UnityExplorer.Loader.ML
MelonPreferences.Save();
}
// TEMPORARY - JUST REQUIRED UNTIL ML 0.3.1 RELEASED
public static KeyCode KeycodeReader(TomlObject value)
{
try
@ -98,6 +105,28 @@ namespace UnityExplorer.Loader.ML
{
return MelonPreferences.Mapper.ToToml(value.ToString());
}
public static UI.Main.MenuPages MenuPagesReader(TomlObject value)
{
try
{
var kc = (UI.Main.MenuPages)Enum.Parse(typeof(UI.Main.MenuPages), (value as TomlString).Value);
if (kc == default)
throw new Exception();
return kc;
}
catch
{
return UI.Main.MenuPages.Home;
}
}
public static TomlObject MenuPagesWriter(UI.Main.MenuPages value)
{
return MelonPreferences.Mapper.ToToml(value.ToString());
}
}
}

View File

@ -6,6 +6,9 @@ using System.Reflection;
using UnityEngine;
using UnityExplorer.Core.Config;
using UnityExplorer.Loader.STANDALONE;
using UnityEngine.EventSystems;
using UnityExplorer.Core.Input;
using UnityExplorer.Core;
#if CPP
using UnhollowerRuntimeLib;
#endif
@ -86,17 +89,9 @@ namespace UnityExplorer
#if CPP
ClassInjector.RegisterTypeInIl2Cpp<ExplorerBehaviour>();
var obj = new GameObject(
"ExplorerBehaviour",
new Il2CppSystem.Type[] { Il2CppType.Of<ExplorerBehaviour>() }
);
#else
var obj = new GameObject(
"ExplorerBehaviour",
new Type[] { typeof(ExplorerBehaviour) }
);
#endif
var obj = new GameObject("ExplorerBehaviour");
obj.AddComponent<ExplorerBehaviour>();
GameObject.DontDestroyOnLoad(obj);
obj.hideFlags = HideFlags.HideAndDontSave;
@ -114,6 +109,48 @@ namespace UnityExplorer
ExplorerCore.Update();
}
}
public void SetupPatches()
{
try
{
this.HarmonyInstance.PatchAll();
}
catch (Exception ex)
{
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
}
}
[HarmonyPatch(typeof(EventSystem), "current", MethodType.Setter)]
public class PATCH_EventSystem_current
{
[HarmonyPrefix]
public static void Prefix_EventSystem_set_current(ref EventSystem value)
{
CursorUnlocker.Prefix_EventSystem_set_current(ref value);
}
}
[HarmonyPatch(typeof(Cursor), "lockState", MethodType.Setter)]
public class PATCH_Cursor_lockState
{
[HarmonyPrefix]
public static void Prefix_set_lockState(ref CursorLockMode value)
{
CursorUnlocker.Prefix_set_lockState(ref value);
}
}
[HarmonyPatch(typeof(Cursor), "visible", MethodType.Setter)]
public class PATCH_Cursor_visible
{
[HarmonyPrefix]
public static void Prefix_set_visible(ref bool value)
{
CursorUnlocker.Prefix_set_visible(ref value);
}
}
}
}
#endif

View File

@ -10,7 +10,7 @@ using UnityExplorer.Core.Runtime;
using UnityExplorer.Core;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.InteractiveValues;
using UnityExplorer.UI.Main.Home.Inspectors.Reflection;
using UnityExplorer.UI.Inspectors.Reflection;
namespace UnityExplorer.UI.CacheObject
{
@ -86,7 +86,7 @@ namespace UnityExplorer.UI.CacheObject
{
try
{
Type baseType = ReflectionUtility.GetType(IValue.Value) ?? FallbackType;
Type baseType = ReflectionUtility.GetActualType(IValue.Value) ?? FallbackType;
if (!ReflectionProvider.Instance.IsReflectionSupported(baseType))
throw new Exception("Type not supported with reflection");
@ -94,7 +94,7 @@ namespace UnityExplorer.UI.CacheObject
UpdateReflection();
if (IValue.Value != null)
IValue.Value = IValue.Value.Cast(ReflectionUtility.GetType(IValue.Value));
IValue.Value = IValue.Value.Cast(ReflectionUtility.GetActualType(IValue.Value));
}
catch (Exception e)
{
@ -319,15 +319,13 @@ namespace UnityExplorer.UI.CacheObject
default, new Color(1, 1, 1, 0));
UIFactory.SetLayoutElement(evalGroupObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 5000);
var colors = new ColorBlock();
colors = RuntimeProvider.Instance.SetColorBlock(colors, new Color(0.4f, 0.4f, 0.4f),
new Color(0.4f, 0.7f, 0.4f), new Color(0.3f, 0.3f, 0.3f));
var evalButton = UIFactory.CreateButton(evalGroupObj,
"EvalButton",
$"Evaluate ({ParamCount})",
null,
colors);
null);
RuntimeProvider.Instance.SetColorBlock(evalButton, new Color(0.4f, 0.4f, 0.4f),
new Color(0.4f, 0.7f, 0.4f), new Color(0.3f, 0.3f, 0.3f));
UIFactory.SetLayoutElement(evalButton.gameObject, minWidth: 100, minHeight: 22, flexibleWidth: 0);
@ -345,7 +343,7 @@ namespace UnityExplorer.UI.CacheObject
argsHolder.SetActive(true);
m_isEvaluating = true;
evalText.text = "Evaluate";
evalButton.colors = RuntimeProvider.Instance.SetColorBlock(evalButton.colors, new Color(0.3f, 0.6f, 0.3f));
RuntimeProvider.Instance.SetColorBlock(evalButton, new Color(0.3f, 0.6f, 0.3f));
cancelButton.gameObject.SetActive(true);
}
@ -365,18 +363,17 @@ namespace UnityExplorer.UI.CacheObject
m_isEvaluating = false;
evalText.text = $"Evaluate ({ParamCount})";
evalButton.colors = RuntimeProvider.Instance.SetColorBlock(evalButton.colors, new Color(0.4f, 0.4f, 0.4f));
RuntimeProvider.Instance.SetColorBlock(evalButton, new Color(0.4f, 0.4f, 0.4f));
});
}
else if (this is CacheMethod)
{
// simple method evaluate button
var colors = new ColorBlock();
colors = RuntimeProvider.Instance.SetColorBlock(colors, new Color(0.4f, 0.4f, 0.4f),
var evalButton = UIFactory.CreateButton(m_rightGroup, "EvalButton", "Evaluate", () => { (this as CacheMethod).Evaluate(); });
RuntimeProvider.Instance.SetColorBlock(evalButton, new Color(0.4f, 0.4f, 0.4f),
new Color(0.4f, 0.7f, 0.4f), new Color(0.3f, 0.3f, 0.3f));
var evalButton = UIFactory.CreateButton(m_rightGroup, "EvalButton", "Evaluate", () => { (this as CacheMethod).Evaluate(); }, colors);
UIFactory.SetLayoutElement(evalButton.gameObject, minWidth: 100, minHeight: 22, flexibleWidth: 0);
}
}

View File

@ -55,7 +55,7 @@ namespace UnityExplorer.UI.CacheObject
// if the type has changed fundamentally, make a new interactivevalue for it
var type = value == null
? FallbackType
: ReflectionUtility.GetType(value);
: ReflectionUtility.GetActualType(value);
var ivalueType = InteractiveValue.GetIValueForType(type);

View File

@ -56,8 +56,12 @@ namespace UnityExplorer.UI.CacheObject
var rowObj = UIFactory.CreateHorizontalGroup(m_mainContent, "PairedGroup", false, false, true, true, 0, new Vector4(0,0,5,2),
bgColor);
var indexLabel = UIFactory.CreateLabel(rowObj, "IndexLabel", $"{this.PairType} {this.Index}:", TextAnchor.MiddleLeft);
string lbl = $"{this.PairType}";
if (this.PairType == PairTypes.Key)
lbl = $"[{Index}] {lbl}";
var indexLabel = UIFactory.CreateLabel(rowObj, "IndexLabel", lbl, TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(indexLabel.gameObject, minWidth: 80, flexibleWidth: 30, minHeight: 25);
IValue.m_mainContentParent = rowObj;

View File

@ -7,7 +7,7 @@ using UnityEngine.UI;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Main.Home.Inspectors.GameObjects
namespace UnityExplorer.UI.Inspectors.GameObjects
{
public class ChildList
{
@ -168,15 +168,13 @@ namespace UnityExplorer.UI.Main.Home.Inspectors.GameObjects
s_childListToggles.Add(toggle);
toggle.onValueChanged.AddListener((bool val) => { OnToggleClicked(thisIndex, val); });
ColorBlock mainColors = new ColorBlock();
RuntimeProvider.Instance.SetColorBlock(mainColors, new Color(0.07f, 0.07f, 0.07f),
new Color(0.2f, 0.2f, 0.2f, 1), new Color(0.05f, 0.05f, 0.05f));
var mainBtn = UIFactory.CreateButton(btnGroupObj,
"MainButton",
"",
() => { OnChildListObjectClicked(thisIndex); },
mainColors);
() => { OnChildListObjectClicked(thisIndex); });
RuntimeProvider.Instance.SetColorBlock(mainBtn, new Color(0.07f, 0.07f, 0.07f),
new Color(0.2f, 0.2f, 0.2f, 1), new Color(0.05f, 0.05f, 0.05f));
UIFactory.SetLayoutElement(mainBtn.gameObject, minHeight: 25, flexibleHeight: 0, minWidth: 25, flexibleWidth: 9999);

View File

@ -8,7 +8,7 @@ using UnityExplorer.Core;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Main.Home.Inspectors.GameObjects
namespace UnityExplorer.UI.Inspectors.GameObjects
{
public class ComponentList
{
@ -75,14 +75,10 @@ namespace UnityExplorer.UI.Main.Home.Inspectors.GameObjects
var text = s_compListTexts[i];
text.text = SignatureHighlighter.ParseFullSyntax(ReflectionUtility.GetType(comp), true);
text.text = SignatureHighlighter.ParseFullSyntax(ReflectionUtility.GetActualType(comp), true);
var toggle = s_compToggles[i];
#if CPP
if (comp.TryCast<Behaviour>() is Behaviour behaviour)
#else
if (comp is Behaviour behaviour)
#endif
{
if (!toggle.gameObject.activeSelf)
toggle.gameObject.SetActive(true);
@ -109,19 +105,13 @@ namespace UnityExplorer.UI.Main.Home.Inspectors.GameObjects
internal static void OnCompToggleClicked(int index, bool value)
{
var comp = s_compShortlist[index];
#if CPP
comp.TryCast<Behaviour>().enabled = value;
#else
(comp as Behaviour).enabled = value;
#endif
}
internal static void OnCompListObjectClicked(int index)
{
if (index >= s_compShortlist.Count || !s_compShortlist[index])
{
return;
}
InspectorManager.Instance.Inspect(s_compShortlist[index]);
}
@ -175,15 +165,13 @@ namespace UnityExplorer.UI.Main.Home.Inspectors.GameObjects
// Main component button
ColorBlock mainColors = new ColorBlock();
RuntimeProvider.Instance.SetColorBlock(mainColors, new Color(0.07f, 0.07f, 0.07f),
new Color(0.2f, 0.2f, 0.2f, 1), new Color(0.05f, 0.05f, 0.05f));
var mainBtn = UIFactory.CreateButton(groupObj,
"MainButton",
"",
() => { OnCompListObjectClicked(thisIndex); },
mainColors);
() => { OnCompListObjectClicked(thisIndex); });
RuntimeProvider.Instance.SetColorBlock(mainBtn, new Color(0.07f, 0.07f, 0.07f),
new Color(0.2f, 0.2f, 0.2f, 1), new Color(0.05f, 0.05f, 0.05f));
UIFactory.SetLayoutElement(mainBtn.gameObject, minHeight: 25, flexibleHeight: 0, minWidth: 25, flexibleWidth: 999);

View File

@ -8,7 +8,7 @@ using UnityExplorer.Core.Input;
using UnityExplorer.Core.Runtime;
using UnityExplorer.Core.Unity;
namespace UnityExplorer.UI.Main.Home.Inspectors.GameObjects
namespace UnityExplorer.UI.Inspectors.GameObjects
{
public class GameObjectControls
{
@ -408,7 +408,7 @@ namespace UnityExplorer.UI.Main.Home.Inspectors.GameObjects
var sliderObj = UIFactory.CreateSlider(rowObject, "VectorSlider", out Slider slider);
UIFactory.SetLayoutElement(sliderObj, minHeight: 20, flexibleHeight: 0, minWidth: 200, flexibleWidth: 9000);
sliderObj.transform.Find("Fill Area").gameObject.SetActive(false);
slider.colors = RuntimeProvider.Instance.SetColorBlock(slider.colors, new Color(0.65f, 0.65f, 0.65f));
RuntimeProvider.Instance.SetColorBlock(slider, new Color(0.65f, 0.65f, 0.65f));
slider.minValue = -2;
slider.maxValue = 2;
slider.value = 0;

View File

@ -7,7 +7,7 @@ using UnityEngine.UI;
using UnityExplorer.Core.Runtime;
using UnityExplorer.Core.Unity;
namespace UnityExplorer.UI.Main.Home.Inspectors.GameObjects
namespace UnityExplorer.UI.Inspectors.GameObjects
{
public class GameObjectInspector : InspectorBase
{

View File

@ -11,6 +11,7 @@ using UnityExplorer.Core.Input;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI;
using UnityExplorer.UI.Main;
using UnityExplorer.UI.Inspectors;
namespace UnityExplorer.UI.Main.Home
{
@ -202,11 +203,6 @@ namespace UnityExplorer.UI.Main.Home
position = mousePos
};
#if MONO
var list = new List<RaycastResult>();
#else
var list = new Il2CppSystem.Collections.Generic.List<RaycastResult>();
#endif
//ExplorerCore.Log("~~~~~~~~~ begin raycast ~~~~~~~~");
GameObject hitObject = null;
int highestLayer = int.MinValue;
@ -214,7 +210,10 @@ namespace UnityExplorer.UI.Main.Home
int highestDepth = int.MinValue;
foreach (var gr in graphicRaycasters)
{
gr.Raycast(ped, list);
var list = new List<RaycastResult>();
RuntimeProvider.Instance.GraphicRaycast(gr, ped, list);
//gr.Raycast(ped, list);
if (list.Count > 0)
{
@ -289,8 +288,6 @@ namespace UnityExplorer.UI.Main.Home
_wasDisabledGraphics.Clear();
}
#region UI
internal static Text s_objNameLabel;
internal static Text s_objPathLabel;
internal static Text s_mousePosLabel;
@ -327,7 +324,5 @@ namespace UnityExplorer.UI.Main.Home
s_UIContent.SetActive(false);
}
#endregion
}
}

View File

@ -3,7 +3,7 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Unity;
namespace UnityExplorer.UI.Main.Home.Inspectors
namespace UnityExplorer.UI.Inspectors
{
public abstract class InspectorBase
{

View File

@ -9,12 +9,11 @@ using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.Main.Home;
using UnityExplorer.UI.Main.Home.Inspectors;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.Main.Home.Inspectors.GameObjects;
using UnityExplorer.UI.Main.Home.Inspectors.Reflection;
using UnityExplorer.UI.Inspectors.GameObjects;
using UnityExplorer.UI.Inspectors.Reflection;
namespace UnityExplorer.UI.Main.Home
namespace UnityExplorer.UI.Inspectors
{
public class InspectorManager
{
@ -55,21 +54,11 @@ namespace UnityExplorer.UI.Main.Home
// check if currently inspecting this object
foreach (InspectorBase tab in m_currentInspectors)
{
if (ReferenceEquals(obj, tab.Target))
if (RuntimeProvider.Instance.IsReferenceEqual(obj, tab.Target))
{
SetInspectorTab(tab);
return;
}
#if CPP
else if (unityObj && tab.Target is UnityEngine.Object uTabObj)
{
if (unityObj.m_CachedPtr == uTabObj.m_CachedPtr)
{
SetInspectorTab(tab);
return;
}
}
#endif
}
InspectorBase inspector;
@ -141,12 +130,12 @@ namespace UnityExplorer.UI.Main.Home
public void OnSetInspectorTab(InspectorBase inspector)
{
Color activeColor = new Color(0, 0.25f, 0, 1);
inspector.m_tabButton.colors = RuntimeProvider.Instance.SetColorBlock(inspector.m_tabButton.colors, activeColor, activeColor);
RuntimeProvider.Instance.SetColorBlock(inspector.m_tabButton, activeColor, activeColor);
}
public void OnUnsetInspectorTab()
{
m_activeInspector.m_tabButton.colors = RuntimeProvider.Instance.SetColorBlock(m_activeInspector.m_tabButton.colors,
RuntimeProvider.Instance.SetColorBlock(m_activeInspector.m_tabButton,
new Color(0.2f, 0.2f, 0.2f, 1), new Color(0.1f, 0.3f, 0.1f, 1));
}

View File

@ -9,7 +9,7 @@ using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Runtime;
namespace UnityExplorer.UI.Main.Home.Inspectors.Reflection
namespace UnityExplorer.UI.Inspectors.Reflection
{
public enum MemberScopes
{
@ -30,12 +30,12 @@ namespace UnityExplorer.UI.Main.Home.Inspectors.Reflection
internal void OnScopeFilterClicked(MemberScopes type, Button button)
{
if (m_lastActiveScopeButton)
m_lastActiveScopeButton.colors = RuntimeProvider.Instance.SetColorBlock(m_lastActiveScopeButton.colors, new Color(0.2f, 0.2f, 0.2f));
RuntimeProvider.Instance.SetColorBlock(m_lastActiveScopeButton, new Color(0.2f, 0.2f, 0.2f));
m_scopeFilter = type;
m_lastActiveScopeButton = button;
m_lastActiveScopeButton.colors = RuntimeProvider.Instance.SetColorBlock(m_lastActiveScopeButton.colors, new Color(0.2f, 0.6f, 0.2f));
RuntimeProvider.Instance.SetColorBlock(m_lastActiveScopeButton, new Color(0.2f, 0.6f, 0.2f));
FilterMembers(null, true);
m_sliderScroller.m_slider.value = 1f;
@ -240,11 +240,11 @@ namespace UnityExplorer.UI.Main.Home.Inspectors.Reflection
btn.onClick.AddListener(() => { OnScopeFilterClicked(type, btn); });
btn.colors = RuntimeProvider.Instance.SetColorBlock(btn.colors, highlighted: new Color(0.3f, 0.7f, 0.3f));
RuntimeProvider.Instance.SetColorBlock(btn, highlighted: new Color(0.3f, 0.7f, 0.3f));
if (setEnabled)
{
btn.colors = RuntimeProvider.Instance.SetColorBlock(btn.colors, new Color(0.2f, 0.6f, 0.2f));
RuntimeProvider.Instance.SetColorBlock(btn, new Color(0.2f, 0.6f, 0.2f));
m_scopeFilter = type;
m_lastActiveScopeButton = btn;
}

View File

@ -9,9 +9,11 @@ using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.Main;
using UnityExplorer.UI.Main.Home;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Main.Home.Inspectors.Reflection
namespace UnityExplorer.UI.Inspectors.Reflection
{
public class ReflectionInspector : InspectorBase
{
@ -78,7 +80,7 @@ namespace UnityExplorer.UI.Main.Home.Inspectors.Reflection
if (this is StaticInspector)
m_targetType = target as Type;
else
m_targetType = ReflectionUtility.GetType(target);
m_targetType = ReflectionUtility.GetActualType(target);
m_targetTypeShortName = SignatureHighlighter.ParseFullSyntax(m_targetType, false);
@ -239,12 +241,12 @@ namespace UnityExplorer.UI.Main.Home.Inspectors.Reflection
internal void OnMemberFilterClicked(MemberTypes type, Button button)
{
if (m_lastActiveMemButton)
m_lastActiveMemButton.colors = RuntimeProvider.Instance.SetColorBlock(m_lastActiveMemButton.colors, new Color(0.2f, 0.2f, 0.2f));
RuntimeProvider.Instance.SetColorBlock(m_lastActiveMemButton, new Color(0.2f, 0.2f, 0.2f));
m_memberFilter = type;
m_lastActiveMemButton = button;
m_lastActiveMemButton.colors = RuntimeProvider.Instance.SetColorBlock(m_lastActiveMemButton.colors, new Color(0.2f, 0.6f, 0.2f));
RuntimeProvider.Instance.SetColorBlock(m_lastActiveMemButton, new Color(0.2f, 0.6f, 0.2f));
FilterMembers(null, true);
m_sliderScroller.m_slider.value = 1f;
@ -459,11 +461,11 @@ namespace UnityExplorer.UI.Main.Home.Inspectors.Reflection
UIFactory.SetLayoutElement(btn.gameObject, minHeight: 25, minWidth: 70);
btn.onClick.AddListener(() => { OnMemberFilterClicked(type, btn); });
btn.colors = RuntimeProvider.Instance.SetColorBlock(btn.colors, highlighted: new Color(0.3f, 0.7f, 0.3f));
RuntimeProvider.Instance.SetColorBlock(btn, highlighted: new Color(0.3f, 0.7f, 0.3f));
if (setEnabled)
{
btn.colors = RuntimeProvider.Instance.SetColorBlock(btn.colors, new Color(0.2f, 0.6f, 0.2f));
RuntimeProvider.Instance.SetColorBlock(btn, new Color(0.2f, 0.6f, 0.2f));
m_memberFilter = type;
m_lastActiveMemButton = btn;
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityExplorer.UI.Main.Home.Inspectors.Reflection
namespace UnityExplorer.UI.Inspectors.Reflection
{
public class StaticInspector : ReflectionInspector
{

View File

@ -13,7 +13,9 @@ using UnityExplorer.UI.CacheObject;
using UnityExplorer.Core;
using UnityExplorer.UI.Utility;
#if CPP
using CppDictionary = Il2CppSystem.Collections.IDictionary;
using AltIDictionary = Il2CppSystem.Collections.IDictionary;
#else
using AltIDictionary = System.Collections.IDictionary;
#endif
namespace UnityExplorer.UI.InteractiveValues
@ -48,11 +50,7 @@ namespace UnityExplorer.UI.InteractiveValues
}
internal IDictionary RefIDictionary;
#if CPP
internal CppDictionary RefCppDictionary;
#else
internal IDictionary RefCppDictionary = null;
#endif
internal AltIDictionary RefAltIDictionary;
internal Type m_typeOfKeys;
internal Type m_typeofValues;
@ -73,10 +71,11 @@ namespace UnityExplorer.UI.InteractiveValues
{
RefIDictionary = Value as IDictionary;
#if CPP
try { RefCppDictionary = (Value as Il2CppSystem.Object).TryCast<CppDictionary>(); }
catch { }
#endif
if (RefIDictionary == null)
{
try { RefAltIDictionary = Value.TryCast<AltIDictionary>(); }
catch { }
}
if (m_subContentParent.activeSelf)
{
@ -129,10 +128,8 @@ namespace UnityExplorer.UI.InteractiveValues
m_entries.Clear();
}
#if CPP
if (RefIDictionary == null && Value != null)
RefIDictionary = EnumerateWithReflection();
#endif
RefIDictionary = RuntimeProvider.Instance.Reflection.EnumerateDictionary(Value, m_typeOfKeys, m_typeofValues);
if (RefIDictionary != null)
{
@ -212,65 +209,11 @@ namespace UnityExplorer.UI.InteractiveValues
RefreshDisplay();
}
#region CPP fixes
#if CPP
// temp fix for Il2Cpp IDictionary until interfaces are fixed
private IDictionary EnumerateWithReflection()
{
var valueType = ReflectionUtility.GetType(Value);
// get keys and values
var keys = valueType.GetProperty("Keys").GetValue(Value, null);
var values = valueType.GetProperty("Values").GetValue(Value, null);
// create lists to hold them
var keyList = new List<object>();
var valueList = new List<object>();
// store entries with reflection
EnumerateCollection(keys, keyList);
EnumerateCollection(values, valueList);
// make actual mono dictionary
var dict = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>)
.MakeGenericType(m_typeOfKeys, m_typeofValues));
// finally iterate into mono dictionary
for (int i = 0; i < keyList.Count; i++)
dict.Add(keyList[i], valueList[i]);
return dict;
}
private void EnumerateCollection(object collection, List<object> list)
{
// invoke GetEnumerator
var enumerator = collection.GetType().GetMethod("GetEnumerator").Invoke(collection, null);
// get the type of it
var enumeratorType = enumerator.GetType();
// reflect MoveNext and Current
var moveNext = enumeratorType.GetMethod("MoveNext");
var current = enumeratorType.GetProperty("Current");
// iterate
while ((bool)moveNext.Invoke(enumerator, null))
{
list.Add(current.GetValue(enumerator, null));
}
}
#endif
#endregion
#region UI CONSTRUCTION
internal GameObject m_listContent;
internal LayoutElement m_listLayout;
internal PageHandler m_pageHandler;
//internal List<GameObject> m_rowHolders = new List<GameObject>();
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
@ -300,7 +243,5 @@ namespace UnityExplorer.UI.InteractiveValues
contentFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
}
#endregion
}
}

View File

@ -94,7 +94,7 @@ namespace UnityExplorer.UI.InteractiveValues
{
base.RefreshUIForValue();
if (m_subContentConstructed)
if (m_subContentConstructed && !(this is InteractiveFlags))
{
m_dropdownText.text = Value?.ToString() ?? "<no value set>";
}

View File

@ -6,6 +6,7 @@ using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Unity;
using UnityExplorer.UI;
@ -38,11 +39,6 @@ namespace UnityExplorer.UI.InteractiveValues
internal IEnumerable RefIEnumerable;
internal IList RefIList;
#if CPP
internal Il2CppSystem.Collections.ICollection CppICollection;
#else
internal ICollection CppICollection = null;
#endif
internal readonly Type m_baseEntryType;
@ -55,14 +51,6 @@ namespace UnityExplorer.UI.InteractiveValues
RefIEnumerable = Value as IEnumerable;
RefIList = Value as IList;
#if CPP
if (Value != null && RefIList == null)
{
try { CppICollection = (Value as Il2CppSystem.Object).TryCast<Il2CppSystem.Collections.ICollection>(); }
catch { }
}
#endif
if (m_subContentParent.activeSelf)
{
GetCacheEntries();
@ -91,8 +79,8 @@ namespace UnityExplorer.UI.InteractiveValues
if (Value != null)
{
string count = "?";
if (m_recacheWanted && (RefIList != null || CppICollection != null))
count = RefIList?.Count.ToString() ?? CppICollection.Count.ToString();
if (m_recacheWanted && RefIList != null)
count = RefIList.Count.ToString();
else if (!m_recacheWanted)
count = m_entries.Count.ToString();
@ -116,10 +104,8 @@ namespace UnityExplorer.UI.InteractiveValues
m_entries.Clear();
}
#if CPP
if (RefIEnumerable == null && Value != null)
RefIEnumerable = EnumerateWithReflection();
#endif
RefIEnumerable = RuntimeProvider.Instance.Reflection.EnumerateEnumerable(Value);
if (RefIEnumerable != null)
{
@ -183,62 +169,6 @@ namespace UnityExplorer.UI.InteractiveValues
RefreshDisplay();
}
#region CPP Helpers
#if CPP
// some temp fixes for Il2Cpp IEnumerables until interfaces are fixed
internal static readonly Dictionary<Type, MethodInfo> s_getEnumeratorMethods = new Dictionary<Type, MethodInfo>();
internal static readonly Dictionary<Type, EnumeratorInfo> s_enumeratorInfos = new Dictionary<Type, EnumeratorInfo>();
internal class EnumeratorInfo
{
internal MethodInfo moveNext;
internal PropertyInfo current;
}
private IEnumerable EnumerateWithReflection()
{
if (Value == null)
return null;
// new test
var CppEnumerable = (Value as Il2CppSystem.Object)?.TryCast<Il2CppSystem.Collections.IEnumerable>();
if (CppEnumerable != null)
{
var type = Value.GetType();
if (!s_getEnumeratorMethods.ContainsKey(type))
s_getEnumeratorMethods.Add(type, type.GetMethod("GetEnumerator"));
var enumerator = s_getEnumeratorMethods[type].Invoke(Value, null);
var enumeratorType = enumerator.GetType();
if (!s_enumeratorInfos.ContainsKey(enumeratorType))
{
s_enumeratorInfos.Add(enumeratorType, new EnumeratorInfo
{
current = enumeratorType.GetProperty("Current"),
moveNext = enumeratorType.GetMethod("MoveNext"),
});
}
var info = s_enumeratorInfos[enumeratorType];
// iterate
var list = new List<object>();
while ((bool)info.moveNext.Invoke(enumerator, null))
list.Add(info.current.GetValue(enumerator));
return list;
}
return null;
}
#endif
#endregion
#region UI CONSTRUCTION
internal GameObject m_listContent;
internal LayoutElement m_listLayout;
@ -273,7 +203,5 @@ namespace UnityExplorer.UI.InteractiveValues
contentFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
}
#endregion
}
}

View File

@ -26,8 +26,6 @@ namespace UnityExplorer.UI.InteractiveValues
public override void OnValueUpdated()
{
base.OnValueUpdated();
if (Owner.CanWrite)
{
var enabledNames = new List<string>();
@ -37,10 +35,10 @@ namespace UnityExplorer.UI.InteractiveValues
enabledNames.AddRange(enabled);
for (int i = 0; i < m_values.Length; i++)
{
m_enabledFlags[i] = enabledNames.Contains(m_values[i].Value);
}
}
base.OnValueUpdated();
}
public override void RefreshUIForValue()
@ -48,6 +46,8 @@ namespace UnityExplorer.UI.InteractiveValues
GetDefaultLabel();
m_baseLabel.text = DefaultLabel;
base.RefreshUIForValue();
if (m_subContentConstructed)
{
for (int i = 0; i < m_values.Length; i++)

View File

@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using System.Reflection;
namespace UnityExplorer.UI.InteractiveValues
{
// Class for supporting any "float struct" (ie Vector, Rect, etc).
// Supports any struct where all the public instance fields are floats (or types assignable to float)
public class StructInfo
{
public string[] FieldNames { get; }
private readonly FieldInfo[] m_fields;
public StructInfo(Type type)
{
m_fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
.Where(it => !it.IsLiteral)
.ToArray();
FieldNames = m_fields.Select(it => it.Name)
.ToArray();
}
public object SetValue(ref object instance, int fieldIndex, float val)
{
m_fields[fieldIndex].SetValue(instance, val);
return instance;
}
public float GetValue(object instance, int fieldIndex)
=> (float)m_fields[fieldIndex].GetValue(instance);
public void RefreshUI(InputField[] inputs, object instance)
{
try
{
for (int i = 0; i < m_fields.Length; i++)
{
var field = m_fields[i];
float val = (float)field.GetValue(instance);
inputs[i].text = val.ToString();
}
}
catch (Exception ex)
{
ExplorerCore.Log(ex);
}
}
}
public class InteractiveFloatStruct : InteractiveValue
{
private static readonly Dictionary<string, bool> _typeSupportCache = new Dictionary<string, bool>();
public static bool IsTypeSupported(Type type)
{
if (!type.IsValueType)
return false;
if (string.IsNullOrEmpty(type.AssemblyQualifiedName))
return false;
if (_typeSupportCache.TryGetValue(type.AssemblyQualifiedName, out bool ret))
return ret;
ret = true;
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
foreach (var field in fields)
{
if (field.IsLiteral)
continue;
if (!typeof(float).IsAssignableFrom(field.FieldType))
{
ret = false;
break;
}
}
_typeSupportCache.Add(type.AssemblyQualifiedName, ret);
return ret;
}
//~~~~~~~~~ Instance ~~~~~~~~~~
public InteractiveFloatStruct(object value, Type valueType) : base(value, valueType) { }
public override bool HasSubContent => true;
public override bool SubContentWanted => true;
public StructInfo StructInfo;
public override void RefreshUIForValue()
{
InitializeStructInfo();
base.RefreshUIForValue();
if (m_subContentConstructed)
StructInfo.RefreshUI(m_inputs, this.Value);
}
internal override void OnToggleSubcontent(bool toggle)
{
InitializeStructInfo();
base.OnToggleSubcontent(toggle);
StructInfo.RefreshUI(m_inputs, this.Value);
}
internal Type m_lastStructType;
internal void InitializeStructInfo()
{
var type = Value?.GetType() ?? FallbackType;
if (StructInfo != null && type == m_lastStructType)
return;
if (StructInfo != null && m_subContentConstructed)
DestroySubContent();
m_lastStructType = type;
StructInfo = new StructInfo(type);
if (m_subContentParent.activeSelf)
ConstructSubcontent();
}
#region UI CONSTRUCTION
internal InputField[] m_inputs;
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
}
public override void ConstructSubcontent()
{
base.ConstructSubcontent();
if (StructInfo == null)
{
ExplorerCore.LogWarning("Setting up subcontent but structinfo is null");
return;
}
var editorContainer = UIFactory.CreateVerticalGroup(m_subContentParent, "EditorContent", false, true, true, true, 2, new Vector4(4, 4, 4, 4),
new Color(0.08f, 0.08f, 0.08f));
m_inputs = new InputField[StructInfo.FieldNames.Length];
for (int i = 0; i < StructInfo.FieldNames.Length; i++)
AddEditorRow(i, editorContainer);
RefreshUIForValue();
}
internal void AddEditorRow(int index, GameObject groupObj)
{
try
{
var row = UIFactory.CreateHorizontalGroup(groupObj, "EditorRow", false, true, true, true, 5, default, new Color(1, 1, 1, 0));
string name = StructInfo.FieldNames[index];
var label = UIFactory.CreateLabel(row, "RowLabel", $"{name}:", TextAnchor.MiddleRight, Color.cyan);
UIFactory.SetLayoutElement(label.gameObject, minWidth: 30, flexibleWidth: 0, minHeight: 25);
var inputFieldObj = UIFactory.CreateInputField(row, "InputField", "...", 14, 3, 1);
UIFactory.SetLayoutElement(inputFieldObj, minWidth: 120, minHeight: 25, flexibleWidth: 0);
var inputField = inputFieldObj.GetComponent<InputField>();
m_inputs[index] = inputField;
inputField.onValueChanged.AddListener((string val) =>
{
try
{
float f = float.Parse(val);
Value = StructInfo.SetValue(ref this.Value, index, f);
Owner.SetValue();
}
catch { }
});
}
catch (Exception ex)
{
ExplorerCore.Log(ex);
}
}
#endregion
}
}

View File

@ -9,6 +9,7 @@ using UnityExplorer.Core.Unity;
using UnityExplorer.UI;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.Core.Runtime;
namespace UnityExplorer.UI.InteractiveValues
{
@ -23,6 +24,9 @@ namespace UnityExplorer.UI.InteractiveValues
public override void OnValueUpdated()
{
if (!(Value is string) && Value != null)
Value = RuntimeProvider.Instance.Reflection.UnboxString(Value);
base.OnValueUpdated();
}
@ -88,10 +92,18 @@ namespace UnityExplorer.UI.InteractiveValues
m_labelLayout.flexibleWidth = 0;
}
internal void OnApplyClicked()
internal void SetValueFromInput()
{
Value = m_valueInput.text;
if (!typeof(string).IsAssignableFrom(Owner.FallbackType))
ReflectionProvider.Instance.BoxStringToType(ref Value, Owner.FallbackType);
Owner.SetValue();
// revert back to string now
OnValueUpdated();
RefreshUIForValue();
}
@ -162,7 +174,7 @@ namespace UnityExplorer.UI.InteractiveValues
if (Owner.CanWrite)
{
var apply = UIFactory.CreateButton(groupObj, "ApplyButton", "Apply", OnApplyClicked, new Color(0.2f, 0.2f, 0.2f));
var apply = UIFactory.CreateButton(groupObj, "ApplyButton", "Apply", SetValueFromInput, new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(apply.gameObject, minWidth: 50, minHeight: 25, flexibleWidth: 0);
}
else

View File

@ -1,298 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Unity;
using UnityExplorer.UI;
namespace UnityExplorer.UI.InteractiveValues
{
#region IStructInfo helper
public interface IStructInfo
{
string[] FieldNames { get; }
object SetValue(ref object value, int fieldIndex, float val);
void RefreshUI(InputField[] inputs, object value);
}
public class StructInfo<T> : IStructInfo where T : struct
{
public string[] FieldNames { get; set; }
public delegate void SetMethod(ref T value, int fieldIndex, float val);
public SetMethod SetValueMethod;
public delegate void UpdateMethod(InputField[] inputs, object value);
public UpdateMethod UpdateUIMethod;
public object SetValue(ref object value, int fieldIndex, float val)
{
var box = (T)value;
SetValueMethod.Invoke(ref box, fieldIndex, val);
return box;
}
public void RefreshUI(InputField[] inputs, object value)
{
UpdateUIMethod.Invoke(inputs, value);
}
}
// This part is a bit ugly, but everything else is generalized above.
// I could generalize it more with reflection, but it would be different for
// mono/il2cpp and also slower.
public static class StructInfoFactory
{
public static IStructInfo Create(Type type)
{
if (type == typeof(Vector2))
{
return new StructInfo<Vector2>()
{
FieldNames = new[] { "x", "y", },
SetValueMethod = (ref Vector2 vec, int fieldIndex, float val) =>
{
switch (fieldIndex)
{
case 0: vec.x = val; break;
case 1: vec.y = val; break;
}
},
UpdateUIMethod = (InputField[] inputs, object value) =>
{
Vector2 vec = (Vector2)value;
inputs[0].text = vec.x.ToString();
inputs[1].text = vec.y.ToString();
}
};
}
else if (type == typeof(Vector3))
{
return new StructInfo<Vector3>()
{
FieldNames = new[] { "x", "y", "z" },
SetValueMethod = (ref Vector3 vec, int fieldIndex, float val) =>
{
switch (fieldIndex)
{
case 0: vec.x = val; break;
case 1: vec.y = val; break;
case 2: vec.z = val; break;
}
},
UpdateUIMethod = (InputField[] inputs, object value) =>
{
Vector3 vec = (Vector3)value;
inputs[0].text = vec.x.ToString();
inputs[1].text = vec.y.ToString();
inputs[2].text = vec.z.ToString();
}
};
}
else if (type == typeof(Vector4))
{
return new StructInfo<Vector4>()
{
FieldNames = new[] { "x", "y", "z", "w" },
SetValueMethod = (ref Vector4 vec, int fieldIndex, float val) =>
{
switch (fieldIndex)
{
case 0: vec.x = val; break;
case 1: vec.y = val; break;
case 2: vec.z = val; break;
case 3: vec.w = val; break;
}
},
UpdateUIMethod = (InputField[] inputs, object value) =>
{
Vector4 vec = (Vector4)value;
inputs[0].text = vec.x.ToString();
inputs[1].text = vec.y.ToString();
inputs[2].text = vec.z.ToString();
inputs[3].text = vec.w.ToString();
}
};
}
else if (type == typeof(Rect))
{
return new StructInfo<Rect>()
{
FieldNames = new[] { "x", "y", "width", "height" },
SetValueMethod = (ref Rect vec, int fieldIndex, float val) =>
{
switch (fieldIndex)
{
case 0: vec.x = val; break;
case 1: vec.y = val; break;
case 2: vec.width = val; break;
case 3: vec.height = val; break;
}
},
UpdateUIMethod = (InputField[] inputs, object value) =>
{
Rect vec = (Rect)value;
inputs[0].text = vec.x.ToString();
inputs[1].text = vec.y.ToString();
inputs[2].text = vec.width.ToString();
inputs[3].text = vec.height.ToString();
}
};
}
else if (type == typeof(Color))
{
return new StructInfo<Color>()
{
FieldNames = new[] { "r", "g", "b", "a" },
SetValueMethod = (ref Color vec, int fieldIndex, float val) =>
{
switch (fieldIndex)
{
case 0: vec.r = val; break;
case 1: vec.g = val; break;
case 2: vec.b = val; break;
case 3: vec.a = val; break;
}
},
UpdateUIMethod = (InputField[] inputs, object value) =>
{
Color vec = (Color)value;
inputs[0].text = vec.r.ToString();
inputs[1].text = vec.g.ToString();
inputs[2].text = vec.b.ToString();
inputs[3].text = vec.a.ToString();
}
};
}
else
throw new NotImplementedException();
}
}
#endregion
public class InteractiveUnityStruct : InteractiveValue
{
public static bool SupportsType(Type type) => s_supportedTypes.Contains(type);
private static readonly HashSet<Type> s_supportedTypes = new HashSet<Type>
{
typeof(Vector2),
typeof(Vector3),
typeof(Vector4),
typeof(Rect),
//typeof(Color) // todo might make a special editor for colors
};
//~~~~~~~~~ Instance ~~~~~~~~~~
public InteractiveUnityStruct(object value, Type valueType) : base(value, valueType) { }
public override bool HasSubContent => true;
public override bool SubContentWanted => true;
public override bool WantInspectBtn => true;
public IStructInfo StructInfo;
public override void RefreshUIForValue()
{
InitializeStructInfo();
base.RefreshUIForValue();
if (m_subContentConstructed)
StructInfo.RefreshUI(m_inputs, this.Value);
}
internal override void OnToggleSubcontent(bool toggle)
{
InitializeStructInfo();
base.OnToggleSubcontent(toggle);
StructInfo.RefreshUI(m_inputs, this.Value);
}
internal Type m_lastStructType;
internal void InitializeStructInfo()
{
var type = Value?.GetType() ?? FallbackType;
if (StructInfo != null && type == m_lastStructType)
return;
if (StructInfo != null)
DestroySubContent();
m_lastStructType = type;
StructInfo = StructInfoFactory.Create(type);
if (m_subContentParent.activeSelf)
{
ConstructSubcontent();
}
}
#region UI CONSTRUCTION
internal InputField[] m_inputs;
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
}
public override void ConstructSubcontent()
{
base.ConstructSubcontent();
if (StructInfo == null)
{
ExplorerCore.LogWarning("Setting up subcontent but structinfo is null");
return;
}
var editorContainer = UIFactory.CreateVerticalGroup(m_subContentParent, "EditorContent", false, true, true, true, 2, new Vector4(4,4,4,4),
new Color(0.08f, 0.08f, 0.08f));
m_inputs = new InputField[StructInfo.FieldNames.Length];
for (int i = 0; i < StructInfo.FieldNames.Length; i++)
AddEditorRow(i, editorContainer);
if (Owner.CanWrite)
{
var applyBtn = UIFactory.CreateButton(editorContainer, "ApplyButton", "Apply", OnSetValue, new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(applyBtn.gameObject, minWidth: 175, minHeight: 25, flexibleWidth: 0);
void OnSetValue()
{
Owner.SetValue();
RefreshUIForValue();
}
}
}
internal void AddEditorRow(int index, GameObject groupObj)
{
var rowObj = UIFactory.CreateHorizontalGroup(groupObj, "EditorRow", false, true, true, true, 5, default, new Color(1, 1, 1, 0));
var label = UIFactory.CreateLabel(rowObj, "RowLabel", $"{StructInfo.FieldNames[index]}:", TextAnchor.MiddleRight, Color.cyan);
UIFactory.SetLayoutElement(label.gameObject, minWidth: 50, flexibleWidth: 0, minHeight: 25);
var inputFieldObj = UIFactory.CreateInputField(rowObj, "InputField", "...", 14, 3, 1);
UIFactory.SetLayoutElement(inputFieldObj, minWidth: 120, minHeight: 25, flexibleWidth: 0);
var inputField = inputFieldObj.GetComponent<InputField>();
m_inputs[index] = inputField;
inputField.onValueChanged.AddListener((string val) => { Value = StructInfo.SetValue(ref this.Value, index, float.Parse(val)); });
}
#endregion
}
}

View File

@ -12,6 +12,7 @@ using UnityExplorer.UI;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.Main.Home;
using UnityExplorer.UI.Inspectors;
namespace UnityExplorer.UI.InteractiveValues
{
@ -30,8 +31,8 @@ namespace UnityExplorer.UI.InteractiveValues
// arbitrarily check some types, fastest methods first.
if (type == typeof(bool))
return typeof(InteractiveBool);
// if type is primitive then it must be a number if its not a bool
else if (type.IsPrimitive)
// if type is primitive then it must be a number if its not a bool. Also check for decimal.
else if (type.IsPrimitive || type == typeof(decimal))
return typeof(InteractiveNumber);
// check for strings
else if (type == typeof(string))
@ -48,8 +49,8 @@ namespace UnityExplorer.UI.InteractiveValues
// check for unity struct types
else if (typeof(Color).IsAssignableFrom(type))
return typeof(InteractiveColor);
else if (InteractiveUnityStruct.SupportsType(type))
return typeof(InteractiveUnityStruct);
else if (InteractiveFloatStruct.IsTypeSupported(type))
return typeof(InteractiveFloatStruct);
// check Transform, force InteractiveValue so they dont become InteractiveEnumerables.
else if (typeof(Transform).IsAssignableFrom(type))
return typeof(InteractiveValue);
@ -66,7 +67,7 @@ namespace UnityExplorer.UI.InteractiveValues
public static InteractiveValue Create(object value, Type fallbackType)
{
var type = ReflectionUtility.GetType(value) ?? fallbackType;
var type = ReflectionUtility.GetActualType(value) ?? fallbackType;
var iType = GetIValueForType(type);
return (InteractiveValue)Activator.CreateInstance(iType, new object[] { value, type });

View File

@ -7,9 +7,19 @@ using UnityEngine.UI;
namespace UnityExplorer.UI.Main
{
public enum MenuPages
{
Home,
Search,
CSConsole,
Options
}
public abstract class BaseMenuPage
{
public abstract string Name { get; }
public abstract MenuPages Type { get; }
public bool WasDisabled { get; internal set; }
public GameObject Content;
public Button RefNavbarButton { get; set; }
@ -20,7 +30,6 @@ namespace UnityExplorer.UI.Main
set => Content?.SetActive(true);
}
public abstract bool Init();
public abstract void Update();
}

View File

@ -271,12 +271,10 @@ namespace UnityExplorer.UI.Main.CSConsole
mainGroup.childForceExpandHeight = false;
mainGroup.childForceExpandWidth = true;
ColorBlock btnColors = new ColorBlock();
RuntimeProvider.Instance.SetColorBlock(btnColors, new Color(0, 0, 0, 0), highlighted: new Color(0.2f, 0.2f, 0.2f, 1.0f));
for (int i = 0; i < MAX_LABELS; i++)
{
var btn = UIFactory.CreateButton(content, "AutoCompleteButton", "", null, btnColors);
var btn = UIFactory.CreateButton(content, "AutoCompleteButton", "", null);
RuntimeProvider.Instance.SetColorBlock(btn, new Color(0, 0, 0, 0), highlighted: new Color(0.2f, 0.2f, 0.2f, 1.0f));
var nav = btn.navigation;
nav.mode = Navigation.Mode.Vertical;

View File

@ -13,15 +13,13 @@ using UnityExplorer.UI.Main.CSConsole;
using UnityExplorer.Core;
using UnityExplorer.Core.Unity;
using UnityExplorer.UI.Utility;
#if CPP
using UnityExplorer.Core.Runtime.Il2Cpp;
#endif
namespace UnityExplorer.UI.Main.CSConsole
{
public class CSharpConsole : BaseMenuPage
{
public override string Name => "C# Console";
public override MenuPages Type => MenuPages.CSConsole;
public static CSharpConsole Instance { get; private set; }
@ -53,13 +51,10 @@ namespace UnityExplorer.UI.Main.CSConsole
InitConsole();
AutoCompleter.Init();
#if MONO
DummyBehaviour.Setup();
#endif
ResetConsole(false);
// Make sure compiler is supported on this platform
Evaluator.Compile("");
Evaluator.Compile("new object();");
return true;
}
@ -73,6 +68,8 @@ namespace UnityExplorer.UI.Main.CSConsole
ExplorerCore.LogWarning(info);
this.RefNavbarButton.GetComponentInChildren<Text>().text += " (disabled)";
return false;
}
}
@ -100,9 +97,6 @@ namespace UnityExplorer.UI.Main.CSConsole
UpdateConsole();
AutoCompleter.Update();
#if CPP
Il2CppCoroutine.Process();
#endif
}
public void AddUsing(string asm)

View File

@ -217,7 +217,6 @@ namespace UnityExplorer.UI.Main
{
LogUnity = val;
ConfigManager.Log_Unity_Debug.Value = val;
ConfigManager.Handler.SaveConfig();
});
ConfigManager.Log_Unity_Debug.OnValueChanged += (bool val) => { unityToggle.isOn = val; };

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors;
namespace UnityExplorer.UI.Main.Home
{
@ -12,6 +13,8 @@ namespace UnityExplorer.UI.Main.Home
public static HomePage Instance { get; internal set; }
public override MenuPages Type => MenuPages.Home;
public override bool Init()
{
Instance = this;

View File

@ -13,6 +13,7 @@ using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Main.Search;
using System.IO;
using UnityExplorer.Core;
using UnityExplorer.UI.Inspectors;
namespace UnityExplorer.UI.Main.Home
{
@ -521,15 +522,13 @@ namespace UnityExplorer.UI.Main.Home
m_shortListToggles.Add(toggle);
toggle.onValueChanged.AddListener((bool val) => { OnToggleClicked(thisIndex, val); });
ColorBlock mainColors = new ColorBlock();
mainColors = RuntimeProvider.Instance.SetColorBlock(mainColors, new Color(0.1f, 0.1f, 0.1f),
new Color(0.2f, 0.2f, 0.2f), new Color(0.05f, 0.05f, 0.05f));
var mainButton = UIFactory.CreateButton(btnGroupObj,
"MainButton",
"",
() => { SceneListObjectClicked(thisIndex); },
mainColors);
() => { SceneListObjectClicked(thisIndex); });
RuntimeProvider.Instance.SetColorBlock(mainButton, new Color(0.1f, 0.1f, 0.1f),
new Color(0.2f, 0.2f, 0.2f), new Color(0.05f, 0.05f, 0.05f));
UIFactory.SetLayoutElement(mainButton.gameObject, minHeight: 25, minWidth: 230);
@ -538,15 +537,13 @@ namespace UnityExplorer.UI.Main.Home
mainText.horizontalOverflow = HorizontalWrapMode.Overflow;
m_shortListTexts.Add(mainText);
ColorBlock inspectColors = new ColorBlock();
inspectColors = RuntimeProvider.Instance.SetColorBlock(inspectColors, new Color(0.15f, 0.15f, 0.15f),
new Color(0.2f, 0.2f, 0.2f), new Color(0.1f, 0.1f, 0.1f));
var inspectButton = UIFactory.CreateButton(btnGroupObj,
"InspectButton",
"Inspect",
() => { InspectorManager.Instance.Inspect(m_shortList[thisIndex]); },
inspectColors);
() => { InspectorManager.Instance.Inspect(m_shortList[thisIndex]); });
RuntimeProvider.Instance.SetColorBlock(inspectButton, new Color(0.15f, 0.15f, 0.15f),
new Color(0.2f, 0.2f, 0.2f), new Color(0.1f, 0.1f, 0.1f));
UIFactory.SetLayoutElement(inspectButton.gameObject, minWidth: 60, minHeight: 25);
}

View File

@ -26,7 +26,7 @@ namespace UnityExplorer.UI.Main
public readonly List<BaseMenuPage> Pages = new List<BaseMenuPage>();
private BaseMenuPage m_activePage;
public static Action<int> OnActiveTabChanged;
public static Action<MenuPages> OnActiveTabChanged;
// Navbar buttons
private Button m_lastNavButtonPressed;
@ -38,8 +38,6 @@ namespace UnityExplorer.UI.Main
internal bool pageLayoutInit;
internal int layoutInitIndex;
private int origDesiredPage = -1;
public static void Create()
{
if (Instance != null)
@ -67,6 +65,7 @@ namespace UnityExplorer.UI.Main
if (!page.Init())
{
page.WasDisabled = true;
// page init failed.
Pages.RemoveAt(i);
i--;
@ -88,9 +87,6 @@ namespace UnityExplorer.UI.Main
{
if (!pageLayoutInit)
{
if (origDesiredPage == -1)
origDesiredPage = ConfigManager.Last_Active_Tab?.Value ?? 0;
if (layoutInitIndex < Pages.Count)
{
SetPage(Pages[layoutInitIndex]);
@ -100,7 +96,8 @@ namespace UnityExplorer.UI.Main
{
pageLayoutInit = true;
MainPanel.transform.position = initPos;
SetPage(Pages[origDesiredPage]);
SetPage(ConfigManager.Default_Tab.Value);
}
return;
}
@ -108,9 +105,17 @@ namespace UnityExplorer.UI.Main
m_activePage?.Update();
}
public void SetPage(MenuPages page)
{
var pageObj = Pages.Find(it => it.Type == page);
if (pageObj == null || pageObj.WasDisabled)
return;
SetPage(pageObj);
}
public void SetPage(BaseMenuPage page)
{
if (page == null || m_activePage == page)
if (page == null || m_activePage == page || page.WasDisabled)
return;
m_activePage?.Content?.SetActive(false);
@ -131,17 +136,17 @@ namespace UnityExplorer.UI.Main
m_lastNavButtonPressed = button;
OnActiveTabChanged?.Invoke(Pages.IndexOf(m_activePage));
OnActiveTabChanged?.Invoke(m_activePage.Type);
}
internal void SetButtonActiveColors(Button button)
{
button.colors = RuntimeProvider.Instance.SetColorBlock(button.colors, m_navButtonSelected);
RuntimeProvider.Instance.SetColorBlock(button, m_navButtonSelected);
}
internal void SetButtonInactiveColors(Button button)
{
button.colors = RuntimeProvider.Instance.SetColorBlock(button.colors, m_navButtonNormal);
RuntimeProvider.Instance.SetColorBlock(button, m_navButtonNormal);
}
#region UI Construction
@ -179,15 +184,13 @@ namespace UnityExplorer.UI.Main
// Hide button
ColorBlock colorBlock = new ColorBlock();
RuntimeProvider.Instance.SetColorBlock(colorBlock, new Color(65f / 255f, 23f / 255f, 23f / 255f),
new Color(35f / 255f, 10f / 255f, 10f / 255f), new Color(156f / 255f, 0f, 0f));
var hideButton = UIFactory.CreateButton(titleBar,
"HideButton",
$"Hide ({ConfigManager.Main_Menu_Toggle.Value})",
() => { UIManager.ShowMenu = false; },
colorBlock);
() => { UIManager.ShowMenu = false; });
RuntimeProvider.Instance.SetColorBlock(hideButton, new Color(65f / 255f, 23f / 255f, 23f / 255f),
new Color(35f / 255f, 10f / 255f, 10f / 255f), new Color(156f / 255f, 0f, 0f));
UIFactory.SetLayoutElement(hideButton.gameObject, minWidth: 90, flexibleWidth: 0);
@ -208,16 +211,14 @@ namespace UnityExplorer.UI.Main
GameObject navbarObj = UIFactory.CreateHorizontalGroup(content, "MainNavBar", true, true, true, true, 5);
UIFactory.SetLayoutElement(navbarObj, minHeight: 25, flexibleHeight: 0);
ColorBlock colorBlock = new ColorBlock();
colorBlock = RuntimeProvider.Instance.SetColorBlock(colorBlock, m_navButtonNormal, m_navButtonHighlight, m_navButtonSelected);
foreach (var page in Pages)
{
Button btn = UIFactory.CreateButton(navbarObj,
$"Button_{page.Name}",
page.Name,
() => { SetPage(page); },
colorBlock);
() => { SetPage(page); });
RuntimeProvider.Instance.SetColorBlock(btn, m_navButtonNormal, m_navButtonHighlight, m_navButtonSelected);
page.RefNavbarButton = btn;
}

View File

@ -12,6 +12,7 @@ namespace UnityExplorer.UI.Main.Options
public class OptionsPage : BaseMenuPage
{
public override string Name => "Options";
public override MenuPages Type => MenuPages.Options;
internal static readonly List<CacheConfigEntry> _cachedConfigEntries = new List<CacheConfigEntry>();

View File

@ -10,12 +10,14 @@ using UnityExplorer.Core;
using UnityExplorer.UI.Utility;
using UnityExplorer.Core.Search;
using UnityExplorer.UI.Main.Home;
using UnityExplorer.UI.Inspectors;
namespace UnityExplorer.UI.Main.Search
{
public class SearchPage : BaseMenuPage
{
public override string Name => "Search";
public override MenuPages Type => MenuPages.Search;
public static SearchPage Instance;
@ -129,7 +131,7 @@ namespace UnityExplorer.UI.Main.Search
if (m_context != SearchContext.StaticClass)
{
var name = SignatureHighlighter.ParseFullSyntax(ReflectionUtility.GetType(obj), true);
var name = SignatureHighlighter.ParseFullSyntax(ReflectionUtility.GetActualType(obj), true);
if (unityObj && m_context != SearchContext.Singleton)
{
@ -259,7 +261,7 @@ namespace UnityExplorer.UI.Main.Search
m_selectedContextButton = button;
m_selectedContextButton.colors = RuntimeProvider.Instance.SetColorBlock(m_selectedContextButton.colors,
RuntimeProvider.Instance.SetColorBlock(m_selectedContextButton,
new Color(0.35f, 0.7f, 0.35f), new Color(0.35f, 0.7f, 0.35f));
m_context = context;
@ -437,15 +439,13 @@ namespace UnityExplorer.UI.Main.Search
UIFactory.SetLayoutElement(btnGroupObj, flexibleWidth: 320, minHeight: 25, flexibleHeight: 0);
btnGroupObj.AddComponent<Mask>();
var mainColors = new ColorBlock();
RuntimeProvider.Instance.SetColorBlock(mainColors, new Color(0.1f, 0.1f, 0.1f),
new Color(0.2f, 0.2f, 0.2f), new Color(0.05f, 0.05f, 0.05f));
var mainButton = UIFactory.CreateButton(btnGroupObj,
"ResultButton",
"",
() => { OnResultClicked(thisIndex); },
mainColors);
() => { OnResultClicked(thisIndex); });
RuntimeProvider.Instance.SetColorBlock(mainButton, new Color(0.1f, 0.1f, 0.1f),
new Color(0.2f, 0.2f, 0.2f), new Color(0.05f, 0.05f, 0.05f));
UIFactory.SetLayoutElement(mainButton.gameObject, minHeight: 25, flexibleHeight: 0, minWidth: 320, flexibleWidth: 0);

View File

@ -52,7 +52,7 @@ namespace UnityExplorer.UI
internal static void SetDefaultSelectableColors(Selectable selectable)
{
selectable.colors = RuntimeProvider.Instance.SetColorBlock(selectable.colors, new Color(0.2f, 0.2f, 0.2f),
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));
// Deselect all Buttons after they are clicked.
@ -60,19 +60,6 @@ namespace UnityExplorer.UI
button.onClick.AddListener(() => { button.OnDeselect(null); });
}
//public static void SetColorBlockValues(ref this ColorBlock colorBlock, Color? normal = null, Color? highlighted = null,
// Color? pressed = null)
//{
// if (normal != null)
// colorBlock.normalColor = (Color)normal;
// if (highlighted != null)
// colorBlock.highlightedColor = (Color)highlighted;
// if (pressed != null)
// colorBlock.pressedColor = (Color)pressed;
//}
/// <summary>
/// Get and/or Add a LayoutElement component to the GameObject, and set any of the values on it.
/// </summary>
@ -276,10 +263,12 @@ namespace UnityExplorer.UI
{
var colors = new ColorBlock();
normalColor = normalColor ?? new Color(0.25f, 0.25f, 0.25f);
colors = RuntimeProvider.Instance.SetColorBlock(colors, normalColor, new Color(0.4f, 0.4f, 0.4f),
new Color(0.15f, 0.15f, 0.15f));
return CreateButton(parent, name, text, onClick, colors);
var btn = CreateButton(parent, name, text, onClick, colors);
RuntimeProvider.Instance.SetColorBlock(btn, normalColor, new Color(0.4f, 0.4f, 0.4f), new Color(0.15f, 0.15f, 0.15f));
return btn;
}
public static Button CreateButton(GameObject parent, string name, string text, Action onClick, ColorBlock colors)
@ -296,7 +285,7 @@ namespace UnityExplorer.UI
SetDefaultSelectableColors(button);
colors.colorMultiplier = 1;
button.colors = colors;
RuntimeProvider.Instance.SetColorBlock(button, colors);
Text textComp = textObj.AddComponent<Text>();
textComp.text = text;
@ -364,7 +353,7 @@ namespace UnityExplorer.UI
slider.targetGraphic = handleImage;
slider.direction = Slider.Direction.LeftToRight;
slider.colors = RuntimeProvider.Instance.SetColorBlock(slider.colors, new Color(0.4f, 0.4f, 0.4f),
RuntimeProvider.Instance.SetColorBlock(slider, new Color(0.4f, 0.4f, 0.4f),
new Color(0.55f, 0.55f, 0.55f), new Color(0.3f, 0.3f, 0.3f));
return sliderObj;
@ -504,7 +493,7 @@ namespace UnityExplorer.UI
mainInput.transition = Selectable.Transition.ColorTint;
mainInput.targetGraphic = mainImage;
mainInput.colors = RuntimeProvider.Instance.SetColorBlock(mainInput.colors, new Color(1, 1, 1, 1),
RuntimeProvider.Instance.SetColorBlock(mainInput, new Color(1, 1, 1, 1),
new Color(0.95f, 0.95f, 0.95f, 1.0f), new Color(0.78f, 0.78f, 0.78f, 1.0f));
SetLayoutGroup<VerticalLayoutGroup>(mainObj, true, true, true, true);
@ -609,7 +598,7 @@ namespace UnityExplorer.UI
Toggle itemToggle = itemObj.AddComponent<Toggle>();
itemToggle.targetGraphic = itemBgImage;
itemToggle.isOn = true;
itemToggle.colors = RuntimeProvider.Instance.SetColorBlock(itemToggle.colors,
RuntimeProvider.Instance.SetColorBlock(itemToggle,
new Color(0.35f, 0.35f, 0.35f, 1.0f), new Color(0.25f, 0.45f, 0.25f, 1.0f));
itemToggle.onValueChanged.AddListener((bool val) => { itemToggle.OnDeselect(null); });

View File

@ -53,8 +53,7 @@ namespace UnityExplorer.UI
// Force refresh of anchors etc
Canvas.ForceUpdateCanvases();
if (!ConfigManager.Hide_On_Startup.Value)
ShowMenu = true;
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
ExplorerCore.Log("UI initialized.");
}
@ -141,6 +140,8 @@ namespace UnityExplorer.UI
ExplorerCore.Log("This game does not ship with the 'UI/Default' shader, using manual Default Shader...");
Graphic.defaultGraphicMaterial.shader = BackupShader;
}
else
BackupShader = Graphic.defaultGraphicMaterial.shader;
ConsoleFont = bundle.LoadAsset<Font>("CONSOLA");
@ -149,9 +150,11 @@ namespace UnityExplorer.UI
private static AssetBundle LoadExplorerUi(string id)
{
var data = ReadFully(typeof(ExplorerCore)
var stream = typeof(ExplorerCore)
.Assembly
.GetManifestResourceStream($"UnityExplorer.Resources.explorerui.{id}.bundle"));
.GetManifestResourceStream($"UnityExplorer.Resources.explorerui.{id}.bundle");
var data = ReadFully(stream);
return AssetBundle.LoadFromMemory(data);
}

View File

@ -219,7 +219,7 @@
<Compile Include="Core\Config\ConfigManager.cs" />
<Compile Include="Core\Config\IConfigElement.cs" />
<Compile Include="Core\Config\ConfigHandler.cs" />
<Compile Include="Core\CSharp\DummyBehaviour.cs" />
<Compile Include="Core\Runtime\Mono\DummyBehaviour.cs" />
<Compile Include="Core\CSharp\ScriptEvaluator.cs" />
<Compile Include="Core\CSharp\ScriptInteraction.cs" />
<Compile Include="Core\CSharp\Suggestion.cs" />
@ -246,6 +246,7 @@
<Compile Include="Core\Search\SceneFilter.cs" />
<Compile Include="Core\Search\SearchContext.cs" />
<Compile Include="Core\Search\SearchProvider.cs" />
<Compile Include="Core\TestClass.cs" />
<Compile Include="Core\Unity\UnityHelpers.cs" />
<Compile Include="Loader\BIE\BepInExConfigHandler.cs" />
<Compile Include="Loader\BIE\ExplorerBepInPlugin.cs" />
@ -272,7 +273,7 @@
<Compile Include="UI\InteractiveValues\InteractiveFlags.cs" />
<Compile Include="UI\InteractiveValues\InteractiveNumber.cs" />
<Compile Include="UI\InteractiveValues\InteractiveString.cs" />
<Compile Include="UI\InteractiveValues\InteractiveUnityStruct.cs" />
<Compile Include="UI\InteractiveValues\InteractiveFloatStruct.cs" />
<Compile Include="UI\InteractiveValues\InteractiveValue.cs" />
<Compile Include="UI\Main\BaseMenuPage.cs" />
<Compile Include="UI\Main\CSConsole\AutoCompleter.cs" />
@ -286,16 +287,16 @@
<Compile Include="UI\Main\CSConsole\Lexer\SymbolMatch.cs" />
<Compile Include="UI\Main\DebugConsole.cs" />
<Compile Include="UI\Main\Home\HomePage.cs" />
<Compile Include="UI\Main\Home\InspectorManager.cs" />
<Compile Include="UI\Main\Home\Inspectors\GameObjects\ChildList.cs" />
<Compile Include="UI\Main\Home\Inspectors\GameObjects\ComponentList.cs" />
<Compile Include="UI\Main\Home\Inspectors\GameObjects\GameObjectControls.cs" />
<Compile Include="UI\Main\Home\Inspectors\GameObjects\GameObjectInspector.cs" />
<Compile Include="UI\Main\Home\Inspectors\InspectorBase.cs" />
<Compile Include="UI\Main\Home\Inspectors\Reflection\InstanceInspector.cs" />
<Compile Include="UI\Main\Home\Inspectors\Reflection\ReflectionInspector.cs" />
<Compile Include="UI\Main\Home\Inspectors\Reflection\StaticInspector.cs" />
<Compile Include="UI\Main\Home\InspectUnderMouse.cs" />
<Compile Include="UI\Inspectors\InspectorManager.cs" />
<Compile Include="UI\Inspectors\GameObjects\ChildList.cs" />
<Compile Include="UI\Inspectors\GameObjects\ComponentList.cs" />
<Compile Include="UI\Inspectors\GameObjects\GameObjectControls.cs" />
<Compile Include="UI\Inspectors\GameObjects\GameObjectInspector.cs" />
<Compile Include="UI\Inspectors\InspectorBase.cs" />
<Compile Include="UI\Inspectors\Reflection\InstanceInspector.cs" />
<Compile Include="UI\Inspectors\Reflection\ReflectionInspector.cs" />
<Compile Include="UI\Inspectors\Reflection\StaticInspector.cs" />
<Compile Include="UI\Inspectors\InspectUnderMouse.cs" />
<Compile Include="UI\Main\Home\SceneExplorer.cs" />
<Compile Include="UI\Main\MainMenu.cs" />
<Compile Include="UI\Main\Options\OptionsPage.cs" />