Compare commits

..

20 Commits

Author SHA1 Message Date
1c0011bef9 bump version 2021-03-16 18:13:14 +11:00
9e996816ef starting work on a cleanup/rewrite 2021-03-16 18:12:39 +11:00
9665753dc8 fix BIE5 mono release 2021-03-16 18:12:26 +11:00
942e9d7555 some commenting 2021-03-14 16:08:53 +11:00
9efb9581f5 Cleanups, method summaries, bump version 2021-03-12 18:41:57 +11:00
f10a462b00 More reflection caching, use deobfuscated names for ToString labels 2021-03-12 18:41:38 +11:00
9072b16c5a Save window size between launches 2021-03-12 18:39:57 +11:00
21408993c2 create subfolder for standalone 2021-03-11 18:40:04 +11:00
7a8b5b50d1 bump version, update readme 2021-03-11 18:32:57 +11:00
1a5e843070 handle ExplorerStandalone update internally 2021-03-11 18:16:52 +11:00
ade7539fde cleanups 2021-03-11 17:57:58 +11:00
5c588e5a03 Update README.md 2021-03-10 05:04:43 +11:00
af094832fe Update README.md 2021-03-09 22:31:26 +11:00
af0ee2e690 Update README.md 2021-03-09 22:29:53 +11:00
d2d6fb4d55 Update README.md 2021-03-09 22:28:43 +11:00
6c25662fe9 Update README.md 2021-03-09 17:36:49 +11:00
4bcf82ca10 separate Bep5 and Bep6 support. Fix enums not backed by ints. 2021-03-09 17:35:54 +11:00
ce38e8ac50 bump to MelonLoader 0.3.0 2021-03-06 17:25:54 +11:00
12cd718f12 3.1.12: store Il2CppToMonoType key as string AssemblyQualifiedName instead of Type object 2021-03-04 01:44:34 +11:00
995e2a3e93 3.1.11: fix potential crash on scene reload 2021-03-03 22:09:14 +11:00
40 changed files with 1013 additions and 559 deletions

View File

