mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-22 08:32:51 +08:00
Compare commits
95 Commits
Author | SHA1 | Date | |
---|---|---|---|
6d479a6703 | |||
6539f818c3 | |||
bc5ffcab40 | |||
d070ded036 | |||
8f025622b4 | |||
89f137680e | |||
f280b45ed3 | |||
456e15020f | |||
d33d46927d | |||
7a872cecf9 | |||
b2cbdc1802 | |||
3501a28fd1 | |||
40f698122d | |||
0555a644b7 | |||
f66a04c93f | |||
8f07255d1b | |||
46f35129c5 | |||
604c499822 | |||
8964c48ba0 | |||
e85a3e0f1e | |||
c658d393f5 | |||
9a45e29e02 | |||
418ece55e3 | |||
bf455893e7 | |||
6d9cb8205a | |||
0f431e997b | |||
2107df70ad | |||
a9fbea7c96 | |||
77878ddd94 | |||
594abc47f8 | |||
5e326916a2 | |||
31c2debb78 | |||
d919497e43 | |||
0c40b4fad9 | |||
bba912667f | |||
1807e7c5ff | |||
9da2ea9b1b | |||
1a5843f8e1 | |||
25e48f2f37 | |||
1c0011bef9 | |||
9e996816ef | |||
9665753dc8 | |||
942e9d7555 | |||
9efb9581f5 | |||
f10a462b00 | |||
9072b16c5a | |||
21408993c2 | |||
7a8b5b50d1 | |||
1a5e843070 | |||
ade7539fde | |||
5c588e5a03 | |||
af094832fe | |||
af0ee2e690 | |||
d2d6fb4d55 | |||
6c25662fe9 | |||
4bcf82ca10 | |||
ce38e8ac50 | |||
12cd718f12 | |||
995e2a3e93 | |||
2c95fec646 | |||
69912d7ea4 | |||
9c5596ace4 | |||
d4dac58fc8 | |||
77b97cbe17 | |||
c6f0f34ac0 | |||
d1f4f74d32 | |||
f13068bf01 | |||
dfc288a101 | |||
544009dc21 | |||
fdfaaadd89 | |||
58d60a10d4 | |||
0432c6d56c | |||
8c34aa2be5 | |||
4a1c54fac1 | |||
190467fa5c | |||
44f54d9190 | |||
3b4ea31b50 | |||
ad7b05f721 | |||
852ca8e9eb | |||
7386eca0c2 | |||
97325a5f3a | |||
82e52de557 | |||
28181e2266 | |||
6dfa4806ce | |||
27f6a6ca52 | |||
cd7b260ea7 | |||
ba986274be | |||
d181c0bee9 | |||
a9faec8cf9 | |||
a6bf91b7af | |||
e7c5170232 | |||
be635e46a0 | |||
687f56eac9 | |||
1dfcdb2dca | |||
0025c83930 |
165
README.md
165
README.md
@ -5,31 +5,68 @@
|
||||
<p align="center">
|
||||
An in-game explorer and a suite of debugging tools for <a href="https://docs.unity3d.com/Manual/IL2CPP.html">IL2CPP</a> and <b>Mono</b> Unity games, to aid with modding development.
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="../../releases/latest">
|
||||
<img src="https://img.shields.io/github/release/sinai-dev/Explorer.svg" />
|
||||
</a>
|
||||
|
||||
<img src="https://img.shields.io/github/downloads/sinai-dev/Explorer/total.svg" />
|
||||
</p>
|
||||
|
||||
- [Releases](#releases)
|
||||
- [Features](#features)
|
||||
- [How to install](#how-to-install)
|
||||
- [Mod Config](#mod-config)
|
||||
- [Mouse Control](#mouse-control)
|
||||
- [Building](#building)
|
||||
- [Credits](#credits)
|
||||
|
||||
## Releases
|
||||
## Releases [](../../releases/latest) []() []()
|
||||
|
||||
| Mod Loader | IL2CPP | Mono |
|
||||
| ----------- | ------ | ---- |
|
||||
| [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) | ✔️ [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) |
|
||||
| [BepInEx](https://github.com/BepInEx/BepInEx) 5.X | ✖️ n/a | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.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) |
|
||||
|
||||
<b>IL2CPP Issues:</b>
|
||||
* Some methods may still fail with a `MissingMethodException`, please let me know if you experience this (with full debug log please).
|
||||
## How to install
|
||||
|
||||
### BepInEx
|
||||
|
||||
1. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game. For IL2CPP you should use [BepInEx 6 (Bleeding Edge)](https://builds.bepis.io/projects/bepinex_be), for Mono you should use [BepInEx 5](https://github.com/BepInEx/BepInEx/releases) (until Mono support stabilizes in BepInEx 6).
|
||||
2. Download the UnityExplorer release for BepInEx IL2CPP or Mono above.
|
||||
3. Take the `UnityExplorer.BIE.___.dll` file and put it in `[GameFolder]\BepInEx\plugins\`
|
||||
4. In IL2CPP, you will need to download the [Unity libs](https://github.com/LavaGang/Unity-Runtime-Libraries) for the game's Unity version and put them in the `BepInEx\unity-libs\` folder.
|
||||
|
||||
### MelonLoader
|
||||
|
||||
1. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3+ for your game. Version 0.3 is currently in pre-release, so you must "Enable ALPHA Releases" in your MelonLoader Installer settings to see the option for it.
|
||||
2. Download the UnityExplorer release for MelonLoader IL2CPP or Mono above.
|
||||
3. Take the `UnityExplorer.ML.___.dll` file and put it in the `[GameFolder]\Mods\` folder.
|
||||
|
||||
### Standalone
|
||||
|
||||
The standalone release requires you to also load `0Harmony.dll` (HarmonyX, from the `lib\` folder) to function properly, and the IL2CPP also requires `UnhollowerBaseLib.dll` as well. The Mono release should be fairly easy to use with any loader, but the IL2CPP one may be tricky, I'd recommend just using BepInEx or MelonLoader for IL2CPP.
|
||||
|
||||
1. Load the UnityExplorer DLL from your mod or inject it, as well as `0Harmony.dll` and `UnhollowerBaseLib.dll` as required.
|
||||
2. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();`
|
||||
3. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish.
|
||||
|
||||
## Issues and contributions
|
||||
|
||||
Both issue reports and PR contributions are welcome in this repository.
|
||||
|
||||
### Issue reporting
|
||||
|
||||
To report an issue with UnityExplorer, please use the following template (as well as any other information / images you can provide).
|
||||
|
||||
Template:
|
||||
|
||||
```
|
||||
* Game issue occurs on:
|
||||
* UnityExplorer version: (eg BIE.IL2CPP v3.3.3, etc)
|
||||
|
||||
* Description of issue:
|
||||
|
||||
* Unity log link:
|
||||
* Mod Loader log link:
|
||||
```
|
||||
|
||||
Please upload your Unity log file to [Pastebin](https://pastebin.com/) (or equivalent service) and provide a link to the paste.
|
||||
|
||||
* The log should be found at `%userprofile%\AppData\LocalLow\[Company]\[Game]\` unless redirected by your Mod Loader.
|
||||
* The file will be called either `output_log.txt` or `Player.log`
|
||||
|
||||
As well as the Unity log, please upload your Mod Loader's log:
|
||||
|
||||
* BepInEx: `BepInEx\LogOutput.log`
|
||||
* MelonLoader: `MelonLoader\Latest.log`
|
||||
|
||||
## Features
|
||||
|
||||
@ -44,69 +81,65 @@
|
||||
* <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>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
|
||||
### C# Console Tips
|
||||
|
||||
### BepInEx
|
||||
The C# Console can be used to define temporary classes and methods, or it can be used to evaluate an expression, but you cannot do both at the same time.
|
||||
|
||||
0. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game.
|
||||
1. Download the UnityExplorer release for BepInEx IL2CPP or Mono above.
|
||||
2. Take the `UnityExplorer.dll` file and put it in `[GameFolder]\BepInEx\plugins\`
|
||||
3. Take the `UnityExplorer\` folder (with `explorerui.bundle`) and put it in `[GameFolder]\Mods\`, so it looks like `[GameFolder]\Mods\UnityExplorer\explorerui.bundle`.
|
||||
4. In IL2CPP, it is highly recommended to get the base Unity libs for the game's Unity version and put them in the `BepInEx\unhollowed\base\` folder.
|
||||
For example, you could run this code to define a temporary class (it will be visible within the console until you run `Reset();`).
|
||||
|
||||
### MelonLoader
|
||||
```csharp
|
||||
public class MyClass
|
||||
{
|
||||
public static void Method()
|
||||
{
|
||||
UnityExplorer.ExplorerCore.Log("hello");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
0. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) for your game.
|
||||
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.dll` and `[GameFolder]\Mods\UnityExplorer\explorerui.bundle`.
|
||||
You could then delete or comment out the class and run the following expression to run that method:
|
||||
|
||||
## Mod Config
|
||||
```csharp
|
||||
MyClass.Method();
|
||||
```
|
||||
|
||||
You can access the settings via the "Options" page of the main menu, or directly from the config at `Mods\UnityExplorer\config.xml` (generated after first launch).
|
||||
However, you cannot define a class and run it both at the same time. You must either define class(es) and run that, or define an expression and run that.
|
||||
|
||||
`Main Menu Toggle` (KeyCode)
|
||||
* Default: `F7`
|
||||
* See [this article](https://docs.unity3d.com/ScriptReference/KeyCode.html) for a full list of all accepted KeyCodes.
|
||||
You can also make use of the helper methods in the console to simplify some tasks, which you can see listed when the console has nothing entered for input. These methods are **not** accessible within any temporary classes you define, they can only be used in the expression context.
|
||||
|
||||
`Force Unlock Mouse` (bool)
|
||||
* Default: `true`
|
||||
* Forces the cursor to be unlocked and visible while the UnityExplorer menu is open, and prevents anything else taking control.
|
||||
### Logging
|
||||
|
||||
`Default Page Limit` (int)
|
||||
* Default: `25`
|
||||
* Sets the default items per page when viewing lists or search results.
|
||||
* <b>Requires a restart to take effect</b>, apart from Reflection Inspector tabs.
|
||||
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.
|
||||
|
||||
`Default Output Path` (string)
|
||||
* Default: `Mods\Explorer`
|
||||
* 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.
|
||||
These logs are also visible in the Debug Console part of the UI.
|
||||
|
||||
`Log Unity Debug` (bool)
|
||||
* Default: `false`
|
||||
* Listens for Unity `Debug.Log` messages and prints them to UnityExplorer's log.
|
||||
### Settings
|
||||
|
||||
You can change the settings via the "Options" page of the main menu, or directly from the config file.
|
||||
|
||||
Depending on the release you are using, the config file will be found at:
|
||||
* BepInEx: `BepInEx\config\com.sinai.unityexplorer.cfg`
|
||||
* MelonLoader: `UserData\MelonPreferences.cfg`
|
||||
* Standalone `{DLL_location}\UnityExplorer\config.ini`
|
||||
|
||||
## 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.
|
||||
Building the project should be straight-forward, the references are all inside the `lib\` folder.
|
||||
|
||||
1. Install MelonLoader or BepInEx for your game.
|
||||
2. Open the `src\Explorer.csproj` file in a text editor.
|
||||
3. Set the relevant `GameFolder` values for the versions you want to build, eg. set `MLCppGameFolder` if you want to build for a MelonLoader IL2CPP game.
|
||||
4. Open the `src\Explorer.sln` project.
|
||||
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.
|
||||
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 in Visual Studio.
|
||||
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. Alternatively, use "Batch Build" and select all releases.
|
||||
3. The DLLs are built to the `Release\` folder in the root of the repository.
|
||||
4. If ILRepack complains about an error, just change the Active config to a different release and then back again. This sometimes happens for the first time you build the project.
|
||||
|
||||
## Credits
|
||||
## Acknowledgments
|
||||
|
||||
Written by Sinai.
|
||||
* [ManlyMarco](https://github.com/ManlyMarco) for [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor), snippets from the REPL Console were used for UnityExplorer's C# Console.
|
||||
* [denikson](https://github.com/denikson) (aka Horse) for [mcs-unity](https://github.com/denikson/mcs-unity), used as the `Mono.CSharp` reference for the C# Console.
|
||||
* [HerpDerpenstine](https://github.com/HerpDerpinstine) for [MelonCoroutines](https://github.com/LavaGang/MelonLoader/blob/master/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs), they were included for standalone IL2CPP coroutine support.
|
||||
* [InGameCodeEditor](https://assetstore.unity.com/packages/tools/gui/ingame-code-editor-144254) was used as the base for the syntax highlighting for UnityExplorer's C# console (`UnityExplorer.UI.Main.CSConsole.Lexer`).
|
||||
|
||||
### Licensing
|
||||
### Disclaimer
|
||||
|
||||
This project uses code from:
|
||||
* (GPL) [ManlyMarco](https://github.com/ManlyMarco)'s [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor), which I used for some aspects of the C# Console and Auto-Complete features. The snippets I used are indicated with a comment.
|
||||
* (MIT) [denikson](https://github.com/denikson) (aka Horse)'s [mcs-unity](https://github.com/denikson/mcs-unity). I commented out the `SkipVisibilityExt` constructor since it was causing an exception with the Hook it attempted in IL2CPP.
|
||||
* (Apache) [InGameCodeEditor](https://assetstore.unity.com/packages/tools/gui/ingame-code-editor-144254) was used as the base for the syntax highlighting for UnityExplorer's C# console, although it has been heavily rewritten and optimized. Used classes are in the `UnityExplorer.CSConsole.Lexer` namespace.
|
||||
UnityExplorer is in no way associated with Unity Technologies. "Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere.
|
||||
|
BIN
lib/0Harmony.dll
BIN
lib/0Harmony.dll
Binary file not shown.
BIN
lib/BepInEx.Unity.dll
Normal file
BIN
lib/BepInEx.Unity.dll
Normal file
Binary file not shown.
BIN
lib/BepInEx.dll
BIN
lib/BepInEx.dll
Binary file not shown.
BIN
lib/INIFileParser.dll
Normal file
BIN
lib/INIFileParser.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
lib/MelonLoader.dll
Normal file
BIN
lib/MelonLoader.dll
Normal file
Binary file not shown.
BIN
lib/UnhollowerBaseLib.dll
Normal file
BIN
lib/UnhollowerBaseLib.dll
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
lib/unhollowed/Il2CppSystem.Core.dll
Normal file
BIN
lib/unhollowed/Il2CppSystem.Core.dll
Normal file
Binary file not shown.
BIN
lib/unhollowed/Il2Cppmscorlib.dll
Normal file
BIN
lib/unhollowed/Il2Cppmscorlib.dll
Normal file
Binary file not shown.
BIN
lib/unhollowed/UnityEngine.CoreModule.dll
Normal file
BIN
lib/unhollowed/UnityEngine.CoreModule.dll
Normal file
Binary file not shown.
BIN
lib/unhollowed/UnityEngine.IMGUIModule.dll
Normal file
BIN
lib/unhollowed/UnityEngine.IMGUIModule.dll
Normal file
Binary file not shown.
BIN
lib/unhollowed/UnityEngine.PhysicsModule.dll
Normal file
BIN
lib/unhollowed/UnityEngine.PhysicsModule.dll
Normal file
Binary file not shown.
BIN
lib/unhollowed/UnityEngine.TextRenderingModule.dll
Normal file
BIN
lib/unhollowed/UnityEngine.TextRenderingModule.dll
Normal file
Binary file not shown.
BIN
lib/unhollowed/UnityEngine.UI.dll
Normal file
BIN
lib/unhollowed/UnityEngine.UI.dll
Normal file
Binary file not shown.
BIN
lib/unhollowed/UnityEngine.UIModule.dll
Normal file
BIN
lib/unhollowed/UnityEngine.UIModule.dll
Normal file
Binary file not shown.
BIN
lib/unhollowed/UnityEngine.dll
Normal file
BIN
lib/unhollowed/UnityEngine.dll
Normal file
Binary file not shown.
@ -1,68 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Xml.Serialization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.Config
|
||||
{
|
||||
public class ModConfig
|
||||
{
|
||||
[XmlIgnore] public static readonly XmlSerializer Serializer = new XmlSerializer(typeof(ModConfig));
|
||||
|
||||
//[XmlIgnore] private const string EXPLORER_FOLDER = @"Mods\UnityExplorer";
|
||||
[XmlIgnore] private const string SETTINGS_PATH = ExplorerCore.EXPLORER_FOLDER + @"\config.xml";
|
||||
|
||||
[XmlIgnore] public static ModConfig Instance;
|
||||
|
||||
// Actual configs
|
||||
public KeyCode Main_Menu_Toggle = KeyCode.F7;
|
||||
public bool Force_Unlock_Mouse = true;
|
||||
public int Default_Page_Limit = 25;
|
||||
public string Default_Output_Path = ExplorerCore.EXPLORER_FOLDER;
|
||||
public bool Log_Unity_Debug = false;
|
||||
public bool Save_Logs_To_Disk = true;
|
||||
|
||||
public static event Action OnConfigChanged;
|
||||
|
||||
internal static void InvokeConfigChanged()
|
||||
{
|
||||
OnConfigChanged?.Invoke();
|
||||
}
|
||||
|
||||
public static void OnLoad()
|
||||
{
|
||||
if (LoadSettings())
|
||||
return;
|
||||
|
||||
Instance = new ModConfig();
|
||||
SaveSettings();
|
||||
}
|
||||
|
||||
public static bool LoadSettings()
|
||||
{
|
||||
if (!File.Exists(SETTINGS_PATH))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
using (var file = File.OpenRead(SETTINGS_PATH))
|
||||
Instance = (ModConfig)Serializer.Deserialize(file);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Instance != null;
|
||||
}
|
||||
|
||||
public static void SaveSettings()
|
||||
{
|
||||
if (File.Exists(SETTINGS_PATH))
|
||||
File.Delete(SETTINGS_PATH);
|
||||
|
||||
using (var file = File.Create(SETTINGS_PATH))
|
||||
Serializer.Serialize(file, Instance);
|
||||
}
|
||||
}
|
||||
}
|
29
src/Core/CSharp/DummyBehaviour.cs
Normal file
29
src/Core/CSharp/DummyBehaviour.cs
Normal file
@ -0,0 +1,29 @@
|
||||
#if MONO
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.Core.CSharp
|
||||
{
|
||||
public class DummyBehaviour : MonoBehaviour
|
||||
{
|
||||
public static DummyBehaviour Instance;
|
||||
|
||||
public static void Setup()
|
||||
{
|
||||
var obj = new GameObject("Explorer_DummyBehaviour");
|
||||
DontDestroyOnLoad(obj);
|
||||
obj.hideFlags |= HideFlags.HideAndDontSave;
|
||||
|
||||
obj.AddComponent<DummyBehaviour>();
|
||||
}
|
||||
|
||||
internal void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -4,9 +4,9 @@ using System.IO;
|
||||
using System.Reflection;
|
||||
using Mono.CSharp;
|
||||
|
||||
// Thanks to ManlyMarco for this
|
||||
// Thanks to ManlyMarco for most of this
|
||||
|
||||
namespace UnityExplorer.CSConsole
|
||||
namespace UnityExplorer.Core.CSharp
|
||||
{
|
||||
public class ScriptEvaluator : Evaluator, IDisposable
|
||||
{
|
||||
@ -15,11 +15,12 @@ namespace UnityExplorer.CSConsole
|
||||
"mscorlib", "System.Core", "System", "System.Xml"
|
||||
};
|
||||
|
||||
private readonly TextWriter tw;
|
||||
internal static TextWriter _textWriter;
|
||||
internal static StreamReportPrinter _reportPrinter;
|
||||
|
||||
public ScriptEvaluator(TextWriter tw) : base(BuildContext(tw))
|
||||
{
|
||||
this.tw = tw;
|
||||
_textWriter = tw;
|
||||
|
||||
ImportAppdomainAssemblies(ReferenceAssembly);
|
||||
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
|
||||
@ -28,23 +29,22 @@ namespace UnityExplorer.CSConsole
|
||||
public void Dispose()
|
||||
{
|
||||
AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
|
||||
tw.Dispose();
|
||||
_textWriter.Dispose();
|
||||
}
|
||||
|
||||
private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
|
||||
{
|
||||
string name = args.LoadedAssembly.GetName().Name;
|
||||
|
||||
if (StdLib.Contains(name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReferenceAssembly(args.LoadedAssembly);
|
||||
}
|
||||
|
||||
private static CompilerContext BuildContext(TextWriter tw)
|
||||
{
|
||||
var reporter = new StreamReportPrinter(tw);
|
||||
_reportPrinter = new StreamReportPrinter(tw);
|
||||
|
||||
var settings = new CompilerSettings
|
||||
{
|
||||
@ -56,7 +56,7 @@ namespace UnityExplorer.CSConsole
|
||||
EnhancedWarnings = false
|
||||
};
|
||||
|
||||
return new CompilerContext(settings, reporter);
|
||||
return new CompilerContext(settings, _reportPrinter);
|
||||
}
|
||||
|
||||
private static void ImportAppdomainAssemblies(Action<Assembly> import)
|
@ -1,10 +1,14 @@
|
||||
using System;
|
||||
using Mono.CSharp;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Modules;
|
||||
using UnityExplorer.Inspectors;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
using UnityExplorer.UI.Main.CSConsole;
|
||||
using UnityExplorer.UI.Main.Home;
|
||||
|
||||
namespace UnityExplorer.CSConsole
|
||||
namespace UnityExplorer.Core.CSharp
|
||||
{
|
||||
public class ScriptInteraction : InteractiveBase
|
||||
{
|
||||
@ -13,19 +17,24 @@ namespace UnityExplorer.CSConsole
|
||||
ExplorerCore.Log(message);
|
||||
}
|
||||
|
||||
public static void StartCoroutine(IEnumerator ienumerator)
|
||||
{
|
||||
RuntimeProvider.Instance.StartConsoleCoroutine(ienumerator);
|
||||
}
|
||||
|
||||
public static void AddUsing(string directive)
|
||||
{
|
||||
CSConsolePage.Instance.AddUsing(directive);
|
||||
CSharpConsole.Instance.AddUsing(directive);
|
||||
}
|
||||
|
||||
public static void GetUsing()
|
||||
{
|
||||
ExplorerCore.Log(CSConsolePage.Instance.m_evaluator.GetUsing());
|
||||
ExplorerCore.Log(CSharpConsole.Instance.Evaluator.GetUsing());
|
||||
}
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
CSConsolePage.Instance.ResetConsole();
|
||||
CSharpConsole.Instance.ResetConsole();
|
||||
}
|
||||
|
||||
public static object CurrentTarget()
|
@ -3,9 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.UI.Main.CSConsole;
|
||||
|
||||
namespace UnityExplorer.CSConsole
|
||||
namespace UnityExplorer.Core.CSharp
|
||||
{
|
||||
public struct Suggestion
|
||||
{
|
||||
@ -45,10 +46,10 @@ namespace UnityExplorer.CSConsole
|
||||
|
||||
// ~~~~ Static ~~~~
|
||||
|
||||
public static HashSet<string> Namespaces => m_namspaces ?? GetNamespaces();
|
||||
private static HashSet<string> m_namspaces;
|
||||
public static HashSet<string> Namespaces => m_namespaces ?? GetNamespaces();
|
||||
private static HashSet<string> m_namespaces;
|
||||
|
||||
public static HashSet<string> Keywords => m_keywords ?? (m_keywords = new HashSet<string>(CSharpLexer.validKeywordMatcher.Keywords));
|
||||
public static HashSet<string> Keywords => m_keywords ?? (m_keywords = new HashSet<string>(CSLexerHighlighter.validKeywordMatcher.Keywords));
|
||||
private static HashSet<string> m_keywords;
|
||||
|
||||
private static readonly Color keywordColor = new Color(80f / 255f, 150f / 255f, 215f / 255f);
|
||||
@ -61,7 +62,7 @@ namespace UnityExplorer.CSConsole
|
||||
.Where(x => x.IsPublic && !string.IsNullOrEmpty(x.Namespace))
|
||||
.Select(x => x.Namespace));
|
||||
|
||||
return m_namspaces = set;
|
||||
return m_namespaces = set;
|
||||
|
||||
IEnumerable<Type> GetTypes(Assembly asm) => asm.TryGetTypes();
|
||||
}
|
74
src/Core/Config/ConfigElement.cs
Normal file
74
src/Core/Config/ConfigElement.cs
Normal file
@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityExplorer.Core.Config
|
||||
{
|
||||
public class ConfigElement<T> : IConfigElement
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Description { get; }
|
||||
|
||||
public bool IsInternal { get; }
|
||||
public Type ElementType => typeof(T);
|
||||
|
||||
public Action<T> OnValueChanged;
|
||||
public Action OnValueChangedNotify { get; set; }
|
||||
|
||||
public object DefaultValue { get; }
|
||||
|
||||
public T Value
|
||||
{
|
||||
get => m_value;
|
||||
set => SetValue(value);
|
||||
}
|
||||
private T m_value;
|
||||
|
||||
object IConfigElement.BoxedValue
|
||||
{
|
||||
get => m_value;
|
||||
set => SetValue((T)value);
|
||||
}
|
||||
|
||||
public ConfigElement(string name, string description, T defaultValue, bool isInternal = false)
|
||||
{
|
||||
Name = name;
|
||||
Description = description;
|
||||
|
||||
m_value = defaultValue;
|
||||
DefaultValue = defaultValue;
|
||||
|
||||
IsInternal = isInternal;
|
||||
|
||||
ConfigManager.RegisterConfigElement(this);
|
||||
}
|
||||
|
||||
private void SetValue(T value)
|
||||
{
|
||||
if ((m_value == null && value == null) || (m_value != null && m_value.Equals(value)))
|
||||
return;
|
||||
|
||||
m_value = value;
|
||||
|
||||
ConfigManager.Handler.SetConfigValue(this, value);
|
||||
|
||||
OnValueChanged?.Invoke(value);
|
||||
OnValueChangedNotify?.Invoke();
|
||||
|
||||
ConfigManager.Handler.OnAnyConfigChanged();
|
||||
}
|
||||
|
||||
object IConfigElement.GetLoaderConfigValue() => GetLoaderConfigValue();
|
||||
|
||||
public T GetLoaderConfigValue()
|
||||
{
|
||||
return ConfigManager.Handler.GetConfigValue(this);
|
||||
}
|
||||
|
||||
public void RevertToDefaultValue()
|
||||
{
|
||||
Value = (T)DefaultValue;
|
||||
}
|
||||
}
|
||||
}
|
24
src/Core/Config/ConfigHandler.cs
Normal file
24
src/Core/Config/ConfigHandler.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityExplorer.Core.Config
|
||||
{
|
||||
public abstract class ConfigHandler
|
||||
{
|
||||
public abstract void RegisterConfigElement<T>(ConfigElement<T> element);
|
||||
|
||||
public abstract void SetConfigValue<T>(ConfigElement<T> element, T value);
|
||||
|
||||
public abstract T GetConfigValue<T>(ConfigElement<T> element);
|
||||
|
||||
public abstract void Init();
|
||||
|
||||
public abstract void LoadConfig();
|
||||
|
||||
public abstract void SaveConfig();
|
||||
|
||||
public virtual void OnAnyConfigChanged() { }
|
||||
}
|
||||
}
|
246
src/Core/Config/ConfigManager.cs
Normal file
246
src/Core/Config/ConfigManager.cs
Normal file
@ -0,0 +1,246 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.UI.Main;
|
||||
using UnityExplorer.UI.Main.Home;
|
||||
|
||||
namespace UnityExplorer.Core.Config
|
||||
{
|
||||
public static class ConfigManager
|
||||
{
|
||||
// Each Mod Loader has its own ConfigHandler.
|
||||
// See the UnityExplorer.Loader namespace for the implementations.
|
||||
public static ConfigHandler Handler { get; private set; }
|
||||
|
||||
public static ConfigElement<KeyCode> Main_Menu_Toggle;
|
||||
public static ConfigElement<bool> Force_Unlock_Mouse;
|
||||
public static ConfigElement<int> Default_Page_Limit;
|
||||
public static ConfigElement<string> Default_Output_Path;
|
||||
public static ConfigElement<bool> Log_Unity_Debug;
|
||||
public static ConfigElement<bool> Hide_On_Startup;
|
||||
|
||||
public static ConfigElement<string> Last_Window_Anchors;
|
||||
public static ConfigElement<string> Last_Window_Position;
|
||||
public static ConfigElement<int> Last_Active_Tab;
|
||||
public static ConfigElement<bool> Last_DebugConsole_State;
|
||||
public static ConfigElement<bool> Last_SceneExplorer_State;
|
||||
|
||||
internal static readonly Dictionary<string, IConfigElement> ConfigElements = new Dictionary<string, IConfigElement>();
|
||||
|
||||
public static void Init(ConfigHandler configHandler)
|
||||
{
|
||||
Handler = configHandler;
|
||||
Handler.Init();
|
||||
|
||||
CreateConfigElements();
|
||||
|
||||
Handler.LoadConfig();
|
||||
|
||||
SceneExplorer.OnToggleShow += SceneExplorer_OnToggleShow;
|
||||
PanelDragger.OnFinishResize += PanelDragger_OnFinishResize;
|
||||
PanelDragger.OnFinishDrag += PanelDragger_OnFinishDrag;
|
||||
MainMenu.OnActiveTabChanged += MainMenu_OnActiveTabChanged;
|
||||
DebugConsole.OnToggleShow += DebugConsole_OnToggleShow;
|
||||
|
||||
InitConsoleCallback();
|
||||
}
|
||||
|
||||
internal static void RegisterConfigElement<T>(ConfigElement<T> configElement)
|
||||
{
|
||||
Handler.RegisterConfigElement(configElement);
|
||||
ConfigElements.Add(configElement.Name, configElement);
|
||||
}
|
||||
|
||||
private static void CreateConfigElements()
|
||||
{
|
||||
Main_Menu_Toggle = new ConfigElement<KeyCode>("Main Menu Toggle",
|
||||
"The UnityEngine.KeyCode to toggle the UnityExplorer Menu.",
|
||||
KeyCode.F7);
|
||||
|
||||
Hide_On_Startup = new ConfigElement<bool>("Hide On Startup",
|
||||
"Should UnityExplorer be hidden on startup?",
|
||||
false);
|
||||
|
||||
Log_Unity_Debug = new ConfigElement<bool>("Log Unity Debug",
|
||||
"Should UnityEngine.Debug.Log messages be printed to UnityExplorer's log?",
|
||||
false);
|
||||
|
||||
Force_Unlock_Mouse = new ConfigElement<bool>("Force Unlock Mouse",
|
||||
"Force the Cursor to be unlocked (visible) when the UnityExplorer menu is open.",
|
||||
true);
|
||||
|
||||
Default_Page_Limit = new ConfigElement<int>("Default Page Limit",
|
||||
"The default maximum number of elements per 'page' in UnityExplorer.",
|
||||
25);
|
||||
|
||||
Default_Output_Path = new ConfigElement<string>("Default Output Path",
|
||||
"The default output path when exporting things from UnityExplorer.",
|
||||
Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Output"));
|
||||
|
||||
// Internal configs
|
||||
|
||||
Last_Window_Anchors = new ConfigElement<string>("Last_Window_Anchors",
|
||||
"For internal use, the last anchors of the UnityExplorer window.",
|
||||
DEFAULT_WINDOW_ANCHORS,
|
||||
true);
|
||||
|
||||
Last_Window_Position = new ConfigElement<string>("Last_Window_Position",
|
||||
"For internal use, the last position of the UnityExplorer window.",
|
||||
DEFAULT_WINDOW_POSITION,
|
||||
true);
|
||||
|
||||
Last_Active_Tab = new ConfigElement<int>("Last_Active_Tab",
|
||||
"For internal use, the last active tab index.",
|
||||
0,
|
||||
true);
|
||||
|
||||
Last_DebugConsole_State = new ConfigElement<bool>("Last_DebugConsole_State",
|
||||
"For internal use, the collapsed state of the Debug Console.",
|
||||
true,
|
||||
true);
|
||||
|
||||
Last_SceneExplorer_State = new ConfigElement<bool>("Last_SceneExplorer_State",
|
||||
"For internal use, the collapsed state of the Scene Explorer.",
|
||||
true,
|
||||
true);
|
||||
}
|
||||
|
||||
// Internal config callback listeners
|
||||
|
||||
private static void PanelDragger_OnFinishResize(RectTransform rect)
|
||||
{
|
||||
Last_Window_Anchors.Value = rect.RectAnchorsToString();
|
||||
}
|
||||
|
||||
private static void PanelDragger_OnFinishDrag(RectTransform rect)
|
||||
{
|
||||
Last_Window_Position.Value = rect.RectPositionToString();
|
||||
}
|
||||
|
||||
private static void MainMenu_OnActiveTabChanged(int page)
|
||||
{
|
||||
Last_Active_Tab.Value = page;
|
||||
}
|
||||
|
||||
private static void DebugConsole_OnToggleShow(bool showing)
|
||||
{
|
||||
Last_DebugConsole_State.Value = showing;
|
||||
}
|
||||
|
||||
private static void SceneExplorer_OnToggleShow(bool showing)
|
||||
{
|
||||
Last_SceneExplorer_State.Value = showing;
|
||||
}
|
||||
|
||||
#region CONSOLE ONEXIT CALLBACK
|
||||
|
||||
internal static void InitConsoleCallback()
|
||||
{
|
||||
handler = new ConsoleEventDelegate(ConsoleEventCallback);
|
||||
SetConsoleCtrlHandler(handler, true);
|
||||
}
|
||||
|
||||
static bool ConsoleEventCallback(int eventType)
|
||||
{
|
||||
// 2 is Console Quit
|
||||
if (eventType == 2)
|
||||
Handler.SaveConfig();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static ConsoleEventDelegate handler;
|
||||
private delegate bool ConsoleEventDelegate(int eventType);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
|
||||
|
||||
#endregion
|
||||
|
||||
#region WINDOW ANCHORS / POSITION HELPERS
|
||||
|
||||
// Window Anchors helpers
|
||||
|
||||
private const string DEFAULT_WINDOW_ANCHORS = "0.25,0.10,0.78,0.95";
|
||||
private const string DEFAULT_WINDOW_POSITION = "0,0";
|
||||
|
||||
internal static CultureInfo _enCulture = new CultureInfo("en-US");
|
||||
|
||||
internal static string RectAnchorsToString(this RectTransform rect)
|
||||
{
|
||||
try
|
||||
{
|
||||
return string.Format(_enCulture, "{0},{1},{2},{3}", new object[]
|
||||
{
|
||||
rect.anchorMin.x,
|
||||
rect.anchorMin.y,
|
||||
rect.anchorMax.x,
|
||||
rect.anchorMax.y
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
return DEFAULT_WINDOW_ANCHORS;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SetAnchorsFromString(this RectTransform panel, string stringAnchors)
|
||||
{
|
||||
Vector4 anchors;
|
||||
try
|
||||
{
|
||||
var split = stringAnchors.Split(',');
|
||||
|
||||
if (split.Length != 4)
|
||||
throw new Exception();
|
||||
|
||||
anchors.x = float.Parse(split[0], _enCulture);
|
||||
anchors.y = float.Parse(split[1], _enCulture);
|
||||
anchors.z = float.Parse(split[2], _enCulture);
|
||||
anchors.w = float.Parse(split[3], _enCulture);
|
||||
}
|
||||
catch
|
||||
{
|
||||
anchors = new Vector4(0.25f, 0.1f, 0.78f, 0.95f);
|
||||
}
|
||||
|
||||
panel.anchorMin = new Vector2(anchors.x, anchors.y);
|
||||
panel.anchorMax = new Vector2(anchors.z, anchors.w);
|
||||
}
|
||||
|
||||
internal static string RectPositionToString(this RectTransform rect)
|
||||
{
|
||||
return string.Format(_enCulture, "{0},{1}", new object[]
|
||||
{
|
||||
rect.localPosition.x, rect.localPosition.y
|
||||
});
|
||||
}
|
||||
|
||||
internal static void SetPositionFromString(this RectTransform rect, string stringPosition)
|
||||
{
|
||||
try
|
||||
{
|
||||
var split = stringPosition.Split(',');
|
||||
|
||||
if (split.Length != 2)
|
||||
throw new Exception();
|
||||
|
||||
Vector3 vector = rect.localPosition;
|
||||
vector.x = float.Parse(split[0], _enCulture);
|
||||
vector.y = float.Parse(split[1], _enCulture);
|
||||
rect.localPosition = vector;
|
||||
}
|
||||
catch //(Exception ex)
|
||||
{
|
||||
//ExplorerCore.LogWarning("Exception setting window position: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
22
src/Core/Config/IConfigElement.cs
Normal file
22
src/Core/Config/IConfigElement.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace UnityExplorer.Core.Config
|
||||
{
|
||||
public interface IConfigElement
|
||||
{
|
||||
string Name { get; }
|
||||
string Description { get; }
|
||||
|
||||
bool IsInternal { get; }
|
||||
Type ElementType { get; }
|
||||
|
||||
object BoxedValue { get; set; }
|
||||
object DefaultValue { get; }
|
||||
|
||||
object GetLoaderConfigValue();
|
||||
|
||||
void RevertToDefaultValue();
|
||||
|
||||
Action OnValueChangedNotify { get; set; }
|
||||
}
|
||||
}
|
@ -1,34 +1,34 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityExplorer.Input;
|
||||
using UnityExplorer.Core.Input;
|
||||
using BF = System.Reflection.BindingFlags;
|
||||
using UnityExplorer.Config;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.UI;
|
||||
#if ML
|
||||
using Harmony;
|
||||
#else
|
||||
using HarmonyLib;
|
||||
#endif
|
||||
|
||||
namespace UnityExplorer.UI
|
||||
namespace UnityExplorer.Core.Input
|
||||
{
|
||||
public class ForceUnlockCursor
|
||||
public class CursorUnlocker
|
||||
{
|
||||
public static bool Unlock
|
||||
{
|
||||
get => m_forceUnlock;
|
||||
set => SetForceUnlock(value);
|
||||
set
|
||||
{
|
||||
m_forceUnlock = value;
|
||||
UpdateCursorControl();
|
||||
}
|
||||
}
|
||||
private static bool m_forceUnlock;
|
||||
|
||||
private static void SetForceUnlock(bool unlock)
|
||||
{
|
||||
m_forceUnlock = unlock;
|
||||
UpdateCursorControl();
|
||||
}
|
||||
|
||||
public static bool ShouldForceMouse => ExplorerCore.ShowMenu && Unlock;
|
||||
public static bool ShouldActuallyUnlock => UIManager.ShowMenu && Unlock;
|
||||
|
||||
private static CursorLockMode m_lastLockMode;
|
||||
private static bool m_lastVisibleState;
|
||||
@ -37,100 +37,17 @@ namespace UnityExplorer.UI
|
||||
|
||||
private static Type CursorType
|
||||
=> m_cursorType
|
||||
?? (m_cursorType = ReflectionHelpers.GetTypeByName("UnityEngine.Cursor"));
|
||||
?? (m_cursorType = ReflectionUtility.GetTypeByName("UnityEngine.Cursor"));
|
||||
private static Type m_cursorType;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
ModConfig.OnConfigChanged += ModConfig_OnConfigChanged;
|
||||
|
||||
SetupPatches();
|
||||
|
||||
Unlock = true;
|
||||
}
|
||||
UpdateCursorControl();
|
||||
|
||||
internal static void ModConfig_OnConfigChanged()
|
||||
{
|
||||
Unlock = ModConfig.Instance.Force_Unlock_Mouse;
|
||||
}
|
||||
|
||||
private static void SetupPatches()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CursorType == null)
|
||||
{
|
||||
throw new Exception("Could not find Type 'UnityEngine.Cursor'!");
|
||||
}
|
||||
|
||||
// Get current cursor state and enable cursor
|
||||
try
|
||||
{
|
||||
//m_lastLockMode = Cursor.lockState;
|
||||
m_lastLockMode = (CursorLockMode?)typeof(Cursor).GetProperty("lockState", BF.Public | BF.Static)?.GetValue(null, null)
|
||||
?? CursorLockMode.None;
|
||||
|
||||
//m_lastVisibleState = Cursor.visible;
|
||||
m_lastVisibleState = (bool?)typeof(Cursor).GetProperty("visible", BF.Public | BF.Static)?.GetValue(null, null)
|
||||
?? false;
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Setup Harmony Patches
|
||||
TryPatch(typeof(Cursor),
|
||||
"lockState",
|
||||
new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_lockState))),
|
||||
true);
|
||||
|
||||
TryPatch(typeof(Cursor),
|
||||
"visible",
|
||||
new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_visible))),
|
||||
true);
|
||||
|
||||
#if BIE
|
||||
#if CPP
|
||||
// temporarily disabling this patch in BepInEx il2cpp as it's causing a crash in some games.
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
TryPatch(typeof(EventSystem),
|
||||
"current",
|
||||
new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_EventSystem_set_current))),
|
||||
true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExplorerCore.Log($"Exception on ForceUnlockCursor.Init! {e.GetType()}, {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryPatch(Type type, string property, HarmonyMethod patch, bool setter)
|
||||
{
|
||||
try
|
||||
{
|
||||
var harmony =
|
||||
#if ML
|
||||
ExplorerMelonMod.Instance.harmonyInstance;
|
||||
#else
|
||||
ExplorerBepInPlugin.HarmonyInstance;
|
||||
#endif
|
||||
|
||||
System.Reflection.PropertyInfo prop = type.GetProperty(property);
|
||||
|
||||
if (setter) // setter is prefix
|
||||
{
|
||||
harmony.Patch(prop.GetSetMethod(), prefix: patch);
|
||||
}
|
||||
else // getter is postfix
|
||||
{
|
||||
harmony.Patch(prop.GetGetMethod(), postfix: patch);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string suf = setter ? "set_" : "get_";
|
||||
ExplorerCore.Log($"Unable to patch {type.Name}.{suf}{property}: {e.Message}");
|
||||
}
|
||||
Unlock = ConfigManager.Force_Unlock_Mouse.Value;
|
||||
ConfigManager.Force_Unlock_Mouse.OnValueChanged += (bool val) => { Unlock = val; };
|
||||
}
|
||||
|
||||
public static void UpdateCursorControl()
|
||||
@ -138,16 +55,24 @@ namespace UnityExplorer.UI
|
||||
try
|
||||
{
|
||||
m_currentlySettingCursor = true;
|
||||
if (ShouldForceMouse)
|
||||
|
||||
if (ShouldActuallyUnlock)
|
||||
{
|
||||
Cursor.lockState = CursorLockMode.None;
|
||||
Cursor.visible = true;
|
||||
|
||||
if (UIManager.EventSys)
|
||||
SetEventSystem();
|
||||
}
|
||||
else
|
||||
{
|
||||
Cursor.lockState = m_lastLockMode;
|
||||
Cursor.visible = m_lastVisibleState;
|
||||
|
||||
if (UIManager.EventSys)
|
||||
ReleaseEventSystem();
|
||||
}
|
||||
|
||||
m_currentlySettingCursor = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -164,11 +89,23 @@ namespace UnityExplorer.UI
|
||||
|
||||
public static void SetEventSystem()
|
||||
{
|
||||
// temp disabled for new InputSystem
|
||||
if (InputManager.CurrentType == InputType.InputSystem)
|
||||
return;
|
||||
|
||||
// Disable current event system object
|
||||
if (m_lastEventSystem || EventSystem.current)
|
||||
{
|
||||
if (!m_lastEventSystem)
|
||||
m_lastEventSystem = EventSystem.current;
|
||||
|
||||
m_lastEventSystem.enabled = false;
|
||||
}
|
||||
|
||||
// Set to our current system
|
||||
m_settingEventSystem = true;
|
||||
EventSystem.current = UIManager.EventSys;
|
||||
UIManager.EventSys.enabled = true;
|
||||
InputManager.ActivateUIModule();
|
||||
m_settingEventSystem = false;
|
||||
}
|
||||
@ -180,6 +117,8 @@ namespace UnityExplorer.UI
|
||||
|
||||
if (m_lastEventSystem)
|
||||
{
|
||||
m_lastEventSystem.enabled = true;
|
||||
|
||||
m_settingEventSystem = true;
|
||||
EventSystem.current = m_lastEventSystem;
|
||||
m_lastInputModule?.ActivateModule();
|
||||
@ -187,7 +126,30 @@ namespace UnityExplorer.UI
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
// Patches
|
||||
|
||||
private static void SetupPatches()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CursorType == null)
|
||||
throw new Exception("Could not load Type 'UnityEngine.Cursor'!");
|
||||
|
||||
// Get current cursor state and enable cursor
|
||||
m_lastLockMode = (CursorLockMode?)CursorType.GetProperty("lockState", BF.Public | BF.Static)?.GetValue(null, null)
|
||||
?? CursorLockMode.None;
|
||||
|
||||
m_lastVisibleState = (bool?)CursorType.GetProperty("visible", BF.Public | BF.Static)?.GetValue(null, null)
|
||||
?? false;
|
||||
|
||||
ExplorerCore.Loader.SetupPatches();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExplorerCore.Log($"Error on CursorUnlocker.Init! {e.GetType()}, {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Prefix_EventSystem_set_current(ref EventSystem value)
|
||||
{
|
||||
if (!m_settingEventSystem)
|
||||
@ -195,10 +157,8 @@ namespace UnityExplorer.UI
|
||||
m_lastEventSystem = value;
|
||||
m_lastInputModule = value?.currentInputModule;
|
||||
|
||||
if (ExplorerCore.ShowMenu)
|
||||
{
|
||||
if (ShouldActuallyUnlock)
|
||||
value = UIManager.EventSys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,31 +166,25 @@ namespace UnityExplorer.UI
|
||||
// Also keep track of when anything else tries to set Cursor state, this will be the
|
||||
// value that we set back to when we close the menu or disable force-unlock.
|
||||
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_set_lockState(ref CursorLockMode value)
|
||||
{
|
||||
if (!m_currentlySettingCursor)
|
||||
{
|
||||
m_lastLockMode = value;
|
||||
|
||||
if (ShouldForceMouse)
|
||||
{
|
||||
if (ShouldActuallyUnlock)
|
||||
value = CursorLockMode.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_set_visible(ref bool value)
|
||||
{
|
||||
if (!m_currentlySettingCursor)
|
||||
{
|
||||
m_lastVisibleState = value;
|
||||
|
||||
if (ShouldForceMouse)
|
||||
{
|
||||
if (ShouldActuallyUnlock)
|
||||
value = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityExplorer.Input
|
||||
namespace UnityExplorer.Core.Input
|
||||
{
|
||||
public interface IHandleInput
|
||||
{
|
@ -1,13 +1,9 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Helpers;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using UnityEngine.EventSystems;
|
||||
#if CPP
|
||||
using UnhollowerBaseLib;
|
||||
#endif
|
||||
|
||||
namespace UnityExplorer.Input
|
||||
namespace UnityExplorer.Core.Input
|
||||
{
|
||||
public enum InputType
|
||||
{
|
||||
@ -43,12 +39,12 @@ namespace UnityExplorer.Input
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if (InputSystem.TKeyboard != null || (ReflectionHelpers.LoadModule("Unity.InputSystem") && InputSystem.TKeyboard != null))
|
||||
if (InputSystem.TKeyboard != null || (ReflectionUtility.LoadModule("Unity.InputSystem") && InputSystem.TKeyboard != null))
|
||||
{
|
||||
m_inputModule = new InputSystem();
|
||||
CurrentType = InputType.InputSystem;
|
||||
}
|
||||
else if (LegacyInput.TInput != null || (ReflectionHelpers.LoadModule("UnityEngine.InputLegacyModule") && LegacyInput.TInput != null))
|
||||
else if (LegacyInput.TInput != null || (ReflectionUtility.LoadModule("UnityEngine.InputLegacyModule") && LegacyInput.TInput != null))
|
||||
{
|
||||
m_inputModule = new LegacyInput();
|
||||
CurrentType = InputType.Legacy;
|
||||
@ -60,6 +56,8 @@ namespace UnityExplorer.Input
|
||||
m_inputModule = new NoInput();
|
||||
CurrentType = InputType.None;
|
||||
}
|
||||
|
||||
CursorUnlocker.Init();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityExplorer.UI;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityExplorer.Input
|
||||
namespace UnityExplorer.Core.Input
|
||||
{
|
||||
public class InputSystem : IHandleInput
|
||||
{
|
||||
@ -17,7 +17,7 @@ namespace UnityExplorer.Input
|
||||
m_kbCurrentProp = TKeyboard.GetProperty("current");
|
||||
m_kbIndexer = TKeyboard.GetProperty("Item", new Type[] { TKey });
|
||||
|
||||
var btnControl = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Controls.ButtonControl");
|
||||
var btnControl = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Controls.ButtonControl");
|
||||
m_btnIsPressedProp = btnControl.GetProperty("isPressed");
|
||||
m_btnWasPressedProp = btnControl.GetProperty("wasPressedThisFrame");
|
||||
|
||||
@ -25,21 +25,21 @@ namespace UnityExplorer.Input
|
||||
m_leftButtonProp = TMouse.GetProperty("leftButton");
|
||||
m_rightButtonProp = TMouse.GetProperty("rightButton");
|
||||
|
||||
m_positionProp = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Pointer")
|
||||
m_positionProp = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Pointer")
|
||||
.GetProperty("position");
|
||||
|
||||
m_readVector2InputMethod = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.InputControl`1")
|
||||
m_readVector2InputMethod = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputControl`1")
|
||||
.MakeGenericType(typeof(Vector2))
|
||||
.GetMethod("ReadValue");
|
||||
}
|
||||
|
||||
public static Type TKeyboard => m_tKeyboard ?? (m_tKeyboard = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Keyboard"));
|
||||
public static Type TKeyboard => m_tKeyboard ?? (m_tKeyboard = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Keyboard"));
|
||||
private static Type m_tKeyboard;
|
||||
|
||||
public static Type TMouse => m_tMouse ?? (m_tMouse = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Mouse"));
|
||||
public static Type TMouse => m_tMouse ?? (m_tMouse = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Mouse"));
|
||||
private static Type m_tMouse;
|
||||
|
||||
public static Type TKey => m_tKey ?? (m_tKey = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Key"));
|
||||
public static Type TKey => m_tKey ?? (m_tKey = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Key"));
|
||||
private static Type m_tKey;
|
||||
|
||||
private static PropertyInfo m_btnIsPressedProp;
|
@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityExplorer.UI;
|
||||
|
||||
namespace UnityExplorer.Input
|
||||
namespace UnityExplorer.Core.Input
|
||||
{
|
||||
public class LegacyInput : IHandleInput
|
||||
{
|
||||
@ -20,7 +20,7 @@ namespace UnityExplorer.Input
|
||||
m_getMouseButtonDownMethod = TInput.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) });
|
||||
}
|
||||
|
||||
public static Type TInput => m_tInput ?? (m_tInput = ReflectionHelpers.GetTypeByName("UnityEngine.Input"));
|
||||
public static Type TInput => m_tInput ?? (m_tInput = ReflectionUtility.GetTypeByName("UnityEngine.Input"));
|
||||
private static Type m_tInput;
|
||||
|
||||
private static PropertyInfo m_mousePositionProp;
|
@ -1,7 +1,7 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityExplorer.Input
|
||||
namespace UnityExplorer.Core.Input
|
||||
{
|
||||
// Just a stub for games where no Input module was able to load at all.
|
||||
|
215
src/Core/ReflectionUtility.cs
Normal file
215
src/Core/ReflectionUtility.cs
Normal file
@ -0,0 +1,215 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using BF = System.Reflection.BindingFlags;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
|
||||
namespace UnityExplorer.Core
|
||||
{
|
||||
public static class ReflectionUtility
|
||||
{
|
||||
public const BF AllFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
|
||||
|
||||
/// <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 GetType(this object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return null;
|
||||
|
||||
return ReflectionProvider.Instance.GetActualType(obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cast an object to its underlying Type.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to cast</param>
|
||||
/// <returns>The object, cast to the underlying Type if possible, otherwise the original object.</returns>
|
||||
public static object Cast(this object obj)
|
||||
=> ReflectionProvider.Instance.Cast(obj, GetType(obj));
|
||||
|
||||
/// <summary>
|
||||
/// Cast an object to a Type, if possible.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to cast</param>
|
||||
/// <param name="castTo">The Type to cast to </param>
|
||||
/// <returns>The object, cast to the Type provided if possible, otherwise the original object.</returns>
|
||||
public static object Cast(this object obj, Type castTo)
|
||||
=> ReflectionProvider.Instance.Cast(obj, castTo);
|
||||
|
||||
/// <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(this Type t)
|
||||
=> ReflectionProvider.Instance.IsAssignableFrom(typeof(IEnumerable), t);
|
||||
|
||||
/// <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(this Type t)
|
||||
=> ReflectionProvider.Instance.IsAssignableFrom(typeof(IDictionary), t);
|
||||
|
||||
/// <summary>
|
||||
/// [INTERNAL] Used to load Unhollowed DLLs in IL2CPP.
|
||||
/// </summary>
|
||||
internal static bool LoadModule(string module)
|
||||
=> ReflectionProvider.Instance.LoadModule(module);
|
||||
|
||||
// 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)
|
||||
{
|
||||
s_typesByName.TryGetValue(fullName, out Type ret);
|
||||
|
||||
if (ret != null)
|
||||
return ret;
|
||||
|
||||
foreach (var type in from asm in AppDomain.CurrentDomain.GetAssemblies()
|
||||
from type in asm.TryGetTypes()
|
||||
select type)
|
||||
{
|
||||
if (type.FullName == fullName)
|
||||
{
|
||||
ret = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_typesByName.ContainsKey(fullName))
|
||||
s_typesByName[fullName] = ret;
|
||||
else
|
||||
s_typesByName.Add(fullName, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// cache for GetBaseTypes
|
||||
internal static readonly Dictionary<string, Type[]> s_cachedTypeInheritance = new Dictionary<string, Type[]>();
|
||||
|
||||
/// <summary>
|
||||
/// Get all base types of the provided Type, including itself.
|
||||
/// </summary>
|
||||
public static Type[] GetAllBaseTypes(this object obj) => GetAllBaseTypes(GetType(obj));
|
||||
|
||||
/// <summary>
|
||||
/// Get all base types of the provided Type, including itself.
|
||||
/// </summary>
|
||||
public static Type[] GetAllBaseTypes(this Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
var name = type.AssemblyQualifiedName;
|
||||
|
||||
if (s_cachedTypeInheritance.TryGetValue(name, out Type[] ret))
|
||||
return ret;
|
||||
|
||||
List<Type> list = new List<Type>();
|
||||
|
||||
while (type != null)
|
||||
{
|
||||
list.Add(type);
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
ret = list.ToArray();
|
||||
|
||||
s_cachedTypeInheritance.Add(name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely get all valid Types inside an Assembly.
|
||||
/// </summary>
|
||||
/// <param name="asm">The Assembly to find Types in.</param>
|
||||
/// <returns>All possible Types which could be retrieved from the Assembly, or an empty array.</returns>
|
||||
public static IEnumerable<Type> TryGetTypes(this Assembly asm)
|
||||
{
|
||||
try
|
||||
{
|
||||
return asm.GetTypes();
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
try
|
||||
{
|
||||
return asm.GetExportedTypes();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return e.Types.Where(t => t != null);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Enumerable.Empty<Type>();
|
||||
}
|
||||
}
|
||||
|
||||
internal static Dictionary<Type, Dictionary<string, FieldInfo>> s_cachedFieldInfos = new Dictionary<Type, Dictionary<string, FieldInfo>>();
|
||||
|
||||
public static FieldInfo GetFieldInfo(Type type, string fieldName)
|
||||
{
|
||||
if (!s_cachedFieldInfos.ContainsKey(type))
|
||||
s_cachedFieldInfos.Add(type, new Dictionary<string, FieldInfo>());
|
||||
|
||||
if (!s_cachedFieldInfos[type].ContainsKey(fieldName))
|
||||
s_cachedFieldInfos[type].Add(fieldName, type.GetField(fieldName, AllFlags));
|
||||
|
||||
return s_cachedFieldInfos[type][fieldName];
|
||||
}
|
||||
|
||||
internal static Dictionary<Type, Dictionary<string, PropertyInfo>> s_cachedPropInfos = new Dictionary<Type, Dictionary<string, PropertyInfo>>();
|
||||
|
||||
public static PropertyInfo GetPropertyInfo(Type type, string propertyName)
|
||||
{
|
||||
if (!s_cachedPropInfos.ContainsKey(type))
|
||||
s_cachedPropInfos.Add(type, new Dictionary<string, PropertyInfo>());
|
||||
|
||||
if (!s_cachedPropInfos[type].ContainsKey(propertyName))
|
||||
s_cachedPropInfos[type].Add(propertyName, type.GetProperty(propertyName, AllFlags));
|
||||
|
||||
return s_cachedPropInfos[type][propertyName];
|
||||
}
|
||||
|
||||
/// <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 ReflectionExToString(this Exception e, bool innerMost = false)
|
||||
{
|
||||
if (innerMost)
|
||||
{
|
||||
while (e.InnerException != null)
|
||||
{
|
||||
#if CPP
|
||||
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException)
|
||||
break;
|
||||
#endif
|
||||
e = e.InnerException;
|
||||
}
|
||||
}
|
||||
|
||||
return $"{e.GetType()}: {e.Message}";
|
||||
}
|
||||
}
|
||||
}
|
@ -6,9 +6,9 @@ using System.Text;
|
||||
using UnhollowerBaseLib;
|
||||
using UnhollowerRuntimeLib;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Runtime.Il2Cpp;
|
||||
|
||||
namespace UnityExplorer.Unstrip
|
||||
namespace UnityExplorer
|
||||
{
|
||||
public class AssetBundle
|
||||
{
|
||||
@ -18,12 +18,23 @@ namespace UnityExplorer.Unstrip
|
||||
|
||||
public static AssetBundle LoadFromFile(string path)
|
||||
{
|
||||
var iCall = ICallHelper.GetICall<d_LoadFromFile>("UnityEngine.AssetBundle::LoadFromFile_Internal");
|
||||
var iCall = ICallManager.GetICall<d_LoadFromFile>("UnityEngine.AssetBundle::LoadFromFile_Internal");
|
||||
|
||||
var ptr = iCall.Invoke(IL2CPP.ManagedStringToIl2Cpp(path), 0u, 0UL);
|
||||
|
||||
return new AssetBundle(ptr);
|
||||
}
|
||||
|
||||
private delegate IntPtr d_LoadFromMemory(IntPtr binary, uint crc);
|
||||
|
||||
public static AssetBundle LoadFromMemory(byte[] binary, uint crc = 0)
|
||||
{
|
||||
var iCall = ICallManager.GetICall<d_LoadFromMemory>("UnityEngine.AssetBundle::LoadFromMemory_Internal");
|
||||
|
||||
var ptr = iCall(((Il2CppStructArray<byte>) binary).Pointer, crc);
|
||||
|
||||
return new AssetBundle(ptr);
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~ Instance ~~~~~~~~~~~~
|
||||
|
||||
@ -37,7 +48,7 @@ namespace UnityExplorer.Unstrip
|
||||
|
||||
public UnityEngine.Object[] LoadAllAssets()
|
||||
{
|
||||
var iCall = ICallHelper.GetICall<d_LoadAssetWithSubAssets_Internal>("UnityEngine.AssetBundle::LoadAssetWithSubAssets_Internal");
|
||||
var iCall = ICallManager.GetICall<d_LoadAssetWithSubAssets_Internal>("UnityEngine.AssetBundle::LoadAssetWithSubAssets_Internal");
|
||||
var ptr = iCall.Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(""), Il2CppType.Of<UnityEngine.Object>().Pointer);
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
@ -52,7 +63,7 @@ namespace UnityExplorer.Unstrip
|
||||
|
||||
public T LoadAsset<T>(string name) where T : UnityEngine.Object
|
||||
{
|
||||
var iCall = ICallHelper.GetICall<d_LoadAsset_Internal>("UnityEngine.AssetBundle::LoadAsset_Internal");
|
||||
var iCall = ICallManager.GetICall<d_LoadAsset_Internal>("UnityEngine.AssetBundle::LoadAsset_Internal");
|
||||
var ptr = iCall.Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(name), Il2CppType.Of<T>().Pointer);
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
72
src/Core/Runtime/Il2Cpp/ICallManager.cs
Normal file
72
src/Core/Runtime/Il2Cpp/ICallManager.cs
Normal file
@ -0,0 +1,72 @@
|
||||
#if CPP
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
{
|
||||
[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "External methods")]
|
||||
public static class ICallManager
|
||||
{
|
||||
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||
public static extern IntPtr il2cpp_resolve_icall([MarshalAs(UnmanagedType.LPStr)] string name);
|
||||
|
||||
private static readonly Dictionary<string, Delegate> iCallCache = new Dictionary<string, 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(signature))
|
||||
return (T)iCallCache[signature];
|
||||
|
||||
IntPtr ptr = il2cpp_resolve_icall(signature);
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
throw new MissingMethodException($"Could not find any iCall with the signature '{signature}'!");
|
||||
|
||||
Delegate iCall = Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
|
||||
iCallCache.Add(signature, iCall);
|
||||
|
||||
return (T)iCall;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, Delegate> s_unreliableCache = new Dictionary<string, Delegate>();
|
||||
|
||||
/// <summary>
|
||||
/// Get an iCall which may be one of multiple different signatures (ie, it changed in different Unity versions).
|
||||
/// Each possible signature must have the same Type pattern, it can only vary by name.
|
||||
/// </summary>
|
||||
public static T GetICallUnreliable<T>(IEnumerable<string> possibleSignatures) where T : Delegate
|
||||
{
|
||||
// use the first possible signature as the 'key'.
|
||||
string key = possibleSignatures.First();
|
||||
|
||||
if (s_unreliableCache.ContainsKey(key))
|
||||
return (T)s_unreliableCache[key];
|
||||
|
||||
T iCall;
|
||||
IntPtr ptr;
|
||||
foreach (var sig in possibleSignatures)
|
||||
{
|
||||
ptr = il2cpp_resolve_icall(sig);
|
||||
if (ptr != IntPtr.Zero)
|
||||
{
|
||||
iCall = (T)Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
|
||||
s_unreliableCache.Add(key, iCall);
|
||||
return iCall;
|
||||
}
|
||||
}
|
||||
|
||||
throw new MissingMethodException($"Could not find any iCall from list of provided signatures starting with '{key}'!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
156
src/Core/Runtime/Il2Cpp/Il2CppCoroutine.cs
Normal file
156
src/Core/Runtime/Il2Cpp/Il2CppCoroutine.cs
Normal file
@ -0,0 +1,156 @@
|
||||
#if CPP
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnhollowerBaseLib;
|
||||
using UnityEngine;
|
||||
|
||||
// CREDIT HerpDerpenstine
|
||||
// https://github.com/LavaGang/MelonLoader/blob/master/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs
|
||||
|
||||
namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
{
|
||||
public static class Il2CppCoroutine
|
||||
{
|
||||
private struct CoroTuple
|
||||
{
|
||||
public object WaitCondition;
|
||||
public IEnumerator Coroutine;
|
||||
}
|
||||
private static readonly List<CoroTuple> ourCoroutinesStore = new List<CoroTuple>();
|
||||
private static readonly List<IEnumerator> ourNextFrameCoroutines = new List<IEnumerator>();
|
||||
private static readonly List<IEnumerator> ourWaitForFixedUpdateCoroutines = new List<IEnumerator>();
|
||||
private static readonly List<IEnumerator> ourWaitForEndOfFrameCoroutines = new List<IEnumerator>();
|
||||
|
||||
private static readonly List<IEnumerator> tempList = new List<IEnumerator>();
|
||||
|
||||
internal static object Start(IEnumerator routine)
|
||||
{
|
||||
if (routine != null) ProcessNextOfCoroutine(routine);
|
||||
return routine;
|
||||
}
|
||||
|
||||
internal static void Stop(IEnumerator enumerator)
|
||||
{
|
||||
if (ourNextFrameCoroutines.Contains(enumerator)) // the coroutine is running itself
|
||||
ourNextFrameCoroutines.Remove(enumerator);
|
||||
else
|
||||
{
|
||||
int coroTupleIndex = ourCoroutinesStore.FindIndex(c => c.Coroutine == enumerator);
|
||||
if (coroTupleIndex != -1) // the coroutine is waiting for a subroutine
|
||||
{
|
||||
object waitCondition = ourCoroutinesStore[coroTupleIndex].WaitCondition;
|
||||
if (waitCondition is IEnumerator waitEnumerator)
|
||||
Stop(waitEnumerator);
|
||||
|
||||
ourCoroutinesStore.RemoveAt(coroTupleIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProcessCoroList(List<IEnumerator> target)
|
||||
{
|
||||
if (target.Count == 0) return;
|
||||
|
||||
// use a temp list to make sure waits made during processing are not handled by same processing invocation
|
||||
// additionally, a temp list reduces allocations compared to an array
|
||||
tempList.AddRange(target);
|
||||
target.Clear();
|
||||
foreach (var enumerator in tempList) ProcessNextOfCoroutine(enumerator);
|
||||
tempList.Clear();
|
||||
}
|
||||
|
||||
internal static void Process()
|
||||
{
|
||||
for (var i = ourCoroutinesStore.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var tuple = ourCoroutinesStore[i];
|
||||
if (tuple.WaitCondition is WaitForSeconds waitForSeconds)
|
||||
{
|
||||
if ((waitForSeconds.m_Seconds -= Time.deltaTime) <= 0)
|
||||
{
|
||||
ourCoroutinesStore.RemoveAt(i);
|
||||
ProcessNextOfCoroutine(tuple.Coroutine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProcessCoroList(ourNextFrameCoroutines);
|
||||
}
|
||||
|
||||
internal static void ProcessWaitForFixedUpdate() => ProcessCoroList(ourWaitForFixedUpdateCoroutines);
|
||||
|
||||
internal static void ProcessWaitForEndOfFrame() => ProcessCoroList(ourWaitForEndOfFrameCoroutines);
|
||||
|
||||
private static void ProcessNextOfCoroutine(IEnumerator enumerator)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!enumerator.MoveNext()) // Run the next step of the coroutine. If it's done, restore the parent routine
|
||||
{
|
||||
var indices = ourCoroutinesStore.Select((it, idx) => (idx, it)).Where(it => it.it.WaitCondition == enumerator).Select(it => it.idx).ToList();
|
||||
for (var i = indices.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var index = indices[i];
|
||||
ourNextFrameCoroutines.Add(ourCoroutinesStore[index].Coroutine);
|
||||
ourCoroutinesStore.RemoveAt(index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExplorerCore.LogError(e.ToString());
|
||||
Stop(FindOriginalCoro(enumerator)); // We want the entire coroutine hierachy to stop when an error happen
|
||||
}
|
||||
|
||||
var next = enumerator.Current;
|
||||
switch (next)
|
||||
{
|
||||
case null:
|
||||
ourNextFrameCoroutines.Add(enumerator);
|
||||
return;
|
||||
case WaitForFixedUpdate _:
|
||||
ourWaitForFixedUpdateCoroutines.Add(enumerator);
|
||||
return;
|
||||
case WaitForEndOfFrame _:
|
||||
ourWaitForEndOfFrameCoroutines.Add(enumerator);
|
||||
return;
|
||||
case WaitForSeconds _:
|
||||
break; // do nothing, this one is supported in Process
|
||||
case Il2CppObjectBase il2CppObjectBase:
|
||||
var nextAsEnumerator = il2CppObjectBase.TryCast<Il2CppSystem.Collections.IEnumerator>();
|
||||
if (nextAsEnumerator != null) // il2cpp IEnumerator also handles CustomYieldInstruction
|
||||
next = new Il2CppEnumeratorWrapper(nextAsEnumerator);
|
||||
else
|
||||
ExplorerCore.LogWarning($"Unknown coroutine yield object of type {il2CppObjectBase} for coroutine {enumerator}");
|
||||
break;
|
||||
}
|
||||
|
||||
ourCoroutinesStore.Add(new CoroTuple { WaitCondition = next, Coroutine = enumerator });
|
||||
|
||||
if (next is IEnumerator nextCoro)
|
||||
ProcessNextOfCoroutine(nextCoro);
|
||||
}
|
||||
|
||||
private static IEnumerator FindOriginalCoro(IEnumerator enumerator)
|
||||
{
|
||||
int index = ourCoroutinesStore.FindIndex(ct => ct.WaitCondition == enumerator);
|
||||
if (index == -1)
|
||||
return enumerator;
|
||||
return FindOriginalCoro(ourCoroutinesStore[index].Coroutine);
|
||||
}
|
||||
|
||||
private class Il2CppEnumeratorWrapper : IEnumerator
|
||||
{
|
||||
private readonly Il2CppSystem.Collections.IEnumerator il2cppEnumerator;
|
||||
|
||||
public Il2CppEnumeratorWrapper(Il2CppSystem.Collections.IEnumerator il2CppEnumerator) => il2cppEnumerator = il2CppEnumerator;
|
||||
public bool MoveNext() => il2cppEnumerator.MoveNext();
|
||||
public void Reset() => il2cppEnumerator.Reset();
|
||||
public object Current => il2cppEnumerator.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
184
src/Core/Runtime/Il2Cpp/Il2CppProvider.cs
Normal file
184
src/Core/Runtime/Il2Cpp/Il2CppProvider.cs
Normal file
@ -0,0 +1,184 @@
|
||||
#if CPP
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using BF = System.Reflection.BindingFlags;
|
||||
using System.Text;
|
||||
using UnhollowerBaseLib;
|
||||
using UnhollowerRuntimeLib;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.SceneManagement;
|
||||
using System.Collections;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
{
|
||||
public class Il2CppProvider : RuntimeProvider
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
Reflection = new Il2CppReflection();
|
||||
TextureUtil = new Il2CppTextureUtil();
|
||||
}
|
||||
|
||||
public override void SetupEvents()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Application.add_logMessageReceived(new Action<string, string, LogType>(ExplorerCore.Instance.OnUnityLog));
|
||||
|
||||
var logType = ReflectionUtility.GetTypeByName("UnityEngine.Application+LogCallback");
|
||||
var castMethod = logType.GetMethod("op_Implicit", new[] { typeof(Action<string, string, LogType>) });
|
||||
var addMethod = typeof(Application).GetMethod("add_logMessageReceived", BF.Static | BF.Public, null, new[] { logType }, null);
|
||||
addMethod.Invoke(null, new[]
|
||||
{
|
||||
castMethod.Invoke(null, new[] { new Action<string, string, LogType>(Application_logMessageReceived) })
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
ExplorerCore.LogWarning("Exception setting up Unity log listener, make sure Unity libraries have been unstripped!");
|
||||
}
|
||||
}
|
||||
|
||||
private void Application_logMessageReceived(string condition, string stackTrace, LogType type)
|
||||
{
|
||||
ExplorerCore.Log(condition, type, true);
|
||||
}
|
||||
|
||||
public override void StartConsoleCoroutine(IEnumerator routine)
|
||||
{
|
||||
Il2CppCoroutine.Start(routine);
|
||||
}
|
||||
|
||||
// Unity API Handlers
|
||||
|
||||
// LayerMask.LayerToName
|
||||
|
||||
internal delegate IntPtr d_LayerToName(int layer);
|
||||
|
||||
public override string LayerToName(int layer)
|
||||
{
|
||||
var iCall = ICallManager.GetICall<d_LayerToName>("UnityEngine.LayerMask::LayerToName");
|
||||
return IL2CPP.Il2CppStringToManaged(iCall.Invoke(layer));
|
||||
}
|
||||
|
||||
// Resources.FindObjectsOfTypeAll
|
||||
|
||||
internal delegate IntPtr d_FindObjectsOfTypeAll(IntPtr type);
|
||||
|
||||
public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type)
|
||||
{
|
||||
var iCall = ICallManager.GetICallUnreliable<d_FindObjectsOfTypeAll>(new[]
|
||||
{
|
||||
"UnityEngine.Resources::FindObjectsOfTypeAll",
|
||||
"UnityEngine.ResourcesAPIInternal::FindObjectsOfTypeAll" // Unity 2020+ updated to this
|
||||
});
|
||||
|
||||
return new Il2CppReferenceArray<UnityEngine.Object>(iCall.Invoke(Il2CppType.From(type).Pointer));
|
||||
}
|
||||
|
||||
public override int GetSceneHandle(Scene scene)
|
||||
=> scene.handle;
|
||||
|
||||
// Scene.GetRootGameObjects();
|
||||
|
||||
internal delegate void d_GetRootGameObjects(int handle, IntPtr list);
|
||||
|
||||
public override GameObject[] GetRootGameObjects(Scene scene)
|
||||
{
|
||||
if (!scene.isLoaded)
|
||||
return new GameObject[0];
|
||||
|
||||
int handle = scene.handle;
|
||||
|
||||
if (handle == -1)
|
||||
return new GameObject[0];
|
||||
|
||||
int count = GetRootCount(handle);
|
||||
|
||||
if (count < 1)
|
||||
return new GameObject[0];
|
||||
|
||||
var list = new Il2CppSystem.Collections.Generic.List<GameObject>(count);
|
||||
|
||||
var iCall = ICallManager.GetICall<d_GetRootGameObjects>("UnityEngine.SceneManagement.Scene::GetRootGameObjectsInternal");
|
||||
|
||||
iCall.Invoke(handle, list.Pointer);
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
// Scene.rootCount
|
||||
|
||||
internal delegate int d_GetRootCountInternal(int handle);
|
||||
|
||||
public override int GetRootCount(Scene scene) => GetRootCount(scene.handle);
|
||||
|
||||
public static int GetRootCount(int handle)
|
||||
{
|
||||
return ICallManager.GetICall<d_GetRootCountInternal>("UnityEngine.SceneManagement.Scene::GetRootCountInternal")
|
||||
.Invoke(handle);
|
||||
}
|
||||
|
||||
internal static bool? s_doPropertiesExist;
|
||||
|
||||
public override ColorBlock SetColorBlock(ColorBlock colors, Color? normal = null, Color? highlighted = null, Color? pressed = null)
|
||||
{
|
||||
if (s_doPropertiesExist == null)
|
||||
{
|
||||
var prop = ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "normalColor") as PropertyInfo;
|
||||
s_doPropertiesExist = prop != null && prop.CanWrite;
|
||||
}
|
||||
|
||||
colors.colorMultiplier = 1;
|
||||
|
||||
object boxed = (object)colors;
|
||||
|
||||
if (s_doPropertiesExist == true)
|
||||
{
|
||||
if (normal != null)
|
||||
ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "normalColor").SetValue(boxed, (Color)normal);
|
||||
if (pressed != null)
|
||||
ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "pressedColor").SetValue(boxed, (Color)pressed);
|
||||
if (highlighted != null)
|
||||
ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "highlightedColor").SetValue(boxed, (Color)highlighted);
|
||||
}
|
||||
else if (s_doPropertiesExist == false)
|
||||
{
|
||||
if (normal != null)
|
||||
ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_NormalColor").SetValue(boxed, (Color)normal);
|
||||
if (pressed != null)
|
||||
ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_PressedColor").SetValue(boxed, (Color)pressed);
|
||||
if (highlighted != null)
|
||||
ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_HighlightedColor").SetValue(boxed, (Color)highlighted);
|
||||
}
|
||||
|
||||
colors = (ColorBlock)boxed;
|
||||
|
||||
return colors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Il2CppExtensions
|
||||
{
|
||||
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 SetChildControlHeight(this HorizontalOrVerticalLayoutGroup group, bool value) => group.childControlHeight = value;
|
||||
public static void SetChildControlWidth(this HorizontalOrVerticalLayoutGroup group, bool value) => group.childControlWidth = value;
|
||||
}
|
||||
|
||||
#endif
|
362
src/Core/Runtime/Il2Cpp/Il2CppReflection.cs
Normal file
362
src/Core/Runtime/Il2Cpp/Il2CppReflection.cs
Normal file
@ -0,0 +1,362 @@
|
||||
#if CPP
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnhollowerBaseLib;
|
||||
using UnhollowerRuntimeLib;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using UnityExplorer.Core;
|
||||
using CppType = Il2CppSystem.Type;
|
||||
using BF = System.Reflection.BindingFlags;
|
||||
|
||||
namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
{
|
||||
[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "External methods")]
|
||||
public class Il2CppReflection : ReflectionProvider
|
||||
{
|
||||
public Il2CppReflection() : base()
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
TryLoadGameModules();
|
||||
}
|
||||
|
||||
public override object Cast(object obj, Type castTo)
|
||||
{
|
||||
return Il2CppCast(obj, castTo);
|
||||
}
|
||||
|
||||
public override string ProcessTypeNameInString(Type type, string theString, ref string typeName)
|
||||
{
|
||||
if (!Il2CppTypeNotNull(type))
|
||||
return theString;
|
||||
|
||||
var cppType = Il2CppType.From(type);
|
||||
if (cppType != null && s_deobfuscatedTypeNames.ContainsKey(cppType.FullName))
|
||||
{
|
||||
typeName = s_deobfuscatedTypeNames[cppType.FullName];
|
||||
theString = theString.Replace(cppType.FullName, typeName);
|
||||
}
|
||||
|
||||
return theString;
|
||||
}
|
||||
|
||||
public override Type GetActualType(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return null;
|
||||
|
||||
var type = obj.GetType();
|
||||
|
||||
if (obj is Il2CppSystem.Object cppObject)
|
||||
{
|
||||
// weird specific case - if the object is an Il2CppSystem.Type, then return so manually.
|
||||
if (cppObject is CppType)
|
||||
return typeof(CppType);
|
||||
|
||||
if (!string.IsNullOrEmpty(type.Namespace))
|
||||
{
|
||||
// Il2CppSystem-namespace objects should just return GetType,
|
||||
// because using GetIl2CppType returns the System namespace type instead.
|
||||
if (type.Namespace.StartsWith("System.") || type.Namespace.StartsWith("Il2CppSystem."))
|
||||
return cppObject.GetType();
|
||||
}
|
||||
|
||||
var cppType = cppObject.GetIl2CppType();
|
||||
|
||||
// check if type is injected
|
||||
IntPtr classPtr = il2cpp_object_get_class(cppObject.Pointer);
|
||||
if (RuntimeSpecificsStore.IsInjected(classPtr))
|
||||
{
|
||||
var typeByName = ReflectionUtility.GetTypeByName(cppType.FullName);
|
||||
if (typeByName != null)
|
||||
return typeByName;
|
||||
}
|
||||
|
||||
// this should be fine for all other il2cpp objects
|
||||
var getType = GetMonoType(cppType);
|
||||
if (getType != null)
|
||||
return getType;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
// caching for GetMonoType
|
||||
private static readonly Dictionary<string, Type> Il2CppToMonoType = new Dictionary<string, Type>();
|
||||
|
||||
// keep deobfuscated type name cache, used to display proper name.
|
||||
internal static Dictionary<string, string> s_deobfuscatedTypeNames = 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)
|
||||
{
|
||||
string name = cppType.AssemblyQualifiedName;
|
||||
|
||||
if (Il2CppToMonoType.ContainsKey(name))
|
||||
return Il2CppToMonoType[name];
|
||||
|
||||
Type ret = Type.GetType(name);
|
||||
|
||||
// Thanks to Slaynash for this deobfuscation snippet!
|
||||
if (ret == null)
|
||||
{
|
||||
string baseName = cppType.FullName;
|
||||
string baseAssembly = cppType.Assembly.GetName().name;
|
||||
|
||||
ret = AppDomain.CurrentDomain
|
||||
.GetAssemblies()
|
||||
.FirstOrDefault(a
|
||||
=> a.GetName().Name == baseAssembly)?
|
||||
.TryGetTypes()
|
||||
.FirstOrDefault(t
|
||||
=> t.CustomAttributes.Any(ca
|
||||
=> ca.AttributeType.Name == "ObfuscatedNameAttribute"
|
||||
&& (string)ca.ConstructorArguments[0].Value == baseName));
|
||||
|
||||
if (ret != null)
|
||||
{
|
||||
// deobfuscated type was found, add to cache.
|
||||
s_deobfuscatedTypeNames.Add(cppType.FullName, ret.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
Il2CppToMonoType.Add(name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// cached class pointers for Il2CppCast
|
||||
private static readonly Dictionary<string, IntPtr> s_cppClassPointers = new Dictionary<string, IntPtr>();
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to cast the object to its underlying type.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object you want to cast.</param>
|
||||
/// <returns>The object, as the underlying type if successful or the input value if not.</returns>
|
||||
public static object Il2CppCast(object obj) => Il2CppCast(obj, Instance.GetActualType(obj));
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to cast the object to the provided type.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object you want to cast.</param>
|
||||
/// <param name="castTo">The Type you want to cast to.</param>
|
||||
/// <returns>The object, as the type (or a normal C# object) if successful or the input value if not.</returns>
|
||||
public static object Il2CppCast(object obj, Type castTo)
|
||||
{
|
||||
if (!(obj is Il2CppSystem.Object ilObj))
|
||||
return obj;
|
||||
|
||||
if (!Il2CppTypeNotNull(castTo, out IntPtr castToPtr))
|
||||
return obj;
|
||||
|
||||
IntPtr castFromPtr = il2cpp_object_get_class(ilObj.Pointer);
|
||||
|
||||
if (!il2cpp_class_is_assignable_from(castToPtr, castFromPtr))
|
||||
return obj;
|
||||
|
||||
if (RuntimeSpecificsStore.IsInjected(castToPtr))
|
||||
return UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(ilObj.Pointer);
|
||||
|
||||
return Activator.CreateInstance(castTo, ilObj.Pointer);
|
||||
}
|
||||
|
||||
/// <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 (s_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);
|
||||
|
||||
s_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);
|
||||
|
||||
internal static IntPtr s_cppEnumerableClassPtr;
|
||||
internal static IntPtr s_cppDictionaryClassPtr;
|
||||
|
||||
public override bool IsAssignableFrom(Type toAssignTo, Type toAssignFrom)
|
||||
{
|
||||
if (toAssignTo.IsAssignableFrom(toAssignFrom))
|
||||
return true;
|
||||
|
||||
if (toAssignTo == typeof(IEnumerable))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (s_cppEnumerableClassPtr == IntPtr.Zero)
|
||||
Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IEnumerable), out s_cppEnumerableClassPtr);
|
||||
|
||||
if (s_cppEnumerableClassPtr != IntPtr.Zero
|
||||
&& Il2CppTypeNotNull(toAssignFrom, out IntPtr assignFromPtr)
|
||||
&& il2cpp_class_is_assignable_from(s_cppEnumerableClassPtr, assignFromPtr))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
else if (toAssignTo == typeof(IDictionary))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (s_cppDictionaryClassPtr == IntPtr.Zero)
|
||||
if (!Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IDictionary), out s_cppDictionaryClassPtr))
|
||||
return false;
|
||||
|
||||
if (Il2CppTypeNotNull(toAssignFrom, out IntPtr classPtr))
|
||||
{
|
||||
if (il2cpp_class_is_assignable_from(s_cppDictionaryClassPtr, classPtr))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool IsReflectionSupported(Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
var gArgs = type.GetGenericArguments();
|
||||
if (!gArgs.Any())
|
||||
return true;
|
||||
|
||||
foreach (var gType in gArgs)
|
||||
{
|
||||
if (!Supported(gType))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
bool Supported(Type t)
|
||||
{
|
||||
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(t))
|
||||
return true;
|
||||
|
||||
if (!Il2CppTypeNotNull(t, out IntPtr ptr))
|
||||
return false;
|
||||
|
||||
return CppType.internal_from_handle(IL2CPP.il2cpp_class_get_type(ptr)) is CppType;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
|
||||
|
||||
internal static void TryLoadGameModules()
|
||||
{
|
||||
Instance.LoadModule("Assembly-CSharp");
|
||||
Instance.LoadModule("Assembly-CSharp-firstpass");
|
||||
}
|
||||
|
||||
public override bool LoadModule(string module)
|
||||
{
|
||||
#if ML
|
||||
var path = Path.Combine("MelonLoader", "Managed", $"{module}.dll");
|
||||
#else
|
||||
var path = Path.Combine("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;
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~ not used ~~~~~~~~~~~~
|
||||
|
||||
// cached il2cpp unbox methods
|
||||
internal static readonly Dictionary<string, MethodInfo> s_unboxMethods = new Dictionary<string, MethodInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to unbox the object to the underlying struct type.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object which is a struct underneath.</param>
|
||||
/// <returns>The struct if successful, otherwise null.</returns>
|
||||
public static object Unbox(object obj) => Unbox(obj, Instance.GetActualType(obj));
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to unbox the object to the struct type.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object which is a struct underneath.</param>
|
||||
/// <param name="type">The type of the struct you want to unbox to.</param>
|
||||
/// <returns>The struct if successful, otherwise null.</returns>
|
||||
public static object Unbox(object obj, Type type)
|
||||
{
|
||||
if (!type.IsValueType)
|
||||
return null;
|
||||
|
||||
if (!(obj is Il2CppSystem.Object))
|
||||
return obj;
|
||||
|
||||
var name = type.AssemblyQualifiedName;
|
||||
|
||||
if (!s_unboxMethods.ContainsKey(name))
|
||||
{
|
||||
s_unboxMethods.Add(name, typeof(Il2CppObjectBase)
|
||||
.GetMethod("Unbox")
|
||||
.MakeGenericMethod(type));
|
||||
}
|
||||
|
||||
return s_unboxMethods[name].Invoke(obj, new object[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
77
src/Core/Runtime/Il2Cpp/Il2CppTextureUtil.cs
Normal file
77
src/Core/Runtime/Il2Cpp/Il2CppTextureUtil.cs
Normal file
@ -0,0 +1,77 @@
|
||||
#if CPP
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnhollowerBaseLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
{
|
||||
public class Il2CppTextureUtil : TextureUtilProvider
|
||||
{
|
||||
public override Texture2D NewTexture2D(int width, int height)
|
||||
=> new Texture2D((int)width, (int)height, TextureFormat.RGBA32, Texture.GenerateAllMips, false, IntPtr.Zero);
|
||||
|
||||
internal delegate void d_Blit2(IntPtr source, IntPtr dest);
|
||||
|
||||
public override void Blit(Texture2D tex, RenderTexture rt)
|
||||
{
|
||||
var iCall = ICallManager.GetICall<d_Blit2>("UnityEngine.Graphics::Blit2");
|
||||
iCall.Invoke(tex.Pointer, rt.Pointer);
|
||||
}
|
||||
|
||||
// byte[] ImageConversion.EncodeToPNG(this Texture2D image);
|
||||
|
||||
internal delegate IntPtr d_EncodeToPNG(IntPtr tex);
|
||||
|
||||
public override byte[] EncodeToPNG(Texture2D tex)
|
||||
{
|
||||
var iCall = ICallManager.GetICall<d_EncodeToPNG>("UnityEngine.ImageConversion::EncodeToPNG");
|
||||
|
||||
IntPtr ptr = iCall.Invoke(tex.Pointer);
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
return new Il2CppStructArray<byte>(ptr);
|
||||
}
|
||||
|
||||
// bool ImageConversion.LoadImage(this Texture2D tex, byte[] data, bool markNonReadable);
|
||||
|
||||
internal delegate bool d_LoadImage(IntPtr tex, IntPtr data, bool markNonReadable);
|
||||
|
||||
public override bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable)
|
||||
{
|
||||
var il2cppArray = (Il2CppStructArray<byte>)data;
|
||||
|
||||
var iCall = ICallManager.GetICall<d_LoadImage>("UnityEngine.ImageConversion::LoadImage");
|
||||
|
||||
return iCall.Invoke(tex.Pointer, il2cppArray.Pointer, markNonReadable);
|
||||
}
|
||||
|
||||
// Sprite Sprite.Create
|
||||
|
||||
public override Sprite CreateSprite(Texture2D texture)
|
||||
{
|
||||
return CreateSpriteImpl(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero, 100f, 0u, Vector4.zero);
|
||||
}
|
||||
|
||||
internal delegate IntPtr d_CreateSprite(IntPtr texture, ref Rect rect, ref Vector2 pivot, float pixelsPerUnit,
|
||||
uint extrude, int meshType, ref Vector4 border, bool generateFallbackPhysicsShape);
|
||||
|
||||
public static Sprite CreateSpriteImpl(Texture texture, Rect rect, Vector2 pivot, float pixelsPerUnit, uint extrude, Vector4 border)
|
||||
{
|
||||
var iCall = ICallManager.GetICall<d_CreateSprite>("UnityEngine.Sprite::CreateSprite_Injected");
|
||||
|
||||
var ptr = iCall.Invoke(texture.Pointer, ref rect, ref pivot, pixelsPerUnit, extrude, 1, ref border, false);
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
return null;
|
||||
else
|
||||
return new Sprite(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
120
src/Core/Runtime/Mono/MonoProvider.cs
Normal file
120
src/Core/Runtime/Mono/MonoProvider.cs
Normal file
@ -0,0 +1,120 @@
|
||||
#if MONO
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.Core.CSharp;
|
||||
|
||||
namespace UnityExplorer.Core.Runtime.Mono
|
||||
{
|
||||
public class MonoProvider : RuntimeProvider
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
Reflection = new MonoReflection();
|
||||
TextureUtil = new MonoTextureUtil();
|
||||
}
|
||||
|
||||
public override void SetupEvents()
|
||||
{
|
||||
Application.logMessageReceived += Application_logMessageReceived;
|
||||
}
|
||||
|
||||
private void Application_logMessageReceived(string condition, string stackTrace, LogType type)
|
||||
{
|
||||
ExplorerCore.Log(condition, type, true);
|
||||
}
|
||||
|
||||
public override void StartConsoleCoroutine(IEnumerator routine)
|
||||
{
|
||||
DummyBehaviour.Instance.StartCoroutine(routine);
|
||||
}
|
||||
|
||||
public override string LayerToName(int layer)
|
||||
=> LayerMask.LayerToName(layer);
|
||||
|
||||
public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type)
|
||||
=> Resources.FindObjectsOfTypeAll(type);
|
||||
|
||||
private static readonly FieldInfo fi_Scene_handle = typeof(Scene).GetField("m_Handle", ReflectionUtility.AllFlags);
|
||||
|
||||
public override int GetSceneHandle(Scene scene)
|
||||
{
|
||||
return (int)fi_Scene_handle.GetValue(scene);
|
||||
}
|
||||
|
||||
public override GameObject[] GetRootGameObjects(Scene scene)
|
||||
{
|
||||
if (!scene.isLoaded)
|
||||
return new GameObject[0];
|
||||
|
||||
return scene.GetRootGameObjects();
|
||||
}
|
||||
|
||||
public override int GetRootCount(Scene scene)
|
||||
{
|
||||
return scene.rootCount;
|
||||
}
|
||||
|
||||
public override ColorBlock SetColorBlock(ColorBlock colors, Color? normal = null, Color? highlighted = null, Color? pressed = null)
|
||||
{
|
||||
if (normal != null)
|
||||
colors.normalColor = (Color)normal;
|
||||
|
||||
if (highlighted != null)
|
||||
colors.highlightedColor = (Color)highlighted;
|
||||
|
||||
if (pressed != null)
|
||||
colors.pressedColor = (Color)pressed;
|
||||
|
||||
return colors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MonoExtensions
|
||||
{
|
||||
public static void AddListener(this UnityEvent _event, Action listener)
|
||||
{
|
||||
_event.AddListener(new UnityAction(listener));
|
||||
}
|
||||
|
||||
public static void AddListener<T>(this UnityEvent<T> _event, Action<T> listener)
|
||||
{
|
||||
_event.AddListener(new UnityAction<T>(listener));
|
||||
}
|
||||
|
||||
public static void Clear(this StringBuilder sb)
|
||||
{
|
||||
sb.Remove(0, sb.Length);
|
||||
}
|
||||
|
||||
private static PropertyInfo pi_childControlHeight;
|
||||
|
||||
public static void SetChildControlHeight(this HorizontalOrVerticalLayoutGroup group, bool value)
|
||||
{
|
||||
if (pi_childControlHeight == null)
|
||||
pi_childControlHeight = group.GetType().GetProperty("childControlHeight");
|
||||
|
||||
pi_childControlHeight?.SetValue(group, value, null);
|
||||
}
|
||||
|
||||
private static PropertyInfo pi_childControlWidth;
|
||||
|
||||
public static void SetChildControlWidth(this HorizontalOrVerticalLayoutGroup group, bool value)
|
||||
{
|
||||
if (pi_childControlWidth == null)
|
||||
pi_childControlWidth = group.GetType().GetProperty("childControlWidth");
|
||||
|
||||
pi_childControlWidth?.SetValue(group, value, null);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
33
src/Core/Runtime/Mono/MonoReflection.cs
Normal file
33
src/Core/Runtime/Mono/MonoReflection.cs
Normal file
@ -0,0 +1,33 @@
|
||||
#if MONO
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityExplorer.Core.Runtime.Mono
|
||||
{
|
||||
public class MonoReflection : ReflectionProvider
|
||||
{
|
||||
// Mono doesn't need to explicitly cast things.
|
||||
public override object Cast(object obj, Type castTo)
|
||||
=> obj;
|
||||
|
||||
// Vanilla GetType is fine for mono
|
||||
public override Type GetActualType(object obj)
|
||||
=> obj.GetType();
|
||||
|
||||
public override bool IsAssignableFrom(Type toAssignTo, Type toAssignFrom)
|
||||
=> toAssignTo.IsAssignableFrom(toAssignFrom);
|
||||
|
||||
public override bool IsReflectionSupported(Type type)
|
||||
=> true;
|
||||
|
||||
public override bool LoadModule(string module)
|
||||
=> true;
|
||||
|
||||
public override string ProcessTypeNameInString(Type type, string theString, ref string typeName)
|
||||
=> theString;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
66
src/Core/Runtime/Mono/MonoTextureUtil.cs
Normal file
66
src/Core/Runtime/Mono/MonoTextureUtil.cs
Normal file
@ -0,0 +1,66 @@
|
||||
#if MONO
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Core;
|
||||
|
||||
namespace UnityExplorer.Core.Runtime.Mono
|
||||
{
|
||||
public class MonoTextureUtil : TextureUtilProvider
|
||||
{
|
||||
public override void Blit(Texture2D tex, RenderTexture rt)
|
||||
{
|
||||
Graphics.Blit(tex, rt);
|
||||
}
|
||||
|
||||
public override Sprite CreateSprite(Texture2D texture)
|
||||
{
|
||||
return Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);
|
||||
}
|
||||
|
||||
public override bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable)
|
||||
{
|
||||
return tex.LoadImage(data, markNonReadable);
|
||||
}
|
||||
|
||||
public override Texture2D NewTexture2D(int width, int height)
|
||||
{
|
||||
return new Texture2D(width, height);
|
||||
}
|
||||
|
||||
public override byte[] EncodeToPNG(Texture2D tex)
|
||||
{
|
||||
return EncodeToPNGSafe(tex);
|
||||
}
|
||||
|
||||
private static MethodInfo EncodeToPNGMethod => m_encodeToPNGMethod ?? GetEncodeToPNGMethod();
|
||||
private static MethodInfo m_encodeToPNGMethod;
|
||||
|
||||
public static byte[] EncodeToPNGSafe(Texture2D tex)
|
||||
{
|
||||
var method = EncodeToPNGMethod;
|
||||
|
||||
if (method.IsStatic)
|
||||
return (byte[])method.Invoke(null, new object[] { tex });
|
||||
else
|
||||
return (byte[])method.Invoke(tex, new object[0]);
|
||||
}
|
||||
|
||||
private static MethodInfo GetEncodeToPNGMethod()
|
||||
{
|
||||
if (ReflectionUtility.GetTypeByName("UnityEngine.ImageConversion") is Type imageConversion)
|
||||
return m_encodeToPNGMethod = imageConversion.GetMethod("EncodeToPNG", ReflectionUtility.AllFlags);
|
||||
|
||||
var method = typeof(Texture2D).GetMethod("EncodeToPNG", ReflectionUtility.AllFlags);
|
||||
if (method != null)
|
||||
return m_encodeToPNGMethod = method;
|
||||
|
||||
ExplorerCore.Log("ERROR: Cannot get any EncodeToPNG method!");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
29
src/Core/Runtime/ReflectionProvider.cs
Normal file
29
src/Core/Runtime/ReflectionProvider.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityExplorer.Core.Runtime
|
||||
{
|
||||
public abstract class ReflectionProvider
|
||||
{
|
||||
public static ReflectionProvider Instance;
|
||||
|
||||
public ReflectionProvider()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public abstract Type GetActualType(object obj);
|
||||
|
||||
public abstract object Cast(object obj, Type castTo);
|
||||
|
||||
public abstract bool IsAssignableFrom(Type toAssignTo, Type toAssignFrom);
|
||||
|
||||
public abstract bool IsReflectionSupported(Type type);
|
||||
|
||||
public abstract string ProcessTypeNameInString(Type type, string theString, ref string typeName);
|
||||
|
||||
public abstract bool LoadModule(string module);
|
||||
}
|
||||
}
|
57
src/Core/Runtime/RuntimeProvider.cs
Normal file
57
src/Core/Runtime/RuntimeProvider.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityExplorer.Core.Runtime
|
||||
{
|
||||
// Work in progress, this will be used to replace all the "if CPP / if MONO"
|
||||
// pre-processor directives all over the codebase.
|
||||
|
||||
public abstract class RuntimeProvider
|
||||
{
|
||||
public static RuntimeProvider Instance;
|
||||
|
||||
public ReflectionProvider Reflection;
|
||||
public TextureUtilProvider TextureUtil;
|
||||
|
||||
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();
|
||||
|
||||
public abstract void StartConsoleCoroutine(IEnumerator routine);
|
||||
|
||||
// Unity API handlers
|
||||
|
||||
public abstract string LayerToName(int layer);
|
||||
|
||||
public abstract UnityEngine.Object[] FindObjectsOfTypeAll(Type type);
|
||||
|
||||
public abstract int GetSceneHandle(Scene scene);
|
||||
|
||||
public abstract GameObject[] GetRootGameObjects(Scene scene);
|
||||
|
||||
public abstract int GetRootCount(Scene scene);
|
||||
|
||||
public abstract ColorBlock SetColorBlock(ColorBlock colors, Color? normal = null, Color? highlighted = null, Color? pressed = null);
|
||||
}
|
||||
}
|
@ -1,41 +1,32 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
#if CPP
|
||||
using UnityExplorer.Unstrip;
|
||||
#endif
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.Helpers
|
||||
namespace UnityExplorer.Core.Runtime
|
||||
{
|
||||
public static class Texture2DHelpers
|
||||
public abstract class TextureUtilProvider
|
||||
{
|
||||
#if CPP // If Mono
|
||||
#else
|
||||
private static bool isNewEncodeMethod = false;
|
||||
private static MethodInfo EncodeToPNGMethod => m_encodeToPNGMethod ?? GetEncodeToPNGMethod();
|
||||
private static MethodInfo m_encodeToPNGMethod;
|
||||
public static TextureUtilProvider Instance;
|
||||
|
||||
private static MethodInfo GetEncodeToPNGMethod()
|
||||
public TextureUtilProvider()
|
||||
{
|
||||
if (ReflectionHelpers.GetTypeByName("UnityEngine.ImageConversion") is Type imageConversion)
|
||||
{
|
||||
isNewEncodeMethod = true;
|
||||
return m_encodeToPNGMethod = imageConversion.GetMethod("EncodeToPNG", ReflectionHelpers.CommonFlags);
|
||||
}
|
||||
|
||||
var method = typeof(Texture2D).GetMethod("EncodeToPNG", ReflectionHelpers.CommonFlags);
|
||||
if (method != null)
|
||||
{
|
||||
return m_encodeToPNGMethod = method;
|
||||
}
|
||||
|
||||
ExplorerCore.Log("ERROR: Cannot get any EncodeToPNG method!");
|
||||
return null;
|
||||
Instance = this;
|
||||
}
|
||||
#endif
|
||||
|
||||
public static bool IsReadable(this Texture2D tex)
|
||||
public abstract byte[] EncodeToPNG(Texture2D tex);
|
||||
|
||||
public abstract Texture2D NewTexture2D(int width, int height);
|
||||
|
||||
public abstract void Blit(Texture2D tex, RenderTexture rt);
|
||||
|
||||
public abstract bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable);
|
||||
|
||||
public abstract Sprite CreateSprite(Texture2D texture);
|
||||
|
||||
public static bool IsReadable(Texture2D tex)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -52,25 +43,30 @@ namespace UnityExplorer.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
public static Texture2D Copy(Texture2D orig, Rect rect) //, bool isDTXnmNormal = false)
|
||||
public static bool LoadImage(Texture2D tex, string filePath, bool markNonReadable)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
return false;
|
||||
|
||||
return Instance.LoadImage(tex, File.ReadAllBytes(filePath), markNonReadable);
|
||||
}
|
||||
|
||||
public static Texture2D Copy(Texture2D orig, Rect rect)
|
||||
{
|
||||
Color[] pixels;
|
||||
|
||||
if (!orig.IsReadable())
|
||||
if (!IsReadable(orig))
|
||||
orig = ForceReadTexture(orig);
|
||||
|
||||
pixels = orig.GetPixels((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
|
||||
|
||||
var _newTex = new Texture2D((int)rect.width, (int)rect.height);
|
||||
_newTex.SetPixels(pixels);
|
||||
Texture2D newTex = Instance.NewTexture2D((int)rect.width, (int)rect.height);
|
||||
|
||||
return _newTex;
|
||||
newTex.SetPixels(pixels);
|
||||
|
||||
return newTex;
|
||||
}
|
||||
|
||||
#if CPP
|
||||
internal delegate void d_Blit2(IntPtr source, IntPtr dest);
|
||||
#endif
|
||||
|
||||
public static Texture2D ForceReadTexture(Texture2D tex)
|
||||
{
|
||||
try
|
||||
@ -82,12 +78,7 @@ namespace UnityExplorer.Helpers
|
||||
rt.filterMode = FilterMode.Point;
|
||||
RenderTexture.active = rt;
|
||||
|
||||
#if MONO
|
||||
Graphics.Blit(tex, rt);
|
||||
#else
|
||||
var iCall = ICallHelper.GetICall<d_Blit2>("UnityEngine.Graphics::Blit2");
|
||||
iCall.Invoke(tex.Pointer, rt.Pointer);
|
||||
#endif
|
||||
Instance.Blit(tex, rt);
|
||||
|
||||
var _newTex = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false);
|
||||
|
||||
@ -115,7 +106,7 @@ namespace UnityExplorer.Helpers
|
||||
string savepath = dir + @"\" + name + ".png";
|
||||
|
||||
// Make sure we can EncodeToPNG it.
|
||||
if (tex.format != TextureFormat.ARGB32 || !tex.IsReadable())
|
||||
if (tex.format != TextureFormat.ARGB32 || !IsReadable(tex))
|
||||
{
|
||||
tex = ForceReadTexture(tex);
|
||||
}
|
||||
@ -126,20 +117,9 @@ namespace UnityExplorer.Helpers
|
||||
tex.Apply(false, false);
|
||||
}
|
||||
|
||||
#if CPP
|
||||
data = tex.EncodeToPNG();
|
||||
#else
|
||||
if (isNewEncodeMethod)
|
||||
{
|
||||
data = (byte[])EncodeToPNGMethod.Invoke(null, new object[] { tex });
|
||||
}
|
||||
else
|
||||
{
|
||||
data = (byte[])EncodeToPNGMethod.Invoke(tex, new object[0]);
|
||||
}
|
||||
#endif
|
||||
data = Instance.EncodeToPNG(tex);
|
||||
|
||||
if (data == null || data.Length < 1)
|
||||
if (data == null || !data.Any())
|
||||
{
|
||||
ExplorerCore.LogWarning("Couldn't get any data for the texture!");
|
||||
}
|
14
src/Core/Search/ChildFilter.cs
Normal file
14
src/Core/Search/ChildFilter.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityExplorer.Core.Search
|
||||
{
|
||||
internal enum ChildFilter
|
||||
{
|
||||
Any,
|
||||
RootObject,
|
||||
HasParent
|
||||
}
|
||||
}
|
15
src/Core/Search/SceneFilter.cs
Normal file
15
src/Core/Search/SceneFilter.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityExplorer.Core.Search
|
||||
{
|
||||
internal enum SceneFilter
|
||||
{
|
||||
Any,
|
||||
Asset,
|
||||
DontDestroyOnLoad,
|
||||
Explicit,
|
||||
}
|
||||
}
|
17
src/Core/Search/SearchContext.cs
Normal file
17
src/Core/Search/SearchContext.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityExplorer.Core.Search
|
||||
{
|
||||
internal enum SearchContext
|
||||
{
|
||||
UnityObject,
|
||||
GameObject,
|
||||
Component,
|
||||
Custom,
|
||||
Singleton,
|
||||
StaticClass
|
||||
}
|
||||
}
|
227
src/Core/Search/SearchProvider.cs
Normal file
227
src/Core/Search/SearchProvider.cs
Normal file
@ -0,0 +1,227 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
using UnityExplorer.UI.Main;
|
||||
using UnityExplorer.UI.Main.Search;
|
||||
|
||||
namespace UnityExplorer.Core.Search
|
||||
{
|
||||
public static class SearchProvider
|
||||
{
|
||||
internal static object[] StaticClassSearch(string input)
|
||||
{
|
||||
var list = new List<Type>();
|
||||
|
||||
var nameFilter = "";
|
||||
if (!string.IsNullOrEmpty(input))
|
||||
nameFilter = input.ToLower();
|
||||
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (var type in asm.TryGetTypes().Where(it => it.IsSealed && it.IsAbstract))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ToLower().Contains(nameFilter))
|
||||
continue;
|
||||
|
||||
list.Add(type);
|
||||
}
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
internal static string[] s_instanceNames = new string[]
|
||||
{
|
||||
"m_instance",
|
||||
"m_Instance",
|
||||
"s_instance",
|
||||
"s_Instance",
|
||||
"_instance",
|
||||
"_Instance",
|
||||
"instance",
|
||||
"Instance",
|
||||
"<Instance>k__BackingField",
|
||||
"<instance>k__BackingField",
|
||||
};
|
||||
|
||||
internal static object[] SingletonSearch(string input)
|
||||
{
|
||||
var instances = new List<object>();
|
||||
|
||||
var nameFilter = "";
|
||||
if (!string.IsNullOrEmpty(input))
|
||||
nameFilter = input.ToLower();
|
||||
|
||||
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
|
||||
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
// Search all non-static, non-enum classes.
|
||||
foreach (var type in asm.TryGetTypes().Where(it => !(it.IsSealed && it.IsAbstract) && !it.IsEnum))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ToLower().Contains(nameFilter))
|
||||
continue;
|
||||
#if CPP
|
||||
// Only look for Properties in IL2CPP, not for Mono.
|
||||
PropertyInfo pi;
|
||||
foreach (var name in s_instanceNames)
|
||||
{
|
||||
pi = type.GetProperty(name, flags);
|
||||
if (pi != null)
|
||||
{
|
||||
var instance = pi.GetValue(null, null);
|
||||
if (instance != null)
|
||||
{
|
||||
instances.Add(instance);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Look for a typical Instance backing field.
|
||||
FieldInfo fi;
|
||||
foreach (var name in s_instanceNames)
|
||||
{
|
||||
fi = type.GetField(name, flags);
|
||||
if (fi != null)
|
||||
{
|
||||
var instance = fi.GetValue(null);
|
||||
if (instance != null)
|
||||
{
|
||||
instances.Add(instance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
return instances.ToArray();
|
||||
}
|
||||
|
||||
internal static object[] UnityObjectSearch(string input, string customTypeInput, SearchContext context,
|
||||
ChildFilter childFilter, SceneFilter sceneFilter)
|
||||
{
|
||||
Type searchType = null;
|
||||
switch (context)
|
||||
{
|
||||
case SearchContext.GameObject:
|
||||
searchType = typeof(GameObject); break;
|
||||
|
||||
case SearchContext.Component:
|
||||
searchType = typeof(Component); break;
|
||||
|
||||
case SearchContext.Custom:
|
||||
if (string.IsNullOrEmpty(customTypeInput))
|
||||
{
|
||||
ExplorerCore.LogWarning("Custom Type input must not be empty!");
|
||||
return null;
|
||||
}
|
||||
if (ReflectionUtility.GetTypeByName(customTypeInput) is Type customType)
|
||||
if (typeof(UnityEngine.Object).IsAssignableFrom(customType))
|
||||
searchType = customType;
|
||||
else
|
||||
ExplorerCore.LogWarning($"Custom type '{customType.FullName}' is not assignable from UnityEngine.Object!");
|
||||
else
|
||||
ExplorerCore.LogWarning($"Could not find a type by the name '{customTypeInput}'!");
|
||||
break;
|
||||
|
||||
default:
|
||||
searchType = typeof(UnityEngine.Object); break;
|
||||
}
|
||||
|
||||
if (searchType == null)
|
||||
return null;
|
||||
|
||||
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(searchType);
|
||||
var results = new List<object>();
|
||||
|
||||
// perform filter comparers
|
||||
|
||||
string nameFilter = null;
|
||||
if (!string.IsNullOrEmpty(input))
|
||||
nameFilter = input.ToLower();
|
||||
|
||||
bool canGetGameObject = (sceneFilter != SceneFilter.Any || childFilter != ChildFilter.Any)
|
||||
&& (context == SearchContext.GameObject || typeof(Component).IsAssignableFrom(searchType));
|
||||
|
||||
string sceneFilterString = null;
|
||||
if (!canGetGameObject)
|
||||
{
|
||||
if (context != SearchContext.UnityObject && (sceneFilter != SceneFilter.Any || childFilter != ChildFilter.Any))
|
||||
ExplorerCore.LogWarning($"Type '{searchType}' cannot have Scene or Child filters applied to it");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sceneFilter == SceneFilter.DontDestroyOnLoad)
|
||||
sceneFilterString = "DontDestroyOnLoad";
|
||||
else if (sceneFilter == SceneFilter.Explicit)
|
||||
sceneFilterString = SearchPage.Instance.m_sceneDropdown.options[SearchPage.Instance.m_sceneDropdown.value].text;
|
||||
}
|
||||
|
||||
foreach (var obj in allObjects)
|
||||
{
|
||||
// name check
|
||||
if (!string.IsNullOrEmpty(nameFilter) && !obj.name.ToLower().Contains(nameFilter))
|
||||
continue;
|
||||
|
||||
if (canGetGameObject)
|
||||
{
|
||||
#if MONO
|
||||
var go = context == SearchContext.GameObject
|
||||
? obj as GameObject
|
||||
: (obj as Component).gameObject;
|
||||
#else
|
||||
var go = context == SearchContext.GameObject
|
||||
? obj.TryCast<GameObject>()
|
||||
: obj.TryCast<Component>().gameObject;
|
||||
#endif
|
||||
|
||||
// scene check
|
||||
if (sceneFilter != SceneFilter.Any)
|
||||
{
|
||||
if (!go)
|
||||
continue;
|
||||
|
||||
switch (context)
|
||||
{
|
||||
case SearchContext.GameObject:
|
||||
if (go.scene.name != sceneFilterString)
|
||||
continue;
|
||||
break;
|
||||
case SearchContext.Custom:
|
||||
case SearchContext.Component:
|
||||
if (go.scene.name != sceneFilterString)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (childFilter != ChildFilter.Any)
|
||||
{
|
||||
if (!go)
|
||||
continue;
|
||||
|
||||
// root object check (no parent)
|
||||
if (childFilter == ChildFilter.HasParent && !go.transform.parent)
|
||||
continue;
|
||||
else if (childFilter == ChildFilter.RootObject && go.transform.parent)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
results.Add(obj);
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
103
src/Core/Unity/UnityHelpers.cs
Normal file
103
src/Core/Unity/UnityHelpers.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace UnityExplorer.Core.Unity
|
||||
{
|
||||
public static class UnityHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if an object is null, and if it's a UnityEngine.Object then also check if it was destroyed.
|
||||
/// </summary>
|
||||
public static bool IsNullOrDestroyed(this object obj, bool suppressWarning = true)
|
||||
{
|
||||
var unityObj = obj as Object;
|
||||
if (obj == null)
|
||||
{
|
||||
if (!suppressWarning)
|
||||
ExplorerCore.LogWarning("The target instance is null!");
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (obj is Object)
|
||||
{
|
||||
if (!unityObj)
|
||||
{
|
||||
if (!suppressWarning)
|
||||
ExplorerCore.LogWarning("The target UnityEngine.Object was destroyed!");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Print a nice {X, Y, Z} output of the Vector3, formatted to 3 decimal places.
|
||||
/// </summary>
|
||||
public static string ToStringPretty(this Vector3 vec)
|
||||
{
|
||||
return $"{vec.x:F3}, {vec.y:F3}, {vec.z:F3}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the full Transform heirarchy path for this provided Transform.
|
||||
/// </summary>
|
||||
public static string GetTransformPath(this Transform transform, bool includeSelf = false)
|
||||
{
|
||||
string path = includeSelf
|
||||
? transform.transform.name
|
||||
: "";
|
||||
|
||||
while (transform.parent)
|
||||
{
|
||||
transform = transform.parent;
|
||||
path = $"{transform.name}/{path}";
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts Color to 6-digit RGB hex code (without # symbol). Eg, RGBA(1,0,0,1) -> FF0000
|
||||
/// </summary>
|
||||
public static string ToHex(this Color color)
|
||||
{
|
||||
byte r = (byte)Mathf.Clamp(Mathf.RoundToInt(color.r * 255f), 0, 255);
|
||||
byte g = (byte)Mathf.Clamp(Mathf.RoundToInt(color.g * 255f), 0, 255);
|
||||
byte b = (byte)Mathf.Clamp(Mathf.RoundToInt(color.b * 255f), 0, 255);
|
||||
|
||||
return $"{r:X2}{g:X2}{b:X2}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assumes the string is a 6-digit RGB Hex color code, which it will parse into a UnityEngine.Color.
|
||||
/// Eg, FF0000 -> RGBA(1,0,0,1)
|
||||
/// </summary>
|
||||
public static Color ToColor(this string _string)
|
||||
{
|
||||
_string = _string.Replace("#", "");
|
||||
|
||||
if (_string.Length != 6)
|
||||
return Color.magenta;
|
||||
|
||||
var r = byte.Parse(_string.Substring(0, 2), NumberStyles.HexNumber);
|
||||
var g = byte.Parse(_string.Substring(2, 2), NumberStyles.HexNumber);
|
||||
var b = byte.Parse(_string.Substring(4, 2), NumberStyles.HexNumber);
|
||||
|
||||
var color = new Color
|
||||
{
|
||||
r = (float)(r / (decimal)255),
|
||||
g = (float)(g / (decimal)255),
|
||||
b = (float)(b / (decimal)255),
|
||||
a = 1
|
||||
};
|
||||
|
||||
return color;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
#if BIE
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using BepInEx;
|
||||
using BepInEx.Logging;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI.Modules;
|
||||
#if CPP
|
||||
using UnhollowerRuntimeLib;
|
||||
using BepInEx.IL2CPP;
|
||||
#endif
|
||||
|
||||
namespace UnityExplorer
|
||||
{
|
||||
#if MONO
|
||||
[BepInPlugin(ExplorerCore.GUID, "UnityExplorer", ExplorerCore.VERSION)]
|
||||
public class ExplorerBepInPlugin : BaseUnityPlugin
|
||||
{
|
||||
public static ExplorerBepInPlugin Instance;
|
||||
|
||||
public static ManualLogSource Logging => Instance?.Logger;
|
||||
|
||||
public static readonly Harmony HarmonyInstance = new Harmony(ExplorerCore.GUID);
|
||||
|
||||
internal void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
new ExplorerCore();
|
||||
|
||||
// HarmonyInstance.PatchAll();
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
ExplorerCore.Update();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CPP
|
||||
[BepInPlugin(ExplorerCore.GUID, "UnityExplorer", ExplorerCore.VERSION)]
|
||||
public class ExplorerBepInPlugin : BasePlugin
|
||||
{
|
||||
public static ExplorerBepInPlugin Instance;
|
||||
|
||||
public static ManualLogSource Logging => Instance?.Log;
|
||||
|
||||
public static readonly Harmony HarmonyInstance = new Harmony(ExplorerCore.GUID);
|
||||
|
||||
// Init
|
||||
public override void Load()
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
ClassInjector.RegisterTypeInIl2Cpp<ExplorerBehaviour>();
|
||||
|
||||
var obj = new GameObject(
|
||||
"ExplorerBehaviour",
|
||||
new Il2CppSystem.Type[] { Il2CppType.Of<ExplorerBehaviour>() }
|
||||
);
|
||||
obj.hideFlags = HideFlags.HideAndDontSave;
|
||||
GameObject.DontDestroyOnLoad(obj);
|
||||
|
||||
new ExplorerCore();
|
||||
|
||||
// HarmonyInstance.PatchAll();
|
||||
}
|
||||
|
||||
// BepInEx Il2Cpp mod class doesn't have monobehaviour methods yet, so wrap them in a dummy.
|
||||
public class ExplorerBehaviour : MonoBehaviour
|
||||
{
|
||||
public ExplorerBehaviour(IntPtr ptr) : base(ptr) { }
|
||||
|
||||
internal void Awake()
|
||||
{
|
||||
Logging.LogMessage("ExplorerBehaviour.Awake");
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
ExplorerCore.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
@ -1,216 +1,93 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityExplorer.Config;
|
||||
using UnityExplorer.Input;
|
||||
using UnityExplorer.Inspectors;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Modules;
|
||||
#if CPP
|
||||
using UnityExplorer.Helpers;
|
||||
#endif
|
||||
using UnityExplorer.UI.Main;
|
||||
|
||||
namespace UnityExplorer
|
||||
{
|
||||
public class ExplorerCore
|
||||
{
|
||||
public const string NAME = "UnityExplorer";
|
||||
public const string VERSION = "3.0.4";
|
||||
public const string VERSION = "3.3.4";
|
||||
public const string AUTHOR = "Sinai";
|
||||
public const string GUID = "com.sinai.unityexplorer";
|
||||
public const string EXPLORER_FOLDER = @"Mods\UnityExplorer";
|
||||
|
||||
public static ExplorerCore Instance { get; private set; }
|
||||
|
||||
public static bool ShowMenu
|
||||
{
|
||||
get => s_showMenu;
|
||||
set => SetShowMenu(value);
|
||||
}
|
||||
public static bool s_showMenu;
|
||||
public static IExplorerLoader Loader { get; private set; }
|
||||
|
||||
private static bool s_doneUIInit;
|
||||
private static float s_timeSinceStartup;
|
||||
// Prevent using ctor, must use Init method.
|
||||
private ExplorerCore() { }
|
||||
|
||||
public ExplorerCore()
|
||||
public static void Init(IExplorerLoader loader)
|
||||
{
|
||||
if (Instance != null)
|
||||
{
|
||||
Log("An instance of Explorer is already active!");
|
||||
Log("An instance of UnityExplorer is already active!");
|
||||
return;
|
||||
}
|
||||
|
||||
Instance = this;
|
||||
Loader = loader;
|
||||
Instance = new ExplorerCore();
|
||||
|
||||
#if CPP
|
||||
ReflectionHelpers.TryLoadGameModules();
|
||||
#endif
|
||||
if (!Directory.Exists(Loader.ExplorerFolder))
|
||||
Directory.CreateDirectory(Loader.ExplorerFolder);
|
||||
|
||||
if (!Directory.Exists(EXPLORER_FOLDER))
|
||||
Directory.CreateDirectory(EXPLORER_FOLDER);
|
||||
ConfigManager.Init(Loader.ConfigHandler);
|
||||
|
||||
ModConfig.OnLoad();
|
||||
RuntimeProvider.Init();
|
||||
|
||||
InputManager.Init();
|
||||
ForceUnlockCursor.Init();
|
||||
|
||||
SetupEvents();
|
||||
|
||||
ShowMenu = true;
|
||||
UIManager.Init();
|
||||
|
||||
Log($"{NAME} {VERSION} initialized.");
|
||||
}
|
||||
|
||||
public static void Update()
|
||||
{
|
||||
if (!s_doneUIInit)
|
||||
CheckUIInit();
|
||||
|
||||
if (MouseInspector.Enabled)
|
||||
MouseInspector.UpdateInspect();
|
||||
else
|
||||
{
|
||||
if (InputManager.GetKeyDown(ModConfig.Instance.Main_Menu_Toggle))
|
||||
ShowMenu = !ShowMenu;
|
||||
|
||||
if (ShowMenu && s_doneUIInit)
|
||||
UIManager.Update();
|
||||
}
|
||||
UIManager.Update();
|
||||
}
|
||||
|
||||
private static void CheckUIInit()
|
||||
public static void Log(object message)
|
||||
=> Log(message, LogType.Log, false);
|
||||
|
||||
public static void LogWarning(object message)
|
||||
=> Log(message, LogType.Warning, false);
|
||||
|
||||
public static void LogError(object message)
|
||||
=> Log(message, LogType.Error, false);
|
||||
|
||||
internal static void Log(object message, LogType logType, bool isFromUnity = false)
|
||||
{
|
||||
s_timeSinceStartup += Time.deltaTime;
|
||||
|
||||
if (s_timeSinceStartup > 0.1f)
|
||||
{
|
||||
s_doneUIInit = true;
|
||||
try
|
||||
{
|
||||
UIManager.Init();
|
||||
Log("Initialized UnityExplorer UI.");
|
||||
}
|
||||
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 (isFromUnity && !ConfigManager.Log_Unity_Debug.Value)
|
||||
return;
|
||||
|
||||
message = $"[UNITY] {message}";
|
||||
string log = message?.ToString() ?? "";
|
||||
|
||||
switch (type)
|
||||
switch (logType)
|
||||
{
|
||||
case LogType.Assert:
|
||||
case LogType.Log:
|
||||
Log(message, true);
|
||||
Loader.OnLogMessage(log);
|
||||
DebugConsole.Log(log, Color.white);
|
||||
break;
|
||||
|
||||
case LogType.Warning:
|
||||
LogWarning(message, true);
|
||||
Loader.OnLogWarning(log);
|
||||
DebugConsole.Log(log, Color.yellow);
|
||||
break;
|
||||
case LogType.Exception:
|
||||
|
||||
case LogType.Error:
|
||||
LogError(message, true);
|
||||
case LogType.Exception:
|
||||
Loader.OnLogError(log);
|
||||
DebugConsole.Log(log, Color.red);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Log(object message, bool unity = false)
|
||||
{
|
||||
DebugConsole.Log(message?.ToString());
|
||||
|
||||
if (unity)
|
||||
return;
|
||||
|
||||
#if ML
|
||||
MelonLoader.MelonLogger.Log(message?.ToString());
|
||||
#else
|
||||
ExplorerBepInPlugin.Logging?.LogMessage(message?.ToString());
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void LogWarning(object message, bool unity = false)
|
||||
{
|
||||
DebugConsole.Log(message?.ToString(), "FFFF00");
|
||||
|
||||
if (unity)
|
||||
return;
|
||||
|
||||
#if ML
|
||||
MelonLoader.MelonLogger.LogWarning(message?.ToString());
|
||||
#else
|
||||
ExplorerBepInPlugin.Logging?.LogWarning(message?.ToString());
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void LogError(object message, bool unity = false)
|
||||
{
|
||||
DebugConsole.Log(message?.ToString(), "FF0000");
|
||||
|
||||
if (unity)
|
||||
return;
|
||||
|
||||
#if ML
|
||||
MelonLoader.MelonLogger.LogError(message?.ToString());
|
||||
#else
|
||||
ExplorerBepInPlugin.Logging?.LogError(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -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
|
@ -1,36 +0,0 @@
|
||||
#if CPP
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace UnityExplorer.Helpers
|
||||
{
|
||||
[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "External methods")]
|
||||
public static class ICallHelper
|
||||
{
|
||||
private static readonly Dictionary<string, Delegate> iCallCache = new Dictionary<string, Delegate>();
|
||||
|
||||
public static T GetICall<T>(string iCallName) where T : Delegate
|
||||
{
|
||||
if (iCallCache.ContainsKey(iCallName))
|
||||
return (T)iCallCache[iCallName];
|
||||
|
||||
IntPtr ptr = il2cpp_resolve_icall(iCallName);
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
throw new MissingMethodException($"Could not resolve internal call by name '{iCallName}'!");
|
||||
}
|
||||
|
||||
Delegate iCall = Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
|
||||
iCallCache.Add(iCallName, iCall);
|
||||
|
||||
return (T)iCall;
|
||||
}
|
||||
|
||||
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||
public static extern IntPtr il2cpp_resolve_icall([MarshalAs(UnmanagedType.LPStr)] string name);
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,278 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using BF = System.Reflection.BindingFlags;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
#if CPP
|
||||
using CppType = Il2CppSystem.Type;
|
||||
using UnhollowerBaseLib;
|
||||
using UnhollowerRuntimeLib;
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace UnityExplorer.Helpers
|
||||
{
|
||||
[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "External methods")]
|
||||
public static class ReflectionHelpers
|
||||
{
|
||||
public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
|
||||
|
||||
public static Type GetTypeByName(string fullName)
|
||||
{
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (var type in asm.TryGetTypes())
|
||||
{
|
||||
if (type.FullName == fullName)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Type[] GetAllBaseTypes(object obj) => GetAllBaseTypes(GetActualType(obj));
|
||||
|
||||
public static Type[] GetAllBaseTypes(Type type)
|
||||
{
|
||||
List<Type> list = new List<Type>();
|
||||
|
||||
while (type != null)
|
||||
{
|
||||
list.Add(type);
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public static Type GetActualType(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return null;
|
||||
|
||||
var type = obj.GetType();
|
||||
#if CPP
|
||||
if (obj is Il2CppSystem.Object ilObject)
|
||||
{
|
||||
if (ilObject is CppType)
|
||||
return typeof(CppType);
|
||||
|
||||
if (!string.IsNullOrEmpty(type.Namespace))
|
||||
{
|
||||
// Il2CppSystem-namespace objects should just return GetType,
|
||||
// because using GetIl2CppType returns the System namespace type instead.
|
||||
if (type.Namespace.StartsWith("System.") || type.Namespace.StartsWith("Il2CppSystem."))
|
||||
return ilObject.GetType();
|
||||
}
|
||||
|
||||
var il2cppType = ilObject.GetIl2CppType();
|
||||
|
||||
// check if type is injected
|
||||
IntPtr classPtr = il2cpp_object_get_class(ilObject.Pointer);
|
||||
if (RuntimeSpecificsStore.IsInjected(classPtr))
|
||||
{
|
||||
var typeByName = GetTypeByName(il2cppType.FullName);
|
||||
if (typeByName != null)
|
||||
return typeByName;
|
||||
}
|
||||
|
||||
// this should be fine for all other il2cpp objects
|
||||
var getType = GetMonoType(il2cppType);
|
||||
if (getType != null)
|
||||
return getType;
|
||||
}
|
||||
#endif
|
||||
return type;
|
||||
}
|
||||
|
||||
#if CPP
|
||||
private static readonly Dictionary<CppType, Type> Il2CppToMonoType = new Dictionary<CppType, Type>();
|
||||
|
||||
public static Type GetMonoType(CppType cppType)
|
||||
{
|
||||
if (Il2CppToMonoType.ContainsKey(cppType))
|
||||
return Il2CppToMonoType[cppType];
|
||||
|
||||
var getType = Type.GetType(cppType.AssemblyQualifiedName);
|
||||
Il2CppToMonoType.Add(cppType, getType);
|
||||
return getType;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<Type, IntPtr> CppClassPointers = new Dictionary<Type, IntPtr>();
|
||||
|
||||
public static object Il2CppCast(this object obj, Type castTo)
|
||||
{
|
||||
if (!(obj is Il2CppSystem.Object ilObj))
|
||||
return obj;
|
||||
|
||||
if (!Il2CppTypeNotNull(castTo, out IntPtr castToPtr))
|
||||
return obj;
|
||||
|
||||
IntPtr castFromPtr = il2cpp_object_get_class(ilObj.Pointer);
|
||||
|
||||
if (!il2cpp_class_is_assignable_from(castToPtr, castFromPtr))
|
||||
return obj;
|
||||
|
||||
if (RuntimeSpecificsStore.IsInjected(castToPtr))
|
||||
return UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(ilObj.Pointer);
|
||||
|
||||
return Activator.CreateInstance(castTo, ilObj.Pointer);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
public static IEnumerable<Type> TryGetTypes(this Assembly asm)
|
||||
{
|
||||
try
|
||||
{
|
||||
return asm.GetTypes();
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
try
|
||||
{
|
||||
return asm.GetExportedTypes();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return e.Types.Where(t => t != null);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Enumerable.Empty<Type>();
|
||||
}
|
||||
}
|
||||
|
||||
#if CPP
|
||||
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
|
||||
|
||||
public static bool IsEnumerable(Type t)
|
||||
{
|
||||
if (typeof(IEnumerable).IsAssignableFrom(t))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#if CPP
|
||||
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
|
||||
{
|
||||
return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g)
|
||||
|| typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g)
|
||||
|| typeof(Il2CppSystem.Collections.Generic.HashSet<>).IsAssignableFrom(g);
|
||||
}
|
||||
else
|
||||
{
|
||||
return typeof(Il2CppSystem.Collections.IList).IsAssignableFrom(t);
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool IsDictionary(Type t)
|
||||
{
|
||||
if (typeof(IDictionary).IsAssignableFrom(t))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#if CPP
|
||||
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
|
||||
{
|
||||
return typeof(Il2CppSystem.Collections.Generic.Dictionary<,>).IsAssignableFrom(g)
|
||||
|| typeof(Il2CppSystem.Collections.Generic.IDictionary<,>).IsAssignableFrom(g);
|
||||
}
|
||||
else
|
||||
{
|
||||
return typeof(Il2CppSystem.Collections.IDictionary).IsAssignableFrom(t)
|
||||
|| typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(t);
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string ExceptionToString(Exception e, bool innerMost = false)
|
||||
{
|
||||
while (innerMost && e.InnerException != null)
|
||||
{
|
||||
#if CPP
|
||||
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException runtimeEx)
|
||||
break;
|
||||
#endif
|
||||
e = e.InnerException;
|
||||
}
|
||||
|
||||
return e.GetType() + ", " + e.Message;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.Helpers
|
||||
{
|
||||
public static class UnityHelpers
|
||||
{
|
||||
private static Camera m_mainCamera;
|
||||
|
||||
public static Camera MainCamera
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_mainCamera)
|
||||
{
|
||||
m_mainCamera = Camera.main;
|
||||
}
|
||||
return m_mainCamera;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsNullOrDestroyed(this object obj, bool suppressWarning = true)
|
||||
{
|
||||
var unityObj = obj as Object;
|
||||
if (obj == null)
|
||||
{
|
||||
if (!suppressWarning)
|
||||
ExplorerCore.LogWarning("The target instance is null!");
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (obj is Object)
|
||||
{
|
||||
if (!unityObj)
|
||||
{
|
||||
if (!suppressWarning)
|
||||
ExplorerCore.LogWarning("The target UnityEngine.Object was destroyed!");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string ToStringLong(this Vector3 vec)
|
||||
{
|
||||
return $"{vec.x:F3}, {vec.y:F3}, {vec.z:F3}";
|
||||
}
|
||||
|
||||
public static string GetTransformPath(this Transform t, bool includeThisName = false)
|
||||
{
|
||||
string path = includeThisName ? t.transform.name : "";
|
||||
|
||||
while (t.parent != null)
|
||||
{
|
||||
t = t.parent;
|
||||
path = $"{t.name}/{path}";
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Target Name="ILRepacker" AfterTargets="Build">
|
||||
|
||||
<ItemGroup>
|
||||
<InputAssemblies Include="$(OutputPath)$(AssemblyName).dll" />
|
||||
<InputAssemblies Include="..\lib\mcs.dll" />
|
||||
</ItemGroup>
|
||||
|
||||
<ILRepack
|
||||
Parallel="true"
|
||||
Internalize="true"
|
||||
DebugInfo="false"
|
||||
LibraryPath="..\lib\"
|
||||
InputAssemblies="@(InputAssemblies)"
|
||||
TargetKind="Dll"
|
||||
OutputFile="$(OutputPath)$(AssemblyName).dll"
|
||||
/>
|
||||
|
||||
</Target>
|
||||
|
||||
<Target Name="ILRepacker" AfterTargets="Build">
|
||||
<ItemGroup>
|
||||
<InputAssemblies Include="$(OutputPath)$(AssemblyName).dll" />
|
||||
<InputAssemblies Include="..\lib\mcs.dll" />
|
||||
<InputAssemblies Include="..\lib\INIFileParser.dll" Condition="'$(IsStandalone)' == 'true'"/>
|
||||
</ItemGroup>
|
||||
<ILRepack
|
||||
Parallel="true"
|
||||
Internalize="true"
|
||||
DebugInfo="false"
|
||||
LibraryPath="..\lib\"
|
||||
InputAssemblies="@(InputAssemblies)"
|
||||
TargetKind="Dll"
|
||||
OutputFile="$(OutputPath)$(AssemblyName).dll" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
@ -1,630 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.UI;
|
||||
//using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Input;
|
||||
using UnityExplorer.Unstrip;
|
||||
|
||||
namespace UnityExplorer.Inspectors.GameObjects
|
||||
{
|
||||
public class GameObjectControls
|
||||
{
|
||||
internal static GameObjectControls Instance;
|
||||
|
||||
public GameObjectControls()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
private static InputField s_setParentInput;
|
||||
|
||||
private static ControlEditor s_positionControl;
|
||||
private static ControlEditor s_localPosControl;
|
||||
private static ControlEditor s_rotationControl;
|
||||
private static ControlEditor s_scaleControl;
|
||||
|
||||
// Transform Vector editors
|
||||
|
||||
internal struct ControlEditor
|
||||
{
|
||||
public InputField fullValue;
|
||||
public Slider[] sliders;
|
||||
public InputField[] inputs;
|
||||
public Text[] values;
|
||||
}
|
||||
|
||||
internal static bool s_sliderChangedWanted;
|
||||
private static Slider s_currentSlider;
|
||||
private static ControlType s_currentSliderType;
|
||||
private static VectorValue s_currentSliderValueType;
|
||||
private static float s_currentSliderValue;
|
||||
|
||||
internal enum ControlType
|
||||
{
|
||||
position,
|
||||
localPosition,
|
||||
eulerAngles,
|
||||
localScale
|
||||
}
|
||||
|
||||
internal enum VectorValue
|
||||
{
|
||||
x, y, z
|
||||
};
|
||||
|
||||
internal void RefreshControls()
|
||||
{
|
||||
var go = GameObjectInspector.ActiveInstance.TargetGO;
|
||||
|
||||
s_positionControl.fullValue.text = go.transform.position.ToStringLong();
|
||||
s_positionControl.values[0].text = go.transform.position.x.ToString("F3");
|
||||
s_positionControl.values[1].text = go.transform.position.y.ToString("F3");
|
||||
s_positionControl.values[2].text = go.transform.position.z.ToString("F3");
|
||||
|
||||
s_localPosControl.fullValue.text = go.transform.localPosition.ToStringLong();
|
||||
s_localPosControl.values[0].text = go.transform.localPosition.x.ToString("F3");
|
||||
s_localPosControl.values[1].text = go.transform.localPosition.y.ToString("F3");
|
||||
s_localPosControl.values[2].text = go.transform.localPosition.z.ToString("F3");
|
||||
|
||||
s_rotationControl.fullValue.text = go.transform.eulerAngles.ToStringLong();
|
||||
s_rotationControl.values[0].text = go.transform.eulerAngles.x.ToString("F3");
|
||||
s_rotationControl.values[1].text = go.transform.eulerAngles.y.ToString("F3");
|
||||
s_rotationControl.values[2].text = go.transform.eulerAngles.z.ToString("F3");
|
||||
|
||||
s_scaleControl.fullValue.text = go.transform.localScale.ToStringLong();
|
||||
s_scaleControl.values[0].text = go.transform.localScale.x.ToString("F3");
|
||||
s_scaleControl.values[1].text = go.transform.localScale.y.ToString("F3");
|
||||
s_scaleControl.values[2].text = go.transform.localScale.z.ToString("F3");
|
||||
|
||||
}
|
||||
|
||||
internal static void OnSetParentClicked()
|
||||
{
|
||||
var go = GameObjectInspector.ActiveInstance.TargetGO;
|
||||
|
||||
if (!go)
|
||||
return;
|
||||
|
||||
var input = s_setParentInput.text;
|
||||
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
go.transform.parent = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GameObject.Find(input) is GameObject newParent)
|
||||
{
|
||||
go.transform.parent = newParent.transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
ExplorerCore.Log($"Could not find any GameObject from name or path '{input}'! Note: The target must be enabled.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void OnSliderControlChanged(float value, Slider slider, ControlType controlType, VectorValue vectorValue)
|
||||
{
|
||||
if (value == 0)
|
||||
s_sliderChangedWanted = false;
|
||||
else
|
||||
{
|
||||
if (!s_sliderChangedWanted)
|
||||
{
|
||||
s_sliderChangedWanted = true;
|
||||
s_currentSlider = slider;
|
||||
s_currentSliderType = controlType;
|
||||
s_currentSliderValueType = vectorValue;
|
||||
}
|
||||
|
||||
s_currentSliderValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void UpdateSliderControl()
|
||||
{
|
||||
if (!InputManager.GetMouseButton(0))
|
||||
{
|
||||
s_sliderChangedWanted = false;
|
||||
s_currentSlider.value = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameObjectInspector.ActiveInstance == null) return;
|
||||
|
||||
var transform = GameObjectInspector.ActiveInstance.TargetGO.transform;
|
||||
|
||||
// get the current vector for the control type
|
||||
Vector3 vector = Vector2.zero;
|
||||
switch (s_currentSliderType)
|
||||
{
|
||||
case ControlType.position:
|
||||
vector = transform.position; break;
|
||||
case ControlType.localPosition:
|
||||
vector = transform.localPosition; break;
|
||||
case ControlType.eulerAngles:
|
||||
vector = transform.eulerAngles; break;
|
||||
case ControlType.localScale:
|
||||
vector = transform.localScale; break;
|
||||
}
|
||||
|
||||
// apply vector value change
|
||||
switch (s_currentSliderValueType)
|
||||
{
|
||||
case VectorValue.x:
|
||||
vector.x += s_currentSliderValue; break;
|
||||
case VectorValue.y:
|
||||
vector.y += s_currentSliderValue; break;
|
||||
case VectorValue.z:
|
||||
vector.z += s_currentSliderValue; break;
|
||||
}
|
||||
|
||||
// set vector to transform member
|
||||
switch (s_currentSliderType)
|
||||
{
|
||||
case ControlType.position:
|
||||
transform.position = vector; break;
|
||||
case ControlType.localPosition:
|
||||
transform.localPosition = vector; break;
|
||||
case ControlType.eulerAngles:
|
||||
transform.eulerAngles = vector; break;
|
||||
case ControlType.localScale:
|
||||
transform.localScale = vector; break;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void OnVectorControlInputApplied(ControlType controlType, VectorValue vectorValue)
|
||||
{
|
||||
if (!(InspectorManager.Instance.m_activeInspector is GameObjectInspector instance)) return;
|
||||
|
||||
// get relevant input for controltype + value
|
||||
|
||||
InputField[] inputs = null;
|
||||
switch (controlType)
|
||||
{
|
||||
case ControlType.position:
|
||||
inputs = s_positionControl.inputs; break;
|
||||
case ControlType.localPosition:
|
||||
inputs = s_localPosControl.inputs; break;
|
||||
case ControlType.eulerAngles:
|
||||
inputs = s_rotationControl.inputs; break;
|
||||
case ControlType.localScale:
|
||||
inputs = s_scaleControl.inputs; break;
|
||||
}
|
||||
InputField input = inputs[(int)vectorValue];
|
||||
|
||||
float val = float.Parse(input.text);
|
||||
|
||||
// apply transform value
|
||||
|
||||
Vector3 vector = Vector3.zero;
|
||||
var transform = instance.TargetGO.transform;
|
||||
switch (controlType)
|
||||
{
|
||||
case ControlType.position:
|
||||
vector = transform.position; break;
|
||||
case ControlType.localPosition:
|
||||
vector = transform.localPosition; break;
|
||||
case ControlType.eulerAngles:
|
||||
vector = transform.eulerAngles; break;
|
||||
case ControlType.localScale:
|
||||
vector = transform.localScale; break;
|
||||
}
|
||||
|
||||
switch (vectorValue)
|
||||
{
|
||||
case VectorValue.x:
|
||||
vector.x = val; break;
|
||||
case VectorValue.y:
|
||||
vector.y = val; break;
|
||||
case VectorValue.z:
|
||||
vector.z = val; break;
|
||||
}
|
||||
|
||||
// set back to transform
|
||||
switch (controlType)
|
||||
{
|
||||
case ControlType.position:
|
||||
transform.position = vector; break;
|
||||
case ControlType.localPosition:
|
||||
transform.localPosition = vector; break;
|
||||
case ControlType.eulerAngles:
|
||||
transform.eulerAngles = vector; break;
|
||||
case ControlType.localScale:
|
||||
transform.localScale = vector; break;
|
||||
}
|
||||
}
|
||||
|
||||
#region UI CONSTRUCTION
|
||||
|
||||
internal void ConstructControls(GameObject parent)
|
||||
{
|
||||
var controlsObj = UIFactory.CreateVerticalGroup(parent, new Color(0.07f, 0.07f, 0.07f));
|
||||
var controlsGroup = controlsObj.GetComponent<VerticalLayoutGroup>();
|
||||
controlsGroup.childForceExpandWidth = false;
|
||||
controlsGroup.childControlWidth = true;
|
||||
controlsGroup.childForceExpandHeight = false;
|
||||
controlsGroup.spacing = 5;
|
||||
controlsGroup.padding.top = 4;
|
||||
controlsGroup.padding.left = 4;
|
||||
controlsGroup.padding.right = 4;
|
||||
controlsGroup.padding.bottom = 4;
|
||||
|
||||
// ~~~~~~ Top row ~~~~~~
|
||||
|
||||
var topRow = UIFactory.CreateHorizontalGroup(controlsObj, new Color(1, 1, 1, 0));
|
||||
var topRowGroup = topRow.GetComponent<HorizontalLayoutGroup>();
|
||||
topRowGroup.childForceExpandWidth = false;
|
||||
topRowGroup.childControlWidth = true;
|
||||
topRowGroup.childForceExpandHeight = false;
|
||||
topRowGroup.childControlHeight = true;
|
||||
topRowGroup.spacing = 5;
|
||||
|
||||
var hideButtonObj = UIFactory.CreateButton(topRow);
|
||||
var hideButton = hideButtonObj.GetComponent<Button>();
|
||||
var hideColors = hideButton.colors;
|
||||
hideColors.normalColor = new Color(0.16f, 0.16f, 0.16f);
|
||||
hideButton.colors = hideColors;
|
||||
var hideText = hideButtonObj.GetComponentInChildren<Text>();
|
||||
hideText.text = "Show";
|
||||
hideText.fontSize = 14;
|
||||
var hideButtonLayout = hideButtonObj.AddComponent<LayoutElement>();
|
||||
hideButtonLayout.minWidth = 40;
|
||||
hideButtonLayout.flexibleWidth = 0;
|
||||
hideButtonLayout.minHeight = 25;
|
||||
hideButtonLayout.flexibleHeight = 0;
|
||||
|
||||
var topTitle = UIFactory.CreateLabel(topRow, TextAnchor.MiddleLeft);
|
||||
var topText = topTitle.GetComponent<Text>();
|
||||
topText.text = "Controls";
|
||||
var titleLayout = topTitle.AddComponent<LayoutElement>();
|
||||
titleLayout.minWidth = 100;
|
||||
titleLayout.flexibleWidth = 9500;
|
||||
titleLayout.minHeight = 25;
|
||||
|
||||
//// ~~~~~~~~ Content ~~~~~~~~ //
|
||||
|
||||
var contentObj = UIFactory.CreateVerticalGroup(controlsObj, new Color(1, 1, 1, 0));
|
||||
var contentGroup = contentObj.GetComponent<VerticalLayoutGroup>();
|
||||
contentGroup.childForceExpandHeight = false;
|
||||
contentGroup.childControlHeight = true;
|
||||
contentGroup.spacing = 5;
|
||||
contentGroup.childForceExpandWidth = true;
|
||||
contentGroup.childControlWidth = true;
|
||||
|
||||
// ~~ add hide button callback now that we have scroll reference ~~
|
||||
hideButton.onClick.AddListener(OnHideClicked);
|
||||
void OnHideClicked()
|
||||
{
|
||||
if (hideText.text == "Show")
|
||||
{
|
||||
hideText.text = "Hide";
|
||||
contentObj.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
hideText.text = "Show";
|
||||
contentObj.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
// transform controls
|
||||
ConstructVector3Editor(contentObj, "Position", ControlType.position, out s_positionControl);
|
||||
ConstructVector3Editor(contentObj, "Local Position", ControlType.localPosition, out s_localPosControl);
|
||||
ConstructVector3Editor(contentObj, "Rotation", ControlType.eulerAngles, out s_rotationControl);
|
||||
ConstructVector3Editor(contentObj, "Scale", ControlType.localScale, out s_scaleControl);
|
||||
|
||||
// set parent
|
||||
ConstructSetParent(contentObj);
|
||||
|
||||
// bottom row buttons
|
||||
ConstructBottomButtons(contentObj);
|
||||
|
||||
// set controls content inactive now that content is made (otherwise TMP font size goes way too big?)
|
||||
contentObj.SetActive(false);
|
||||
}
|
||||
|
||||
internal void ConstructSetParent(GameObject contentObj)
|
||||
{
|
||||
var setParentGroupObj = UIFactory.CreateHorizontalGroup(contentObj, new Color(1, 1, 1, 0));
|
||||
var setParentGroup = setParentGroupObj.GetComponent<HorizontalLayoutGroup>();
|
||||
setParentGroup.childForceExpandHeight = false;
|
||||
setParentGroup.childControlHeight = true;
|
||||
setParentGroup.childForceExpandWidth = false;
|
||||
setParentGroup.childControlWidth = true;
|
||||
setParentGroup.spacing = 5;
|
||||
var setParentLayout = setParentGroupObj.AddComponent<LayoutElement>();
|
||||
setParentLayout.minHeight = 25;
|
||||
setParentLayout.flexibleHeight = 0;
|
||||
|
||||
var setParentLabelObj = UIFactory.CreateLabel(setParentGroupObj, TextAnchor.MiddleLeft);
|
||||
var setParentLabel = setParentLabelObj.GetComponent<Text>();
|
||||
setParentLabel.text = "Set Parent:";
|
||||
setParentLabel.color = Color.grey;
|
||||
setParentLabel.fontSize = 14;
|
||||
var setParentLabelLayout = setParentLabelObj.AddComponent<LayoutElement>();
|
||||
setParentLabelLayout.minWidth = 110;
|
||||
setParentLabelLayout.minHeight = 25;
|
||||
setParentLabelLayout.flexibleWidth = 0;
|
||||
|
||||
var setParentInputObj = UIFactory.CreateInputField(setParentGroupObj);
|
||||
s_setParentInput = setParentInputObj.GetComponent<InputField>();
|
||||
var placeholderInput = s_setParentInput.placeholder.GetComponent<Text>();
|
||||
placeholderInput.text = "Enter a GameObject name or path...";
|
||||
var setParentInputLayout = setParentInputObj.AddComponent<LayoutElement>();
|
||||
setParentInputLayout.minHeight = 25;
|
||||
setParentInputLayout.preferredWidth = 400;
|
||||
setParentInputLayout.flexibleWidth = 9999;
|
||||
|
||||
var applyButtonObj = UIFactory.CreateButton(setParentGroupObj);
|
||||
var applyButton = applyButtonObj.GetComponent<Button>();
|
||||
applyButton.onClick.AddListener(OnSetParentClicked);
|
||||
var applyText = applyButtonObj.GetComponentInChildren<Text>();
|
||||
applyText.text = "Apply";
|
||||
var applyLayout = applyButtonObj.AddComponent<LayoutElement>();
|
||||
applyLayout.minWidth = 55;
|
||||
applyLayout.flexibleWidth = 0;
|
||||
applyLayout.minHeight = 25;
|
||||
applyLayout.flexibleHeight = 0;
|
||||
}
|
||||
|
||||
internal void ConstructVector3Editor(GameObject parent, string title, ControlType type, out ControlEditor editor)
|
||||
{
|
||||
editor = new ControlEditor();
|
||||
|
||||
var topBarObj = UIFactory.CreateHorizontalGroup(parent, new Color(1, 1, 1, 0));
|
||||
var topGroup = topBarObj.GetComponent<HorizontalLayoutGroup>();
|
||||
topGroup.childForceExpandWidth = false;
|
||||
topGroup.childControlWidth = true;
|
||||
topGroup.childForceExpandHeight = false;
|
||||
topGroup.childControlHeight = true;
|
||||
topGroup.spacing = 5;
|
||||
var topLayout = topBarObj.AddComponent<LayoutElement>();
|
||||
topLayout.minHeight = 25;
|
||||
topLayout.flexibleHeight = 0;
|
||||
|
||||
var titleObj = UIFactory.CreateLabel(topBarObj, TextAnchor.MiddleLeft);
|
||||
var titleText = titleObj.GetComponent<Text>();
|
||||
titleText.text = title;
|
||||
titleText.color = Color.grey;
|
||||
titleText.fontSize = 14;
|
||||
var titleLayout = titleObj.AddComponent<LayoutElement>();
|
||||
titleLayout.minWidth = 110;
|
||||
titleLayout.flexibleWidth = 0;
|
||||
titleLayout.minHeight = 25;
|
||||
|
||||
// expand button
|
||||
var expandButtonObj = UIFactory.CreateButton(topBarObj);
|
||||
var expandButton = expandButtonObj.GetComponent<Button>();
|
||||
var expandText = expandButtonObj.GetComponentInChildren<Text>();
|
||||
expandText.text = "▼";
|
||||
expandText.fontSize = 12;
|
||||
var btnLayout = expandButtonObj.AddComponent<LayoutElement>();
|
||||
btnLayout.minWidth = 35;
|
||||
btnLayout.flexibleWidth = 0;
|
||||
btnLayout.minHeight = 25;
|
||||
btnLayout.flexibleHeight = 0;
|
||||
|
||||
// readonly value input
|
||||
|
||||
var valueInputObj = UIFactory.CreateInputField(topBarObj);
|
||||
var valueInput = valueInputObj.GetComponent<InputField>();
|
||||
valueInput.readOnly = true;
|
||||
//valueInput.richText = true;
|
||||
//valueInput.isRichTextEditingAllowed = true;
|
||||
var valueInputLayout = valueInputObj.AddComponent<LayoutElement>();
|
||||
valueInputLayout.minHeight = 25;
|
||||
valueInputLayout.flexibleHeight = 0;
|
||||
valueInputLayout.preferredWidth = 400;
|
||||
valueInputLayout.flexibleWidth = 9999;
|
||||
|
||||
editor.fullValue = valueInput;
|
||||
|
||||
editor.sliders = new Slider[3];
|
||||
editor.inputs = new InputField[3];
|
||||
editor.values = new Text[3];
|
||||
|
||||
var xRow = ConstructEditorRow(parent, editor, type, VectorValue.x);
|
||||
xRow.SetActive(false);
|
||||
var yRow = ConstructEditorRow(parent, editor, type, VectorValue.y);
|
||||
yRow.SetActive(false);
|
||||
var zRow = ConstructEditorRow(parent, editor, type, VectorValue.z);
|
||||
zRow.SetActive(false);
|
||||
|
||||
// add expand callback now that we have group reference
|
||||
expandButton.onClick.AddListener(ToggleExpand);
|
||||
void ToggleExpand()
|
||||
{
|
||||
if (xRow.activeSelf)
|
||||
{
|
||||
xRow.SetActive(false);
|
||||
yRow.SetActive(false);
|
||||
zRow.SetActive(false);
|
||||
expandText.text = "▼";
|
||||
}
|
||||
else
|
||||
{
|
||||
xRow.SetActive(true);
|
||||
yRow.SetActive(true);
|
||||
zRow.SetActive(true);
|
||||
expandText.text = "▲";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal GameObject ConstructEditorRow(GameObject parent, ControlEditor editor, ControlType type, VectorValue vectorValue)
|
||||
{
|
||||
var rowObject = UIFactory.CreateHorizontalGroup(parent, new Color(1, 1, 1, 0));
|
||||
var rowGroup = rowObject.GetComponent<HorizontalLayoutGroup>();
|
||||
rowGroup.childForceExpandWidth = false;
|
||||
rowGroup.childControlWidth = true;
|
||||
rowGroup.childForceExpandHeight = false;
|
||||
rowGroup.childControlHeight = true;
|
||||
rowGroup.spacing = 5;
|
||||
var rowLayout = rowObject.AddComponent<LayoutElement>();
|
||||
rowLayout.minHeight = 25;
|
||||
rowLayout.flexibleHeight = 0;
|
||||
rowLayout.minWidth = 100;
|
||||
|
||||
// Value labels
|
||||
|
||||
var labelObj = UIFactory.CreateLabel(rowObject, TextAnchor.MiddleLeft);
|
||||
var labelText = labelObj.GetComponent<Text>();
|
||||
labelText.color = Color.cyan;
|
||||
labelText.text = $"{vectorValue.ToString().ToUpper()}:";
|
||||
labelText.fontSize = 14;
|
||||
labelText.resizeTextMaxSize = 14;
|
||||
labelText.resizeTextForBestFit = true;
|
||||
var labelLayout = labelObj.AddComponent<LayoutElement>();
|
||||
labelLayout.minHeight = 25;
|
||||
labelLayout.flexibleHeight = 0;
|
||||
labelLayout.minWidth = 25;
|
||||
labelLayout.flexibleWidth = 0;
|
||||
|
||||
// actual value label
|
||||
var valueLabelObj = UIFactory.CreateLabel(rowObject, TextAnchor.MiddleLeft);
|
||||
var valueLabel = valueLabelObj.GetComponent<Text>();
|
||||
editor.values[(int)vectorValue] = valueLabel;
|
||||
var valueLabelLayout = valueLabelObj.AddComponent<LayoutElement>();
|
||||
valueLabelLayout.minWidth = 85;
|
||||
valueLabelLayout.flexibleWidth = 0;
|
||||
valueLabelLayout.minHeight = 25;
|
||||
|
||||
// input field
|
||||
|
||||
var inputHolder = UIFactory.CreateVerticalGroup(rowObject, new Color(1, 1, 1, 0));
|
||||
var inputHolderGroup = inputHolder.GetComponent<VerticalLayoutGroup>();
|
||||
inputHolderGroup.childForceExpandHeight = false;
|
||||
inputHolderGroup.childControlHeight = true;
|
||||
inputHolderGroup.childForceExpandWidth = false;
|
||||
inputHolderGroup.childControlWidth = true;
|
||||
|
||||
var inputObj = UIFactory.CreateInputField(inputHolder);
|
||||
var input = inputObj.GetComponent<InputField>();
|
||||
input.characterValidation = InputField.CharacterValidation.Decimal;
|
||||
|
||||
var inputLayout = inputObj.AddComponent<LayoutElement>();
|
||||
inputLayout.minHeight = 25;
|
||||
inputLayout.flexibleHeight = 0;
|
||||
inputLayout.minWidth = 90;
|
||||
inputLayout.flexibleWidth = 50;
|
||||
|
||||
editor.inputs[(int)vectorValue] = input;
|
||||
|
||||
// apply button
|
||||
|
||||
var applyBtnObj = UIFactory.CreateButton(rowObject);
|
||||
var applyBtn = applyBtnObj.GetComponent<Button>();
|
||||
var applyText = applyBtnObj.GetComponentInChildren<Text>();
|
||||
applyText.text = "Apply";
|
||||
applyText.fontSize = 14;
|
||||
var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
|
||||
applyLayout.minWidth = 60;
|
||||
applyLayout.minHeight = 25;
|
||||
|
||||
applyBtn.onClick.AddListener(() => { OnVectorControlInputApplied(type, vectorValue); });
|
||||
|
||||
// Slider
|
||||
|
||||
var sliderObj = UIFactory.CreateSlider(rowObject);
|
||||
sliderObj.transform.Find("Fill Area").gameObject.SetActive(false);
|
||||
var sliderLayout = sliderObj.AddComponent<LayoutElement>();
|
||||
sliderLayout.minHeight = 20;
|
||||
sliderLayout.flexibleHeight = 0;
|
||||
sliderLayout.minWidth = 200;
|
||||
sliderLayout.flexibleWidth = 9000;
|
||||
var slider = sliderObj.GetComponent<Slider>();
|
||||
var sliderColors = slider.colors;
|
||||
sliderColors.normalColor = new Color(0.65f, 0.65f, 0.65f);
|
||||
slider.colors = sliderColors;
|
||||
slider.minValue = -2;
|
||||
slider.maxValue = 2;
|
||||
slider.value = 0;
|
||||
slider.onValueChanged.AddListener((float val) => { OnSliderControlChanged(val, slider, type, vectorValue); });
|
||||
editor.sliders[(int)vectorValue] = slider;
|
||||
|
||||
return rowObject;
|
||||
}
|
||||
|
||||
internal void ConstructBottomButtons(GameObject contentObj)
|
||||
{
|
||||
var bottomRow = UIFactory.CreateHorizontalGroup(contentObj, new Color(1, 1, 1, 0));
|
||||
var bottomGroup = bottomRow.GetComponent<HorizontalLayoutGroup>();
|
||||
bottomGroup.childForceExpandWidth = true;
|
||||
bottomGroup.childControlWidth = true;
|
||||
bottomGroup.spacing = 4;
|
||||
var bottomLayout = bottomRow.AddComponent<LayoutElement>();
|
||||
bottomLayout.minHeight = 25;
|
||||
|
||||
var instantiateBtnObj = UIFactory.CreateButton(bottomRow, new Color(0.2f, 0.2f, 0.2f));
|
||||
var instantiateBtn = instantiateBtnObj.GetComponent<Button>();
|
||||
|
||||
instantiateBtn.onClick.AddListener(InstantiateBtn);
|
||||
|
||||
var instantiateText = instantiateBtnObj.GetComponentInChildren<Text>();
|
||||
instantiateText.text = "Instantiate";
|
||||
instantiateText.fontSize = 14;
|
||||
var instantiateLayout = instantiateBtnObj.AddComponent<LayoutElement>();
|
||||
instantiateLayout.minWidth = 150;
|
||||
|
||||
void InstantiateBtn()
|
||||
{
|
||||
var go = GameObjectInspector.ActiveInstance.TargetGO;
|
||||
if (!go)
|
||||
return;
|
||||
|
||||
var clone = GameObject.Instantiate(go);
|
||||
InspectorManager.Instance.Inspect(clone);
|
||||
}
|
||||
|
||||
var dontDestroyBtnObj = UIFactory.CreateButton(bottomRow, new Color(0.2f, 0.2f, 0.2f));
|
||||
var dontDestroyBtn = dontDestroyBtnObj.GetComponent<Button>();
|
||||
|
||||
dontDestroyBtn.onClick.AddListener(DontDestroyOnLoadBtn);
|
||||
|
||||
var dontDestroyText = dontDestroyBtnObj.GetComponentInChildren<Text>();
|
||||
dontDestroyText.text = "Set DontDestroyOnLoad";
|
||||
dontDestroyText.fontSize = 14;
|
||||
var dontDestroyLayout = dontDestroyBtnObj.AddComponent<LayoutElement>();
|
||||
dontDestroyLayout.flexibleWidth = 5000;
|
||||
|
||||
void DontDestroyOnLoadBtn()
|
||||
{
|
||||
var go = GameObjectInspector.ActiveInstance.TargetGO;
|
||||
if (!go)
|
||||
return;
|
||||
|
||||
GameObject.DontDestroyOnLoad(go);
|
||||
}
|
||||
|
||||
var destroyBtnObj = UIFactory.CreateButton(bottomRow, new Color(0.2f, 0.2f, 0.2f));
|
||||
var destroyBtn = destroyBtnObj.GetComponent<Button>();
|
||||
|
||||
destroyBtn.onClick.AddListener(DestroyBtn);
|
||||
|
||||
var destroyText = destroyBtnObj.GetComponentInChildren<Text>();
|
||||
destroyText.text = "Destroy";
|
||||
destroyText.fontSize = 14;
|
||||
destroyText.color = Color.red;
|
||||
var destroyLayout = destroyBtnObj.AddComponent<LayoutElement>();
|
||||
destroyLayout.minWidth = 150;
|
||||
|
||||
void DestroyBtn()
|
||||
{
|
||||
var go = GameObjectInspector.ActiveInstance.TargetGO;
|
||||
if (!go)
|
||||
return;
|
||||
|
||||
GameObject.Destroy(go);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,456 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.Unstrip;
|
||||
//using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Inspectors.GameObjects;
|
||||
|
||||
namespace UnityExplorer.Inspectors
|
||||
{
|
||||
public class GameObjectInspector : InspectorBase
|
||||
{
|
||||
public override string TabLabel => $" <color=cyan>[G]</color> {TargetGO?.name}";
|
||||
|
||||
public static GameObjectInspector ActiveInstance { get; private set; }
|
||||
|
||||
public GameObject TargetGO;
|
||||
|
||||
// sub modules
|
||||
private static ChildList s_childList;
|
||||
private static ComponentList s_compList;
|
||||
private static GameObjectControls s_controls;
|
||||
|
||||
// static UI elements (only constructed once)
|
||||
|
||||
private static bool m_UIConstructed;
|
||||
|
||||
private static GameObject s_content;
|
||||
public override GameObject Content
|
||||
{
|
||||
get => s_content;
|
||||
set => s_content = value;
|
||||
}
|
||||
|
||||
private static string m_lastName;
|
||||
public static InputField m_nameInput;
|
||||
|
||||
private static string m_lastPath;
|
||||
public static InputField m_pathInput;
|
||||
private static RectTransform m_pathInputRect;
|
||||
private static GameObject m_pathGroupObj;
|
||||
private static Text m_hiddenPathText;
|
||||
private static RectTransform m_hiddenPathRect;
|
||||
|
||||
private static Toggle m_enabledToggle;
|
||||
private static Text m_enabledText;
|
||||
private static bool? m_lastEnabledState;
|
||||
|
||||
private static Dropdown m_layerDropdown;
|
||||
private static int m_lastLayer = -1;
|
||||
|
||||
private static Text m_sceneText;
|
||||
private static string m_lastScene;
|
||||
|
||||
public GameObjectInspector(GameObject target) : base(target)
|
||||
{
|
||||
ActiveInstance = this;
|
||||
|
||||
TargetGO = target;
|
||||
|
||||
if (!TargetGO)
|
||||
{
|
||||
ExplorerCore.LogWarning("Target GameObject is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
// one UI is used for all gameobject inspectors. no point recreating it.
|
||||
if (!m_UIConstructed)
|
||||
{
|
||||
m_UIConstructed = true;
|
||||
|
||||
s_childList = new ChildList();
|
||||
s_compList = new ComponentList();
|
||||
s_controls = new GameObjectControls();
|
||||
|
||||
ConstructUI();
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetActive()
|
||||
{
|
||||
base.SetActive();
|
||||
ActiveInstance = this;
|
||||
}
|
||||
|
||||
public override void SetInactive()
|
||||
{
|
||||
base.SetInactive();
|
||||
ActiveInstance = null;
|
||||
}
|
||||
|
||||
internal void ChangeInspectorTarget(GameObject newTarget)
|
||||
{
|
||||
if (!newTarget)
|
||||
return;
|
||||
|
||||
this.Target = this.TargetGO = newTarget;
|
||||
}
|
||||
|
||||
// Update
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (m_pendingDestroy || !this.IsActive)
|
||||
return;
|
||||
|
||||
RefreshTopInfo();
|
||||
|
||||
s_childList.RefreshChildObjectList();
|
||||
|
||||
s_compList.RefreshComponentList();
|
||||
|
||||
s_controls.RefreshControls();
|
||||
|
||||
if (GameObjectControls.s_sliderChangedWanted)
|
||||
GameObjectControls.UpdateSliderControl();
|
||||
}
|
||||
|
||||
private void RefreshTopInfo()
|
||||
{
|
||||
if (m_lastName != TargetGO.name)
|
||||
{
|
||||
m_lastName = TargetGO.name;
|
||||
m_nameInput.text = m_lastName;
|
||||
}
|
||||
|
||||
if (TargetGO.transform.parent)
|
||||
{
|
||||
if (!m_pathGroupObj.activeSelf)
|
||||
m_pathGroupObj.SetActive(true);
|
||||
|
||||
var path = TargetGO.transform.GetTransformPath(true);
|
||||
if (m_lastPath != path)
|
||||
{
|
||||
m_lastPath = path;
|
||||
|
||||
m_pathInput.text = path;
|
||||
m_hiddenPathText.text = path;
|
||||
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(m_pathInputRect);
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(m_hiddenPathRect);
|
||||
}
|
||||
}
|
||||
else if (m_pathGroupObj.activeSelf)
|
||||
m_pathGroupObj.SetActive(false);
|
||||
|
||||
if (m_lastEnabledState != TargetGO.activeSelf)
|
||||
{
|
||||
m_lastEnabledState = TargetGO.activeSelf;
|
||||
|
||||
m_enabledToggle.isOn = TargetGO.activeSelf;
|
||||
m_enabledText.text = TargetGO.activeSelf ? "Enabled" : "Disabled";
|
||||
m_enabledText.color = TargetGO.activeSelf ? Color.green : Color.red;
|
||||
}
|
||||
|
||||
if (m_lastLayer != TargetGO.layer)
|
||||
{
|
||||
m_lastLayer = TargetGO.layer;
|
||||
m_layerDropdown.value = TargetGO.layer;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(m_lastScene) || m_lastScene != TargetGO.scene.name)
|
||||
{
|
||||
m_lastScene = TargetGO.scene.name;
|
||||
|
||||
if (!string.IsNullOrEmpty(TargetGO.scene.name))
|
||||
m_sceneText.text = m_lastScene;
|
||||
else
|
||||
m_sceneText.text = "None (Asset/Resource)";
|
||||
}
|
||||
}
|
||||
|
||||
// UI Callbacks
|
||||
|
||||
private static void OnApplyNameClicked()
|
||||
{
|
||||
if (ActiveInstance == null)
|
||||
return;
|
||||
|
||||
ActiveInstance.TargetGO.name = m_nameInput.text;
|
||||
}
|
||||
|
||||
private static void OnEnableToggled(bool enabled)
|
||||
{
|
||||
if (ActiveInstance == null)
|
||||
return;
|
||||
|
||||
ActiveInstance.TargetGO.SetActive(enabled);
|
||||
}
|
||||
|
||||
private static void OnLayerSelected(int layer)
|
||||
{
|
||||
if (ActiveInstance == null)
|
||||
return;
|
||||
|
||||
ActiveInstance.TargetGO.layer = layer;
|
||||
}
|
||||
|
||||
internal static void OnBackButtonClicked()
|
||||
{
|
||||
if (ActiveInstance == null)
|
||||
return;
|
||||
|
||||
ActiveInstance.ChangeInspectorTarget(ActiveInstance.TargetGO.transform.parent.gameObject);
|
||||
}
|
||||
|
||||
#region UI CONSTRUCTION
|
||||
|
||||
private void ConstructUI()
|
||||
{
|
||||
var parent = InspectorManager.Instance.m_inspectorContent;
|
||||
|
||||
s_content = UIFactory.CreateScrollView(parent, out GameObject scrollContent, out _, new Color(0.1f, 0.1f, 0.1f));
|
||||
|
||||
var parentLayout = scrollContent.transform.parent.gameObject.AddComponent<VerticalLayoutGroup>();
|
||||
parentLayout.childForceExpandWidth = true;
|
||||
parentLayout.childControlWidth = true;
|
||||
parentLayout.childForceExpandHeight = true;
|
||||
parentLayout.childControlHeight = true;
|
||||
|
||||
var scrollGroup = scrollContent.GetComponent<VerticalLayoutGroup>();
|
||||
scrollGroup.childForceExpandHeight = true;
|
||||
scrollGroup.childControlHeight = true;
|
||||
scrollGroup.childForceExpandWidth = true;
|
||||
scrollGroup.childControlWidth = true;
|
||||
scrollGroup.spacing = 5;
|
||||
var contentFitter = scrollContent.GetComponent<ContentSizeFitter>();
|
||||
contentFitter.verticalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
|
||||
ConstructTopArea(scrollContent);
|
||||
|
||||
s_controls.ConstructControls(scrollContent);
|
||||
|
||||
var midGroupObj = ConstructMidGroup(scrollContent);
|
||||
|
||||
s_childList.ConstructChildList(midGroupObj);
|
||||
s_compList.ConstructCompList(midGroupObj);
|
||||
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(s_content.GetComponent<RectTransform>());
|
||||
Canvas.ForceUpdateCanvases();
|
||||
}
|
||||
|
||||
private void ConstructTopArea(GameObject scrollContent)
|
||||
{
|
||||
// path row
|
||||
|
||||
m_pathGroupObj = UIFactory.CreateHorizontalGroup(scrollContent, new Color(0.1f, 0.1f, 0.1f));
|
||||
var pathGroup = m_pathGroupObj.GetComponent<HorizontalLayoutGroup>();
|
||||
pathGroup.childForceExpandHeight = false;
|
||||
pathGroup.childForceExpandWidth = false;
|
||||
pathGroup.childControlHeight = false;
|
||||
pathGroup.childControlWidth = true;
|
||||
pathGroup.spacing = 5;
|
||||
var pathRect = m_pathGroupObj.GetComponent<RectTransform>();
|
||||
pathRect.sizeDelta = new Vector2(pathRect.sizeDelta.x, 20);
|
||||
var pathLayout = m_pathGroupObj.AddComponent<LayoutElement>();
|
||||
pathLayout.minHeight = 20;
|
||||
pathLayout.flexibleHeight = 75;
|
||||
|
||||
var backButtonObj = UIFactory.CreateButton(m_pathGroupObj);
|
||||
var backButton = backButtonObj.GetComponent<Button>();
|
||||
|
||||
backButton.onClick.AddListener(OnBackButtonClicked);
|
||||
|
||||
var backColors = backButton.colors;
|
||||
backColors.normalColor = new Color(0.15f, 0.15f, 0.15f);
|
||||
backButton.colors = backColors;
|
||||
var backText = backButtonObj.GetComponentInChildren<Text>();
|
||||
backText.text = "◄";
|
||||
var backLayout = backButtonObj.AddComponent<LayoutElement>();
|
||||
backLayout.minWidth = 55;
|
||||
backLayout.flexibleWidth = 0;
|
||||
backLayout.minHeight = 25;
|
||||
backLayout.flexibleHeight = 0;
|
||||
|
||||
var pathHiddenTextObj = UIFactory.CreateLabel(m_pathGroupObj, TextAnchor.MiddleLeft);
|
||||
m_hiddenPathText = pathHiddenTextObj.GetComponent<Text>();
|
||||
m_hiddenPathText.color = Color.clear;
|
||||
m_hiddenPathText.fontSize = 14;
|
||||
//m_hiddenPathText.lineSpacing = 1.5f;
|
||||
m_hiddenPathText.raycastTarget = false;
|
||||
var hiddenFitter = pathHiddenTextObj.AddComponent<ContentSizeFitter>();
|
||||
hiddenFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
var hiddenLayout = pathHiddenTextObj.AddComponent<LayoutElement>();
|
||||
hiddenLayout.minHeight = 25;
|
||||
hiddenLayout.flexibleHeight = 125;
|
||||
hiddenLayout.minWidth = 250;
|
||||
hiddenLayout.flexibleWidth = 9000;
|
||||
var hiddenGroup = pathHiddenTextObj.AddComponent<HorizontalLayoutGroup>();
|
||||
hiddenGroup.childForceExpandWidth = true;
|
||||
hiddenGroup.childControlWidth = true;
|
||||
hiddenGroup.childForceExpandHeight = true;
|
||||
hiddenGroup.childControlHeight = true;
|
||||
|
||||
var pathInputObj = UIFactory.CreateInputField(pathHiddenTextObj);
|
||||
var pathInputRect = pathInputObj.GetComponent<RectTransform>();
|
||||
pathInputRect.sizeDelta = new Vector2(pathInputRect.sizeDelta.x, 25);
|
||||
m_pathInput = pathInputObj.GetComponent<InputField>();
|
||||
m_pathInput.text = TargetGO.transform.GetTransformPath();
|
||||
m_pathInput.readOnly = true;
|
||||
m_pathInput.lineType = InputField.LineType.MultiLineNewline;
|
||||
var pathInputLayout = pathInputObj.AddComponent<LayoutElement>();
|
||||
pathInputLayout.minHeight = 25;
|
||||
pathInputLayout.flexibleHeight = 75;
|
||||
pathInputLayout.preferredWidth = 400;
|
||||
pathInputLayout.flexibleWidth = 9999;
|
||||
var textRect = m_pathInput.textComponent.GetComponent<RectTransform>();
|
||||
textRect.offsetMin = new Vector2(3, 3);
|
||||
textRect.offsetMax = new Vector2(3, 3);
|
||||
m_pathInput.textComponent.color = new Color(0.75f, 0.75f, 0.75f);
|
||||
//m_pathInput.textComponent.lineSpacing = 1.5f;
|
||||
|
||||
m_pathInputRect = m_pathInput.GetComponent<RectTransform>();
|
||||
m_hiddenPathRect = m_hiddenPathText.GetComponent<RectTransform>();
|
||||
|
||||
// name row
|
||||
|
||||
var nameRowObj = UIFactory.CreateHorizontalGroup(scrollContent, new Color(0.1f, 0.1f, 0.1f));
|
||||
var nameGroup = nameRowObj.GetComponent<HorizontalLayoutGroup>();
|
||||
nameGroup.childForceExpandHeight = false;
|
||||
nameGroup.childForceExpandWidth = false;
|
||||
nameGroup.childControlHeight = true;
|
||||
nameGroup.childControlWidth = true;
|
||||
nameGroup.spacing = 5;
|
||||
var nameRect = nameRowObj.GetComponent<RectTransform>();
|
||||
nameRect.sizeDelta = new Vector2(nameRect.sizeDelta.x, 25);
|
||||
var nameLayout = nameRowObj.AddComponent<LayoutElement>();
|
||||
nameLayout.minHeight = 25;
|
||||
nameLayout.flexibleHeight = 0;
|
||||
|
||||
var nameTextObj = UIFactory.CreateLabel(nameRowObj, TextAnchor.MiddleCenter);
|
||||
var nameTextText = nameTextObj.GetComponent<Text>();
|
||||
nameTextText.text = "Name:";
|
||||
nameTextText.fontSize = 14;
|
||||
nameTextText.color = Color.grey;
|
||||
var nameTextLayout = nameTextObj.AddComponent<LayoutElement>();
|
||||
nameTextLayout.minHeight = 25;
|
||||
nameTextLayout.flexibleHeight = 0;
|
||||
nameTextLayout.minWidth = 55;
|
||||
nameTextLayout.flexibleWidth = 0;
|
||||
|
||||
var nameInputObj = UIFactory.CreateInputField(nameRowObj);
|
||||
var nameInputRect = nameInputObj.GetComponent<RectTransform>();
|
||||
nameInputRect.sizeDelta = new Vector2(nameInputRect.sizeDelta.x, 25);
|
||||
m_nameInput = nameInputObj.GetComponent<InputField>();
|
||||
m_nameInput.text = TargetGO.name;
|
||||
|
||||
var applyNameBtnObj = UIFactory.CreateButton(nameRowObj);
|
||||
var applyNameBtn = applyNameBtnObj.GetComponent<Button>();
|
||||
|
||||
applyNameBtn.onClick.AddListener(OnApplyNameClicked);
|
||||
|
||||
var applyNameText = applyNameBtnObj.GetComponentInChildren<Text>();
|
||||
applyNameText.text = "Apply";
|
||||
applyNameText.fontSize = 14;
|
||||
var applyNameLayout = applyNameBtnObj.AddComponent<LayoutElement>();
|
||||
applyNameLayout.minWidth = 65;
|
||||
applyNameLayout.minHeight = 25;
|
||||
applyNameLayout.flexibleHeight = 0;
|
||||
var applyNameRect = applyNameBtnObj.GetComponent<RectTransform>();
|
||||
applyNameRect.sizeDelta = new Vector2(applyNameRect.sizeDelta.x, 25);
|
||||
|
||||
var activeLabel = UIFactory.CreateLabel(nameRowObj, TextAnchor.MiddleCenter);
|
||||
var activeLabelLayout = activeLabel.AddComponent<LayoutElement>();
|
||||
activeLabelLayout.minWidth = 55;
|
||||
activeLabelLayout.minHeight = 25;
|
||||
var activeText = activeLabel.GetComponent<Text>();
|
||||
activeText.text = "Active:";
|
||||
activeText.color = Color.grey;
|
||||
activeText.fontSize = 14;
|
||||
|
||||
var enabledToggleObj = UIFactory.CreateToggle(nameRowObj, out m_enabledToggle, out m_enabledText);
|
||||
var toggleLayout = enabledToggleObj.AddComponent<LayoutElement>();
|
||||
toggleLayout.minHeight = 25;
|
||||
toggleLayout.minWidth = 100;
|
||||
toggleLayout.flexibleWidth = 0;
|
||||
m_enabledText.text = "Enabled";
|
||||
m_enabledText.color = Color.green;
|
||||
|
||||
m_enabledToggle.onValueChanged.AddListener(OnEnableToggled);
|
||||
|
||||
// layer and scene row
|
||||
|
||||
var sceneLayerRow = UIFactory.CreateHorizontalGroup(scrollContent, new Color(0.1f, 0.1f, 0.1f));
|
||||
var sceneLayerGroup = sceneLayerRow.GetComponent<HorizontalLayoutGroup>();
|
||||
sceneLayerGroup.childForceExpandWidth = false;
|
||||
sceneLayerGroup.childControlWidth = true;
|
||||
sceneLayerGroup.spacing = 5;
|
||||
|
||||
var layerLabel = UIFactory.CreateLabel(sceneLayerRow, TextAnchor.MiddleCenter);
|
||||
var layerText = layerLabel.GetComponent<Text>();
|
||||
layerText.text = "Layer:";
|
||||
layerText.fontSize = 14;
|
||||
layerText.color = Color.grey;
|
||||
var layerTextLayout = layerLabel.AddComponent<LayoutElement>();
|
||||
layerTextLayout.minWidth = 55;
|
||||
layerTextLayout.flexibleWidth = 0;
|
||||
|
||||
var layerDropdownObj = UIFactory.CreateDropdown(sceneLayerRow, out m_layerDropdown);
|
||||
m_layerDropdown.options.Clear();
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
var layer = LayerMaskUnstrip.LayerToName(i);
|
||||
m_layerDropdown.options.Add(new Dropdown.OptionData { text = $"{i}: {layer}" });
|
||||
}
|
||||
//var itemText = layerDropdownObj.transform.Find("Label").GetComponent<Text>();
|
||||
//itemText.resizeTextForBestFit = true;
|
||||
var layerDropdownLayout = layerDropdownObj.AddComponent<LayoutElement>();
|
||||
layerDropdownLayout.minWidth = 120;
|
||||
layerDropdownLayout.flexibleWidth = 2000;
|
||||
layerDropdownLayout.minHeight = 25;
|
||||
|
||||
m_layerDropdown.onValueChanged.AddListener(OnLayerSelected);
|
||||
|
||||
var scenelabelObj = UIFactory.CreateLabel(sceneLayerRow, TextAnchor.MiddleCenter);
|
||||
var sceneLabel = scenelabelObj.GetComponent<Text>();
|
||||
sceneLabel.text = "Scene:";
|
||||
sceneLabel.color = Color.grey;
|
||||
sceneLabel.fontSize = 14;
|
||||
var sceneLabelLayout = scenelabelObj.AddComponent<LayoutElement>();
|
||||
sceneLabelLayout.minWidth = 55;
|
||||
sceneLabelLayout.flexibleWidth = 0;
|
||||
|
||||
var objectSceneText = UIFactory.CreateLabel(sceneLayerRow, TextAnchor.MiddleLeft);
|
||||
m_sceneText = objectSceneText.GetComponent<Text>();
|
||||
m_sceneText.fontSize = 14;
|
||||
m_sceneText.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||||
var sceneTextLayout = objectSceneText.AddComponent<LayoutElement>();
|
||||
sceneTextLayout.minWidth = 120;
|
||||
sceneTextLayout.flexibleWidth = 2000;
|
||||
}
|
||||
|
||||
private GameObject ConstructMidGroup(GameObject parent)
|
||||
{
|
||||
var midGroupObj = UIFactory.CreateHorizontalGroup(parent, new Color(1, 1, 1, 0));
|
||||
var midGroup = midGroupObj.GetComponent<HorizontalLayoutGroup>();
|
||||
midGroup.spacing = 5;
|
||||
midGroup.childForceExpandWidth = true;
|
||||
midGroup.childControlWidth = true;
|
||||
midGroup.childForceExpandHeight = true;
|
||||
midGroup.childControlHeight = true;
|
||||
|
||||
var midLayout = midGroupObj.AddComponent<LayoutElement>();
|
||||
midLayout.minHeight = 300;
|
||||
midLayout.flexibleHeight = 5000;
|
||||
|
||||
return midGroupObj;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,268 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Modules;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Inspectors.Reflection;
|
||||
|
||||
namespace UnityExplorer.Inspectors
|
||||
{
|
||||
public class InspectorManager
|
||||
{
|
||||
public static InspectorManager Instance { get; private set; }
|
||||
|
||||
public InspectorManager()
|
||||
{
|
||||
Instance = this;
|
||||
ConstructInspectorPane();
|
||||
}
|
||||
|
||||
public InspectorBase m_activeInspector;
|
||||
public readonly List<InspectorBase> m_currentInspectors = new List<InspectorBase>();
|
||||
|
||||
public GameObject m_tabBarContent;
|
||||
public GameObject m_inspectorContent;
|
||||
|
||||
public void Update()
|
||||
{
|
||||
for (int i = 0; i < m_currentInspectors.Count; i++)
|
||||
{
|
||||
if (i >= m_currentInspectors.Count)
|
||||
break;
|
||||
|
||||
m_currentInspectors[i].Update();
|
||||
}
|
||||
}
|
||||
|
||||
public void Inspect(object obj)
|
||||
{
|
||||
#if CPP
|
||||
obj = obj.Il2CppCast(ReflectionHelpers.GetActualType(obj));
|
||||
#endif
|
||||
UnityEngine.Object unityObj = obj as UnityEngine.Object;
|
||||
|
||||
if (obj.IsNullOrDestroyed(false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// check if currently inspecting this object
|
||||
foreach (InspectorBase tab in m_currentInspectors)
|
||||
{
|
||||
if (ReferenceEquals(obj, tab.Target))
|
||||
{
|
||||
SetInspectorTab(tab);
|
||||
return;
|
||||
}
|
||||
#if CPP
|
||||
else if (unityObj && tab.Target is UnityEngine.Object uTabObj)
|
||||
{
|
||||
if (unityObj.m_CachedPtr == uTabObj.m_CachedPtr)
|
||||
{
|
||||
SetInspectorTab(tab);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
InspectorBase inspector;
|
||||
if (obj is GameObject go)
|
||||
inspector = new GameObjectInspector(go);
|
||||
else
|
||||
inspector = new InstanceInspector(obj);
|
||||
|
||||
m_currentInspectors.Add(inspector);
|
||||
SetInspectorTab(inspector);
|
||||
}
|
||||
|
||||
public void Inspect(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
ExplorerCore.LogWarning("The provided type was null!");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var tab in m_currentInspectors.Where(x => x is StaticInspector))
|
||||
{
|
||||
if (ReferenceEquals(tab.Target as Type, type))
|
||||
{
|
||||
SetInspectorTab(tab);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var inspector = new StaticInspector(type);
|
||||
|
||||
m_currentInspectors.Add(inspector);
|
||||
SetInspectorTab(inspector);
|
||||
}
|
||||
|
||||
public void SetInspectorTab(InspectorBase inspector)
|
||||
{
|
||||
MainMenu.Instance.SetPage(HomePage.Instance);
|
||||
|
||||
if (m_activeInspector == inspector)
|
||||
return;
|
||||
|
||||
UnsetInspectorTab();
|
||||
|
||||
m_activeInspector = inspector;
|
||||
inspector.SetActive();
|
||||
|
||||
Color activeColor = new Color(0, 0.25f, 0, 1);
|
||||
ColorBlock colors = inspector.tabButton.colors;
|
||||
colors.normalColor = activeColor;
|
||||
colors.highlightedColor = activeColor;
|
||||
inspector.tabButton.colors = colors;
|
||||
}
|
||||
|
||||
public void UnsetInspectorTab()
|
||||
{
|
||||
if (m_activeInspector == null)
|
||||
return;
|
||||
|
||||
m_activeInspector.SetInactive();
|
||||
|
||||
ColorBlock colors = m_activeInspector.tabButton.colors;
|
||||
colors.normalColor = new Color(0.2f, 0.2f, 0.2f, 1);
|
||||
colors.highlightedColor = new Color(0.1f, 0.3f, 0.1f, 1);
|
||||
m_activeInspector.tabButton.colors = colors;
|
||||
|
||||
m_activeInspector = null;
|
||||
}
|
||||
|
||||
#region INSPECTOR PANE
|
||||
|
||||
public void ConstructInspectorPane()
|
||||
{
|
||||
var mainObj = UIFactory.CreateVerticalGroup(HomePage.Instance.Content, new Color(72f / 255f, 72f / 255f, 72f / 255f));
|
||||
LayoutElement mainLayout = mainObj.AddComponent<LayoutElement>();
|
||||
mainLayout.preferredHeight = 400;
|
||||
mainLayout.flexibleHeight = 9000;
|
||||
mainLayout.preferredWidth = 620;
|
||||
mainLayout.flexibleWidth = 9000;
|
||||
|
||||
var mainGroup = mainObj.GetComponent<VerticalLayoutGroup>();
|
||||
mainGroup.childForceExpandHeight = true;
|
||||
mainGroup.childForceExpandWidth = true;
|
||||
mainGroup.childControlHeight = true;
|
||||
mainGroup.childControlWidth = true;
|
||||
mainGroup.spacing = 4;
|
||||
mainGroup.padding.left = 4;
|
||||
mainGroup.padding.right = 4;
|
||||
mainGroup.padding.top = 4;
|
||||
mainGroup.padding.bottom = 4;
|
||||
|
||||
var topRowObj = UIFactory.CreateHorizontalGroup(mainObj, new Color(1, 1, 1, 0));
|
||||
var topRowGroup = topRowObj.GetComponent<HorizontalLayoutGroup>();
|
||||
topRowGroup.childForceExpandWidth = false;
|
||||
topRowGroup.childControlWidth = true;
|
||||
topRowGroup.childForceExpandHeight = true;
|
||||
topRowGroup.childControlHeight = true;
|
||||
topRowGroup.spacing = 15;
|
||||
|
||||
var inspectorTitle = UIFactory.CreateLabel(topRowObj, TextAnchor.MiddleLeft);
|
||||
Text title = inspectorTitle.GetComponent<Text>();
|
||||
title.text = "Inspector";
|
||||
title.fontSize = 20;
|
||||
var titleLayout = inspectorTitle.AddComponent<LayoutElement>();
|
||||
titleLayout.minHeight = 30;
|
||||
titleLayout.flexibleHeight = 0;
|
||||
titleLayout.minWidth = 90;
|
||||
titleLayout.flexibleWidth = 20000;
|
||||
|
||||
ConstructToolbar(topRowObj);
|
||||
|
||||
// inspector tab bar
|
||||
|
||||
m_tabBarContent = UIFactory.CreateGridGroup(mainObj, new Vector2(185, 20), new Vector2(5, 2), new Color(0.1f, 0.1f, 0.1f, 1));
|
||||
|
||||
var gridGroup = m_tabBarContent.GetComponent<GridLayoutGroup>();
|
||||
gridGroup.padding.top = 3;
|
||||
gridGroup.padding.left = 3;
|
||||
gridGroup.padding.right = 3;
|
||||
gridGroup.padding.bottom = 3;
|
||||
|
||||
// inspector content area
|
||||
|
||||
m_inspectorContent = UIFactory.CreateVerticalGroup(mainObj, new Color(0.1f, 0.1f, 0.1f));
|
||||
var inspectorGroup = m_inspectorContent.GetComponent<VerticalLayoutGroup>();
|
||||
inspectorGroup.childForceExpandHeight = true;
|
||||
inspectorGroup.childForceExpandWidth = true;
|
||||
inspectorGroup.childControlHeight = true;
|
||||
inspectorGroup.childControlWidth = true;
|
||||
|
||||
m_inspectorContent = UIFactory.CreateVerticalGroup(mainObj, new Color(0.1f, 0.1f, 0.1f));
|
||||
var contentGroup = m_inspectorContent.GetComponent<VerticalLayoutGroup>();
|
||||
contentGroup.childForceExpandHeight = true;
|
||||
contentGroup.childForceExpandWidth = true;
|
||||
contentGroup.childControlHeight = true;
|
||||
contentGroup.childControlWidth = true;
|
||||
contentGroup.padding.top = 2;
|
||||
contentGroup.padding.left = 2;
|
||||
contentGroup.padding.right = 2;
|
||||
contentGroup.padding.bottom = 2;
|
||||
|
||||
var contentLayout = m_inspectorContent.AddComponent<LayoutElement>();
|
||||
contentLayout.preferredHeight = 900;
|
||||
contentLayout.flexibleHeight = 10000;
|
||||
contentLayout.preferredWidth = 600;
|
||||
contentLayout.flexibleWidth = 10000;
|
||||
}
|
||||
|
||||
private static void ConstructToolbar(GameObject topRowObj)
|
||||
{
|
||||
var invisObj = UIFactory.CreateHorizontalGroup(topRowObj, new Color(1, 1, 1, 0));
|
||||
var invisGroup = invisObj.GetComponent<HorizontalLayoutGroup>();
|
||||
invisGroup.childForceExpandWidth = false;
|
||||
invisGroup.childForceExpandHeight = false;
|
||||
invisGroup.childControlWidth = true;
|
||||
invisGroup.childControlHeight = true;
|
||||
invisGroup.padding.top = 2;
|
||||
invisGroup.padding.bottom = 2;
|
||||
invisGroup.padding.left = 2;
|
||||
invisGroup.padding.right = 2;
|
||||
invisGroup.spacing = 10;
|
||||
|
||||
// inspect under mouse button
|
||||
AddMouseInspectButton(topRowObj, MouseInspector.MouseInspectMode.UI);
|
||||
AddMouseInspectButton(topRowObj, MouseInspector.MouseInspectMode.World);
|
||||
}
|
||||
|
||||
private static void AddMouseInspectButton(GameObject topRowObj, MouseInspector.MouseInspectMode mode)
|
||||
{
|
||||
var inspectObj = UIFactory.CreateButton(topRowObj);
|
||||
var inspectLayout = inspectObj.AddComponent<LayoutElement>();
|
||||
inspectLayout.minWidth = 120;
|
||||
inspectLayout.flexibleWidth = 0;
|
||||
|
||||
var inspectText = inspectObj.GetComponentInChildren<Text>();
|
||||
inspectText.text = "Mouse Inspect";
|
||||
inspectText.fontSize = 13;
|
||||
|
||||
if (mode == MouseInspector.MouseInspectMode.UI)
|
||||
inspectText.text += " (UI)";
|
||||
|
||||
var inspectBtn = inspectObj.GetComponent<Button>();
|
||||
var inspectColors = inspectBtn.colors;
|
||||
inspectColors.normalColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
inspectBtn.colors = inspectColors;
|
||||
|
||||
inspectBtn.onClick.AddListener(OnInspectMouseClicked);
|
||||
|
||||
void OnInspectMouseClicked()
|
||||
{
|
||||
MouseInspector.Mode = mode;
|
||||
MouseInspector.StartInspect();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,239 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Input;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.Unstrip;
|
||||
|
||||
namespace UnityExplorer.Inspectors
|
||||
{
|
||||
public class MouseInspector
|
||||
{
|
||||
public enum MouseInspectMode
|
||||
{
|
||||
World,
|
||||
UI
|
||||
}
|
||||
|
||||
public static bool Enabled { get; set; }
|
||||
|
||||
public static MouseInspectMode Mode { get; set; }
|
||||
|
||||
internal static Text s_objNameLabel;
|
||||
internal static Text s_objPathLabel;
|
||||
internal static Text s_mousePosLabel;
|
||||
|
||||
private static GameObject s_lastHit;
|
||||
private static Vector3 s_lastMousePos;
|
||||
|
||||
internal static GameObject s_UIContent;
|
||||
|
||||
public static void StartInspect()
|
||||
{
|
||||
Enabled = true;
|
||||
MainMenu.Instance.MainPanel.SetActive(false);
|
||||
s_UIContent.SetActive(true);
|
||||
|
||||
// recache Graphic Raycasters each time we start
|
||||
var casters = ResourcesUnstrip.FindObjectsOfTypeAll(typeof(GraphicRaycaster));
|
||||
m_gCasters = new GraphicRaycaster[casters.Length];
|
||||
for (int i = 0; i < casters.Length; i++)
|
||||
{
|
||||
#if CPP
|
||||
m_gCasters[i] = casters[i].TryCast<GraphicRaycaster>();
|
||||
#else
|
||||
m_gCasters[i] = casters[i] as GraphicRaycaster;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public static void StopInspect()
|
||||
{
|
||||
Enabled = false;
|
||||
MainMenu.Instance.MainPanel.SetActive(true);
|
||||
s_UIContent.SetActive(false);
|
||||
|
||||
ClearHitData();
|
||||
}
|
||||
|
||||
internal static GraphicRaycaster[] m_gCasters;
|
||||
|
||||
public static void UpdateInspect()
|
||||
{
|
||||
if (InputManager.GetKeyDown(KeyCode.Escape))
|
||||
{
|
||||
StopInspect();
|
||||
return;
|
||||
}
|
||||
|
||||
var mousePos = InputManager.MousePosition;
|
||||
|
||||
if (mousePos != s_lastMousePos)
|
||||
UpdatePosition(mousePos);
|
||||
|
||||
if (!UnityHelpers.MainCamera)
|
||||
return;
|
||||
|
||||
// actual inspect raycast
|
||||
|
||||
switch (Mode)
|
||||
{
|
||||
case MouseInspectMode.UI:
|
||||
RaycastUI(mousePos); break;
|
||||
case MouseInspectMode.World:
|
||||
RaycastWorld(mousePos); break;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void OnHitGameObject(GameObject obj)
|
||||
{
|
||||
if (obj != s_lastHit)
|
||||
{
|
||||
s_lastHit = obj;
|
||||
s_objNameLabel.text = $"<b>Click to Inspect:</b> <color=cyan>{obj.name}</color>";
|
||||
s_objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}";
|
||||
}
|
||||
|
||||
if (InputManager.GetMouseButtonDown(0))
|
||||
{
|
||||
StopInspect();
|
||||
InspectorManager.Instance.Inspect(obj);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void RaycastWorld(Vector2 mousePos)
|
||||
{
|
||||
var ray = UnityHelpers.MainCamera.ScreenPointToRay(mousePos);
|
||||
var casts = Physics.RaycastAll(ray, 1000f);
|
||||
|
||||
if (casts.Length > 0)
|
||||
{
|
||||
foreach (var cast in casts)
|
||||
{
|
||||
if (cast.transform)
|
||||
{
|
||||
var obj = cast.transform.gameObject;
|
||||
|
||||
OnHitGameObject(obj);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_lastHit)
|
||||
ClearHitData();
|
||||
}
|
||||
}
|
||||
|
||||
internal static void RaycastUI(Vector2 mousePos)
|
||||
{
|
||||
var ped = new PointerEventData(null)
|
||||
{
|
||||
position = mousePos
|
||||
};
|
||||
|
||||
#if MONO
|
||||
var list = new List<RaycastResult>();
|
||||
#else
|
||||
var list = new Il2CppSystem.Collections.Generic.List<RaycastResult>();
|
||||
#endif
|
||||
foreach (var gr in m_gCasters)
|
||||
{
|
||||
gr.Raycast(ped, list);
|
||||
|
||||
if (list.Count > 0)
|
||||
{
|
||||
foreach (var hit in list)
|
||||
{
|
||||
if (hit.gameObject)
|
||||
{
|
||||
var obj = hit.gameObject;
|
||||
|
||||
OnHitGameObject(obj);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_lastHit)
|
||||
ClearHitData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void UpdatePosition(Vector2 mousePos)
|
||||
{
|
||||
s_lastMousePos = mousePos;
|
||||
|
||||
var inversePos = UIManager.CanvasRoot.transform.InverseTransformPoint(mousePos);
|
||||
|
||||
s_mousePosLabel.text = $"<color=grey>Mouse Position:</color> {mousePos.ToString()}";
|
||||
|
||||
float yFix = mousePos.y < 120 ? 80 : -80;
|
||||
s_UIContent.transform.localPosition = new Vector3(inversePos.x, inversePos.y + yFix, 0);
|
||||
}
|
||||
|
||||
internal static void ClearHitData()
|
||||
{
|
||||
s_lastHit = null;
|
||||
s_objNameLabel.text = "No hits...";
|
||||
s_objPathLabel.text = "";
|
||||
}
|
||||
|
||||
#region UI Construction
|
||||
|
||||
internal static void ConstructUI()
|
||||
{
|
||||
s_UIContent = UIFactory.CreatePanel(UIManager.CanvasRoot, "MouseInspect", out GameObject content);
|
||||
|
||||
s_UIContent.AddComponent<Mask>();
|
||||
|
||||
var baseRect = s_UIContent.GetComponent<RectTransform>();
|
||||
var half = new Vector2(0.5f, 0.5f);
|
||||
baseRect.anchorMin = half;
|
||||
baseRect.anchorMax = half;
|
||||
baseRect.pivot = half;
|
||||
baseRect.sizeDelta = new Vector2(700, 150);
|
||||
|
||||
var group = content.GetComponent<VerticalLayoutGroup>();
|
||||
group.childForceExpandHeight = true;
|
||||
|
||||
// Title text
|
||||
|
||||
var titleObj = UIFactory.CreateLabel(content, TextAnchor.MiddleCenter);
|
||||
var titleText = titleObj.GetComponent<Text>();
|
||||
titleText.text = "<b>Mouse Inspector</b> (press <b>ESC</b> to cancel)";
|
||||
|
||||
var mousePosObj = UIFactory.CreateLabel(content, TextAnchor.MiddleCenter);
|
||||
s_mousePosLabel = mousePosObj.GetComponent<Text>();
|
||||
s_mousePosLabel.text = "Mouse Position:";
|
||||
|
||||
var hitLabelObj = UIFactory.CreateLabel(content, TextAnchor.MiddleLeft);
|
||||
s_objNameLabel = hitLabelObj.GetComponent<Text>();
|
||||
s_objNameLabel.text = "No hits...";
|
||||
s_objNameLabel.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||||
|
||||
var pathLabelObj = UIFactory.CreateLabel(content, TextAnchor.MiddleLeft);
|
||||
s_objPathLabel = pathLabelObj.GetComponent<Text>();
|
||||
s_objPathLabel.fontStyle = FontStyle.Italic;
|
||||
s_objPathLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
|
||||
var pathLayout = pathLabelObj.AddComponent<LayoutElement>();
|
||||
pathLayout.minHeight = 75;
|
||||
pathLayout.flexibleHeight = 0;
|
||||
|
||||
s_UIContent.SetActive(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,284 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.UI;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
public enum MemberScopes
|
||||
{
|
||||
All,
|
||||
Instance,
|
||||
Static
|
||||
}
|
||||
|
||||
public class InstanceInspector : ReflectionInspector
|
||||
{
|
||||
public override string TabLabel => $" <color=cyan>[R]</color> {base.TabLabel}";
|
||||
|
||||
internal MemberScopes m_scopeFilter;
|
||||
internal Button m_lastActiveScopeButton;
|
||||
|
||||
public InstanceInspector(object target) : base(target) { }
|
||||
|
||||
private void OnScopeFilterClicked(MemberScopes type, Button button)
|
||||
{
|
||||
if (m_lastActiveScopeButton)
|
||||
{
|
||||
var lastColors = m_lastActiveScopeButton.colors;
|
||||
lastColors.normalColor = new Color(0.2f, 0.2f, 0.2f);
|
||||
m_lastActiveScopeButton.colors = lastColors;
|
||||
}
|
||||
|
||||
m_scopeFilter = type;
|
||||
m_lastActiveScopeButton = button;
|
||||
|
||||
var colors = m_lastActiveScopeButton.colors;
|
||||
colors.normalColor = new Color(0.2f, 0.6f, 0.2f);
|
||||
m_lastActiveScopeButton.colors = colors;
|
||||
|
||||
FilterMembers(null, true);
|
||||
m_sliderScroller.m_slider.value = 1f;
|
||||
}
|
||||
|
||||
public void ConstructInstanceHelpers()
|
||||
{
|
||||
if (!typeof(Component).IsAssignableFrom(m_targetType) && !typeof(UnityEngine.Object).IsAssignableFrom(m_targetType))
|
||||
return;
|
||||
|
||||
var rowObj = UIFactory.CreateHorizontalGroup(Content, new Color(0.1f, 0.1f, 0.1f));
|
||||
var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
|
||||
rowGroup.childForceExpandWidth = true;
|
||||
rowGroup.childControlWidth = true;
|
||||
rowGroup.spacing = 5;
|
||||
rowGroup.padding.top = 2;
|
||||
rowGroup.padding.bottom = 2;
|
||||
rowGroup.padding.right = 2;
|
||||
rowGroup.padding.left = 2;
|
||||
var rowLayout = rowObj.AddComponent<LayoutElement>();
|
||||
rowLayout.minHeight = 25;
|
||||
rowLayout.flexibleWidth = 5000;
|
||||
|
||||
if (typeof(Component).IsAssignableFrom(m_targetType))
|
||||
{
|
||||
ConstructCompHelper(rowObj);
|
||||
}
|
||||
|
||||
ConstructUObjHelper(rowObj);
|
||||
|
||||
// WIP
|
||||
|
||||
//if (m_targetType == typeof(Texture2D))
|
||||
// ConstructTextureHelper();
|
||||
}
|
||||
|
||||
internal void ConstructCompHelper(GameObject rowObj)
|
||||
{
|
||||
var labelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft);
|
||||
var labelLayout = labelObj.AddComponent<LayoutElement>();
|
||||
labelLayout.minWidth = 90;
|
||||
labelLayout.minHeight = 25;
|
||||
labelLayout.flexibleWidth = 0;
|
||||
var labelText = labelObj.GetComponent<Text>();
|
||||
labelText.text = "GameObject:";
|
||||
|
||||
#if MONO
|
||||
var comp = Target as Component;
|
||||
#else
|
||||
var comp = (Target as Il2CppSystem.Object).TryCast<Component>();
|
||||
#endif
|
||||
|
||||
var goBtnObj = UIFactory.CreateButton(rowObj, new Color(0.2f, 0.5f, 0.2f));
|
||||
var goBtnLayout = goBtnObj.AddComponent<LayoutElement>();
|
||||
goBtnLayout.minHeight = 25;
|
||||
goBtnLayout.minWidth = 200;
|
||||
goBtnLayout.flexibleWidth = 0;
|
||||
var text = goBtnObj.GetComponentInChildren<Text>();
|
||||
text.text = comp.name;
|
||||
var btn = goBtnObj.GetComponent<Button>();
|
||||
btn.onClick.AddListener(() => { InspectorManager.Instance.Inspect(comp.gameObject); });
|
||||
}
|
||||
|
||||
internal void ConstructUObjHelper(GameObject rowObj)
|
||||
{
|
||||
var labelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft);
|
||||
var labelLayout = labelObj.AddComponent<LayoutElement>();
|
||||
labelLayout.minWidth = 60;
|
||||
labelLayout.minHeight = 25;
|
||||
labelLayout.flexibleWidth = 0;
|
||||
var labelText = labelObj.GetComponent<Text>();
|
||||
labelText.text = "Name:";
|
||||
|
||||
#if MONO
|
||||
var uObj = Target as UnityEngine.Object;
|
||||
#else
|
||||
var uObj = (Target as Il2CppSystem.Object).TryCast<UnityEngine.Object>();
|
||||
#endif
|
||||
|
||||
var inputObj = UIFactory.CreateInputField(rowObj, 14, 3, 1);
|
||||
var inputLayout = inputObj.AddComponent<LayoutElement>();
|
||||
inputLayout.minHeight = 25;
|
||||
inputLayout.flexibleWidth = 2000;
|
||||
var inputField = inputObj.GetComponent<InputField>();
|
||||
inputField.readOnly = true;
|
||||
inputField.text = uObj.name;
|
||||
|
||||
//var goBtnObj = UIFactory.CreateButton(rowObj, new Color(0.2f, 0.5f, 0.2f));
|
||||
//var goBtnLayout = goBtnObj.AddComponent<LayoutElement>();
|
||||
//goBtnLayout.minHeight = 25;
|
||||
//goBtnLayout.minWidth = 200;
|
||||
//goBtnLayout.flexibleWidth = 0;
|
||||
//var text = goBtnObj.GetComponentInChildren<Text>();
|
||||
//text.text = comp.name;
|
||||
//var btn = goBtnObj.GetComponent<Button>();
|
||||
//btn.onClick.AddListener(() => { InspectorManager.Instance.Inspect(comp.gameObject); });
|
||||
}
|
||||
|
||||
//internal bool showingTextureHelper;
|
||||
//internal bool constructedTextureViewer;
|
||||
|
||||
//internal void ConstructTextureHelper()
|
||||
//{
|
||||
// var rowObj = UIFactory.CreateHorizontalGroup(Content, new Color(0.1f, 0.1f, 0.1f));
|
||||
// var rowLayout = rowObj.AddComponent<LayoutElement>();
|
||||
// rowLayout.minHeight = 25;
|
||||
// rowLayout.flexibleHeight = 0;
|
||||
// var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
|
||||
// rowGroup.childForceExpandHeight = true;
|
||||
// rowGroup.childForceExpandWidth = false;
|
||||
// rowGroup.padding.top = 3;
|
||||
// rowGroup.padding.left = 3;
|
||||
// rowGroup.padding.bottom = 3;
|
||||
// rowGroup.padding.right = 3;
|
||||
// rowGroup.spacing = 5;
|
||||
|
||||
// var showBtnObj = UIFactory.CreateButton(rowObj, new Color(0.2f, 0.2f, 0.2f));
|
||||
// var showBtnLayout = showBtnObj.AddComponent<LayoutElement>();
|
||||
// showBtnLayout.minWidth = 50;
|
||||
// showBtnLayout.flexibleWidth = 0;
|
||||
// var showText = showBtnObj.GetComponentInChildren<Text>();
|
||||
// showText.text = "Show";
|
||||
// var showBtn = showBtnObj.GetComponent<Button>();
|
||||
|
||||
// var labelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft);
|
||||
// var labelText = labelObj.GetComponent<Text>();
|
||||
// labelText.text = "Texture Viewer";
|
||||
|
||||
// var textureViewerObj = UIFactory.CreateScrollView(Content, out GameObject scrollContent, out _, new Color(0.1f, 0.1f, 0.1f));
|
||||
// var viewerGroup = scrollContent.GetComponent<VerticalLayoutGroup>();
|
||||
// viewerGroup.childForceExpandHeight = false;
|
||||
// viewerGroup.childForceExpandWidth = false;
|
||||
// viewerGroup.childControlHeight = true;
|
||||
// viewerGroup.childControlWidth = true;
|
||||
// var mainLayout = textureViewerObj.GetComponent<LayoutElement>();
|
||||
// mainLayout.flexibleHeight = -1;
|
||||
// mainLayout.flexibleWidth = 2000;
|
||||
// mainLayout.minHeight = 25;
|
||||
|
||||
// textureViewerObj.SetActive(false);
|
||||
|
||||
// showBtn.onClick.AddListener(() =>
|
||||
// {
|
||||
// showingTextureHelper = !showingTextureHelper;
|
||||
|
||||
// if (showingTextureHelper)
|
||||
// {
|
||||
// if (!constructedTextureViewer)
|
||||
// ConstructTextureViewerArea(scrollContent);
|
||||
|
||||
// showText.text = "Hide";
|
||||
// textureViewerObj.SetActive(true);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// showText.text = "Show";
|
||||
// textureViewerObj.SetActive(false);
|
||||
// }
|
||||
// });
|
||||
//}
|
||||
|
||||
//internal void ConstructTextureViewerArea(GameObject parent)
|
||||
//{
|
||||
// constructedTextureViewer = true;
|
||||
|
||||
// var tex = Target as Texture2D;
|
||||
|
||||
// if (!tex)
|
||||
// {
|
||||
// ExplorerCore.LogWarning("Could not cast the target instance to Texture2D!");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// var imageObj = UIFactory.CreateUIObject("TextureViewerImage", parent, new Vector2(1, 1));
|
||||
// var image = imageObj.AddComponent<Image>();
|
||||
// var sprite = UIManager.CreateSprite(tex);
|
||||
// image.sprite = sprite;
|
||||
|
||||
// var fitter = imageObj.AddComponent<ContentSizeFitter>();
|
||||
// fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
// //fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
// var imageLayout = imageObj.AddComponent<LayoutElement>();
|
||||
// imageLayout.preferredHeight = sprite.rect.height;
|
||||
// imageLayout.preferredWidth = sprite.rect.width;
|
||||
//}
|
||||
|
||||
public void ConstructInstanceFilters(GameObject parent)
|
||||
{
|
||||
var memberFilterRowObj = UIFactory.CreateHorizontalGroup(parent, new Color(1, 1, 1, 0));
|
||||
var memFilterGroup = memberFilterRowObj.GetComponent<HorizontalLayoutGroup>();
|
||||
memFilterGroup.childForceExpandHeight = false;
|
||||
memFilterGroup.childForceExpandWidth = false;
|
||||
memFilterGroup.childControlWidth = true;
|
||||
memFilterGroup.childControlHeight = true;
|
||||
memFilterGroup.spacing = 5;
|
||||
var memFilterLayout = memberFilterRowObj.AddComponent<LayoutElement>();
|
||||
memFilterLayout.minHeight = 25;
|
||||
memFilterLayout.flexibleHeight = 0;
|
||||
memFilterLayout.flexibleWidth = 5000;
|
||||
|
||||
var memLabelObj = UIFactory.CreateLabel(memberFilterRowObj, TextAnchor.MiddleLeft);
|
||||
var memLabelLayout = memLabelObj.AddComponent<LayoutElement>();
|
||||
memLabelLayout.minWidth = 100;
|
||||
memLabelLayout.minHeight = 25;
|
||||
memLabelLayout.flexibleWidth = 0;
|
||||
var memLabelText = memLabelObj.GetComponent<Text>();
|
||||
memLabelText.text = "Filter scope:";
|
||||
memLabelText.color = Color.grey;
|
||||
|
||||
AddFilterButton(memberFilterRowObj, MemberScopes.All, true);
|
||||
AddFilterButton(memberFilterRowObj, MemberScopes.Instance);
|
||||
AddFilterButton(memberFilterRowObj, MemberScopes.Static);
|
||||
}
|
||||
|
||||
private void AddFilterButton(GameObject parent, MemberScopes type, bool setEnabled = false)
|
||||
{
|
||||
var btnObj = UIFactory.CreateButton(parent, new Color(0.2f, 0.2f, 0.2f));
|
||||
|
||||
var btnLayout = btnObj.AddComponent<LayoutElement>();
|
||||
btnLayout.minHeight = 25;
|
||||
btnLayout.minWidth = 70;
|
||||
|
||||
var text = btnObj.GetComponentInChildren<Text>();
|
||||
text.text = type.ToString();
|
||||
|
||||
var btn = btnObj.GetComponent<Button>();
|
||||
|
||||
btn.onClick.AddListener(() => { OnScopeFilterClicked(type, btn); });
|
||||
|
||||
var colors = btn.colors;
|
||||
colors.highlightedColor = new Color(0.3f, 0.7f, 0.3f);
|
||||
|
||||
if (setEnabled)
|
||||
{
|
||||
colors.normalColor = new Color(0.2f, 0.6f, 0.2f);
|
||||
m_scopeFilter = type;
|
||||
m_lastActiveScopeButton = btn;
|
||||
}
|
||||
|
||||
btn.colors = colors;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,559 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Modules;
|
||||
using UnityExplorer.UI.Shared;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Unstrip;
|
||||
using UnityExplorer.Helpers;
|
||||
|
||||
namespace UnityExplorer.Inspectors
|
||||
{
|
||||
public class SceneExplorer
|
||||
{
|
||||
public static SceneExplorer Instance;
|
||||
|
||||
internal static Action OnToggleShow;
|
||||
|
||||
public SceneExplorer()
|
||||
{
|
||||
Instance = this;
|
||||
ConstructScenePane();
|
||||
}
|
||||
|
||||
private static bool Hiding;
|
||||
|
||||
private const float UPDATE_INTERVAL = 1f;
|
||||
private float m_timeOfLastSceneUpdate;
|
||||
|
||||
// private int m_currentSceneHandle = -1;
|
||||
public static Scene DontDestroyScene => DontDestroyObject.scene;
|
||||
internal Scene m_currentScene;
|
||||
internal Scene[] m_currentScenes = new Scene[0];
|
||||
|
||||
private GameObject m_selectedSceneObject;
|
||||
private int m_lastCount;
|
||||
|
||||
private Dropdown m_sceneDropdown;
|
||||
private Text m_sceneDropdownText;
|
||||
private Text m_scenePathText;
|
||||
private GameObject m_mainInspectBtn;
|
||||
private GameObject m_backButtonObj;
|
||||
|
||||
public PageHandler m_pageHandler;
|
||||
private GameObject m_pageContent;
|
||||
private GameObject[] m_allObjects = new GameObject[0];
|
||||
private readonly List<GameObject> m_shortList = new List<GameObject>();
|
||||
private readonly List<Text> m_shortListTexts = new List<Text>();
|
||||
private readonly List<Toggle> m_shortListToggles = new List<Toggle>();
|
||||
|
||||
|
||||
internal static GameObject DontDestroyObject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_dontDestroyObject)
|
||||
{
|
||||
m_dontDestroyObject = new GameObject("DontDestroyMe");
|
||||
GameObject.DontDestroyOnLoad(m_dontDestroyObject);
|
||||
}
|
||||
return m_dontDestroyObject;
|
||||
}
|
||||
}
|
||||
|
||||
internal static GameObject m_dontDestroyObject;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
RefreshSceneSelector();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (Hiding || Time.realtimeSinceStartup - m_timeOfLastSceneUpdate < UPDATE_INTERVAL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshSceneSelector();
|
||||
|
||||
if (!m_selectedSceneObject)
|
||||
{
|
||||
if (m_currentScene != default)
|
||||
{
|
||||
#if CPP
|
||||
SetSceneObjectList(SceneUnstrip.GetRootGameObjects(m_currentScene.handle));
|
||||
#else
|
||||
SetSceneObjectList(m_currentScene.GetRootGameObjects());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RefreshSelectedSceneObject();
|
||||
}
|
||||
}
|
||||
|
||||
//#if CPP
|
||||
// public int GetSceneHandle(string sceneName)
|
||||
// {
|
||||
// if (sceneName == "DontDestroyOnLoad")
|
||||
// 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()
|
||||
{
|
||||
var names = new List<string>();
|
||||
var scenes = new List<Scene>();
|
||||
|
||||
for (int i = 0; i < SceneManager.sceneCount; i++)
|
||||
{
|
||||
Scene scene = SceneManager.GetSceneAt(i);
|
||||
|
||||
if (scene == default)
|
||||
continue;
|
||||
|
||||
scenes.Add(scene);
|
||||
names.Add(scene.name);
|
||||
}
|
||||
|
||||
names.Add("DontDestroyOnLoad");
|
||||
scenes.Add(DontDestroyScene);
|
||||
|
||||
m_sceneDropdown.options.Clear();
|
||||
|
||||
foreach (string scene in names)
|
||||
{
|
||||
m_sceneDropdown.options.Add(new Dropdown.OptionData { text = scene });
|
||||
}
|
||||
|
||||
if (!names.Contains(m_sceneDropdownText.text))
|
||||
{
|
||||
m_sceneDropdownText.text = names[0];
|
||||
SetTargetScene(scenes[0]);
|
||||
}
|
||||
|
||||
m_currentScenes = scenes.ToArray();
|
||||
}
|
||||
|
||||
//#if CPP
|
||||
// public void SetTargetScene(string name) => SetTargetScene(scene.handle);
|
||||
//#endif
|
||||
|
||||
public void SetTargetScene(Scene scene)
|
||||
{
|
||||
if (scene == default)
|
||||
return;
|
||||
|
||||
m_currentScene = scene;
|
||||
#if CPP
|
||||
GameObject[] rootObjs = SceneUnstrip.GetRootGameObjects(scene.handle);
|
||||
#else
|
||||
GameObject[] rootObjs = SceneUnstrip.GetRootGameObjects(scene);
|
||||
#endif
|
||||
SetSceneObjectList(rootObjs);
|
||||
|
||||
m_selectedSceneObject = null;
|
||||
|
||||
if (m_backButtonObj.activeSelf)
|
||||
{
|
||||
m_backButtonObj.SetActive(false);
|
||||
m_mainInspectBtn.SetActive(false);
|
||||
}
|
||||
|
||||
m_scenePathText.text = "Scene root:";
|
||||
//m_scenePathText.ForceMeshUpdate();
|
||||
}
|
||||
|
||||
public void SetTargetObject(GameObject obj)
|
||||
{
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
m_scenePathText.text = obj.name;
|
||||
//m_scenePathText.ForceMeshUpdate();
|
||||
|
||||
m_selectedSceneObject = obj;
|
||||
|
||||
RefreshSelectedSceneObject();
|
||||
|
||||
if (!m_backButtonObj.activeSelf)
|
||||
{
|
||||
m_backButtonObj.SetActive(true);
|
||||
m_mainInspectBtn.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshSelectedSceneObject()
|
||||
{
|
||||
GameObject[] list = new GameObject[m_selectedSceneObject.transform.childCount];
|
||||
for (int i = 0; i < m_selectedSceneObject.transform.childCount; i++)
|
||||
{
|
||||
list[i] = m_selectedSceneObject.transform.GetChild(i).gameObject;
|
||||
}
|
||||
|
||||
SetSceneObjectList(list);
|
||||
}
|
||||
|
||||
private void SetSceneObjectList(GameObject[] objects)
|
||||
{
|
||||
m_allObjects = objects;
|
||||
RefreshSceneObjectList();
|
||||
}
|
||||
|
||||
private void SceneListObjectClicked(int index)
|
||||
{
|
||||
if (index >= m_shortList.Count || !m_shortList[index])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var obj = m_shortList[index];
|
||||
if (obj.transform.childCount > 0)
|
||||
SetTargetObject(obj);
|
||||
else
|
||||
InspectorManager.Instance.Inspect(obj);
|
||||
}
|
||||
|
||||
private void OnSceneListPageTurn()
|
||||
{
|
||||
RefreshSceneObjectList();
|
||||
}
|
||||
|
||||
private void OnToggleClicked(int index, bool val)
|
||||
{
|
||||
if (index >= m_shortList.Count || !m_shortList[index])
|
||||
return;
|
||||
|
||||
var obj = m_shortList[index];
|
||||
obj.SetActive(val);
|
||||
}
|
||||
|
||||
private void RefreshSceneObjectList()
|
||||
{
|
||||
m_timeOfLastSceneUpdate = Time.realtimeSinceStartup;
|
||||
|
||||
var objects = m_allObjects;
|
||||
m_pageHandler.ListCount = objects.Length;
|
||||
|
||||
//int startIndex = m_sceneListPageHandler.StartIndex;
|
||||
|
||||
int newCount = 0;
|
||||
|
||||
foreach (var itemIndex in m_pageHandler)
|
||||
{
|
||||
newCount++;
|
||||
|
||||
// normalized index starting from 0
|
||||
var i = itemIndex - m_pageHandler.StartIndex;
|
||||
|
||||
if (itemIndex >= objects.Length)
|
||||
{
|
||||
if (i > m_lastCount || i >= m_shortListTexts.Count)
|
||||
break;
|
||||
|
||||
GameObject label = m_shortListTexts[i].transform.parent.parent.gameObject;
|
||||
if (label.activeSelf)
|
||||
label.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameObject obj = objects[itemIndex];
|
||||
|
||||
if (!obj)
|
||||
continue;
|
||||
|
||||
if (i >= m_shortList.Count)
|
||||
{
|
||||
m_shortList.Add(obj);
|
||||
AddObjectListButton();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_shortList[i] = obj;
|
||||
}
|
||||
|
||||
var text = m_shortListTexts[i];
|
||||
|
||||
var name = obj.name;
|
||||
|
||||
if (obj.transform.childCount > 0)
|
||||
name = $"<color=grey>[{obj.transform.childCount}]</color> {name}";
|
||||
|
||||
text.text = name;
|
||||
text.color = obj.activeSelf ? Color.green : Color.red;
|
||||
|
||||
var tog = m_shortListToggles[i];
|
||||
tog.isOn = obj.activeSelf;
|
||||
|
||||
var label = text.transform.parent.parent.gameObject;
|
||||
if (!label.activeSelf)
|
||||
{
|
||||
label.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_lastCount = newCount;
|
||||
}
|
||||
|
||||
#region UI CONSTRUCTION
|
||||
|
||||
public void ConstructScenePane()
|
||||
{
|
||||
GameObject leftPane = UIFactory.CreateVerticalGroup(HomePage.Instance.Content, new Color(72f / 255f, 72f / 255f, 72f / 255f));
|
||||
LayoutElement leftLayout = leftPane.AddComponent<LayoutElement>();
|
||||
leftLayout.minWidth = 350;
|
||||
leftLayout.flexibleWidth = 0;
|
||||
|
||||
VerticalLayoutGroup leftGroup = leftPane.GetComponent<VerticalLayoutGroup>();
|
||||
leftGroup.padding.left = 4;
|
||||
leftGroup.padding.right = 4;
|
||||
leftGroup.padding.top = 8;
|
||||
leftGroup.padding.bottom = 4;
|
||||
leftGroup.spacing = 4;
|
||||
leftGroup.childControlWidth = true;
|
||||
leftGroup.childControlHeight = true;
|
||||
leftGroup.childForceExpandWidth = true;
|
||||
leftGroup.childForceExpandHeight = true;
|
||||
|
||||
GameObject titleObj = UIFactory.CreateLabel(leftPane, TextAnchor.UpperLeft);
|
||||
Text titleLabel = titleObj.GetComponent<Text>();
|
||||
titleLabel.text = "Scene Explorer";
|
||||
titleLabel.fontSize = 20;
|
||||
LayoutElement titleLayout = titleObj.AddComponent<LayoutElement>();
|
||||
titleLayout.minHeight = 30;
|
||||
titleLayout.flexibleHeight = 0;
|
||||
|
||||
GameObject sceneDropdownObj = UIFactory.CreateDropdown(leftPane, out m_sceneDropdown);
|
||||
LayoutElement dropdownLayout = sceneDropdownObj.AddComponent<LayoutElement>();
|
||||
dropdownLayout.minHeight = 40;
|
||||
dropdownLayout.flexibleHeight = 0;
|
||||
dropdownLayout.minWidth = 320;
|
||||
dropdownLayout.flexibleWidth = 2;
|
||||
|
||||
m_sceneDropdownText = m_sceneDropdown.transform.Find("Label").GetComponent<Text>();
|
||||
m_sceneDropdown.onValueChanged.AddListener((int val) => { SetSceneFromDropdown(val); });
|
||||
|
||||
void SetSceneFromDropdown(int val)
|
||||
{
|
||||
//string scene = m_sceneDropdown.options[val].text;
|
||||
SetTargetScene(m_currentScenes[val]);
|
||||
}
|
||||
|
||||
GameObject scenePathGroupObj = UIFactory.CreateHorizontalGroup(leftPane, new Color(1, 1, 1, 0f));
|
||||
HorizontalLayoutGroup scenePathGroup = scenePathGroupObj.GetComponent<HorizontalLayoutGroup>();
|
||||
scenePathGroup.childControlHeight = true;
|
||||
scenePathGroup.childControlWidth = true;
|
||||
scenePathGroup.childForceExpandHeight = true;
|
||||
scenePathGroup.childForceExpandWidth = true;
|
||||
scenePathGroup.spacing = 5;
|
||||
LayoutElement scenePathLayout = scenePathGroupObj.AddComponent<LayoutElement>();
|
||||
scenePathLayout.minHeight = 20;
|
||||
scenePathLayout.minWidth = 335;
|
||||
scenePathLayout.flexibleWidth = 0;
|
||||
|
||||
m_backButtonObj = UIFactory.CreateButton(scenePathGroupObj);
|
||||
Text backButtonText = m_backButtonObj.GetComponentInChildren<Text>();
|
||||
backButtonText.text = "◄";
|
||||
LayoutElement backButtonLayout = m_backButtonObj.AddComponent<LayoutElement>();
|
||||
backButtonLayout.minWidth = 40;
|
||||
backButtonLayout.flexibleWidth = 0;
|
||||
Button backButton = m_backButtonObj.GetComponent<Button>();
|
||||
var colors = backButton.colors;
|
||||
colors.normalColor = new Color(0.12f, 0.12f, 0.12f);
|
||||
backButton.colors = colors;
|
||||
|
||||
backButton.onClick.AddListener(() => { SetSceneObjectParent(); });
|
||||
|
||||
void SetSceneObjectParent()
|
||||
{
|
||||
if (!m_selectedSceneObject || !m_selectedSceneObject.transform.parent?.gameObject)
|
||||
{
|
||||
m_selectedSceneObject = null;
|
||||
SetTargetScene(m_currentScene);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetTargetObject(m_selectedSceneObject.transform.parent.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
GameObject scenePathLabel = UIFactory.CreateHorizontalGroup(scenePathGroupObj);
|
||||
Image image = scenePathLabel.GetComponent<Image>();
|
||||
image.color = Color.white;
|
||||
|
||||
LayoutElement scenePathLabelLayout = scenePathLabel.AddComponent<LayoutElement>();
|
||||
scenePathLabelLayout.minWidth = 210;
|
||||
scenePathLabelLayout.minHeight = 20;
|
||||
scenePathLabelLayout.flexibleHeight = 0;
|
||||
scenePathLabelLayout.flexibleWidth = 120;
|
||||
|
||||
scenePathLabel.AddComponent<Mask>().showMaskGraphic = false;
|
||||
|
||||
GameObject scenePathLabelText = UIFactory.CreateLabel(scenePathLabel, TextAnchor.MiddleLeft);
|
||||
m_scenePathText = scenePathLabelText.GetComponent<Text>();
|
||||
m_scenePathText.text = "Scene root:";
|
||||
m_scenePathText.fontSize = 15;
|
||||
m_scenePathText.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||||
|
||||
LayoutElement textLayout = scenePathLabelText.gameObject.AddComponent<LayoutElement>();
|
||||
textLayout.minWidth = 210;
|
||||
textLayout.flexibleWidth = 120;
|
||||
textLayout.minHeight = 20;
|
||||
textLayout.flexibleHeight = 0;
|
||||
|
||||
m_mainInspectBtn = UIFactory.CreateButton(scenePathGroupObj);
|
||||
Text inspectButtonText = m_mainInspectBtn.GetComponentInChildren<Text>();
|
||||
inspectButtonText.text = "Inspect";
|
||||
LayoutElement inspectButtonLayout = m_mainInspectBtn.AddComponent<LayoutElement>();
|
||||
inspectButtonLayout.minWidth = 65;
|
||||
inspectButtonLayout.flexibleWidth = 0;
|
||||
Button inspectButton = m_mainInspectBtn.GetComponent<Button>();
|
||||
colors = inspectButton.colors;
|
||||
colors.normalColor = new Color(0.12f, 0.12f, 0.12f);
|
||||
inspectButton.colors = colors;
|
||||
|
||||
inspectButton.onClick.AddListener(() => { InspectorManager.Instance.Inspect(m_selectedSceneObject); });
|
||||
|
||||
GameObject scrollObj = UIFactory.CreateScrollView(leftPane, out m_pageContent, out SliderScrollbar scroller, new Color(0.1f, 0.1f, 0.1f));
|
||||
|
||||
m_pageHandler = new PageHandler(scroller);
|
||||
m_pageHandler.ConstructUI(leftPane);
|
||||
m_pageHandler.OnPageChanged += OnSceneListPageTurn;
|
||||
|
||||
// hide button
|
||||
|
||||
var hideButtonObj = UIFactory.CreateButton(leftPane);
|
||||
var hideBtn = hideButtonObj.GetComponent<Button>();
|
||||
|
||||
var hideColors = hideBtn.colors;
|
||||
hideColors.normalColor = new Color(0.15f, 0.15f, 0.15f);
|
||||
hideBtn.colors = hideColors;
|
||||
var hideText = hideButtonObj.GetComponentInChildren<Text>();
|
||||
hideText.text = "Hide Scene Explorer";
|
||||
hideText.fontSize = 13;
|
||||
var hideLayout = hideButtonObj.AddComponent<LayoutElement>();
|
||||
hideLayout.minWidth = 20;
|
||||
hideLayout.minHeight = 20;
|
||||
|
||||
hideBtn.onClick.AddListener(OnHide);
|
||||
|
||||
void OnHide()
|
||||
{
|
||||
if (!Hiding)
|
||||
{
|
||||
Hiding = true;
|
||||
|
||||
hideText.text = "►";
|
||||
titleObj.SetActive(false);
|
||||
sceneDropdownObj.SetActive(false);
|
||||
scenePathGroupObj.SetActive(false);
|
||||
scrollObj.SetActive(false);
|
||||
m_pageHandler.Hide();
|
||||
|
||||
leftLayout.minWidth = 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
Hiding = false;
|
||||
|
||||
hideText.text = "Hide Scene Explorer";
|
||||
titleObj.SetActive(true);
|
||||
sceneDropdownObj.SetActive(true);
|
||||
scenePathGroupObj.SetActive(true);
|
||||
scrollObj.SetActive(true);
|
||||
|
||||
leftLayout.minWidth = 350;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
OnToggleShow?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddObjectListButton()
|
||||
{
|
||||
int thisIndex = m_shortListTexts.Count();
|
||||
|
||||
GameObject btnGroupObj = UIFactory.CreateHorizontalGroup(m_pageContent, new Color(0.1f, 0.1f, 0.1f));
|
||||
HorizontalLayoutGroup btnGroup = btnGroupObj.GetComponent<HorizontalLayoutGroup>();
|
||||
btnGroup.childForceExpandWidth = true;
|
||||
btnGroup.childControlWidth = true;
|
||||
btnGroup.childForceExpandHeight = false;
|
||||
btnGroup.childControlHeight = true;
|
||||
LayoutElement btnLayout = btnGroupObj.AddComponent<LayoutElement>();
|
||||
btnLayout.flexibleWidth = 320;
|
||||
btnLayout.minHeight = 25;
|
||||
btnLayout.flexibleHeight = 0;
|
||||
btnGroupObj.AddComponent<Mask>();
|
||||
|
||||
var toggleObj = UIFactory.CreateToggle(btnGroupObj, out Toggle toggle, out Text toggleText, new Color(0.1f, 0.1f, 0.1f));
|
||||
var toggleLayout = toggleObj.AddComponent<LayoutElement>();
|
||||
toggleLayout.minHeight = 25;
|
||||
toggleLayout.minWidth = 25;
|
||||
toggleText.text = "";
|
||||
toggle.isOn = false;
|
||||
m_shortListToggles.Add(toggle);
|
||||
toggle.onValueChanged.AddListener((bool val) => { OnToggleClicked(thisIndex, val); });
|
||||
|
||||
GameObject mainButtonObj = UIFactory.CreateButton(btnGroupObj);
|
||||
LayoutElement mainBtnLayout = mainButtonObj.AddComponent<LayoutElement>();
|
||||
mainBtnLayout.minHeight = 25;
|
||||
mainBtnLayout.flexibleHeight = 0;
|
||||
mainBtnLayout.minWidth = 230;
|
||||
mainBtnLayout.flexibleWidth = 0;
|
||||
Button mainBtn = mainButtonObj.GetComponent<Button>();
|
||||
ColorBlock mainColors = mainBtn.colors;
|
||||
mainColors.normalColor = new Color(0.1f, 0.1f, 0.1f);
|
||||
mainColors.highlightedColor = new Color(0.2f, 0.2f, 0.2f, 1);
|
||||
mainBtn.colors = mainColors;
|
||||
|
||||
mainBtn.onClick.AddListener(() => { SceneListObjectClicked(thisIndex); });
|
||||
|
||||
Text mainText = mainButtonObj.GetComponentInChildren<Text>();
|
||||
mainText.alignment = TextAnchor.MiddleLeft;
|
||||
mainText.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||||
m_shortListTexts.Add(mainText);
|
||||
|
||||
GameObject inspectBtnObj = UIFactory.CreateButton(btnGroupObj);
|
||||
LayoutElement inspectBtnLayout = inspectBtnObj.AddComponent<LayoutElement>();
|
||||
inspectBtnLayout.minWidth = 60;
|
||||
inspectBtnLayout.flexibleWidth = 0;
|
||||
inspectBtnLayout.minHeight = 25;
|
||||
inspectBtnLayout.flexibleHeight = 0;
|
||||
Text inspectText = inspectBtnObj.GetComponentInChildren<Text>();
|
||||
inspectText.text = "Inspect";
|
||||
inspectText.color = Color.white;
|
||||
|
||||
Button inspectBtn = inspectBtnObj.GetComponent<Button>();
|
||||
ColorBlock inspectColors = inspectBtn.colors;
|
||||
inspectColors.normalColor = new Color(0.15f, 0.15f, 0.15f);
|
||||
mainColors.highlightedColor = new Color(0.2f, 0.2f, 0.2f, 0.5f);
|
||||
inspectBtn.colors = inspectColors;
|
||||
|
||||
inspectBtn.onClick.AddListener(() => { InspectorManager.Instance.Inspect(m_shortList[thisIndex]); });
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
69
src/Loader/BIE/BepInExConfigHandler.cs
Normal file
69
src/Loader/BIE/BepInExConfigHandler.cs
Normal file
@ -0,0 +1,69 @@
|
||||
#if BIE
|
||||
using BepInEx.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityExplorer.Core.Config;
|
||||
|
||||
namespace UnityExplorer.Loader.BIE
|
||||
{
|
||||
public class BepInExConfigHandler : ConfigHandler
|
||||
{
|
||||
private ConfigFile Config => ExplorerBepInPlugin.Instance.Config;
|
||||
|
||||
private const string CTG_NAME = "UnityExplorer";
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
// Not necessary
|
||||
}
|
||||
|
||||
public override void RegisterConfigElement<T>(ConfigElement<T> config)
|
||||
{
|
||||
var entry = Config.Bind(CTG_NAME, config.Name, config.Value, config.Description);
|
||||
|
||||
entry.SettingChanged += (object o, EventArgs e) =>
|
||||
{
|
||||
config.Value = entry.Value;
|
||||
};
|
||||
}
|
||||
|
||||
public override T GetConfigValue<T>(ConfigElement<T> element)
|
||||
{
|
||||
if (Config.TryGetEntry(CTG_NAME, element.Name, out ConfigEntry<T> configEntry))
|
||||
return configEntry.Value;
|
||||
else
|
||||
throw new Exception("Could not get config entry '" + element.Name + "'");
|
||||
}
|
||||
|
||||
public override void SetConfigValue<T>(ConfigElement<T> element, T value)
|
||||
{
|
||||
if (Config.TryGetEntry(CTG_NAME, element.Name, out ConfigEntry<T> configEntry))
|
||||
configEntry.Value = value;
|
||||
else
|
||||
ExplorerCore.Log("Could not get config entry '" + element.Name + "'");
|
||||
}
|
||||
|
||||
public override void LoadConfig()
|
||||
{
|
||||
foreach (var entry in ConfigManager.ConfigElements)
|
||||
{
|
||||
var key = entry.Key;
|
||||
var def = new ConfigDefinition(CTG_NAME, key);
|
||||
if (Config.ContainsKey(def) && Config[def] is ConfigEntryBase configEntry)
|
||||
{
|
||||
var config = entry.Value;
|
||||
config.BoxedValue = configEntry.BoxedValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void SaveConfig()
|
||||
{
|
||||
// not required
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
149
src/Loader/BIE/ExplorerBepInPlugin.cs
Normal file
149
src/Loader/BIE/ExplorerBepInPlugin.cs
Normal file
@ -0,0 +1,149 @@
|
||||
#if BIE
|
||||
using BepInEx;
|
||||
using BepInEx.Logging;
|
||||
using HarmonyLib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Loader.BIE;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Core;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityExplorer.Core.Input;
|
||||
#if CPP
|
||||
using BepInEx.IL2CPP;
|
||||
using UnhollowerRuntimeLib;
|
||||
#endif
|
||||
|
||||
namespace UnityExplorer
|
||||
{
|
||||
[BepInPlugin(ExplorerCore.GUID, "UnityExplorer", ExplorerCore.VERSION)]
|
||||
|
||||
public class ExplorerBepInPlugin :
|
||||
#if MONO
|
||||
BaseUnityPlugin
|
||||
#else
|
||||
BasePlugin
|
||||
#endif
|
||||
, IExplorerLoader
|
||||
{
|
||||
public static ExplorerBepInPlugin Instance;
|
||||
|
||||
public ManualLogSource LogSource
|
||||
#if MONO
|
||||
=> Logger;
|
||||
#else
|
||||
=> Log;
|
||||
#endif
|
||||
|
||||
public ConfigHandler ConfigHandler => _configHandler;
|
||||
private BepInExConfigHandler _configHandler;
|
||||
|
||||
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 => LogSource.LogMessage;
|
||||
public Action<object> OnLogWarning => LogSource.LogWarning;
|
||||
public Action<object> OnLogError => LogSource.LogError;
|
||||
|
||||
// Init common to Mono and Il2Cpp
|
||||
internal void UniversalInit()
|
||||
{
|
||||
Instance = this;
|
||||
_configHandler = new BepInExConfigHandler();
|
||||
}
|
||||
|
||||
#if MONO // Mono-specific
|
||||
internal void Awake()
|
||||
{
|
||||
UniversalInit();
|
||||
ExplorerCore.Init(this);
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
ExplorerCore.Update();
|
||||
}
|
||||
|
||||
#else // Il2Cpp-specific
|
||||
public override void Load()
|
||||
{
|
||||
UniversalInit();
|
||||
|
||||
ClassInjector.RegisterTypeInIl2Cpp<ExplorerBehaviour>();
|
||||
|
||||
var obj = new GameObject("ExplorerBehaviour");
|
||||
obj.AddComponent<ExplorerBehaviour>();
|
||||
obj.hideFlags = HideFlags.HideAndDontSave;
|
||||
GameObject.DontDestroyOnLoad(obj);
|
||||
|
||||
ExplorerCore.Init(this);
|
||||
}
|
||||
|
||||
// BepInEx Il2Cpp mod class doesn't have monobehaviour methods yet, so wrap them in a dummy.
|
||||
public class ExplorerBehaviour : MonoBehaviour
|
||||
{
|
||||
public ExplorerBehaviour(IntPtr ptr) : base(ptr) { }
|
||||
|
||||
internal void Awake()
|
||||
{
|
||||
Instance.LogSource.LogMessage("ExplorerBehaviour.Awake");
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
ExplorerCore.Update();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public void SetupPatches()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.HarmonyInstance.PatchAll();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(EventSystem), "current", MethodType.Setter)]
|
||||
public class PATCH_EventSystem_current
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_EventSystem_set_current(ref EventSystem value)
|
||||
{
|
||||
CursorUnlocker.Prefix_EventSystem_set_current(ref value);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Cursor), "lockState", MethodType.Setter)]
|
||||
public class PATCH_Cursor_lockState
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_set_lockState(ref CursorLockMode value)
|
||||
{
|
||||
CursorUnlocker.Prefix_set_lockState(ref value);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Cursor), "visible", MethodType.Setter)]
|
||||
public class PATCH_Cursor_visible
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_set_visible(ref bool value)
|
||||
{
|
||||
CursorUnlocker.Prefix_set_visible(ref value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
22
src/Loader/IExplorerLoader.cs
Normal file
22
src/Loader/IExplorerLoader.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityExplorer.Core.Config;
|
||||
|
||||
namespace UnityExplorer
|
||||
{
|
||||
public interface IExplorerLoader
|
||||
{
|
||||
string ExplorerFolder { get; }
|
||||
|
||||
string ConfigFolder { get; }
|
||||
ConfigHandler ConfigHandler { get; }
|
||||
|
||||
Action<object> OnLogMessage { get; }
|
||||
Action<object> OnLogWarning { get; }
|
||||
Action<object> OnLogError { get; }
|
||||
|
||||
void SetupPatches();
|
||||
}
|
||||
}
|
84
src/Loader/ML/ExplorerMelonMod.cs
Normal file
84
src/Loader/ML/ExplorerMelonMod.cs
Normal file
@ -0,0 +1,84 @@
|
||||
#if ML
|
||||
using System;
|
||||
using System.IO;
|
||||
using Harmony;
|
||||
using MelonLoader;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityExplorer;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityExplorer.Loader.ML;
|
||||
|
||||
[assembly: MelonInfo(typeof(ExplorerMelonMod), ExplorerCore.NAME, ExplorerCore.VERSION, ExplorerCore.AUTHOR)]
|
||||
[assembly: MelonGame(null, null)]
|
||||
|
||||
namespace UnityExplorer
|
||||
{
|
||||
public class ExplorerMelonMod : MelonMod, IExplorerLoader
|
||||
{
|
||||
public static ExplorerMelonMod Instance;
|
||||
|
||||
public string ExplorerFolder => Path.Combine("Mods", ExplorerCore.NAME);
|
||||
public string ConfigFolder => ExplorerFolder;
|
||||
|
||||
public ConfigHandler ConfigHandler => _configHandler;
|
||||
public MelonLoaderConfigHandler _configHandler;
|
||||
|
||||
public Action<object> OnLogMessage => MelonLogger.Msg;
|
||||
public Action<object> OnLogWarning => MelonLogger.Warning;
|
||||
public Action<object> OnLogError => MelonLogger.Error;
|
||||
|
||||
public Harmony.HarmonyInstance HarmonyInstance => Instance.Harmony;
|
||||
|
||||
public override void OnApplicationStart()
|
||||
{
|
||||
Instance = this;
|
||||
_configHandler = new MelonLoaderConfigHandler();
|
||||
|
||||
ExplorerCore.Init(this);
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
ExplorerCore.Update();
|
||||
}
|
||||
|
||||
public void SetupPatches()
|
||||
{
|
||||
try
|
||||
{
|
||||
PrefixProperty(typeof(Cursor),
|
||||
"lockState",
|
||||
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_lockState))));
|
||||
|
||||
PrefixProperty(typeof(Cursor),
|
||||
"visible",
|
||||
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_visible))));
|
||||
|
||||
PrefixProperty(typeof(EventSystem),
|
||||
"current",
|
||||
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_EventSystem_set_current))));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
private void PrefixProperty(Type type, string property, HarmonyMethod prefix)
|
||||
{
|
||||
try
|
||||
{
|
||||
var prop = type.GetProperty(property);
|
||||
this.Harmony.Patch(prop.GetSetMethod(), prefix: prefix);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExplorerCore.Log($"Unable to patch {type.Name}.set_{property}: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
103
src/Loader/ML/MelonLoaderConfigHandler.cs
Normal file
103
src/Loader/ML/MelonLoaderConfigHandler.cs
Normal file
@ -0,0 +1,103 @@
|
||||
#if ML
|
||||
using MelonLoader;
|
||||
using MelonLoader.Tomlyn.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.Core.Config;
|
||||
|
||||
namespace UnityExplorer.Loader.ML
|
||||
{
|
||||
public class MelonLoaderConfigHandler : ConfigHandler
|
||||
{
|
||||
internal const string CTG_NAME = "UnityExplorer";
|
||||
|
||||
internal MelonPreferences_Category prefCategory;
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
prefCategory = MelonPreferences.CreateCategory(CTG_NAME, $"{CTG_NAME} Settings");
|
||||
|
||||
MelonPreferences.Mapper.RegisterMapper(KeycodeReader, KeycodeWriter);
|
||||
}
|
||||
|
||||
public override void LoadConfig()
|
||||
{
|
||||
foreach (var entry in ConfigManager.ConfigElements)
|
||||
{
|
||||
var key = entry.Key;
|
||||
if (prefCategory.GetEntry(key) is MelonPreferences_Entry)
|
||||
{
|
||||
var config = entry.Value;
|
||||
config.BoxedValue = config.GetLoaderConfigValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void RegisterConfigElement<T>(ConfigElement<T> config)
|
||||
{
|
||||
var entry = prefCategory.CreateEntry(config.Name, config.Value, null, config.IsInternal) as MelonPreferences_Entry<T>;
|
||||
|
||||
entry.OnValueChangedUntyped += () =>
|
||||
{
|
||||
if ((entry.Value == null && config.Value == null) || config.Value.Equals(entry.Value))
|
||||
return;
|
||||
|
||||
config.Value = entry.Value;
|
||||
};
|
||||
}
|
||||
|
||||
public override void SetConfigValue<T>(ConfigElement<T> config, T value)
|
||||
{
|
||||
if (prefCategory.GetEntry<T>(config.Name) is MelonPreferences_Entry<T> entry)
|
||||
{
|
||||
entry.Value = value;
|
||||
entry.Save();
|
||||
}
|
||||
}
|
||||
|
||||
public override T GetConfigValue<T>(ConfigElement<T> config)
|
||||
{
|
||||
if (prefCategory.GetEntry<T>(config.Name) is MelonPreferences_Entry<T> entry)
|
||||
return entry.Value;
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public override void OnAnyConfigChanged()
|
||||
{
|
||||
}
|
||||
|
||||
public override void SaveConfig()
|
||||
{
|
||||
MelonPreferences.Save();
|
||||
}
|
||||
|
||||
public static KeyCode KeycodeReader(TomlObject value)
|
||||
{
|
||||
try
|
||||
{
|
||||
KeyCode kc = (KeyCode)Enum.Parse(typeof(KeyCode), (value as TomlString).Value);
|
||||
|
||||
if (kc == default)
|
||||
throw new Exception();
|
||||
|
||||
return kc;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return KeyCode.F7;
|
||||
}
|
||||
}
|
||||
|
||||
public static TomlObject KeycodeWriter(KeyCode value)
|
||||
{
|
||||
return MelonPreferences.Mapper.ToToml(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
156
src/Loader/STANDALONE/ExplorerStandalone.cs
Normal file
156
src/Loader/STANDALONE/ExplorerStandalone.cs
Normal file
@ -0,0 +1,156 @@
|
||||
#if STANDALONE
|
||||
using HarmonyLib;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Loader.STANDALONE;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityExplorer.Core;
|
||||
#if CPP
|
||||
using UnhollowerRuntimeLib;
|
||||
#endif
|
||||
|
||||
namespace UnityExplorer
|
||||
{
|
||||
public class ExplorerStandalone : IExplorerLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Call this to initialize UnityExplorer without adding a log listener.
|
||||
/// </summary>
|
||||
/// <returns>The new (or active, if one exists) instance of ExplorerStandalone.</returns>
|
||||
public static ExplorerStandalone CreateInstance()
|
||||
=> CreateInstance(null);
|
||||
|
||||
/// <summary>
|
||||
/// Call this to initialize UnityExplorer and add a listener for UnityExplorer's log messages.
|
||||
/// </summary>
|
||||
/// <param name="logListener">Your log listener to handle UnityExplorer logs.</param>
|
||||
/// <returns>The new (or active, if one exists) instance of ExplorerStandalone.</returns>
|
||||
public static ExplorerStandalone CreateInstance(Action<string, LogType> logListener)
|
||||
{
|
||||
if (Instance != null)
|
||||
return Instance;
|
||||
|
||||
OnLog += logListener;
|
||||
|
||||
var instance = new ExplorerStandalone();
|
||||
instance.Init();
|
||||
return instance;
|
||||
}
|
||||
|
||||
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 ConfigHandler ConfigHandler => _configHandler;
|
||||
private StandaloneConfigHandler _configHandler;
|
||||
|
||||
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;
|
||||
_configHandler = new StandaloneConfigHandler();
|
||||
|
||||
#if CPP
|
||||
ClassInjector.RegisterTypeInIl2Cpp<ExplorerBehaviour>();
|
||||
#endif
|
||||
var obj = new GameObject("ExplorerBehaviour");
|
||||
obj.AddComponent<ExplorerBehaviour>();
|
||||
|
||||
GameObject.DontDestroyOnLoad(obj);
|
||||
obj.hideFlags = HideFlags.HideAndDontSave;
|
||||
|
||||
ExplorerCore.Init(this);
|
||||
}
|
||||
|
||||
public class ExplorerBehaviour : MonoBehaviour
|
||||
{
|
||||
#if CPP
|
||||
public ExplorerBehaviour(IntPtr ptr) : base(ptr) { }
|
||||
#endif
|
||||
internal void Update()
|
||||
{
|
||||
ExplorerCore.Update();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetupPatches()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.HarmonyInstance.PatchAll();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(EventSystem), "current", MethodType.Setter)]
|
||||
public class PATCH_EventSystem_current
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_EventSystem_set_current(ref EventSystem value)
|
||||
{
|
||||
CursorUnlocker.Prefix_EventSystem_set_current(ref value);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Cursor), "lockState", MethodType.Setter)]
|
||||
public class PATCH_Cursor_lockState
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_set_lockState(ref CursorLockMode value)
|
||||
{
|
||||
CursorUnlocker.Prefix_set_lockState(ref value);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Cursor), "visible", MethodType.Setter)]
|
||||
public class PATCH_Cursor_visible
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static void Prefix_set_visible(ref bool value)
|
||||
{
|
||||
CursorUnlocker.Prefix_set_visible(ref value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
108
src/Loader/STANDALONE/StandaloneConfigHandler.cs
Normal file
108
src/Loader/STANDALONE/StandaloneConfigHandler.cs
Normal file
@ -0,0 +1,108 @@
|
||||
#if STANDALONE
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityExplorer.Core.Config;
|
||||
using IniParser.Parser;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.Loader.STANDALONE
|
||||
{
|
||||
public class StandaloneConfigHandler : ConfigHandler
|
||||
{
|
||||
internal static IniDataParser _parser;
|
||||
internal static string INI_PATH;
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
INI_PATH = Path.Combine(ExplorerCore.Loader.ConfigFolder, "config.ini");
|
||||
_parser = new IniDataParser();
|
||||
_parser.Configuration.CommentString = "#";
|
||||
}
|
||||
|
||||
public override void LoadConfig()
|
||||
{
|
||||
if (!TryLoadConfig())
|
||||
SaveConfig();
|
||||
}
|
||||
|
||||
public override void RegisterConfigElement<T>(ConfigElement<T> element)
|
||||
{
|
||||
// Not necessary
|
||||
}
|
||||
|
||||
public override void SetConfigValue<T>(ConfigElement<T> element, T value)
|
||||
{
|
||||
// Not necessary, just save.
|
||||
SaveConfig();
|
||||
}
|
||||
|
||||
public override T GetConfigValue<T>(ConfigElement<T> element)
|
||||
{
|
||||
// Not necessary, just return the value.
|
||||
return element.Value;
|
||||
}
|
||||
|
||||
public bool TryLoadConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(INI_PATH))
|
||||
return false;
|
||||
|
||||
string ini = File.ReadAllText(INI_PATH);
|
||||
|
||||
var data = _parser.Parse(ini);
|
||||
|
||||
foreach (var config in data.Sections["Config"])
|
||||
{
|
||||
if (ConfigManager.ConfigElements.TryGetValue(config.KeyName, out IConfigElement configElement))
|
||||
configElement.BoxedValue = StringToConfigValue(config.Value, configElement.ElementType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public object StringToConfigValue(string value, Type elementType)
|
||||
{
|
||||
if (elementType == typeof(KeyCode))
|
||||
return (KeyCode)Enum.Parse(typeof(KeyCode), value);
|
||||
else if (elementType == typeof(bool))
|
||||
return bool.Parse(value);
|
||||
else if (elementType == typeof(int))
|
||||
return int.Parse(value);
|
||||
else
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void OnAnyConfigChanged()
|
||||
{
|
||||
SaveConfig();
|
||||
}
|
||||
|
||||
public override void SaveConfig()
|
||||
{
|
||||
var data = new IniParser.Model.IniData();
|
||||
|
||||
data.Sections.AddSection("Config");
|
||||
var sec = data.Sections["Config"];
|
||||
|
||||
foreach (var entry in ConfigManager.ConfigElements)
|
||||
sec.AddKey(entry.Key, entry.Value.BoxedValue.ToString());
|
||||
|
||||
if (!Directory.Exists(ExplorerCore.Loader.ConfigFolder))
|
||||
Directory.CreateDirectory(ExplorerCore.Loader.ConfigFolder);
|
||||
|
||||
File.WriteAllText(INI_PATH, data.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -2,13 +2,6 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityExplorer;
|
||||
|
||||
#if ML
|
||||
using MelonLoader;
|
||||
|
||||
[assembly: MelonInfo(typeof(ExplorerMelonMod), "UnityExplorer", ExplorerCore.VERSION, ExplorerCore.AUTHOR)]
|
||||
[assembly: MelonGame(null, null)]
|
||||
#endif
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
|
BIN
src/Resources/explorerui.legacy.bundle
Normal file
BIN
src/Resources/explorerui.legacy.bundle
Normal file
Binary file not shown.
BIN
src/Resources/explorerui.legacy.bundle.bak.5.3.8
Normal file
BIN
src/Resources/explorerui.legacy.bundle.bak.5.3.8
Normal file
Binary file not shown.
@ -1,262 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityExplorer.UI;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
#if CPP
|
||||
#endif
|
||||
|
||||
namespace UnityExplorer.Tests
|
||||
{
|
||||
public static class StaticTestClass
|
||||
{
|
||||
public static int StaticProperty => 5;
|
||||
public static int StaticField = 69;
|
||||
public static List<string> StaticList = new List<string>
|
||||
{
|
||||
"one",
|
||||
"two",
|
||||
"three",
|
||||
};
|
||||
public static void StaticMethod() { }
|
||||
|
||||
}
|
||||
|
||||
public class TestClass
|
||||
{
|
||||
public string AAALongString = @"1
|
||||
2
|
||||
3
|
||||
4
|
||||
5";
|
||||
|
||||
public Vector2 AATestVector2 = new Vector2(1, 2);
|
||||
public Vector3 AATestVector3 = new Vector3(1, 2, 3);
|
||||
public Vector4 AATestVector4 = new Vector4(1, 2, 3, 4);
|
||||
public Rect AATestRect = new Rect(1, 2, 3, 4);
|
||||
public Color AATestColor = new Color(0.1f, 0.2f, 0.3f, 0.4f);
|
||||
|
||||
public bool ATestBoolMethod() => false;
|
||||
|
||||
public bool this[int index]
|
||||
{
|
||||
get => index % 2 == 0;
|
||||
set => m_thisBool = value;
|
||||
}
|
||||
internal bool m_thisBool;
|
||||
|
||||
static int testInt;
|
||||
public static List<string> ExceptionList
|
||||
{
|
||||
get
|
||||
{
|
||||
testInt++;
|
||||
if (testInt % 2 == 0)
|
||||
throw new Exception("its even");
|
||||
else
|
||||
return new List<string> { "one" };
|
||||
}
|
||||
}
|
||||
|
||||
static bool abool;
|
||||
public static bool ATestExceptionBool
|
||||
{
|
||||
get
|
||||
{
|
||||
abool = !abool;
|
||||
if (!abool)
|
||||
throw new Exception("false");
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ExceptionString => throw new NotImplementedException();
|
||||
|
||||
public static string ANullString = null;
|
||||
public static float ATestFloat = 420.69f;
|
||||
public static int ATestInt = -1;
|
||||
public static string ATestString = "hello world";
|
||||
public static uint ATestUInt = 1u;
|
||||
public static byte ATestByte = 255;
|
||||
public static ulong AReadonlyUlong = 82934UL;
|
||||
|
||||
public static TestClass Instance => m_instance ?? (m_instance = new TestClass());
|
||||
private static TestClass m_instance;
|
||||
|
||||
public object AmbigObject;
|
||||
|
||||
public List<List<List<string>>> ANestedNestedList = new List<List<List<string>>>
|
||||
{
|
||||
new List<List<string>>
|
||||
{
|
||||
new List<string>
|
||||
{
|
||||
"one",
|
||||
"two",
|
||||
},
|
||||
new List<string>
|
||||
{
|
||||
"three",
|
||||
"four"
|
||||
}
|
||||
},
|
||||
new List<List<string>>
|
||||
{
|
||||
new List<string>
|
||||
{
|
||||
"five",
|
||||
"six"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static bool SetOnlyProperty
|
||||
{
|
||||
set => m_setOnlyProperty = value;
|
||||
}
|
||||
private static bool m_setOnlyProperty;
|
||||
public static bool ReadSetOnlyProperty => m_setOnlyProperty;
|
||||
|
||||
public Texture TestTexture;
|
||||
public static Sprite TestSprite;
|
||||
|
||||
#if CPP
|
||||
public static Il2CppSystem.Collections.Generic.HashSet<string> CppHashSetTest;
|
||||
public static Il2CppSystem.Collections.Generic.List<string> CppStringTest;
|
||||
public static Il2CppSystem.Collections.IList CppIList;
|
||||
#endif
|
||||
|
||||
public TestClass()
|
||||
{
|
||||
int a = 0;
|
||||
foreach (var list in ANestedNestedList)
|
||||
{
|
||||
foreach (var list2 in list)
|
||||
{
|
||||
for (int i = 0; i < 33; i++)
|
||||
list2.Add(a++.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
#if CPP
|
||||
TestTexture = UIManager.MakeSolidTexture(Color.white, 1000, 600);
|
||||
TestTexture.name = "TestTexture";
|
||||
|
||||
var r = new Rect(0, 0, TestTexture.width, TestTexture.height);
|
||||
var v2 = Vector2.zero;
|
||||
var v4 = Vector4.zero;
|
||||
TestSprite = Sprite.CreateSprite_Injected((Texture2D)TestTexture, ref r, ref v2, 100f, 0u, SpriteMeshType.Tight, ref v4, false);
|
||||
|
||||
GameObject.DontDestroyOnLoad(TestTexture);
|
||||
GameObject.DontDestroyOnLoad(TestSprite);
|
||||
|
||||
//// test loading a tex from file
|
||||
//var dataToLoad = System.IO.File.ReadAllBytes(@"Mods\UnityExplorer\Tex_Nemundis_Nebula.png");
|
||||
//ExplorerCore.Log($"Tex load success: {TestTexture.LoadImage(dataToLoad, false)}");
|
||||
|
||||
CppHashSetTest = new Il2CppSystem.Collections.Generic.HashSet<string>();
|
||||
CppHashSetTest.Add("1");
|
||||
CppHashSetTest.Add("2");
|
||||
CppHashSetTest.Add("3");
|
||||
|
||||
CppStringTest = new Il2CppSystem.Collections.Generic.List<string>();
|
||||
CppStringTest.Add("1");
|
||||
CppStringTest.Add("2");
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string TestRefInOutGeneric<T>(ref string arg0, in int arg1, out string arg2) where T : Component
|
||||
{
|
||||
arg2 = "this is arg2";
|
||||
|
||||
return $"T: '{typeof(T).FullName}', ref arg0: '{arg0}', in arg1: '{arg1}', out arg2: '{arg2}'";
|
||||
}
|
||||
|
||||
// test a non-generic dictionary
|
||||
|
||||
public Hashtable TestNonGenericDict()
|
||||
{
|
||||
return new Hashtable
|
||||
{
|
||||
{ "One", 1 },
|
||||
{ "Two", 2 },
|
||||
{ "Three", 3 },
|
||||
};
|
||||
}
|
||||
|
||||
// test HashSets
|
||||
|
||||
public static HashSet<string> HashSetTest = new HashSet<string>
|
||||
{
|
||||
"One",
|
||||
"Two",
|
||||
"Three"
|
||||
};
|
||||
|
||||
|
||||
// Test indexed parameter
|
||||
|
||||
public string this[int arg0, string arg1]
|
||||
{
|
||||
get
|
||||
{
|
||||
return $"arg0: {arg0}, arg1: {arg1}";
|
||||
}
|
||||
}
|
||||
|
||||
// Test basic list
|
||||
|
||||
public static List<string> TestList = new List<string>
|
||||
{
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"etc..."
|
||||
};
|
||||
|
||||
// Test a nested dictionary
|
||||
|
||||
public static Dictionary<int, Dictionary<string, int>> NestedDictionary = new Dictionary<int, Dictionary<string, int>>
|
||||
{
|
||||
{
|
||||
1,
|
||||
new Dictionary<string, int>
|
||||
{
|
||||
{
|
||||
"Sub 1", 123
|
||||
},
|
||||
{
|
||||
"Sub 2", 456
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
2,
|
||||
new Dictionary<string, int>
|
||||
{
|
||||
{
|
||||
"Sub 3", 789
|
||||
},
|
||||
{
|
||||
"Sub 4", 000
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Test a basic method
|
||||
|
||||
public static Color TestMethod(float r, float g, float b, float a)
|
||||
{
|
||||
return new Color(r, g, b, a);
|
||||
}
|
||||
|
||||
// A method with default arguments
|
||||
|
||||
public static Vector3 TestDefaultArgs(float arg0, float arg1, float arg2 = 5.0f)
|
||||
{
|
||||
return new Vector3(arg0, arg1, arg2);
|
||||
}
|
||||
}
|
||||
}
|
102
src/UI/CacheObject/CacheConfigEntry.cs
Normal file
102
src/UI/CacheObject/CacheConfigEntry.cs
Normal file
@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.UI.InteractiveValues;
|
||||
|
||||
namespace UnityExplorer.UI.CacheObject
|
||||
{
|
||||
public class CacheConfigEntry : CacheObjectBase
|
||||
{
|
||||
public IConfigElement RefConfig { get; }
|
||||
|
||||
public override Type FallbackType => RefConfig.ElementType;
|
||||
|
||||
public override bool HasEvaluated => true;
|
||||
public override bool HasParameters => false;
|
||||
public override bool IsMember => false;
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public CacheConfigEntry(IConfigElement config, GameObject parent)
|
||||
{
|
||||
RefConfig = config;
|
||||
|
||||
m_parentContent = parent;
|
||||
|
||||
config.OnValueChangedNotify += () => { UpdateValue(); };
|
||||
|
||||
CreateIValue(config.BoxedValue, config.ElementType);
|
||||
}
|
||||
|
||||
public override void CreateIValue(object value, Type fallbackType)
|
||||
{
|
||||
IValue = InteractiveValue.Create(value, fallbackType);
|
||||
IValue.Owner = this;
|
||||
IValue.m_mainContentParent = m_mainGroup;
|
||||
IValue.m_subContentParent = this.m_subContent;
|
||||
}
|
||||
|
||||
public override void UpdateValue()
|
||||
{
|
||||
IValue.Value = RefConfig.BoxedValue;
|
||||
|
||||
base.UpdateValue();
|
||||
}
|
||||
|
||||
public override void SetValue()
|
||||
{
|
||||
RefConfig.BoxedValue = IValue.Value;
|
||||
}
|
||||
|
||||
internal GameObject m_mainGroup;
|
||||
|
||||
internal override void ConstructUI()
|
||||
{
|
||||
base.ConstructUI();
|
||||
|
||||
m_mainGroup = UIFactory.CreateVerticalGroup(m_mainContent, "ConfigHolder", true, false, true, true, 5, new Vector4(2, 2, 2, 2));
|
||||
|
||||
var horiGroup = UIFactory.CreateHorizontalGroup(m_mainGroup, "ConfigEntryHolder", false, false, true, true, childAlignment: TextAnchor.MiddleLeft);
|
||||
UIFactory.SetLayoutElement(horiGroup, minHeight: 30, flexibleHeight: 0);
|
||||
|
||||
// config entry label
|
||||
|
||||
var configLabel = UIFactory.CreateLabel(horiGroup, "ConfigLabel", this.RefConfig.Name, TextAnchor.MiddleLeft);
|
||||
var leftRect = configLabel.GetComponent<RectTransform>();
|
||||
leftRect.anchorMin = Vector2.zero;
|
||||
leftRect.anchorMax = Vector2.one;
|
||||
leftRect.offsetMin = Vector2.zero;
|
||||
leftRect.offsetMax = Vector2.zero;
|
||||
leftRect.sizeDelta = Vector2.zero;
|
||||
UIFactory.SetLayoutElement(configLabel.gameObject, minWidth: 250, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
|
||||
|
||||
// Default button
|
||||
|
||||
var defaultButton = UIFactory.CreateButton(horiGroup,
|
||||
"RevertDefaultButton",
|
||||
"Default",
|
||||
() => { RefConfig.RevertToDefaultValue(); },
|
||||
new Color(0.3f, 0.3f, 0.3f));
|
||||
UIFactory.SetLayoutElement(defaultButton.gameObject, minWidth: 80, minHeight: 22, flexibleWidth: 0);
|
||||
|
||||
// Description label
|
||||
|
||||
var desc = UIFactory.CreateLabel(m_mainGroup, "Description", $"<i>{RefConfig.Description}</i>", TextAnchor.MiddleLeft, Color.grey);
|
||||
UIFactory.SetLayoutElement(desc.gameObject, minWidth: 250, minHeight: 20, flexibleWidth: 9999, flexibleHeight: 0);
|
||||
|
||||
// IValue
|
||||
|
||||
if (IValue != null)
|
||||
{
|
||||
IValue.m_mainContentParent = m_mainGroup;
|
||||
IValue.m_subContentParent = this.m_subContent;
|
||||
}
|
||||
|
||||
// makes the subcontent look nicer
|
||||
m_subContent.transform.SetParent(m_mainGroup.transform, false);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,8 +6,9 @@ using System.Text;
|
||||
using UnityExplorer.UI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI.InteractiveValues;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.CacheObject
|
||||
{
|
||||
public class CacheEnumerated : CacheObjectBase
|
||||
{
|
||||
@ -44,18 +45,11 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
base.ConstructUI();
|
||||
|
||||
var rowObj = UIFactory.CreateHorizontalGroup(m_mainContent, new Color(1, 1, 1, 0));
|
||||
var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
|
||||
rowGroup.padding.left = 5;
|
||||
rowGroup.padding.right = 2;
|
||||
var rowObj = UIFactory.CreateHorizontalGroup(m_mainContent, "CacheEnumeratedGroup", false, true, true, true, 0, new Vector4(0,0,5,2),
|
||||
new Color(1, 1, 1, 0));
|
||||
|
||||
var indexLabelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft);
|
||||
var indexLayout = indexLabelObj.AddComponent<LayoutElement>();
|
||||
indexLayout.minWidth = 20;
|
||||
indexLayout.flexibleWidth = 30;
|
||||
indexLayout.minHeight = 25;
|
||||
var indexText = indexLabelObj.GetComponent<Text>();
|
||||
indexText.text = this.Index + ":";
|
||||
var indexLabel = UIFactory.CreateLabel(rowObj, "IndexLabel", $"{this.Index}:", TextAnchor.MiddleLeft);
|
||||
UIFactory.SetLayoutElement(indexLabel.gameObject, minWidth: 20, flexibleWidth: 30, minHeight: 25);
|
||||
|
||||
IValue.m_mainContentParent = rowObj;
|
||||
}
|
@ -4,10 +4,9 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.CacheObject
|
||||
{
|
||||
public class CacheField : CacheMember
|
||||
{
|
||||
@ -33,6 +32,9 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
var fi = MemInfo as FieldInfo;
|
||||
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, IValue.Value);
|
||||
|
||||
if (this.ParentInspector?.ParentMember != null)
|
||||
this.ParentInspector.ParentMember.SetValue();
|
||||
}
|
||||
}
|
||||
}
|
@ -5,13 +5,14 @@ using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Shared;
|
||||
using UnityExplorer.Helpers;
|
||||
#if CPP
|
||||
using UnhollowerBaseLib;
|
||||
#endif
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.UI.Utility;
|
||||
using UnityExplorer.UI.InteractiveValues;
|
||||
using UnityExplorer.UI.Main.Home.Inspectors.Reflection;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.CacheObject
|
||||
{
|
||||
public abstract class CacheMember : CacheObjectBase
|
||||
{
|
||||
@ -19,6 +20,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
public override Type FallbackType { get; }
|
||||
|
||||
public ReflectionInspector ParentInspector { get; set; }
|
||||
public MemberInfo MemInfo { get; set; }
|
||||
public Type DeclaringType { get; set; }
|
||||
public object DeclaringInstance { get; set; }
|
||||
@ -49,10 +51,8 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
DeclaringType = memberInfo.DeclaringType;
|
||||
DeclaringInstance = declaringInstance;
|
||||
this.m_parentContent = parentContent;
|
||||
#if CPP
|
||||
if (DeclaringInstance != null)
|
||||
DeclaringInstance = DeclaringInstance.Il2CppCast(DeclaringType);
|
||||
#endif
|
||||
|
||||
DeclaringInstance = ReflectionProvider.Instance.Cast(declaringInstance, DeclaringType);
|
||||
}
|
||||
|
||||
public static bool CanProcessArgs(ParameterInfo[] parameters)
|
||||
@ -86,19 +86,19 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
try
|
||||
{
|
||||
#if CPP
|
||||
if (!IsReflectionSupported())
|
||||
throw new Exception("Type not supported with Reflection");
|
||||
#endif
|
||||
Type baseType = ReflectionUtility.GetType(IValue.Value) ?? FallbackType;
|
||||
|
||||
if (!ReflectionProvider.Instance.IsReflectionSupported(baseType))
|
||||
throw new Exception("Type not supported with reflection");
|
||||
|
||||
UpdateReflection();
|
||||
#if CPP
|
||||
|
||||
if (IValue.Value != null)
|
||||
IValue.Value = IValue.Value.Il2CppCast(ReflectionHelpers.GetActualType(IValue.Value));
|
||||
#endif
|
||||
IValue.Value = IValue.Value.Cast(ReflectionUtility.GetType(IValue.Value));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ReflectionException = ReflectionHelpers.ExceptionToString(e, true);
|
||||
ReflectionException = e.ReflectionExToString(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,46 +178,9 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
private string GetRichTextName()
|
||||
{
|
||||
return m_richTextName = UISyntaxHighlight.ParseFullSyntax(MemInfo.DeclaringType, false, MemInfo);
|
||||
return m_richTextName = SignatureHighlighter.ParseFullSyntax(MemInfo.DeclaringType, false, MemInfo);
|
||||
}
|
||||
|
||||
#if CPP
|
||||
internal bool IsReflectionSupported()
|
||||
{
|
||||
try
|
||||
{
|
||||
var baseType = ReflectionHelpers.GetActualType(IValue.Value) ?? IValue.FallbackType;
|
||||
|
||||
var gArgs = baseType.GetGenericArguments();
|
||||
if (gArgs.Length < 1)
|
||||
return true;
|
||||
|
||||
foreach (var arg in gArgs)
|
||||
{
|
||||
if (!Check(arg))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
bool Check(Type type)
|
||||
{
|
||||
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(type))
|
||||
return true;
|
||||
|
||||
if (!ReflectionHelpers.Il2CppTypeNotNull(type, out IntPtr ptr))
|
||||
return false;
|
||||
|
||||
return Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(ptr)) is Il2CppSystem.Type;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#region UI
|
||||
|
||||
internal float GetMemberLabelWidth(RectTransform scrollRect)
|
||||
@ -252,75 +215,46 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
base.ConstructUI();
|
||||
|
||||
var topGroupObj = UIFactory.CreateHorizontalGroup(m_mainContent, new Color(1, 1, 1, 0));
|
||||
var topGroupObj = UIFactory.CreateHorizontalGroup(m_mainContent, "CacheMemberGroup", false, false, true, true, 10, new Vector4(0, 0, 3, 3),
|
||||
new Color(1, 1, 1, 0));
|
||||
|
||||
m_topRowRect = topGroupObj.GetComponent<RectTransform>();
|
||||
var topLayout = topGroupObj.AddComponent<LayoutElement>();
|
||||
topLayout.minHeight = 25;
|
||||
topLayout.flexibleHeight = 0;
|
||||
topLayout.minWidth = 300;
|
||||
topLayout.flexibleWidth = 5000;
|
||||
var topGroup = topGroupObj.GetComponent<HorizontalLayoutGroup>();
|
||||
topGroup.childForceExpandHeight = false;
|
||||
topGroup.childForceExpandWidth = false;
|
||||
topGroup.childControlHeight = true;
|
||||
topGroup.childControlWidth = true;
|
||||
topGroup.spacing = 10;
|
||||
topGroup.padding.left = 3;
|
||||
topGroup.padding.right = 3;
|
||||
topGroup.padding.top = 0;
|
||||
topGroup.padding.bottom = 0;
|
||||
|
||||
UIFactory.SetLayoutElement(topGroupObj, minHeight: 25, flexibleHeight: 0, minWidth: 300, flexibleWidth: 5000);
|
||||
|
||||
// left group
|
||||
|
||||
m_leftGroup = UIFactory.CreateHorizontalGroup(topGroupObj, new Color(1, 1, 1, 0));
|
||||
var leftLayout = m_leftGroup.AddComponent<LayoutElement>();
|
||||
leftLayout.minHeight = 25;
|
||||
leftLayout.flexibleHeight = 0;
|
||||
leftLayout.minWidth = 125;
|
||||
leftLayout.flexibleWidth = 200;
|
||||
var leftGroup = m_leftGroup.GetComponent<HorizontalLayoutGroup>();
|
||||
leftGroup.childForceExpandHeight = true;
|
||||
leftGroup.childForceExpandWidth = false;
|
||||
leftGroup.childControlHeight = true;
|
||||
leftGroup.childControlWidth = true;
|
||||
leftGroup.spacing = 4;
|
||||
m_leftGroup = UIFactory.CreateHorizontalGroup(topGroupObj, "LeftGroup", false, true, true, true, 4, default, new Color(1, 1, 1, 0));
|
||||
UIFactory.SetLayoutElement(m_leftGroup, minHeight: 25, flexibleHeight: 0, minWidth: 125, flexibleWidth: 200);
|
||||
|
||||
// member label
|
||||
|
||||
var labelObj = UIFactory.CreateLabel(m_leftGroup, TextAnchor.MiddleLeft);
|
||||
var leftRect = labelObj.GetComponent<RectTransform>();
|
||||
m_memLabelText = UIFactory.CreateLabel(m_leftGroup, "MemLabelText", RichTextName, TextAnchor.MiddleLeft);
|
||||
m_memLabelText.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
var leftRect = m_memLabelText.GetComponent<RectTransform>();
|
||||
leftRect.anchorMin = Vector2.zero;
|
||||
leftRect.anchorMax = Vector2.one;
|
||||
leftRect.offsetMin = Vector2.zero;
|
||||
leftRect.offsetMax = Vector2.zero;
|
||||
leftRect.sizeDelta = Vector2.zero;
|
||||
m_leftLayout = labelObj.AddComponent<LayoutElement>();
|
||||
m_leftLayout = m_memLabelText.gameObject.AddComponent<LayoutElement>();
|
||||
m_leftLayout.preferredWidth = 125;
|
||||
m_leftLayout.minHeight = 25;
|
||||
m_leftLayout.flexibleHeight = 100;
|
||||
var labelFitter = labelObj.AddComponent<ContentSizeFitter>();
|
||||
var labelFitter = m_memLabelText.gameObject.AddComponent<ContentSizeFitter>();
|
||||
labelFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
labelFitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
m_memLabelText = labelObj.GetComponent<Text>();
|
||||
m_memLabelText.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
m_memLabelText.text = this.RichTextName;
|
||||
|
||||
// right group
|
||||
|
||||
m_rightGroup = UIFactory.CreateVerticalGroup(topGroupObj, new Color(1, 1, 1, 0));
|
||||
m_rightGroup = UIFactory.CreateVerticalGroup(topGroupObj, "RightGroup", false, true, true, true, 2, new Vector4(4,2,0,0),
|
||||
new Color(1, 1, 1, 0));
|
||||
|
||||
m_rightLayout = m_rightGroup.AddComponent<LayoutElement>();
|
||||
m_rightLayout.minHeight = 25;
|
||||
m_rightLayout.flexibleHeight = 480;
|
||||
m_rightLayout.minWidth = 125;
|
||||
m_rightLayout.flexibleWidth = 5000;
|
||||
var rightGroup = m_rightGroup.GetComponent<VerticalLayoutGroup>();
|
||||
rightGroup.childForceExpandHeight = true;
|
||||
rightGroup.childForceExpandWidth = false;
|
||||
rightGroup.childControlHeight = true;
|
||||
rightGroup.childControlWidth = true;
|
||||
rightGroup.spacing = 2;
|
||||
rightGroup.padding.top = 4;
|
||||
rightGroup.padding.bottom = 2;
|
||||
|
||||
ConstructArgInput(out GameObject argsHolder);
|
||||
|
||||
@ -335,27 +269,17 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
if (HasParameters)
|
||||
{
|
||||
argsHolder = UIFactory.CreateVerticalGroup(m_rightGroup, new Color(1, 1, 1, 0));
|
||||
var argsGroup = argsHolder.GetComponent<VerticalLayoutGroup>();
|
||||
argsGroup.spacing = 4;
|
||||
argsHolder = UIFactory.CreateVerticalGroup(m_rightGroup, "ArgsHolder", true, false, true, true, 4, new Color(1, 1, 1, 0));
|
||||
|
||||
if (this is CacheMethod cm && cm.GenericArgs.Length > 0)
|
||||
{
|
||||
cm.ConstructGenericArgInput(argsHolder);
|
||||
}
|
||||
|
||||
// todo normal args
|
||||
|
||||
if (m_arguments.Length > 0)
|
||||
{
|
||||
var titleObj = UIFactory.CreateLabel(argsHolder, TextAnchor.MiddleLeft);
|
||||
var titleText = titleObj.GetComponent<Text>();
|
||||
titleText.text = "<b>Arguments:</b>";
|
||||
UIFactory.CreateLabel(argsHolder, "ArgumentsLabel", "Arguments:", TextAnchor.MiddleLeft);
|
||||
|
||||
for (int i = 0; i < m_arguments.Length; i++)
|
||||
{
|
||||
AddArgRow(i, argsHolder);
|
||||
}
|
||||
}
|
||||
|
||||
argsHolder.SetActive(false);
|
||||
@ -366,30 +290,16 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
var arg = m_arguments[i];
|
||||
|
||||
var rowObj = UIFactory.CreateHorizontalGroup(parent, new Color(1, 1, 1, 0));
|
||||
var rowLayout = rowObj.AddComponent<LayoutElement>();
|
||||
rowLayout.minHeight = 25;
|
||||
rowLayout.flexibleWidth = 5000;
|
||||
var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
|
||||
rowGroup.childForceExpandHeight = false;
|
||||
rowGroup.childForceExpandWidth = true;
|
||||
rowGroup.spacing = 4;
|
||||
var rowObj = UIFactory.CreateHorizontalGroup(parent, "ArgRow", true, false, true, true, 4, default, new Color(1, 1, 1, 0));
|
||||
UIFactory.SetLayoutElement(rowObj, minHeight: 25, flexibleWidth: 5000);
|
||||
|
||||
var argLabelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft);
|
||||
var argLabelLayout = argLabelObj.AddComponent<LayoutElement>();
|
||||
argLabelLayout.minHeight = 25;
|
||||
var argText = argLabelObj.GetComponent<Text>();
|
||||
var argTypeTxt = UISyntaxHighlight.ParseFullSyntax(arg.ParameterType, false);
|
||||
argText.text = $"{argTypeTxt} <color={UISyntaxHighlight.Local}>{arg.Name}</color>";
|
||||
var argTypeTxt = SignatureHighlighter.ParseFullSyntax(arg.ParameterType, false);
|
||||
var argLabel = UIFactory.CreateLabel(rowObj, "ArgLabel", $"{argTypeTxt} <color={SignatureHighlighter.LOCAL_ARG}>{arg.Name}</color>",
|
||||
TextAnchor.MiddleLeft);
|
||||
UIFactory.SetLayoutElement(argLabel.gameObject, minHeight: 25);
|
||||
|
||||
var argInputObj = UIFactory.CreateInputField(rowObj, 14, (int)TextAnchor.MiddleLeft, 1);
|
||||
var argInputLayout = argInputObj.AddComponent<LayoutElement>();
|
||||
argInputLayout.flexibleWidth = 1200;
|
||||
argInputLayout.preferredWidth = 150;
|
||||
argInputLayout.minWidth = 20;
|
||||
argInputLayout.minHeight = 25;
|
||||
argInputLayout.flexibleHeight = 0;
|
||||
//argInputLayout.layoutPriority = 2;
|
||||
var argInputObj = UIFactory.CreateInputField(rowObj, "ArgInput", "...", 14, (int)TextAnchor.MiddleLeft, 1);
|
||||
UIFactory.SetLayoutElement(argInputObj, flexibleWidth: 1200, preferredWidth: 150, minWidth: 20, minHeight: 25, flexibleHeight: 0);
|
||||
|
||||
var argInput = argInputObj.GetComponent<InputField>();
|
||||
argInput.onValueChanged.AddListener((string val) => { m_argumentInput[i] = val; });
|
||||
@ -405,38 +315,28 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
if (HasParameters)
|
||||
{
|
||||
var evalGroupObj = UIFactory.CreateHorizontalGroup(m_rightGroup, new Color(1, 1, 1, 0));
|
||||
var evalGroup = evalGroupObj.GetComponent<HorizontalLayoutGroup>();
|
||||
evalGroup.childForceExpandWidth = false;
|
||||
evalGroup.childForceExpandHeight = false;
|
||||
evalGroup.spacing = 5;
|
||||
var evalGroupLayout = evalGroupObj.AddComponent<LayoutElement>();
|
||||
evalGroupLayout.minHeight = 25;
|
||||
evalGroupLayout.flexibleHeight = 0;
|
||||
evalGroupLayout.flexibleWidth = 5000;
|
||||
var evalGroupObj = UIFactory.CreateHorizontalGroup(m_rightGroup, "EvalGroup", false, false, true, true, 5,
|
||||
default, new Color(1, 1, 1, 0));
|
||||
UIFactory.SetLayoutElement(evalGroupObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 5000);
|
||||
|
||||
var evalButtonObj = UIFactory.CreateButton(evalGroupObj, new Color(0.4f, 0.4f, 0.4f));
|
||||
var evalLayout = evalButtonObj.AddComponent<LayoutElement>();
|
||||
evalLayout.minWidth = 100;
|
||||
evalLayout.minHeight = 22;
|
||||
evalLayout.flexibleWidth = 0;
|
||||
var evalText = evalButtonObj.GetComponentInChildren<Text>();
|
||||
evalText.text = $"Evaluate ({ParamCount})";
|
||||
var colors = new ColorBlock();
|
||||
colors = RuntimeProvider.Instance.SetColorBlock(colors, new Color(0.4f, 0.4f, 0.4f),
|
||||
new Color(0.4f, 0.7f, 0.4f), new Color(0.3f, 0.3f, 0.3f));
|
||||
|
||||
var evalButton = evalButtonObj.GetComponent<Button>();
|
||||
var colors = evalButton.colors;
|
||||
colors.highlightedColor = new Color(0.4f, 0.7f, 0.4f);
|
||||
evalButton.colors = colors;
|
||||
var evalButton = UIFactory.CreateButton(evalGroupObj,
|
||||
"EvalButton",
|
||||
$"Evaluate ({ParamCount})",
|
||||
null,
|
||||
colors);
|
||||
|
||||
var cancelButtonObj = UIFactory.CreateButton(evalGroupObj, new Color(0.3f, 0.3f, 0.3f));
|
||||
var cancelLayout = cancelButtonObj.AddComponent<LayoutElement>();
|
||||
cancelLayout.minWidth = 100;
|
||||
cancelLayout.minHeight = 22;
|
||||
cancelLayout.flexibleWidth = 0;
|
||||
var cancelText = cancelButtonObj.GetComponentInChildren<Text>();
|
||||
cancelText.text = "Close";
|
||||
UIFactory.SetLayoutElement(evalButton.gameObject, minWidth: 100, minHeight: 22, flexibleWidth: 0);
|
||||
|
||||
cancelButtonObj.SetActive(false);
|
||||
var evalText = evalButton.GetComponentInChildren<Text>();
|
||||
|
||||
var cancelButton = UIFactory.CreateButton(evalGroupObj, "CancelButton", "Close", null, new Color(0.3f, 0.3f, 0.3f));
|
||||
UIFactory.SetLayoutElement(cancelButton.gameObject, minWidth: 100, minHeight: 22, flexibleWidth: 0);
|
||||
|
||||
cancelButton.gameObject.SetActive(false);
|
||||
|
||||
evalButton.onClick.AddListener(() =>
|
||||
{
|
||||
@ -445,11 +345,9 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
argsHolder.SetActive(true);
|
||||
m_isEvaluating = true;
|
||||
evalText.text = "Evaluate";
|
||||
colors = evalButton.colors;
|
||||
colors.normalColor = new Color(0.3f, 0.6f, 0.3f);
|
||||
evalButton.colors = colors;
|
||||
evalButton.colors = RuntimeProvider.Instance.SetColorBlock(evalButton.colors, new Color(0.3f, 0.6f, 0.3f));
|
||||
|
||||
cancelButtonObj.SetActive(true);
|
||||
cancelButton.gameObject.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -460,41 +358,26 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
}
|
||||
});
|
||||
|
||||
var cancelButton = cancelButtonObj.GetComponent<Button>();
|
||||
cancelButton.onClick.AddListener(() =>
|
||||
{
|
||||
cancelButtonObj.SetActive(false);
|
||||
cancelButton.gameObject.SetActive(false);
|
||||
argsHolder.SetActive(false);
|
||||
m_isEvaluating = false;
|
||||
|
||||
evalText.text = $"Evaluate ({ParamCount})";
|
||||
colors = evalButton.colors;
|
||||
colors.normalColor = new Color(0.4f, 0.4f, 0.4f);
|
||||
evalButton.colors = colors;
|
||||
evalButton.colors = RuntimeProvider.Instance.SetColorBlock(evalButton.colors, new Color(0.4f, 0.4f, 0.4f));
|
||||
});
|
||||
}
|
||||
else if (this is CacheMethod)
|
||||
{
|
||||
// simple method evaluate button
|
||||
|
||||
var evalButtonObj = UIFactory.CreateButton(m_rightGroup, new Color(0.3f, 0.6f, 0.3f));
|
||||
var evalLayout = evalButtonObj.AddComponent<LayoutElement>();
|
||||
evalLayout.minWidth = 100;
|
||||
evalLayout.minHeight = 22;
|
||||
evalLayout.flexibleWidth = 0;
|
||||
var evalText = evalButtonObj.GetComponentInChildren<Text>();
|
||||
evalText.text = "Evaluate";
|
||||
var colors = new ColorBlock();
|
||||
colors = RuntimeProvider.Instance.SetColorBlock(colors, new Color(0.4f, 0.4f, 0.4f),
|
||||
new Color(0.4f, 0.7f, 0.4f), new Color(0.3f, 0.3f, 0.3f));
|
||||
|
||||
var evalButton = evalButtonObj.GetComponent<Button>();
|
||||
var colors = evalButton.colors;
|
||||
colors.highlightedColor = new Color(0.4f, 0.7f, 0.4f);
|
||||
evalButton.colors = colors;
|
||||
|
||||
evalButton.onClick.AddListener(OnMainEvaluateButton);
|
||||
void OnMainEvaluateButton()
|
||||
{
|
||||
(this as CacheMethod).Evaluate();
|
||||
}
|
||||
var evalButton = UIFactory.CreateButton(m_rightGroup, "EvalButton", "Evaluate", () => { (this as CacheMethod).Evaluate(); }, colors);
|
||||
UIFactory.SetLayoutElement(evalButton.gameObject, minWidth: 100, minHeight: 22, flexibleWidth: 0);
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,11 @@ using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.UI.Utility;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.CacheObject
|
||||
{
|
||||
public class CacheMethod : CacheMember
|
||||
{
|
||||
@ -71,8 +73,11 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
while (e.InnerException != null)
|
||||
e = e.InnerException;
|
||||
|
||||
ExplorerCore.LogWarning($"Exception evaluating: {e.GetType()}, {e.Message}");
|
||||
ReflectionException = ReflectionHelpers.ExceptionToString(e);
|
||||
ReflectionException = ReflectionUtility.ReflectionExToString(e);
|
||||
}
|
||||
|
||||
IValue.Value = ret;
|
||||
@ -87,7 +92,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
for (int i = 0; i < GenericArgs.Length; i++)
|
||||
{
|
||||
var input = m_genericArgInput[i];
|
||||
if (ReflectionHelpers.GetTypeByName(input) is Type t)
|
||||
if (ReflectionUtility.GetTypeByName(input) is Type t)
|
||||
{
|
||||
if (GenericConstraints[i].Length == 0)
|
||||
{
|
||||
@ -125,14 +130,10 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
internal void ConstructGenericArgInput(GameObject parent)
|
||||
{
|
||||
var titleObj = UIFactory.CreateLabel(parent, TextAnchor.MiddleLeft);
|
||||
var titleText = titleObj.GetComponent<Text>();
|
||||
titleText.text = "<b>Generic Arguments:</b>";
|
||||
UIFactory.CreateLabel(parent, "GenericArgLabel", "Generic Arguments:", TextAnchor.MiddleLeft);
|
||||
|
||||
for (int i = 0; i < GenericArgs.Length; i++)
|
||||
{
|
||||
AddGenericArgRow(i, parent);
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddGenericArgRow(int i, GameObject parent)
|
||||
@ -147,39 +148,24 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
if (constrainTxt != "")
|
||||
constrainTxt += ", ";
|
||||
|
||||
constrainTxt += $"{UISyntaxHighlight.ParseFullSyntax(constraint, false)}";
|
||||
constrainTxt += $"{SignatureHighlighter.ParseFullSyntax(constraint, false)}";
|
||||
}
|
||||
}
|
||||
else
|
||||
constrainTxt = $"Any";
|
||||
|
||||
var rowObj = UIFactory.CreateHorizontalGroup(parent, new Color(1, 1, 1, 0));
|
||||
var rowLayout = rowObj.AddComponent<LayoutElement>();
|
||||
rowLayout.minHeight = 25;
|
||||
rowLayout.flexibleWidth = 5000;
|
||||
var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
|
||||
rowGroup.childForceExpandHeight = true;
|
||||
rowGroup.spacing = 4;
|
||||
var rowObj = UIFactory.CreateHorizontalGroup(parent, "ArgRowObj", false, true, true, true, 4, default, new Color(1, 1, 1, 0));
|
||||
UIFactory.SetLayoutElement(rowObj, minHeight: 25, flexibleWidth: 5000);
|
||||
|
||||
var argLabelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft);
|
||||
//var argLayout = argLabelObj.AddComponent<LayoutElement>();
|
||||
//argLayout.minWidth = 20;
|
||||
var argText = argLabelObj.GetComponent<Text>();
|
||||
argText.text = $"{constrainTxt} <color={UISyntaxHighlight.Enum}>{arg.Name}</color>";
|
||||
var argLabelObj = UIFactory.CreateLabel(rowObj, "ArgLabelObj", $"{constrainTxt} <color={SignatureHighlighter.CONST_VAR}>{arg.Name}</color>",
|
||||
TextAnchor.MiddleLeft);
|
||||
|
||||
var argInputObj = UIFactory.CreateInputField(rowObj, 14, (int)TextAnchor.MiddleLeft, 1);
|
||||
var argInputLayout = argInputObj.AddComponent<LayoutElement>();
|
||||
argInputLayout.flexibleWidth = 1200;
|
||||
var argInputObj = UIFactory.CreateInputField(rowObj, "ArgInput", "...", 14, (int)TextAnchor.MiddleLeft, 1);
|
||||
UIFactory.SetLayoutElement(argInputObj, flexibleWidth: 1200);
|
||||
|
||||
var argInput = argInputObj.GetComponent<InputField>();
|
||||
argInput.onValueChanged.AddListener((string val) => { m_genericArgInput[i] = val; });
|
||||
|
||||
//var constraintLabelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft);
|
||||
//var constraintLayout = constraintLabelObj.AddComponent<LayoutElement>();
|
||||
//constraintLayout.minWidth = 60;
|
||||
//constraintLayout.flexibleWidth = 100;
|
||||
//var constraintText = constraintLabelObj.GetComponent<Text>();
|
||||
//constraintText.text = ;
|
||||
}
|
||||
|
||||
#endregion
|
@ -4,11 +4,12 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Shared;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.UI.InteractiveValues;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.CacheObject
|
||||
{
|
||||
public abstract class CacheObjectBase
|
||||
{
|
||||
@ -54,7 +55,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
// if the type has changed fundamentally, make a new interactivevalue for it
|
||||
var type = value == null
|
||||
? FallbackType
|
||||
: ReflectionHelpers.GetActualType(value);
|
||||
: ReflectionUtility.GetType(value);
|
||||
|
||||
var ivalueType = InteractiveValue.GetIValueForType(type);
|
||||
|
||||
@ -85,33 +86,18 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
m_constructedUI = true;
|
||||
|
||||
m_mainContent = UIFactory.CreateVerticalGroup(m_parentContent, new Color(0.1f, 0.1f, 0.1f));
|
||||
m_mainContent.name = "CacheObjectBase.MainContent";
|
||||
m_mainContent = UIFactory.CreateVerticalGroup(m_parentContent, "CacheObjectBase.MainContent", true, true, true, true, 0, default,
|
||||
new Color(0.1f, 0.1f, 0.1f));
|
||||
m_mainRect = m_mainContent.GetComponent<RectTransform>();
|
||||
m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
|
||||
var mainGroup = m_mainContent.GetComponent<VerticalLayoutGroup>();
|
||||
mainGroup.childForceExpandWidth = true;
|
||||
mainGroup.childControlWidth = true;
|
||||
mainGroup.childForceExpandHeight = true;
|
||||
mainGroup.childControlHeight = true;
|
||||
var mainLayout = m_mainContent.AddComponent<LayoutElement>();
|
||||
mainLayout.minHeight = 25;
|
||||
mainLayout.flexibleHeight = 9999;
|
||||
mainLayout.minWidth = 200;
|
||||
mainLayout.flexibleWidth = 5000;
|
||||
|
||||
UIFactory.SetLayoutElement(m_mainContent, minHeight: 25, flexibleHeight: 9999, minWidth: 200, flexibleWidth: 5000);
|
||||
|
||||
// subcontent
|
||||
|
||||
m_subContent = UIFactory.CreateVerticalGroup(m_mainContent, new Color(0.085f, 0.085f, 0.085f));
|
||||
m_subContent.name = "CacheObjectBase.SubContent";
|
||||
var subGroup = m_subContent.GetComponent<VerticalLayoutGroup>();
|
||||
subGroup.childForceExpandWidth = true;
|
||||
subGroup.childForceExpandHeight = false;
|
||||
var subLayout = m_subContent.AddComponent<LayoutElement>();
|
||||
subLayout.minHeight = 30;
|
||||
subLayout.flexibleHeight = 9999;
|
||||
subLayout.minWidth = 125;
|
||||
subLayout.flexibleWidth = 9000;
|
||||
m_subContent = UIFactory.CreateVerticalGroup(m_mainContent, "CacheObjectBase.SubContent", true, false, true, true, 0, default,
|
||||
new Color(0.085f, 0.085f, 0.085f));
|
||||
UIFactory.SetLayoutElement(m_subContent, minHeight: 30, flexibleHeight: 9999, minWidth: 125, flexibleWidth: 9000);
|
||||
|
||||
m_subContent.SetActive(false);
|
||||
|
@ -6,9 +6,9 @@ using System.Text;
|
||||
using UnityExplorer.UI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI.InteractiveValues;
|
||||
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.CacheObject
|
||||
{
|
||||
public enum PairTypes
|
||||
{
|
||||
@ -50,18 +50,15 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
base.ConstructUI();
|
||||
|
||||
var rowObj = UIFactory.CreateHorizontalGroup(m_mainContent, new Color(1, 1, 1, 0));
|
||||
var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
|
||||
rowGroup.padding.left = 5;
|
||||
rowGroup.padding.right = 2;
|
||||
Color bgColor = this.PairType == PairTypes.Key
|
||||
? new Color(0.07f, 0.07f, 0.07f)
|
||||
: new Color(0.1f, 0.1f, 0.1f);
|
||||
|
||||
var indexLabelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft);
|
||||
var indexLayout = indexLabelObj.AddComponent<LayoutElement>();
|
||||
indexLayout.minWidth = 80;
|
||||
indexLayout.flexibleWidth = 30;
|
||||
indexLayout.minHeight = 25;
|
||||
var indexText = indexLabelObj.GetComponent<Text>();
|
||||
indexText.text = $"{this.PairType} {this.Index}:";
|
||||
var rowObj = UIFactory.CreateHorizontalGroup(m_mainContent, "PairedGroup", false, false, true, true, 0, new Vector4(0,0,5,2),
|
||||
bgColor);
|
||||
|
||||
var indexLabel = UIFactory.CreateLabel(rowObj, "IndexLabel", $"{this.PairType} {this.Index}:", TextAnchor.MiddleLeft);
|
||||
UIFactory.SetLayoutElement(indexLabel.gameObject, minWidth: 80, flexibleWidth: 30, minHeight: 25);
|
||||
|
||||
IValue.m_mainContentParent = rowObj;
|
||||
}
|
@ -4,10 +4,10 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.CacheObject
|
||||
{
|
||||
public class CacheProperty : CacheMember
|
||||
{
|
||||
@ -63,6 +63,9 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
|
||||
|
||||
pi.SetValue(target, IValue.Value, ParseArguments());
|
||||
|
||||
if (this.ParentInspector?.ParentMember != null)
|
||||
this.ParentInspector.ParentMember.SetValue();
|
||||
}
|
||||
}
|
||||
}
|
@ -4,10 +4,11 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.CacheObject;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
public class InteractiveBool : InteractiveValue
|
||||
{
|
||||
@ -87,27 +88,22 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
if (Owner.CanWrite)
|
||||
{
|
||||
var toggleObj = UIFactory.CreateToggle(m_valueContent, out m_toggle, out _, new Color(0.1f, 0.1f, 0.1f));
|
||||
var toggleLayout = toggleObj.AddComponent<LayoutElement>();
|
||||
toggleLayout.minWidth = 24;
|
||||
|
||||
var toggleObj = UIFactory.CreateToggle(m_mainContent, "InteractiveBoolToggle", out m_toggle, out _, new Color(0.1f, 0.1f, 0.1f));
|
||||
UIFactory.SetLayoutElement(toggleObj, minWidth: 24);
|
||||
m_toggle.onValueChanged.AddListener(OnToggleValueChanged);
|
||||
|
||||
m_baseLabel.transform.SetAsLastSibling();
|
||||
|
||||
var applyBtnObj = UIFactory.CreateButton(m_valueContent, new Color(0.2f, 0.2f, 0.2f));
|
||||
var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
|
||||
applyLayout.minWidth = 50;
|
||||
applyLayout.minHeight = 25;
|
||||
applyLayout.flexibleWidth = 0;
|
||||
m_applyBtn = applyBtnObj.GetComponent<Button>();
|
||||
m_applyBtn.onClick.AddListener(() => { Owner.SetValue(); });
|
||||
m_applyBtn = UIFactory.CreateButton(m_mainContent,
|
||||
"ApplyButton",
|
||||
"Apply",
|
||||
() => { Owner.SetValue(); },
|
||||
new Color(0.2f, 0.2f, 0.2f));
|
||||
|
||||
var applyText = applyBtnObj.GetComponentInChildren<Text>();
|
||||
applyText.text = "Apply";
|
||||
UIFactory.SetLayoutElement(m_applyBtn.gameObject, minWidth: 50, minHeight: 25, flexibleWidth: 0);
|
||||
|
||||
toggleObj.SetActive(false);
|
||||
applyBtnObj.SetActive(false);
|
||||
m_applyBtn.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
168
src/UI/InteractiveValues/InteractiveColor.cs
Normal file
168
src/UI/InteractiveValues/InteractiveColor.cs
Normal file
@ -0,0 +1,168 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
public class InteractiveColor : InteractiveValue
|
||||
{
|
||||
//~~~~~~~~~ Instance ~~~~~~~~~~
|
||||
|
||||
public InteractiveColor(object value, Type valueType) : base(value, valueType) { }
|
||||
|
||||
public override bool HasSubContent => true;
|
||||
public override bool SubContentWanted => true;
|
||||
public override bool WantInspectBtn => true;
|
||||
|
||||
public override void RefreshUIForValue()
|
||||
{
|
||||
base.RefreshUIForValue();
|
||||
|
||||
if (m_subContentConstructed)
|
||||
RefreshUI();
|
||||
}
|
||||
|
||||
private void RefreshUI()
|
||||
{
|
||||
var color = (Color)this.Value;
|
||||
|
||||
m_inputs[0].text = color.r.ToString();
|
||||
m_inputs[1].text = color.g.ToString();
|
||||
m_inputs[2].text = color.b.ToString();
|
||||
m_inputs[3].text = color.a.ToString();
|
||||
|
||||
if (m_colorImage)
|
||||
m_colorImage.color = color;
|
||||
}
|
||||
|
||||
internal override void OnToggleSubcontent(bool toggle)
|
||||
{
|
||||
base.OnToggleSubcontent(toggle);
|
||||
|
||||
RefreshUI();
|
||||
}
|
||||
|
||||
#region UI CONSTRUCTION
|
||||
|
||||
private Image m_colorImage;
|
||||
|
||||
private readonly InputField[] m_inputs = new InputField[4];
|
||||
private readonly Slider[] m_sliders = new Slider[4];
|
||||
|
||||
public override void ConstructUI(GameObject parent, GameObject subGroup)
|
||||
{
|
||||
base.ConstructUI(parent, subGroup);
|
||||
|
||||
//// Limit the label width for colors, they're always about the same so make use of that space.
|
||||
//UIFactory.SetLayoutElement(this.m_baseLabel.gameObject, flexibleWidth: 0, minWidth: 250);
|
||||
}
|
||||
|
||||
public override void ConstructSubcontent()
|
||||
{
|
||||
base.ConstructSubcontent();
|
||||
|
||||
var horiGroup = UIFactory.CreateHorizontalGroup(m_subContentParent, "ColorEditor", false, false, true, true, 5,
|
||||
default, default, TextAnchor.MiddleLeft);
|
||||
|
||||
var editorContainer = UIFactory.CreateVerticalGroup(horiGroup, "EditorContent", false, true, true, true, 2, new Vector4(4, 4, 4, 4),
|
||||
new Color(0.08f, 0.08f, 0.08f));
|
||||
UIFactory.SetLayoutElement(editorContainer, minWidth: 300, flexibleWidth: 0);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
AddEditorRow(i, editorContainer);
|
||||
|
||||
if (Owner.CanWrite)
|
||||
{
|
||||
var applyBtn = UIFactory.CreateButton(editorContainer, "ApplyButton", "Apply", OnSetValue, new Color(0.2f, 0.2f, 0.2f));
|
||||
UIFactory.SetLayoutElement(applyBtn.gameObject, minWidth: 175, minHeight: 25, flexibleWidth: 0);
|
||||
|
||||
void OnSetValue()
|
||||
{
|
||||
Owner.SetValue();
|
||||
RefreshUIForValue();
|
||||
}
|
||||
}
|
||||
|
||||
var imgHolder = UIFactory.CreateVerticalGroup(horiGroup, "ImgHolder", true, true, true, true, 0, new Vector4(1, 1, 1, 1),
|
||||
new Color(0.08f, 0.08f, 0.08f));
|
||||
UIFactory.SetLayoutElement(imgHolder, minWidth: 128, minHeight: 128, flexibleWidth: 0, flexibleHeight: 0);
|
||||
|
||||
var imgObj = UIFactory.CreateUIObject("ColorImageHelper", imgHolder, new Vector2(100, 25));
|
||||
m_colorImage = imgObj.AddComponent<Image>();
|
||||
m_colorImage.color = (Color)this.Value;
|
||||
}
|
||||
|
||||
private static readonly string[] s_fieldNames = new[] { "R", "G", "B", "A" };
|
||||
|
||||
internal void AddEditorRow(int index, GameObject groupObj)
|
||||
{
|
||||
var row = UIFactory.CreateHorizontalGroup(groupObj, "EditorRow_" + s_fieldNames[index],
|
||||
false, true, true, true, 5, default, new Color(1, 1, 1, 0));
|
||||
|
||||
var label = UIFactory.CreateLabel(row, "RowLabel", $"{s_fieldNames[index]}:", TextAnchor.MiddleRight, Color.cyan);
|
||||
UIFactory.SetLayoutElement(label.gameObject, minWidth: 50, flexibleWidth: 0, minHeight: 25);
|
||||
|
||||
var inputFieldObj = UIFactory.CreateInputField(row, "InputField", "...", 14, 3, 1);
|
||||
UIFactory.SetLayoutElement(inputFieldObj, minWidth: 120, minHeight: 25, flexibleWidth: 0);
|
||||
|
||||
var inputField = inputFieldObj.GetComponent<InputField>();
|
||||
m_inputs[index] = inputField;
|
||||
inputField.characterValidation = InputField.CharacterValidation.Decimal;
|
||||
|
||||
inputField.onValueChanged.AddListener((string value) =>
|
||||
{
|
||||
float val = float.Parse(value);
|
||||
SetValueToColor(val);
|
||||
m_sliders[index].value = val;
|
||||
});
|
||||
|
||||
var sliderObj = UIFactory.CreateSlider(row, "Slider", out Slider slider);
|
||||
m_sliders[index] = slider;
|
||||
UIFactory.SetLayoutElement(sliderObj, minWidth: 200, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
|
||||
slider.minValue = 0;
|
||||
slider.maxValue = 1;
|
||||
slider.value = GetValueFromColor();
|
||||
|
||||
slider.onValueChanged.AddListener((float value) =>
|
||||
{
|
||||
inputField.text = value.ToString();
|
||||
SetValueToColor(value);
|
||||
m_inputs[index].text = value.ToString();
|
||||
});
|
||||
|
||||
// methods for writing to the color for this field
|
||||
|
||||
void SetValueToColor(float floatValue)
|
||||
{
|
||||
Color _color = (Color)Value;
|
||||
switch (index)
|
||||
{
|
||||
case 0: _color.r = floatValue; break;
|
||||
case 1: _color.g = floatValue; break;
|
||||
case 2: _color.b = floatValue; break;
|
||||
case 3: _color.a = floatValue; break;
|
||||
}
|
||||
Value = _color;
|
||||
m_colorImage.color = _color;
|
||||
}
|
||||
|
||||
float GetValueFromColor()
|
||||
{
|
||||
Color _color = (Color)Value;
|
||||
switch (index)
|
||||
{
|
||||
case 0: return _color.r;
|
||||
case 1: return _color.g;
|
||||
case 2: return _color.b;
|
||||
case 3: return _color.a;
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -5,13 +5,18 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Config;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Shared;
|
||||
using System.Reflection;
|
||||
using UnityExplorer.UI.CacheObject;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.UI.Utility;
|
||||
#if CPP
|
||||
using CppDictionary = Il2CppSystem.Collections.IDictionary;
|
||||
#endif
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
public class InteractiveDictionary : InteractiveValue
|
||||
{
|
||||
@ -32,7 +37,6 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
public override bool WantInspectBtn => false;
|
||||
public override bool HasSubContent => true;
|
||||
// todo fix for il2cpp
|
||||
public override bool SubContentWanted
|
||||
{
|
||||
get
|
||||
@ -44,7 +48,11 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
}
|
||||
|
||||
internal IDictionary RefIDictionary;
|
||||
|
||||
#if CPP
|
||||
internal CppDictionary RefCppDictionary;
|
||||
#else
|
||||
internal IDictionary RefCppDictionary = null;
|
||||
#endif
|
||||
internal Type m_typeOfKeys;
|
||||
internal Type m_typeofValues;
|
||||
|
||||
@ -52,7 +60,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
= new List<KeyValuePair<CachePaired, CachePaired>>();
|
||||
|
||||
internal readonly KeyValuePair<CachePaired, CachePaired>[] m_displayedEntries
|
||||
= new KeyValuePair<CachePaired, CachePaired>[ModConfig.Instance.Default_Page_Limit];
|
||||
= new KeyValuePair<CachePaired, CachePaired>[ConfigManager.Default_Page_Limit.Value];
|
||||
|
||||
internal bool m_recacheWanted = true;
|
||||
|
||||
@ -65,6 +73,11 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
RefIDictionary = Value as IDictionary;
|
||||
|
||||
#if CPP
|
||||
try { RefCppDictionary = (Value as Il2CppSystem.Object).TryCast<CppDictionary>(); }
|
||||
catch { }
|
||||
#endif
|
||||
|
||||
if (m_subContentParent.activeSelf)
|
||||
{
|
||||
GetCacheEntries();
|
||||
@ -129,13 +142,6 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
var value = RefIDictionary[key];
|
||||
|
||||
//if (index >= m_rowHolders.Count)
|
||||
//{
|
||||
// AddRowHolder();
|
||||
//}
|
||||
|
||||
//var holder = m_rowHolders[index];
|
||||
|
||||
var cacheKey = new CachePaired(index, this, this.RefIDictionary, PairTypes.Key, m_listContent);
|
||||
cacheKey.CreateIValue(key, this.m_typeOfKeys);
|
||||
cacheKey.Disable();
|
||||
@ -206,12 +212,13 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
RefreshDisplay();
|
||||
}
|
||||
|
||||
#region CPP fixes
|
||||
#region CPP fixes
|
||||
#if CPP
|
||||
// temp fix for Il2Cpp IDictionary until interfaces are fixed
|
||||
|
||||
private IDictionary EnumerateWithReflection()
|
||||
{
|
||||
var valueType = Value?.GetType() ?? FallbackType;
|
||||
var valueType = ReflectionUtility.GetType(Value);
|
||||
|
||||
// get keys and values
|
||||
var keys = valueType.GetProperty("Keys").GetValue(Value, null);
|
||||
@ -222,8 +229,8 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
var valueList = new List<object>();
|
||||
|
||||
// store entries with reflection
|
||||
EnumerateWithReflection(keys, keyList);
|
||||
EnumerateWithReflection(values, valueList);
|
||||
EnumerateCollection(keys, keyList);
|
||||
EnumerateCollection(values, valueList);
|
||||
|
||||
// make actual mono dictionary
|
||||
var dict = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>)
|
||||
@ -236,7 +243,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
return dict;
|
||||
}
|
||||
|
||||
private void EnumerateWithReflection(object collection, List<object> list)
|
||||
private void EnumerateCollection(object collection, List<object> list)
|
||||
{
|
||||
// invoke GetEnumerator
|
||||
var enumerator = collection.GetType().GetMethod("GetEnumerator").Invoke(collection, null);
|
||||
@ -253,9 +260,9 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region UI CONSTRUCTION
|
||||
#region UI CONSTRUCTION
|
||||
|
||||
internal GameObject m_listContent;
|
||||
internal LayoutElement m_listLayout;
|
||||
@ -277,38 +284,23 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
m_pageHandler.ConstructUI(m_subContentParent);
|
||||
m_pageHandler.OnPageChanged += OnPageTurned;
|
||||
|
||||
var scrollObj = UIFactory.CreateVerticalGroup(this.m_subContentParent, new Color(0.08f, 0.08f, 0.08f));
|
||||
m_listContent = scrollObj;
|
||||
m_listContent = UIFactory.CreateVerticalGroup(m_subContentParent, "DictionaryContent", true, true, true, true, 2, new Vector4(5,5,5,5),
|
||||
new Color(0.08f, 0.08f, 0.08f));
|
||||
|
||||
var scrollRect = scrollObj.GetComponent<RectTransform>();
|
||||
var scrollRect = m_listContent.GetComponent<RectTransform>();
|
||||
scrollRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0);
|
||||
|
||||
m_listLayout = Owner.m_mainContent.GetComponent<LayoutElement>();
|
||||
m_listLayout.minHeight = 25;
|
||||
m_listLayout.flexibleHeight = 0;
|
||||
|
||||
Owner.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
|
||||
|
||||
var scrollGroup = m_listContent.GetComponent<VerticalLayoutGroup>();
|
||||
scrollGroup.childForceExpandHeight = true;
|
||||
scrollGroup.childControlHeight = true;
|
||||
scrollGroup.spacing = 2;
|
||||
scrollGroup.padding.top = 5;
|
||||
scrollGroup.padding.left = 5;
|
||||
scrollGroup.padding.right = 5;
|
||||
scrollGroup.padding.bottom = 5;
|
||||
|
||||
var contentFitter = scrollObj.AddComponent<ContentSizeFitter>();
|
||||
var contentFitter = m_listContent.AddComponent<ContentSizeFitter>();
|
||||
contentFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
}
|
||||
|
||||
//internal void AddRowHolder()
|
||||
//{
|
||||
// var obj = UIFactory.CreateHorizontalGroup(m_listContent, new Color(0.15f, 0.15f, 0.15f));
|
||||
|
||||
// m_rowHolders.Add(obj);
|
||||
//}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -4,10 +4,10 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.UI;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
public class InteractiveEnum : InteractiveValue
|
||||
{
|
||||
@ -47,13 +47,34 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
var list = new List<KeyValuePair<int, string>>();
|
||||
var set = new HashSet<string>();
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
var name = value.ToString();
|
||||
|
||||
if (set.Contains(name))
|
||||
continue;
|
||||
|
||||
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());
|
||||
@ -115,32 +136,18 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
if (Owner.CanWrite)
|
||||
{
|
||||
var groupObj = UIFactory.CreateHorizontalGroup(m_subContentParent, new Color(1, 1, 1, 0));
|
||||
var group = groupObj.GetComponent<HorizontalLayoutGroup>();
|
||||
group.padding.top = 3;
|
||||
group.padding.left = 3;
|
||||
group.padding.right = 3;
|
||||
group.padding.bottom = 3;
|
||||
group.spacing = 5;
|
||||
var groupObj = UIFactory.CreateHorizontalGroup(m_subContentParent, "InteractiveEnumGroup", false, true, true, true, 5,
|
||||
new Vector4(3,3,3,3),new Color(1, 1, 1, 0));
|
||||
|
||||
// apply button
|
||||
|
||||
var applyObj = UIFactory.CreateButton(groupObj, new Color(0.3f, 0.3f, 0.3f));
|
||||
var applyLayout = applyObj.AddComponent<LayoutElement>();
|
||||
applyLayout.minHeight = 25;
|
||||
applyLayout.minWidth = 50;
|
||||
var applyText = applyObj.GetComponentInChildren<Text>();
|
||||
applyText.text = "Apply";
|
||||
var applyBtn = applyObj.GetComponent<Button>();
|
||||
applyBtn.onClick.AddListener(SetValueFromDropdown);
|
||||
var apply = UIFactory.CreateButton(groupObj, "ApplyButton", "Apply", SetValueFromDropdown, new Color(0.3f, 0.3f, 0.3f));
|
||||
UIFactory.SetLayoutElement(apply.gameObject, minHeight: 25, minWidth: 50);
|
||||
|
||||
// dropdown
|
||||
|
||||
var dropdownObj = UIFactory.CreateDropdown(groupObj, out m_dropdown);
|
||||
var dropLayout = dropdownObj.AddComponent<LayoutElement>();
|
||||
dropLayout.minWidth = 150;
|
||||
dropLayout.minHeight = 25;
|
||||
dropLayout.flexibleWidth = 120;
|
||||
var dropdownObj = UIFactory.CreateDropdown(groupObj, out m_dropdown, "", 14, null);
|
||||
UIFactory.SetLayoutElement(dropdownObj, minWidth: 150, minHeight: 25, flexibleWidth: 120);
|
||||
|
||||
foreach (var kvp in m_values)
|
||||
{
|
@ -6,12 +6,13 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Config;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Shared;
|
||||
using UnityExplorer.UI.CacheObject;
|
||||
using UnityExplorer.UI.Utility;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
public class InteractiveEnumerable : InteractiveValue
|
||||
{
|
||||
@ -37,11 +38,16 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
internal IEnumerable RefIEnumerable;
|
||||
internal IList RefIList;
|
||||
#if CPP
|
||||
internal Il2CppSystem.Collections.ICollection CppICollection;
|
||||
#else
|
||||
internal ICollection CppICollection = null;
|
||||
#endif
|
||||
|
||||
internal readonly Type m_baseEntryType;
|
||||
|
||||
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[ConfigManager.Default_Page_Limit.Value];
|
||||
internal bool m_recacheWanted = true;
|
||||
|
||||
public override void OnValueUpdated()
|
||||
@ -49,6 +55,14 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
RefIEnumerable = Value as IEnumerable;
|
||||
RefIList = Value as IList;
|
||||
|
||||
#if CPP
|
||||
if (Value != null && RefIList == null)
|
||||
{
|
||||
try { CppICollection = (Value as Il2CppSystem.Object).TryCast<Il2CppSystem.Collections.ICollection>(); }
|
||||
catch { }
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_subContentParent.activeSelf)
|
||||
{
|
||||
GetCacheEntries();
|
||||
@ -77,8 +91,8 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
if (Value != null)
|
||||
{
|
||||
string count = "?";
|
||||
if (m_recacheWanted && RefIList != null)
|
||||
count = RefIList.Count.ToString();
|
||||
if (m_recacheWanted && (RefIList != null || CppICollection != null))
|
||||
count = RefIList?.Count.ToString() ?? CppICollection.Count.ToString();
|
||||
else if (!m_recacheWanted)
|
||||
count = m_entries.Count.ToString();
|
||||
|
||||
@ -169,89 +183,62 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
RefreshDisplay();
|
||||
}
|
||||
|
||||
#region CPP Helpers
|
||||
#region CPP Helpers
|
||||
|
||||
#if CPP
|
||||
// some temp fixes for Il2Cpp IEnumerables until interfaces are fixed
|
||||
|
||||
internal static readonly Dictionary<Type, MethodInfo> s_getEnumeratorMethods = new Dictionary<Type, MethodInfo>();
|
||||
|
||||
internal static readonly Dictionary<Type, EnumeratorInfo> s_enumeratorInfos = new Dictionary<Type, EnumeratorInfo>();
|
||||
|
||||
internal class EnumeratorInfo
|
||||
{
|
||||
internal MethodInfo moveNext;
|
||||
internal PropertyInfo current;
|
||||
}
|
||||
|
||||
private IEnumerable EnumerateWithReflection()
|
||||
{
|
||||
if (Value.IsNullOrDestroyed())
|
||||
if (Value == null)
|
||||
return null;
|
||||
|
||||
var genericDef = Value.GetType().GetGenericTypeDefinition();
|
||||
|
||||
if (genericDef == typeof(Il2CppSystem.Collections.Generic.List<>))
|
||||
return CppListToMono(genericDef);
|
||||
else if (genericDef == typeof(Il2CppSystem.Collections.Generic.HashSet<>))
|
||||
return CppHashSetToMono();
|
||||
else
|
||||
return CppIListToMono();
|
||||
}
|
||||
|
||||
// List<T>.ToArray()
|
||||
private IEnumerable CppListToMono(Type genericTypeDef)
|
||||
{
|
||||
if (genericTypeDef == null) return null;
|
||||
|
||||
return genericTypeDef
|
||||
.MakeGenericType(new Type[] { this.m_baseEntryType })
|
||||
.GetMethod("ToArray")
|
||||
.Invoke(Value, new object[0]) as IEnumerable;
|
||||
}
|
||||
|
||||
// HashSet.GetEnumerator
|
||||
private IEnumerable CppHashSetToMono()
|
||||
{
|
||||
var set = new HashSet<object>();
|
||||
|
||||
// invoke GetEnumerator
|
||||
var enumerator = Value.GetType().GetMethod("GetEnumerator").Invoke(Value, null);
|
||||
// get the type of it
|
||||
var enumeratorType = enumerator.GetType();
|
||||
// reflect MoveNext and Current
|
||||
var moveNext = enumeratorType.GetMethod("MoveNext");
|
||||
var current = enumeratorType.GetProperty("Current");
|
||||
// iterate
|
||||
while ((bool)moveNext.Invoke(enumerator, null))
|
||||
set.Add(current.GetValue(enumerator));
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
// IList.Item
|
||||
private IList CppIListToMono()
|
||||
{
|
||||
try
|
||||
// new test
|
||||
var CppEnumerable = (Value as Il2CppSystem.Object)?.TryCast<Il2CppSystem.Collections.IEnumerable>();
|
||||
if (CppEnumerable != null)
|
||||
{
|
||||
var genericType = typeof(List<>).MakeGenericType(new Type[] { this.m_baseEntryType });
|
||||
var list = (IList)Activator.CreateInstance(genericType);
|
||||
var type = Value.GetType();
|
||||
if (!s_getEnumeratorMethods.ContainsKey(type))
|
||||
s_getEnumeratorMethods.Add(type, type.GetMethod("GetEnumerator"));
|
||||
|
||||
var enumerator = s_getEnumeratorMethods[type].Invoke(Value, null);
|
||||
var enumeratorType = enumerator.GetType();
|
||||
|
||||
for (int i = 0; ; i++)
|
||||
if (!s_enumeratorInfos.ContainsKey(enumeratorType))
|
||||
{
|
||||
try
|
||||
s_enumeratorInfos.Add(enumeratorType, new EnumeratorInfo
|
||||
{
|
||||
var itm = Value?.GetType()
|
||||
.GetProperty("Item")
|
||||
.GetValue(Value, new object[] { i });
|
||||
list.Add(itm);
|
||||
}
|
||||
catch { break; }
|
||||
current = enumeratorType.GetProperty("Current"),
|
||||
moveNext = enumeratorType.GetMethod("MoveNext"),
|
||||
});
|
||||
}
|
||||
var info = s_enumeratorInfos[enumeratorType];
|
||||
|
||||
// iterate
|
||||
var list = new List<object>();
|
||||
while ((bool)info.moveNext.Invoke(enumerator, null))
|
||||
list.Add(info.current.GetValue(enumerator));
|
||||
|
||||
return list;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExplorerCore.Log("Exception converting Il2Cpp IList to Mono IList: " + e.GetType() + ", " + e.Message);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region UI CONSTRUCTION
|
||||
#region UI CONSTRUCTION
|
||||
|
||||
internal GameObject m_listContent;
|
||||
internal LayoutElement m_listLayout;
|
||||
@ -271,10 +258,10 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
m_pageHandler.ConstructUI(m_subContentParent);
|
||||
m_pageHandler.OnPageChanged += OnPageTurned;
|
||||
|
||||
var scrollObj = UIFactory.CreateVerticalGroup(this.m_subContentParent, new Color(0.08f, 0.08f, 0.08f));
|
||||
m_listContent = scrollObj;
|
||||
m_listContent = UIFactory.CreateVerticalGroup(this.m_subContentParent, "EnumerableContent", true, true, true, true, 2, new Vector4(5,5,5,5),
|
||||
new Color(0.08f, 0.08f, 0.08f));
|
||||
|
||||
var scrollRect = scrollObj.GetComponent<RectTransform>();
|
||||
var scrollRect = m_listContent.GetComponent<RectTransform>();
|
||||
scrollRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0);
|
||||
|
||||
m_listLayout = Owner.m_mainContent.GetComponent<LayoutElement>();
|
||||
@ -282,20 +269,11 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
m_listLayout.flexibleHeight = 0;
|
||||
Owner.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
|
||||
|
||||
var scrollGroup = m_listContent.GetComponent<VerticalLayoutGroup>();
|
||||
scrollGroup.childForceExpandHeight = true;
|
||||
scrollGroup.childControlHeight = true;
|
||||
scrollGroup.spacing = 2;
|
||||
scrollGroup.padding.top = 5;
|
||||
scrollGroup.padding.left = 5;
|
||||
scrollGroup.padding.right = 5;
|
||||
scrollGroup.padding.bottom = 5;
|
||||
|
||||
var contentFitter = scrollObj.AddComponent<ContentSizeFitter>();
|
||||
var contentFitter = m_listContent.AddComponent<ContentSizeFitter>();
|
||||
contentFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -4,10 +4,10 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.UI;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
public class InteractiveFlags : InteractiveEnum
|
||||
{
|
||||
@ -94,35 +94,18 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
if (Owner.CanWrite)
|
||||
{
|
||||
var groupObj = UIFactory.CreateVerticalGroup(m_subContentParent, new Color(1, 1, 1, 0));
|
||||
var group = groupObj.GetComponent<VerticalLayoutGroup>();
|
||||
group.childForceExpandHeight = true;
|
||||
group.childForceExpandWidth = false;
|
||||
group.childControlHeight = true;
|
||||
group.childControlWidth = true;
|
||||
group.padding.top = 3;
|
||||
group.padding.left = 3;
|
||||
group.padding.right = 3;
|
||||
group.padding.bottom = 3;
|
||||
group.spacing = 5;
|
||||
var groupObj = UIFactory.CreateVerticalGroup(m_subContentParent, "InteractiveFlagsContent", false, true, true, true, 5,
|
||||
new Vector4(3,3,3,3), new Color(1, 1, 1, 0));
|
||||
|
||||
// apply button
|
||||
|
||||
var applyObj = UIFactory.CreateButton(groupObj, new Color(0.3f, 0.3f, 0.3f));
|
||||
var applyLayout = applyObj.AddComponent<LayoutElement>();
|
||||
applyLayout.minHeight = 25;
|
||||
applyLayout.minWidth = 50;
|
||||
var applyText = applyObj.GetComponentInChildren<Text>();
|
||||
applyText.text = "Apply";
|
||||
var applyBtn = applyObj.GetComponent<Button>();
|
||||
applyBtn.onClick.AddListener(SetValueFromToggles);
|
||||
var apply = UIFactory.CreateButton(groupObj, "ApplyButton", "Apply", SetValueFromToggles, new Color(0.3f, 0.3f, 0.3f));
|
||||
UIFactory.SetLayoutElement(apply.gameObject, minWidth: 50, minHeight: 25);
|
||||
|
||||
// toggles
|
||||
|
||||
for (int i = 0; i < m_values.Length; i++)
|
||||
{
|
||||
AddToggle(i, groupObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,11 +113,8 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
var value = m_values[index];
|
||||
|
||||
var toggleObj = UIFactory.CreateToggle(groupObj, out Toggle toggle, out Text text, new Color(0.1f, 0.1f, 0.1f));
|
||||
var toggleLayout = toggleObj.AddComponent<LayoutElement>();
|
||||
toggleLayout.minWidth = 100;
|
||||
toggleLayout.flexibleWidth = 2000;
|
||||
toggleLayout.minHeight = 25;
|
||||
var toggleObj = UIFactory.CreateToggle(groupObj, "FlagToggle", out Toggle toggle, out Text text, new Color(0.1f, 0.1f, 0.1f));
|
||||
UIFactory.SetLayoutElement(toggleObj, minWidth: 100, flexibleWidth: 2000, minHeight: 25);
|
||||
|
||||
m_toggles[index] = toggle;
|
||||
|
@ -5,10 +5,13 @@ using System.Text;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.UI.Utility;
|
||||
using UnityExplorer.UI.CacheObject;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
public class InteractiveNumber : InteractiveValue
|
||||
{
|
||||
@ -46,7 +49,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
return;
|
||||
}
|
||||
|
||||
m_baseLabel.text = UISyntaxHighlight.ParseFullSyntax(FallbackType, false);
|
||||
m_baseLabel.text = SignatureHighlighter.ParseFullSyntax(FallbackType, false);
|
||||
m_valueInput.text = Value.ToString();
|
||||
|
||||
var type = Value.GetType();
|
||||
@ -84,7 +87,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExplorerCore.LogWarning("Could not parse input! " + ReflectionHelpers.ExceptionToString(e, true));
|
||||
ExplorerCore.LogWarning("Could not parse input! " + ReflectionUtility.ReflectionExToString(e, true));
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,27 +102,16 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
labelLayout.minWidth = 50;
|
||||
labelLayout.flexibleWidth = 0;
|
||||
|
||||
var inputObj = UIFactory.CreateInputField(m_valueContent);
|
||||
var inputLayout = inputObj.AddComponent<LayoutElement>();
|
||||
inputLayout.minWidth = 120;
|
||||
inputLayout.minHeight = 25;
|
||||
inputLayout.flexibleWidth = 0;
|
||||
var inputObj = UIFactory.CreateInputField(m_mainContent, "InteractiveNumberInput", "...");
|
||||
UIFactory.SetLayoutElement(inputObj, minWidth: 120, minHeight: 25, flexibleWidth: 0);
|
||||
|
||||
m_valueInput = inputObj.GetComponent<InputField>();
|
||||
m_valueInput.gameObject.SetActive(false);
|
||||
|
||||
if (Owner.CanWrite)
|
||||
{
|
||||
var applyBtnObj = UIFactory.CreateButton(m_valueContent, new Color(0.2f, 0.2f, 0.2f));
|
||||
var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
|
||||
applyLayout.minWidth = 50;
|
||||
applyLayout.minHeight = 25;
|
||||
applyLayout.flexibleWidth = 0;
|
||||
m_applyBtn = applyBtnObj.GetComponent<Button>();
|
||||
m_applyBtn.onClick.AddListener(OnApplyClicked);
|
||||
|
||||
var applyText = applyBtnObj.GetComponentInChildren<Text>();
|
||||
applyText.text = "Apply";
|
||||
m_applyBtn = UIFactory.CreateButton(m_mainContent, "ApplyButton", "Apply", OnApplyClicked, new Color(0.2f, 0.2f, 0.2f));
|
||||
UIFactory.SetLayoutElement(m_applyBtn.gameObject, minWidth: 50, minHeight: 25, flexibleWidth: 0);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,10 +5,12 @@ using System.Text;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Utility;
|
||||
using UnityExplorer.UI.CacheObject;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
public class InteractiveString : InteractiveValue
|
||||
{
|
||||
@ -109,36 +111,27 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
base.ConstructUI(parent, subGroup);
|
||||
|
||||
GetDefaultLabel(false);
|
||||
m_richValueType = UISyntaxHighlight.ParseFullSyntax(FallbackType, false);
|
||||
m_richValueType = SignatureHighlighter.ParseFullSyntax(FallbackType, false);
|
||||
|
||||
m_labelLayout = m_baseLabel.gameObject.GetComponent<LayoutElement>();
|
||||
|
||||
var readonlyInputObj = UIFactory.CreateLabel(m_valueContent, TextAnchor.MiddleLeft);
|
||||
m_readonlyInput = readonlyInputObj.GetComponent<Text>();
|
||||
m_readonlyInput = UIFactory.CreateLabel(m_mainContent, "ReadonlyLabel", "", TextAnchor.MiddleLeft);
|
||||
m_readonlyInput.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||||
|
||||
var testFitter = readonlyInputObj.AddComponent<ContentSizeFitter>();
|
||||
var testFitter = m_readonlyInput.gameObject.AddComponent<ContentSizeFitter>();
|
||||
testFitter.verticalFit = ContentSizeFitter.FitMode.MinSize;
|
||||
|
||||
var labelLayout = readonlyInputObj.AddComponent<LayoutElement>();
|
||||
labelLayout.minHeight = 25;
|
||||
labelLayout.preferredHeight = 25;
|
||||
labelLayout.flexibleHeight = 0;
|
||||
UIFactory.SetLayoutElement(m_readonlyInput.gameObject, minHeight: 25, preferredHeight: 25, flexibleHeight: 0);
|
||||
}
|
||||
|
||||
public override void ConstructSubcontent()
|
||||
{
|
||||
base.ConstructSubcontent();
|
||||
|
||||
var groupObj = UIFactory.CreateVerticalGroup(m_subContentParent, new Color(1, 1, 1, 0));
|
||||
var group = groupObj.GetComponent<VerticalLayoutGroup>();
|
||||
group.spacing = 4;
|
||||
group.padding.top = 3;
|
||||
group.padding.left = 3;
|
||||
group.padding.right = 3;
|
||||
group.padding.bottom = 3;
|
||||
var groupObj = UIFactory.CreateVerticalGroup(m_subContentParent, "SubContent", false, false, true, true, 4, new Vector4(3,3,3,3),
|
||||
new Color(1, 1, 1, 0));
|
||||
|
||||
m_hiddenObj = UIFactory.CreateLabel(groupObj, TextAnchor.MiddleLeft);
|
||||
m_hiddenObj = UIFactory.CreateLabel(groupObj, "HiddenLabel", "", TextAnchor.MiddleLeft).gameObject;
|
||||
m_hiddenObj.SetActive(false);
|
||||
var hiddenText = m_hiddenObj.GetComponent<Text>();
|
||||
hiddenText.color = Color.clear;
|
||||
@ -147,23 +140,11 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
hiddenText.supportRichText = false;
|
||||
var hiddenFitter = m_hiddenObj.AddComponent<ContentSizeFitter>();
|
||||
hiddenFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
var hiddenLayout = m_hiddenObj.AddComponent<LayoutElement>();
|
||||
hiddenLayout.minHeight = 25;
|
||||
hiddenLayout.flexibleHeight = 500;
|
||||
hiddenLayout.minWidth = 250;
|
||||
hiddenLayout.flexibleWidth = 9000;
|
||||
var hiddenGroup = m_hiddenObj.AddComponent<HorizontalLayoutGroup>();
|
||||
hiddenGroup.childForceExpandWidth = true;
|
||||
hiddenGroup.childControlWidth = true;
|
||||
hiddenGroup.childForceExpandHeight = true;
|
||||
hiddenGroup.childControlHeight = true;
|
||||
UIFactory.SetLayoutElement(m_hiddenObj, minHeight: 25, flexibleHeight: 500, minWidth: 250, flexibleWidth: 9000);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(m_hiddenObj, true, true, true, true);
|
||||
|
||||
var inputObj = UIFactory.CreateInputField(m_hiddenObj, 14, 3);
|
||||
var inputLayout = inputObj.AddComponent<LayoutElement>();
|
||||
inputLayout.minWidth = 120;
|
||||
inputLayout.minHeight = 25;
|
||||
inputLayout.flexibleWidth = 5000;
|
||||
inputLayout.flexibleHeight = 5000;
|
||||
var inputObj = UIFactory.CreateInputField(m_hiddenObj, "StringInputField", "...", 14, 3);
|
||||
UIFactory.SetLayoutElement(inputObj, minWidth: 120, minHeight: 25, flexibleWidth: 5000, flexibleHeight: 5000);
|
||||
|
||||
m_valueInput = inputObj.GetComponent<InputField>();
|
||||
m_valueInput.lineType = InputField.LineType.MultiLineNewline;
|
||||
@ -181,17 +162,8 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
if (Owner.CanWrite)
|
||||
{
|
||||
var applyBtnObj = UIFactory.CreateButton(groupObj, new Color(0.2f, 0.2f, 0.2f));
|
||||
var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
|
||||
applyLayout.minWidth = 50;
|
||||
applyLayout.minHeight = 25;
|
||||
applyLayout.flexibleWidth = 0;
|
||||
|
||||
var applyBtn = applyBtnObj.GetComponent<Button>();
|
||||
applyBtn.onClick.AddListener(OnApplyClicked);
|
||||
|
||||
var applyText = applyBtnObj.GetComponentInChildren<Text>();
|
||||
applyText.text = "Apply";
|
||||
var apply = UIFactory.CreateButton(groupObj, "ApplyButton", "Apply", OnApplyClicked, new Color(0.2f, 0.2f, 0.2f));
|
||||
UIFactory.SetLayoutElement(apply.gameObject, minWidth: 50, minHeight: 25, flexibleWidth: 0);
|
||||
}
|
||||
else
|
||||
{
|
@ -4,10 +4,10 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.UI;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
#region IStructInfo helper
|
||||
|
||||
@ -183,7 +183,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
typeof(Vector3),
|
||||
typeof(Vector4),
|
||||
typeof(Rect),
|
||||
typeof(Color) // todo might make a special editor for colors
|
||||
//typeof(Color) // todo might make a special editor for colors
|
||||
};
|
||||
|
||||
//~~~~~~~~~ Instance ~~~~~~~~~~
|
||||
@ -225,17 +225,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
return;
|
||||
|
||||
if (StructInfo != null)
|
||||
{
|
||||
DestroySubContent();
|
||||
//// changing types, destroy subcontent
|
||||
//for (int i = 0; i < m_subContentParent.transform.childCount; i++)
|
||||
//{
|
||||
// var child = m_subContentParent.transform.GetChild(i);
|
||||
// GameObject.Destroy(child.gameObject);
|
||||
//}
|
||||
|
||||
//m_UIConstructed = false;
|
||||
}
|
||||
|
||||
m_lastStructType = type;
|
||||
|
||||
@ -266,68 +256,38 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
return;
|
||||
}
|
||||
|
||||
var editorContainer = UIFactory.CreateVerticalGroup(m_subContentParent, new Color(0.08f, 0.08f, 0.08f));
|
||||
var editorGroup = editorContainer.GetComponent<VerticalLayoutGroup>();
|
||||
editorGroup.childForceExpandWidth = false;
|
||||
editorGroup.padding.top = 4;
|
||||
editorGroup.padding.right = 4;
|
||||
editorGroup.padding.left = 4;
|
||||
editorGroup.padding.bottom = 4;
|
||||
editorGroup.spacing = 2;
|
||||
var editorContainer = UIFactory.CreateVerticalGroup(m_subContentParent, "EditorContent", false, true, true, true, 2, new Vector4(4,4,4,4),
|
||||
new Color(0.08f, 0.08f, 0.08f));
|
||||
|
||||
m_inputs = new InputField[StructInfo.FieldNames.Length];
|
||||
|
||||
for (int i = 0; i < StructInfo.FieldNames.Length; i++)
|
||||
{
|
||||
AddEditorRow(i, editorContainer);
|
||||
}
|
||||
|
||||
if (Owner.CanWrite)
|
||||
{
|
||||
var applyBtnObj = UIFactory.CreateButton(editorContainer, new Color(0.2f, 0.2f, 0.2f));
|
||||
var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
|
||||
applyLayout.minWidth = 175;
|
||||
applyLayout.minHeight = 25;
|
||||
applyLayout.flexibleWidth = 0;
|
||||
var m_applyBtn = applyBtnObj.GetComponent<Button>();
|
||||
m_applyBtn.onClick.AddListener(OnSetValue);
|
||||
var applyBtn = UIFactory.CreateButton(editorContainer, "ApplyButton", "Apply", OnSetValue, new Color(0.2f, 0.2f, 0.2f));
|
||||
UIFactory.SetLayoutElement(applyBtn.gameObject, minWidth: 175, minHeight: 25, flexibleWidth: 0);
|
||||
|
||||
void OnSetValue()
|
||||
{
|
||||
Owner.SetValue();
|
||||
RefreshUIForValue();
|
||||
}
|
||||
|
||||
var applyText = applyBtnObj.GetComponentInChildren<Text>();
|
||||
applyText.text = "Apply";
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddEditorRow(int index, GameObject groupObj)
|
||||
{
|
||||
var rowObj = UIFactory.CreateHorizontalGroup(groupObj, new Color(1, 1, 1, 0));
|
||||
var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
|
||||
rowGroup.childForceExpandHeight = true;
|
||||
rowGroup.childForceExpandWidth = false;
|
||||
rowGroup.spacing = 5;
|
||||
var rowObj = UIFactory.CreateHorizontalGroup(groupObj, "EditorRow", false, true, true, true, 5, default, new Color(1, 1, 1, 0));
|
||||
|
||||
var label = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleRight);
|
||||
var labelLayout = label.AddComponent<LayoutElement>();
|
||||
labelLayout.minWidth = 50;
|
||||
labelLayout.flexibleWidth = 0;
|
||||
labelLayout.minHeight = 25;
|
||||
var labelText = label.GetComponent<Text>();
|
||||
labelText.text = $"{StructInfo.FieldNames[index]}:";
|
||||
labelText.color = Color.cyan;
|
||||
var label = UIFactory.CreateLabel(rowObj, "RowLabel", $"{StructInfo.FieldNames[index]}:", TextAnchor.MiddleRight, Color.cyan);
|
||||
UIFactory.SetLayoutElement(label.gameObject, minWidth: 50, flexibleWidth: 0, minHeight: 25);
|
||||
|
||||
var inputFieldObj = UIFactory.CreateInputField(rowObj, "InputField", "...", 14, 3, 1);
|
||||
UIFactory.SetLayoutElement(inputFieldObj, minWidth: 120, minHeight: 25, flexibleWidth: 0);
|
||||
|
||||
var inputFieldObj = UIFactory.CreateInputField(rowObj, 14, 3, 1);
|
||||
var inputField = inputFieldObj.GetComponent<InputField>();
|
||||
inputField.characterValidation = InputField.CharacterValidation.Decimal;
|
||||
var inputLayout = inputFieldObj.AddComponent<LayoutElement>();
|
||||
inputLayout.flexibleWidth = 0;
|
||||
inputLayout.minWidth = 120;
|
||||
inputLayout.minHeight = 25;
|
||||
|
||||
m_inputs[index] = inputField;
|
||||
|
||||
inputField.onValueChanged.AddListener((string val) => { Value = StructInfo.SetValue(ref this.Value, index, float.Parse(val)); });
|
@ -5,43 +5,68 @@ using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Utility;
|
||||
using UnityExplorer.UI.CacheObject;
|
||||
using UnityExplorer.UI.Main.Home;
|
||||
|
||||
namespace UnityExplorer.Inspectors.Reflection
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
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)
|
||||
{
|
||||
// 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))
|
||||
return typeof(InteractiveBool);
|
||||
else if (type == typeof(string))
|
||||
return typeof(InteractiveString);
|
||||
// if type is primitive then it must be a number if its not a bool
|
||||
else if (type.IsPrimitive)
|
||||
return typeof(InteractiveNumber);
|
||||
// check for strings
|
||||
else if (type == typeof(string))
|
||||
return typeof(InteractiveString);
|
||||
// check for enum/flags
|
||||
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);
|
||||
else
|
||||
return typeof(InteractiveEnum);
|
||||
}
|
||||
// check for unity struct types
|
||||
else if (typeof(Color).IsAssignableFrom(type))
|
||||
return typeof(InteractiveColor);
|
||||
else if (InteractiveUnityStruct.SupportsType(type))
|
||||
return typeof(InteractiveUnityStruct);
|
||||
// check Transform, force InteractiveValue so they dont become InteractiveEnumerables.
|
||||
else if (typeof(Transform).IsAssignableFrom(type))
|
||||
return typeof(InteractiveValue);
|
||||
else if (ReflectionHelpers.IsDictionary(type))
|
||||
// check Dictionaries before Enumerables
|
||||
else if (ReflectionUtility.IsDictionary(type))
|
||||
return typeof(InteractiveDictionary);
|
||||
else if (ReflectionHelpers.IsEnumerable(type))
|
||||
// finally check for Enumerables
|
||||
else if (ReflectionUtility.IsEnumerable(type))
|
||||
return typeof(InteractiveEnumerable);
|
||||
// fallback to default
|
||||
else
|
||||
return typeof(InteractiveValue);
|
||||
}
|
||||
|
||||
public static InteractiveValue Create(object value, Type fallbackType)
|
||||
{
|
||||
var type = ReflectionHelpers.GetActualType(value) ?? fallbackType;
|
||||
var type = ReflectionUtility.GetType(value) ?? fallbackType;
|
||||
var iType = GetIValueForType(type);
|
||||
|
||||
return (InteractiveValue)Activator.CreateInstance(iType, new object[] { value, type });
|
||||
@ -72,11 +97,11 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
public virtual void OnDestroy()
|
||||
{
|
||||
if (this.m_valueContent)
|
||||
if (this.m_mainContent)
|
||||
{
|
||||
m_valueContent.transform.SetParent(null, false);
|
||||
m_valueContent.SetActive(false);
|
||||
GameObject.Destroy(this.m_valueContent.gameObject);
|
||||
m_mainContent.transform.SetParent(null, false);
|
||||
m_mainContent.SetActive(false);
|
||||
GameObject.Destroy(this.m_mainContent.gameObject);
|
||||
}
|
||||
|
||||
DestroySubContent();
|
||||
@ -176,11 +201,15 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
ConstructSubcontent();
|
||||
}
|
||||
|
||||
internal MethodInfo m_toStringMethod;
|
||||
internal MethodInfo m_toStringFormatMethod;
|
||||
internal bool m_gotToStringMethods;
|
||||
|
||||
public string GetDefaultLabel(bool updateType = true)
|
||||
{
|
||||
var valueType = Value?.GetType() ?? this.FallbackType;
|
||||
if (updateType)
|
||||
m_richValueType = UISyntaxHighlight.ParseFullSyntax(valueType, true);
|
||||
m_richValueType = SignatureHighlighter.ParseFullSyntax(valueType, true);
|
||||
|
||||
if (!Owner.HasEvaluated)
|
||||
return m_defaultLabel = $"<i><color=grey>Not yet evaluated</color> ({m_richValueType})</i>";
|
||||
@ -190,6 +219,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
string label;
|
||||
|
||||
// Two dirty fixes for TextAsset and EventSystem, which can have very long ToString results.
|
||||
if (Value is TextAsset textAsset)
|
||||
{
|
||||
label = textAsset.text;
|
||||
@ -203,22 +233,46 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
label = m_richValueType;
|
||||
}
|
||||
else
|
||||
else // For everything else...
|
||||
{
|
||||
var toString = (string)valueType.GetMethod("ToString", new Type[0])?.Invoke(Value, null)
|
||||
?? Value.ToString();
|
||||
if (!m_gotToStringMethods)
|
||||
{
|
||||
m_gotToStringMethods = true;
|
||||
|
||||
var fullnametemp = valueType.ToString();
|
||||
if (fullnametemp.StartsWith("Il2CppSystem"))
|
||||
fullnametemp = fullnametemp.Substring(6, fullnametemp.Length - 6);
|
||||
m_toStringMethod = valueType.GetMethod("ToString", new Type[0]);
|
||||
m_toStringFormatMethod = valueType.GetMethod("ToString", new Type[] { typeof(string) });
|
||||
|
||||
var temp = toString.Replace(fullnametemp, "").Trim();
|
||||
// test format method actually works
|
||||
try
|
||||
{
|
||||
m_toStringFormatMethod.Invoke(Value, new object[] { "F3" });
|
||||
}
|
||||
catch
|
||||
{
|
||||
m_toStringFormatMethod = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(temp))
|
||||
string toString;
|
||||
if (m_toStringFormatMethod != null)
|
||||
toString = (string)m_toStringFormatMethod.Invoke(Value, new object[] { "F3" });
|
||||
else
|
||||
toString = (string)m_toStringMethod.Invoke(Value, new object[0]);
|
||||
|
||||
toString = toString ?? "";
|
||||
|
||||
string typeName = valueType.FullName;
|
||||
if (typeName.StartsWith("Il2CppSystem."))
|
||||
typeName = typeName.Substring(6, typeName.Length - 6);
|
||||
|
||||
toString = ReflectionProvider.Instance.ProcessTypeNameInString(valueType, toString, ref typeName);
|
||||
|
||||
// If the ToString is just the type name, use our syntax highlighted type name instead.
|
||||
if (toString == typeName)
|
||||
{
|
||||
label = m_richValueType;
|
||||
}
|
||||
else
|
||||
else // Otherwise, parse the result and put our highlighted name in.
|
||||
{
|
||||
if (toString.Length > 200)
|
||||
toString = toString.Substring(0, 200) + "...";
|
||||
@ -241,7 +295,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
internal GameObject m_mainContentParent;
|
||||
internal GameObject m_subContentParent;
|
||||
|
||||
internal GameObject m_valueContent;
|
||||
internal GameObject m_mainContent;
|
||||
internal GameObject m_inspectButton;
|
||||
internal Text m_baseLabel;
|
||||
|
||||
@ -252,69 +306,42 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
m_UIConstructed = true;
|
||||
|
||||
m_valueContent = UIFactory.CreateHorizontalGroup(parent, new Color(1, 1, 1, 0));
|
||||
m_valueContent.name = "InteractiveValue.ValueContent";
|
||||
var mainRect = m_valueContent.GetComponent<RectTransform>();
|
||||
mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
|
||||
var mainGroup = m_valueContent.GetComponent<HorizontalLayoutGroup>();
|
||||
mainGroup.childForceExpandWidth = false;
|
||||
mainGroup.childControlWidth = true;
|
||||
mainGroup.childForceExpandHeight = false;
|
||||
mainGroup.childControlHeight = true;
|
||||
mainGroup.spacing = 4;
|
||||
mainGroup.childAlignment = TextAnchor.UpperLeft;
|
||||
var mainLayout = m_valueContent.AddComponent<LayoutElement>();
|
||||
mainLayout.flexibleWidth = 9000;
|
||||
mainLayout.minWidth = 175;
|
||||
mainLayout.minHeight = 25;
|
||||
mainLayout.flexibleHeight = 0;
|
||||
m_mainContent = UIFactory.CreateHorizontalGroup(parent, $"InteractiveValue_{this.GetType().Name}", false, false, true, true, 4, default,
|
||||
new Color(1, 1, 1, 0), TextAnchor.UpperLeft);
|
||||
|
||||
// subcontent expand button TODO
|
||||
var mainRect = m_mainContent.GetComponent<RectTransform>();
|
||||
mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
|
||||
|
||||
UIFactory.SetLayoutElement(m_mainContent, flexibleWidth: 9000, minWidth: 175, minHeight: 25, flexibleHeight: 0);
|
||||
|
||||
// subcontent expand button
|
||||
if (HasSubContent)
|
||||
{
|
||||
var subBtnObj = UIFactory.CreateButton(m_valueContent, new Color(0.3f, 0.3f, 0.3f));
|
||||
var btnLayout = subBtnObj.AddComponent<LayoutElement>();
|
||||
btnLayout.minHeight = 25;
|
||||
btnLayout.minWidth = 25;
|
||||
btnLayout.flexibleWidth = 0;
|
||||
btnLayout.flexibleHeight = 0;
|
||||
var btnText = subBtnObj.GetComponentInChildren<Text>();
|
||||
btnText.text = "▲";
|
||||
m_subExpandBtn = subBtnObj.GetComponent<Button>();
|
||||
m_subExpandBtn.onClick.AddListener(() =>
|
||||
{
|
||||
ToggleSubcontent();
|
||||
});
|
||||
m_subExpandBtn = UIFactory.CreateButton(m_mainContent, "ExpandSubcontentButton", "▲", ToggleSubcontent, new Color(0.3f, 0.3f, 0.3f));
|
||||
UIFactory.SetLayoutElement(m_subExpandBtn.gameObject, minHeight: 25, minWidth: 25, flexibleWidth: 0, flexibleHeight: 0);
|
||||
}
|
||||
|
||||
// inspect button
|
||||
|
||||
m_inspectButton = UIFactory.CreateButton(m_valueContent, new Color(0.3f, 0.3f, 0.3f, 0.2f));
|
||||
var inspectLayout = m_inspectButton.AddComponent<LayoutElement>();
|
||||
inspectLayout.minWidth = 60;
|
||||
inspectLayout.minHeight = 25;
|
||||
inspectLayout.flexibleHeight = 0;
|
||||
inspectLayout.flexibleWidth = 0;
|
||||
var inspectText = m_inspectButton.GetComponentInChildren<Text>();
|
||||
inspectText.text = "Inspect";
|
||||
var inspectBtn = m_inspectButton.GetComponent<Button>();
|
||||
var inspectBtn = UIFactory.CreateButton(m_mainContent,
|
||||
"InspectButton",
|
||||
"Inspect",
|
||||
() =>
|
||||
{
|
||||
if (!Value.IsNullOrDestroyed(false))
|
||||
InspectorManager.Instance.Inspect(this.Value, this.Owner);
|
||||
},
|
||||
new Color(0.3f, 0.3f, 0.3f, 0.2f));
|
||||
|
||||
inspectBtn.onClick.AddListener(OnInspectClicked);
|
||||
void OnInspectClicked()
|
||||
{
|
||||
if (!Value.IsNullOrDestroyed(false))
|
||||
InspectorManager.Instance.Inspect(this.Value);
|
||||
}
|
||||
m_inspectButton = inspectBtn.gameObject;
|
||||
UIFactory.SetLayoutElement(m_inspectButton, minWidth: 60, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
|
||||
|
||||
m_inspectButton.SetActive(false);
|
||||
|
||||
// value label
|
||||
|
||||
var labelObj = UIFactory.CreateLabel(m_valueContent, TextAnchor.MiddleLeft);
|
||||
m_baseLabel = labelObj.GetComponent<Text>();
|
||||
var labelLayout = labelObj.AddComponent<LayoutElement>();
|
||||
labelLayout.flexibleWidth = 9000;
|
||||
labelLayout.minHeight = 25;
|
||||
m_baseLabel = UIFactory.CreateLabel(m_mainContent, "ValueLabel", "", TextAnchor.MiddleLeft);
|
||||
UIFactory.SetLayoutElement(m_baseLabel.gameObject, flexibleWidth: 9000, minHeight: 25);
|
||||
|
||||
m_subContentParent = subGroup;
|
||||
}
|
27
src/UI/Main/BaseMenuPage.cs
Normal file
27
src/UI/Main/BaseMenuPage.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UnityExplorer.UI.Main
|
||||
{
|
||||
public abstract class BaseMenuPage
|
||||
{
|
||||
public abstract string Name { get; }
|
||||
|
||||
public GameObject Content;
|
||||
public Button RefNavbarButton { get; set; }
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => Content?.activeSelf ?? false;
|
||||
set => Content?.SetActive(true);
|
||||
}
|
||||
|
||||
|
||||
public abstract bool Init();
|
||||
public abstract void Update();
|
||||
}
|
||||
}
|
@ -4,11 +4,14 @@ using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.Core.CSharp;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Modules;
|
||||
using UnityExplorer.UI.Main;
|
||||
|
||||
namespace UnityExplorer.CSConsole
|
||||
namespace UnityExplorer.UI.Main.CSConsole
|
||||
{
|
||||
public class AutoCompleter
|
||||
{
|
||||
@ -18,7 +21,6 @@ namespace UnityExplorer.CSConsole
|
||||
private const int UPDATES_PER_BATCH = 100;
|
||||
|
||||
public static GameObject m_mainObj;
|
||||
//private static RectTransform m_thisRect;
|
||||
|
||||
private static readonly List<GameObject> m_suggestionButtons = new List<GameObject>();
|
||||
private static readonly List<Text> m_suggestionTexts = new List<Text>();
|
||||
@ -41,16 +43,12 @@ namespace UnityExplorer.CSConsole
|
||||
public static void Update()
|
||||
{
|
||||
if (!m_mainObj)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CodeEditor.EnableAutocompletes)
|
||||
if (!CSharpConsole.EnableAutocompletes)
|
||||
{
|
||||
if (m_mainObj.activeSelf)
|
||||
{
|
||||
m_mainObj.SetActive(false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@ -132,11 +130,11 @@ namespace UnityExplorer.CSConsole
|
||||
{
|
||||
try
|
||||
{
|
||||
var editor = CSConsolePage.Instance.m_codeEditor;
|
||||
var editor = CSharpConsole.Instance;
|
||||
|
||||
if (!editor.InputField.isFocused)
|
||||
return;
|
||||
|
||||
|
||||
var textGen = editor.InputText.cachedTextGenerator;
|
||||
int caretPos = editor.m_lastCaretPos;
|
||||
|
||||
@ -164,7 +162,7 @@ namespace UnityExplorer.CSConsole
|
||||
|
||||
public static void CheckAutocomplete()
|
||||
{
|
||||
var m_codeEditor = CSConsolePage.Instance.m_codeEditor;
|
||||
var m_codeEditor = CSharpConsole.Instance;
|
||||
string input = m_codeEditor.InputField.text;
|
||||
int caretIndex = m_codeEditor.InputField.caretPosition;
|
||||
|
||||
@ -192,9 +190,9 @@ namespace UnityExplorer.CSConsole
|
||||
|
||||
public static void ClearAutocompletes()
|
||||
{
|
||||
if (CodeEditor.AutoCompletes.Any())
|
||||
if (CSharpConsole.AutoCompletes.Any())
|
||||
{
|
||||
CodeEditor.AutoCompletes.Clear();
|
||||
CSharpConsole.AutoCompletes.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,8 +201,8 @@ namespace UnityExplorer.CSConsole
|
||||
try
|
||||
{
|
||||
// Credit ManylMarco
|
||||
CodeEditor.AutoCompletes.Clear();
|
||||
string[] completions = CSConsolePage.Instance.m_evaluator.GetCompletions(input, out string prefix);
|
||||
CSharpConsole.AutoCompletes.Clear();
|
||||
string[] completions = CSharpConsole.Instance.Evaluator.GetCompletions(input, out string prefix);
|
||||
if (completions != null)
|
||||
{
|
||||
if (prefix == null)
|
||||
@ -212,7 +210,7 @@ namespace UnityExplorer.CSConsole
|
||||
prefix = input;
|
||||
}
|
||||
|
||||
CodeEditor.AutoCompletes.AddRange(completions
|
||||
CSharpConsole.AutoCompletes.AddRange(completions
|
||||
.Where(x => !string.IsNullOrEmpty(x))
|
||||
.Select(x => new Suggestion(x, prefix, Suggestion.Contexts.Other))
|
||||
);
|
||||
@ -231,7 +229,7 @@ namespace UnityExplorer.CSConsole
|
||||
x.Substring(0, trimmed.Length),
|
||||
Suggestion.Contexts.Namespace));
|
||||
|
||||
CodeEditor.AutoCompletes.AddRange(namespaces);
|
||||
CSharpConsole.AutoCompletes.AddRange(namespaces);
|
||||
|
||||
IEnumerable<Suggestion> keywords = Suggestion.Keywords
|
||||
.Where(x => x.StartsWith(trimmed) && x.Length > trimmed.Length)
|
||||
@ -240,7 +238,7 @@ namespace UnityExplorer.CSConsole
|
||||
x.Substring(0, trimmed.Length),
|
||||
Suggestion.Contexts.Keyword));
|
||||
|
||||
CodeEditor.AutoCompletes.AddRange(keywords);
|
||||
CSharpConsole.AutoCompletes.AddRange(keywords);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -255,7 +253,7 @@ namespace UnityExplorer.CSConsole
|
||||
{
|
||||
var parent = UIManager.CanvasRoot;
|
||||
|
||||
var obj = UIFactory.CreateScrollView(parent, out GameObject content, out _, new Color(0.1f, 0.1f, 0.1f, 0.95f));
|
||||
var obj = UIFactory.CreateScrollView(parent, "AutoCompleterScrollView", out GameObject content, out _, new Color(0.1f, 0.1f, 0.1f, 0.95f));
|
||||
|
||||
m_mainObj = obj;
|
||||
|
||||
@ -268,43 +266,35 @@ namespace UnityExplorer.CSConsole
|
||||
mainRect.offsetMax = Vector2.zero;
|
||||
|
||||
var mainGroup = content.GetComponent<VerticalLayoutGroup>();
|
||||
mainGroup.childControlHeight = false;
|
||||
mainGroup.childControlWidth = true;
|
||||
mainGroup.SetChildControlHeight(false);
|
||||
mainGroup.SetChildControlWidth(true);
|
||||
mainGroup.childForceExpandHeight = false;
|
||||
mainGroup.childForceExpandWidth = true;
|
||||
|
||||
ColorBlock btnColors = new ColorBlock();
|
||||
RuntimeProvider.Instance.SetColorBlock(btnColors, new Color(0, 0, 0, 0), highlighted: new Color(0.2f, 0.2f, 0.2f, 1.0f));
|
||||
|
||||
for (int i = 0; i < MAX_LABELS; i++)
|
||||
{
|
||||
var buttonObj = UIFactory.CreateButton(content);
|
||||
Button btn = buttonObj.GetComponent<Button>();
|
||||
ColorBlock btnColors = btn.colors;
|
||||
btnColors.normalColor = new Color(0f, 0f, 0f, 0f);
|
||||
btnColors.highlightedColor = new Color(0.2f, 0.2f, 0.2f, 1.0f);
|
||||
btn.colors = btnColors;
|
||||
var btn = UIFactory.CreateButton(content, "AutoCompleteButton", "", null, btnColors);
|
||||
|
||||
var nav = btn.navigation;
|
||||
nav.mode = Navigation.Mode.Vertical;
|
||||
btn.navigation = nav;
|
||||
|
||||
var btnLayout = buttonObj.AddComponent<LayoutElement>();
|
||||
btnLayout.minHeight = 20;
|
||||
UIFactory.SetLayoutElement(btn.gameObject, minHeight: 20);
|
||||
|
||||
var text = btn.GetComponentInChildren<Text>();
|
||||
text.alignment = TextAnchor.MiddleLeft;
|
||||
text.color = Color.white;
|
||||
|
||||
var hiddenChild = UIFactory.CreateUIObject("HiddenText", buttonObj);
|
||||
var hiddenChild = UIFactory.CreateUIObject("HiddenText", btn.gameObject);
|
||||
hiddenChild.SetActive(false);
|
||||
var hiddenText = hiddenChild.AddComponent<Text>();
|
||||
m_hiddenSuggestionTexts.Add(hiddenText);
|
||||
btn.onClick.AddListener(UseAutocompleteButton);
|
||||
btn.onClick.AddListener(() => { CSharpConsole.Instance.UseAutocomplete(hiddenText.text); });
|
||||
|
||||
void UseAutocompleteButton()
|
||||
{
|
||||
CSConsolePage.Instance.m_codeEditor.UseAutocomplete(hiddenText.text);
|
||||
}
|
||||
|
||||
m_suggestionButtons.Add(buttonObj);
|
||||
m_suggestionButtons.Add(btn.gameObject);
|
||||
m_suggestionTexts.Add(text);
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.CSConsole.Lexer;
|
||||
using UnityExplorer.UI.Main.CSConsole.Lexer;
|
||||
|
||||
namespace UnityExplorer.CSConsole
|
||||
namespace UnityExplorer.UI.Main.CSConsole
|
||||
{
|
||||
public struct LexerMatchInfo
|
||||
{
|
||||
@ -18,7 +18,7 @@ namespace UnityExplorer.CSConsole
|
||||
End,
|
||||
};
|
||||
|
||||
public class CSharpLexer
|
||||
public class CSLexerHighlighter
|
||||
{
|
||||
private string inputString;
|
||||
private readonly Matcher[] matchers;
|
||||
@ -40,33 +40,16 @@ namespace UnityExplorer.CSConsole
|
||||
{
|
||||
'[', ']', '(', ')', '{', '}', ';', ':', ',', '.'
|
||||
};
|
||||
|
||||
public static CommentMatch commentMatcher = new CommentMatch();
|
||||
public static SymbolMatch symbolMatcher = new SymbolMatch();
|
||||
public static NumberMatch numberMatcher = new NumberMatch();
|
||||
public static StringMatch stringMatcher = new StringMatch();
|
||||
|
||||
public static KeywordMatch validKeywordMatcher = new KeywordMatch
|
||||
{
|
||||
highlightColor = new Color(0.33f, 0.61f, 0.83f, 1.0f),
|
||||
Keywords = new[] { "add", "as", "ascending", "await", "bool", "break", "by", "byte",
|
||||
"case", "catch", "char", "checked", "const", "continue", "decimal", "default", "descending", "do", "dynamic",
|
||||
"else", "equals", "false", "finally", "float", "for", "foreach", "from", "global", "goto", "group",
|
||||
"if", "in", "int", "into", "is", "join", "let", "lock", "long", "new", "null", "object", "on", "orderby", "out",
|
||||
"ref", "remove", "return", "sbyte", "select", "short", "sizeof", "stackalloc", "string",
|
||||
"switch", "throw", "true", "try", "typeof", "uint", "ulong", "ushort", "var", "where", "while", "yield" }
|
||||
};
|
||||
|
||||
public static KeywordMatch invalidKeywordMatcher = new KeywordMatch()
|
||||
{
|
||||
highlightColor = new Color(0.95f, 0.10f, 0.10f, 1.0f),
|
||||
Keywords = new[] { "abstract", "async", "base", "class", "delegate", "enum", "explicit", "extern", "fixed", "get",
|
||||
"implicit", "interface", "internal", "namespace", "operator", "override", "params", "private", "protected", "public",
|
||||
"using", "partial", "readonly", "sealed", "set", "static", "struct", "this", "unchecked", "unsafe", "value", "virtual", "volatile", "void" }
|
||||
};
|
||||
public static KeywordMatch validKeywordMatcher = new KeywordMatch();
|
||||
|
||||
// ~~~~~~~ ctor ~~~~~~~
|
||||
|
||||
public CSharpLexer()
|
||||
public CSLexerHighlighter()
|
||||
{
|
||||
startDelimiters = new HashSet<char>(delimiters);
|
||||
endDelimiters = new HashSet<char>(delimiters);
|
||||
@ -78,7 +61,6 @@ namespace UnityExplorer.CSConsole
|
||||
numberMatcher,
|
||||
stringMatcher,
|
||||
validKeywordMatcher,
|
||||
invalidKeywordMatcher,
|
||||
};
|
||||
|
||||
foreach (Matcher lexer in matchers)
|
@ -1,24 +1,153 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityExplorer.Input;
|
||||
using UnityExplorer.CSConsole.Lexer;
|
||||
using UnityExplorer.Core.CSharp;
|
||||
using System.Linq;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Modules;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityExplorer.UI.Shared;
|
||||
using UnityExplorer.Helpers;
|
||||
using UnityExplorer.UI.Main.CSConsole;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.UI.Utility;
|
||||
#if CPP
|
||||
using UnityExplorer.Core.Runtime.Il2Cpp;
|
||||
#endif
|
||||
|
||||
namespace UnityExplorer.CSConsole
|
||||
namespace UnityExplorer.UI.Main.CSConsole
|
||||
{
|
||||
// Handles most of the UI side of the C# console, including syntax highlighting.
|
||||
|
||||
public class CodeEditor
|
||||
public class CSharpConsole : BaseMenuPage
|
||||
{
|
||||
public override string Name => "C# Console";
|
||||
|
||||
public static CSharpConsole Instance { get; private set; }
|
||||
|
||||
public ScriptEvaluator Evaluator;
|
||||
internal StringBuilder m_evalLogBuilder;
|
||||
|
||||
public static List<string> UsingDirectives;
|
||||
|
||||
public static readonly string[] DefaultUsing = new string[]
|
||||
{
|
||||
"System",
|
||||
"System.Linq",
|
||||
"System.Collections",
|
||||
"System.Collections.Generic",
|
||||
"System.Reflection",
|
||||
"UnityEngine",
|
||||
#if CPP
|
||||
"UnhollowerBaseLib",
|
||||
"UnhollowerRuntimeLib",
|
||||
#endif
|
||||
};
|
||||
|
||||
public override bool Init()
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
try
|
||||
{
|
||||
InitConsole();
|
||||
|
||||
AutoCompleter.Init();
|
||||
#if MONO
|
||||
DummyBehaviour.Setup();
|
||||
#endif
|
||||
|
||||
ResetConsole(false);
|
||||
// Make sure compiler is supported on this platform
|
||||
Evaluator.Compile("");
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string info = "The C# Console has been disabled because";
|
||||
if (e is NotSupportedException && e.TargetSite?.Name == "DefineDynamicAssembly")
|
||||
info += " Reflection.Emit is not supported.";
|
||||
else
|
||||
info += $" of an unknown error.\r\n({e.ReflectionExToString()})";
|
||||
|
||||
ExplorerCore.LogWarning(info);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetConsole(bool log = true)
|
||||
{
|
||||
if (Evaluator != null)
|
||||
Evaluator.Dispose();
|
||||
|
||||
m_evalLogBuilder = new StringBuilder();
|
||||
|
||||
Evaluator = new ScriptEvaluator(new StringWriter(m_evalLogBuilder)) { InteractiveBaseClass = typeof(ScriptInteraction) };
|
||||
|
||||
UsingDirectives = new List<string>();
|
||||
|
||||
foreach (string use in DefaultUsing)
|
||||
AddUsing(use);
|
||||
|
||||
if (log)
|
||||
ExplorerCore.Log($"C# Console reset. Using directives:\r\n{Evaluator.GetUsing()}");
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
UpdateConsole();
|
||||
|
||||
AutoCompleter.Update();
|
||||
#if CPP
|
||||
Il2CppCoroutine.Process();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void AddUsing(string asm)
|
||||
{
|
||||
if (!UsingDirectives.Contains(asm))
|
||||
{
|
||||
Evaluate($"using {asm};", true);
|
||||
UsingDirectives.Add(asm);
|
||||
}
|
||||
}
|
||||
|
||||
public void Evaluate(string code, bool supressLog = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
Evaluator.Run(code);
|
||||
|
||||
string output = ScriptEvaluator._textWriter.ToString();
|
||||
var outputSplit = output.Split('\n');
|
||||
if (outputSplit.Length >= 2)
|
||||
output = outputSplit[outputSplit.Length - 2];
|
||||
m_evalLogBuilder.Clear();
|
||||
|
||||
if (ScriptEvaluator._reportPrinter.ErrorsCount > 0)
|
||||
throw new FormatException($"Unable to compile the code. Evaluator's last output was:\r\n{output}");
|
||||
|
||||
if (!supressLog)
|
||||
ExplorerCore.Log("Code executed successfully.");
|
||||
}
|
||||
catch (FormatException fex)
|
||||
{
|
||||
if (!supressLog)
|
||||
ExplorerCore.LogWarning(fex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!supressLog)
|
||||
ExplorerCore.LogWarning(ex);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================================================
|
||||
|
||||
// UI stuff
|
||||
|
||||
public InputField InputField { get; internal set; }
|
||||
public Text InputText { get; internal set; }
|
||||
public int CurrentIndent { get; private set; }
|
||||
@ -31,8 +160,7 @@ namespace UnityExplorer.CSConsole
|
||||
public string HighlightedText => inputHighlightText.text;
|
||||
private Text inputHighlightText;
|
||||
|
||||
private readonly CSharpLexer highlightLexer;
|
||||
private readonly StringBuilder sbHighlight;
|
||||
private CSLexerHighlighter highlightLexer;
|
||||
|
||||
internal int m_lastCaretPos;
|
||||
internal int m_fixCaretPos;
|
||||
@ -51,6 +179,8 @@ The following helper methods are available:
|
||||
|
||||
* <color=#add490>Log(""message"")</color> logs a message to the debug console
|
||||
|
||||
* <color=#add490>StartCoroutine(IEnumerator routine)</color> start the IEnumerator as a UnityEngine.Coroutine
|
||||
|
||||
* <color=#add490>CurrentTarget()</color> returns the currently inspected target on the Home page
|
||||
|
||||
* <color=#add490>AllTargets()</color> returns an object[] array containing all inspected instances
|
||||
@ -66,18 +196,33 @@ The following helper methods are available:
|
||||
* <color=#add490>Reset()</color> resets all using directives and variables
|
||||
";
|
||||
|
||||
public CodeEditor()
|
||||
public void InitConsole()
|
||||
{
|
||||
sbHighlight = new StringBuilder();
|
||||
highlightLexer = new CSharpLexer();
|
||||
highlightLexer = new CSLexerHighlighter();
|
||||
|
||||
ConstructUI();
|
||||
|
||||
InputField.onValueChanged.AddListener((string s) => { OnInputChanged(s); });
|
||||
}
|
||||
|
||||
public void Update()
|
||||
internal static bool IsUserCopyPasting()
|
||||
{
|
||||
return (InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
||||
&& InputManager.GetKeyDown(KeyCode.V);
|
||||
}
|
||||
|
||||
public void UpdateConsole()
|
||||
{
|
||||
if (s_copyPasteBuffer != null)
|
||||
{
|
||||
if (!IsUserCopyPasting())
|
||||
{
|
||||
OnInputChanged(s_copyPasteBuffer);
|
||||
|
||||
s_copyPasteBuffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (EnableCtrlRShortcut)
|
||||
{
|
||||
if ((InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
||||
@ -86,7 +231,7 @@ The following helper methods are available:
|
||||
var text = InputField.text.Trim();
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
CSConsolePage.Instance.Evaluate(text);
|
||||
Evaluate(text);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -106,7 +251,7 @@ The following helper methods are available:
|
||||
if (!m_fixwanted)
|
||||
{
|
||||
EventSystem.current.SetSelectedGameObject(InputField.gameObject, null);
|
||||
m_fixwanted = true;
|
||||
m_fixwanted = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -149,18 +294,27 @@ The following helper methods are available:
|
||||
AutoCompleter.ClearAutocompletes();
|
||||
}
|
||||
|
||||
public void OnInputChanged(string newInput, bool forceUpdate = false)
|
||||
{
|
||||
string newText = newInput;
|
||||
internal static string s_copyPasteBuffer;
|
||||
|
||||
UpdateIndent(newInput);
|
||||
public void OnInputChanged(string newText, bool forceUpdate = false)
|
||||
{
|
||||
if (IsUserCopyPasting())
|
||||
{
|
||||
//Console.WriteLine("Copy+Paste detected!");
|
||||
s_copyPasteBuffer = newText;
|
||||
return;
|
||||
}
|
||||
|
||||
if (EnableAutoIndent)
|
||||
UpdateIndent(newText);
|
||||
|
||||
if (!forceUpdate && string.IsNullOrEmpty(newText))
|
||||
inputHighlightText.text = string.Empty;
|
||||
else
|
||||
inputHighlightText.text = SyntaxHighlightContent(newText);
|
||||
|
||||
UpdateAutocompletes();
|
||||
if (EnableAutocompletes)
|
||||
UpdateAutocompletes();
|
||||
}
|
||||
|
||||
private void UpdateIndent(string newText)
|
||||
@ -187,9 +341,9 @@ The following helper methods are available:
|
||||
|
||||
if (character == '"')
|
||||
stringState = !stringState;
|
||||
else if (!stringState && character == CSharpLexer.indentOpen)
|
||||
else if (!stringState && character == CSLexerHighlighter.indentOpen)
|
||||
CurrentIndent++;
|
||||
else if (!stringState && character == CSharpLexer.indentClose)
|
||||
else if (!stringState && character == CSLexerHighlighter.indentClose)
|
||||
CurrentIndent--;
|
||||
}
|
||||
|
||||
@ -203,35 +357,29 @@ The following helper methods are available:
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
sbHighlight.Length = 0;
|
||||
//Console.WriteLine("Highlighting input text:\r\n" + inputText);
|
||||
|
||||
foreach (LexerMatchInfo match in highlightLexer.GetMatches(inputText))
|
||||
string ret = "";
|
||||
|
||||
foreach (var match in highlightLexer.GetMatches(inputText))
|
||||
{
|
||||
for (int i = offset; i < match.startIndex; i++)
|
||||
{
|
||||
sbHighlight.Append(inputText[i]);
|
||||
}
|
||||
ret += inputText[i];
|
||||
|
||||
sbHighlight.Append($"{match.htmlColor}");
|
||||
ret += $"{match.htmlColor}";
|
||||
|
||||
for (int i = match.startIndex; i < match.endIndex; i++)
|
||||
{
|
||||
sbHighlight.Append(inputText[i]);
|
||||
}
|
||||
ret += inputText[i];
|
||||
|
||||
sbHighlight.Append(CLOSE_COLOR_TAG);
|
||||
ret += CLOSE_COLOR_TAG;
|
||||
|
||||
offset = match.endIndex;
|
||||
}
|
||||
|
||||
for (int i = offset; i < inputText.Length; i++)
|
||||
{
|
||||
sbHighlight.Append(inputText[i]);
|
||||
}
|
||||
ret += inputText[i];
|
||||
|
||||
inputText = sbHighlight.ToString();
|
||||
|
||||
return inputText;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void AutoIndentCaret()
|
||||
@ -262,8 +410,8 @@ The following helper methods are available:
|
||||
}
|
||||
|
||||
// check if should add auto-close }
|
||||
int numOpen = InputField.text.Where(x => x == CSharpLexer.indentOpen).Count();
|
||||
int numClose = InputField.text.Where(x => x == CSharpLexer.indentClose).Count();
|
||||
int numOpen = InputField.text.Where(x => x == CSLexerHighlighter.indentOpen).Count();
|
||||
int numClose = InputField.text.Where(x => x == CSLexerHighlighter.indentClose).Count();
|
||||
|
||||
if (numOpen > numClose)
|
||||
{
|
||||
@ -308,93 +456,54 @@ The following helper methods are available:
|
||||
|
||||
public void ConstructUI()
|
||||
{
|
||||
CSConsolePage.Instance.Content = UIFactory.CreateUIObject("C# Console", MainMenu.Instance.PageViewport);
|
||||
|
||||
var mainLayout = CSConsolePage.Instance.Content.AddComponent<LayoutElement>();
|
||||
mainLayout.preferredHeight = 500;
|
||||
mainLayout.flexibleHeight = 9000;
|
||||
|
||||
var mainGroup = CSConsolePage.Instance.Content.AddComponent<VerticalLayoutGroup>();
|
||||
mainGroup.childControlHeight = true;
|
||||
mainGroup.childControlWidth = true;
|
||||
mainGroup.childForceExpandHeight = true;
|
||||
mainGroup.childForceExpandWidth = true;
|
||||
Content = UIFactory.CreateVerticalGroup(MainMenu.Instance.PageViewport, "CSharpConsole", true, true, true, true);
|
||||
UIFactory.SetLayoutElement(Content, preferredHeight: 500, flexibleHeight: 9000);
|
||||
|
||||
#region TOP BAR
|
||||
|
||||
// Main group object
|
||||
|
||||
var topBarObj = UIFactory.CreateHorizontalGroup(CSConsolePage.Instance.Content);
|
||||
LayoutElement topBarLayout = topBarObj.AddComponent<LayoutElement>();
|
||||
topBarLayout.minHeight = 50;
|
||||
topBarLayout.flexibleHeight = 0;
|
||||
var topBarObj = UIFactory.CreateHorizontalGroup(Content, "TopBar", true, true, true, true, 10, new Vector4(8, 8, 30, 30),
|
||||
default, TextAnchor.LowerCenter);
|
||||
UIFactory.SetLayoutElement(topBarObj, minHeight: 50, flexibleHeight: 0);
|
||||
|
||||
var topBarGroup = topBarObj.GetComponent<HorizontalLayoutGroup>();
|
||||
topBarGroup.padding.left = 30;
|
||||
topBarGroup.padding.right = 30;
|
||||
topBarGroup.padding.top = 8;
|
||||
topBarGroup.padding.bottom = 8;
|
||||
topBarGroup.spacing = 10;
|
||||
topBarGroup.childForceExpandHeight = true;
|
||||
topBarGroup.childForceExpandWidth = true;
|
||||
topBarGroup.childControlWidth = true;
|
||||
topBarGroup.childControlHeight = true;
|
||||
topBarGroup.childAlignment = TextAnchor.LowerCenter;
|
||||
// Top label
|
||||
|
||||
var topBarLabel = UIFactory.CreateLabel(topBarObj, TextAnchor.MiddleLeft);
|
||||
var topBarLabelLayout = topBarLabel.AddComponent<LayoutElement>();
|
||||
topBarLabelLayout.preferredWidth = 150;
|
||||
topBarLabelLayout.flexibleWidth = 5000;
|
||||
var topBarText = topBarLabel.GetComponent<Text>();
|
||||
topBarText.text = "C# Console";
|
||||
topBarText.fontSize = 20;
|
||||
var topBarLabel = UIFactory.CreateLabel(topBarObj, "TopLabel", "C# Console", TextAnchor.MiddleLeft, default, true, 25);
|
||||
UIFactory.SetLayoutElement(topBarLabel.gameObject, preferredWidth: 150, flexibleWidth: 5000);
|
||||
|
||||
// Enable Ctrl+R toggle
|
||||
|
||||
var ctrlRToggleObj = UIFactory.CreateToggle(topBarObj, out Toggle ctrlRToggle, out Text ctrlRToggleText);
|
||||
ctrlRToggle.onValueChanged.AddListener(CtrlRToggleCallback);
|
||||
void CtrlRToggleCallback(bool val)
|
||||
{
|
||||
EnableCtrlRShortcut = val;
|
||||
}
|
||||
var ctrlRToggleObj = UIFactory.CreateToggle(topBarObj, "CtrlRToggle", out Toggle ctrlRToggle, out Text ctrlRToggleText);
|
||||
ctrlRToggle.onValueChanged.AddListener((bool val) => { EnableCtrlRShortcut = val; });
|
||||
|
||||
ctrlRToggleText.text = "Run on Ctrl+R";
|
||||
ctrlRToggleText.alignment = TextAnchor.UpperLeft;
|
||||
var ctrlRLayout = ctrlRToggleObj.AddComponent<LayoutElement>();
|
||||
ctrlRLayout.minWidth = 140;
|
||||
ctrlRLayout.flexibleWidth = 0;
|
||||
ctrlRLayout.minHeight = 25;
|
||||
UIFactory.SetLayoutElement(ctrlRToggleObj, minWidth: 140, flexibleWidth: 0, minHeight: 25);
|
||||
|
||||
// Enable Suggestions toggle
|
||||
|
||||
var suggestToggleObj = UIFactory.CreateToggle(topBarObj, out Toggle suggestToggle, out Text suggestToggleText);
|
||||
suggestToggle.onValueChanged.AddListener(SuggestToggleCallback);
|
||||
void SuggestToggleCallback(bool val)
|
||||
var suggestToggleObj = UIFactory.CreateToggle(topBarObj, "SuggestionToggle", out Toggle suggestToggle, out Text suggestToggleText);
|
||||
suggestToggle.onValueChanged.AddListener((bool val) =>
|
||||
{
|
||||
EnableAutocompletes = val;
|
||||
AutoCompleter.Update();
|
||||
}
|
||||
});
|
||||
|
||||
suggestToggleText.text = "Suggestions";
|
||||
suggestToggleText.alignment = TextAnchor.UpperLeft;
|
||||
var suggestLayout = suggestToggleObj.AddComponent<LayoutElement>();
|
||||
suggestLayout.minWidth = 120;
|
||||
suggestLayout.flexibleWidth = 0;
|
||||
suggestLayout.minHeight = 25;
|
||||
|
||||
UIFactory.SetLayoutElement(suggestToggleObj, minWidth: 120, flexibleWidth: 0, minHeight: 25);
|
||||
|
||||
// Enable Auto-indent toggle
|
||||
|
||||
var autoIndentToggleObj = UIFactory.CreateToggle(topBarObj, out Toggle autoIndentToggle, out Text autoIndentToggleText);
|
||||
autoIndentToggle.onValueChanged.AddListener(OnIndentChanged);
|
||||
void OnIndentChanged(bool val) => EnableAutoIndent = val;
|
||||
var autoIndentToggleObj = UIFactory.CreateToggle(topBarObj, "IndentToggle", out Toggle autoIndentToggle, out Text autoIndentToggleText);
|
||||
autoIndentToggle.onValueChanged.AddListener((bool val) => EnableAutoIndent = val);
|
||||
|
||||
autoIndentToggleText.text = "Auto-indent on Enter";
|
||||
autoIndentToggleText.alignment = TextAnchor.UpperLeft;
|
||||
|
||||
var autoIndentLayout = autoIndentToggleObj.AddComponent<LayoutElement>();
|
||||
autoIndentLayout.minWidth = 180;
|
||||
autoIndentLayout.flexibleWidth = 0;
|
||||
autoIndentLayout.minHeight = 25;
|
||||
UIFactory.SetLayoutElement(autoIndentToggleObj, minWidth: 180, flexibleWidth: 0, minHeight: 25);
|
||||
|
||||
#endregion
|
||||
|
||||
@ -402,7 +511,7 @@ The following helper methods are available:
|
||||
|
||||
int fontSize = 16;
|
||||
|
||||
var inputObj = UIFactory.CreateSrollInputField(CSConsolePage.Instance.Content, out InputFieldScroller consoleScroll, fontSize);
|
||||
var inputObj = UIFactory.CreateSrollInputField(Content, "ConsoleInput", STARTUP_TEXT, out InputFieldScroller consoleScroll, fontSize);
|
||||
|
||||
var inputField = consoleScroll.inputField;
|
||||
|
||||
@ -412,7 +521,6 @@ The following helper methods are available:
|
||||
mainTextInput.color = new Color(1, 1, 1, 0.5f);
|
||||
|
||||
var placeHolderText = inputField.placeholder.GetComponent<Text>();
|
||||
placeHolderText.text = STARTUP_TEXT;
|
||||
placeHolderText.fontSize = fontSize;
|
||||
|
||||
var highlightTextObj = UIFactory.CreateUIObject("HighlightText", mainTextObj.gameObject);
|
||||
@ -429,31 +537,28 @@ The following helper methods are available:
|
||||
|
||||
#endregion
|
||||
|
||||
#region COMPILE BUTTON
|
||||
#region COMPILE BUTTON BAR
|
||||
|
||||
var compileBtnObj = UIFactory.CreateButton(CSConsolePage.Instance.Content);
|
||||
var compileBtnLayout = compileBtnObj.AddComponent<LayoutElement>();
|
||||
compileBtnLayout.preferredWidth = 80;
|
||||
compileBtnLayout.flexibleWidth = 0;
|
||||
compileBtnLayout.minHeight = 45;
|
||||
compileBtnLayout.flexibleHeight = 0;
|
||||
var compileButton = compileBtnObj.GetComponent<Button>();
|
||||
var compileBtnColors = compileButton.colors;
|
||||
compileBtnColors.normalColor = new Color(14f / 255f, 80f / 255f, 14f / 255f);
|
||||
compileButton.colors = compileBtnColors;
|
||||
var btnText = compileBtnObj.GetComponentInChildren<Text>();
|
||||
btnText.text = "Run";
|
||||
var horozGroupObj = UIFactory.CreateHorizontalGroup(Content, "BigButtons", true, true, true, true, 0, new Vector4(2,2,2,2),
|
||||
new Color(1, 1, 1, 0));
|
||||
|
||||
var resetButton = UIFactory.CreateButton(horozGroupObj, "ResetButton", "Reset", () => ResetConsole(), "666666".ToColor());
|
||||
var resetBtnText = resetButton.GetComponentInChildren<Text>();
|
||||
resetBtnText.fontSize = 18;
|
||||
UIFactory.SetLayoutElement(resetButton.gameObject, preferredWidth: 80, flexibleWidth: 0, minHeight: 45, flexibleHeight: 0);
|
||||
|
||||
var compileButton = UIFactory.CreateButton(horozGroupObj, "CompileButton", "Compile", CompileCallback,
|
||||
new Color(14f / 255f, 80f / 255f, 14f / 255f));
|
||||
var btnText = compileButton.GetComponentInChildren<Text>();
|
||||
btnText.fontSize = 18;
|
||||
btnText.color = Color.white;
|
||||
UIFactory.SetLayoutElement(compileButton.gameObject, preferredWidth: 80, flexibleWidth: 0, minHeight: 45, flexibleHeight: 0);
|
||||
|
||||
// Set compile button callback now that we have the Input Field reference
|
||||
compileButton.onClick.AddListener(CompileCallback);
|
||||
void CompileCallback()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField.text))
|
||||
{
|
||||
CSConsolePage.Instance.Evaluate(inputField.text.Trim());
|
||||
}
|
||||
Evaluate(inputField.text.Trim());
|
||||
else
|
||||
ExplorerCore.Log("Cannot evaluate empty input!");
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -477,5 +582,15 @@ The following helper methods are available:
|
||||
this.InputText = mainTextInput;
|
||||
this.inputHighlightText = highlightTextInput;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ================================================================================================
|
||||
|
||||
private class VoidType
|
||||
{
|
||||
public static readonly VoidType Value = new VoidType();
|
||||
private VoidType() { }
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user