@ -24,9 +24,12 @@
| Mod Loader | IL2CPP | Mono | | Mod Loader | IL2CPP | Mono |
| ----------- | ------ | ---- | | ----------- | ------ | ---- |
| [BepInEx](https://github.com/BepInEx/BepInEx) | ✔️ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx.Il2Cpp.zip) | ✔️ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx.Mono.zip) | | [BepInEx](https://github.com/BepInEx/BepInEx) 6.X | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx.Il2Cpp.zip) | ❔* [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx6.Mono.zip) |
| [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) | ✔️ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Il2Cpp.zip) | ✔️ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Mono.zip) | | [BepInEx](https://github.com/BepInEx/BepInEx) 5.X | ❌ n/a | [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip) |
| Standalone | ✔️ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Il2Cpp.zip) | ✔️ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Mono.zip) | | [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3 | [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Il2Cpp.zip) | [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Mono.zip) |
| Standalone | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Mono.zip) |
\* BepInEx 6.X Mono release may not work on all games yet.
## Features ## Features
@ -41,12 +44,14 @@
* <b>Reflection Inspector</b>: Inspect Properties and Fields. Can also set primitive values and evaluate primitive methods. * <b>Reflection Inspector</b>: Inspect Properties and Fields. Can also set primitive values and evaluate primitive methods.
* <b>Search</b>: Search for UnityEngine.Objects with various filters, or use the helpers for static Instances and Classes. * <b>Search</b>: Search for UnityEngine.Objects with various filters, or use the helpers for static Instances and Classes.
* <b>C# Console</b>: Interactive console for evaluating C# methods on the fly, with some basic helpers. * <b>C# Console</b>: Interactive console for evaluating C# methods on the fly, with some basic helpers.
* <b>Inspect-under-mouse</b>: Hover over an object with a collider and inspect it by clicking on it. * <b>Inspect-under-mouse</b>: Hover over an object with a collider and inspect it by clicking on it. There's also a UI mode to inspect UI objects.
## How to install ## How to install
### BepInEx ### BepInEx
Note: 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).
0. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game. 0. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game.
1. Download the UnityExplorer release for BepInEx IL2CPP or Mono above. 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\` 2. Take the `UnityExplorer.BIE.___.dll` file and put it in `[GameFolder]\BepInEx\plugins\`
@ -54,20 +59,29 @@
### MelonLoader ### MelonLoader
Note: You must use version 0.3 of MelonLoader or greater. Version 0.3 is currently in pre-release, so you must opt-in from your MelonLoader installer (enable alpha releases).
0. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) for your game. 0. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) for your game.
1. Download the UnityExplorer release for MelonLoader IL2CPP or Mono above. 1. Download the UnityExplorer release for MelonLoader IL2CPP or Mono above.
2. Take the contents of the release and put it in the `[GameFolder]\Mods\` folder. It should look like `[GameFolder]\Mods\UnityExplorer.ML.___.dll` 2. Take the contents of the release and put it in the `[GameFolder]\Mods\` folder. It should look like `[GameFolder]\Mods\UnityExplorer.ML.___.dll`
### Standalone ### Standalone
0. Load the DLL from your mod or inject it. The standalone release is based on the BepInEx build, so it requires Harmony 2.0 (or HarmonyX) to function properly.
1. Create an instance of Unity Explorer with `new ExplorerCore();`
2. You will need to call ExplorerCore.Update() (static method) from your Update method.
3. Subscribe to the `ExplorerCore.OnLog__` methods for logging.
## Mod Config 0. Load the DLL from your mod or inject it. You must also make sure that the required libraries (Harmony, Unhollower for Il2Cpp, etc) are loaded.
1. Create an instance of Unity Explorer with `ExplorerStandalone.CreateInstance();`
2. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish.
You can access the settings via the "Options" page of the main menu, or directly from the config at `Mods\UnityExplorer\config.ini` (generated after first launch). ## Logging
Explorer saves all logs to disk (only keeps the most recent 10 logs). They can be found in a "UnityExplorer" folder in the same place as where you put the DLL file.
These logs are also visible in the Debug Console part of the UI.
## Settings
You can change the settings via the "Options" page of the main menu, or directly from the config file (generated after first launch). The config file will be found either inside a "UnityExplorer" folder in the same directory as where you put the DLL file, or for BepInEx it will be at `BepInEx\config\UnityExplorer\`.
`Main Menu Toggle` (KeyCode) `Main Menu Toggle` (KeyCode)
* Default: `F7` * Default: `F7`
@ -85,23 +99,30 @@ You can access the settings via the "Options" page of the main menu, or directly
`Default Output Path` (string) `Default Output Path` (string)
* Default: `Mods\UnityExplorer` * Default: `Mods\UnityExplorer`
* Where output is generated to, by default (for Texture PNG saving, etc). * Where output is generated to, by default (for Texture PNG saving, etc).
* Currently this is not actually used for anything, but it will be soon.
`Log Unity Debug` (bool) `Log Unity Debug` (bool)
* Default: `false` * Default: `false`
* Listens for Unity `Debug.Log` messages and prints them to UnityExplorer's log. * 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
If you'd like to build this yourself, you will need to have installed BepInEx and/or MelonLoader for at least one Unity game. If you want to build all 4 versions, you will need at least one IL2CPP and one Mono game, with BepInEx and MelonLoader installed for both. If you'd like to build this yourself, all you need to do is download this repository and build from Visual Studio. If you want to build for BepInEx or MelonLoader IL2CPP then you will need to install the mod loader for a game and set the directory in the `csproj` file.
1. Install BepInEx or MelonLoader for your game, or use the standalone build. For IL2CPP:
1. Install BepInEx or MelonLoader for your game.
2. Open the `src\UnityExplorer.csproj` file in a text editor. 2. Open the `src\UnityExplorer.csproj` file in a text editor.
3. For IL2CPP builds, make sure you set `BIECppGameFolder` (for BepInEx) and/or `MLCppGameFolder` (for MelonLoader) so the project can locate the necessary references. 3. Set `BIECppGameFolder` (for BepInEx) and/or `MLCppGameFolder` (for MelonLoader) so the project can locate the necessary references.
4. Open the `src\UnityExplorer.sln` project. 4. For Standalone builds, you can either install BepInEx for the game to build, or just change the .csproj file and set the Unhollower reference manually.
5. Select `Solution 'UnityExplorer' (1 of 1 project)` in the Solution Explorer panel, and set the <b>Active config</b> property to the version you want to build, then build it.
5. The DLLs are built to the `Release\` folder in the root of the repository. For all builds:
6. If ILRepack fails or is missing, use the NuGet package manager to re-install `ILRepack.Lib.MSBuild.Task`, then re-build. 1. Open the `src\UnityExplorer.sln` project.
2. Select `Solution 'UnityExplorer' (1 of 1 project)` in the Solution Explorer panel, and set the <b>Active config</b> property to the version you want to build, then build it.
3. The DLLs are built to the `Release\` folder in the root of the repository.
4. If ILRepack fails or is missing, use the NuGet package manager to re-install `ILRepack.Lib.MSBuild.Task`, then re-build.
## Credits ## Credits

BIN
lib/BepInEx.Unity.dll Normal file

Binary file not shown.

Binary file not shown.

BIN
lib/MelonLoader.dll Normal file

Binary file not shown.

View File

@ -3,29 +3,34 @@ using System.IO;
using UnityEngine; using UnityEngine;
using IniParser; using IniParser;
using IniParser.Parser; using IniParser.Parser;
using UnityExplorer.UI;
namespace UnityExplorer.Config namespace UnityExplorer.Config
{ {
public class ModConfig public class ExplorerConfig
{ {
public static ModConfig Instance; public static ExplorerConfig Instance;
internal static readonly IniDataParser _parser = new IniDataParser(); internal static readonly IniDataParser _parser = new IniDataParser();
internal static readonly string INI_PATH = Path.Combine(ExplorerCore.EXPLORER_FOLDER, "config.ini"); internal static readonly string INI_PATH = Path.Combine(ExplorerCore.Loader.ConfigFolder, "config.ini");
static ModConfig() static ExplorerConfig()
{ {
_parser.Configuration.CommentString = "#"; _parser.Configuration.CommentString = "#";
PanelDragger.OnFinishResize += PanelDragger_OnFinishResize;
} }
// Actual configs // Actual configs
public KeyCode Main_Menu_Toggle = KeyCode.F7; public KeyCode Main_Menu_Toggle = KeyCode.F7;
public bool Force_Unlock_Mouse = true; public bool Force_Unlock_Mouse = true;
public int Default_Page_Limit = 25; public int Default_Page_Limit = 25;
public string Default_Output_Path = ExplorerCore.EXPLORER_FOLDER + @"\Output"; public string Default_Output_Path = Path.Combine(ExplorerCore.EXPLORER_FOLDER, "Output");
public bool Log_Unity_Debug = false; public bool Log_Unity_Debug = false;
public bool Hide_On_Startup = false; public bool Hide_On_Startup = false;
//public bool Save_Logs_To_Disk = true; public string Window_Anchors = DEFAULT_WINDOW_ANCHORS;
private const string DEFAULT_WINDOW_ANCHORS = "0.25,0.1,0.78,0.95";
public static event Action OnConfigChanged; public static event Action OnConfigChanged;
@ -36,7 +41,7 @@ namespace UnityExplorer.Config
public static void OnLoad() public static void OnLoad()
{ {
Instance = new ModConfig(); Instance = new ExplorerConfig();
if (LoadSettings()) if (LoadSettings())
return; return;
@ -75,9 +80,9 @@ namespace UnityExplorer.Config
case nameof(Hide_On_Startup): case nameof(Hide_On_Startup):
Instance.Hide_On_Startup = bool.Parse(config.Value); Instance.Hide_On_Startup = bool.Parse(config.Value);
break; break;
//case nameof(Save_Logs_To_Disk): case nameof(Window_Anchors):
// Instance.Save_Logs_To_Disk = bool.Parse(config.Value); Instance.Window_Anchors = config.Value;
// break; break;
} }
} }
@ -97,9 +102,52 @@ namespace UnityExplorer.Config
sec.AddKey(nameof(Log_Unity_Debug), Instance.Log_Unity_Debug.ToString()); sec.AddKey(nameof(Log_Unity_Debug), Instance.Log_Unity_Debug.ToString());
sec.AddKey(nameof(Default_Output_Path), Instance.Default_Output_Path); sec.AddKey(nameof(Default_Output_Path), Instance.Default_Output_Path);
sec.AddKey(nameof(Hide_On_Startup), Instance.Hide_On_Startup.ToString()); sec.AddKey(nameof(Hide_On_Startup), Instance.Hide_On_Startup.ToString());
//sec.AddKey("Save_Logs_To_Disk", Instance.Save_Logs_To_Disk.ToString()); sec.AddKey(nameof(Window_Anchors), GetWindowAnchorsString());
if (!Directory.Exists(ExplorerCore.Loader.ConfigFolder))
Directory.CreateDirectory(ExplorerCore.Loader.ConfigFolder);
File.WriteAllText(INI_PATH, data.ToString()); File.WriteAllText(INI_PATH, data.ToString());
} }
// ============ Window Anchors specific stuff ============== //
private static void PanelDragger_OnFinishResize()
{
Instance.Window_Anchors = GetWindowAnchorsString();
SaveSettings();
}
internal Vector4 GetWindowAnchorsVector()
{
try
{
var split = Window_Anchors.Split(',');
Vector4 ret = Vector4.zero;
ret.x = float.Parse(split[0]);
ret.y = float.Parse(split[1]);
ret.z = float.Parse(split[2]);
ret.w = float.Parse(split[3]);
return ret;
}
catch
{
Window_Anchors = DEFAULT_WINDOW_ANCHORS;
return GetWindowAnchorsVector();
}
}
internal static string GetWindowAnchorsString()
{
try
{
var rect = PanelDragger.Instance.Panel;
return $"{rect.anchorMin.x},{rect.anchorMin.y},{rect.anchorMax.x},{rect.anchorMax.y}";
}
catch
{
return DEFAULT_WINDOW_ANCHORS;
}
}
} }
} }

View File

@ -7,6 +7,7 @@ using UnityExplorer.Config;
using UnityExplorer.Helpers; using UnityExplorer.Helpers;
using UnityExplorer.Input; using UnityExplorer.Input;
using UnityExplorer.Inspectors; using UnityExplorer.Inspectors;
using UnityExplorer.Runtime;
using UnityExplorer.UI; using UnityExplorer.UI;
using UnityExplorer.UI.Modules; using UnityExplorer.UI.Modules;
@ -15,43 +16,22 @@ namespace UnityExplorer
public class ExplorerCore public class ExplorerCore
{ {
public const string NAME = "UnityExplorer"; public const string NAME = "UnityExplorer";
public const string VERSION = "3.1.10"; public const string VERSION = "3.2.3";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer"; public const string GUID = "com.sinai.unityexplorer";
#if ML
public static string EXPLORER_FOLDER = Path.Combine("Mods", NAME);
#elif BIE
public static string EXPLORER_FOLDER = Path.Combine(BepInEx.Paths.ConfigPath, NAME);
#elif STANDALONE
public static string EXPLORER_FOLDER
{
get
{
if (s_explorerFolder == null)
{
s_explorerFolder = (new Uri(Assembly.GetExecutingAssembly().CodeBase)).AbsolutePath;
s_explorerFolder = Uri.UnescapeDataString(s_explorerFolder);
s_explorerFolder = Path.GetDirectoryName(s_explorerFolder);
}
return s_explorerFolder;
}
}
private static string s_explorerFolder;
#endif
public static ExplorerCore Instance { get; private set; } public static ExplorerCore Instance { get; private set; }
public static bool ShowMenu public static IExplorerLoader Loader =>
{ #if ML
get => s_showMenu; ExplorerMelonMod.Instance;
set => SetShowMenu(value); #elif BIE
} ExplorerBepInPlugin.Instance;
public static bool s_showMenu; #elif STANDALONE
ExplorerStandalone.Instance;
#endif
private static bool s_doneUIInit; public static string EXPLORER_FOLDER => Loader.ExplorerFolder;
private static float s_timeSinceStartup;
public ExplorerCore() public ExplorerCore()
{ {
@ -63,106 +43,33 @@ namespace UnityExplorer
Instance = this; Instance = this;
#if CPP RuntimeProvider.Init();
ReflectionHelpers.TryLoadGameModules();
#endif
if (!Directory.Exists(EXPLORER_FOLDER)) if (!Directory.Exists(EXPLORER_FOLDER))
Directory.CreateDirectory(EXPLORER_FOLDER); Directory.CreateDirectory(EXPLORER_FOLDER);
ModConfig.OnLoad(); ExplorerConfig.OnLoad();
InputManager.Init(); InputManager.Init();
ForceUnlockCursor.Init(); ForceUnlockCursor.Init();
SetupEvents(); UIManager.ShowMenu = true;
ShowMenu = true;
Log($"{NAME} {VERSION} initialized."); Log($"{NAME} {VERSION} initialized.");
} }
public static void Update() public static void Update()
{ {
if (!s_doneUIInit) UIManager.CheckUIInit();
CheckUIInit();
if (MouseInspector.Enabled) if (MouseInspector.Enabled)
MouseInspector.UpdateInspect(); MouseInspector.UpdateInspect();
else else
{ UIManager.Update();
if (InputManager.GetKeyDown(ModConfig.Instance.Main_Menu_Toggle))
ShowMenu = !ShowMenu;
if (ShowMenu && s_doneUIInit)
UIManager.Update();
}
} }
private static void CheckUIInit() public void OnUnityLog(string message, string stackTrace, LogType type)
{
s_timeSinceStartup += Time.deltaTime;
if (s_timeSinceStartup > 0.1f)
{
s_doneUIInit = true;
try
{
UIManager.Init();
Log("Initialized UnityExplorer UI.");
if (ModConfig.Instance.Hide_On_Startup)
ShowMenu = false;
// InspectorManager.Instance.Inspect(Tests.TestClass.Instance);
}
catch (Exception e)
{
LogWarning($"Exception setting up UI: {e}");
}
}
}
private void SetupEvents()
{
#if CPP
try
{
Application.add_logMessageReceived(new Action<string, string, LogType>(OnUnityLog));
SceneManager.add_sceneLoaded(new Action<Scene, LoadSceneMode>((Scene a, LoadSceneMode b) => { OnSceneLoaded(); }));
SceneManager.add_activeSceneChanged(new Action<Scene, Scene>((Scene a, Scene b) => { OnSceneLoaded(); }));
}
catch { }
#else
Application.logMessageReceived += OnUnityLog;
SceneManager.sceneLoaded += (Scene a, LoadSceneMode b) => { OnSceneLoaded(); };
SceneManager.activeSceneChanged += (Scene a, Scene b) => { OnSceneLoaded(); };
#endif
}
internal void OnSceneLoaded()
{
UIManager.OnSceneChange();
}
private static void SetShowMenu(bool show)
{
s_showMenu = show;
if (UIManager.CanvasRoot)
{
UIManager.CanvasRoot.SetActive(show);
if (show)
ForceUnlockCursor.SetEventSystem();
else
ForceUnlockCursor.ReleaseEventSystem();
}
ForceUnlockCursor.UpdateCursorControl();
}
private void OnUnityLog(string message, string stackTrace, LogType type)
{ {
if (!DebugConsole.LogUnity) if (!DebugConsole.LogUnity)
return; return;
@ -185,12 +92,6 @@ namespace UnityExplorer
} }
} }
#if STANDALONE
public static Action<string> OnLogMessage;
public static Action<string> OnLogWarning;
public static Action<string> OnLogError;
#endif
public static void Log(object message, bool unity = false) public static void Log(object message, bool unity = false)
{ {
DebugConsole.Log(message?.ToString()); DebugConsole.Log(message?.ToString());
@ -198,13 +99,7 @@ namespace UnityExplorer
if (unity) if (unity)
return; return;
#if ML Loader.OnLogMessage(message);
MelonLoader.MelonLogger.Log(message?.ToString());
#elif BIE
ExplorerBepInPlugin.Logging?.LogMessage(message?.ToString());
#elif STANDALONE
OnLogMessage?.Invoke(message?.ToString());
#endif
} }
public static void LogWarning(object message, bool unity = false) public static void LogWarning(object message, bool unity = false)
@ -214,13 +109,7 @@ namespace UnityExplorer
if (unity) if (unity)
return; return;
#if ML Loader.OnLogWarning(message);
MelonLoader.MelonLogger.LogWarning(message?.ToString());
#elif BIE
ExplorerBepInPlugin.Logging?.LogWarning(message?.ToString());
#elif STANDALONE
OnLogWarning?.Invoke(message?.ToString());
#endif
} }
public static void LogError(object message, bool unity = false) public static void LogError(object message, bool unity = false)
@ -230,24 +119,7 @@ namespace UnityExplorer
if (unity) if (unity)
return; return;
#if ML Loader.OnLogError(message);
MelonLoader.MelonLogger.LogError(message?.ToString());
#elif BIE
ExplorerBepInPlugin.Logging?.LogError(message?.ToString());
#elif STANDALONE
OnLogError?.Invoke(message?.ToString());
#endif
}
public static string RemoveInvalidFilenameChars(string s)
{
var invalid = System.IO.Path.GetInvalidFileNameChars();
foreach (var c in invalid)
{
s = s.Replace(c.ToString(), "");
}
return s;
} }
} }
} }

View File

@ -1,29 +0,0 @@
#if ML
using System;
using MelonLoader;
namespace UnityExplorer
{
public class ExplorerMelonMod : MelonMod
{
public static ExplorerMelonMod Instance;
public override void OnApplicationStart()
{
Instance = this;
new ExplorerCore();
}
public override void OnUpdate()
{
ExplorerCore.Update();
}
public override void OnLevelWasLoaded(int level)
{
ExplorerCore.Instance.OnSceneLoaded();
}
}
}
#endif

View File

@ -1,11 +0,0 @@
#if STANDALONE
using HarmonyLib;
namespace UnityExplorer
{
public class ExplorerStandalone
{
public static readonly Harmony HarmonyInstance = new Harmony(ExplorerCore.GUID);
}
}
#endif

View File

@ -1,33 +0,0 @@
#if CPP
using System;
using UnityEngine.Events;
namespace UnityExplorer.Helpers
{
// Possibly temporary, just so Il2Cpp can do the same style "AddListener" as Mono.
// Just saves me having a preprocessor directive for every single AddListener.
public static class EventHelper
{
public static void AddListener(this UnityEvent action, Action listener)
{
action.AddListener(listener);
}
public static void AddListener<T>(this UnityEvent<T> action, Action<T> listener)
{
action.AddListener(listener);
}
public static void AddListener<T0, T1>(this UnityEvent<T0, T1> action, Action<T0, T1> listener)
{
action.AddListener(listener);
}
public static void AddListener<T0, T1, T2>(this UnityEvent<T0, T1, T2> action, Action<T0, T1, T2> listener)
{
action.AddListener(listener);
}
}
}
#endif

View File

@ -11,20 +11,27 @@ namespace UnityExplorer.Helpers
{ {
private static readonly Dictionary<string, Delegate> iCallCache = new Dictionary<string, Delegate>(); private static readonly Dictionary<string, Delegate> iCallCache = new Dictionary<string, Delegate>();
public static T GetICall<T>(string iCallName) where T : Delegate /// <summary>
/// Helper to get and cache an iCall by providing the signature (eg. "UnityEngine.Resources::FindObjectsOfTypeAll").
/// </summary>
/// <typeparam name="T">The Type of Delegate to provide for the iCall.</typeparam>
/// <param name="signature">The signature of the iCall you want to get.</param>
/// <returns>The <typeparamref name="T"/> delegate if successful.</returns>
/// <exception cref="MissingMethodException">If the iCall could not be found.</exception>
public static T GetICall<T>(string signature) where T : Delegate
{ {
if (iCallCache.ContainsKey(iCallName)) if (iCallCache.ContainsKey(signature))
return (T)iCallCache[iCallName]; return (T)iCallCache[signature];
IntPtr ptr = il2cpp_resolve_icall(iCallName); IntPtr ptr = il2cpp_resolve_icall(signature);
if (ptr == IntPtr.Zero) if (ptr == IntPtr.Zero)
{ {
throw new MissingMethodException($"Could not resolve internal call by name '{iCallName}'!"); throw new MissingMethodException($"Could not resolve internal call by name '{signature}'!");
} }
Delegate iCall = Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)); Delegate iCall = Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
iCallCache.Add(iCallName, iCall); iCallCache.Add(signature, iCall);
return (T)iCall; return (T)iCall;
} }

View File

@ -21,26 +21,61 @@ namespace UnityExplorer.Helpers
{ {
public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static; public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
// cache for GetTypeByName
internal static readonly Dictionary<string, Type> s_typesByName = new Dictionary<string, Type>();
/// <summary>
/// Find a <see cref="Type"/> in the current AppDomain whose <see cref="Type.FullName"/> matches the provided <paramref name="fullName"/>.
/// </summary>
/// <param name="fullName">The <see cref="Type.FullName"/> you want to search for - case sensitive and full matches only.</param>
/// <returns>The Type if found, otherwise null.</returns>
public static Type GetTypeByName(string fullName) public static Type GetTypeByName(string fullName)
{ {
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) s_typesByName.TryGetValue(fullName, out Type ret);
if (ret != null)
return ret;
foreach (var type in from asm in AppDomain.CurrentDomain.GetAssemblies()
from type in asm.TryGetTypes()
select type)
{ {
foreach (var type in asm.TryGetTypes()) if (type.FullName == fullName)
{ {
if (type.FullName == fullName) ret = type;
{ break;
return type;
}
} }
} }
return null; if (s_typesByName.ContainsKey(fullName))
s_typesByName[fullName] = ret;
else
s_typesByName.Add(fullName, ret);
return ret;
} }
// cache for GetBaseTypes
internal static readonly Dictionary<string, Type[]> s_cachedTypeInheritance = new Dictionary<string, Type[]>();
/// <summary>
/// Get all base types of the provided Type, including itself.
/// </summary>
public static Type[] GetAllBaseTypes(object obj) => GetAllBaseTypes(GetActualType(obj)); public static Type[] GetAllBaseTypes(object obj) => GetAllBaseTypes(GetActualType(obj));
/// <summary>
/// Get all base types of the provided Type, including itself.
/// </summary>
public static Type[] GetAllBaseTypes(Type type) public static Type[] GetAllBaseTypes(Type type)
{ {
if (type == null)
throw new ArgumentNullException("type");
var name = type.AssemblyQualifiedName;
if (s_cachedTypeInheritance.TryGetValue(name, out Type[] ret))
return ret;
List<Type> list = new List<Type>(); List<Type> list = new List<Type>();
while (type != null) while (type != null)
@ -49,9 +84,18 @@ namespace UnityExplorer.Helpers
type = type.BaseType; type = type.BaseType;
} }
return list.ToArray(); ret = list.ToArray();
s_cachedTypeInheritance.Add(name, ret);
return ret;
} }
/// <summary>
/// Helper for IL2CPP to get the underlying true Type (Unhollowed) of the object.
/// </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 GetActualType(this object obj) public static Type GetActualType(this object obj)
{ {
if (obj == null) if (obj == null)
@ -59,9 +103,9 @@ namespace UnityExplorer.Helpers
var type = obj.GetType(); var type = obj.GetType();
#if CPP #if CPP
if (obj is Il2CppSystem.Object ilObject) if (obj is Il2CppSystem.Object cppObject)
{ {
if (ilObject is CppType) if (cppObject is CppType)
return typeof(CppType); return typeof(CppType);
if (!string.IsNullOrEmpty(type.Namespace)) if (!string.IsNullOrEmpty(type.Namespace))
@ -69,22 +113,22 @@ namespace UnityExplorer.Helpers
// Il2CppSystem-namespace objects should just return GetType, // Il2CppSystem-namespace objects should just return GetType,
// because using GetIl2CppType returns the System namespace type instead. // because using GetIl2CppType returns the System namespace type instead.
if (type.Namespace.StartsWith("System.") || type.Namespace.StartsWith("Il2CppSystem.")) if (type.Namespace.StartsWith("System.") || type.Namespace.StartsWith("Il2CppSystem."))
return ilObject.GetType(); return cppObject.GetType();
} }
var il2cppType = ilObject.GetIl2CppType(); var cppType = cppObject.GetIl2CppType();
// check if type is injected // check if type is injected
IntPtr classPtr = il2cpp_object_get_class(ilObject.Pointer); IntPtr classPtr = il2cpp_object_get_class(cppObject.Pointer);
if (RuntimeSpecificsStore.IsInjected(classPtr)) if (RuntimeSpecificsStore.IsInjected(classPtr))
{ {
var typeByName = GetTypeByName(il2cppType.FullName); var typeByName = GetTypeByName(cppType.FullName);
if (typeByName != null) if (typeByName != null)
return typeByName; return typeByName;
} }
// this should be fine for all other il2cpp objects // this should be fine for all other il2cpp objects
var getType = GetMonoType(il2cppType); var getType = GetMonoType(cppType);
if (getType != null) if (getType != null)
return getType; return getType;
} }
@ -93,35 +137,55 @@ namespace UnityExplorer.Helpers
} }
#if CPP #if CPP
private static readonly Dictionary<CppType, Type> Il2CppToMonoType = new Dictionary<CppType, Type>(); // caching for GetMonoType
private static readonly Dictionary<string, Type> Il2CppToMonoType = new Dictionary<string, Type>();
// keep unobfuscated type name cache, used to display proper name.
internal static Dictionary<string, string> UnobfuscatedTypeNames = new Dictionary<string, string>();
/// <summary>
/// Try to get the Mono (Unhollowed) Type representation of the provided <see cref="Il2CppSystem.Type"/>.
/// </summary>
/// <param name="cppType">The Cpp Type you want to convert to Mono.</param>
/// <returns>The Mono Type if found, otherwise null.</returns>
public static Type GetMonoType(CppType cppType) public static Type GetMonoType(CppType cppType)
{ {
if (Il2CppToMonoType.ContainsKey(cppType)) string name = cppType.AssemblyQualifiedName;
return Il2CppToMonoType[cppType];
var getType = Type.GetType(cppType.AssemblyQualifiedName); if (Il2CppToMonoType.ContainsKey(name))
return Il2CppToMonoType[name];
if (getType != null) Type ret = Type.GetType(name);
{
Il2CppToMonoType.Add(cppType, getType); if (ret == null)
return getType;
}
else
{ {
string baseName = cppType.FullName; string baseName = cppType.FullName;
string baseAssembly = cppType.Assembly.GetName().name; string baseAssembly = cppType.Assembly.GetName().name;
Type unhollowedType = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == baseAssembly)?.GetTypes().FirstOrDefault(t => ret = AppDomain.CurrentDomain
t.CustomAttributes.Any(ca => .GetAssemblies()
ca.AttributeType.Name == "ObfuscatedNameAttribute" && (string)ca.ConstructorArguments[0].Value == baseName)); .FirstOrDefault(a
=> a.GetName().Name == baseAssembly)?
.TryGetTypes()
.FirstOrDefault(t
=> t.CustomAttributes.Any(ca
=> ca.AttributeType.Name == "ObfuscatedNameAttribute"
&& (string)ca.ConstructorArguments[0].Value == baseName));
Il2CppToMonoType.Add(cppType, unhollowedType); if (ret != null)
return unhollowedType; {
// unobfuscated type was found, add to cache.
UnobfuscatedTypeNames.Add(cppType.FullName, ret.FullName);
}
} }
Il2CppToMonoType.Add(name, ret);
return ret;
} }
private static readonly Dictionary<Type, IntPtr> CppClassPointers = new Dictionary<Type, IntPtr>(); // cached class pointers for Il2CppCast
private static readonly Dictionary<string, IntPtr> CppClassPointers = new Dictionary<string, IntPtr>();
/// <summary> /// <summary>
/// Attempt to cast the object to its underlying type. /// Attempt to cast the object to its underlying type.
@ -155,7 +219,43 @@ namespace UnityExplorer.Helpers
return Activator.CreateInstance(castTo, ilObj.Pointer); return Activator.CreateInstance(castTo, ilObj.Pointer);
} }
internal static readonly Dictionary<Type, MethodInfo> s_unboxMethods = new Dictionary<Type, MethodInfo>(); /// <summary>
/// Get the Il2Cpp Class Pointer for the provided Mono (Unhollowed) Type.
/// </summary>
/// <param name="type">The Mono/Unhollowed Type you want the Il2Cpp Class Pointer for.</param>
/// <returns>True if successful, false if not.</returns>
public static bool Il2CppTypeNotNull(Type type) => Il2CppTypeNotNull(type, out _);
/// <summary>
/// Get the Il2Cpp Class Pointer for the provided Mono (Unhollowed) Type.
/// </summary>
/// <param name="type">The Mono/Unhollowed Type you want the Il2Cpp Class Pointer for.</param>
/// <param name="il2cppPtr">The IntPtr for the Il2Cpp class, or IntPtr.Zero if not found.</param>
/// <returns>True if successful, false if not.</returns>
public static bool Il2CppTypeNotNull(Type type, out IntPtr il2cppPtr)
{
if (CppClassPointers.TryGetValue(type.AssemblyQualifiedName, out il2cppPtr))
return il2cppPtr != IntPtr.Zero;
il2cppPtr = (IntPtr)typeof(Il2CppClassPointerStore<>)
.MakeGenericType(new Type[] { type })
.GetField("NativeClassPtr", BF.Public | BF.Static)
.GetValue(null);
CppClassPointers.Add(type.AssemblyQualifiedName, il2cppPtr);
return il2cppPtr != IntPtr.Zero;
}
// Extern C++ methods
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern bool il2cpp_class_is_assignable_from(IntPtr klass, IntPtr oklass);
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr il2cpp_object_get_class(IntPtr obj);
// cached il2cpp unbox methods
internal static readonly Dictionary<string, MethodInfo> s_unboxMethods = new Dictionary<string, MethodInfo>();
/// <summary> /// <summary>
/// Attempt to unbox the object to the underlying struct type. /// Attempt to unbox the object to the underlying struct type.
@ -178,41 +278,18 @@ namespace UnityExplorer.Helpers
if (!(obj is Il2CppSystem.Object)) if (!(obj is Il2CppSystem.Object))
return obj; return obj;
if (!s_unboxMethods.ContainsKey(type)) var name = type.AssemblyQualifiedName;
if (!s_unboxMethods.ContainsKey(name))
{ {
s_unboxMethods.Add(type, typeof(Il2CppObjectBase) s_unboxMethods.Add(name, typeof(Il2CppObjectBase)
.GetMethod("Unbox") .GetMethod("Unbox")
.MakeGenericMethod(type)); .MakeGenericMethod(type));
} }
return s_unboxMethods[type].Invoke(obj, new object[0]); return s_unboxMethods[name].Invoke(obj, new object[0]);
} }
public static bool Il2CppTypeNotNull(Type type) => Il2CppTypeNotNull(type, out _);
public static bool Il2CppTypeNotNull(Type type, out IntPtr il2cppPtr)
{
if (!CppClassPointers.ContainsKey(type))
{
il2cppPtr = (IntPtr)typeof(Il2CppClassPointerStore<>)
.MakeGenericType(new Type[] { type })
.GetField("NativeClassPtr", BF.Public | BF.Static)
.GetValue(null);
CppClassPointers.Add(type, il2cppPtr);
}
else
il2cppPtr = CppClassPointers[type];
return il2cppPtr != IntPtr.Zero;
}
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern bool il2cpp_class_is_assignable_from(IntPtr klass, IntPtr oklass);
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr il2cpp_object_get_class(IntPtr obj);
#endif #endif
public static IEnumerable<Type> TryGetTypes(this Assembly asm) public static IEnumerable<Type> TryGetTypes(this Assembly asm)
@ -238,49 +315,17 @@ namespace UnityExplorer.Helpers
} }
} }
#if CPP // Helper for IL2CPP to check if a Type is assignable to IEnumerable
internal static void TryLoadGameModules()
{
LoadModule("Assembly-CSharp");
LoadModule("Assembly-CSharp-firstpass");
}
public static bool LoadModule(string module)
{
#if ML
var path = $@"MelonLoader\Managed\{module}.dll";
#else
var path = $@"BepInEx\unhollowed\{module}.dll";
#endif
return LoadModuleInternal(path);
}
internal static bool LoadModuleInternal(string fullPath)
{
if (!File.Exists(fullPath))
return false;
try
{
Assembly.Load(File.ReadAllBytes(fullPath));
return true;
}
catch (Exception e)
{
Console.WriteLine(e.GetType() + ", " + e.Message);
}
return false;
}
#else
public static bool LoadModule(string module) => true;
#endif
#if CPP #if CPP
internal static IntPtr s_cppEnumerableClassPtr; internal static IntPtr s_cppEnumerableClassPtr;
#endif #endif
/// <summary>
/// Check if the provided Type is assignable to IEnumerable.
/// </summary>
/// <param name="t">The Type to check</param>
/// <returns>True if the Type is assignable to IEnumerable, otherwise false.</returns>
public static bool IsEnumerable(Type t) public static bool IsEnumerable(Type t)
{ {
if (typeof(IEnumerable).IsAssignableFrom(t)) if (typeof(IEnumerable).IsAssignableFrom(t))
@ -303,10 +348,17 @@ namespace UnityExplorer.Helpers
return false; return false;
} }
// Helper for IL2CPP to check if a Type is assignable to IDictionary
#if CPP #if CPP
internal static IntPtr s_cppDictionaryClassPtr; internal static IntPtr s_cppDictionaryClassPtr;
#endif #endif
/// <summary>
/// Check if the provided Type is assignable to IDictionary.
/// </summary>
/// <param name="t">The Type to check</param>
/// <returns>True if the Type is assignable to IDictionary, otherwise false.</returns>
public static bool IsDictionary(Type t) public static bool IsDictionary(Type t)
{ {
if (typeof(IDictionary).IsAssignableFrom(t)) if (typeof(IDictionary).IsAssignableFrom(t))
@ -329,18 +381,68 @@ namespace UnityExplorer.Helpers
return false; return false;
} }
public static string ExceptionToString(Exception e, bool innerMost = false) // Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
{
while (innerMost && e.InnerException != null)
{
#if CPP #if CPP
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException runtimeEx) internal static void TryLoadGameModules()
break; {
LoadModule("Assembly-CSharp");
LoadModule("Assembly-CSharp-firstpass");
}
public static bool LoadModule(string module)
{
#if ML
var path = Path.Combine("MelonLoader", "Managed", $"{module}.dll");
#else
var path = Path.Combine("BepInEx", "unhollowed", $"{module}.dll");
#endif #endif
e = e.InnerException; return LoadModuleInternal(path);
}
internal static bool LoadModuleInternal(string fullPath)
{
if (!File.Exists(fullPath))
return false;
try
{
Assembly.Load(File.ReadAllBytes(fullPath));
return true;
}
catch (Exception e)
{
Console.WriteLine(e.GetType() + ", " + e.Message);
} }
return e.GetType() + ", " + e.Message; return false;
}
#else
// For Mono, just return true and do nothing, Mono will sort it out itself.
public static bool LoadModule(string module) => true;
#endif
/// <summary>
/// Helper to display a simple "{ExceptionType}: {Message}" of the exception, and optionally use the inner-most exception.
/// </summary>
/// <param name="e">The Exception to convert to string.</param>
/// <param name="innerMost">Should the inner-most Exception of the stack be used? If false, the Exception you provided will be used directly.</param>
/// <returns>The exception to string.</returns>
public static string ExceptionToString(Exception e, bool innerMost = false)
{
if (innerMost)
{
while (e.InnerException != null)
{
#if CPP
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException)
break;
#endif
e = e.InnerException;
}
}
return $"{e.GetType()}: {e.Message}";
} }
} }
} }

View File

@ -381,7 +381,7 @@ namespace UnityExplorer.Inspectors.Reflection
argLabelLayout.minHeight = 25; argLabelLayout.minHeight = 25;
var argText = argLabelObj.GetComponent<Text>(); var argText = argLabelObj.GetComponent<Text>();
var argTypeTxt = UISyntaxHighlight.ParseFullSyntax(arg.ParameterType, false); var argTypeTxt = UISyntaxHighlight.ParseFullSyntax(arg.ParameterType, false);
argText.text = $"{argTypeTxt} <color={UISyntaxHighlight.Local}>{arg.Name}</color>"; argText.text = $"{argTypeTxt} <color={UISyntaxHighlight.LOCAL_ARG}>{arg.Name}</color>";
var argInputObj = UIFactory.CreateInputField(rowObj, 14, (int)TextAnchor.MiddleLeft, 1); var argInputObj = UIFactory.CreateInputField(rowObj, 14, (int)TextAnchor.MiddleLeft, 1);
var argInputLayout = argInputObj.AddComponent<LayoutElement>(); var argInputLayout = argInputObj.AddComponent<LayoutElement>();

View File

@ -168,7 +168,7 @@ namespace UnityExplorer.Inspectors.Reflection
//var argLayout = argLabelObj.AddComponent<LayoutElement>(); //var argLayout = argLabelObj.AddComponent<LayoutElement>();
//argLayout.minWidth = 20; //argLayout.minWidth = 20;
var argText = argLabelObj.GetComponent<Text>(); var argText = argLabelObj.GetComponent<Text>();
argText.text = $"{constrainTxt} <color={UISyntaxHighlight.Enum}>{arg.Name}</color>"; argText.text = $"{constrainTxt} <color={UISyntaxHighlight.CONST_VAR}>{arg.Name}</color>";
var argInputObj = UIFactory.CreateInputField(rowObj, 14, (int)TextAnchor.MiddleLeft, 1); var argInputObj = UIFactory.CreateInputField(rowObj, 14, (int)TextAnchor.MiddleLeft, 1);
var argInputLayout = argInputObj.AddComponent<LayoutElement>(); var argInputLayout = argInputObj.AddComponent<LayoutElement>();

View File

@ -251,7 +251,7 @@ namespace UnityExplorer.Inspectors.Reflection
if (string.IsNullOrEmpty(name)) if (string.IsNullOrEmpty(name))
name = "untitled"; name = "untitled";
var savePath = $@"{Config.ModConfig.Instance.Default_Output_Path}\{name}.png"; var savePath = $@"{Config.ExplorerConfig.Instance.Default_Output_Path}\{name}.png";
inputField.text = savePath; inputField.text = savePath;
saveBtn.onClick.AddListener(() => saveBtn.onClick.AddListener(() =>

View File

@ -59,7 +59,7 @@ namespace UnityExplorer.Inspectors.Reflection
= new List<KeyValuePair<CachePaired, CachePaired>>(); = new List<KeyValuePair<CachePaired, CachePaired>>();
internal readonly KeyValuePair<CachePaired, CachePaired>[] m_displayedEntries internal readonly KeyValuePair<CachePaired, CachePaired>[] m_displayedEntries
= new KeyValuePair<CachePaired, CachePaired>[ModConfig.Instance.Default_Page_Limit]; = new KeyValuePair<CachePaired, CachePaired>[ExplorerConfig.Instance.Default_Page_Limit];
internal bool m_recacheWanted = true; internal bool m_recacheWanted = true;

View File

@ -47,13 +47,34 @@ namespace UnityExplorer.Inspectors.Reflection
var list = new List<KeyValuePair<int, string>>(); var list = new List<KeyValuePair<int, string>>();
var set = new HashSet<string>(); var set = new HashSet<string>();
foreach (var value in values) foreach (var value in values)
{ {
var name = value.ToString(); var name = value.ToString();
if (set.Contains(name)) if (set.Contains(name))
continue; continue;
set.Add(name); set.Add(name);
list.Add(new KeyValuePair<int, string>((int)value, name));
var backingType = Enum.GetUnderlyingType(type);
int intValue;
try
{
// this approach is necessary, a simple '(int)value' is not sufficient.
var unbox = Convert.ChangeType(value, backingType);
intValue = (int)Convert.ChangeType(unbox, typeof(int));
}
catch (Exception ex)
{
ExplorerCore.LogWarning("[InteractiveEnum] Could not Unbox underlying type " + backingType.Name + " from " + type.FullName);
ExplorerCore.Log(ex.ToString());
continue;
}
list.Add(new KeyValuePair<int, string>(intValue, name));
} }
s_enumNamesCache.Add(type, list.ToArray()); s_enumNamesCache.Add(type, list.ToArray());

View File

@ -46,7 +46,7 @@ namespace UnityExplorer.Inspectors.Reflection
internal readonly Type m_baseEntryType; internal readonly Type m_baseEntryType;
internal readonly List<CacheEnumerated> m_entries = new List<CacheEnumerated>(); internal readonly List<CacheEnumerated> m_entries = new List<CacheEnumerated>();
internal readonly CacheEnumerated[] m_displayedEntries = new CacheEnumerated[ModConfig.Instance.Default_Page_Limit]; internal readonly CacheEnumerated[] m_displayedEntries = new CacheEnumerated[ExplorerConfig.Instance.Default_Page_Limit];
internal bool m_recacheWanted = true; internal bool m_recacheWanted = true;
public override void OnValueUpdated() public override void OnValueUpdated()

View File

@ -12,29 +12,47 @@ namespace UnityExplorer.Inspectors.Reflection
{ {
public class InteractiveValue public class InteractiveValue
{ {
/// <summary>
/// Get the <see cref="InteractiveValue"/> subclass which supports the provided <paramref name="type"/>.
/// </summary>
/// <param name="type">The <see cref="Type"/> which you want the <see cref="InteractiveValue"/> Type for.</param>
/// <returns>The best subclass of <see cref="InteractiveValue"/> which supports the provided <paramref name="type"/>.</returns>
public static Type GetIValueForType(Type type) public static Type GetIValueForType(Type type)
{ {
// rather ugly but I couldn't think of a cleaner way that was worth it.
// switch-case doesn't really work here.
// arbitrarily check some types, fastest methods first.
if (type == typeof(bool)) if (type == typeof(bool))
return typeof(InteractiveBool); return typeof(InteractiveBool);
else if (type == typeof(string)) // if type is primitive then it must be a number if its not a bool
return typeof(InteractiveString);
else if (type.IsPrimitive) else if (type.IsPrimitive)
return typeof(InteractiveNumber); return typeof(InteractiveNumber);
// check for strings
else if (type == typeof(string))
return typeof(InteractiveString);
// check for enum/flags
else if (typeof(Enum).IsAssignableFrom(type)) else if (typeof(Enum).IsAssignableFrom(type))
{ {
if (type.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] attributes && attributes.Length > 0) // NET 3.5 doesn't have "GetCustomAttribute", gotta use the multiple version.
if (type.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] fa && fa.Any())
return typeof(InteractiveFlags); return typeof(InteractiveFlags);
else else
return typeof(InteractiveEnum); return typeof(InteractiveEnum);
} }
// check for unity struct types
else if (InteractiveUnityStruct.SupportsType(type)) else if (InteractiveUnityStruct.SupportsType(type))
return typeof(InteractiveUnityStruct); return typeof(InteractiveUnityStruct);
// check Transform, force InteractiveValue so they dont become InteractiveEnumerables.
else if (typeof(Transform).IsAssignableFrom(type)) else if (typeof(Transform).IsAssignableFrom(type))
return typeof(InteractiveValue); return typeof(InteractiveValue);
// check Dictionaries before Enumerables
else if (ReflectionHelpers.IsDictionary(type)) else if (ReflectionHelpers.IsDictionary(type))
return typeof(InteractiveDictionary); return typeof(InteractiveDictionary);
// finally check for Enumerables
else if (ReflectionHelpers.IsEnumerable(type)) else if (ReflectionHelpers.IsEnumerable(type))
return typeof(InteractiveEnumerable); return typeof(InteractiveEnumerable);
// fallback to default
else else
return typeof(InteractiveValue); return typeof(InteractiveValue);
} }
@ -194,6 +212,7 @@ namespace UnityExplorer.Inspectors.Reflection
string label; string label;
// Two dirty fixes for TextAsset and EventSystem, which can have very long ToString results.
if (Value is TextAsset textAsset) if (Value is TextAsset textAsset)
{ {
label = textAsset.text; label = textAsset.text;
@ -207,7 +226,7 @@ namespace UnityExplorer.Inspectors.Reflection
{ {
label = m_richValueType; label = m_richValueType;
} }
else else // For everything else...
{ {
if (!m_gotToStringMethods) if (!m_gotToStringMethods)
{ {
@ -233,17 +252,24 @@ namespace UnityExplorer.Inspectors.Reflection
else else
toString = (string)m_toStringMethod.Invoke(Value, new object[0]); toString = (string)m_toStringMethod.Invoke(Value, new object[0]);
var fullnametemp = valueType.ToString(); string typeName = valueType.FullName;
if (fullnametemp.StartsWith("Il2CppSystem")) if (typeName.StartsWith("Il2CppSystem."))
fullnametemp = fullnametemp.Substring(6, fullnametemp.Length - 6); typeName = typeName.Substring(6, typeName.Length - 6);
#if CPP
var cppType = UnhollowerRuntimeLib.Il2CppType.From(valueType);
if (cppType != null && ReflectionHelpers.UnobfuscatedTypeNames.ContainsKey(cppType.FullName))
{
typeName = ReflectionHelpers.UnobfuscatedTypeNames[cppType.FullName];
toString = toString.Replace(cppType.FullName, typeName);
}
#endif
var temp = toString.Replace(fullnametemp, "").Trim(); // If the ToString is just the type name, use our syntax highlighted type name instead.
if (toString == typeName)
if (string.IsNullOrEmpty(temp))
{ {
label = m_richValueType; label = m_richValueType;
} }
else else // Otherwise, parse the result and put our highlighted name in.
{ {
if (toString.Length > 200) if (toString.Length > 200)
toString = toString.Substring(0, 200) + "..."; toString = toString.Substring(0, 200) + "...";

View File

@ -43,6 +43,7 @@ namespace UnityExplorer.Inspectors
"Collider2D.Cast", "Collider2D.Cast",
"Collider2D.Raycast", "Collider2D.Raycast",
"Texture2D.SetPixelDataImpl", "Texture2D.SetPixelDataImpl",
"Camera.CalculateProjectionMatrixFromPhysicalProperties",
#endif #endif
}; };
private static readonly HashSet<string> bl_memberNameStartsWith = new HashSet<string> private static readonly HashSet<string> bl_memberNameStartsWith = new HashSet<string>
@ -68,7 +69,7 @@ namespace UnityExplorer.Inspectors
// filtered members based on current filters // filtered members based on current filters
internal readonly List<CacheMember> m_membersFiltered = new List<CacheMember>(); internal readonly List<CacheMember> m_membersFiltered = new List<CacheMember>();
// actual shortlist of displayed members // actual shortlist of displayed members
internal readonly CacheMember[] m_displayedMembers = new CacheMember[ModConfig.Instance.Default_Page_Limit]; internal readonly CacheMember[] m_displayedMembers = new CacheMember[ExplorerConfig.Instance.Default_Page_Limit];
internal bool m_autoUpdate; internal bool m_autoUpdate;
@ -172,11 +173,11 @@ namespace UnityExplorer.Inspectors
{ {
try try
{ {
var sig = GetSig(member);
//ExplorerCore.Log($"Trying to cache member {sig}..."); //ExplorerCore.Log($"Trying to cache member {sig}...");
//ExplorerCore.Log(member.DeclaringType.FullName + "." + member.Name); //ExplorerCore.Log(member.DeclaringType.FullName + "." + member.Name);
var sig = GetSig(member);
var mi = member as MethodInfo; var mi = member as MethodInfo;
var pi = member as PropertyInfo; var pi = member as PropertyInfo;
var fi = member as FieldInfo; var fi = member as FieldInfo;

View File

@ -55,16 +55,15 @@ namespace UnityExplorer.Inspectors
{ {
get get
{ {
if (!m_dontDestroyObject) if (!s_dontDestroyObject)
{ {
m_dontDestroyObject = new GameObject("DontDestroyMe"); s_dontDestroyObject = new GameObject("DontDestroyMe");
GameObject.DontDestroyOnLoad(m_dontDestroyObject); GameObject.DontDestroyOnLoad(s_dontDestroyObject);
} }
return m_dontDestroyObject; return s_dontDestroyObject;
} }
} }
internal static GameObject s_dontDestroyObject;
internal static GameObject m_dontDestroyObject;
public void Init() public void Init()
{ {
@ -97,32 +96,21 @@ namespace UnityExplorer.Inspectors
} }
} }
//#if CPP //internal void OnSceneChange()
// public int GetSceneHandle(string sceneName) //{
// { // m_sceneDropdown.OnCancel(null);
// if (sceneName == "DontDestroyOnLoad") // RefreshSceneSelector();
// return DontDestroyScene; //}
// for (int i = 0; i < SceneManager.sceneCount; i++)
// {
// var scene = SceneManager.GetSceneAt(i);
// if (scene.name == sceneName)
// return scene.handle;
// }
// return -1;
// }
//#endif
internal void OnSceneChange()
{
m_sceneDropdown.OnCancel(null);
RefreshSceneSelector();
}
private void RefreshSceneSelector() private void RefreshSceneSelector()
{ {
var names = new List<string>(); var newNames = new List<string>();
var scenes = new List<Scene>(); var newScenes = new List<Scene>();
if (m_currentScenes == null)
m_currentScenes = new Scene[0];
bool anyChange = SceneManager.sceneCount != m_currentScenes.Length - 1;
for (int i = 0; i < SceneManager.sceneCount; i++) for (int i = 0; i < SceneManager.sceneCount; i++)
{ {
@ -131,33 +119,35 @@ namespace UnityExplorer.Inspectors
if (scene == default) if (scene == default)
continue; continue;
scenes.Add(scene); if (!anyChange && !m_currentScenes.Any(it => it.GetHandle() == scene.GetHandle()))
names.Add(scene.name); anyChange = true;
newScenes.Add(scene);
newNames.Add(scene.name);
} }
names.Add("DontDestroyOnLoad"); newNames.Add("DontDestroyOnLoad");
scenes.Add(DontDestroyScene); newScenes.Add(DontDestroyScene);
m_sceneDropdown.options.Clear(); m_sceneDropdown.options.Clear();
foreach (string scene in names) foreach (string scene in newNames)
{ {
m_sceneDropdown.options.Add(new Dropdown.OptionData { text = scene }); m_sceneDropdown.options.Add(new Dropdown.OptionData { text = scene });
} }
if (!names.Contains(m_sceneDropdownText.text)) if (anyChange)
{ {
m_sceneDropdownText.text = names[0]; m_sceneDropdown.OnCancel(null);
SetTargetScene(scenes[0]); m_sceneDropdownText.text = newNames[0];
SetTargetScene(newScenes[0]);
SearchPage.Instance.OnSceneChange();
} }
m_currentScenes = scenes.ToArray(); m_currentScenes = newScenes.ToArray();
} }
//#if CPP
// public void SetTargetScene(string name) => SetTargetScene(scene.handle);
//#endif
public void SetTargetScene(Scene scene) public void SetTargetScene(Scene scene)
{ {
if (scene == default) if (scene == default)
@ -167,7 +157,7 @@ namespace UnityExplorer.Inspectors
#if CPP #if CPP
GameObject[] rootObjs = SceneUnstrip.GetRootGameObjects(scene.handle); GameObject[] rootObjs = SceneUnstrip.GetRootGameObjects(scene.handle);
#else #else
GameObject[] rootObjs = SceneUnstrip.GetRootGameObjects(scene); GameObject[] rootObjs = scene.GetRootGameObjects();
#endif #endif
SetSceneObjectList(rootObjs); SetSceneObjectList(rootObjs);

View File

@ -0,0 +1,41 @@
#if BIE5
using System;
using System.IO;
using System.Reflection;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
namespace UnityExplorer
{
[BepInPlugin(ExplorerCore.GUID, "UnityExplorer", ExplorerCore.VERSION)]
public class ExplorerBepInPlugin : BaseUnityPlugin, IExplorerLoader
{
public static ExplorerBepInPlugin Instance;
public static ManualLogSource Logging => Instance?.Logger;
public Harmony HarmonyInstance => s_harmony;
private static readonly Harmony s_harmony = new Harmony(ExplorerCore.GUID);
public string ExplorerFolder => Path.Combine(Paths.PluginPath, ExplorerCore.NAME);
public string ConfigFolder => Path.Combine(Paths.ConfigPath, ExplorerCore.NAME);
public Action<object> OnLogMessage => (object log) => { Logging?.LogMessage(log?.ToString() ?? ""); };
public Action<object> OnLogWarning => (object log) => { Logging?.LogWarning(log?.ToString() ?? ""); };
public Action<object> OnLogError => (object log) => { Logging?.LogError(log?.ToString() ?? ""); };
internal void Awake()
{
Instance = this;
new ExplorerCore();
}
internal void Update()
{
ExplorerCore.Update();
}
}
}
#endif

View File

@ -1,4 +1,4 @@
#if BIE #if BIE6
using System; using System;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
@ -18,21 +18,27 @@ namespace UnityExplorer
{ {
#if MONO #if MONO
[BepInPlugin(ExplorerCore.GUID, "UnityExplorer", ExplorerCore.VERSION)] [BepInPlugin(ExplorerCore.GUID, "UnityExplorer", ExplorerCore.VERSION)]
public class ExplorerBepInPlugin : BaseUnityPlugin public class ExplorerBepInPlugin : BaseUnityPlugin, IExplorerLoader
{ {
public static ExplorerBepInPlugin Instance; public static ExplorerBepInPlugin Instance;
public static ManualLogSource Logging => Instance?.Logger; public static ManualLogSource Logging => Instance?.Logger;
public static readonly Harmony HarmonyInstance = new Harmony(ExplorerCore.GUID); public Harmony HarmonyInstance => s_harmony;
private static readonly Harmony s_harmony = new Harmony(ExplorerCore.GUID);
public string ExplorerFolder => Path.Combine(Paths.PluginPath, ExplorerCore.NAME);
public string ConfigFolder => Path.Combine(Paths.ConfigPath, ExplorerCore.NAME);
public Action<object> OnLogMessage => (object log) => { Logging?.LogMessage(log?.ToString() ?? ""); };
public Action<object> OnLogWarning => (object log) => { Logging?.LogWarning(log?.ToString() ?? ""); };
public Action<object> OnLogError => (object log) => { Logging?.LogError(log?.ToString() ?? ""); };
internal void Awake() internal void Awake()
{ {
Instance = this; Instance = this;
new ExplorerCore(); new ExplorerCore();
// HarmonyInstance.PatchAll();
} }
internal void Update() internal void Update()
@ -44,13 +50,21 @@ namespace UnityExplorer
#if CPP #if CPP
[BepInPlugin(ExplorerCore.GUID, "UnityExplorer", ExplorerCore.VERSION)] [BepInPlugin(ExplorerCore.GUID, "UnityExplorer", ExplorerCore.VERSION)]
public class ExplorerBepInPlugin : BasePlugin public class ExplorerBepInPlugin : BasePlugin, IExplorerLoader
{ {
public static ExplorerBepInPlugin Instance; public static ExplorerBepInPlugin Instance;
public static ManualLogSource Logging => Instance?.Log; public static ManualLogSource Logging => Instance?.Log;
public static readonly Harmony HarmonyInstance = new Harmony(ExplorerCore.GUID); public Harmony HarmonyInstance => s_harmony;
private static readonly Harmony s_harmony = new Harmony(ExplorerCore.GUID);
public string ExplorerFolder => Path.Combine(Paths.PluginPath, ExplorerCore.NAME);
public string ConfigFolder => Path.Combine(Paths.ConfigPath, ExplorerCore.NAME);
public Action<object> OnLogMessage => (object log) => { Logging?.LogMessage(log?.ToString() ?? ""); };
public Action<object> OnLogWarning => (object log) => { Logging?.LogWarning(log?.ToString() ?? ""); };
public Action<object> OnLogError => (object log) => { Logging?.LogError(log?.ToString() ?? ""); };
// Init // Init
public override void Load() public override void Load()
@ -67,8 +81,6 @@ namespace UnityExplorer
GameObject.DontDestroyOnLoad(obj); GameObject.DontDestroyOnLoad(obj);
new ExplorerCore(); new ExplorerCore();
// HarmonyInstance.PatchAll();
} }
// BepInEx Il2Cpp mod class doesn't have monobehaviour methods yet, so wrap them in a dummy. // BepInEx Il2Cpp mod class doesn't have monobehaviour methods yet, so wrap them in a dummy.

View File

@ -0,0 +1,39 @@
#if ML
using System;
using System.IO;
using MelonLoader;
namespace UnityExplorer
{
public class ExplorerMelonMod : MelonMod, IExplorerLoader
{
public static ExplorerMelonMod Instance;
public string ExplorerFolder => Path.Combine("Mods", ExplorerCore.NAME);
public string ConfigFolder => ExplorerFolder;
public Action<object> OnLogMessage => (object log) => { MelonLogger.Msg(log?.ToString() ?? ""); };
public Action<object> OnLogWarning => (object log) => { MelonLogger.Warning(log?.ToString() ?? ""); };
public Action<object> OnLogError => (object log) => { MelonLogger.Error(log?.ToString() ?? ""); };
public Harmony.HarmonyInstance HarmonyInstance => Instance.Harmony;
public override void OnApplicationStart()
{
Instance = this;
new ExplorerCore();
}
public override void OnUpdate()
{
ExplorerCore.Update();
}
//public override void OnSceneWasLoaded(int buildIndex, string sceneName)
//{
// ExplorerCore.Instance.OnSceneLoaded();
//}
}
}
#endif

View File

@ -0,0 +1,105 @@
#if STANDALONE
using HarmonyLib;
using System;
using System.IO;
using System.Reflection;
using UnityEngine;
#if CPP
using UnhollowerRuntimeLib;
#endif
namespace UnityExplorer
{
public class ExplorerStandalone : IExplorerLoader
{
/// <summary>
/// Call this to initialize UnityExplorer. Optionally, also subscribe to the 'OnLog' event to handle logging.
/// </summary>
/// <returns>The new (or active, if one exists) instance of ExplorerStandalone.</returns>
public static ExplorerStandalone CreateInstance()
{
if (Instance != null)
return Instance;
return new ExplorerStandalone();
}
private ExplorerStandalone()
{
Init();
}
public static ExplorerStandalone Instance { get; private set; }
/// <summary>
/// Invoked whenever Explorer logs something. Subscribe to this to handle logging.
/// </summary>
public static event Action<string, LogType> OnLog;
public Harmony HarmonyInstance => s_harmony;
public static readonly Harmony s_harmony = new Harmony(ExplorerCore.GUID);
public string ExplorerFolder
{
get
{
if (s_explorerFolder == null)
{
s_explorerFolder =
Path.Combine(
Path.GetDirectoryName(
Uri.UnescapeDataString(new Uri(Assembly.GetExecutingAssembly().CodeBase)
.AbsolutePath)),
"UnityExplorer");
if (!Directory.Exists(s_explorerFolder))
Directory.CreateDirectory(s_explorerFolder);
}
return s_explorerFolder;
}
}
private static string s_explorerFolder;
public string ConfigFolder => ExplorerFolder;
Action<object> IExplorerLoader.OnLogMessage => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Log); };
Action<object> IExplorerLoader.OnLogWarning => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Warning); };
Action<object> IExplorerLoader.OnLogError => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Error); };
private void Init()
{
Instance = this;
#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
obj.hideFlags = HideFlags.HideAndDontSave;
GameObject.DontDestroyOnLoad(obj);
new ExplorerCore();
}
public class ExplorerBehaviour : MonoBehaviour
{
#if CPP
public ExplorerBehaviour(IntPtr ptr) : base(ptr) { }
#endif
internal void Update()
{
ExplorerCore.Update();
}
}
}
}
#endif

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityExplorer
{
public interface IExplorerLoader
{
string ExplorerFolder { get; }
string ConfigFolder { get; }
Action<object> OnLogMessage { get; }
Action<object> OnLogWarning { get; }
Action<object> OnLogError { get; }
#if ML
Harmony.HarmonyInstance HarmonyInstance { get; }
#else
HarmonyLib.Harmony HarmonyInstance { get; }
#endif
}
}

View File

@ -0,0 +1,47 @@
#if CPP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityExplorer.Helpers;
namespace UnityExplorer.Runtime.Il2Cpp
{
public class Il2CppProvider : RuntimeProvider
{
public override void Initialize()
{
ReflectionHelpers.TryLoadGameModules();
}
public override void SetupEvents()
{
Application.add_logMessageReceived(
new Action<string, string, LogType>(ExplorerCore.Instance.OnUnityLog));
//SceneManager.add_sceneLoaded(
// new Action<Scene, LoadSceneMode>(ExplorerCore.Instance.OnSceneLoaded1));
//SceneManager.add_activeSceneChanged(
// new Action<Scene, Scene>(ExplorerCore.Instance.OnSceneLoaded2));
}
}
}
public static class UnityEventExtensions
{
public static void AddListener(this UnityEvent action, Action listener)
{
action.AddListener(listener);
}
public static void AddListener<T>(this UnityEvent<T> action, Action<T> listener)
{
action.AddListener(listener);
}
}
#endif

View File

@ -0,0 +1,26 @@
#if MONO
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace UnityExplorer.Runtime.Mono
{
public class MonoProvider : RuntimeProvider
{
public override void Initialize()
{
}
public override void SetupEvents()
{
Application.logMessageReceived += ExplorerCore.Instance.OnUnityLog;
//SceneManager.sceneLoaded += ExplorerCore.Instance.OnSceneLoaded1;
//SceneManager.activeSceneChanged += ExplorerCore.Instance.OnSceneLoaded2;
}
}
}
#endif

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityExplorer.Runtime
{
// 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;
public RuntimeProvider()
{
Initialize();
SetupEvents();
}
public static void Init() =>
#if CPP
Instance = new Il2Cpp.Il2CppProvider();
#else
Instance = new Mono.MonoProvider();
#endif
public abstract void Initialize();
public abstract void SetupEvents();
}
}

View File

@ -13,6 +13,14 @@ using UnityExplorer.Helpers;
namespace UnityExplorer.Tests namespace UnityExplorer.Tests
{ {
internal enum TestByteEnum : byte
{
One,
Two,
Three,
TwoFiftyFive = 255,
}
public static class StaticTestClass public static class StaticTestClass
{ {
public static int StaticProperty => 5; public static int StaticProperty => 5;
@ -28,6 +36,8 @@ namespace UnityExplorer.Tests
public class TestClass public class TestClass
{ {
internal static TestByteEnum testingByte = TestByteEnum.One;
public string AAALongString = @"1 public string AAALongString = @"1
2 2
3 3
@ -188,12 +198,12 @@ namespace UnityExplorer.Tests
} }
} }
public static string TestRefInOutGeneric<T>(ref string arg0, in int arg1, out string arg2) where T : Component //public static string TestRefInOutGeneric<T>(ref string arg0, in int arg1, out string arg2) where T : Component
{ //{
arg2 = "this is arg2"; // arg2 = "this is arg2";
return $"T: '{typeof(T).FullName}', ref arg0: '{arg0}', in arg1: '{arg1}', out arg2: '{arg2}'"; // return $"T: '{typeof(T).FullName}', ref arg0: '{arg0}', in arg1: '{arg1}', out arg2: '{arg2}'";
} //}
// test a non-generic dictionary // test a non-generic dictionary

View File

@ -28,7 +28,7 @@ namespace UnityExplorer.UI
UpdateCursorControl(); UpdateCursorControl();
} }
public static bool ShouldForceMouse => ExplorerCore.ShowMenu && Unlock; public static bool ShouldForceMouse => UIManager.ShowMenu && Unlock;
private static CursorLockMode m_lastLockMode; private static CursorLockMode m_lastLockMode;
private static bool m_lastVisibleState; private static bool m_lastVisibleState;
@ -42,7 +42,7 @@ namespace UnityExplorer.UI
public static void Init() public static void Init()
{ {
ModConfig.OnConfigChanged += ModConfig_OnConfigChanged; ExplorerConfig.OnConfigChanged += ModConfig_OnConfigChanged;
SetupPatches(); SetupPatches();
@ -51,7 +51,7 @@ namespace UnityExplorer.UI
internal static void ModConfig_OnConfigChanged() internal static void ModConfig_OnConfigChanged()
{ {
Unlock = ModConfig.Instance.Force_Unlock_Mouse; Unlock = ExplorerConfig.Instance.Force_Unlock_Mouse;
} }
private static void SetupPatches() private static void SetupPatches()
@ -102,14 +102,7 @@ namespace UnityExplorer.UI
{ {
try try
{ {
var harmony = var harmony = ExplorerCore.Loader.HarmonyInstance;
#if ML
ExplorerMelonMod.Instance.harmonyInstance;
#elif BIE
ExplorerBepInPlugin.HarmonyInstance;
#elif STANDALONE
ExplorerStandalone.HarmonyInstance;
#endif
System.Reflection.PropertyInfo prop = type.GetProperty(property); System.Reflection.PropertyInfo prop = type.GetProperty(property);
@ -208,7 +201,7 @@ namespace UnityExplorer.UI
m_lastEventSystem = value; m_lastEventSystem = value;
m_lastInputModule = value?.currentInputModule; m_lastInputModule = value?.currentInputModule;
if (ExplorerCore.ShowMenu) if (UIManager.ShowMenu)
{ {
value = UIManager.EventSys; value = UIManager.EventSys;
} }

View File

@ -155,8 +155,11 @@ namespace UnityExplorer.UI
MainPanel = UIFactory.CreatePanel(UIManager.CanvasRoot, "MainMenu", out GameObject content); MainPanel = UIFactory.CreatePanel(UIManager.CanvasRoot, "MainMenu", out GameObject content);
RectTransform panelRect = MainPanel.GetComponent<RectTransform>(); RectTransform panelRect = MainPanel.GetComponent<RectTransform>();
panelRect.anchorMin = new Vector2(0.25f, 0.1f); //panelRect.anchorMin = new Vector2(0.25f, 0.1f);
panelRect.anchorMax = new Vector2(0.78f, 0.95f); //panelRect.anchorMax = new Vector2(0.78f, 0.95f);
var anchors = ExplorerConfig.Instance.GetWindowAnchorsVector();
panelRect.anchorMin = new Vector2(anchors.x, anchors.y);
panelRect.anchorMax = new Vector2(anchors.z, anchors.w);
MainPanel.AddComponent<Mask>(); MainPanel.AddComponent<Mask>();
@ -208,7 +211,7 @@ namespace UnityExplorer.UI
GameObject hideBtnObj = UIFactory.CreateButton(titleBar); GameObject hideBtnObj = UIFactory.CreateButton(titleBar);
Button hideBtn = hideBtnObj.GetComponent<Button>(); Button hideBtn = hideBtnObj.GetComponent<Button>();
hideBtn.onClick.AddListener(() => { ExplorerCore.ShowMenu = false; }); hideBtn.onClick.AddListener(() => { UIManager.ShowMenu = false; });
ColorBlock colorBlock = hideBtn.colors; ColorBlock colorBlock = hideBtn.colors;
colorBlock.normalColor = new Color(65f / 255f, 23f / 255f, 23f / 255f); colorBlock.normalColor = new Color(65f / 255f, 23f / 255f, 23f / 255f);
colorBlock.pressedColor = new Color(35f / 255f, 10f / 255f, 10f / 255f); colorBlock.pressedColor = new Color(35f / 255f, 10f / 255f, 10f / 255f);
@ -224,13 +227,13 @@ namespace UnityExplorer.UI
hideText.resizeTextForBestFit = true; hideText.resizeTextForBestFit = true;
hideText.resizeTextMinSize = 8; hideText.resizeTextMinSize = 8;
hideText.resizeTextMaxSize = 14; hideText.resizeTextMaxSize = 14;
hideText.text = $"Hide ({ModConfig.Instance.Main_Menu_Toggle})"; hideText.text = $"Hide ({ExplorerConfig.Instance.Main_Menu_Toggle})";
ModConfig.OnConfigChanged += ModConfig_OnConfigChanged; ExplorerConfig.OnConfigChanged += ModConfig_OnConfigChanged;
void ModConfig_OnConfigChanged() void ModConfig_OnConfigChanged()
{ {
hideText.text = $"Hide ({ModConfig.Instance.Main_Menu_Toggle})"; hideText.text = $"Hide ({ExplorerConfig.Instance.Main_Menu_Toggle})";
} }
} }

View File

@ -15,7 +15,7 @@ namespace UnityExplorer.UI.Modules
{ {
public static DebugConsole Instance { get; private set; } public static DebugConsole Instance { get; private set; }
public static bool LogUnity { get; set; } = ModConfig.Instance.Log_Unity_Debug; public static bool LogUnity { get; set; } = ExplorerConfig.Instance.Log_Unity_Debug;
//public static bool SaveToDisk { get; set; } = ModConfig.Instance.Save_Logs_To_Disk; //public static bool SaveToDisk { get; set; } = ModConfig.Instance.Save_Logs_To_Disk;
internal static StreamWriter s_streamWriter; internal static StreamWriter s_streamWriter;
@ -69,7 +69,7 @@ namespace UnityExplorer.UI.Modules
} }
var fileName = "UnityExplorer " + DateTime.Now.ToString("u") + ".txt"; var fileName = "UnityExplorer " + DateTime.Now.ToString("u") + ".txt";
fileName = ExplorerCore.RemoveInvalidFilenameChars(fileName); fileName = RemoveInvalidFilenameChars(fileName);
var stream = File.Create(path + @"\" + fileName); var stream = File.Create(path + @"\" + fileName);
s_streamWriter = new StreamWriter(stream) s_streamWriter = new StreamWriter(stream)
@ -81,6 +81,16 @@ namespace UnityExplorer.UI.Modules
s_streamWriter.WriteLine(msg); s_streamWriter.WriteLine(msg);
} }
public static string RemoveInvalidFilenameChars(string s)
{
var invalid = Path.GetInvalidFileNameChars();
foreach (var c in invalid)
{
s = s.Replace(c.ToString(), "");
}
return s;
}
public static void Log(string message) public static void Log(string message)
{ {
Log(message, null); Log(message, null);
@ -256,8 +266,8 @@ namespace UnityExplorer.UI.Modules
void ToggleLogUnity(bool val) void ToggleLogUnity(bool val)
{ {
LogUnity = val; LogUnity = val;
ModConfig.Instance.Log_Unity_Debug = val; ExplorerConfig.Instance.Log_Unity_Debug = val;
ModConfig.SaveSettings(); ExplorerConfig.SaveSettings();
} }
var unityToggleLayout = unityToggleObj.AddComponent<LayoutElement>(); var unityToggleLayout = unityToggleObj.AddComponent<LayoutElement>();

View File

@ -32,19 +32,19 @@ namespace UnityExplorer.UI.Modules
internal void OnApply() internal void OnApply()
{ {
if (!string.IsNullOrEmpty(m_keycodeInput.text) && Enum.Parse(typeof(KeyCode), m_keycodeInput.text) is KeyCode keyCode) if (!string.IsNullOrEmpty(m_keycodeInput.text) && Enum.Parse(typeof(KeyCode), m_keycodeInput.text) is KeyCode keyCode)
ModConfig.Instance.Main_Menu_Toggle = keyCode; ExplorerConfig.Instance.Main_Menu_Toggle = keyCode;
ModConfig.Instance.Force_Unlock_Mouse = m_unlockMouseToggle.isOn; ExplorerConfig.Instance.Force_Unlock_Mouse = m_unlockMouseToggle.isOn;
if (!string.IsNullOrEmpty(m_pageLimitInput.text) && int.TryParse(m_pageLimitInput.text, out int lim)) if (!string.IsNullOrEmpty(m_pageLimitInput.text) && int.TryParse(m_pageLimitInput.text, out int lim))
ModConfig.Instance.Default_Page_Limit = lim; ExplorerConfig.Instance.Default_Page_Limit = lim;
ModConfig.Instance.Default_Output_Path = m_defaultOutputInput.text; ExplorerConfig.Instance.Default_Output_Path = m_defaultOutputInput.text;
ModConfig.Instance.Hide_On_Startup = m_hideOnStartupToggle.isOn; ExplorerConfig.Instance.Hide_On_Startup = m_hideOnStartupToggle.isOn;
ModConfig.SaveSettings(); ExplorerConfig.SaveSettings();
ModConfig.InvokeConfigChanged(); ExplorerConfig.InvokeConfigChanged();
} }
#region UI CONSTRUCTION #region UI CONSTRUCTION
@ -131,7 +131,7 @@ namespace UnityExplorer.UI.Modules
labelLayout.minHeight = 25; labelLayout.minHeight = 25;
UIFactory.CreateToggle(rowObj, out m_hideOnStartupToggle, out Text toggleText); UIFactory.CreateToggle(rowObj, out m_hideOnStartupToggle, out Text toggleText);
m_hideOnStartupToggle.isOn = ModConfig.Instance.Hide_On_Startup; m_hideOnStartupToggle.isOn = ExplorerConfig.Instance.Hide_On_Startup;
toggleText.text = ""; toggleText.text = "";
} }
@ -159,7 +159,7 @@ namespace UnityExplorer.UI.Modules
var keycodeInputObj = UIFactory.CreateInputField(rowObj); var keycodeInputObj = UIFactory.CreateInputField(rowObj);
m_keycodeInput = keycodeInputObj.GetComponent<InputField>(); m_keycodeInput = keycodeInputObj.GetComponent<InputField>();
m_keycodeInput.text = ModConfig.Instance.Main_Menu_Toggle.ToString(); m_keycodeInput.text = ExplorerConfig.Instance.Main_Menu_Toggle.ToString();
m_keycodeInput.placeholder.gameObject.GetComponent<Text>().text = "KeyCode, eg. F7"; m_keycodeInput.placeholder.gameObject.GetComponent<Text>().text = "KeyCode, eg. F7";
} }
@ -186,7 +186,7 @@ namespace UnityExplorer.UI.Modules
labelLayout.minHeight = 25; labelLayout.minHeight = 25;
UIFactory.CreateToggle(rowObj, out m_unlockMouseToggle, out Text toggleText); UIFactory.CreateToggle(rowObj, out m_unlockMouseToggle, out Text toggleText);
m_unlockMouseToggle.isOn = ModConfig.Instance.Force_Unlock_Mouse; m_unlockMouseToggle.isOn = ExplorerConfig.Instance.Force_Unlock_Mouse;
toggleText.text = ""; toggleText.text = "";
} }
@ -216,7 +216,7 @@ namespace UnityExplorer.UI.Modules
var inputObj = UIFactory.CreateInputField(rowObj); var inputObj = UIFactory.CreateInputField(rowObj);
m_pageLimitInput = inputObj.GetComponent<InputField>(); m_pageLimitInput = inputObj.GetComponent<InputField>();
m_pageLimitInput.text = ModConfig.Instance.Default_Page_Limit.ToString(); m_pageLimitInput.text = ExplorerConfig.Instance.Default_Page_Limit.ToString();
m_pageLimitInput.placeholder.gameObject.GetComponent<Text>().text = "Integer, eg. 20"; m_pageLimitInput.placeholder.gameObject.GetComponent<Text>().text = "Integer, eg. 20";
} }
@ -247,7 +247,7 @@ namespace UnityExplorer.UI.Modules
var inputObj = UIFactory.CreateInputField(rowObj); var inputObj = UIFactory.CreateInputField(rowObj);
m_defaultOutputInput = inputObj.GetComponent<InputField>(); m_defaultOutputInput = inputObj.GetComponent<InputField>();
m_defaultOutputInput.text = ModConfig.Instance.Default_Output_Path.ToString(); m_defaultOutputInput.text = ExplorerConfig.Instance.Default_Output_Path.ToString();
m_defaultOutputInput.placeholder.gameObject.GetComponent<Text>().text = @"Directory, eg. Mods\UnityExplorer"; m_defaultOutputInput.placeholder.gameObject.GetComponent<Text>().text = @"Directory, eg. Mods\UnityExplorer";
} }

View File

@ -109,6 +109,9 @@ namespace UnityExplorer.UI.Modules
private void RefreshResultList() private void RefreshResultList()
{ {
if (m_resultListPageHandler == null || m_results == null)
return;
m_resultListPageHandler.ListCount = m_results.Length; m_resultListPageHandler.ListCount = m_results.Length;
int newCount = 0; int newCount = 0;

View File

@ -21,7 +21,7 @@ namespace UnityExplorer.UI.Shared
{ {
public PageHandler(SliderScrollbar scroll) public PageHandler(SliderScrollbar scroll)
{ {
ItemsPerPage = ModConfig.Instance?.Default_Page_Limit ?? 20; ItemsPerPage = ExplorerConfig.Instance?.Default_Page_Limit ?? 20;
m_scrollbar = scroll; m_scrollbar = scroll;
} }

View File

@ -8,6 +8,8 @@ using System.Reflection;
using UnityExplorer.Helpers; using UnityExplorer.Helpers;
using UnityExplorer.UI.Shared; using UnityExplorer.UI.Shared;
using UnityExplorer.Input; using UnityExplorer.Input;
using System;
using UnityExplorer.Config;
#if CPP #if CPP
using UnityExplorer.Unstrip; using UnityExplorer.Unstrip;
#endif #endif
@ -24,6 +26,43 @@ namespace UnityExplorer.UI
internal static Sprite ResizeCursor { get; private set; } internal static Sprite ResizeCursor { get; private set; }
internal static Shader BackupShader { get; private set; } internal static Shader BackupShader { get; private set; }
public static bool ShowMenu
{
get => s_showMenu;
set => SetShowMenu(value);
}
public static bool s_showMenu;
private static bool s_doneUIInit;
private static float s_timeSinceStartup;
internal static void CheckUIInit()
{
if (s_doneUIInit)
return;
s_timeSinceStartup += Time.deltaTime;
if (s_timeSinceStartup > 0.1f)
{
s_doneUIInit = true;
try
{
Init();
ExplorerCore.Log("Initialized UnityExplorer UI.");
if (ExplorerConfig.Instance.Hide_On_Startup)
ShowMenu = false;
// InspectorManager.Instance.Inspect(Tests.TestClass.Instance);
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception setting up UI: {e}");
}
}
}
public static void Init() public static void Init()
{ {
LoadBundle(); LoadBundle();
@ -40,13 +79,40 @@ namespace UnityExplorer.UI
Canvas.ForceUpdateCanvases(); Canvas.ForceUpdateCanvases();
} }
public static void OnSceneChange() private static void SetShowMenu(bool show)
{ {
SceneExplorer.Instance?.OnSceneChange(); if (s_showMenu == show)
SearchPage.Instance?.OnSceneChange(); return;
s_showMenu = show;
if (CanvasRoot)
{
CanvasRoot.SetActive(show);
if (show)
ForceUnlockCursor.SetEventSystem();
else
ForceUnlockCursor.ReleaseEventSystem();
}
ForceUnlockCursor.UpdateCursorControl();
} }
//public static void OnSceneChange()
//{
// SceneExplorer.Instance?.OnSceneChange();
// SearchPage.Instance?.OnSceneChange();
//}
public static void Update() public static void Update()
{ {
if (InputManager.GetKeyDown(ExplorerConfig.Instance.Main_Menu_Toggle))
ShowMenu = !ShowMenu;
if (!ShowMenu || !s_doneUIInit || !CanvasRoot)
return;
MainMenu.Instance?.Update(); MainMenu.Instance?.Update();
if (EventSys) if (EventSys)

View File

@ -8,36 +8,36 @@ namespace UnityExplorer.UI
{ {
public class UISyntaxHighlight public class UISyntaxHighlight
{ {
public const string Field_Static = "#8d8dc6"; public const string FIELD_STATIC = "#8d8dc6";
public const string Field_Instance = "#c266ff"; public const string FIELD_INSTANCE = "#c266ff";
public const string Method_Static = "#b55b02"; public const string METHOD_STATIC = "#b55b02";
public const string Method_Instance = "#ff8000"; public const string METHOD_INSTANCE = "#ff8000";
public const string Prop_Static = "#588075"; public const string PROP_STATIC = "#588075";
public const string Prop_Instance = "#55a38e"; public const string PROP_INSTANCE = "#55a38e";
public const string Class_Static = "#3a8d71"; public const string CLASS_STATIC = "#3a8d71";
public const string Class_Instance = "#2df7b2"; public const string CLASS_INSTANCE = "#2df7b2";
public const string Local = "#a6e9e9"; public const string CLASS_STRUCT = "#0fba3a";
public const string StructGreen = "#0fba3a"; public const string LOCAL_ARG = "#a6e9e9";
public static string Enum = "#92c470"; public static string CONST_VAR = "#92c470";
internal static readonly Color s_silver = new Color(0.66f, 0.66f, 0.66f); internal static readonly Color s_silver = new Color(0.66f, 0.66f, 0.66f);
internal static string GetClassColor(Type type) internal static string GetClassColor(Type type)
{ {
if (type.IsAbstract && type.IsSealed) if (type.IsAbstract && type.IsSealed)
return Class_Static; return CLASS_STATIC;
else if (type.IsEnum || type.IsGenericParameter) else if (type.IsEnum || type.IsGenericParameter)
return Enum; return CONST_VAR;
else if (type.IsValueType) else if (type.IsValueType)
return StructGreen; return CLASS_STRUCT;
else else
return Class_Instance; return CLASS_INSTANCE;
} }
public static string ParseFullSyntax(Type type, bool includeNamespace, MemberInfo memberInfo = null) public static string ParseFullSyntax(Type type, bool includeNamespace, MemberInfo memberInfo = null)
@ -49,7 +49,7 @@ namespace UnityExplorer.UI
if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter)) if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter))
{ {
ret = $"<color={Enum}>{type.Name}</color>"; ret = $"<color={CONST_VAR}>{type.Name}</color>";
} }
else else
{ {
@ -134,7 +134,7 @@ namespace UnityExplorer.UI
if (allGeneric) if (allGeneric)
{ {
args += $"<color={Enum}>{arg.Name}</color>"; args += $"<color={CONST_VAR}>{arg.Name}</color>";
continue; continue;
} }
@ -153,30 +153,30 @@ namespace UnityExplorer.UI
if (fi.IsStatic) if (fi.IsStatic)
{ {
isStatic = true; isStatic = true;
memberColor = Field_Static; memberColor = FIELD_STATIC;
} }
else else
memberColor = Field_Instance; memberColor = FIELD_INSTANCE;
} }
else if (memberInfo is MethodInfo mi) else if (memberInfo is MethodInfo mi)
{ {
if (mi.IsStatic) if (mi.IsStatic)
{ {
isStatic = true; isStatic = true;
memberColor = Method_Static; memberColor = METHOD_STATIC;
} }
else else
memberColor = Method_Instance; memberColor = METHOD_INSTANCE;
} }
else if (memberInfo is PropertyInfo pi) else if (memberInfo is PropertyInfo pi)
{ {
if (pi.GetAccessors(true)[0].IsStatic) if (pi.GetAccessors(true)[0].IsStatic)
{ {
isStatic = true; isStatic = true;
memberColor = Prop_Static; memberColor = PROP_STATIC;
} }
else else
memberColor = Prop_Instance; memberColor = PROP_INSTANCE;
} }
return memberColor; return memberColor;
} }

View File

@ -11,7 +11,7 @@
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
<OutputPath>..\Release\Explorer.MelonLoader.Il2Cpp\</OutputPath> <OutputPath>..\Release\UnityExplorer.MelonLoader.Il2Cpp\</OutputPath>
<DefineConstants> <DefineConstants>
</DefineConstants> </DefineConstants>
<IsCpp>false</IsCpp> <IsCpp>false</IsCpp>
@ -38,7 +38,6 @@
<AssemblyName>UnityExplorer.ML.IL2CPP</AssemblyName> <AssemblyName>UnityExplorer.ML.IL2CPP</AssemblyName>
<IsCpp>true</IsCpp> <IsCpp>true</IsCpp>
<IsMelonLoader>true</IsMelonLoader> <IsMelonLoader>true</IsMelonLoader>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_ML_Mono|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_ML_Mono|AnyCPU' ">
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
@ -48,25 +47,30 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
<IsCpp>false</IsCpp> <IsCpp>false</IsCpp>
<IsMelonLoader>true</IsMelonLoader> <IsMelonLoader>true</IsMelonLoader>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Cpp|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Cpp|AnyCPU' ">
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<OutputPath>..\Release\UnityExplorer.BepInEx.Il2Cpp\</OutputPath> <OutputPath>..\Release\UnityExplorer.BepInEx.Il2Cpp\</OutputPath>
<DefineConstants>CPP,BIE</DefineConstants> <DefineConstants>CPP,BIE,BIE6</DefineConstants>
<AssemblyName>UnityExplorer.BIE.IL2CPP</AssemblyName> <AssemblyName>UnityExplorer.BIE.IL2CPP</AssemblyName>
<IsCpp>true</IsCpp> <IsCpp>true</IsCpp>
<IsMelonLoader>false</IsMelonLoader> <IsMelonLoader>false</IsMelonLoader>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Mono|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE6_Mono|AnyCPU' ">
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<OutputPath>..\Release\UnityExplorer.BepInEx.Mono\</OutputPath> <OutputPath>..\Release\UnityExplorer.BepInEx6.Mono\</OutputPath>
<DefineConstants>MONO,BIE</DefineConstants> <DefineConstants>MONO,BIE,BIE6</DefineConstants>
<AssemblyName>UnityExplorer.BIE.Mono</AssemblyName> <AssemblyName>UnityExplorer.BIE6.Mono</AssemblyName>
<IsCpp>false</IsCpp>
<IsMelonLoader>false</IsMelonLoader>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release_BIE5_Mono|AnyCPU'">
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<OutputPath>..\Release\UnityExplorer.BepInEx5.Mono\</OutputPath>
<DefineConstants>MONO,BIE,BIE5</DefineConstants>
<AssemblyName>UnityExplorer.BIE5.Mono</AssemblyName>
<IsCpp>false</IsCpp> <IsCpp>false</IsCpp>
<IsMelonLoader>false</IsMelonLoader> <IsMelonLoader>false</IsMelonLoader>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release_STANDALONE_Mono|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release_STANDALONE_Mono|AnyCPU'">
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
@ -75,7 +79,7 @@
<AssemblyName>UnityExplorer.STANDALONE.Mono</AssemblyName> <AssemblyName>UnityExplorer.STANDALONE.Mono</AssemblyName>
<IsCpp>false</IsCpp> <IsCpp>false</IsCpp>
<IsMelonLoader>false</IsMelonLoader> <IsMelonLoader>false</IsMelonLoader>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <IsStandalone>true</IsStandalone>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release_STANDALONE_Cpp|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release_STANDALONE_Cpp|AnyCPU'">
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
@ -84,7 +88,7 @@
<AssemblyName>UnityExplorer.STANDALONE.IL2CPP</AssemblyName> <AssemblyName>UnityExplorer.STANDALONE.IL2CPP</AssemblyName>
<IsCpp>true</IsCpp> <IsCpp>true</IsCpp>
<IsMelonLoader>false</IsMelonLoader> <IsMelonLoader>false</IsMelonLoader>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <IsStandalone>true</IsStandalone>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="INIFileParser, Version=2.5.2.0, Culture=neutral, PublicKeyToken=79af7b307b65cf3c, processorArchitecture=MSIL"> <Reference Include="INIFileParser, Version=2.5.2.0, Culture=neutral, PublicKeyToken=79af7b307b65cf3c, processorArchitecture=MSIL">
@ -116,13 +120,13 @@
</ItemGroup> </ItemGroup>
<!-- MelonLoader Mono refs --> <!-- MelonLoader Mono refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|false'"> <ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|false'">
<Reference Include="MelonLoader.ModHandler"> <Reference Include="MelonLoader">
<HintPath>..\lib\MelonLoader.ModHandler.dll</HintPath> <HintPath>..\lib\MelonLoader.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<!-- BepInEx Mono refs --> <!-- BepInEx 5 Mono refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='false|false'"> <ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)|$(Configuration)'=='false|false|Release_BIE5_Mono'">
<Reference Include="BepInEx"> <Reference Include="BepInEx">
<HintPath>..\lib\BepInEx.dll</HintPath> <HintPath>..\lib\BepInEx.dll</HintPath>
<Private>False</Private> <Private>False</Private>
@ -132,10 +136,32 @@
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<!-- BepInEx 6 Mono refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)|$(Configuration)'=='false|false|Release_BIE6_Mono'">
<Reference Include="BepInEx">
<HintPath>..\lib\BepInEx.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="BepInEx.Unity">
<HintPath>..\lib\BepInEx.Unity.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="0Harmony">
<HintPath>..\lib\0Harmony.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- Standalone refs -->
<ItemGroup Condition="'$(IsStandalone)'=='true'">
<Reference Include="0Harmony">
<HintPath>..\lib\0Harmony.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- MelonLoader Il2Cpp refs --> <!-- MelonLoader Il2Cpp refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|true'"> <ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|true'">
<Reference Include="MelonLoader.ModHandler"> <Reference Include="MelonLoader">
<HintPath>$(MLCppGameFolder)\MelonLoader\MelonLoader.ModHandler.dll</HintPath> <HintPath>$(MLCppGameFolder)\MelonLoader\MelonLoader.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UnhollowerBaseLib"> <Reference Include="UnhollowerBaseLib">
@ -235,8 +261,8 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ExplorerStandalone.cs" /> <Compile Include="Loader\ExplorerBepIn6Plugin.cs" />
<Compile Include="Helpers\EventHelper.cs" /> <Compile Include="Loader\ExplorerStandalone.cs" />
<Compile Include="Inspectors\MouseInspector.cs" /> <Compile Include="Inspectors\MouseInspector.cs" />
<Compile Include="Inspectors\Reflection\CacheObject\CacheEnumerated.cs" /> <Compile Include="Inspectors\Reflection\CacheObject\CacheEnumerated.cs" />
<Compile Include="Inspectors\Reflection\CacheObject\CacheField.cs" /> <Compile Include="Inspectors\Reflection\CacheObject\CacheField.cs" />
@ -246,10 +272,10 @@
<Compile Include="Inspectors\Reflection\CacheObject\CacheProperty.cs" /> <Compile Include="Inspectors\Reflection\CacheObject\CacheProperty.cs" />
<Compile Include="Inspectors\Reflection\CacheObject\CacheObjectBase.cs" /> <Compile Include="Inspectors\Reflection\CacheObject\CacheObjectBase.cs" />
<Compile Include="Helpers\Texture2DHelpers.cs" /> <Compile Include="Helpers\Texture2DHelpers.cs" />
<Compile Include="Config\ModConfig.cs" /> <Compile Include="Config\ExplorerConfig.cs" />
<Compile Include="ExplorerCore.cs" /> <Compile Include="ExplorerCore.cs" />
<Compile Include="ExplorerBepInPlugin.cs" /> <Compile Include="Loader\ExplorerBepIn5Plugin.cs" />
<Compile Include="ExplorerMelonMod.cs" /> <Compile Include="Loader\ExplorerMelonMod.cs" />
<Compile Include="Helpers\ReflectionHelpers.cs" /> <Compile Include="Helpers\ReflectionHelpers.cs" />
<Compile Include="Helpers\UnityHelpers.cs" /> <Compile Include="Helpers\UnityHelpers.cs" />
<Compile Include="Inspectors\GameObjects\ChildList.cs" /> <Compile Include="Inspectors\GameObjects\ChildList.cs" />
@ -263,6 +289,10 @@
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveNumber.cs" /> <Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveNumber.cs" />
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveString.cs" /> <Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveString.cs" />
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveUnityStruct.cs" /> <Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveUnityStruct.cs" />
<Compile Include="Loader\IExplorerLoader.cs" />
<Compile Include="Runtime\Il2Cpp\Il2CppProvider.cs" />
<Compile Include="Runtime\RuntimeProvider.cs" />
<Compile Include="Runtime\Mono\MonoProvider.cs" />
<Compile Include="UI\ForceUnlockCursor.cs" /> <Compile Include="UI\ForceUnlockCursor.cs" />
<Compile Include="Input\IHandleInput.cs" /> <Compile Include="Input\IHandleInput.cs" />
<Compile Include="Tests\Tests.cs" /> <Compile Include="Tests\Tests.cs" />

View File

@ -8,7 +8,8 @@ EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release_BIE_Cpp|Any CPU = Release_BIE_Cpp|Any CPU Release_BIE_Cpp|Any CPU = Release_BIE_Cpp|Any CPU
Release_BIE_Mono|Any CPU = Release_BIE_Mono|Any CPU Release_BIE5_Mono|Any CPU = Release_BIE5_Mono|Any CPU
Release_BIE6_Mono|Any CPU = Release_BIE6_Mono|Any CPU
Release_ML_Cpp|Any CPU = Release_ML_Cpp|Any CPU Release_ML_Cpp|Any CPU = Release_ML_Cpp|Any CPU
Release_ML_Mono|Any CPU = Release_ML_Mono|Any CPU Release_ML_Mono|Any CPU = Release_ML_Mono|Any CPU
Release_STANDALONE_Cpp|Any CPU = Release_STANDALONE_Cpp|Any CPU Release_STANDALONE_Cpp|Any CPU = Release_STANDALONE_Cpp|Any CPU
@ -17,8 +18,10 @@ Global
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Cpp|Any CPU.ActiveCfg = Release_BIE_Cpp|Any CPU {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Cpp|Any CPU.ActiveCfg = Release_BIE_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Cpp|Any CPU.Build.0 = Release_BIE_Cpp|Any CPU {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Cpp|Any CPU.Build.0 = Release_BIE_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Mono|Any CPU.ActiveCfg = Release_BIE_Mono|Any CPU {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE5_Mono|Any CPU.ActiveCfg = Release_BIE5_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Mono|Any CPU.Build.0 = Release_BIE_Mono|Any CPU {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE5_Mono|Any CPU.Build.0 = Release_BIE5_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE6_Mono|Any CPU.ActiveCfg = Release_BIE6_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE6_Mono|Any CPU.Build.0 = Release_BIE6_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Cpp|Any CPU.ActiveCfg = Release_ML_Cpp|Any CPU {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Cpp|Any CPU.ActiveCfg = Release_ML_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Cpp|Any CPU.Build.0 = Release_ML_Cpp|Any CPU {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Cpp|Any CPU.Build.0 = Release_ML_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Mono|Any CPU.ActiveCfg = Release_ML_Mono|Any CPU {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Mono|Any CPU.ActiveCfg = Release_ML_Mono|Any CPU

View File

@ -3,36 +3,25 @@ using UnityExplorer.Helpers;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using UnityExplorer.Inspectors; using UnityExplorer.Inspectors;
using System.Reflection;
namespace UnityExplorer.Unstrip namespace UnityExplorer.Unstrip
{ {
public class SceneUnstrip public static class SceneUnstrip
{ {
#if MONO #if MONO
public static GameObject[] GetRootGameObjects(Scene scene) => scene.GetRootGameObjects(); private static readonly FieldInfo fi_Scene_handle = typeof(Scene).GetField("m_Handle", ReflectionHelpers.CommonFlags);
//public static GameObject[] GetRootGameObjects(int handle)
//{
// Scene scene = default;
// if (handle == SceneExplorer.DontDestroyHandle)
// scene = SceneExplorer.DontDestroyObject.scene;
// else
// {
// for (int i = 0; i < SceneManager.sceneCount; i++)
// {
// var iscene = SceneManager.GetSceneAt(i);
// if (iscene.handle == handle)
// scene = iscene;
// }
// }
// if (scene != default && scene.handle != -1)
// return scene.GetRootGameObjects();
// return new GameObject[0];
//}
#endif #endif
public static int GetHandle(this Scene scene)
{
#if CPP
return scene.handle;
#else
return (int)fi_Scene_handle.GetValue(scene);
#endif
}
#if CPP #if CPP
//Scene.GetRootGameObjects(); //Scene.GetRootGameObjects();
@ -43,13 +32,16 @@ namespace UnityExplorer.Unstrip
public static GameObject[] GetRootGameObjects(int handle) public static GameObject[] GetRootGameObjects(int handle)
{ {
if (handle == -1) if (handle == -1)
{
return new GameObject[0]; return new GameObject[0];
}
Il2CppSystem.Collections.Generic.List<GameObject> list = new Il2CppSystem.Collections.Generic.List<GameObject>(GetRootCount(handle)); int count = GetRootCount(handle);
d_GetRootGameObjects iCall = ICallHelper.GetICall<d_GetRootGameObjects>("UnityEngine.SceneManagement.Scene::GetRootGameObjectsInternal"); if (count < 1)
return new GameObject[0];
var list = new Il2CppSystem.Collections.Generic.List<GameObject>(count);
var iCall = ICallHelper.GetICall<d_GetRootGameObjects>("UnityEngine.SceneManagement.Scene::GetRootGameObjectsInternal");
iCall.Invoke(handle, list.Pointer); iCall.Invoke(handle, list.Pointer);
@ -58,14 +50,14 @@ namespace UnityExplorer.Unstrip
//Scene.rootCount; //Scene.rootCount;
internal delegate int GetRootCountInternal_delegate(int handle); internal delegate int d_GetRootCountInternal(int handle);
public static int GetRootCount(Scene scene) => GetRootCount(scene.handle); public static int GetRootCount(Scene scene) => GetRootCount(scene.handle);
public static int GetRootCount(int handle) public static int GetRootCount(int handle)
{ {
GetRootCountInternal_delegate iCall = ICallHelper.GetICall<GetRootCountInternal_delegate>("UnityEngine.SceneManagement.Scene::GetRootCountInternal"); return ICallHelper.GetICall<d_GetRootCountInternal>("UnityEngine.SceneManagement.Scene::GetRootCountInternal")
return iCall.Invoke(handle); .Invoke(handle);
} }
#endif #endif
} }