Compare commits

...

126 Commits
4.1.3 ... 4.3.3

Author SHA1 Message Date
b93567c7ea Bump version 2021-10-16 18:11:08 +11:00
957d80c7ec Fix IOUtility creating folders for file paths 2021-10-16 18:10:23 +11:00
d530d10798 Exceptions is IsNullOrDestroyed should return true 2021-10-12 20:27:32 +11:00
66daabf770 Add try/catch to IsNullOrDestroyed 2021-10-12 20:20:08 +11:00
9fe998aa22 Cleanup 2021-10-01 17:21:54 +10:00
28b6db80f9 Fix InputSystem EventSystem bug 2021-10-01 17:21:50 +10:00
22effbb399 Update README.md 2021-09-30 21:13:29 +10:00
50f0c31e98 Add patch for asset bundle unloading to prevent UnityExplorer's bundle being unloaded 2021-09-30 21:09:27 +10:00
911522457e Clarify "Classes" help in C# Console 2021-09-30 21:09:04 +10:00
d5d1841109 Include Unhollower in ILRepack references 2021-09-30 21:08:45 +10:00
239534e09c Add GetAllLoadedAssetBundles 2021-09-30 21:08:29 +10:00
bc5d16051f Fix issue with InputSystem 2021-09-30 21:08:08 +10:00
f81822f219 bump lib versions 2021-09-30 21:06:38 +10:00
f159cf5ea7 Attempt fix at bundle unloading 2021-09-23 16:40:47 +10:00
1e6bacb32b manual unity libs no longer required for BepInEx 2021-09-23 16:21:47 +10:00
a80cef4c1d Bump version 2021-09-07 19:11:58 +10:00
450bb77f2e Use Harmony's FullDescription for hook parameter types 2021-09-07 19:11:19 +10:00
0479102db6 Cleanup toggle Enabled logic 2021-09-07 18:06:38 +10:00
93181f02be Update README.md 2021-09-07 17:33:01 +10:00
371054d6df Add hook source editor for custom dynamic hooks 2021-09-07 17:23:20 +10:00
427f23b80a Bump version 2021-09-07 03:40:14 +10:00
a0d5ab8792 Fix ByRef parameters breaking generated patch code 2021-09-07 03:40:09 +10:00
297034e38b Cleanup 2021-09-07 03:19:40 +10:00
57f59d1295 Finalize hook manager update 2021-09-06 23:10:10 +10:00
fbdb84eefa Implement HookManager UI and logic 2021-09-06 23:04:40 +10:00
6989ea1b19 Misc cleanups 2021-09-06 23:03:55 +10:00
fcdfeb2dec Redesign mouse inspector class, add UI results panel 2021-09-06 17:10:01 +10:00
a1d0b6432e Cleanup 2021-09-06 15:51:40 +10:00
0b84405e57 Fix incorrect startup delay logic 2021-09-02 18:29:21 +10:00
c31e0949d3 Update ExplorerMelonMod.cs 2021-09-02 17:30:56 +10:00
5f0495a7ea Update README.md 2021-08-24 18:42:22 +10:00
28de6779d8 Fix for Canvas.renderingDisplaySize crash 2021-08-24 17:47:42 +10:00
fae85c2968 Bump version 2021-08-23 18:35:36 +10:00
fc8fa9aa7a Fix TestClass init mistake 2021-08-23 18:35:31 +10:00
bcf9a801a9 Fix suggestions still being given in comments sometimes 2021-08-23 18:35:22 +10:00
20298aa47b Make Type and Member names selectable / copy+paste-able 2021-08-23 18:35:08 +10:00
e2c1c186c3 Add wrapper to handle LemonAction issue 2021-08-23 18:31:26 +10:00
9ce6508828 Update README.md 2021-08-19 16:20:41 +10:00
506e75c5fe Handle Unhollowed modules path through IExplorerLoader. Allow Standalone release to specify manually. 2021-08-19 16:20:25 +10:00
a2f22051f0 Cleanup 2021-08-19 16:19:49 +10:00
4e3203a91b Make Assembly.GetTypes patch a finalizer instead of prefix 2021-08-10 17:31:12 +10:00
a411ce2dba Add patch for Assembly.GetTypes to catch exceptions 2021-08-10 17:17:44 +10:00
48132b3d46 Cleanup ILRepack 2021-08-10 17:17:21 +10:00
e49ed3028f Cleanup 2021-08-06 18:44:13 +10:00
31dd54d25c Reduce string alloc 2021-08-06 16:02:58 +10:00
3cfdd6fa43 Update readme -noci 2021-08-04 01:16:52 +10:00
1fa1283a68 Cleanup and update readme 2021-08-03 18:30:07 +10:00
afa4135b67 Update ML lib for "LemonCleanup" breaking changes 2021-08-03 16:12:28 +10:00
48d1cf574d Fix evaluate exception logic with methods that have arguments 2021-08-03 16:11:54 +10:00
df0abbc847 Small optimizations/cleanup 2021-08-03 16:11:16 +10:00
5c9dcb1d43 Bump HarmonyX to 2.5.2 2021-07-28 19:14:37 +10:00
602770d980 Cleanup 2021-07-28 19:01:43 +10:00
f26371f95f Go back to previous panel transform saving, no need to change 2021-07-28 18:27:10 +10:00
a673c39f4a Include arial in legacy bundles 2021-07-28 18:26:53 +10:00
40583cae3d Include arial in legacy bundles, cleanup 2021-07-28 06:41:02 +10:00
20018a9ba9 Use invariant decimal separator for NumberFormatString 2021-07-28 06:40:05 +10:00
dabf92a1a5 Use CurrentCulture, and use whitespace instead of comma as separator 2021-07-27 22:17:26 +10:00
b7e275f02c Cleanup 2021-07-27 22:12:48 +10:00
f393e0d706 Include Arial font, don't rely on built-in resource
TODO add font to legacy bundles
2021-07-27 18:14:44 +10:00
ab8acc9e84 Change directory case 2021-07-21 20:53:03 +10:00
66c30ee70e Cleanup 2021-07-21 20:48:21 +10:00
ddd271c00d Cleanup 2021-07-21 20:12:34 +10:00
30fe9e4dde Cleanup 2021-07-21 18:37:01 +10:00
fa3a436037 Cleanup 2021-07-21 18:29:01 +10:00
ca27d2b20f Update CursorUnlocker.cs 2021-07-21 05:18:22 +10:00
4315e0c547 Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2021-07-21 04:23:55 +10:00
3e19e74329 Update readme -noci 2021-07-21 04:17:41 +10:00
5a3ffebadc Remove legacy build 2021-07-21 04:16:53 +10:00
6e6b6239d8 Do patching in CursorUnlocker, add patch for SetSelectedGameObject 2021-07-21 04:14:34 +10:00
03c8e5a8bd Add Harmony instance to ExplorerCore, bump version 2021-07-21 04:14:12 +10:00
44f7209843 Remove ML 0.3 builds 2021-07-21 04:13:54 +10:00
734e45cf9f Remove patching in mod loader classes 2021-07-21 04:13:42 +10:00
97838e0b3a Update Unhollower 2021-07-21 04:13:15 +10:00
304b47f898 Bump version 2021-07-19 21:43:40 +10:00
3d1fcbcd9f Fix duplicate Scene hashcodes breaking PopulateSceneDropdown 2021-07-19 21:43:36 +10:00
36fc17aa43 Get rid of pointless ReadOnlyCollection properties 2021-07-19 21:43:16 +10:00
adfa29e63c Bump version 2021-07-15 05:20:31 +10:00
3ffdcea73b Force all devices to always be supported by InputSystem 2021-07-15 05:20:26 +10:00
dfd55260a8 Fallback to null check loading if unityVersion throws an exception 2021-07-15 05:19:48 +10:00
29c78dc5a6 Manually get Major and Minor instead of using Version class 2021-07-11 23:22:33 +10:00
bf59d9d6cd Handle cases where Unity doesn't include 'f' suffix on version
Not sure if this ever happens but just in case
2021-07-11 18:19:09 +10:00
bb0c59534a Bump version 2021-07-11 18:11:18 +10:00
802bb722bc Use Application.unityVersion to load appropriate AssetBundle
And fix issue with Dropdowns and Time input label not refreshing properly on launch
2021-07-11 18:11:14 +10:00
9ca992b0d7 Fix incorrect config description example 2021-07-07 23:27:43 +10:00
5f3b3a6870 cleanup -noci 2021-07-07 23:21:40 +10:00
f5bce439cb Favor public GetEnumerator over private. Use GetActualType. Improve logging. 2021-07-06 16:42:20 +10:00
e2b2c9038a Add 'other' issue template -noci 2021-07-05 19:05:04 +10:00
fbefccd6b7 Update issue template -noci 2021-07-02 23:32:02 +10:00
271c91f0d0 Update README.md 2021-07-02 20:03:41 +10:00
eb221bd868 Bump version 2021-07-02 18:45:09 +10:00
5b516eb4cc Add assembly name to class search result labels 2021-07-02 18:45:03 +10:00
601567f9d2 Remove arbitrary static restriction on class search 2021-07-02 17:53:12 +10:00
7ff508b874 [IL2CPP] Support private interface implementations on IDictionary and IEnumerable 2021-07-02 17:45:18 +10:00
09a7cd35cf Remove known issues from readme -noci 2021-07-02 17:35:34 +10:00
db4a338d26 Update issue template -noci 2021-07-02 17:33:41 +10:00
c08e02057c Create enhancement.yaml 2021-07-02 17:31:36 +10:00
362fcdc51a Update bug_report.yaml 2021-07-02 17:22:24 +10:00
73bd172e4d Update bug_report.yaml 2021-07-02 17:16:09 +10:00
454d3bd0b4 Add bug report issue template 2021-07-02 17:10:26 +10:00
f815a13d9a Namespace cleanup, move some categories out of UI namespace 2021-06-30 07:49:58 +10:00
65c4d49274 Update readme 2021-06-26 18:08:21 +10:00
d99137526e Fix InspectUnderMouse breaking ScrollPool in rare cases 2021-06-22 20:39:46 +10:00
92447b55cd Update README.md 2021-06-22 19:48:41 +10:00
87d5d5a2de Bump version 2021-06-22 19:46:14 +10:00
08cff3386b Fix issues with Il2Cpp nullables 2021-06-22 19:46:09 +10:00
6033200579 Don't use WaitForEndOfFrame in SrollPool init coroutine 2021-06-22 19:45:57 +10:00
94ec1c4908 Update line numbers on panel resize 2021-06-21 19:50:11 +10:00
7a400e762c Cleanup 2021-06-21 19:49:44 +10:00
67f9f744bb Handle Unity 2021+ InputField.onEndEdit change 2021-06-21 19:26:05 +10:00
7a59f9a2a1 Update info about startup script -noci 2021-06-20 19:10:44 +10:00
9b42eef1b9 Line numbers and startup script 2021-06-20 19:06:52 +10:00
830000b019 Bump version 2021-06-20 19:06:33 +10:00
34910ab273 Bump version 2021-06-14 18:56:56 +10:00
86b036095e Allow panels to be dragged further outside the game window 2021-06-14 18:56:51 +10:00
b57e5be2e6 Add GameObject.activeSelf toggle to TransformTree, adjust UI Toggle design 2021-06-14 18:43:26 +10:00
2d8ae45814 Cleanup 2021-06-14 18:43:08 +10:00
66dc262a68 Move inner exception null check outside IL2CPP ppd 2021-06-11 18:12:14 +10:00
4342901206 Prevent null exceptions being used in ReflectionExToString 2021-06-11 17:36:35 +10:00
58b7c72a5c Allow UI inspect without main camera 2021-06-11 17:36:17 +10:00
623dc7b7be Clarify VS build instructions 2021-06-10 18:14:33 +10:00
e6f4939cc9 Update README.md 2021-06-07 19:50:36 +10:00
7a539ba78b Bump version 2021-06-07 19:28:25 +10:00
0d10f94eb5 Use Paths.BepInExRootPath, and cleanup dirs 2021-06-07 19:27:39 +10:00
91671bf243 Failsafe in case Component is null from GetComponents? 2021-06-07 19:27:09 +10:00
a72877befb Make AllTypes protected, force using GetTypeByName 2021-06-07 19:26:46 +10:00
16335c1bc4 Auto-cleanup 2021-06-05 19:36:09 +10:00
148 changed files with 3296 additions and 2152 deletions

60
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View File

@ -0,0 +1,60 @@
name: Bug Report
description: File a bug or crash report
title: "[Bug]: "
labels: [bug]
body:
- type: markdown
attributes:
value: |
Thanks for submitting a bug report, please fill out as much detail as possible.
- type: checkboxes
id: latestversion
attributes:
label: Are you on the latest version of UnityExplorer?
description: If not, you must update first.
options:
- label: Yes, I'm on the latest version of UnityExplorer.
required: true
- type: dropdown
id: version
attributes:
label: Which release are you using?
description: Please select your environment for UnityExplorer.
options:
- BepInEx IL2CPP
- BepInEx 6.X Mono
- BepInEx 5.X Mono
- MelonLoader IL2CPP
- MelonLoader Mono
- Standalone IL2CPP
- Standalone Mono
validations:
required: true
- type: textarea
id: game
attributes:
label: Which game did this occur on?
description: Please tell us the name of the game. If it's a personal or private project, just let us know the Unity version.
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: Describe the issue.
description: What happened? Should something else have happened instead? Please provide steps to reproduce the issue if possible.
placeholder: Tell us what you see!
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: |
Please copy and paste any relevant logs and stack traces.
* Unity log: `%userprofile%\AppData\LocalLow\{Company}\{Game}\Player.log` or `output_log.txt`
* BepInEx: `BepInEx\LogOutput.log`
* MelonLoader: `MelonLoader\latest.log`
* Standalone: `{DLL_Location}\UnityExplorer\Logs\` (pick the most recent one)
render: shell
validations:
required: false

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1 @@
blank_issues_enabled: false

18
.github/ISSUE_TEMPLATE/enhancement.yaml vendored Normal file
View File

@ -0,0 +1,18 @@
name: New feature or enhancement
description: Suggest or discuss a feature or enhancement for UnityExplorer
title: "[Enhancement]: "
labels: [enhancement]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to discuss UnityExplorer, please provide as much detail as possible.
- type: textarea
id: description
attributes:
label: Describe the new feature or enhancement
description: |
Please go into as much detail as necessary in describing the new feature or enhancement.
If providing examples or suggestions for the required C# code, please use syntax-highlighted code blocks.
validations:
required: true

13
.github/ISSUE_TEMPLATE/other.yaml vendored Normal file
View File

@ -0,0 +1,13 @@
name: Other
description: Something else?
title: "[Other]: "
labels: [Other]
body:
- type: textarea
id: description
attributes:
label: Describe the issue
description: |
Please describe the issue in as much detail as possible.
validations:
required: true

View File

@ -79,22 +79,6 @@ jobs:
name: UnityExplorer.MelonLoader.Mono name: UnityExplorer.MelonLoader.Mono
path: ./Release/UnityExplorer.MelonLoader.Mono/* path: ./Release/UnityExplorer.MelonLoader.Mono/*
# MelonLoader 0.3.0 Il2Cpp
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_MLLegacy_Cpp
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.MelonLoader_Legacy.Il2Cpp
path: ./Release/UnityExplorer.MelonLoader_Legacy.Il2Cpp/*
# MelonLoader 0.3.0 Mono
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_MLLegacy_Mono
- uses: actions/upload-artifact@v2
with:
name: UnityExplorer.MelonLoader_Legacy.Mono
path: ./Release/UnityExplorer.MelonLoader_Legacy.Mono/*
# Standalone Il2Cpp # Standalone Il2Cpp
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_STANDALONE_Cpp - run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_STANDALONE_Cpp

View File

@ -6,16 +6,15 @@
🔍 An in-game UI for exploring, debugging and modifying Unity games. 🔍 An in-game UI for exploring, debugging and modifying Unity games.
</p> </p>
<p align="center"> <p align="center">
✔️ Supports most Unity versions from 5.2 to 2020+ (IL2CPP and Mono). ✔️ Supports most Unity versions from 5.2 to 2021+ (IL2CPP and Mono).
</p>
<p align="center">
☕ Enjoy this tool? Consider supporting me on <a href="https://ko-fi.com/sinaidev">ko-fi</a>!
</p> </p>
# Releases [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/total.svg)](../../releases) # Releases [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/total.svg)](../../releases)
[![](https://img.shields.io/github/release/sinai-dev/UnityExplorer.svg?label=version)](../../releases/latest) [![](https://img.shields.io/github/workflow/status/sinai-dev/UnityExplorer/Build%20UnityExplorer)](https://github.com/sinai-dev/UnityExplorer/actions) [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/latest/total.svg)](../../releases/latest) [![](https://img.shields.io/github/release/sinai-dev/UnityExplorer.svg?label=version)](../../releases/latest) [![](https://img.shields.io/github/workflow/status/sinai-dev/UnityExplorer/Build%20UnityExplorer)](https://github.com/sinai-dev/UnityExplorer/actions) [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/latest/total.svg)](../../releases/latest)
⚡ Thunderstore releases: [BepInEx Mono](https://thunderstore.io/package/sinai-dev/UnityExplorer) | [BepInEx IL2CPP](https://gtfo.thunderstore.io/package/sinai-dev/UnityExplorer_IL2CPP) | [MelonLoader IL2CPP](https://boneworks.thunderstore.io/package/sinai-dev/UnityExplorer_IL2CPP_ML)
## BepInEx ## BepInEx
| Release | IL2CPP | Mono | | Release | IL2CPP | Mono |
@ -24,7 +23,6 @@
| BIE 5.X | ✖️ n/a | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip) | | BIE 5.X | ✖️ n/a | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip) |
1. Take the `UnityExplorer.BIE.[version].dll` file and put it in `BepInEx\plugins\` 1. Take the `UnityExplorer.BIE.[version].dll` file and put it in `BepInEx\plugins\`
2. In IL2CPP, you will need to download the [Unity libs](https://github.com/LavaGang/Unity-Runtime-Libraries) for the game's Unity version, create a folder `BepInEx\unity-libs\`, then extract the Unity libs into this folder.
<i>Note: BepInEx 6 is obtainable via [BepisBuilds](https://builds.bepis.io/projects/bepinex_be)</i> <i>Note: BepInEx 6 is obtainable via [BepisBuilds](https://builds.bepis.io/projects/bepinex_be)</i>
@ -32,8 +30,7 @@
| Release | IL2CPP | Mono | | Release | IL2CPP | Mono |
| ------- | ------ | ---- | | ------- | ------ | ---- |
| ML 0.4.0 | ✅ [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) | | ML 0.4+ | ✅ [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) |
| ML 0.3.0 | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader_Legacy.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader_Legacy.Mono.zip) |
1. Take the `UnityExplorer.ML.[version].dll` file and put it in the `Mods\` folder created by MelonLoader. 1. Take the `UnityExplorer.ML.[version].dll` file and put it in the `Mods\` folder created by MelonLoader.
@ -50,10 +47,23 @@ The standalone release can be used with any injector or loader of your choice, b
3. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();` 3. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();`
4. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish 4. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish
# Known issues # Common issues
* Any `MissingMethodException` or `NotSupportedException`: please report the issue and provide a copy of your mod loader log and/or Unity log.
* In IL2CPP, some IEnumerable and IDictionary types may fail enumeration. Waiting for the Unhollower rewrite to address this any further. These are some common fixes to issues which are present in some games, please create an issue in this repository if these fixes don't work.
* The C# Console's completions have some minor issues such as not suggestion global classes which have no namespace, and erronously suggesting classes from using directives when they shouldn't be suggested. These are issues with mcs itself which I am looking into.
### Input not working properly
This can be caused by a number of issues, but most commonly by the Event System.
1. Open the UnityExplorer config file. See the "Settings" section below if you're unsure where it is.
2. For the "Disable EventSystem Override" option, set the value to `true`
3. Restart the game.
### UI not appearing or gets destroyed during startup
1. Open the UnityExplorer config file. See the "Settings" section below if you're unsure where it is.
2. For the "Startup Delay" option, set the value to something higher, at least as long as it takes to reach the main menu of the game.
3. Restart the game.
# Features # Features
@ -88,8 +98,15 @@ The inspector is used to see detailed information on objects of any type and man
### C# Console ### C# Console
* The C# Console uses the `Mono.CSharp.Evaluator` to define temporary classes or run immediate REPL code. * The C# Console uses the `Mono.CSharp.Evaluator` to define temporary classes or run immediate REPL code.
* You can execute a script automatically on startup by naming it `startup.cs` and placing it in the `UnityExplorer\Scripts\` folder (this folder will be created where you placed the DLL file).
* See the "Help" dropdown in the C# console menu for more detailed information. * See the "Help" dropdown in the C# console menu for more detailed information.
### Hook Manager
* The Hooks panel allows you to hook methods at the click of a button for debugging purposes.
* Simply enter any class (generic types not yet supported) and hook the methods you want from the menu.
* You can edit the source code of the generated hook with the "Edit Hook Source" button. Accepted method names are `Prefix` (which can return `bool` or `void`), `Postfix`, `Finalizer` (which can return `Exception` or `void`), and `Transpiler` (which must return `IEnumerable<HarmonyLib.CodeInstruction>`). You can define multiple patches if you wish.
### Mouse-Inspect ### Mouse-Inspect
* The "Mouse Inspect" dropdown on the main UnityExplorer navbar allows you to inspect objects under the mouse. * The "Mouse Inspect" dropdown on the main UnityExplorer navbar allows you to inspect objects under the mouse.
@ -105,19 +122,19 @@ The inspector is used to see detailed information on objects of any type and man
# Building # Building
For Visual Studio:
0. Clone the repository and run `git submodule update --init --recursive` to get the submodules.
1. Open the `src\UnityExplorer.sln` project.
2. Build `mcs` (Release/AnyCPU, you may need to run `nuget restore mcs.sln`), and if using IL2CPP then build `Il2CppAssemblyUnhollower` (Release/AnyCPU) as well.
3. Build the UnityExplorer release(s) you want to use, either by selecting the config as the Active Config, or batch-building.
If you fork the repository on GitHub you can build using the [dotnet workflow](https://github.com/sinai-dev/UnityExplorer/blob/master/.github/workflows/dotnet.yml): If you fork the repository on GitHub you can build using the [dotnet workflow](https://github.com/sinai-dev/UnityExplorer/blob/master/.github/workflows/dotnet.yml):
0. Click on the Actions tab and enable workflows in your repository 0. Click on the Actions tab and enable workflows in your repository
1. Click on the "Build UnityExplorer" workflow, then click "Run Workflow" and run it manually, or make a new commit to trigger the workflow. 1. Click on the "Build UnityExplorer" workflow, then click "Run Workflow" and run it manually, or make a new commit to trigger the workflow.
2. Take the artifact from the completed run. 2. Take the artifact from the completed run.
For Visual Studio:
0. Clone the repository and run `git submodule update --init --recursive` to get the submodules.
1. Open the `src\UnityExplorer.sln` project.
2. Build `mcs`, and if using IL2CPP then build `UnhollowerBaseLib` as well.
3. Build the UnityExplorer release(s) you want to use, either by selecting the config as the Active Config, or batch-building.
# Acknowledgments # Acknowledgments
* [ManlyMarco](https://github.com/ManlyMarco) for [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor) \[[license](THIRDPARTY_LICENSES.md#runtimeunityeditor-license)\], the ScriptEvaluator from RUE's REPL console was used as the base for UnityExplorer's C# console. * [ManlyMarco](https://github.com/ManlyMarco) for [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor) \[[license](THIRDPARTY_LICENSES.md#runtimeunityeditor-license)\], the ScriptEvaluator from RUE's REPL console was used as the base for UnityExplorer's C# console.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,10 +3,11 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityExplorer.UI.CSConsole.Lexers; using UnityExplorer.CSConsole.Lexers;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets.AutoComplete; using UnityExplorer.UI.Widgets.AutoComplete;
namespace UnityExplorer.UI.CSConsole namespace UnityExplorer.CSConsole
{ {
public class CSAutoCompleter : ISuggestionProvider public class CSAutoCompleter : ISuggestionProvider
{ {
@ -22,7 +23,6 @@ namespace UnityExplorer.UI.CSConsole
AutoCompleteModal.Instance.ReleaseOwnership(this); AutoCompleteModal.Instance.ReleaseOwnership(this);
} }
// Delimiters for completions, notably does not include '.'
private readonly HashSet<char> delimiters = new HashSet<char> private readonly HashSet<char> delimiters = new HashSet<char>
{ {
'{', '}', ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&', '?' '{', '}', ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&', '?'
@ -67,11 +67,10 @@ namespace UnityExplorer.UI.CSConsole
} }
string input = InputField.Text.Substring(startIdx, caret - startIdx + 1); string input = InputField.Text.Substring(startIdx, caret - startIdx + 1);
// Get MCS completions // Get MCS completions
string[] evaluatorCompletions = ConsoleController.Evaluator.GetCompletions(input, out string prefix); string[] evaluatorCompletions = ConsoleController.Evaluator.GetCompletions(input, out string prefix);
if (evaluatorCompletions != null && evaluatorCompletions.Any()) if (evaluatorCompletions != null && evaluatorCompletions.Any())
{ {
suggestions.AddRange(from completion in evaluatorCompletions suggestions.AddRange(from completion in evaluatorCompletions
@ -100,7 +99,7 @@ namespace UnityExplorer.UI.CSConsole
{ {
if (!keywordHighlights.ContainsKey(kw)) if (!keywordHighlights.ContainsKey(kw))
keywordHighlights.Add(kw, $"<color=#{SignatureHighlighter.keywordBlueHex}>{kw}</color>"); keywordHighlights.Add(kw, $"<color=#{SignatureHighlighter.keywordBlueHex}>{kw}</color>");
string completion = kw.Substring(input.Length, kw.Length - input.Length); string completion = kw.Substring(input.Length, kw.Length - input.Length);
suggestions.Add(new Suggestion(keywordHighlights[kw], completion)); suggestions.Add(new Suggestion(keywordHighlights[kw], completion));
} }
@ -124,7 +123,7 @@ namespace UnityExplorer.UI.CSConsole
private readonly StringBuilder highlightBuilder = new StringBuilder(); private readonly StringBuilder highlightBuilder = new StringBuilder();
private const string OPEN_HIGHLIGHT = "<color=cyan>"; private const string OPEN_HIGHLIGHT = "<color=cyan>";
private string GetHighlightString(string prefix, string completion) private string GetHighlightString(string prefix, string completion)
{ {
highlightBuilder.Clear(); highlightBuilder.Clear();

View File

@ -1,20 +1,21 @@
using System; using Mono.CSharp;
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.CSConsole;
using UnityExplorer.Core.Input; using UnityExplorer.Core.Input;
using UnityExplorer.CSConsole;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets.AutoComplete; using UnityExplorer.UI.Widgets.AutoComplete;
using System.Reflection;
using Mono.CSharp;
namespace UnityExplorer.UI.CSConsole namespace UnityExplorer.CSConsole
{ {
public static class ConsoleController public static class ConsoleController
{ {
@ -36,6 +37,8 @@ namespace UnityExplorer.UI.CSConsole
public static bool EnableAutoIndent { get; private set; } = true; public static bool EnableAutoIndent { get; private set; } = true;
public static bool EnableSuggestions { get; private set; } = true; public static bool EnableSuggestions { get; private set; } = true;
internal static string ScriptsFolder => Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Scripts");
internal static readonly string[] DefaultUsing = new string[] internal static readonly string[] DefaultUsing = new string[]
{ {
"System", "System",
@ -51,6 +54,7 @@ namespace UnityExplorer.UI.CSConsole
public static void Init() public static void Init()
{ {
// Make sure console is supported on this platform
try try
{ {
ResetConsole(false); ResetConsole(false);
@ -63,21 +67,42 @@ namespace UnityExplorer.UI.CSConsole
return; return;
} }
// Setup console
Lexer = new LexerBuilder(); Lexer = new LexerBuilder();
Completer = new CSAutoCompleter(); Completer = new CSAutoCompleter();
SetupHelpInteraction(); SetupHelpInteraction();
Panel.OnInputChanged += OnInputChanged; Panel.OnInputChanged += OnInputChanged;
Panel.InputScroll.OnScroll += OnInputScrolled; Panel.InputScroller.OnScroll += OnInputScrolled;
Panel.OnCompileClicked += Evaluate; Panel.OnCompileClicked += Evaluate;
Panel.OnResetClicked += ResetConsole; Panel.OnResetClicked += ResetConsole;
Panel.OnHelpDropdownChanged += HelpSelected; Panel.OnHelpDropdownChanged += HelpSelected;
Panel.OnAutoIndentToggled += OnToggleAutoIndent; Panel.OnAutoIndentToggled += OnToggleAutoIndent;
Panel.OnCtrlRToggled += OnToggleCtrlRShortcut; Panel.OnCtrlRToggled += OnToggleCtrlRShortcut;
Panel.OnSuggestionsToggled += OnToggleSuggestions; Panel.OnSuggestionsToggled += OnToggleSuggestions;
} Panel.OnPanelResized += OnInputScrolled;
// Run startup script
try
{
if (!Directory.Exists(ScriptsFolder))
Directory.CreateDirectory(ScriptsFolder);
var startupPath = Path.Combine(ScriptsFolder, "startup.cs");
if (File.Exists(startupPath))
{
ExplorerCore.Log($"Executing startup script from '{startupPath}'...");
var text = File.ReadAllText(startupPath);
Input.Text = text;
Evaluate();
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception executing startup script: {ex}");
}
}
#region UI Listeners and options #region UI Listeners and options
@ -176,7 +201,7 @@ namespace UnityExplorer.UI.CSConsole
{ {
// The compiled code was not REPL, so it was a using directive or it defined classes. // The compiled code was not REPL, so it was a using directive or it defined classes.
string output = ScriptEvaluator._textWriter.ToString(); string output = Evaluator._textWriter.ToString();
var outputSplit = output.Split('\n'); var outputSplit = output.Split('\n');
if (outputSplit.Length >= 2) if (outputSplit.Length >= 2)
output = outputSplit[outputSplit.Length - 2]; output = outputSplit[outputSplit.Length - 2];
@ -221,7 +246,7 @@ namespace UnityExplorer.UI.CSConsole
if (InputManager.GetKeyDown(KeyCode.Escape)) if (InputManager.GetKeyDown(KeyCode.Escape))
{ {
Input.Text = previousInput; Input.Text = previousInput;
if (EnableSuggestions && AutoCompleteModal.CheckEscape(Completer)) if (EnableSuggestions && AutoCompleteModal.CheckEscape(Completer))
OnAutocompleteEscaped(); OnAutocompleteEscaped();
@ -240,7 +265,7 @@ namespace UnityExplorer.UI.CSConsole
} }
var inStringOrComment = HighlightVisibleInput(); var inStringOrComment = HighlightVisibleInput();
if (!settingCaretCoroutine) if (!settingCaretCoroutine)
{ {
if (EnableSuggestions) if (EnableSuggestions)
@ -317,7 +342,7 @@ namespace UnityExplorer.UI.CSConsole
var charBot = charTop - CSCONSOLE_LINEHEIGHT; var charBot = charTop - CSCONSOLE_LINEHEIGHT;
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f); var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
var viewportMax = viewportMin - Panel.InputScroll.ViewportRect.rect.height; var viewportMax = viewportMin - Panel.InputScroller.ViewportRect.rect.height;
float diff = 0f; float diff = 0f;
if (charTop > viewportMin) if (charTop > viewportMin)
@ -337,7 +362,7 @@ namespace UnityExplorer.UI.CSConsole
{ {
settingCaretCoroutine = true; settingCaretCoroutine = true;
Input.Component.readOnly = true; Input.Component.readOnly = true;
RuntimeProvider.Instance.StartCoroutine(SetAutocompleteCaretCoro(caretPosition)); RuntimeProvider.Instance.StartCoroutine(SetCaretCoroutine(caretPosition));
} }
internal static PropertyInfo SelectionGuardProperty => selectionGuardPropInfo ?? GetSelectionGuardPropInfo(); internal static PropertyInfo SelectionGuardProperty => selectionGuardPropInfo ?? GetSelectionGuardPropInfo();
@ -352,7 +377,7 @@ namespace UnityExplorer.UI.CSConsole
private static PropertyInfo selectionGuardPropInfo; private static PropertyInfo selectionGuardPropInfo;
private static IEnumerator SetAutocompleteCaretCoro(int caretPosition) private static IEnumerator SetCaretCoroutine(int caretPosition)
{ {
var color = Input.Component.selectionColor; var color = Input.Component.selectionColor;
color.a = 0f; color.a = 0f;
@ -376,7 +401,6 @@ namespace UnityExplorer.UI.CSConsole
settingCaretCoroutine = false; settingCaretCoroutine = false;
} }
#region Lexer Highlighting #region Lexer Highlighting
/// <summary> /// <summary>
@ -384,43 +408,83 @@ namespace UnityExplorer.UI.CSConsole
/// </summary> /// </summary>
private static bool HighlightVisibleInput() private static bool HighlightVisibleInput()
{ {
int startIdx = 0; if (string.IsNullOrEmpty(Input.Text))
int endIdx = Input.Text.Length - 1;
int topLine = 0;
// Calculate visible text if necessary
if (Input.Rect.rect.height > Panel.InputScroll.ViewportRect.rect.height)
{ {
topLine = -1; Panel.HighlightText.text = "";
int bottomLine = -1; Panel.LineNumberText.text = "1";
return false;
// the top and bottom position of the viewport in relation to the text height
// they need the half-height adjustment to normalize against the 'line.topY' value.
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
var viewportMax = viewportMin - Panel.InputScroll.ViewportRect.rect.height;
for (int i = 0; i < Input.TextGenerator.lineCount; i++)
{
var line = Input.TextGenerator.lines[i];
// if not set the top line yet, and top of line is below the viewport top
if (topLine == -1 && line.topY <= viewportMin)
topLine = i;
// if bottom of line is below the viewport bottom
if ((line.topY - line.height) >= viewportMax)
bottomLine = i;
}
topLine = Math.Max(0, topLine - 1);
bottomLine = Math.Min(Input.TextGenerator.lineCount - 1, bottomLine + 1);
startIdx = Input.TextGenerator.lines[topLine].startCharIdx;
endIdx = (bottomLine >= Input.TextGenerator.lineCount - 1)
? Input.Text.Length - 1
: (Input.TextGenerator.lines[bottomLine + 1].startCharIdx - 1);
} }
// Calculate the visible lines
int topLine = -1;
int bottomLine = -1;
// the top and bottom position of the viewport in relation to the text height
// they need the half-height adjustment to normalize against the 'line.topY' value.
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
var viewportMax = viewportMin - Panel.InputScroller.ViewportRect.rect.height;
for (int i = 0; i < Input.TextGenerator.lineCount; i++)
{
var line = Input.TextGenerator.lines[i];
// if not set the top line yet, and top of line is below the viewport top
if (topLine == -1 && line.topY <= viewportMin)
topLine = i;
// if bottom of line is below the viewport bottom
if ((line.topY - line.height) >= viewportMax)
bottomLine = i;
}
topLine = Math.Max(0, topLine - 1);
bottomLine = Math.Min(Input.TextGenerator.lineCount - 1, bottomLine + 1);
int startIdx = Input.TextGenerator.lines[topLine].startCharIdx;
int endIdx = (bottomLine >= Input.TextGenerator.lineCount - 1)
? Input.Text.Length - 1
: (Input.TextGenerator.lines[bottomLine + 1].startCharIdx - 1);
// Highlight the visible text with the LexerBuilder // Highlight the visible text with the LexerBuilder
Panel.HighlightText.text = Lexer.BuildHighlightedString(Input.Text, startIdx, endIdx, topLine, LastCaretPosition, out bool ret); Panel.HighlightText.text = Lexer.BuildHighlightedString(Input.Text, startIdx, endIdx, topLine, LastCaretPosition, out bool ret);
// Set the line numbers
// determine true starting line number (not the same as the cached TextGenerator line numbers)
int realStartLine = 0;
for (int i = 0; i < startIdx; i++)
{
if (LexerBuilder.IsNewLine(Input.Text[i]))
realStartLine++;
}
realStartLine++;
char lastPrev = '\n';
var sb = new StringBuilder();
// append leading new lines for spacing (no point rendering line numbers we cant see)
for (int i = 0; i < topLine; i++)
sb.Append('\n');
// append the displayed line numbers
for (int i = topLine; i <= bottomLine; i++)
{
if (i > 0)
lastPrev = Input.Text[Input.TextGenerator.lines[i].startCharIdx - 1];
// previous line ended with a newline character, this is an actual new line.
if (LexerBuilder.IsNewLine(lastPrev))
{
sb.Append(realStartLine.ToString());
realStartLine++;
}
sb.Append('\n');
}
Panel.LineNumberText.text = sb.ToString();
return ret; return ret;
} }
@ -555,7 +619,9 @@ If the game was built with Unity's stubbed netstandard 2.0 runtime, you can fix
internal const string STARTUP_TEXT = @"<color=#5d8556>// Welcome to the UnityExplorer C# Console! internal const string STARTUP_TEXT = @"<color=#5d8556>// Welcome to the UnityExplorer C# Console!
// It is recommended to use the Log panel (or a console log window) while using this tool. // It is recommended to use the Log panel (or a console log window) while using this tool.
// Use the Help dropdown to see detailed examples of how to use the console.</color>"; // Use the Help dropdown to see detailed examples of how to use the console.
// To execute a script automatically on startup, put the script at 'UnityExplorer\Scripts\startup.cs'</color>";
internal const string HELP_USINGS = @"// You can add a using directive to any namespace, but you must compile for it to take effect. internal const string HELP_USINGS = @"// You can add a using directive to any namespace, but you must compile for it to take effect.
// It will remain in effect until you Reset the console. // It will remain in effect until you Reset the console.
@ -592,7 +658,7 @@ var x = 5;
// You can soft-overwrite a class by compiling it again with the same name. The old class will still technically exist in memory. // You can soft-overwrite a class by compiling it again with the same name. The old class will still technically exist in memory.
// Compiled classes can be accessed from both inside and outside this console. // Compiled classes can be accessed from both inside and outside this console.
// Note: in IL2CPP, injecting these classes with ClassInjector may crash the game! // Note: in IL2CPP, you must declare a Namespace to inject these classes with ClassInjector or it will crash the game.
public class HelloWorld public class HelloWorld
{ {

View File

@ -5,16 +5,17 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.CSConsole.Lexers; using UnityExplorer.CSConsole.Lexers;
namespace UnityExplorer.UI.CSConsole namespace UnityExplorer.CSConsole
{ {
public struct MatchInfo public struct MatchInfo
{ {
public int startIndex; public int startIndex;
public int endIndex; public int endIndex;
public string htmlColorTag;
public bool isStringOrComment; public bool isStringOrComment;
public bool matchToEndOfLine;
public string htmlColorTag;
} }
public class LexerBuilder public class LexerBuilder
@ -112,12 +113,23 @@ namespace UnityExplorer.UI.CSConsole
sb.Append(input[i]); sb.Append(input[i]);
sb.Append(SignatureHighlighter.CLOSE_COLOR); sb.Append(SignatureHighlighter.CLOSE_COLOR);
// check caretIdx to determine inStringOrComment state
if (caretIdx >= match.startIndex && (caretIdx <= match.endIndex || (caretIdx >= input.Length && match.endIndex >= input.Length - 1)))
caretInStringOrComment = match.isStringOrComment;
// update the last unhighlighted start index // update the last unhighlighted start index
lastUnhighlighted = match.endIndex + 1; lastUnhighlighted = match.endIndex + 1;
int matchEndIdx = match.endIndex;
if (match.matchToEndOfLine)
{
while (input.Length - 1 >= matchEndIdx)
{
matchEndIdx++;
if (IsNewLine(input[matchEndIdx]))
break;
}
}
// check caretIdx to determine inStringOrComment state
if (caretIdx >= match.startIndex && (caretIdx <= (matchEndIdx+1) || (caretIdx >= input.Length && matchEndIdx >= input.Length - 1)))
caretInStringOrComment = match.isStringOrComment;
} }
// Append trailing unhighlighted input // Append trailing unhighlighted input

View File

@ -2,7 +2,7 @@
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
namespace UnityExplorer.UI.CSConsole.Lexers namespace UnityExplorer.CSConsole.Lexers
{ {
public class CommentLexer : Lexer public class CommentLexer : Lexer
{ {
@ -44,7 +44,7 @@ namespace UnityExplorer.UI.CSConsole.Lexers
while (!lexer.EndOfInput && !(lexer.Current == '/' && lexer.Previous == '*')); while (!lexer.EndOfInput && !(lexer.Current == '/' && lexer.Previous == '*'));
return true; return true;
} }
} }
return false; return false;

View File

@ -2,7 +2,7 @@
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
namespace UnityExplorer.UI.CSConsole.Lexers namespace UnityExplorer.CSConsole.Lexers
{ {
public class KeywordLexer : Lexer public class KeywordLexer : Lexer
{ {
@ -12,12 +12,12 @@ namespace UnityExplorer.UI.CSConsole.Lexers
public static readonly HashSet<string> keywords = new HashSet<string> public static readonly HashSet<string> keywords = new HashSet<string>
{ {
// reserved keywords // reserved keywords
"abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue", "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue",
"decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally", "decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally",
"fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock", "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock",
"long", "namespace", "new", "null", "object", "operator", "out", "override", "params", "private", "protected", "public", "long", "namespace", "new", "null", "object", "operator", "out", "override", "params", "private", "protected", "public",
"readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch",
"this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void", "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void",
"volatile", "while", "volatile", "while",
// contextual keywords // contextual keywords
"add", "and", "alias", "ascending", "async", "await", "by", "descending", "dynamic", "equals", "from", "get", "add", "and", "alias", "ascending", "async", "await", "by", "descending", "dynamic", "equals", "from", "get",

View File

@ -1,8 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine;
using System.Linq; using System.Linq;
using UnityEngine;
namespace UnityExplorer.UI.CSConsole.Lexers namespace UnityExplorer.CSConsole.Lexers
{ {
public abstract class Lexer public abstract class Lexer
{ {

View File

@ -1,6 +1,6 @@
using UnityEngine; using UnityEngine;
namespace UnityExplorer.UI.CSConsole.Lexers namespace UnityExplorer.CSConsole.Lexers
{ {
public class NumberLexer : Lexer public class NumberLexer : Lexer
{ {

View File

@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
namespace UnityExplorer.UI.CSConsole.Lexers namespace UnityExplorer.CSConsole.Lexers
{ {
public class StringLexer : Lexer public class StringLexer : Lexer
{ {

View File

@ -3,7 +3,7 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
namespace UnityExplorer.UI.CSConsole.Lexers namespace UnityExplorer.CSConsole.Lexers
{ {
public class SymbolLexer : Lexer public class SymbolLexer : Lexer
{ {

View File

@ -1,13 +1,13 @@
using System; using Mono.CSharp;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using Mono.CSharp;
// Thanks to ManlyMarco for this // Thanks to ManlyMarco for this
namespace UnityExplorer.UI.CSConsole namespace UnityExplorer.CSConsole
{ {
public class ScriptEvaluator : Evaluator, IDisposable public class ScriptEvaluator : Evaluator, IDisposable
{ {
@ -16,7 +16,7 @@ namespace UnityExplorer.UI.CSConsole
"mscorlib", "System.Core", "System", "System.Xml" "mscorlib", "System.Core", "System", "System.Xml"
}; };
internal static TextWriter _textWriter; internal TextWriter _textWriter;
internal static StreamReportPrinter _reportPrinter; internal static StreamReportPrinter _reportPrinter;
public ScriptEvaluator(TextWriter tw) : base(BuildContext(tw)) public ScriptEvaluator(TextWriter tw) : base(BuildContext(tw))
@ -51,8 +51,13 @@ namespace UnityExplorer.UI.CSConsole
ReferenceAssembly(asm); ReferenceAssembly(asm);
} }
private static CompilerContext context;
private static CompilerContext BuildContext(TextWriter tw) private static CompilerContext BuildContext(TextWriter tw)
{ {
if (context != null)
return context;
_reportPrinter = new StreamReportPrinter(tw); _reportPrinter = new StreamReportPrinter(tw);
var settings = new CompilerSettings var settings = new CompilerSettings
@ -65,7 +70,7 @@ namespace UnityExplorer.UI.CSConsole
EnhancedWarnings = false EnhancedWarnings = false
}; };
return new CompilerContext(settings, _reportPrinter); return context = new CompilerContext(settings, _reportPrinter);
} }
private static void ImportAppdomainAssemblies(Action<Assembly> import) private static void ImportAppdomainAssemblies(Action<Assembly> import)

View File

@ -1,19 +1,13 @@
using System; using Mono.CSharp;
using Mono.CSharp; using System;
using System.Collections; using System.Collections;
using UnityEngine;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityExplorer.Core.Runtime;
using System.Text; using System.Text;
using UnityEngine;
using UnityExplorer.Core.Runtime;
/* namespace UnityExplorer.CSConsole
Welcome to the UnityExplorer C# Console!
Use the Help dropdown to see detailed examples of how to use this console.
To see your output, use the Log panel or a Console Log window.
*/
namespace UnityExplorer.UI.CSConsole
{ {
public class ScriptInteraction : InteractiveBase public class ScriptInteraction : InteractiveBase
{ {
@ -58,7 +52,7 @@ namespace UnityExplorer.UI.CSConsole
public static void GetClasses() public static void GetClasses()
{ {
if (ReflectionUtility.GetFieldInfo(typeof(Evaluator), "source_file") if (ReflectionUtility.GetFieldInfo(typeof(Evaluator), "source_file")
.GetValue(Evaluator) is CompilationSourceFile sourceFile .GetValue(Evaluator) is CompilationSourceFile sourceFile
&& sourceFile.Containers.Any()) && sourceFile.Containers.Any())
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
@ -76,4 +70,4 @@ namespace UnityExplorer.UI.CSConsole
} }
} }
} }

View File

@ -3,20 +3,20 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityExplorer.Core.Config; using UnityExplorer.Core.Config;
using UnityExplorer.UI.CacheObject.Views; using UnityExplorer.CacheObject.Views;
namespace UnityExplorer.UI.CacheObject namespace UnityExplorer.CacheObject
{ {
public class CacheConfigEntry : CacheObjectBase public class CacheConfigEntry : CacheObjectBase
{ {
public CacheConfigEntry(IConfigElement configElement) public CacheConfigEntry(IConfigElement configElement)
{ {
this.RefConfigElement = configElement; this.RefConfigElement = configElement;
this.FallbackType = configElement.ElementType;
this.NameLabelText = $"<color=cyan>{configElement.Name}</color>" + this.NameLabelText = $"<color=cyan>{configElement.Name}</color>" +
$"\r\n<color=grey><i>{configElement.Description}</i></color>"; $"\r\n<color=grey><i>{configElement.Description}</i></color>";
this.NameLabelTextRaw = string.Empty;
this.FallbackType = configElement.ElementType;
configElement.OnValueChangedNotify += UpdateValueFromSource; configElement.OnValueChangedNotify += UpdateValueFromSource;
} }

View File

@ -3,9 +3,9 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using UnityExplorer.UI.Inspectors; using UnityExplorer.Inspectors;
namespace UnityExplorer.UI.CacheObject namespace UnityExplorer.CacheObject
{ {
public class CacheField : CacheMember public class CacheField : CacheMember
{ {

View File

@ -2,10 +2,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityExplorer.UI.CacheObject.Views; using UnityExplorer.CacheObject.IValues;
using UnityExplorer.UI.CacheObject.IValues; using UnityExplorer.CacheObject.Views;
namespace UnityExplorer.UI.CacheObject namespace UnityExplorer.CacheObject
{ {
public class CacheKeyValuePair : CacheObjectBase public class CacheKeyValuePair : CacheObjectBase
{ {
@ -61,6 +61,7 @@ namespace UnityExplorer.UI.CacheObject
var kvpCell = cell as CacheKeyValuePairCell; var kvpCell = cell as CacheKeyValuePairCell;
kvpCell.NameLabel.text = $"{DictIndex}:"; kvpCell.NameLabel.text = $"{DictIndex}:";
kvpCell.HiddenNameLabel.Text = "";
kvpCell.Image.color = DictIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor; kvpCell.Image.color = DictIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor;
if (KeyInputWanted) if (KeyInputWanted)

View File

@ -2,10 +2,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityExplorer.UI.CacheObject.Views; using UnityExplorer.CacheObject.IValues;
using UnityExplorer.UI.CacheObject.IValues; using UnityExplorer.CacheObject.Views;
namespace UnityExplorer.UI.CacheObject namespace UnityExplorer.CacheObject
{ {
public class CacheListEntry : CacheObjectBase public class CacheListEntry : CacheObjectBase
{ {
@ -28,6 +28,7 @@ namespace UnityExplorer.UI.CacheObject
var listCell = cell as CacheListEntryCell; var listCell = cell as CacheListEntryCell;
listCell.NameLabel.text = $"{ListIndex}:"; listCell.NameLabel.text = $"{ListIndex}:";
listCell.HiddenNameLabel.Text = "";
listCell.Image.color = ListIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor; listCell.Image.color = ListIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor;
} }

View File

@ -5,17 +5,15 @@ using System.Reflection;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityExplorer.Core.Runtime; using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject.Views; using UnityExplorer.CacheObject.Views;
using UnityExplorer.UI.Inspectors; using UnityExplorer.Inspectors;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
using UnityExplorer.UI;
namespace UnityExplorer.UI.CacheObject namespace UnityExplorer.CacheObject
{ {
public abstract class CacheMember : CacheObjectBase public abstract class CacheMember : CacheObjectBase
{ {
//public ReflectionInspector ParentInspector { get; internal set; }
//public bool AutoUpdateWanted { get; internal set; }
public abstract Type DeclaringType { get; } public abstract Type DeclaringType { get; }
public string NameForFiltering { get; protected set; } public string NameForFiltering { get; protected set; }
public object DeclaringInstance => IsStatic ? null : (m_declaringInstance ?? (m_declaringInstance = Owner.Target.TryCast(DeclaringType))); public object DeclaringInstance => IsStatic ? null : (m_declaringInstance ?? (m_declaringInstance = Owner.Target.TryCast(DeclaringType)));
@ -27,12 +25,13 @@ namespace UnityExplorer.UI.CacheObject
public Type[] GenericArguments { get; protected set; } = ArgumentUtility.EmptyTypes; public Type[] GenericArguments { get; protected set; } = ArgumentUtility.EmptyTypes;
public EvaluateWidget Evaluator { get; protected set; } public EvaluateWidget Evaluator { get; protected set; }
public bool Evaluating => Evaluator != null && Evaluator.UIRoot.activeSelf; public bool Evaluating => Evaluator != null && Evaluator.UIRoot.activeSelf;
public virtual void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member) public virtual void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{ {
this.Owner = inspector; this.Owner = inspector;
this.NameLabelText = SignatureHighlighter.Parse(member.DeclaringType, false, member); this.NameLabelText = SignatureHighlighter.Parse(member.DeclaringType, false, member);
this.NameForFiltering = $"{member.DeclaringType.Name}.{member.Name}"; this.NameForFiltering = $"{member.DeclaringType.Name}.{member.Name}";
this.NameLabelTextRaw = NameForFiltering;
} }
public override void ReleasePooledObjects() public override void ReleasePooledObjects()
@ -59,6 +58,17 @@ namespace UnityExplorer.UI.CacheObject
protected abstract void TrySetValue(object value); protected abstract void TrySetValue(object value);
/// <summary>
/// Evaluate is called when first shown (if ShouldAutoEvaluate), or else when Evaluate button is clicked, or auto-updated.
/// </summary>
public void Evaluate()
{
SetValueFromSource(TryEvaluate());
}
/// <summary>
/// Called when user presses the Evaluate button.
/// </summary>
public void EvaluateAndSetCell() public void EvaluateAndSetCell()
{ {
Evaluate(); Evaluate();
@ -66,27 +76,15 @@ namespace UnityExplorer.UI.CacheObject
SetDataToCell(CellView); SetDataToCell(CellView);
} }
/// <summary>
/// Evaluate when first shown (if ShouldAutoEvaluate), or else when Evaluate button is clicked, or auto-updated.
/// </summary>
public void Evaluate()
{
SetValueFromSource(TryEvaluate());
}
public override void TrySetUserValue(object value) public override void TrySetUserValue(object value)
{ {
TrySetValue(value); TrySetValue(value);
Evaluate(); Evaluate();
} }
protected override void SetValueState(CacheObjectCell cell, ValueStateArgs args) protected override void SetValueState(CacheObjectCell cell, ValueStateArgs args)
{ {
base.SetValueState(cell, args); base.SetValueState(cell, args);
//var memCell = cell as CacheMemberCell;
//memCell.UpdateToggle.gameObject.SetActive(ShouldAutoEvaluate);
} }
private static readonly Color evalEnabledColor = new Color(0.15f, 0.25f, 0.15f); private static readonly Color evalEnabledColor = new Color(0.15f, 0.25f, 0.15f);
@ -99,7 +97,6 @@ namespace UnityExplorer.UI.CacheObject
cell.EvaluateHolder.SetActive(!ShouldAutoEvaluate); cell.EvaluateHolder.SetActive(!ShouldAutoEvaluate);
if (!ShouldAutoEvaluate) if (!ShouldAutoEvaluate)
{ {
//cell.UpdateToggle.gameObject.SetActive(false);
cell.EvaluateButton.Component.gameObject.SetActive(true); cell.EvaluateButton.Component.gameObject.SetActive(true);
if (HasArguments) if (HasArguments)
{ {
@ -118,11 +115,6 @@ namespace UnityExplorer.UI.CacheObject
if (!Evaluating) if (!Evaluating)
RuntimeProvider.Instance.SetColorBlock(cell.EvaluateButton.Component, evalDisabledColor, evalDisabledColor * 1.3f); RuntimeProvider.Instance.SetColorBlock(cell.EvaluateButton.Component, evalDisabledColor, evalDisabledColor * 1.3f);
} }
//else
//{
// cell.UpdateToggle.gameObject.SetActive(true);
// cell.UpdateToggle.isOn = AutoUpdateWanted;
//}
if (State == ValueState.NotEvaluated && !ShouldAutoEvaluate) if (State == ValueState.NotEvaluated && !ShouldAutoEvaluate)
{ {
@ -138,7 +130,6 @@ namespace UnityExplorer.UI.CacheObject
return false; return false;
} }
public void OnEvaluateClicked() public void OnEvaluateClicked()
{ {
if (!HasArguments) if (!HasArguments)
@ -234,7 +225,7 @@ namespace UnityExplorer.UI.CacheObject
return sorted; return sorted;
} }
private static void TryCacheMember(MemberInfo member, List<CacheMember> list, HashSet<string> cachedSigs, private static void TryCacheMember(MemberInfo member, List<CacheMember> list, HashSet<string> cachedSigs,
Type declaringType, ReflectionInspector _inspector, bool ignorePropertyMethodInfos = true) Type declaringType, ReflectionInspector _inspector, bool ignorePropertyMethodInfos = true)
{ {
try try
@ -244,7 +235,7 @@ namespace UnityExplorer.UI.CacheObject
var sig = GetSig(member); var sig = GetSig(member);
//ExplorerCore.Log($"Trying to cache member {sig}..."); //ExplorerCore.Log($"Trying to cache member {sig}... ({member.MemberType})");
CacheMember cached; CacheMember cached;
Type returnType; Type returnType;
@ -253,7 +244,7 @@ namespace UnityExplorer.UI.CacheObject
case MemberTypes.Method: case MemberTypes.Method:
{ {
var mi = member as MethodInfo; var mi = member as MethodInfo;
if (ignorePropertyMethodInfos if (ignorePropertyMethodInfos
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_"))) && (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
return; return;
@ -309,7 +300,6 @@ namespace UnityExplorer.UI.CacheObject
cachedSigs.Add(sig); cachedSigs.Add(sig);
//cached.Initialize(_inspector, declaringType, member, returnType);
cached.SetFallbackType(returnType); cached.SetFallbackType(returnType);
cached.SetInspectorOwner(_inspector, member); cached.SetInspectorOwner(_inspector, member);
@ -322,7 +312,7 @@ namespace UnityExplorer.UI.CacheObject
} }
} }
internal static string GetSig(MemberInfo member) internal static string GetSig(MemberInfo member)
=> $"{member.DeclaringType.Name}.{member.Name}"; => $"{member.DeclaringType.Name}.{member.Name}";
internal static string GetArgumentString(ParameterInfo[] args) internal static string GetArgumentString(ParameterInfo[] args)

View File

@ -3,9 +3,9 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using UnityExplorer.UI.Inspectors; using UnityExplorer.Inspectors;
namespace UnityExplorer.UI.CacheObject namespace UnityExplorer.CacheObject
{ {
public class CacheMethod : CacheMember public class CacheMethod : CacheMember
{ {
@ -30,15 +30,14 @@ namespace UnityExplorer.UI.CacheObject
try try
{ {
var methodInfo = MethodInfo; var methodInfo = MethodInfo;
if (methodInfo.IsGenericMethod) if (methodInfo.IsGenericMethod)
methodInfo = MethodInfo.MakeGenericMethod(Evaluator.TryParseGenericArguments()); methodInfo = MethodInfo.MakeGenericMethod(Evaluator.TryParseGenericArguments());
if (Arguments.Length > 0) object ret;
return methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments()); if (HasArguments)
ret = methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments());
var ret = methodInfo.Invoke(DeclaringInstance, ArgumentUtility.EmptyArgs); else
ret = methodInfo.Invoke(DeclaringInstance, ArgumentUtility.EmptyArgs);
HadException = false; HadException = false;
LastException = null; LastException = null;
return ret; return ret;

View File

@ -7,11 +7,12 @@ using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.Core.Runtime; using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject.Views; using UnityExplorer.CacheObject.IValues;
using UnityExplorer.UI.CacheObject.IValues; using UnityExplorer.CacheObject.Views;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
using UnityExplorer.UI;
namespace UnityExplorer.UI.CacheObject namespace UnityExplorer.CacheObject
{ {
public enum ValueState public enum ValueState
{ {
@ -46,6 +47,7 @@ namespace UnityExplorer.UI.CacheObject
public bool SubContentShowWanted { get; private set; } public bool SubContentShowWanted { get; private set; }
public string NameLabelText { get; protected set; } public string NameLabelText { get; protected set; }
public string NameLabelTextRaw { get; protected set; }
public string ValueLabelText { get; protected set; } public string ValueLabelText { get; protected set; }
public abstract bool ShouldAutoEvaluate { get; } public abstract bool ShouldAutoEvaluate { get; }
@ -180,8 +182,8 @@ namespace UnityExplorer.UI.CacheObject
return ValueState.Enum; return ValueState.Enum;
else if (type == typeof(Color) || type == typeof(Color32)) else if (type == typeof(Color) || type == typeof(Color32))
return ValueState.Color; return ValueState.Color;
else if (InteractiveValueStruct.SupportsType(type)) else if (InteractiveValueStruct.SupportsType(type))
return ValueState.ValueStruct; return ValueState.ValueStruct;
else if (ReflectionUtility.IsDictionary(type)) else if (ReflectionUtility.IsDictionary(type))
return ValueState.Dictionary; return ValueState.Dictionary;
else if (!typeof(Transform).IsAssignableFrom(type) && ReflectionUtility.IsEnumerable(type)) else if (!typeof(Transform).IsAssignableFrom(type) && ReflectionUtility.IsEnumerable(type))
@ -197,7 +199,7 @@ namespace UnityExplorer.UI.CacheObject
switch (State) switch (State)
{ {
case ValueState.NotEvaluated: case ValueState.NotEvaluated:
return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>"; return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>";
case ValueState.Exception: case ValueState.Exception:
return $"<i><color=red>{LastException.ReflectionExToString()}</color></i>"; return $"<i><color=red>{LastException.ReflectionExToString()}</color></i>";
@ -221,7 +223,7 @@ namespace UnityExplorer.UI.CacheObject
return $"\"{ToStringUtility.PruneString(s, 200, 5)}\""; return $"\"{ToStringUtility.PruneString(s, 200, 5)}\"";
} }
break; break;
// try to prefix the count of the collection for lists and dicts // try to prefix the count of the collection for lists and dicts
case ValueState.Collection: case ValueState.Collection:
if (!LastValueWasNull) if (!LastValueWasNull)
@ -259,11 +261,13 @@ namespace UnityExplorer.UI.CacheObject
public virtual void SetDataToCell(CacheObjectCell cell) public virtual void SetDataToCell(CacheObjectCell cell)
{ {
cell.NameLabel.text = NameLabelText; cell.NameLabel.text = NameLabelText;
if (cell.HiddenNameLabel != null)
cell.HiddenNameLabel.Text = NameLabelTextRaw;
cell.ValueLabel.gameObject.SetActive(true); cell.ValueLabel.gameObject.SetActive(true);
cell.SubContentHolder.gameObject.SetActive(SubContentShowWanted); cell.SubContentHolder.gameObject.SetActive(SubContentShowWanted);
if (IValue != null) if (IValue != null)
{ {
IValue.UIRoot.transform.SetParent(cell.SubContentHolder.transform, false); IValue.UIRoot.transform.SetParent(cell.SubContentHolder.transform, false);
IValue.SetLayout(); IValue.SetLayout();
} }

View File

@ -3,9 +3,9 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using UnityExplorer.UI.Inspectors; using UnityExplorer.Inspectors;
namespace UnityExplorer.UI.CacheObject namespace UnityExplorer.CacheObject
{ {
public class CacheProperty : CacheMember public class CacheProperty : CacheMember
{ {
@ -31,7 +31,7 @@ namespace UnityExplorer.UI.CacheObject
object ret; object ret;
if (HasArguments) if (HasArguments)
ret = PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments()); ret = PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments());
else else
ret = PropertyInfo.GetValue(DeclaringInstance, null); ret = PropertyInfo.GetValue(DeclaringInstance, null);
HadException = false; HadException = false;
LastException = null; LastException = null;

View File

@ -3,10 +3,10 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityExplorer.UI.CacheObject; using UnityExplorer.CacheObject;
using UnityExplorer.UI.CacheObject.Views; using UnityExplorer.CacheObject.Views;
namespace UnityExplorer.UI.CacheObject namespace UnityExplorer.CacheObject
{ {
public interface ICacheObjectController public interface ICacheObjectController
{ {

View File

@ -4,9 +4,10 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.CacheObject; using UnityExplorer.CacheObject;
using UnityExplorer.UI;
namespace UnityExplorer.UI.CacheObject.IValues namespace UnityExplorer.CacheObject.IValues
{ {
public class InteractiveColor : InteractiveValue public class InteractiveColor : InteractiveValue
{ {

View File

@ -5,13 +5,14 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.CacheObject; using UnityExplorer.CacheObject;
using UnityExplorer.UI.CacheObject.Views; using UnityExplorer.CacheObject.Views;
using UnityExplorer.UI.Inspectors; using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.CacheObject.IValues namespace UnityExplorer.CacheObject.IValues
{ {
public class InteractiveDictionary : InteractiveValue, ICellPoolDataSource<CacheKeyValuePairCell>, ICacheObjectController public class InteractiveDictionary : InteractiveValue, ICellPoolDataSource<CacheKeyValuePairCell>, ICacheObjectController
{ {
@ -168,7 +169,6 @@ namespace UnityExplorer.UI.CacheObject.IValues
} }
public int AdjustedWidth => (int)UIRect.rect.width - 80; public int AdjustedWidth => (int)UIRect.rect.width - 80;
//public int AdjustedKeyWidth => HalfWidth - 50;
public override void SetLayout() public override void SetLayout()
{ {

View File

@ -5,9 +5,10 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.CacheObject; using UnityExplorer.CacheObject;
using UnityExplorer.UI;
namespace UnityExplorer.UI.CacheObject.IValues namespace UnityExplorer.CacheObject.IValues
{ {
public class InteractiveEnum : InteractiveValue public class InteractiveEnum : InteractiveValue
{ {
@ -20,7 +21,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
public CachedEnumValue ValueAtIdx(int idx) => (CachedEnumValue)CurrentValues[idx]; public CachedEnumValue ValueAtIdx(int idx) => (CachedEnumValue)CurrentValues[idx];
public CachedEnumValue ValueAtKey(object key) => (CachedEnumValue)CurrentValues[key]; public CachedEnumValue ValueAtKey(object key) => (CachedEnumValue)CurrentValues[key];
private Dropdown enumDropdown; private Dropdown enumDropdown;
private GameObject toggleHolder; private GameObject toggleHolder;
private readonly List<Toggle> flagToggles = new List<Toggle>(); private readonly List<Toggle> flagToggles = new List<Toggle>();

View File

@ -5,13 +5,14 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.CacheObject; using UnityExplorer.CacheObject;
using UnityExplorer.UI.CacheObject.Views; using UnityExplorer.CacheObject.Views;
using UnityExplorer.UI.Inspectors; using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.CacheObject.IValues namespace UnityExplorer.CacheObject.IValues
{ {
public class InteractiveList : InteractiveValue, ICellPoolDataSource<CacheListEntryCell>, ICacheObjectController public class InteractiveList : InteractiveValue, ICellPoolDataSource<CacheListEntryCell>, ICacheObjectController
{ {
@ -206,7 +207,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
{ {
ExplorerCore.LogWarning($"Exception setting IList value: {ex}"); ExplorerCore.LogWarning($"Exception setting IList value: {ex}");
} }
} }
// List entry scroll pool // List entry scroll pool
@ -254,8 +255,8 @@ namespace UnityExplorer.UI.CacheObject.IValues
ListScrollPool.Initialize(this, SetLayout); ListScrollPool.Initialize(this, SetLayout);
scrollLayout = scrollObj.GetComponent<LayoutElement>(); scrollLayout = scrollObj.GetComponent<LayoutElement>();
NotSupportedLabel = UIFactory.CreateLabel(ListScrollPool.Content.gameObject, "NotSupportedMessage", NotSupportedLabel = UIFactory.CreateLabel(ListScrollPool.Content.gameObject, "NotSupportedMessage",
"The IEnumerable failed to enumerate. This is likely due to an issue with Unhollowed interfaces.", "The IEnumerable failed to enumerate. This is likely due to an issue with Unhollowed interfaces.",
TextAnchor.MiddleLeft, Color.red); TextAnchor.MiddleLeft, Color.red);
UIFactory.SetLayoutElement(NotSupportedLabel.gameObject, minHeight: 25, flexibleWidth: 9999); UIFactory.SetLayoutElement(NotSupportedLabel.gameObject, minHeight: 25, flexibleWidth: 9999);

View File

@ -6,10 +6,11 @@ using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.Core.Config; using UnityExplorer.Core.Config;
using UnityExplorer.UI.CacheObject; using UnityExplorer.CacheObject;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
using UnityExplorer.UI;
namespace UnityExplorer.UI.CacheObject.IValues namespace UnityExplorer.CacheObject.IValues
{ {
public class InteractiveString : InteractiveValue public class InteractiveString : InteractiveValue
{ {
@ -18,7 +19,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
public InputFieldRef inputField; public InputFieldRef inputField;
public ButtonRef ApplyButton; public ButtonRef ApplyButton;
public GameObject SaveFileRow; public GameObject SaveFileRow;
public InputFieldRef SaveFilePath; public InputFieldRef SaveFilePath;
@ -36,7 +37,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
{ {
if (s == null) if (s == null)
return false; return false;
return s.Length >= UIManager.MAX_INPUTFIELD_CHARS; return s.Length >= UIManager.MAX_INPUTFIELD_CHARS;
} }
@ -79,11 +80,11 @@ namespace UnityExplorer.UI.CacheObject.IValues
return; return;
} }
var path = IOUtility.EnsureValidDirectory(SaveFilePath.Text); var path = IOUtility.EnsureValidFilePath(SaveFilePath.Text);
if (File.Exists(path)) if (File.Exists(path))
File.Delete(path); File.Delete(path);
File.WriteAllText(path, RealValue); File.WriteAllText(path, RealValue);
} }
@ -98,7 +99,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
UIFactory.SetLayoutElement(SaveFileRow, flexibleWidth: 9999); UIFactory.SetLayoutElement(SaveFileRow, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(SaveFileRow, false, true, true, true, 3); UIFactory.SetLayoutGroup<VerticalLayoutGroup>(SaveFileRow, false, true, true, true, 3);
UIFactory.CreateLabel(SaveFileRow, "Info", "<color=red>String is too long! Save to file if you want to see the full string.</color>", UIFactory.CreateLabel(SaveFileRow, "Info", "<color=red>String is too long! Save to file if you want to see the full string.</color>",
TextAnchor.MiddleLeft); TextAnchor.MiddleLeft);
var horizRow = UIFactory.CreateUIObject("Horiz", SaveFileRow); var horizRow = UIFactory.CreateUIObject("Horiz", SaveFileRow);

View File

@ -4,10 +4,11 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.CacheObject; using UnityExplorer.CacheObject;
using UnityExplorer.UI;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.CacheObject.IValues namespace UnityExplorer.CacheObject.IValues
{ {
public abstract class InteractiveValue : IPooledObject public abstract class InteractiveValue : IPooledObject
{ {

View File

@ -5,9 +5,10 @@ using System.Reflection;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.CacheObject; using UnityExplorer.CacheObject;
using UnityExplorer.UI;
namespace UnityExplorer.UI.CacheObject.IValues namespace UnityExplorer.CacheObject.IValues
{ {
public class InteractiveValueStruct : InteractiveValue public class InteractiveValueStruct : InteractiveValue
{ {
@ -143,7 +144,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
ExplorerCore.LogWarning("Exception setting value: " + ex); ExplorerCore.LogWarning("Exception setting value: " + ex);
} }
} }
// UI Setup for type // UI Setup for type
private void SetupUIForType() private void SetupUIForType()

View File

@ -4,8 +4,9 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI;
namespace UnityExplorer.UI.CacheObject.Views namespace UnityExplorer.CacheObject.Views
{ {
public class ConfigEntryCell : CacheObjectCell public class ConfigEntryCell : CacheObjectCell
{ {

View File

@ -4,11 +4,12 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Inspectors; using UnityExplorer.CacheObject.IValues;
using UnityExplorer.UI.CacheObject.IValues; using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.CacheObject.Views namespace UnityExplorer.CacheObject.Views
{ {
public class CacheKeyValuePairCell : CacheObjectCell public class CacheKeyValuePairCell : CacheObjectCell
{ {

View File

@ -4,9 +4,9 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.CacheObject.IValues; using UnityExplorer.CacheObject.IValues;
namespace UnityExplorer.UI.CacheObject.Views namespace UnityExplorer.CacheObject.Views
{ {
public class CacheListEntryCell : CacheObjectCell public class CacheListEntryCell : CacheObjectCell
{ {

View File

@ -4,9 +4,10 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.CacheObject.Views namespace UnityExplorer.CacheObject.Views
{ {
public class CacheMemberCell : CacheObjectCell public class CacheMemberCell : CacheObjectCell
{ {

View File

@ -4,11 +4,12 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Inspectors; using UnityExplorer.CacheObject.IValues;
using UnityExplorer.UI.CacheObject.IValues; using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.CacheObject.Views namespace UnityExplorer.CacheObject.Views
{ {
public abstract class CacheObjectCell : ICell public abstract class CacheObjectCell : ICell
{ {
@ -44,6 +45,7 @@ namespace UnityExplorer.UI.CacheObject.Views
public LayoutElement RightGroupLayout; public LayoutElement RightGroupLayout;
public Text NameLabel; public Text NameLabel;
public InputFieldRef HiddenNameLabel;
public Text TypeLabel; public Text TypeLabel;
public Text ValueLabel; public Text ValueLabel;
public Toggle Toggle; public Toggle Toggle;
@ -115,8 +117,19 @@ namespace UnityExplorer.UI.CacheObject.Views
NameLabel = UIFactory.CreateLabel(horiRow, "NameLabel", "<notset>", TextAnchor.MiddleLeft); NameLabel = UIFactory.CreateLabel(horiRow, "NameLabel", "<notset>", TextAnchor.MiddleLeft);
NameLabel.horizontalOverflow = HorizontalWrapMode.Wrap; NameLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
UIFactory.SetLayoutElement(NameLabel.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0); NameLayout = UIFactory.SetLayoutElement(NameLabel.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0);
NameLayout = NameLabel.GetComponent<LayoutElement>(); UIFactory.SetLayoutGroup<VerticalLayoutGroup>(NameLabel.gameObject, true, true, true, true);
HiddenNameLabel = UIFactory.CreateInputField(NameLabel.gameObject, "HiddenNameLabel", "");
var hiddenRect = HiddenNameLabel.Component.GetComponent<RectTransform>();
hiddenRect.anchorMin = Vector2.zero;
hiddenRect.anchorMax = Vector2.one;
HiddenNameLabel.Component.readOnly = true;
HiddenNameLabel.Component.lineType = UnityEngine.UI.InputField.LineType.MultiLineNewline;
HiddenNameLabel.Component.textComponent.horizontalOverflow = HorizontalWrapMode.Wrap;
HiddenNameLabel.Component.gameObject.GetComponent<Image>().color = Color.clear;
HiddenNameLabel.Component.textComponent.color = Color.clear;
UIFactory.SetLayoutElement(HiddenNameLabel.Component.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0);
// Right vertical group // Right vertical group

View File

@ -5,10 +5,11 @@ using System.Reflection;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
using UnityExplorer.UI.Widgets.AutoComplete; using UnityExplorer.UI.Widgets.AutoComplete;
namespace UnityExplorer.UI.CacheObject.Views namespace UnityExplorer.CacheObject.Views
{ {
public class EvaluateWidget : IPooledObject public class EvaluateWidget : IPooledObject
{ {
@ -67,7 +68,7 @@ namespace UnityExplorer.UI.CacheObject.Views
for (int i = 0; i < genericArguments.Length; i++) for (int i = 0; i < genericArguments.Length; i++)
{ {
outArgs[i] = ReflectionUtility.GetTypeByName(genericInput[i]) outArgs[i] = ReflectionUtility.GetTypeByName(genericInput[i])
?? throw new Exception($"Could not find any type by name '{genericInput[i]}'!"); ?? throw new Exception($"Could not find any type by name '{genericInput[i]}'!");
} }
@ -206,7 +207,7 @@ namespace UnityExplorer.UI.CacheObject.Views
var elemType = arg.ParameterType; var elemType = arg.ParameterType;
if (elemType.IsByRef) if (elemType.IsByRef)
elemType = elemType.GetElementType(); elemType = elemType.GetElementType();
argInputFields[i].PlaceholderText.text = $"eg. {ParseUtility.GetExampleInput(elemType)}"; argInputFields[i].PlaceholderText.text = $"eg. {ParseUtility.GetExampleInput(elemType)}";
} }
} }
} }
@ -249,7 +250,7 @@ namespace UnityExplorer.UI.CacheObject.Views
public GameObject CreateContent(GameObject parent) public GameObject CreateContent(GameObject parent)
{ {
UIRoot = UIFactory.CreateVerticalGroup(parent, "EvaluateWidget", false, false, true, true, 3, new Vector4(2, 2, 2, 2), UIRoot = UIFactory.CreateVerticalGroup(parent, "EvaluateWidget", false, false, true, true, 3, new Vector4(2, 2, 2, 2),
new Color(0.15f, 0.15f, 0.15f)); new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(UIRoot, minWidth: 50, flexibleWidth: 9999, minHeight: 50, flexibleHeight: 800); UIFactory.SetLayoutElement(UIRoot, minWidth: 50, flexibleWidth: 9999, minHeight: 50, flexibleHeight: 800);
//UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize; //UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
@ -275,7 +276,7 @@ namespace UnityExplorer.UI.CacheObject.Views
// evaluate button // evaluate button
var evalButton = UIFactory.CreateButton(UIRoot, "EvaluateButton", "Evaluate", new Color(0.2f, 0.2f, 0.2f)); var evalButton = UIFactory.CreateButton(UIRoot, "EvaluateButton", "Evaluate", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(evalButton.Component.gameObject, minHeight: 25, minWidth: 150, flexibleWidth: 0); UIFactory.SetLayoutElement(evalButton.Component.gameObject, minHeight: 25, minWidth: 150, flexibleWidth: 0);
evalButton.OnClick += () => evalButton.OnClick += () =>
{ {
Owner.EvaluateAndSetCell(); Owner.EvaluateAndSetCell();
}; };

View File

@ -16,18 +16,18 @@ namespace UnityExplorer.Core.Config
// See the UnityExplorer.Loader namespace for the implementations. // See the UnityExplorer.Loader namespace for the implementations.
public static ConfigHandler Handler { get; private set; } public static ConfigHandler Handler { get; private set; }
public static ConfigElement<KeyCode> Master_Toggle; public static ConfigElement<KeyCode> Master_Toggle;
public static ConfigElement<UIManager.VerticalAnchor> Main_Navbar_Anchor; public static ConfigElement<UIManager.VerticalAnchor> Main_Navbar_Anchor;
public static ConfigElement<bool> Force_Unlock_Mouse; public static ConfigElement<bool> Force_Unlock_Mouse;
public static ConfigElement<KeyCode> Force_Unlock_Toggle; public static ConfigElement<KeyCode> Force_Unlock_Toggle;
public static ConfigElement<bool> Aggressive_Mouse_Unlock; public static ConfigElement<bool> Aggressive_Mouse_Unlock;
public static ConfigElement<bool> Disable_EventSystem_Override; public static ConfigElement<bool> Disable_EventSystem_Override;
public static ConfigElement<string> Default_Output_Path; public static ConfigElement<string> Default_Output_Path;
public static ConfigElement<bool> Log_Unity_Debug; public static ConfigElement<bool> Log_Unity_Debug;
public static ConfigElement<bool> Hide_On_Startup; public static ConfigElement<bool> Hide_On_Startup;
public static ConfigElement<float> Startup_Delay_Time; public static ConfigElement<float> Startup_Delay_Time;
public static ConfigElement<string> Reflection_Signature_Blacklist; public static ConfigElement<string> Reflection_Signature_Blacklist;
// internal configs // internal configs
internal static InternalConfigHandler InternalHandler { get; private set; } internal static InternalConfigHandler InternalHandler { get; private set; }
@ -37,6 +37,7 @@ namespace UnityExplorer.Core.Config
public static ConfigElement<string> CSConsoleData; public static ConfigElement<string> CSConsoleData;
public static ConfigElement<string> OptionsPanelData; public static ConfigElement<string> OptionsPanelData;
public static ConfigElement<string> ConsoleLogData; public static ConfigElement<string> ConsoleLogData;
public static ConfigElement<string> HookManagerData;
internal static readonly Dictionary<string, IConfigElement> ConfigElements = new Dictionary<string, IConfigElement>(); internal static readonly Dictionary<string, IConfigElement> ConfigElements = new Dictionary<string, IConfigElement>();
internal static readonly Dictionary<string, IConfigElement> InternalConfigs = new Dictionary<string, IConfigElement>(); internal static readonly Dictionary<string, IConfigElement> InternalConfigs = new Dictionary<string, IConfigElement>();
@ -116,7 +117,7 @@ namespace UnityExplorer.Core.Config
Reflection_Signature_Blacklist = new ConfigElement<string>("Member Signature Blacklist", Reflection_Signature_Blacklist = new ConfigElement<string>("Member Signature Blacklist",
"Use this to blacklist certain member signatures if they are known to cause a crash or other issues.\r\n" + "Use this to blacklist certain member signatures if they are known to cause a crash or other issues.\r\n" +
"Seperate signatures with a semicolon ';'.\r\n" + "Seperate signatures with a semicolon ';'.\r\n" +
"For example, to blacklist Camera.main, you would add 'Camera.main;'", "For example, to blacklist Camera.main, you would add 'UnityEngine.Camera.main;'",
""); "");
// Internal configs (panel save data) // Internal configs (panel save data)
@ -126,6 +127,7 @@ namespace UnityExplorer.Core.Config
CSConsoleData = new ConfigElement<string>("CSConsole", "", "", true); CSConsoleData = new ConfigElement<string>("CSConsole", "", "", true);
OptionsPanelData = new ConfigElement<string>("OptionsPanel", "", "", true); OptionsPanelData = new ConfigElement<string>("OptionsPanel", "", "", true);
ConsoleLogData = new ConfigElement<string>("ConsoleLog", "", "", true); ConsoleLogData = new ConfigElement<string>("ConsoleLog", "", "", true);
HookManagerData = new ConfigElement<string>("HookManager", "", "", true);
} }
} }
} }

View File

@ -87,9 +87,6 @@ namespace UnityExplorer.Core.Config
foreach (var entry in ConfigManager.InternalConfigs) foreach (var entry in ConfigManager.InternalConfigs)
sec.AddKey(entry.Key, entry.Value.BoxedValue.ToString()); 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()); File.WriteAllText(INI_PATH, data.ToString());
} }

View File

@ -1,13 +1,12 @@
using System; using HarmonyLib;
using System;
using System.Collections;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityExplorer.Core.Input;
using BF = System.Reflection.BindingFlags;
using UnityExplorer.Core.Config;
using UnityExplorer.Core; using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UnityExplorer.UI; using UnityExplorer.UI;
using System.Collections;
namespace UnityExplorer.Core.Input namespace UnityExplorer.Core.Input
{ {
@ -114,9 +113,6 @@ namespace UnityExplorer.Core.Input
public static void SetEventSystem() public static void SetEventSystem()
{ {
if (InputManager.CurrentType == InputType.InputSystem)
return;
if (EventSystem.current && EventSystem.current != UIManager.EventSys) if (EventSystem.current && EventSystem.current != UIManager.EventSys)
{ {
lastEventSystem = EventSystem.current; lastEventSystem = EventSystem.current;
@ -133,14 +129,12 @@ namespace UnityExplorer.Core.Input
public static void ReleaseEventSystem() public static void ReleaseEventSystem()
{ {
if (InputManager.CurrentType == InputType.InputSystem)
return;
if (lastEventSystem && lastEventSystem.gameObject.activeSelf) if (lastEventSystem && lastEventSystem.gameObject.activeSelf)
{ {
lastEventSystem.enabled = true; lastEventSystem.enabled = true;
settingEventSystem = true; settingEventSystem = true;
UIManager.EventSys.enabled = false;
EventSystem.current = lastEventSystem; EventSystem.current = lastEventSystem;
lastInputModule?.ActivateModule(); lastInputModule?.ActivateModule();
settingEventSystem = false; settingEventSystem = false;
@ -149,18 +143,97 @@ namespace UnityExplorer.Core.Input
// Patches // Patches
private static void SetupPatches() public static void SetupPatches()
{ {
try try
{ {
ExplorerCore.Loader.SetupCursorPatches(); PrefixPropertySetter(typeof(Cursor),
"lockState",
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_lockState))));
PrefixPropertySetter(typeof(Cursor),
"visible",
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_visible))));
PrefixPropertySetter(typeof(EventSystem),
"current",
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_EventSystem_set_current))));
PrefixMethod(typeof(EventSystem),
"SetSelectedGameObject",
// some games use a modified version of uGUI that includes this extra int argument on this method.
new Type[] { typeof(GameObject), typeof(BaseEventData), typeof(int) },
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_EventSystem_SetSelectedGameObject))),
// most games use these arguments, we'll use them as our "backup".
new Type[] { typeof(GameObject), typeof(BaseEventData) });
//// Not sure if this one is needed.
//PrefixMethod(typeof(PointerInputModule),
// "ClearSelection",
// new Type[0],
// new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_PointerInputModule_ClearSelection))));
}
catch (Exception ex)
{
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
}
}
private static void PrefixMethod(Type type, string method, Type[] arguments, HarmonyMethod prefix, Type[] backupArgs = null)
{
try
{
var methodInfo = type.GetMethod(method, ReflectionUtility.FLAGS, null, arguments, null);
if (methodInfo == null)
{
if (backupArgs != null)
methodInfo = type.GetMethod(method, ReflectionUtility.FLAGS, null, backupArgs, null);
if (methodInfo == null)
throw new MissingMethodException($"Could not find method for patching - '{type.FullName}.{method}'!");
}
var processor = ExplorerCore.Harmony.CreateProcessor(methodInfo);
processor.AddPrefix(prefix);
processor.Patch();
} }
catch (Exception e) catch (Exception e)
{ {
ExplorerCore.Log($"Exception setting up Cursor patches: {e.GetType()}, {e.Message}"); ExplorerCore.LogWarning($"Unable to patch {type.Name}.{method}: {e.Message}");
} }
} }
private static void PrefixPropertySetter(Type type, string property, HarmonyMethod prefix)
{
try
{
var processor = ExplorerCore.Harmony.CreateProcessor(type.GetProperty(property, ReflectionUtility.FLAGS).GetSetMethod());
processor.AddPrefix(prefix);
processor.Patch();
}
catch (Exception e)
{
ExplorerCore.Log($"Unable to patch {type.Name}.set_{property}: {e.Message}");
}
}
// Prevent setting non-UnityExplorer objects as selected when menu is open
public static bool Prefix_EventSystem_SetSelectedGameObject(GameObject __0)
{
if (!UIManager.ShowMenu || !UIManager.CanvasRoot)
return true;
return __0 && __0.transform.root.gameObject.GetInstanceID() == UIManager.CanvasRoot.GetInstanceID();
}
//public static bool Prefix_PointerInputModule_ClearSelection()
//{
// return !(UIManager.ShowMenu && UIManager.CanvasRoot);
//}
// Force EventSystem.current to be UnityExplorer's when menu is open
public static void Prefix_EventSystem_set_current(ref EventSystem value) public static void Prefix_EventSystem_set_current(ref EventSystem value)
{ {
if (!settingEventSystem && value) if (!settingEventSystem && value)

View File

@ -14,7 +14,7 @@ namespace UnityExplorer.Core.Input
bool GetMouseButtonDown(int btn); bool GetMouseButtonDown(int btn);
bool GetMouseButton(int btn); bool GetMouseButton(int btn);
BaseInputModule UIModule { get; } BaseInputModule UIInputModule { get; }
void AddUIInputModule(); void AddUIInputModule();
void ActivateModule(); void ActivateModule();

View File

@ -1,7 +1,8 @@
using System; using System;
using UnityEngine;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityExplorer.UI;
namespace UnityExplorer.Core.Input namespace UnityExplorer.Core.Input
{ {
@ -16,39 +17,43 @@ namespace UnityExplorer.Core.Input
{ {
public static InputType CurrentType { get; private set; } public static InputType CurrentType { get; private set; }
private static IHandleInput m_inputModule; private static IHandleInput m_inputHandler;
public static Vector3 MousePosition => m_inputModule.MousePosition; public static Vector3 MousePosition => m_inputHandler.MousePosition;
public static bool GetKeyDown(KeyCode key) public static bool GetKeyDown(KeyCode key)
{ {
if (key == KeyCode.None) if (key == KeyCode.None)
return false; return false;
return m_inputModule.GetKeyDown(key); return m_inputHandler.GetKeyDown(key);
} }
public static bool GetKey(KeyCode key) public static bool GetKey(KeyCode key)
{ {
if (key == KeyCode.None) if (key == KeyCode.None)
return false; return false;
return m_inputModule.GetKey(key); return m_inputHandler.GetKey(key);
} }
public static bool GetMouseButtonDown(int btn) => m_inputModule.GetMouseButtonDown(btn); public static bool GetMouseButtonDown(int btn) => m_inputHandler.GetMouseButtonDown(btn);
public static bool GetMouseButton(int btn) => m_inputModule.GetMouseButton(btn); public static bool GetMouseButton(int btn) => m_inputHandler.GetMouseButton(btn);
public static BaseInputModule UIInput => m_inputModule.UIModule; public static BaseInputModule UIInput => m_inputHandler.UIInputModule;
public static Vector2 MouseScrollDelta => m_inputModule.MouseScrollDelta; public static Vector2 MouseScrollDelta => m_inputHandler.MouseScrollDelta;
public static void ActivateUIModule() => m_inputModule.ActivateModule();
public static void AddUIModule() public static void AddUIModule()
{ {
m_inputModule.AddUIInputModule(); m_inputHandler.AddUIInputModule();
ActivateUIModule(); ActivateUIModule();
} }
public static void ActivateUIModule()
{
UIManager.EventSys.m_CurrentInputModule = UIInput;
m_inputHandler.ActivateModule();
}
public static void Init() public static void Init()
{ {
InitHandler(); InitHandler();
@ -65,7 +70,7 @@ namespace UnityExplorer.Core.Input
{ {
try try
{ {
m_inputModule = new LegacyInput(); m_inputHandler = new LegacyInput();
CurrentType = InputType.Legacy; CurrentType = InputType.Legacy;
// make sure its working // make sure its working
@ -74,7 +79,7 @@ namespace UnityExplorer.Core.Input
ExplorerCore.Log("Initialized Legacy Input support"); ExplorerCore.Log("Initialized Legacy Input support");
return; return;
} }
catch catch
{ {
// It's not working, we'll fall back to InputSystem. // It's not working, we'll fall back to InputSystem.
} }
@ -84,7 +89,7 @@ namespace UnityExplorer.Core.Input
{ {
try try
{ {
m_inputModule = new InputSystem(); m_inputHandler = new InputSystem();
CurrentType = InputType.InputSystem; CurrentType = InputType.InputSystem;
ExplorerCore.Log("Initialized new InputSystem support."); ExplorerCore.Log("Initialized new InputSystem support.");
return; return;
@ -96,7 +101,7 @@ namespace UnityExplorer.Core.Input
} }
ExplorerCore.LogWarning("Could not find any Input Module Type!"); ExplorerCore.LogWarning("Could not find any Input Module Type!");
m_inputModule = new NoInput(); m_inputHandler = new NoInput();
CurrentType = InputType.None; CurrentType = InputType.None;
} }
} }

View File

@ -1,10 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityExplorer.UI; using UnityExplorer.UI;
using System.Collections.Generic;
using System.Linq;
namespace UnityExplorer.Core.Input namespace UnityExplorer.Core.Input
{ {
@ -12,6 +12,8 @@ namespace UnityExplorer.Core.Input
{ {
public InputSystem() public InputSystem()
{ {
SetupSupportedDevices();
m_kbCurrentProp = TKeyboard.GetProperty("current"); m_kbCurrentProp = TKeyboard.GetProperty("current");
m_kbIndexer = TKeyboard.GetProperty("Item", new Type[] { TKey }); m_kbIndexer = TKeyboard.GetProperty("Item", new Type[] { TKey });
@ -32,7 +34,37 @@ namespace UnityExplorer.Core.Input
.GetMethod("ReadValue"); .GetMethod("ReadValue");
} }
#region reflection cache internal static void SetupSupportedDevices()
{
try
{
// typeof(InputSystem)
Type TInputSystem = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputSystem");
// InputSystem.settings
var settings = TInputSystem.GetProperty("settings", BindingFlags.Public | BindingFlags.Static).GetValue(null, null);
// typeof(InputSettings)
Type TSettings = settings.GetActualType();
// InputSettings.supportedDevices
PropertyInfo supportedProp = TSettings.GetProperty("supportedDevices", BindingFlags.Public | BindingFlags.Instance);
var supportedDevices = supportedProp.GetValue(settings, null);
// An empty supportedDevices list means all devices are supported.
#if CPP
// weird hack for il2cpp, use the implicit operator and cast Il2CppStringArray to ReadOnlyArray<string>
var args = new object[] { new UnhollowerBaseLib.Il2CppStringArray(0) };
var method = supportedDevices.GetActualType().GetMethod("op_Implicit", BindingFlags.Static | BindingFlags.Public);
supportedProp.SetValue(settings, method.Invoke(null, args), null);
#else
supportedProp.SetValue(settings, Activator.CreateInstance(supportedDevices.GetActualType(), new object[] { new string[0] }), null);
#endif
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception setting up InputSystem.settings.supportedDevices list!");
ExplorerCore.Log(ex);
}
}
#region reflection cache
public static Type TKeyboard => m_tKeyboard ?? (m_tKeyboard = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Keyboard")); public static Type TKeyboard => m_tKeyboard ?? (m_tKeyboard = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Keyboard"));
private static Type m_tKeyboard; private static Type m_tKeyboard;
@ -73,7 +105,7 @@ namespace UnityExplorer.Core.Input
private static object m_scrollInfo; private static object m_scrollInfo;
private static PropertyInfo m_scrollDeltaProp; private static PropertyInfo m_scrollDeltaProp;
#endregion #endregion
public Vector2 MousePosition public Vector2 MousePosition
{ {
@ -138,6 +170,8 @@ namespace UnityExplorer.Core.Input
public bool GetMouseButtonDown(int btn) public bool GetMouseButtonDown(int btn)
{ {
if (CurrentMouse == null)
return false;
switch (btn) switch (btn)
{ {
case 0: return (bool)m_btnWasPressedProp.GetValue(LeftMouseButton, null); case 0: return (bool)m_btnWasPressedProp.GetValue(LeftMouseButton, null);
@ -149,6 +183,8 @@ namespace UnityExplorer.Core.Input
public bool GetMouseButton(int btn) public bool GetMouseButton(int btn)
{ {
if (CurrentMouse == null)
return false;
switch (btn) switch (btn)
{ {
case 0: return (bool)m_btnIsPressedProp.GetValue(LeftMouseButton, null); case 0: return (bool)m_btnIsPressedProp.GetValue(LeftMouseButton, null);
@ -165,7 +201,7 @@ namespace UnityExplorer.Core.Input
?? (m_tUIInputModule = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.UI.InputSystemUIInputModule")); ?? (m_tUIInputModule = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.UI.InputSystemUIInputModule"));
internal Type m_tUIInputModule; internal Type m_tUIInputModule;
public BaseInputModule UIModule => m_newInputModule; public BaseInputModule UIInputModule => m_newInputModule;
internal BaseInputModule m_newInputModule; internal BaseInputModule m_newInputModule;
public void AddUIInputModule() public void AddUIInputModule()
@ -203,6 +239,9 @@ namespace UnityExplorer.Core.Input
private void CreateAction(object map, string actionName, string[] bindings, string propertyName) private void CreateAction(object map, string actionName, string[] bindings, string propertyName)
{ {
var disable = map.GetType().GetMethod("Disable");
disable.Invoke(map, ArgumentUtility.EmptyArgs);
var inputActionType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputAction"); var inputActionType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputAction");
var addAction = inputExtensions.GetMethod("AddAction"); var addAction = inputExtensions.GetMethod("AddAction");
var action = addAction.Invoke(null, new object[] { map, actionName, default, null, null, null, null, null }) var action = addAction.Invoke(null, new object[] { map, actionName, default, null, null, null, null, null })
@ -226,8 +265,16 @@ namespace UnityExplorer.Core.Input
public void ActivateModule() public void ActivateModule()
{ {
m_newInputModule.ActivateModule(); try
UI_Enable.Invoke(UI_ActionMap, ArgumentUtility.EmptyArgs); {
m_newInputModule.m_EventSystem = UIManager.EventSys;
m_newInputModule.ActivateModule();
UI_Enable.Invoke(UI_ActionMap, ArgumentUtility.EmptyArgs);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception enabling InputSystem UI Input Module: " + ex);
}
} }
} }
} }

View File

@ -42,17 +42,25 @@ namespace UnityExplorer.Core.Input
// UI Input module // UI Input module
public BaseInputModule UIModule => m_inputModule; public BaseInputModule UIInputModule => m_inputModule;
internal StandaloneInputModule m_inputModule; internal StandaloneInputModule m_inputModule;
public void AddUIInputModule() public void AddUIInputModule()
{ {
m_inputModule = UIManager.CanvasRoot.gameObject.AddComponent<StandaloneInputModule>(); m_inputModule = UIManager.CanvasRoot.gameObject.AddComponent<StandaloneInputModule>();
m_inputModule.m_EventSystem = UIManager.EventSys;
} }
public void ActivateModule() public void ActivateModule()
{ {
m_inputModule.ActivateModule(); try
{
m_inputModule.ActivateModule();
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception enabling StandaloneInputModule: {ex}");
}
} }
} }
} }

View File

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

View File

@ -31,9 +31,6 @@ namespace UnityExplorer
} }
} }
public static HashSet<Type> GetImplementationsOf(this Type baseType, bool allowAbstract, bool allowGeneric)
=> ReflectionUtility.GetImplementationsOf(baseType, allowAbstract, allowGeneric);
// ------- Misc extensions -------- // ------- Misc extensions --------
/// <summary> /// <summary>
@ -99,8 +96,10 @@ namespace UnityExplorer
public static Exception GetInnerMostException(this Exception e) public static Exception GetInnerMostException(this Exception e)
{ {
while (e.InnerException != null) while (e != null)
{ {
if (e.InnerException == null)
break;
#if CPP #if CPP
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException) if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException)
break; break;

View File

@ -14,7 +14,6 @@ using System.Diagnostics.CodeAnalysis;
using UnityExplorer.Core; using UnityExplorer.Core;
using CppType = Il2CppSystem.Type; using CppType = Il2CppSystem.Type;
using BF = System.Reflection.BindingFlags; using BF = System.Reflection.BindingFlags;
using UnityExplorer.Core.Config;
using UnhollowerBaseLib.Attributes; using UnhollowerBaseLib.Attributes;
using UnityEngine; using UnityEngine;
@ -37,7 +36,7 @@ namespace UnityExplorer
$"deobfuscated types count: {DeobfuscatedTypes.Count}"); $"deobfuscated types count: {DeobfuscatedTypes.Count}");
} }
#region IL2CPP Extern and pointers #region IL2CPP Extern and pointers
// Extern C++ methods // Extern C++ methods
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
@ -63,10 +62,10 @@ namespace UnityExplorer
return il2cppPtr != IntPtr.Zero; return il2cppPtr != IntPtr.Zero;
} }
#endregion #endregion
#region Deobfuscation cache #region Deobfuscation cache
private static readonly Dictionary<string, Type> DeobfuscatedTypes = new Dictionary<string, Type>(); private static readonly Dictionary<string, Type> DeobfuscatedTypes = new Dictionary<string, Type>();
private static readonly Dictionary<string, string> reverseDeobCache = new Dictionary<string, string>(); private static readonly Dictionary<string, string> reverseDeobCache = new Dictionary<string, string>();
@ -111,7 +110,7 @@ namespace UnityExplorer
return theString; return theString;
} }
#endregion #endregion
// Get type by name // Get type by name
@ -124,7 +123,7 @@ namespace UnityExplorer
return base.Internal_GetTypeByName(fullName); return base.Internal_GetTypeByName(fullName);
} }
#region Get actual type #region Get actual type
internal override Type Internal_GetActualType(object obj) internal override Type Internal_GetActualType(object obj)
{ {
@ -132,9 +131,11 @@ namespace UnityExplorer
return null; return null;
var type = obj.GetType(); var type = obj.GetType();
try try
{ {
if (type.IsGenericType)
return type;
if (IsString(obj)) if (IsString(obj))
return typeof(string); return typeof(string);
@ -179,14 +180,15 @@ namespace UnityExplorer
if (fullname.StartsWith("System.")) if (fullname.StartsWith("System."))
fullname = $"Il2Cpp{fullname}"; fullname = $"Il2Cpp{fullname}";
AllTypes.TryGetValue(fullname, out Type monoType); if (!AllTypes.TryGetValue(fullname, out Type monoType))
ExplorerCore.LogWarning($"Failed to get type by name '{fullname}'!");
return monoType; return monoType;
} }
#endregion #endregion
#region Casting #region Casting
private static readonly Dictionary<string, IntPtr> cppClassPointers = new Dictionary<string, IntPtr>(); private static readonly Dictionary<string, IntPtr> cppClassPointers = new Dictionary<string, IntPtr>();
@ -216,7 +218,7 @@ namespace UnityExplorer
// from other structs to il2cpp object // from other structs to il2cpp object
else if (typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) else if (typeof(Il2CppSystem.Object).IsAssignableFrom(castTo))
{ {
return BoxIl2CppObject(obj); return BoxIl2CppObject(obj).TryCast(castTo);
} }
else else
return obj; return obj;
@ -278,10 +280,10 @@ namespace UnityExplorer
// return il2cpp_class_is_assignable_from(thisTypePtr, fromTypePtr); // return il2cpp_class_is_assignable_from(thisTypePtr, fromTypePtr);
//} //}
#endregion #endregion
#region Boxing and unboxing ValueTypes #region Boxing and unboxing ValueTypes
// cached il2cpp unbox methods // cached il2cpp unbox methods
internal static readonly Dictionary<string, MethodInfo> unboxMethods = new Dictionary<string, MethodInfo>(); internal static readonly Dictionary<string, MethodInfo> unboxMethods = new Dictionary<string, MethodInfo>();
@ -295,7 +297,27 @@ namespace UnityExplorer
try try
{ {
if (toType.IsEnum) if (toType.IsEnum)
{
// Check for nullable enums
var type = cppObj.GetType();
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Il2CppSystem.Nullable<>))
{
var nullable = cppObj.TryCast(type);
var nullableHasValueProperty = type.GetProperty("HasValue");
if ((bool)nullableHasValueProperty.GetValue(nullable, null))
{
// nullable has a value.
var nullableValueProperty = type.GetProperty("Value");
return Enum.Parse(toType, nullableValueProperty.GetValue(nullable, null).ToString());
}
// nullable and no current value.
return cppObj;
}
return Enum.Parse(toType, cppObj.ToString()); return Enum.Parse(toType, cppObj.ToString());
}
// Not enum, unbox with Il2CppObjectBase.Unbox
var name = toType.AssemblyQualifiedName; var name = toType.AssemblyQualifiedName;
@ -384,10 +406,10 @@ namespace UnityExplorer
return cppStruct; return cppStruct;
} }
#endregion #endregion
#region String boxing/unboxing #region String boxing/unboxing
private const string IL2CPP_STRING_FULLNAME = "Il2CppSystem.String"; private const string IL2CPP_STRING_FULLNAME = "Il2CppSystem.String";
private const string STRING_FULLNAME = "System.String"; private const string STRING_FULLNAME = "System.String";
@ -428,10 +450,10 @@ namespace UnityExplorer
return s; return s;
} }
#endregion #endregion
#region Singleton finder #region Singleton finder
internal override void Internal_FindSingleton(string[] possibleNames, Type type, BF flags, List<object> instances) internal override void Internal_FindSingleton(string[] possibleNames, Type type, BF flags, List<object> instances)
{ {
@ -453,20 +475,10 @@ namespace UnityExplorer
base.Internal_FindSingleton(possibleNames, type, flags, instances); base.Internal_FindSingleton(possibleNames, type, flags, instances);
} }
#endregion #endregion
#region Force-loading game modules #region Force-loading game modules
internal static string UnhollowedFolderPath => Path.GetFullPath(
#if ML
Path.Combine("MelonLoader", "Managed")
#elif BIE
Path.Combine("BepInEx", "unhollowed")
#else
Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Modules")
#endif
);
// Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded. // Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
@ -474,49 +486,38 @@ namespace UnityExplorer
internal void TryLoadGameModules() internal void TryLoadGameModules()
{ {
if (Directory.Exists(UnhollowedFolderPath)) var dir = ExplorerCore.Loader.UnhollowedModulesFolder;
if (Directory.Exists(dir))
{ {
var files = Directory.GetFiles(UnhollowedFolderPath); foreach (var filePath in Directory.GetFiles(dir, "*.dll"))
foreach (var filePath in files) DoLoadModule(filePath);
{
try
{
DoLoadModule(filePath, true);
}
catch //(Exception ex)
{
//ExplorerCore.LogWarning($"Failed to force-load module '{name}': {ex.ReflectionExToString()}");
}
}
} }
else else
ExplorerCore.LogWarning($"Expected Unhollowed folder path does not exist: '{UnhollowedFolderPath}'"); ExplorerCore.LogWarning($"Expected Unhollowed folder path does not exist: '{dir}'. " +
$"If you are using the standalone release, you can specify the Unhollowed modules path when you call CreateInstance().");
} }
internal bool DoLoadModule(string fullPath, bool suppressWarning = false) internal bool DoLoadModule(string fullPath)
{ {
if (!File.Exists(fullPath)) if (string.IsNullOrEmpty(fullPath) || !File.Exists(fullPath))
return false; return false;
try try
{ {
Assembly.LoadFile(fullPath); Assembly.LoadFile(fullPath);
//Assembly.Load(File.ReadAllBytes(fullPath));
return true; return true;
} }
catch (Exception e) catch //(Exception e)
{ {
if (!suppressWarning) //ExplorerCore.LogWarning($"Failed loading module '{Path.GetFileName(fullPath)}'! {e.ReflectionExToString()}");
Console.WriteLine($"Failed loading module '{Path.GetFileName(fullPath)}'! {e.ReflectionExToString()}"); return false;
} }
return false;
} }
#endregion #endregion
#region Il2cpp reflection blacklist #region Il2cpp reflection blacklist
public override string[] DefaultReflectionBlacklist => defaultIl2CppBlacklist.ToArray(); public override string[] DefaultReflectionBlacklist => defaultIl2CppBlacklist.ToArray();
@ -560,6 +561,7 @@ namespace UnityExplorer
"UnityEngine.Audio.AudioMixerPlayable.Create", "UnityEngine.Audio.AudioMixerPlayable.Create",
"UnityEngine.BoxcastCommand.ScheduleBatch", "UnityEngine.BoxcastCommand.ScheduleBatch",
"UnityEngine.Camera.CalculateProjectionMatrixFromPhysicalProperties", "UnityEngine.Camera.CalculateProjectionMatrixFromPhysicalProperties",
"UnityEngine.Canvas.renderingDisplaySize",
"UnityEngine.CapsulecastCommand.ScheduleBatch", "UnityEngine.CapsulecastCommand.ScheduleBatch",
"UnityEngine.Collider2D.Cast", "UnityEngine.Collider2D.Cast",
"UnityEngine.Collider2D.Raycast", "UnityEngine.Collider2D.Raycast",
@ -665,10 +667,10 @@ namespace UnityExplorer
"UnityEngine.XR.InputDevice.SendHapticImpulse", "UnityEngine.XR.InputDevice.SendHapticImpulse",
}; };
#endregion #endregion
#region IL2CPP IEnumerable and IDictionary #region IL2CPP IEnumerable and IDictionary
protected override bool Internal_TryGetEntryType(Type enumerableType, out Type type) protected override bool Internal_TryGetEntryType(Type enumerableType, out Type type)
{ {
@ -780,21 +782,24 @@ namespace UnityExplorer
// Some ugly reflection to use the il2cpp interface for the instance type // Some ugly reflection to use the il2cpp interface for the instance type
var type = list.GetType(); var type = list.GetActualType();
var key = type.AssemblyQualifiedName; var key = type.AssemblyQualifiedName;
if (!getEnumeratorMethods.ContainsKey(key)) if (!getEnumeratorMethods.ContainsKey(key))
{ {
getEnumeratorMethods.Add(key, type.GetMethod("GetEnumerator")); var method = type.GetMethod("GetEnumerator")
?? type.GetMethod("System_Collections_IEnumerable_GetEnumerator", FLAGS);
getEnumeratorMethods.Add(key, method);
// ensure the enumerator type is supported // ensure the enumerator type is supported
try try
{ {
var test = getEnumeratorMethods[key].Invoke(list, null); var test = getEnumeratorMethods[key].Invoke(list, null);
test.GetType().GetMethod("MoveNext").Invoke(test, null); test.GetActualType().GetMethod("MoveNext").Invoke(test, null);
} }
catch catch (Exception ex)
{ {
ExplorerCore.Log($"IEnumerable failed to enumerate: {ex}");
notSupportedTypes.Add(key); notSupportedTypes.Add(key);
} }
} }
@ -803,7 +808,7 @@ namespace UnityExplorer
throw new NotSupportedException($"The IEnumerable type '{type.FullName}' does not support MoveNext."); throw new NotSupportedException($"The IEnumerable type '{type.FullName}' does not support MoveNext.");
cppEnumerator = getEnumeratorMethods[key].Invoke(list, null); cppEnumerator = getEnumeratorMethods[key].Invoke(list, null);
var enumeratorType = cppEnumerator.GetType(); var enumeratorType = cppEnumerator.GetActualType();
var enumInfoKey = enumeratorType.AssemblyQualifiedName; var enumInfoKey = enumeratorType.AssemblyQualifiedName;
@ -857,7 +862,7 @@ namespace UnityExplorer
try try
{ {
var type = dictionary.GetType(); var type = dictionary.GetActualType();
if (typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(type)) if (typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(type))
{ {
@ -867,20 +872,23 @@ namespace UnityExplorer
var keys = type.GetProperty("Keys").GetValue(dictionary, null); var keys = type.GetProperty("Keys").GetValue(dictionary, null);
var keyCollType = keys.GetType(); var keyCollType = keys.GetActualType();
var cacheKey = keys.GetType().AssemblyQualifiedName; var cacheKey = keyCollType.AssemblyQualifiedName;
if (!getEnumeratorMethods.ContainsKey(cacheKey)) if (!getEnumeratorMethods.ContainsKey(cacheKey))
{ {
getEnumeratorMethods.Add(cacheKey, keyCollType.GetMethod("GetEnumerator")); var method = keyCollType.GetMethod("GetEnumerator")
?? keyCollType.GetMethod("System_Collections_IDictionary_GetEnumerator", FLAGS);
getEnumeratorMethods.Add(cacheKey, method);
// test support // test support
try try
{ {
var test = getEnumeratorMethods[cacheKey].Invoke(keys, null); var test = getEnumeratorMethods[cacheKey].Invoke(keys, null);
test.GetType().GetMethod("MoveNext").Invoke(test, null); test.GetActualType().GetMethod("MoveNext").Invoke(test, null);
} }
catch catch (Exception ex)
{ {
ExplorerCore.Log($"IDictionary failed to enumerate: {ex}");
notSupportedTypes.Add(cacheKey); notSupportedTypes.Add(cacheKey);
} }
} }
@ -891,16 +899,16 @@ namespace UnityExplorer
var keyEnumerator = getEnumeratorMethods[cacheKey].Invoke(keys, null); var keyEnumerator = getEnumeratorMethods[cacheKey].Invoke(keys, null);
var keyInfo = new EnumeratorInfo var keyInfo = new EnumeratorInfo
{ {
current = keyEnumerator.GetType().GetProperty("Current"), current = keyEnumerator.GetActualType().GetProperty("Current"),
moveNext = keyEnumerator.GetType().GetMethod("MoveNext"), moveNext = keyEnumerator.GetActualType().GetMethod("MoveNext"),
}; };
var values = type.GetProperty("Values").GetValue(dictionary, null); var values = type.GetProperty("Values").GetValue(dictionary, null);
var valueEnumerator = values.GetType().GetMethod("GetEnumerator").Invoke(values, null); var valueEnumerator = values.GetActualType().GetMethod("GetEnumerator").Invoke(values, null);
var valueInfo = new EnumeratorInfo var valueInfo = new EnumeratorInfo
{ {
current = valueEnumerator.GetType().GetProperty("Current"), current = valueEnumerator.GetActualType().GetProperty("Current"),
moveNext = valueEnumerator.GetType().GetMethod("MoveNext"), moveNext = valueEnumerator.GetActualType().GetMethod("MoveNext"),
}; };
dictEnumerator = EnumerateCppDict(keyInfo, keyEnumerator, valueInfo, valueEnumerator); dictEnumerator = EnumerateCppDict(keyInfo, keyEnumerator, valueInfo, valueEnumerator);

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using HarmonyLib;
namespace UnityExplorer
{
public static class ReflectionPatches
{
public static void Init()
{
try
{
var method = typeof(Assembly).GetMethod(nameof(Assembly.GetTypes), new Type[0]);
var processor = ExplorerCore.Harmony.CreateProcessor(method);
processor.AddFinalizer(typeof(ReflectionPatches).GetMethod(nameof(Assembly_GetTypes)));
processor.Patch();
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception setting up Reflection patch: {ex}");
}
}
private static readonly Type[] emptyTypes = new Type[0];
public static Exception Assembly_GetTypes(Assembly __instance, Exception __exception, ref Type[] __result)
{
if (__exception != null)
{
try
{
__result = __instance.GetExportedTypes();
}
catch (ReflectionTypeLoadException e)
{
try
{
__result = e.Types.Where(it => it != null).ToArray();
}
catch
{
__result = emptyTypes;
}
}
catch
{
__result = emptyTypes;
}
}
return null;
}
}
}

View File

@ -4,11 +4,11 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using BF = System.Reflection.BindingFlags;
using UnityExplorer.Core.Runtime;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityExplorer.Core.Config; using UnityExplorer.Core.Config;
using UnityExplorer.Core.Runtime;
using BF = System.Reflection.BindingFlags;
namespace UnityExplorer namespace UnityExplorer
{ {
@ -27,6 +27,8 @@ namespace UnityExplorer
new ReflectionUtility(); new ReflectionUtility();
#endif #endif
Instance.Initialize(); Instance.Initialize();
ReflectionPatches.Init();
} }
protected virtual void Initialize() protected virtual void Initialize()
@ -42,7 +44,7 @@ namespace UnityExplorer
public static Action<Type> OnTypeLoaded; public static Action<Type> OnTypeLoaded;
/// <summary>Key: Type.FullName</summary> /// <summary>Key: Type.FullName</summary>
public static readonly SortedDictionary<string, Type> AllTypes = new SortedDictionary<string, Type>(StringComparer.OrdinalIgnoreCase); protected static readonly SortedDictionary<string, Type> AllTypes = new SortedDictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
public static readonly List<string> AllNamespaces = new List<string>(); public static readonly List<string> AllNamespaces = new List<string>();
private static readonly HashSet<string> uniqueNamespaces = new HashSet<string>(); private static readonly HashSet<string> uniqueNamespaces = new HashSet<string>();
@ -87,6 +89,7 @@ namespace UnityExplorer
{ {
foreach (var type in asm.TryGetTypes()) foreach (var type in asm.TryGetTypes())
{ {
// Cache namespace if there is one
if (!string.IsNullOrEmpty(type.Namespace) && !uniqueNamespaces.Contains(type.Namespace)) if (!string.IsNullOrEmpty(type.Namespace) && !uniqueNamespaces.Contains(type.Namespace))
{ {
uniqueNamespaces.Add(type.Namespace); uniqueNamespaces.Add(type.Namespace);
@ -100,16 +103,16 @@ namespace UnityExplorer
AllNamespaces.Insert(i, type.Namespace); AllNamespaces.Insert(i, type.Namespace);
} }
// Cache the type. Overwrite type if one exists with the full name
if (AllTypes.ContainsKey(type.FullName)) if (AllTypes.ContainsKey(type.FullName))
AllTypes[type.FullName] = type; AllTypes[type.FullName] = type;
else else
{
AllTypes.Add(type.FullName, type); AllTypes.Add(type.FullName, type);
//allTypeNames.Add(type.FullName);
}
// Invoke listener
OnTypeLoaded?.Invoke(type); OnTypeLoaded?.Invoke(type);
// Check type inheritance cache, add this to any lists it should be in
foreach (var key in typeInheritance.Keys) foreach (var key in typeInheritance.Keys)
{ {
try try
@ -154,13 +157,6 @@ namespace UnityExplorer
internal virtual string Internal_ProcessTypeInString(string theString, Type type) internal virtual string Internal_ProcessTypeInString(string theString, Type type)
=> theString; => theString;
//// Force loading modules
//public static bool LoadModule(string moduleName)
// => Instance.Internal_LoadModule(moduleName);
//
//internal virtual bool Internal_LoadModule(string moduleName)
// => false;
// Singleton finder // Singleton finder
public static void FindSingleton(string[] possibleNames, Type type, BindingFlags flags, List<object> instances) public static void FindSingleton(string[] possibleNames, Type type, BindingFlags flags, List<object> instances)
@ -225,7 +221,7 @@ namespace UnityExplorer
return ret; return ret;
} }
#endregion #endregion
#region Type and Generic Parameter implementation cache #region Type and Generic Parameter implementation cache
@ -255,27 +251,25 @@ namespace UnityExplorer
/// </summary> /// </summary>
/// <param name="baseType">The base type, which can optionally be abstract / interface.</param> /// <param name="baseType">The base type, which can optionally be abstract / interface.</param>
/// <returns>All implementations of the type in the current AppDomain.</returns> /// <returns>All implementations of the type in the current AppDomain.</returns>
public static HashSet<Type> GetImplementationsOf(Type baseType, bool allowAbstract, bool allowGeneric, bool allowRecursive = true) public static HashSet<Type> GetImplementationsOf(Type baseType, bool allowAbstract, bool allowGeneric, bool allowEnum, bool allowRecursive = true)
{ {
var key = GetImplementationKey(baseType); var key = GetImplementationKey(baseType);
int count = AllTypes.Count; int count = AllTypes.Count;
HashSet<Type> ret; HashSet<Type> ret;
if (!baseType.IsGenericParameter) if (!baseType.IsGenericParameter)
ret = GetImplementations(key, baseType, allowAbstract, allowGeneric); ret = GetImplementations(key, baseType, allowAbstract, allowGeneric, allowEnum);
else else
ret = GetGenericParameterImplementations(key, baseType, allowAbstract, allowGeneric); ret = GetGenericParameterImplementations(key, baseType, allowAbstract, allowGeneric);
// types were resolved during the parse, do it again if we're not already rebuilding. // types were resolved during the parse, do it again if we're not already rebuilding.
if (allowRecursive && AllTypes.Count != count) if (allowRecursive && AllTypes.Count != count)
{
ret = GetImplementationsOf(baseType, allowAbstract, allowGeneric, false); ret = GetImplementationsOf(baseType, allowAbstract, allowGeneric, false);
}
return ret; return ret;
} }
private static HashSet<Type> GetImplementations(string key, Type baseType, bool allowAbstract, bool allowGeneric) private static HashSet<Type> GetImplementations(string key, Type baseType, bool allowAbstract, bool allowGeneric, bool allowEnum)
{ {
if (!typeInheritance.ContainsKey(key)) if (!typeInheritance.ContainsKey(key))
{ {
@ -291,7 +285,8 @@ namespace UnityExplorer
if (set.Contains(type) if (set.Contains(type)
|| (type.IsAbstract && type.IsSealed) // ignore static classes || (type.IsAbstract && type.IsSealed) // ignore static classes
|| (!allowAbstract && type.IsAbstract) || (!allowAbstract && type.IsAbstract)
|| (!allowGeneric && (type.IsGenericType || type.IsGenericTypeDefinition))) || (!allowGeneric && (type.IsGenericType || type.IsGenericTypeDefinition))
|| (!allowEnum && type.IsEnum))
continue; continue;
if (type.FullName.Contains("PrivateImplementationDetails") if (type.FullName.Contains("PrivateImplementationDetails")
@ -360,7 +355,7 @@ namespace UnityExplorer
return genericParameterInheritance[key]; return genericParameterInheritance[key];
} }
#endregion #endregion
#region Internal MemberInfo Cache #region Internal MemberInfo Cache
@ -493,7 +488,7 @@ namespace UnityExplorer
// Temp fix for IL2CPP until interface support improves // Temp fix for IL2CPP until interface support improves
// IsEnumerable // IsEnumerable
public static bool IsEnumerable(Type type) => Instance.Internal_IsEnumerable(type); public static bool IsEnumerable(Type type) => Instance.Internal_IsEnumerable(type);
@ -505,7 +500,7 @@ namespace UnityExplorer
// TryGetEnumerator (list) // TryGetEnumerator (list)
public static bool TryGetEnumerator(object list, out IEnumerator enumerator) public static bool TryGetEnumerator(object list, out IEnumerator enumerator)
=> Instance.Internal_TryGetEnumerator(list, out enumerator); => Instance.Internal_TryGetEnumerator(list, out enumerator);
protected virtual bool Internal_TryGetEnumerator(object list, out IEnumerator enumerator) protected virtual bool Internal_TryGetEnumerator(object list, out IEnumerator enumerator)
@ -546,7 +541,7 @@ namespace UnityExplorer
type = typeof(object); type = typeof(object);
return false; return false;
} }
// IsDictionary // IsDictionary
public static bool IsDictionary(Type type) => Instance.Internal_IsDictionary(type); public static bool IsDictionary(Type type) => Instance.Internal_IsDictionary(type);
@ -558,7 +553,7 @@ namespace UnityExplorer
// TryGetEnumerator (dictionary) // TryGetEnumerator (dictionary)
public static bool TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator) public static bool TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)
=> Instance.Internal_TryGetDictEnumerator(dictionary, out dictEnumerator); => Instance.Internal_TryGetDictEnumerator(dictionary, out dictEnumerator);
protected virtual bool Internal_TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator) protected virtual bool Internal_TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)

View File

@ -4,58 +4,69 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using UnhollowerBaseLib.Attributes;
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using UnityExplorer.Core.Runtime.Il2Cpp; using UnityExplorer.Core.Runtime.Il2Cpp;
namespace UnityExplorer namespace UnityExplorer
{ {
public class AssetBundle public class AssetBundle : UnityEngine.Object
{ {
static AssetBundle()
{
ClassInjector.RegisterTypeInIl2Cpp<AssetBundle>();
}
// ~~~~~~~~~~~~ Static ~~~~~~~~~~~~ // ~~~~~~~~~~~~ Static ~~~~~~~~~~~~
internal delegate IntPtr d_LoadFromFile(IntPtr path, uint crc, ulong offset); internal delegate IntPtr d_LoadFromFile(IntPtr path, uint crc, ulong offset);
[HideFromIl2Cpp]
public static AssetBundle LoadFromFile(string path) public static AssetBundle LoadFromFile(string path)
{ {
var iCall = ICallManager.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); var ptr = iCall(IL2CPP.ManagedStringToIl2Cpp(path), 0u, 0UL);
return new AssetBundle(ptr); return new AssetBundle(ptr);
} }
private delegate IntPtr d_LoadFromMemory(IntPtr binary, uint crc); private delegate IntPtr d_LoadFromMemory(IntPtr binary, uint crc);
[HideFromIl2Cpp]
public static AssetBundle LoadFromMemory(byte[] binary, uint crc = 0) public static AssetBundle LoadFromMemory(byte[] binary, uint crc = 0)
{ {
var iCall = ICallManager.GetICall<d_LoadFromMemory>("UnityEngine.AssetBundle::LoadFromMemory_Internal"); var iCall = ICallManager.GetICall<d_LoadFromMemory>("UnityEngine.AssetBundle::LoadFromMemory_Internal");
var ptr = iCall(((Il2CppStructArray<byte>) binary).Pointer, crc); var ptr = iCall(((Il2CppStructArray<byte>)binary).Pointer, crc);
return new AssetBundle(ptr); return new AssetBundle(ptr);
} }
// static void UnloadAllAssetBundles(bool unloadAllObjects); public delegate IntPtr d_GetAllLoadedAssetBundles_Native();
internal delegate void d_UnloadAllAssetBundles(bool unloadAllObjects); [HideFromIl2Cpp]
public static AssetBundle[] GetAllLoadedAssetBundles()
public static void UnloadAllAssetBundles(bool unloadAllObjects)
{ {
var iCall = ICallManager.GetICall<d_UnloadAllAssetBundles>("UnityEngine.AssetBundle::UnloadAllAssetBundles"); var iCall = ICallManager.GetICall<d_GetAllLoadedAssetBundles_Native>("UnityEngine.AssetBundle::GetAllLoadedAssetBundles_Native");
iCall.Invoke(unloadAllObjects); var ptr = iCall();
if (ptr == IntPtr.Zero)
return null;
return (AssetBundle[])new Il2CppReferenceArray<AssetBundle>(ptr);
} }
// ~~~~~~~~~~~~ Instance ~~~~~~~~~~~~ // ~~~~~~~~~~~~ Instance ~~~~~~~~~~~~
private readonly IntPtr m_bundlePtr = IntPtr.Zero; public readonly IntPtr m_bundlePtr = IntPtr.Zero;
public AssetBundle(IntPtr ptr) { m_bundlePtr = ptr; } public AssetBundle(IntPtr ptr) : base(ptr) { m_bundlePtr = ptr; }
// LoadAllAssets() // LoadAllAssets()
internal delegate IntPtr d_LoadAssetWithSubAssets_Internal(IntPtr _this, IntPtr name, IntPtr type); internal delegate IntPtr d_LoadAssetWithSubAssets_Internal(IntPtr _this, IntPtr name, IntPtr type);
[HideFromIl2Cpp]
public UnityEngine.Object[] LoadAllAssets() public UnityEngine.Object[] LoadAllAssets()
{ {
var iCall = ICallManager.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); var ptr = iCall.Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(""), UnhollowerRuntimeLib.Il2CppType.Of<UnityEngine.Object>().Pointer);
if (ptr == IntPtr.Zero) if (ptr == IntPtr.Zero)
return new UnityEngine.Object[0]; return new UnityEngine.Object[0];
@ -67,10 +78,11 @@ namespace UnityExplorer
internal delegate IntPtr d_LoadAsset_Internal(IntPtr _this, IntPtr name, IntPtr type); internal delegate IntPtr d_LoadAsset_Internal(IntPtr _this, IntPtr name, IntPtr type);
[HideFromIl2Cpp]
public T LoadAsset<T>(string name) where T : UnityEngine.Object public T LoadAsset<T>(string name) where T : UnityEngine.Object
{ {
var iCall = ICallManager.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); var ptr = iCall.Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(name), UnhollowerRuntimeLib.Il2CppType.Of<T>().Pointer);
if (ptr == IntPtr.Zero) if (ptr == IntPtr.Zero)
return null; return null;
@ -78,13 +90,15 @@ namespace UnityExplorer
return new UnityEngine.Object(ptr).TryCast<T>(); return new UnityEngine.Object(ptr).TryCast<T>();
} }
// public extern void Unload(bool unloadAllLoadedObjects); // Unload(bool unloadAllLoadedObjects);
internal delegate void d_Unload(IntPtr _this, bool unloadAllLoadedObjects); internal delegate void d_Unload(IntPtr _this, bool unloadAllLoadedObjects);
public void Unload(bool unloadAssets = true) [HideFromIl2Cpp]
public void Unload(bool unloadAllLoadedObjects)
{ {
var iCall = ICallManager.GetICall<d_Unload>("UnityEngine.AssetBundle::Unload"); var iCall = ICallManager.GetICall<d_Unload>("UnityEngine.AssetBundle::Unload");
iCall.Invoke(this.m_bundlePtr, unloadAssets); iCall.Invoke(this.m_bundlePtr, unloadAllLoadedObjects);
} }
} }
} }

View File

@ -7,7 +7,6 @@ using System.Runtime.InteropServices;
namespace UnityExplorer.Core.Runtime.Il2Cpp namespace UnityExplorer.Core.Runtime.Il2Cpp
{ {
[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "External methods")]
public static class ICallManager public static class ICallManager
{ {
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]

View File

@ -6,8 +6,8 @@ using System.Linq;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using UnityEngine; using UnityEngine;
// CREDIT HerpDerpenstine // Credit to HerpDerpenstine and knah
// https://github.com/LavaGang/MelonLoader/blob/master/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs // https://github.com/LavaGang/MelonLoader/blob/master/SM_Il2Cpp/Coroutines.cs
namespace UnityExplorer.Core.Runtime.Il2Cpp namespace UnityExplorer.Core.Runtime.Il2Cpp
{ {

View File

@ -73,6 +73,7 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
return ScriptableObject.CreateInstance(Il2CppType.From(type)); return ScriptableObject.CreateInstance(Il2CppType.From(type));
} }
// Pretty disgusting but couldn't figure out a cleaner way yet unfortunately
public override void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List<RaycastResult> list) public override void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List<RaycastResult> list)
{ {
var il2cppList = new Il2CppSystem.Collections.Generic.List<RaycastResult>(); var il2cppList = new Il2CppSystem.Collections.Generic.List<RaycastResult>();
@ -97,15 +98,17 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
internal delegate IntPtr d_FindObjectsOfTypeAll(IntPtr type); internal delegate IntPtr d_FindObjectsOfTypeAll(IntPtr type);
internal static readonly string[] findObjectsOfTypeAllSignatures = new[]
{
"UnityEngine.Resources::FindObjectsOfTypeAll",
"UnityEngine.ResourcesAPIInternal::FindObjectsOfTypeAll" // Unity 2020+ updated to this
};
public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type) public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type)
{ {
var iCall = ICallManager.GetICallUnreliable<d_FindObjectsOfTypeAll>(new[] return new Il2CppReferenceArray<UnityEngine.Object>(
{ ICallManager.GetICallUnreliable<d_FindObjectsOfTypeAll>(findObjectsOfTypeAllSignatures)
"UnityEngine.Resources::FindObjectsOfTypeAll", .Invoke(Il2CppType.From(type).Pointer));
"UnityEngine.ResourcesAPIInternal::FindObjectsOfTypeAll" // Unity 2020+ updated to this
});
return new Il2CppReferenceArray<UnityEngine.Object>(iCall.Invoke(Il2CppType.From(type).Pointer));
} }
// Scene.GetRootGameObjects(); // Scene.GetRootGameObjects();
@ -117,22 +120,17 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
if (!scene.isLoaded) if (!scene.isLoaded)
return new GameObject[0]; return new GameObject[0];
int handle = scene.handle; if (scene.handle == -1)
if (handle == -1)
return new GameObject[0]; return new GameObject[0];
int count = GetRootCount(handle); int count = GetRootCount(scene.handle);
if (count < 1) if (count < 1)
return new GameObject[0]; return new GameObject[0];
var list = new Il2CppSystem.Collections.Generic.List<GameObject>(count); var list = new Il2CppSystem.Collections.Generic.List<GameObject>(count);
var iCall = ICallManager.GetICall<d_GetRootGameObjects>("UnityEngine.SceneManagement.Scene::GetRootGameObjectsInternal"); var iCall = ICallManager.GetICall<d_GetRootGameObjects>("UnityEngine.SceneManagement.Scene::GetRootGameObjectsInternal");
iCall.Invoke(scene.handle, list.Pointer);
iCall.Invoke(handle, list.Pointer);
return list.ToArray(); return list.ToArray();
} }

View File

@ -38,19 +38,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
return new Il2CppStructArray<byte>(ptr); 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 // Sprite Sprite.Create
public override Sprite CreateSprite(Texture2D texture) public override Sprite CreateSprite(Texture2D texture)

View File

@ -10,6 +10,7 @@ using UnityEngine.Events;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer;
namespace UnityExplorer.Core.Runtime.Mono namespace UnityExplorer.Core.Runtime.Mono
{ {
@ -18,7 +19,6 @@ namespace UnityExplorer.Core.Runtime.Mono
public override void Initialize() public override void Initialize()
{ {
ExplorerCore.Context = RuntimeContext.Mono; ExplorerCore.Context = RuntimeContext.Mono;
//Reflection = new MonoReflection();
TextureUtil = new MonoTextureUtil(); TextureUtil = new MonoTextureUtil();
} }
@ -28,60 +28,35 @@ namespace UnityExplorer.Core.Runtime.Mono
} }
private void Application_logMessageReceived(string condition, string stackTrace, LogType type) private void Application_logMessageReceived(string condition, string stackTrace, LogType type)
{ => ExplorerCore.LogUnity(condition, type);
ExplorerCore.LogUnity(condition, type);
}
public override void StartCoroutine(IEnumerator routine) public override void StartCoroutine(IEnumerator routine)
{ => ExplorerBehaviour.Instance.StartCoroutine(routine);
ExplorerBehaviour.Instance.StartCoroutine(routine);
}
public override void Update() public override void Update()
{ {
} }
public override T AddComponent<T>(GameObject obj, Type type) public override T AddComponent<T>(GameObject obj, Type type)
{ => (T)obj.AddComponent(type);
return (T)obj.AddComponent(type);
}
public override ScriptableObject CreateScriptable(Type type) public override ScriptableObject CreateScriptable(Type type)
{ => ScriptableObject.CreateInstance(type);
return ScriptableObject.CreateInstance(type);
}
public override void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List<RaycastResult> list) public override void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List<RaycastResult> list)
{ => raycaster.Raycast(data, list);
raycaster.Raycast(data, list);
}
public override string LayerToName(int layer) public override string LayerToName(int layer)
=> LayerMask.LayerToName(layer); => LayerMask.LayerToName(layer);
public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type) public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type)
=> Resources.FindObjectsOfTypeAll(type); => Resources.FindObjectsOfTypeAll(type);
//private static readonly FieldInfo fi_Scene_handle = typeof(Scene).GetField("m_Handle", ReflectionUtility.AllFlags); public override GameObject[] GetRootGameObjects(Scene scene)
=> scene.isLoaded ? scene.GetRootGameObjects() : new GameObject[0];
//public override int GetSceneHandle(Scene scene) public override int GetRootCount(Scene scene)
//{ => scene.rootCount;
// 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 void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null, public override void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null,
Color? disabled = null) Color? disabled = null)
@ -103,59 +78,42 @@ namespace UnityExplorer.Core.Runtime.Mono
SetColorBlock(selectable, colors); SetColorBlock(selectable, colors);
} }
public override void SetColorBlock(Selectable selectable, ColorBlock colors) public override void SetColorBlock(Selectable selectable, ColorBlock colors)
{ => selectable.colors = colors;
selectable.colors = colors;
}
} }
} }
public static class MonoExtensions public static class MonoExtensions
{ {
// Helpers to use the same style of AddListener that IL2CPP uses.
public static void AddListener(this UnityEvent _event, Action listener) public static void AddListener(this UnityEvent _event, Action listener)
{ => _event.AddListener(new UnityAction(listener));
_event.AddListener(new UnityAction(listener));
}
public static void AddListener<T>(this UnityEvent<T> _event, Action<T> listener) public static void AddListener<T>(this UnityEvent<T> _event, Action<T> listener)
{ => _event.AddListener(new UnityAction<T>(listener));
_event.AddListener(new UnityAction<T>(listener));
}
public static void RemoveListener(this UnityEvent _event, Action listener) public static void RemoveListener(this UnityEvent _event, Action listener)
{ => _event.RemoveListener(new UnityAction(listener));
_event.RemoveListener(new UnityAction(listener));
}
public static void RemoveListener<T>(this UnityEvent<T> _event, Action<T> listener) public static void RemoveListener<T>(this UnityEvent<T> _event, Action<T> listener)
{ => _event.RemoveListener(new UnityAction<T>(listener));
_event.RemoveListener(new UnityAction<T>(listener));
}
public static void Clear(this StringBuilder sb) // Doesn't exist in NET 3.5
{
sb.Remove(0, sb.Length);
}
private static PropertyInfo pi_childControlHeight; public static void Clear(this StringBuilder sb)
=> sb.Remove(0, sb.Length);
// These properties don't exist in some earlier games, so null check before trying to set them.
public static void SetChildControlHeight(this HorizontalOrVerticalLayoutGroup group, bool value) public static void SetChildControlHeight(this HorizontalOrVerticalLayoutGroup group, bool value)
{ => ReflectionUtility.GetPropertyInfo(typeof(HorizontalOrVerticalLayoutGroup), "childControlHeight")
if (pi_childControlHeight == null) ?.SetValue(group, value, 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) public static void SetChildControlWidth(this HorizontalOrVerticalLayoutGroup group, bool value)
{ => ReflectionUtility.GetPropertyInfo(typeof(HorizontalOrVerticalLayoutGroup), "childControlWidth")
if (pi_childControlWidth == null) ?.SetValue(group, value, null);
pi_childControlWidth = group.GetType().GetProperty("childControlWidth");
pi_childControlWidth?.SetValue(group, value, null);
}
} }
#endif #endif

View File

@ -12,41 +12,28 @@ namespace UnityExplorer.Core.Runtime.Mono
public class MonoTextureUtil : TextureUtilProvider public class MonoTextureUtil : TextureUtilProvider
{ {
public override void Blit(Texture2D tex, RenderTexture rt) public override void Blit(Texture2D tex, RenderTexture rt)
{ => Graphics.Blit(tex, rt);
Graphics.Blit(tex, rt);
}
public override Sprite CreateSprite(Texture2D texture) public override Sprite CreateSprite(Texture2D texture)
{ => Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);
return Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);
}
public override bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable) //public override bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable)
{ // => tex.LoadImage(data, markNonReadable);
return tex.LoadImage(data, markNonReadable);
}
public override Texture2D NewTexture2D(int width, int height) public override Texture2D NewTexture2D(int width, int height)
{ => new Texture2D(width, height);
return new Texture2D(width, height);
}
public override byte[] EncodeToPNG(Texture2D tex) public override byte[] EncodeToPNG(Texture2D tex)
{ => EncodeToPNGSafe(tex);
return EncodeToPNGSafe(tex);
}
private static MethodInfo EncodeToPNGMethod => m_encodeToPNGMethod ?? GetEncodeToPNGMethod(); private static MethodInfo EncodeToPNGMethod => m_encodeToPNGMethod ?? GetEncodeToPNGMethod();
private static MethodInfo m_encodeToPNGMethod; private static MethodInfo m_encodeToPNGMethod;
public static byte[] EncodeToPNGSafe(Texture2D tex) public static byte[] EncodeToPNGSafe(Texture2D tex)
{ {
var method = EncodeToPNGMethod; return EncodeToPNGMethod.IsStatic
? (byte[])EncodeToPNGMethod.Invoke(null, new object[] { tex })
if (method.IsStatic) : (byte[])EncodeToPNGMethod.Invoke(tex, ArgumentUtility.EmptyArgs);
return (byte[])method.Invoke(null, new object[] { tex });
else
return (byte[])method.Invoke(tex, ArgumentUtility.EmptyArgs);
} }
private static MethodInfo GetEncodeToPNGMethod() private static MethodInfo GetEncodeToPNGMethod()

View File

@ -40,8 +40,6 @@ namespace UnityExplorer
public abstract void Update(); public abstract void Update();
//public virtual bool IsReferenceEqual(object a, object b) => ReferenceEquals(a, b);
// Unity API handlers // Unity API handlers
public abstract T AddComponent<T>(GameObject obj, Type type) where T : Component; public abstract T AddComponent<T>(GameObject obj, Type type) where T : Component;
@ -54,12 +52,13 @@ namespace UnityExplorer
public abstract void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List<RaycastResult> list); public abstract void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List<RaycastResult> list);
//public abstract int GetSceneHandle(Scene scene);
public abstract GameObject[] GetRootGameObjects(Scene scene); public abstract GameObject[] GetRootGameObjects(Scene scene);
public abstract int GetRootCount(Scene scene); public abstract int GetRootCount(Scene scene);
public void SetColorBlockAuto(Selectable selectable, Color baseColor)
=> SetColorBlock(selectable, baseColor, baseColor * 1.2f, baseColor * 0.8f);
public abstract void SetColorBlock(Selectable selectable, ColorBlock colors); public abstract void SetColorBlock(Selectable selectable, ColorBlock colors);
public abstract void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null, public abstract void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null,

View File

@ -22,7 +22,7 @@ namespace UnityExplorer.Core.Runtime
public abstract void Blit(Texture2D tex, RenderTexture rt); public abstract void Blit(Texture2D tex, RenderTexture rt);
public abstract bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable); //public abstract bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable);
public abstract Sprite CreateSprite(Texture2D texture); public abstract Sprite CreateSprite(Texture2D texture);
@ -43,27 +43,22 @@ namespace UnityExplorer.Core.Runtime
} }
} }
public static bool LoadImage(Texture2D tex, string filePath, bool markNonReadable) //public static bool LoadImage(Texture2D tex, string filePath, bool markNonReadable)
{ //{
if (!File.Exists(filePath)) // if (!File.Exists(filePath))
return false; // return false;
//
return Instance.LoadImage(tex, File.ReadAllBytes(filePath), markNonReadable); // return Instance.LoadImage(tex, File.ReadAllBytes(filePath), markNonReadable);
} //}
public static Texture2D Copy(Texture2D orig, Rect rect) public static Texture2D Copy(Texture2D orig, Rect rect)
{ {
Color[] pixels;
if (!IsReadable(orig)) if (!IsReadable(orig))
orig = ForceReadTexture(orig); orig = ForceReadTexture(orig);
pixels = orig.GetPixels((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); Color[] pixels = orig.GetPixels((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
Texture2D newTex = Instance.NewTexture2D((int)rect.width, (int)rect.height); Texture2D newTex = Instance.NewTexture2D((int)rect.width, (int)rect.height);
newTex.SetPixels(pixels); newTex.SetPixels(pixels);
return newTex; return newTex;
} }
@ -92,7 +87,7 @@ namespace UnityExplorer.Core.Runtime
} }
catch (Exception e) catch (Exception e)
{ {
ExplorerCore.Log("Exception on ForceReadTexture: " + e.ToString()); ExplorerCore.Log($"Exception on ForceReadTexture: {e.ToString()}");
return default; return default;
} }
} }
@ -103,13 +98,11 @@ namespace UnityExplorer.Core.Runtime
Directory.CreateDirectory(dir); Directory.CreateDirectory(dir);
byte[] data; byte[] data;
string savepath = dir + @"\" + name + ".png"; string savepath = $@"{dir}\{name}.png";
// Make sure we can EncodeToPNG it. // Make sure we can EncodeToPNG it.
if (tex.format != TextureFormat.ARGB32 || !IsReadable(tex)) if (tex.format != TextureFormat.ARGB32 || !IsReadable(tex))
{
tex = ForceReadTexture(tex); tex = ForceReadTexture(tex);
}
if (isDTXnmNormal) if (isDTXnmNormal)
{ {
@ -120,13 +113,9 @@ namespace UnityExplorer.Core.Runtime
data = Instance.EncodeToPNG(tex); data = Instance.EncodeToPNG(tex);
if (data == null || !data.Any()) if (data == null || !data.Any())
{
ExplorerCore.LogWarning("Couldn't get any data for the texture!"); ExplorerCore.LogWarning("Couldn't get any data for the texture!");
}
else else
{
File.WriteAllBytes(savepath, data); File.WriteAllBytes(savepath, data);
}
} }
// Converts DTXnm-format Normal Map to RGBA-format Normal Map. // Converts DTXnm-format Normal Map to RGBA-format Normal Map.

View File

@ -2,11 +2,11 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityExplorer.UI.CacheObject.IValues;
using System.Reflection;
using UnityExplorer.UI; using UnityExplorer.UI;
using UnityExplorer.CacheObject.IValues;
#if CPP #if CPP
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
using UnhollowerBaseLib; using UnhollowerBaseLib;
@ -14,112 +14,31 @@ using UnhollowerBaseLib;
namespace UnityExplorer.Tests namespace UnityExplorer.Tests
{ {
public class TestIndexer : IList<int>
{
private readonly List<int> list = new List<int>() { 1,2,3,4,5 };
public int Count => list.Count;
public bool IsReadOnly => false;
int IList<int>.this[int index]
{
get => list[index];
set => list[index] = value;
}
public int IndexOf(int item) => list.IndexOf(item);
public bool Contains(int item) => list.Contains(item);
public void Add(int item) => list.Add(item);
public void Insert(int index, int item) => list.Insert(index, item);
public bool Remove(int item) => list.Remove(item);
public void RemoveAt(int index) => list.RemoveAt(index);
public void Clear() => list.Clear();
public void CopyTo(int[] array, int arrayIndex) => list.CopyTo(array, arrayIndex);
public IEnumerator<int> GetEnumerator() => list.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator();
}
public static class TestClass public static class TestClass
{ {
public static readonly TestIndexer AAAAATest = new TestIndexer(); static TestClass()
public static void ATestMethod(string s, float f, Vector3 vector, DateTime date, Quaternion quater, bool b, CameraClearFlags enumvalue)
{ {
ExplorerCore.Log($"{s}, {f}, {vector.ToString()}, {date}, {quater.eulerAngles.ToString()}, {b}, {enumvalue}"); Init_Mono();
#if CPP
Init_IL2CPP();
#endif
} }
public static List<int> AWritableList = new List<int> { 1, 2, 3, 4, 5 }; // Test enumerables
public static Dictionary<string, int> AWritableDict = new Dictionary<string, int> { { "one", 1 }, { "two", 2 } }; public static List<object> ListOfInts;
public static List<List<List<string>>> NestedList;
public static IEnumerable ANestedList = new List<List<List<string>>> public static IDictionary MixedDictionary;
{ public static Hashtable Hashtable;
new List<List<string>>
{
new List<string>
{
"one",
"two",
"one",
"two",
"one",
"two",
"one",
"two",
"one",
"two",
"one",
"two",
"one",
"two",
"one",
"two",
},
new List<string>
{
"three",
"four",
}
},
new List<List<string>>
{
new List<string>
{
"five"
}
}
};
public static IDictionary ARandomDictionary = new Dictionary<object, object>
{
{ 1, 2 },
{ "one", "two" },
{ true, false },
{ new Vector3(0,1,2), new Vector3(1,2,3) },
{ CameraClearFlags.Depth, CameraClearFlags.Color },
{ "################################################\r\n##########", null },
{ "subdict", new Dictionary<object,object> { { "key", "value" } } }
};
public static Hashtable TestHashtable = new Hashtable
{
{ "one", "value" },
{ "two", "value" },
{ "three", "value" },
};
public const int ConstantInt = 5;
public static Color AColor = Color.magenta;
public static Color32 AColor32 = Color.red;
public static byte[] ByteArray = new byte[16]; public static byte[] ByteArray = new byte[16];
public static string LongString = new string('#', 10000); public static List<short> ABigList = new List<short>(10000);
public static List<string> BigList = new List<string>(10000);
// Test const behaviour (should be a readonly field)
public const int ConstantInt5 = 5;
// Testing other InteractiveValues
public static Color Color = Color.magenta;
public static Color32 Color32 = Color.red;
public static string ALongString = new string('#', 10000);
public static List<object> RandomList public static List<object> RandomList
{ {
@ -133,25 +52,7 @@ namespace UnityExplorer.Tests
} }
} }
private static void TestGeneric<T>() // Test methods
{
ExplorerCore.Log("Test1 " + typeof(T).FullName);
}
private static void TestGenericClass<T>() where T : class
{
ExplorerCore.Log("Test2 " + typeof(T).FullName);
}
private static void TestComponent<T>() where T : Component
{
ExplorerCore.Log("Test3 " + typeof(T).FullName);
}
private static void TestStruct<T>() where T : struct
{
ExplorerCore.Log("Test3 " + typeof(T).FullName);
}
private static object GetRandomObject() private static object GetRandomObject()
{ {
@ -165,109 +66,133 @@ namespace UnityExplorer.Tests
case 2: return true; case 2: return true;
case 3: return "hello"; case 3: return "hello";
case 4: return 50.5f; case 4: return 50.5f;
case 5: return UnityEngine.CameraClearFlags.Color; case 5: return CameraClearFlags.Color;
case 6: return new List<string> { "sub list", "lol" }; case 6: return new List<string> { "one", "two" };
} }
return ret; return ret;
} }
public static void TestComponent<T>() where T : Component
{
ExplorerCore.Log($"Test3 {typeof(T).FullName}");
}
public static void TestArgumentParse(string s, int i, Color color, CameraClearFlags flags, Vector3 vector, Quaternion quaternion)
{
ExplorerCore.Log($"{s}, {i}, {color.ToString()}, {flags}, {vector.ToString()}, {quaternion.ToString()}");
}
private static void Init_Mono()
{
ExplorerCore.Log($"1: Basic list");
ListOfInts = new List<object> { 1, 2, 3, 4, 5 };
ExplorerCore.Log($"2: Nested list");
NestedList = new List<List<List<string>>>
{
new List<List<string>> {
new List<string> { "1", "2", "3" },
new List<string> { "4", "5", "6" },
},
new List<List<string>>
{
new List<string> { "7", "8", "9" }
}
};
ExplorerCore.Log($"3: Dictionary");
MixedDictionary = new Dictionary<object, object>
{
{ 1, 2 },
{ "one", "two" },
{ true, false },
{ new Vector3(0,1,2), new Vector3(1,2,3) },
{ CameraClearFlags.Depth, CameraClearFlags.Color },
{ "################################################\r\n##########", null },
{ "subdict", new Dictionary<object,object> { { "key", "value" } } }
};
ExplorerCore.Log($"4: Hashtable");
Hashtable = new Hashtable { { "One", 1 }, { "Two", 2 } };
ExplorerCore.Log($"5: Big list");
for (int i = 0; i < ABigList.Capacity; i++)
ABigList.Add((short)UnityEngine.Random.Range(0, short.MaxValue));
ExplorerCore.Log("Finished TestClass Init_Mono");
}
#if CPP #if CPP
public static Il2CppSystem.Collections.IList IL2CPP_IList;
public static Il2CppSystem.Collections.Generic.List<string> IL2CPP_ListString; public static Il2CppSystem.Collections.Generic.List<string> IL2CPP_ListString;
public static Il2CppSystem.Collections.Generic.HashSet<string> IL2CPP_HashSet;
public static Il2CppSystem.Collections.Generic.Dictionary<string, string> IL2CPP_Dict;
public static Il2CppSystem.Collections.Hashtable IL2CPP_HashTable;
public static Il2CppSystem.Collections.IDictionary IL2CPP_IDict;
public static string IL2CPP_systemString = "Test";
public static Il2CppSystem.Object IL2CPP_objectString = "string boxed as cpp object";
public static Il2CppSystem.String IL2CPP_il2cppString = "string boxed as cpp string";
public static string nullString = null;
public static List<Il2CppSystem.Object> IL2CPP_listOfBoxedObjects; public static List<Il2CppSystem.Object> IL2CPP_listOfBoxedObjects;
public static Il2CppStructArray<int> IL2CPP_structArray; public static Il2CppStructArray<int> IL2CPP_structArray;
public static Il2CppStringArray IL2CPP_stringArray;
public static Il2CppReferenceArray<Il2CppSystem.Object> IL2CPP_ReferenceArray; public static Il2CppReferenceArray<Il2CppSystem.Object> IL2CPP_ReferenceArray;
public static Il2CppSystem.Collections.IDictionary IL2CPP_IDict;
public static Il2CppSystem.Collections.IList IL2CPP_IList;
public static Dictionary<Il2CppSystem.String, Il2CppSystem.Object> CppBoxedDict;
public static Il2CppSystem.Collections.Generic.HashSet<string> IL2CPP_HashSet;
public static Il2CppSystem.Collections.Generic.Dictionary<string, string> IL2CPP_Dict;
public static Il2CppSystem.Collections.Hashtable IL2CPP_HashTable;
public static Il2CppSystem.Object cppBoxedInt; public static Il2CppSystem.Object cppBoxedInt;
public static Il2CppSystem.Int32 cppInt; public static Il2CppSystem.Int32 cppInt;
public static Il2CppSystem.Decimal cppDecimal; public static Il2CppSystem.Decimal cppDecimal;
public static Il2CppSystem.Object cppDecimalBoxed; public static Il2CppSystem.Object cppDecimalBoxed;
public static Il2CppSystem.Object cppVector3Boxed; public static Il2CppSystem.Object cppVector3Boxed;
public static string IL2CPP_systemString = "Test";
public static Il2CppSystem.Object IL2CPP_objectString = "string boxed as cpp object";
public static Il2CppSystem.String IL2CPP_il2cppString = "string boxed as cpp string";
public static string nullString = null;
public static Il2CppSystem.Object RandomBoxedColor private static void Init_IL2CPP()
{ {
get ExplorerCore.Log($"IL2CPP 1: Il2Cpp Dictionary<string, string>");
{
int ran = UnityEngine.Random.Range(0, 3);
switch (ran)
{
case 1: return new Color32().BoxIl2CppObject();
case 2: return Color.magenta.BoxIl2CppObject();
default:
return null;
}
}
}
public static Il2CppSystem.Collections.Hashtable cppHashset;
public static Dictionary<Il2CppSystem.String, Il2CppSystem.Object> CppBoxedDict;
#endif
static TestClass()
{
for (int i = 0; i < BigList.Capacity; i++)
BigList.Add(i.ToString());
#if CPP
IL2CPP_Dict = new Il2CppSystem.Collections.Generic.Dictionary<string, string>(); IL2CPP_Dict = new Il2CppSystem.Collections.Generic.Dictionary<string, string>();
IL2CPP_Dict.Add("key1", "value1"); IL2CPP_Dict.Add("key1", "value1");
IL2CPP_Dict.Add("key2", "value2"); IL2CPP_Dict.Add("key2", "value2");
IL2CPP_Dict.Add("key3", "value3"); IL2CPP_Dict.Add("key3", "value3");
ExplorerCore.Log($"IL2CPP 2: Il2Cpp Hashtable");
IL2CPP_HashTable = new Il2CppSystem.Collections.Hashtable(); IL2CPP_HashTable = new Il2CppSystem.Collections.Hashtable();
IL2CPP_HashTable.Add("key1", "value1"); IL2CPP_HashTable.Add("key1", "value1");
IL2CPP_HashTable.Add("key2", "value2"); IL2CPP_HashTable.Add("key2", "value2");
IL2CPP_HashTable.Add("key3", "value3"); IL2CPP_HashTable.Add("key3", "value3");
ExplorerCore.Log($"IL2CPP 3: Il2Cpp IDictionary");
var dict2 = new Il2CppSystem.Collections.Generic.Dictionary<string, string>(); var dict2 = new Il2CppSystem.Collections.Generic.Dictionary<string, string>();
dict2.Add("key1", "value1"); dict2.Add("key1", "value1");
IL2CPP_IDict = dict2.TryCast<Il2CppSystem.Collections.IDictionary>(); IL2CPP_IDict = dict2.TryCast<Il2CppSystem.Collections.IDictionary>();
ExplorerCore.Log($"IL2CPP 4: Il2Cpp List of Il2Cpp Object");
var list = new Il2CppSystem.Collections.Generic.List<Il2CppSystem.Object>(5); var list = new Il2CppSystem.Collections.Generic.List<Il2CppSystem.Object>(5);
list.Add("one"); list.Add("one");
list.Add("two"); list.Add("two");
IL2CPP_IList = list.TryCast<Il2CppSystem.Collections.IList>(); IL2CPP_IList = list.TryCast<Il2CppSystem.Collections.IList>();
ExplorerCore.Log($"IL2CPP 5: Il2Cpp List of strings");
IL2CPP_ListString = new Il2CppSystem.Collections.Generic.List<string>(); IL2CPP_ListString = new Il2CppSystem.Collections.Generic.List<string>();
IL2CPP_ListString.Add("hello,"); IL2CPP_ListString.Add("hello,");
IL2CPP_ListString.Add("world!"); IL2CPP_ListString.Add("world!");
ExplorerCore.Log($"IL2CPP 6: Il2Cpp HashSet of strings");
IL2CPP_HashSet = new Il2CppSystem.Collections.Generic.HashSet<string>(); IL2CPP_HashSet = new Il2CppSystem.Collections.Generic.HashSet<string>();
IL2CPP_HashSet.Add("one"); IL2CPP_HashSet.Add("one");
IL2CPP_HashSet.Add("two"); IL2CPP_HashSet.Add("two");
ExplorerCore.Log($"IL2CPP 7: Dictionary of Il2Cpp String and Il2Cpp Object");
CppBoxedDict = new Dictionary<Il2CppSystem.String, Il2CppSystem.Object>(); CppBoxedDict = new Dictionary<Il2CppSystem.String, Il2CppSystem.Object>();
CppBoxedDict.Add("1", new Il2CppSystem.Int32 { m_value = 1 }.BoxIl2CppObject()); CppBoxedDict.Add("1", new Il2CppSystem.Int32 { m_value = 1 }.BoxIl2CppObject());
CppBoxedDict.Add("2", new Il2CppSystem.Int32 { m_value = 2 }.BoxIl2CppObject()); CppBoxedDict.Add("2", new Il2CppSystem.Int32 { m_value = 2 }.BoxIl2CppObject());
CppBoxedDict.Add("3", new Il2CppSystem.Int32 { m_value = 3 }.BoxIl2CppObject()); CppBoxedDict.Add("3", new Il2CppSystem.Int32 { m_value = 3 }.BoxIl2CppObject());
CppBoxedDict.Add("4", new Il2CppSystem.Int32 { m_value = 4 }.BoxIl2CppObject()); CppBoxedDict.Add("4", new Il2CppSystem.Int32 { m_value = 4 }.BoxIl2CppObject());
cppDecimal = new Il2CppSystem.Decimal(1f); ExplorerCore.Log($"IL2CPP 8: List of boxed Il2Cpp Objects");
cppDecimalBoxed = new Il2CppSystem.Decimal(1f).BoxIl2CppObject();
cppVector3Boxed = Vector3.down.BoxIl2CppObject();
IL2CPP_listOfBoxedObjects = new List<Il2CppSystem.Object>(); IL2CPP_listOfBoxedObjects = new List<Il2CppSystem.Object>();
IL2CPP_listOfBoxedObjects.Add((Il2CppSystem.String)"boxedString"); IL2CPP_listOfBoxedObjects.Add((Il2CppSystem.String)"boxedString");
IL2CPP_listOfBoxedObjects.Add(new Il2CppSystem.Int32 { m_value = 5 }.BoxIl2CppObject()); IL2CPP_listOfBoxedObjects.Add(new Il2CppSystem.Int32 { m_value = 5 }.BoxIl2CppObject());
IL2CPP_listOfBoxedObjects.Add(Color.red.BoxIl2CppObject()); IL2CPP_listOfBoxedObjects.Add(Color.red.BoxIl2CppObject());
// boxed enum test
try try
{ {
var cppType = Il2CppType.Of<CameraClearFlags>(); var cppType = Il2CppType.Of<CameraClearFlags>();
@ -283,9 +208,10 @@ namespace UnityExplorer.Tests
} }
catch (Exception ex) catch (Exception ex)
{ {
ExplorerCore.LogWarning($"Test fail: {ex}"); ExplorerCore.LogWarning($"Boxed enum test fail: {ex}");
} }
ExplorerCore.Log($"IL2CPP 9: Il2Cpp struct array of ints");
IL2CPP_structArray = new UnhollowerBaseLib.Il2CppStructArray<int>(5); IL2CPP_structArray = new UnhollowerBaseLib.Il2CppStructArray<int>(5);
IL2CPP_structArray[0] = 0; IL2CPP_structArray[0] = 0;
IL2CPP_structArray[1] = 1; IL2CPP_structArray[1] = 1;
@ -293,24 +219,21 @@ namespace UnityExplorer.Tests
IL2CPP_structArray[3] = 3; IL2CPP_structArray[3] = 3;
IL2CPP_structArray[4] = 4; IL2CPP_structArray[4] = 4;
IL2CPP_stringArray = new UnhollowerBaseLib.Il2CppStringArray(2); ExplorerCore.Log($"IL2CPP 10: Il2Cpp reference array of boxed objects");
IL2CPP_stringArray[0] = "hello, ";
IL2CPP_stringArray[1] = "world!";
IL2CPP_ReferenceArray = new UnhollowerBaseLib.Il2CppReferenceArray<Il2CppSystem.Object>(3); IL2CPP_ReferenceArray = new UnhollowerBaseLib.Il2CppReferenceArray<Il2CppSystem.Object>(3);
IL2CPP_ReferenceArray[0] = new Il2CppSystem.Int32 { m_value = 5 }.BoxIl2CppObject(); IL2CPP_ReferenceArray[0] = new Il2CppSystem.Int32 { m_value = 5 }.BoxIl2CppObject();
IL2CPP_ReferenceArray[1] = null; IL2CPP_ReferenceArray[1] = null;
IL2CPP_ReferenceArray[2] = (Il2CppSystem.String)"whats up"; IL2CPP_ReferenceArray[2] = (Il2CppSystem.String)"whats up";
ExplorerCore.Log($"IL2CPP 11: Misc il2cpp members");
cppBoxedInt = new Il2CppSystem.Int32() { m_value = 5 }.BoxIl2CppObject(); cppBoxedInt = new Il2CppSystem.Int32() { m_value = 5 }.BoxIl2CppObject();
cppInt = new Il2CppSystem.Int32 { m_value = 420 }; cppInt = new Il2CppSystem.Int32 { m_value = 420 };
cppDecimal = new Il2CppSystem.Decimal(1f);
cppDecimalBoxed = new Il2CppSystem.Decimal(1f).BoxIl2CppObject();
cppVector3Boxed = Vector3.down.BoxIl2CppObject();
cppHashset = new Il2CppSystem.Collections.Hashtable(); ExplorerCore.Log($"Finished Init_Il2Cpp");
cppHashset.Add("key1", "itemOne");
cppHashset.Add("key2", "itemTwo");
cppHashset.Add("key3", "itemThree");
#endif
} }
#endif
} }
} }

View File

@ -11,14 +11,15 @@ namespace UnityExplorer
private static readonly char[] invalidDirectoryCharacters = Path.GetInvalidPathChars(); private static readonly char[] invalidDirectoryCharacters = Path.GetInvalidPathChars();
private static readonly char[] invalidFilenameCharacters = Path.GetInvalidFileNameChars(); private static readonly char[] invalidFilenameCharacters = Path.GetInvalidFileNameChars();
public static string EnsureValidDirectory(string path) public static string EnsureValidFilePath(string fullPathWithFile)
{ {
path = string.Concat(path.Split(invalidDirectoryCharacters)); // Remove invalid path characters
fullPathWithFile = string.Concat(fullPathWithFile.Split(invalidDirectoryCharacters));
if (!Directory.Exists(path)) // Create directory (does nothing if it exists)
Directory.CreateDirectory(path); Directory.CreateDirectory(Path.GetDirectoryName(fullPathWithFile));
return path; return fullPathWithFile;
} }
public static string EnsureValidFilename(string filename) public static string EnsureValidFilename(string filename)

View File

@ -13,7 +13,7 @@ namespace UnityExplorer
/// </summary> /// </summary>
public static bool ContainsIgnoreCase(this string _this, string s) public static bool ContainsIgnoreCase(this string _this, string s)
{ {
return ParseUtility.en_US.CompareInfo.IndexOf(_this, s, CompareOptions.IgnoreCase) >= 0; return CultureInfo.CurrentCulture.CompareInfo.IndexOf(_this, s, CompareOptions.IgnoreCase) >= 0;
} }
/// <summary> /// <summary>

View File

@ -10,8 +10,6 @@ namespace UnityExplorer
{ {
public static class ParseUtility public static class ParseUtility
{ {
public static CultureInfo en_US = new CultureInfo("en-US");
private static readonly HashSet<Type> nonPrimitiveTypes = new HashSet<Type> private static readonly HashSet<Type> nonPrimitiveTypes = new HashSet<Type>
{ {
typeof(string), typeof(string),
@ -19,20 +17,18 @@ namespace UnityExplorer
typeof(DateTime), typeof(DateTime),
}; };
public const string NUMBER_FORMAT = "0.####";
private static readonly Dictionary<int, string> numSequenceStrings = new Dictionary<int, string>();
// Helper for formatting float/double/decimal numbers to maximum of 4 decimal points. // Helper for formatting float/double/decimal numbers to maximum of 4 decimal points.
// And also for formatting a sequence of those numbers, ie a Vector3, Color etc
public static readonly string NumberFormatString = $"0.####";
private static readonly Dictionary<int, string> numSequenceStrings = new Dictionary<int, string>();
public static string FormatDecimalSequence(params object[] numbers) public static string FormatDecimalSequence(params object[] numbers)
{ {
if (numbers.Length <= 0) if (numbers.Length <= 0)
return null; return null;
int count = numbers.Length; return string.Format(CultureInfo.CurrentCulture, GetSequenceFormatString(numbers.Length), numbers);
var formatString = GetSequenceFormatString(count);
return string.Format(en_US, formatString, numbers);
} }
public static string GetSequenceFormatString(int count) public static string GetSequenceFormatString(int count)
@ -46,19 +42,19 @@ namespace UnityExplorer
string[] strings = new string[count]; string[] strings = new string[count];
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
strings[i] = $"{{{i}:{NUMBER_FORMAT}}}"; strings[i] = $"{{{i}:{NumberFormatString}}}";
string s = string.Join(", ", strings); string ret = string.Join(" ", strings);
numSequenceStrings.Add(count, ret);
numSequenceStrings.Add(count, s); return ret;
return s;
} }
// Main parsing API
public static bool CanParse(Type type) public static bool CanParse(Type type)
{ {
if (string.IsNullOrEmpty(type.FullName)) return !string.IsNullOrEmpty(type?.FullName)
return false; && (type.IsPrimitive || type.IsEnum || nonPrimitiveTypes.Contains(type) || customTypes.ContainsKey(type.FullName));
return type.IsPrimitive || type.IsEnum || nonPrimitiveTypes.Contains(type) || customTypes.ContainsKey(type.FullName);
} }
public static bool TryParse(string input, Type type, out object obj, out Exception parseException) public static bool TryParse(string input, Type type, out object obj, out Exception parseException)
@ -100,7 +96,7 @@ namespace UnityExplorer
obj = ReflectionUtility.GetMethodInfo(type, "Parse", ArgumentUtility.ParseArgs) obj = ReflectionUtility.GetMethodInfo(type, "Parse", ArgumentUtility.ParseArgs)
.Invoke(null, new object[] { input }); .Invoke(null, new object[] { input });
} }
return true; return true;
} }
catch (Exception ex) catch (Exception ex)
@ -143,12 +139,12 @@ namespace UnityExplorer
else if (formattedTypes.Contains(type)) else if (formattedTypes.Contains(type))
{ {
return ReflectionUtility.GetMethodInfo(type, "ToString", new Type[] { typeof(string), typeof(IFormatProvider) }) return ReflectionUtility.GetMethodInfo(type, "ToString", new Type[] { typeof(string), typeof(IFormatProvider) })
.Invoke(obj, new object[] { NUMBER_FORMAT, en_US }) .Invoke(obj, new object[] { NumberFormatString, CultureInfo.CurrentCulture })
as string; as string;
} }
else else
return obj.ToString(); return obj.ToString();
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -166,9 +162,7 @@ namespace UnityExplorer
try try
{ {
if (type.IsEnum) if (type.IsEnum)
{
typeInputExamples.Add(type.AssemblyQualifiedName, Enum.GetNames(type).First()); typeInputExamples.Add(type.AssemblyQualifiedName, Enum.GetNames(type).First());
}
else else
{ {
var instance = Activator.CreateInstance(type); var instance = Activator.CreateInstance(type);
@ -222,10 +216,10 @@ namespace UnityExplorer
{ {
Vector2 vector = default; Vector2 vector = default;
var split = input.Split(','); var split = input.Split(' ');
vector.x = float.Parse(split[0].Trim(), en_US); vector.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
vector.y = float.Parse(split[1].Trim(), en_US); vector.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
return vector; return vector;
} }
@ -244,11 +238,11 @@ namespace UnityExplorer
{ {
Vector3 vector = default; Vector3 vector = default;
var split = input.Split(','); var split = input.Split(' ');
vector.x = float.Parse(split[0].Trim(), en_US); vector.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
vector.y = float.Parse(split[1].Trim(), en_US); vector.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
vector.z = float.Parse(split[2].Trim(), en_US); vector.z = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
return vector; return vector;
} }
@ -267,12 +261,12 @@ namespace UnityExplorer
{ {
Vector4 vector = default; Vector4 vector = default;
var split = input.Split(','); var split = input.Split(' ');
vector.x = float.Parse(split[0].Trim(), en_US); vector.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
vector.y = float.Parse(split[1].Trim(), en_US); vector.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
vector.z = float.Parse(split[2].Trim(), en_US); vector.z = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
vector.w = float.Parse(split[3].Trim(), en_US); vector.w = float.Parse(split[3].Trim(), CultureInfo.CurrentCulture);
return vector; return vector;
} }
@ -291,22 +285,22 @@ namespace UnityExplorer
{ {
Vector3 vector = default; Vector3 vector = default;
var split = input.Split(','); var split = input.Split(' ');
if (split.Length == 4) if (split.Length == 4)
{ {
Quaternion quat = default; Quaternion quat = default;
quat.x = float.Parse(split[0].Trim(), en_US); quat.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
quat.y = float.Parse(split[1].Trim(), en_US); quat.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
quat.z = float.Parse(split[2].Trim(), en_US); quat.z = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
quat.w = float.Parse(split[3].Trim(), en_US); quat.w = float.Parse(split[3].Trim(), CultureInfo.CurrentCulture);
return quat; return quat;
} }
else else
{ {
vector.x = float.Parse(split[0].Trim(), en_US); vector.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
vector.y = float.Parse(split[1].Trim(), en_US); vector.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
vector.z = float.Parse(split[2].Trim(), en_US); vector.z = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
return Quaternion.Euler(vector); return Quaternion.Euler(vector);
} }
} }
@ -327,12 +321,12 @@ namespace UnityExplorer
{ {
Rect rect = default; Rect rect = default;
var split = input.Split(','); var split = input.Split(' ');
rect.x = float.Parse(split[0].Trim(), en_US); rect.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
rect.y = float.Parse(split[1].Trim(), en_US); rect.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
rect.width = float.Parse(split[2].Trim(), en_US); rect.width = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
rect.height = float.Parse(split[3].Trim(), en_US); rect.height = float.Parse(split[3].Trim(), CultureInfo.CurrentCulture);
return rect; return rect;
} }
@ -351,13 +345,13 @@ namespace UnityExplorer
{ {
Color color = default; Color color = default;
var split = input.Split(','); var split = input.Split(' ');
color.r = float.Parse(split[0].Trim(), en_US); color.r = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
color.g = float.Parse(split[1].Trim(), en_US); color.g = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
color.b = float.Parse(split[2].Trim(), en_US); color.b = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
if (split.Length > 3) if (split.Length > 3)
color.a = float.Parse(split[3].Trim(), en_US); color.a = float.Parse(split[3].Trim(), CultureInfo.CurrentCulture);
else else
color.a = 1; color.a = 1;
@ -378,13 +372,13 @@ namespace UnityExplorer
{ {
Color32 color = default; Color32 color = default;
var split = input.Split(','); var split = input.Split(' ');
color.r = byte.Parse(split[0].Trim(), en_US); color.r = byte.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
color.g = byte.Parse(split[1].Trim(), en_US); color.g = byte.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
color.b = byte.Parse(split[2].Trim(), en_US); color.b = byte.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
if (split.Length > 3) if (split.Length > 3)
color.a = byte.Parse(split[3].Trim(), en_US); color.a = byte.Parse(split[3].Trim(), CultureInfo.CurrentCulture);
else else
color.a = 255; color.a = 255;
@ -397,7 +391,7 @@ namespace UnityExplorer
return null; return null;
// ints, this is fine // ints, this is fine
return $"{color.r}, {color.g}, {color.b}, {color.a}"; return $"{color.r} {color.g} {color.b} {color.a}";
} }
// Layermask (Int32) // Layermask (Int32)

View File

@ -151,10 +151,10 @@ namespace UnityExplorer
private static string HighlightType(Type type) private static string HighlightType(Type type)
{ {
string key = type.ToString(); string key = type.ToString();
if (typeToRichType.ContainsKey(key)) if (typeToRichType.ContainsKey(key))
return typeToRichType[key]; return typeToRichType[key];
var sb = new StringBuilder(type.Name); var sb = new StringBuilder(type.Name);
bool isArray = false; bool isArray = false;
@ -212,7 +212,7 @@ namespace UnityExplorer
{ {
if (args.Length < 1) if (args.Length < 1)
return string.Empty; return string.Empty;
var sb = new StringBuilder(); var sb = new StringBuilder();
for (int i = 0; i < args.Length; i++) for (int i = 0; i < args.Length; i++)
@ -254,7 +254,7 @@ namespace UnityExplorer
isStatic = true; isStatic = true;
return FIELD_STATIC; return FIELD_STATIC;
} }
return FIELD_INSTANCE; return FIELD_INSTANCE;
} }
else if (memberInfo is MethodInfo mi) else if (memberInfo is MethodInfo mi)
@ -264,7 +264,7 @@ namespace UnityExplorer
isStatic = true; isStatic = true;
return METHOD_STATIC; return METHOD_STATIC;
} }
return METHOD_INSTANCE; return METHOD_INSTANCE;
} }
else if (memberInfo is PropertyInfo pi) else if (memberInfo is PropertyInfo pi)
@ -274,7 +274,7 @@ namespace UnityExplorer
isStatic = true; isStatic = true;
return PROP_STATIC; return PROP_STATIC;
} }
return PROP_INSTANCE; return PROP_INSTANCE;
} }
//else if (memberInfo is EventInfo ei) //else if (memberInfo is EventInfo ei)
@ -284,7 +284,7 @@ namespace UnityExplorer
// isStatic = true; // isStatic = true;
// return EVENT_STATIC; // return EVENT_STATIC;
// } // }
// return EVENT_INSTANCE; // return EVENT_INSTANCE;
//} //}

View File

@ -81,7 +81,7 @@ namespace UnityExplorer
sb.Append(PruneString(obj.name, 50, 1)); sb.Append(PruneString(obj.name, 50, 1));
sb.Append('"'); sb.Append('"');
} }
AppendRichType(sb, richType); AppendRichType(sb, richType);
} }
else if (type.FullName.StartsWith(eventSystemNamespace)) else if (type.FullName.StartsWith(eventSystemNamespace))
@ -93,8 +93,8 @@ namespace UnityExplorer
{ {
var toString = ToString(value); var toString = ToString(value);
if (type.IsGenericType if (type.IsGenericType
|| toString == type.FullName || toString == type.FullName
|| toString == $"{type.FullName} {type.FullName}" || toString == $"{type.FullName} {type.FullName}"
|| toString == $"Il2Cpp{type.FullName}" || type.FullName == $"Il2Cpp{toString}") || toString == $"Il2Cpp{type.FullName}" || type.FullName == $"Il2Cpp{toString}")
{ {

View File

@ -2,12 +2,13 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.Events; using UnityEngine.Events;
using UnityEngine.UI;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
// Project-wide namespace for accessibility
namespace UnityExplorer namespace UnityExplorer
{ {
public static class UnityHelpers public static class UnityHelpers
@ -30,25 +31,28 @@ namespace UnityExplorer
/// </summary> /// </summary>
public static bool IsNullOrDestroyed(this object obj, bool suppressWarning = true) public static bool IsNullOrDestroyed(this object obj, bool suppressWarning = true)
{ {
var unityObj = obj as Object; try
if (obj == null)
{ {
if (!suppressWarning) if (obj == null)
ExplorerCore.LogWarning("The target instance is null!"); {
if (!suppressWarning)
ExplorerCore.LogWarning("The target instance is null!");
return true; return true;
} }
else if (obj is Object) else if (obj is Object unityObj && !unityObj)
{
if (!unityObj)
{ {
if (!suppressWarning) if (!suppressWarning)
ExplorerCore.LogWarning("The target UnityEngine.Object was destroyed!"); ExplorerCore.LogWarning("The target UnityEngine.Object was destroyed!");
return true; return true;
} }
return false;
}
catch
{
return true;
} }
return false;
} }
/// <summary> /// <summary>
@ -107,5 +111,16 @@ namespace UnityExplorer
return color; return color;
} }
private static PropertyInfo onEndEdit;
public static UnityEvent<string> GetOnEndEdit(this InputField _this)
{
if (onEndEdit == null)
onEndEdit = typeof(InputField).GetProperty("onEndEdit")
?? throw new Exception("Could not get InputField.onEndEdit property!");
return onEndEdit.GetValue(_this, null).TryCast<UnityEvent<string>>();
}
} }
} }

View File

@ -11,8 +11,8 @@ using UnityExplorer.Core.Input;
using UnityExplorer.Core.Runtime; using UnityExplorer.Core.Runtime;
using UnityExplorer.Tests; using UnityExplorer.Tests;
using UnityExplorer.UI; using UnityExplorer.UI;
using UnityExplorer.UI.Inspectors; using UnityExplorer.Inspectors;
using UnityExplorer.UI.ObjectExplorer; using UnityExplorer.ObjectExplorer;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
namespace UnityExplorer namespace UnityExplorer
@ -20,13 +20,15 @@ namespace UnityExplorer
public static class ExplorerCore public static class ExplorerCore
{ {
public const string NAME = "UnityExplorer"; public const string NAME = "UnityExplorer";
public const string VERSION = "4.1.3"; public const string VERSION = "4.3.3";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer"; public const string GUID = "com.sinai.unityexplorer";
public static IExplorerLoader Loader { get; private set; } public static IExplorerLoader Loader { get; private set; }
public static RuntimeContext Context { get; internal set; } public static RuntimeContext Context { get; internal set; }
public static HarmonyLib.Harmony Harmony { get; } = new HarmonyLib.Harmony(GUID);
/// <summary> /// <summary>
/// Initialize UnityExplorer with the provided Loader implementation. /// Initialize UnityExplorer with the provided Loader implementation.
/// </summary> /// </summary>
@ -47,10 +49,9 @@ namespace UnityExplorer
Directory.CreateDirectory(Loader.ExplorerFolder); Directory.CreateDirectory(Loader.ExplorerFolder);
ConfigManager.Init(Loader.ConfigHandler); ConfigManager.Init(Loader.ConfigHandler);
ReflectionUtility.Init(); ReflectionUtility.Init();
RuntimeProvider.Init(); RuntimeProvider.Init();
SceneHandler.Init(); SceneHandler.Init();
InputManager.Init(); InputManager.Init();
@ -65,14 +66,13 @@ namespace UnityExplorer
private static IEnumerator SetupCoroutine() private static IEnumerator SetupCoroutine()
{ {
yield return null; yield return null;
float prevRealTime = Time.realtimeSinceStartup;
float start = Time.realtimeSinceStartup;
float delay = ConfigManager.Startup_Delay_Time.Value; float delay = ConfigManager.Startup_Delay_Time.Value;
while (delay > 0) while (delay > 0)
{ {
float diff = Math.Max(Time.deltaTime, Time.realtimeSinceStartup - start); float diff = Math.Max(Time.deltaTime, Time.realtimeSinceStartup - prevRealTime);
delay -= diff; delay -= diff;
prevRealTime = Time.realtimeSinceStartup;
yield return null; yield return null;
} }
@ -103,15 +103,15 @@ namespace UnityExplorer
RuntimeProvider.Instance.ProcessOnPostRender(); RuntimeProvider.Instance.ProcessOnPostRender();
} }
#region LOGGING #region LOGGING
public static void Log(object message) public static void Log(object message)
=> Log(message, LogType.Log); => Log(message, LogType.Log);
public static void LogWarning(object message) public static void LogWarning(object message)
=> Log(message, LogType.Warning); => Log(message, LogType.Warning);
public static void LogError(object message) public static void LogError(object message)
=> Log(message, LogType.Error); => Log(message, LogType.Error);
public static void LogUnity(object message, LogType logType) public static void LogUnity(object message, LogType logType)
@ -146,6 +146,6 @@ namespace UnityExplorer
} }
} }
#endregion #endregion
} }
} }

63
src/Hooks/AddHookCell.cs Normal file
View File

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.Hooks
{
public class AddHookCell : ICell
{
public bool Enabled => UIRoot.activeSelf;
public RectTransform Rect { get; set; }
public GameObject UIRoot { get; set; }
public float DefaultHeight => 30;
public Text MethodNameLabel;
public Text HookedLabel;
public ButtonRef HookButton;
public int CurrentDisplayedIndex;
private void OnHookClicked()
{
HookManager.Instance.AddHookClicked(CurrentDisplayedIndex);
}
public void Enable()
{
this.UIRoot.SetActive(true);
}
public void Disable()
{
this.UIRoot.SetActive(false);
}
public GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateUIObject(this.GetType().Name, parent, new Vector2(100, 30));
Rect = UIRoot.GetComponent<RectTransform>();
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(UIRoot, false, false, true, true, 5, childAlignment: TextAnchor.UpperLeft);
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
HookedLabel = UIFactory.CreateLabel(UIRoot, "HookedLabel", "✓", TextAnchor.MiddleCenter, Color.green);
UIFactory.SetLayoutElement(HookedLabel.gameObject, minHeight: 25, minWidth: 100);
HookButton = UIFactory.CreateButton(UIRoot, "HookButton", "Hook", new Color(0.2f, 0.25f, 0.2f));
UIFactory.SetLayoutElement(HookButton.Component.gameObject, minHeight: 25, minWidth: 100);
HookButton.OnClick += OnHookClicked;
MethodNameLabel = UIFactory.CreateLabel(UIRoot, "MethodName", "NOT SET", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(MethodNameLabel.gameObject, minHeight: 25, flexibleWidth: 9999);
return UIRoot;
}
}
}

79
src/Hooks/HookCell.cs Normal file
View File

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.Hooks
{
public class HookCell : ICell
{
public bool Enabled => UIRoot.activeSelf;
public RectTransform Rect { get; set; }
public GameObject UIRoot { get; set; }
public float DefaultHeight => 30;
public Text MethodNameLabel;
public ButtonRef EditPatchButton;
public ButtonRef ToggleActiveButton;
public ButtonRef DeleteButton;
public int CurrentDisplayedIndex;
private void OnToggleActiveClicked()
{
HookManager.Instance.EnableOrDisableHookClicked(CurrentDisplayedIndex);
}
private void OnDeleteClicked()
{
HookManager.Instance.DeleteHookClicked(CurrentDisplayedIndex);
}
private void OnEditPatchClicked()
{
HookManager.Instance.EditPatchClicked(CurrentDisplayedIndex);
}
public GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateUIObject(this.GetType().Name, parent, new Vector2(100, 30));
Rect = UIRoot.GetComponent<RectTransform>();
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(UIRoot, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft);
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
MethodNameLabel = UIFactory.CreateLabel(UIRoot, "MethodName", "NOT SET", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(MethodNameLabel.gameObject, minHeight: 25, flexibleWidth: 9999);
ToggleActiveButton = UIFactory.CreateButton(UIRoot, "ToggleActiveBtn", "Enabled", new Color(0.15f, 0.2f, 0.15f));
UIFactory.SetLayoutElement(ToggleActiveButton.Component.gameObject, minHeight: 25, minWidth: 100);
ToggleActiveButton.OnClick += OnToggleActiveClicked;
DeleteButton = UIFactory.CreateButton(UIRoot, "DeleteButton", "Delete", new Color(0.2f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(DeleteButton.Component.gameObject, minHeight: 25, minWidth: 100);
DeleteButton.OnClick += OnDeleteClicked;
EditPatchButton = UIFactory.CreateButton(UIRoot, "EditButton", "Edit Hook Source", new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(EditPatchButton.Component.gameObject, minHeight: 25, minWidth: 150);
EditPatchButton.OnClick += OnEditPatchClicked;
return UIRoot;
}
public void Disable()
{
UIRoot.SetActive(false);
}
public void Enable()
{
UIRoot.SetActive(true);
}
}
}

235
src/Hooks/HookInstance.cs Normal file
View File

@ -0,0 +1,235 @@
using System;
using System.CodeDom.Compiler;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using HarmonyLib;
using Mono.CSharp;
using UnityExplorer.CSConsole;
namespace UnityExplorer.Hooks
{
public class HookInstance
{
// Static
private static readonly StringBuilder evalOutput = new StringBuilder();
private static readonly ScriptEvaluator scriptEvaluator = new ScriptEvaluator(new StringWriter(evalOutput));
static HookInstance()
{
scriptEvaluator.Run("using System;");
scriptEvaluator.Run("using System.Reflection;");
scriptEvaluator.Run("using System.Collections;");
scriptEvaluator.Run("using System.Collections.Generic;");
}
// Instance
public bool Enabled;
public MethodInfo TargetMethod;
public string PatchSourceCode;
private readonly string shortSignature;
private PatchProcessor patchProcessor;
private MethodInfo postfix;
private MethodInfo prefix;
private MethodInfo finalizer;
private MethodInfo transpiler;
public HookInstance(MethodInfo targetMethod)
{
this.TargetMethod = targetMethod;
this.shortSignature = $"{targetMethod.DeclaringType.Name}.{targetMethod.Name}";
GenerateDefaultPatchSourceCode(targetMethod);
if (CompileAndGenerateProcessor(PatchSourceCode))
Patch();
}
// Evaluator.source_file
private static readonly FieldInfo fi_sourceFile = ReflectionUtility.GetFieldInfo(typeof(Evaluator), "source_file");
// TypeDefinition.Definition
private static readonly PropertyInfo pi_Definition = ReflectionUtility.GetPropertyInfo(typeof(TypeDefinition), "Definition");
public bool CompileAndGenerateProcessor(string patchSource)
{
Unpatch();
try
{
patchProcessor = ExplorerCore.Harmony.CreateProcessor(TargetMethod);
// Dynamically compile the patch method
var codeBuilder = new StringBuilder();
codeBuilder.AppendLine($"public class DynamicPatch_{DateTime.Now.Ticks}");
codeBuilder.AppendLine("{");
codeBuilder.AppendLine(patchSource);
codeBuilder.AppendLine("}");
scriptEvaluator.Run(codeBuilder.ToString());
if (ScriptEvaluator._reportPrinter.ErrorsCount > 0)
throw new FormatException($"Unable to compile the generated patch!");
// TODO: Publicize MCS to avoid this reflection
// Get the most recent Patch type in the source file
var typeContainer = ((CompilationSourceFile)fi_sourceFile.GetValue(scriptEvaluator))
.Containers
.Last(it => it.MemberName.Name.StartsWith("DynamicPatch_"));
// Get the TypeSpec from the TypeDefinition, then get its "MetaInfo" (System.Type)
var patchClass = ((TypeSpec)pi_Definition.GetValue((Class)typeContainer, null)).GetMetaInfo();
// Create the harmony patches as defined
postfix = patchClass.GetMethod("Postfix", ReflectionUtility.FLAGS);
if (postfix != null)
patchProcessor.AddPostfix(new HarmonyMethod(postfix));
prefix = patchClass.GetMethod("Prefix", ReflectionUtility.FLAGS);
if (prefix != null)
patchProcessor.AddPrefix(new HarmonyMethod(prefix));
finalizer = patchClass.GetMethod("Finalizer", ReflectionUtility.FLAGS);
if (finalizer != null)
patchProcessor.AddFinalizer(new HarmonyMethod(finalizer));
transpiler = patchClass.GetMethod("Transpiler", ReflectionUtility.FLAGS);
if (transpiler != null)
patchProcessor.AddTranspiler(new HarmonyMethod(transpiler));
return true;
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception creating patch processor for target method {TargetMethod.FullDescription()}!\r\n{ex}");
return false;
}
}
private string GenerateDefaultPatchSourceCode(MethodInfo targetMethod)
{
var codeBuilder = new StringBuilder();
// Arguments
codeBuilder.Append("public static void Postfix(System.Reflection.MethodBase __originalMethod");
if (!targetMethod.IsStatic)
codeBuilder.Append($", {targetMethod.DeclaringType.FullName} __instance");
if (targetMethod.ReturnType != typeof(void))
codeBuilder.Append($", {targetMethod.ReturnType.FullName} __result");
var parameters = targetMethod.GetParameters();
int paramIdx = 0;
foreach (var param in parameters)
{
codeBuilder.Append($", {param.ParameterType.FullDescription().Replace("&", "")} __{paramIdx}");
paramIdx++;
}
codeBuilder.Append(")\n");
// Patch body
codeBuilder.AppendLine("{");
codeBuilder.AppendLine(" try {");
// Log message
var logMessage = new StringBuilder();
logMessage.Append($"Patch called: {shortSignature}\\n");
if (!targetMethod.IsStatic)
logMessage.Append("__instance: {__instance.ToString()}\\n");
paramIdx = 0;
foreach (var param in parameters)
{
logMessage.Append($"Parameter {paramIdx} {param.Name}: ");
Type pType = param.ParameterType;
if (pType.IsByRef) pType = pType.GetElementType();
if (pType.IsValueType)
logMessage.Append($"{{__{paramIdx}.ToString()}}");
else
logMessage.Append($"{{__{paramIdx}?.ToString() ?? \"null\"}}");
logMessage.Append("\\n");
paramIdx++;
}
if (targetMethod.ReturnType != typeof(void))
{
logMessage.Append("Return value: ");
if (targetMethod.ReturnType.IsValueType)
logMessage.Append("{__result.ToString()}");
else
logMessage.Append("{__result?.ToString() ?? \"null\"}");
logMessage.Append("\\n");
}
codeBuilder.AppendLine($" UnityExplorer.ExplorerCore.Log($\"{logMessage}\");");
codeBuilder.AppendLine(" }");
codeBuilder.AppendLine(" catch (System.Exception ex) {");
codeBuilder.AppendLine($" UnityExplorer.ExplorerCore.LogWarning($\"Exception in patch of {shortSignature}:\\n{{ex}}\");");
codeBuilder.AppendLine(" }");
// End patch body
codeBuilder.AppendLine("}");
//ExplorerCore.Log(codeBuilder.ToString());
return PatchSourceCode = codeBuilder.ToString();
}
public void TogglePatch()
{
if (!Enabled)
Patch();
else
Unpatch();
}
public void Patch()
{
try
{
patchProcessor.Patch();
Enabled = true;
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception hooking method!\r\n{ex}");
}
}
public void Unpatch()
{
try
{
if (prefix != null)
patchProcessor.Unpatch(prefix);
if (postfix != null)
patchProcessor.Unpatch(postfix);
if (finalizer != null)
patchProcessor.Unpatch(finalizer);
if (transpiler != null)
patchProcessor.Unpatch(transpiler);
Enabled = false;
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception unpatching method: {ex}");
}
}
}
}

271
src/Hooks/HookManager.cs Normal file
View File

@ -0,0 +1,271 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
using System.Text;
using HarmonyLib;
using UnityEngine;
using UnityExplorer.CSConsole;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.Hooks
{
public class HookManager : ICellPoolDataSource<HookCell>, ICellPoolDataSource<AddHookCell>
{
private static HookManager s_instance;
public static HookManager Instance => s_instance ?? (s_instance = new HookManager());
public HookManagerPanel Panel => UIManager.GetPanel<HookManagerPanel>(UIManager.Panels.HookManager);
// This class acts as the data source for both current hooks and eligable methods when adding hooks.
// 'isAddingMethods' keeps track of which pool is currently the displayed one, so our ItemCount reflects the
// correct pool cells.
private bool isAddingMethods;
public int ItemCount => isAddingMethods ? filteredEligableMethods.Count : currentHooks.Count;
// current hooks
private readonly HashSet<string> hookedSignatures = new HashSet<string>();
private readonly OrderedDictionary currentHooks = new OrderedDictionary();
// adding hooks
private readonly List<MethodInfo> currentAddEligableMethods = new List<MethodInfo>();
private readonly List<MethodInfo> filteredEligableMethods = new List<MethodInfo>();
// hook editor
private readonly LexerBuilder Lexer = new LexerBuilder();
private HookInstance currentEditedHook;
// ~~~~~~~~~~~ Main Current Hooks window ~~~~~~~~~~~
public void EnableOrDisableHookClicked(int index)
{
var hook = (HookInstance)currentHooks[index];
hook.TogglePatch();
Panel.HooksScrollPool.Refresh(true, false);
}
public void DeleteHookClicked(int index)
{
var hook = (HookInstance)currentHooks[index];
hook.Unpatch();
currentHooks.RemoveAt(index);
hookedSignatures.Remove(hook.TargetMethod.FullDescription());
Panel.HooksScrollPool.Refresh(true, false);
}
public void EditPatchClicked(int index)
{
Panel.SetPage(HookManagerPanel.Pages.HookSourceEditor);
var hook = (HookInstance)currentHooks[index];
currentEditedHook = hook;
Panel.EditorInput.Text = hook.PatchSourceCode;
}
// Set current hook cell
public void OnCellBorrowed(HookCell cell) { }
public void SetCell(HookCell cell, int index)
{
if (index >= this.currentHooks.Count)
{
cell.Disable();
return;
}
cell.CurrentDisplayedIndex = index;
var hook = (HookInstance)this.currentHooks[index];
cell.MethodNameLabel.text = HighlightMethod(hook.TargetMethod);
cell.ToggleActiveButton.ButtonText.text = hook.Enabled ? "Enabled" : "Disabled";
RuntimeProvider.Instance.SetColorBlockAuto(cell.ToggleActiveButton.Component,
hook.Enabled ? new Color(0.15f, 0.2f, 0.15f) : new Color(0.2f, 0.2f, 0.15f));
}
// ~~~~~~~~~~~ Add Hooks window ~~~~~~~~~~~
public void OnClassSelectedForHooks(string typeFullName)
{
var type = ReflectionUtility.GetTypeByName(typeFullName);
if (type == null)
{
ExplorerCore.LogWarning($"Could not find any type by name {typeFullName}!");
return;
}
Panel.SetAddHooksLabelType(SignatureHighlighter.Parse(type, true));
Panel.ResetMethodFilter();
filteredEligableMethods.Clear();
currentAddEligableMethods.Clear();
foreach (var method in type.GetMethods(ReflectionUtility.FLAGS))
{
if (method.IsGenericMethod /* || method.IsAbstract */ || ReflectionUtility.IsBlacklisted(method))
continue;
currentAddEligableMethods.Add(method);
filteredEligableMethods.Add(method);
}
isAddingMethods = true;
Panel.SetPage(HookManagerPanel.Pages.ClassMethodSelector);
Panel.AddHooksScrollPool.Refresh(true, true);
}
public void DoneAddingHooks()
{
isAddingMethods = false;
Panel.SetPage(HookManagerPanel.Pages.CurrentHooks);
Panel.HooksScrollPool.Refresh(true, false);
}
public void AddHookClicked(int index)
{
if (index >= this.filteredEligableMethods.Count)
return;
AddHook(filteredEligableMethods[index]);
Panel.AddHooksScrollPool.Refresh(true, false);
}
public void AddHook(MethodInfo method)
{
var sig = method.FullDescription();
if (hookedSignatures.Contains(sig))
return;
var hook = new HookInstance(method);
if (hook.Enabled)
{
hookedSignatures.Add(sig);
currentHooks.Add(sig, hook);
}
}
public void OnAddHookFilterInputChanged(string input)
{
filteredEligableMethods.Clear();
if (string.IsNullOrEmpty(input))
filteredEligableMethods.AddRange(currentAddEligableMethods);
else
{
foreach (var method in currentAddEligableMethods)
{
if (method.Name.ContainsIgnoreCase(input))
filteredEligableMethods.Add(method);
}
}
Panel.AddHooksScrollPool.Refresh(true, true);
}
// Set eligable method cell
public void OnCellBorrowed(AddHookCell cell) { }
public void SetCell(AddHookCell cell, int index)
{
if (index >= this.filteredEligableMethods.Count)
{
cell.Disable();
return;
}
cell.CurrentDisplayedIndex = index;
var method = this.filteredEligableMethods[index];
cell.MethodNameLabel.text = HighlightMethod(method);
var sig = method.FullDescription();
if (hookedSignatures.Contains(sig))
{
cell.HookButton.Component.gameObject.SetActive(false);
cell.HookedLabel.gameObject.SetActive(true);
}
else
{
cell.HookButton.Component.gameObject.SetActive(true);
cell.HookedLabel.gameObject.SetActive(false);
}
}
// ~~~~~~~~~~~ Hook source editor window ~~~~~~~~~~~
public void OnEditorInputChanged(string value)
{
Panel.EditorHighlightText.text = Lexer.BuildHighlightedString(value, 0, value.Length - 1, 0,
Panel.EditorInput.Component.caretPosition, out _);
}
public void EditorInputCancel()
{
currentEditedHook = null;
Panel.SetPage(HookManagerPanel.Pages.CurrentHooks);
}
public void EditorInputSave()
{
var input = Panel.EditorInput.Text;
bool wasEnabled = currentEditedHook.Enabled;
if (currentEditedHook.CompileAndGenerateProcessor(input))
{
if (wasEnabled)
currentEditedHook.Patch();
currentEditedHook.PatchSourceCode = input;
currentEditedHook = null;
Panel.SetPage(HookManagerPanel.Pages.CurrentHooks);
}
}
// ~~~~~~~~~~ Method syntax highlighting
private static readonly Dictionary<string, string> highlightedMethods = new Dictionary<string, string>();
private string HighlightMethod(MethodInfo method)
{
var sig = method.FullDescription();
if (highlightedMethods.ContainsKey(sig))
return highlightedMethods[sig];
var sb = new StringBuilder();
// declaring type
sb.Append(SignatureHighlighter.Parse(method.DeclaringType, false));
sb.Append('.');
// method name
var color = !method.IsStatic
? SignatureHighlighter.METHOD_INSTANCE
: SignatureHighlighter.METHOD_STATIC;
sb.Append($"<color={color}>{method.Name}</color>");
// arguments
sb.Append('(');
var args = method.GetParameters();
if (args != null && args.Any())
{
int i = 0;
foreach (var param in args)
{
sb.Append(SignatureHighlighter.Parse(param.ParameterType, false));
sb.Append(' ');
sb.Append($"<color={SignatureHighlighter.LOCAL_ARG}>{param.Name}</color>");
i++;
if (i < args.Length)
sb.Append(", ");
}
}
sb.Append(')');
var ret = sb.ToString();
highlightedMethods.Add(sig, ret);
return ret;
}
}
}

View File

@ -8,24 +8,15 @@
<InputAssemblies Include="..\lib\mcs-unity\mcs\bin\Release\mcs.dll" /> <InputAssemblies Include="..\lib\mcs-unity\mcs\bin\Release\mcs.dll" />
<InputAssemblies Include="packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll" /> <InputAssemblies Include="packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll" />
</ItemGroup> </ItemGroup>
<!-- MonoMod for MelonLoader 0.3.0 -->
<ItemGroup Condition="'$(IsMelonLoaderLegacy)'=='true'">
<InputAssemblies Include="packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.dll" />
<InputAssemblies Include="packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Mdb.dll" />
<InputAssemblies Include="packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Pdb.dll" />
<InputAssemblies Include="packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Rocks.dll" />
<InputAssemblies Include="packages\MonoMod.RuntimeDetour.20.1.1.4\lib\net35\MonoMod.RuntimeDetour.dll" />
<InputAssemblies Include="packages\MonoMod.Utils.20.1.1.4\lib\net35\MonoMod.Utils.dll" />
</ItemGroup>
<!-- Required references for ILRepack --> <!-- Required references for ILRepack -->
<ItemGroup> <ItemGroup>
<ReferenceFolders Include="packages\HarmonyX.2.4.2\lib\net35\" /> <ReferenceFolders Include="packages\HarmonyX.2.5.2\lib\net35\" />
<ReferenceFolders Include="..\lib\BepInEx.6.IL2CPP\" /> <ReferenceFolders Include="..\lib\BepInEx.6.IL2CPP\" />
<ReferenceFolders Include="..\lib\BepInEx.6.Mono\" /> <ReferenceFolders Include="..\lib\BepInEx.6.Mono\" />
<ReferenceFolders Include="..\lib\BepInEx.5\" /> <ReferenceFolders Include="..\lib\BepInEx.5\" />
<ReferenceFolders Include="..\lib\MelonLoader\" /> <ReferenceFolders Include="..\lib\MelonLoader\" />
<ReferenceFolders Include="..\lib\Il2CppAssemblyUnhollower\UnhollowerBaseLib\bin\Release\net4.7.2\" />
</ItemGroup> </ItemGroup>
<ILRepack <ILRepack

View File

@ -6,12 +6,13 @@ using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.Core.Input; using UnityExplorer.Core.Input;
using UnityExplorer.UI;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete; using UnityExplorer.UI.Widgets.AutoComplete;
namespace UnityExplorer.UI.Inspectors namespace UnityExplorer.Inspectors
{ {
public class GameObjectInspector : InspectorBase public class GameObjectInspector : InspectorBase
{ {
@ -147,32 +148,39 @@ namespace UnityExplorer.UI.Inspectors
var behaviours = GOTarget.GetComponents<Behaviour>(); var behaviours = GOTarget.GetComponents<Behaviour>();
bool needRefresh = false; bool needRefresh = false;
if (comps.Length != componentEntries.Count || behaviours.Length != behaviourEntries.Count)
{
needRefresh = true;
}
else
{
foreach (var comp in comps)
{
if (!compInstanceIDs.Contains(comp.GetInstanceID()))
{
needRefresh = true;
break;
}
}
if (!needRefresh) int count = 0;
foreach (var comp in comps)
{
if (!comp)
continue;
count++;
if (!compInstanceIDs.Contains(comp.GetInstanceID()))
{ {
for (int i = 0; i < behaviours.Length; i++) needRefresh = true;
break;
}
}
if (!needRefresh)
{
if (count != componentEntries.Count)
needRefresh = true;
else
{
count = 0;
foreach (var behaviour in behaviours)
{ {
var behaviour = behaviours[i]; if (!behaviour)
if (behaviour.enabled != behaviourEnabledStates[i]) continue;
if (count >= behaviourEnabledStates.Count || behaviour.enabled != behaviourEnabledStates[count])
{ {
needRefresh = true; needRefresh = true;
break; break;
} }
count++;
} }
if (!needRefresh && count != behaviourEntries.Count)
needRefresh = true;
} }
} }
@ -181,9 +189,9 @@ namespace UnityExplorer.UI.Inspectors
componentEntries.Clear(); componentEntries.Clear();
compInstanceIDs.Clear(); compInstanceIDs.Clear();
foreach (var comp in comps) foreach (var comp in comps)
{ {
if (!comp) continue;
componentEntries.Add(comp); componentEntries.Add(comp);
compInstanceIDs.Add(comp.GetInstanceID()); compInstanceIDs.Add(comp.GetInstanceID());
} }
@ -192,6 +200,7 @@ namespace UnityExplorer.UI.Inspectors
behaviourEnabledStates.Clear(); behaviourEnabledStates.Clear();
foreach (var behaviour in behaviours) foreach (var behaviour in behaviours)
{ {
if (!behaviour) continue;
behaviourEntries.Add(behaviour); behaviourEntries.Add(behaviour);
behaviourEnabledStates.Add(behaviour.enabled); behaviourEnabledStates.Add(behaviour.enabled);
} }
@ -208,10 +217,10 @@ namespace UnityExplorer.UI.Inspectors
TransformTree.RefreshData(true, false); TransformTree.RefreshData(true, false);
} }
private void OnAddComponentClicked(string input) private void OnAddComponentClicked(string input)
{ {
if (ReflectionUtility.AllTypes.TryGetValue(input, out Type type)) if (ReflectionUtility.GetTypeByName(input) is Type type)
{ {
try try
{ {
@ -234,10 +243,10 @@ namespace UnityExplorer.UI.Inspectors
public override GameObject CreateContent(GameObject parent) public override GameObject CreateContent(GameObject parent)
{ {
UIRoot = UIFactory.CreateVerticalGroup(parent, "GameObjectInspector", true, false, true, true, 5, UIRoot = UIFactory.CreateVerticalGroup(parent, "GameObjectInspector", true, false, true, true, 5,
new Vector4(4, 4, 4, 4), new Color(0.065f, 0.065f, 0.065f)); new Vector4(4, 4, 4, 4), new Color(0.065f, 0.065f, 0.065f));
var scrollObj = UIFactory.CreateScrollView(UIRoot, "GameObjectInspector", out Content, out var scrollbar, var scrollObj = UIFactory.CreateScrollView(UIRoot, "GameObjectInspector", out Content, out var scrollbar,
new Color(0.065f, 0.065f, 0.065f)); new Color(0.065f, 0.065f, 0.065f));
UIFactory.SetLayoutElement(scrollObj, minHeight: 250, preferredHeight: 300, flexibleHeight: 0, flexibleWidth: 9999); UIFactory.SetLayoutElement(scrollObj, minHeight: 250, preferredHeight: 300, flexibleHeight: 0, flexibleWidth: 9999);
@ -245,7 +254,7 @@ namespace UnityExplorer.UI.Inspectors
// Construct GO Controls // Construct GO Controls
GOControls = new GameObjectControls(this); GOControls = new GameObjectControls(this);
ConstructLists(); ConstructLists();
return UIRoot; return UIRoot;

View File

@ -4,9 +4,10 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors namespace UnityExplorer.Inspectors
{ {
public class ComponentCell : ButtonCell public class ComponentCell : ButtonCell
{ {

View File

@ -5,13 +5,13 @@ using System.Text;
using UnityEngine; using UnityEngine;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors namespace UnityExplorer.Inspectors
{ {
public class ComponentList : ButtonListHandler<Component, ComponentCell> public class ComponentList : ButtonListHandler<Component, ComponentCell>
{ {
public GameObjectInspector Parent; public GameObjectInspector Parent;
public ComponentList(ScrollPool<ComponentCell> scrollPool, Func<List<Component>> getEntriesMethod) public ComponentList(ScrollPool<ComponentCell> scrollPool, Func<List<Component>> getEntriesMethod)
: base(scrollPool, getEntriesMethod, null, null, null) : base(scrollPool, getEntriesMethod, null, null, null)
{ {
base.SetICell = SetComponentCell; base.SetICell = SetComponentCell;
@ -112,7 +112,7 @@ namespace UnityExplorer.UI.Inspectors
cell.BehaviourToggle.interactable = false; cell.BehaviourToggle.interactable = false;
cell.BehaviourToggle.Set(true, false); cell.BehaviourToggle.Set(true, false);
//RuntimeProvider.Instance.SetColorBlock(cell.BehaviourToggle,) //RuntimeProvider.Instance.SetColorBlock(cell.BehaviourToggle,)
cell.BehaviourToggle.graphic.color = new Color(0.2f, 0.2f, 0.2f); cell.BehaviourToggle.graphic.color = new Color(0.2f, 0.2f, 0.2f);
} }
// if component is the first index it must be the transform, dont show Destroy button for it. // if component is the first index it must be the transform, dont show Destroy button for it.

View File

@ -5,8 +5,9 @@ using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.Core.Input; using UnityExplorer.Core.Input;
using UnityExplorer.UI;
namespace UnityExplorer.UI.Inspectors namespace UnityExplorer.Inspectors
{ {
public class GameObjectControls public class GameObjectControls
{ {
@ -186,7 +187,7 @@ namespace UnityExplorer.UI.Inspectors
if (parentToSet) if (parentToSet)
DoSetParent(parentToSet); DoSetParent(parentToSet);
else else
{ {
ExplorerCore.LogWarning($"Could not find any GameObject name or path '{input}'!"); ExplorerCore.LogWarning($"Could not find any GameObject name or path '{input}'!");
UpdateGameObjectInfo(false, true); UpdateGameObjectInfo(false, true);
} }
@ -234,7 +235,7 @@ namespace UnityExplorer.UI.Inspectors
private void OnExploreButtonClicked() private void OnExploreButtonClicked()
{ {
var panel = UIManager.GetPanel<Panels.ObjectExplorerPanel>(UIManager.Panels.ObjectExplorer); var panel = UIManager.GetPanel<UI.Panels.ObjectExplorerPanel>(UIManager.Panels.ObjectExplorer);
panel.SceneExplorer.JumpToTransform(this.Parent.GOTarget.transform); panel.SceneExplorer.JumpToTransform(this.Parent.GOTarget.transform);
} }
@ -442,7 +443,7 @@ namespace UnityExplorer.UI.Inspectors
private void ConstructTopInfo() private void ConstructTopInfo()
{ {
var topInfoHolder = UIFactory.CreateVerticalGroup(Parent.Content, "TopInfoHolder", false, false, true, true, 3, var topInfoHolder = UIFactory.CreateVerticalGroup(Parent.Content, "TopInfoHolder", false, false, true, true, 3,
new Vector4(3, 3, 3, 3), new Color(0.1f, 0.1f, 0.1f), TextAnchor.MiddleLeft); new Vector4(3, 3, 3, 3), new Color(0.1f, 0.1f, 0.1f), TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(topInfoHolder, minHeight: 100, flexibleWidth: 9999); UIFactory.SetLayoutElement(topInfoHolder, minHeight: 100, flexibleWidth: 9999);
topInfoHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize; topInfoHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
@ -468,7 +469,7 @@ namespace UnityExplorer.UI.Inspectors
//UIFactory.SetLayoutElement(pathApplyBtn.Component.gameObject, minHeight: 25, minWidth: 120); //UIFactory.SetLayoutElement(pathApplyBtn.Component.gameObject, minHeight: 25, minWidth: 120);
//pathApplyBtn.OnClick += () => { OnPathEndEdit(PathInput.Text); }; //pathApplyBtn.OnClick += () => { OnPathEndEdit(PathInput.Text); };
PathInput.Component.onEndEdit.AddListener((string val) => { OnPathEndEdit(val); }); PathInput.Component.GetOnEndEdit().AddListener((string val) => { OnPathEndEdit(val); });
// Title and update row // Title and update row
@ -484,7 +485,7 @@ namespace UnityExplorer.UI.Inspectors
NameInput = UIFactory.CreateInputField(titleRow, "NameInput", "untitled"); NameInput = UIFactory.CreateInputField(titleRow, "NameInput", "untitled");
UIFactory.SetLayoutElement(NameInput.Component.gameObject, minHeight: 30, minWidth: 100, flexibleWidth: 9999); UIFactory.SetLayoutElement(NameInput.Component.gameObject, minHeight: 30, minWidth: 100, flexibleWidth: 9999);
NameInput.Component.textComponent.fontSize = 15; NameInput.Component.textComponent.fontSize = 15;
NameInput.Component.onEndEdit.AddListener((string val) => { OnNameEndEdit(val); }); NameInput.Component.GetOnEndEdit().AddListener((string val) => { OnNameEndEdit(val); });
// second row (toggles, instanceID, tag, buttons) // second row (toggles, instanceID, tag, buttons)
@ -521,7 +522,7 @@ namespace UnityExplorer.UI.Inspectors
TagInput = UIFactory.CreateInputField(secondRow, "TagInput", "none"); TagInput = UIFactory.CreateInputField(secondRow, "TagInput", "none");
UIFactory.SetLayoutElement(TagInput.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 999); UIFactory.SetLayoutElement(TagInput.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 999);
TagInput.Component.textComponent.color = Color.white; TagInput.Component.textComponent.color = Color.white;
TagInput.Component.onEndEdit.AddListener((string val) => { OnTagEndEdit(val); }); TagInput.Component.GetOnEndEdit().AddListener((string val) => { OnTagEndEdit(val); });
// Instantiate // Instantiate
var instantiateBtn = UIFactory.CreateButton(secondRow, "InstantiateBtn", "Instantiate", new Color(0.2f, 0.2f, 0.2f)); var instantiateBtn = UIFactory.CreateButton(secondRow, "InstantiateBtn", "Instantiate", new Color(0.2f, 0.2f, 0.2f));
@ -575,7 +576,7 @@ namespace UnityExplorer.UI.Inspectors
var flagsDrop = UIFactory.CreateDropdown(thirdrow, out FlagsDropdown, "None", 14, OnFlagsDropdownChanged); var flagsDrop = UIFactory.CreateDropdown(thirdrow, out FlagsDropdown, "None", 14, OnFlagsDropdownChanged);
FlagsDropdown.captionText.color = SignatureHighlighter.EnumGreen; FlagsDropdown.captionText.color = SignatureHighlighter.EnumGreen;
UIFactory.SetLayoutElement(flagsDrop, minHeight: 25, minWidth: 135, flexibleWidth: 999); UIFactory.SetLayoutElement(flagsDrop, minHeight: 25, minWidth: 135, flexibleWidth: 999);
if (hideFlagsValues == null) if (hideFlagsValues == null)
GetHideFlagNames(); GetHideFlagNames();
foreach (var name in hideFlagsValues.Keys) foreach (var name in hideFlagsValues.Keys)
FlagsDropdown.options.Add(new Dropdown.OptionData(name)); FlagsDropdown.options.Add(new Dropdown.OptionData(name));
@ -644,7 +645,7 @@ namespace UnityExplorer.UI.Inspectors
var inputField = UIFactory.CreateInputField(rowObj, "InputField", "..."); var inputField = UIFactory.CreateInputField(rowObj, "InputField", "...");
UIFactory.SetLayoutElement(inputField.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 999); UIFactory.SetLayoutElement(inputField.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 999);
inputField.Component.onEndEdit.AddListener((string value) => { OnTransformInputEndEdit(type, value); }); inputField.Component.GetOnEndEdit().AddListener((string value) => { OnTransformInputEndEdit(type, value); });
var control = new TransformControl(type, inputField); var control = new TransformControl(type, inputField);

View File

@ -0,0 +1,215 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.Core;
using UnityExplorer.Core.Input;
using UnityExplorer.Core.Runtime;
using UnityExplorer.Inspectors.MouseInspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
namespace UnityExplorer.Inspectors
{
public enum MouseInspectMode
{
World,
UI
}
public class InspectUnderMouse : UIPanel
{
public static InspectUnderMouse Instance { get; private set; }
private readonly WorldInspector worldInspector;
private readonly UiInspector uiInspector;
public static bool Inspecting { get; set; }
public static MouseInspectMode Mode { get; set; }
private static Vector3 lastMousePos;
public MouseInspectorBase CurrentInspector
{
get
{
switch (Mode)
{
case MouseInspectMode.UI:
return uiInspector;
case MouseInspectMode.World:
return worldInspector;
}
return null;
}
}
// UIPanel
public override string Name => "Inspect Under Mouse";
public override UIManager.Panels PanelType => UIManager.Panels.MouseInspector;
public override int MinWidth => -1;
public override int MinHeight => -1;
public override bool CanDragAndResize => false;
public override bool NavButtonWanted => false;
public override bool ShouldSaveActiveState => false;
public override bool ShowByDefault => false;
internal Text objNameLabel;
internal Text objPathLabel;
internal Text mousePosLabel;
public InspectUnderMouse()
{
Instance = this;
worldInspector = new WorldInspector();
uiInspector = new UiInspector();
}
public static void OnDropdownSelect(int index)
{
switch (index)
{
case 0: return;
case 1: Instance.StartInspect(MouseInspectMode.World); break;
case 2: Instance.StartInspect(MouseInspectMode.UI); break;
}
UIManager.MouseInspectDropdown.value = 0;
}
public void StartInspect(MouseInspectMode mode)
{
Mode = mode;
Inspecting = true;
CurrentInspector.OnBeginMouseInspect();
PanelDragger.ForceEnd();
UIManager.NavBarRect.gameObject.SetActive(false);
UIManager.PanelHolder.SetActive(false);
UIRoot.SetActive(true);
}
internal void ClearHitData()
{
CurrentInspector.ClearHitData();
objNameLabel.text = "No hits...";
objPathLabel.text = "";
}
public void StopInspect()
{
CurrentInspector.OnEndInspect();
ClearHitData();
Inspecting = false;
UIManager.NavBarRect.gameObject.SetActive(true);
UIManager.PanelHolder.SetActive(true);
var drop = UIManager.MouseInspectDropdown;
if (drop.transform.Find("Dropdown List") is Transform list)
drop.DestroyDropdownList(list.gameObject);
UIRoot.SetActive(false);
}
private static float timeOfLastRaycast;
public void UpdateInspect()
{
if (InputManager.GetKeyDown(KeyCode.Escape))
{
StopInspect();
return;
}
if (InputManager.GetMouseButtonDown(0))
{
CurrentInspector.OnSelectMouseInspect();
StopInspect();
return;
}
var mousePos = InputManager.MousePosition;
if (mousePos != lastMousePos)
UpdatePosition(mousePos);
if (!timeOfLastRaycast.OccuredEarlierThan(0.1f))
return;
timeOfLastRaycast = Time.realtimeSinceStartup;
CurrentInspector.UpdateMouseInspect(mousePos);
}
internal void UpdatePosition(Vector2 mousePos)
{
lastMousePos = mousePos;
// use the raw mouse pos for the label
mousePosLabel.text = $"<color=grey>Mouse Position:</color> {mousePos.ToString()}";
// constrain the mouse pos we use within certain bounds
if (mousePos.x < 350)
mousePos.x = 350;
if (mousePos.x > Screen.width - 350)
mousePos.x = Screen.width - 350;
if (mousePos.y < Rect.rect.height)
mousePos.y += Rect.rect.height + 10;
else
mousePos.y -= 10;
// calculate and set our UI position
var inversePos = UIManager.CanvasRoot.transform.InverseTransformPoint(mousePos);
UIRoot.transform.localPosition = new Vector3(inversePos.x, inversePos.y, 0);
}
// UI Construction
protected internal override void DoSetDefaultPosAndAnchors()
{
Rect.anchorMin = Vector2.zero;
Rect.anchorMax = Vector2.zero;
Rect.pivot = new Vector2(0.5f, 1);
Rect.sizeDelta = new Vector2(700, 150);
}
public override void ConstructPanelContent()
{
// hide title bar
this.titleBar.SetActive(false);
this.UIRoot.transform.SetParent(UIManager.CanvasRoot.transform, false);
var inspectContent = UIFactory.CreateVerticalGroup(this.content, "InspectContent", true, true, true, true, 3, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(inspectContent, flexibleWidth: 9999, flexibleHeight: 9999);
// Title text
var title = UIFactory.CreateLabel(inspectContent,
"InspectLabel",
"<b>Mouse Inspector</b> (press <b>ESC</b> to cancel)",
TextAnchor.MiddleCenter);
UIFactory.SetLayoutElement(title.gameObject, flexibleWidth: 9999);
mousePosLabel = UIFactory.CreateLabel(inspectContent, "MousePosLabel", "Mouse Position:", TextAnchor.MiddleCenter);
objNameLabel = UIFactory.CreateLabel(inspectContent, "HitLabelObj", "No hits...", TextAnchor.MiddleLeft);
objNameLabel.horizontalOverflow = HorizontalWrapMode.Overflow;
objPathLabel = UIFactory.CreateLabel(inspectContent, "PathLabel", "", TextAnchor.MiddleLeft);
objPathLabel.fontStyle = FontStyle.Italic;
objPathLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
UIFactory.SetLayoutElement(objPathLabel.gameObject, minHeight: 75);
UIRoot.SetActive(false);
}
public override void DoSaveToConfigElement() { }
public override string GetSaveDataFromConfigManager() => null;
}
}

View File

@ -4,10 +4,11 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
namespace UnityExplorer.UI.Inspectors namespace UnityExplorer.Inspectors
{ {
public abstract class InspectorBase : IPooledObject public abstract class InspectorBase : IPooledObject
{ {

View File

@ -5,8 +5,8 @@ using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI; using UnityExplorer.UI;
using UnityExplorer.UI.CacheObject; using UnityExplorer.CacheObject;
using UnityExplorer.UI.Inspectors; using UnityExplorer.Inspectors;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
@ -21,19 +21,6 @@ namespace UnityExplorer
public static float PanelWidth; public static float PanelWidth;
internal static void CloseAllTabs()
{
if (Inspectors.Any())
{
for (int i = Inspectors.Count - 1; i >= 0; i--)
Inspectors[i].CloseInspector();
Inspectors.Clear();
}
UIManager.SetPanelActive(UIManager.Panels.Inspector, false);
}
public static void Inspect(object obj, CacheObjectBase sourceCache = null) public static void Inspect(object obj, CacheObjectBase sourceCache = null)
{ {
if (obj.IsNullOrDestroyed()) if (obj.IsNullOrDestroyed())
@ -50,6 +37,11 @@ namespace UnityExplorer
CreateInspector<ReflectionInspector>(obj, false, sourceCache); CreateInspector<ReflectionInspector>(obj, false, sourceCache);
} }
public static void Inspect(Type type)
{
CreateInspector<ReflectionInspector>(type, true);
}
private static bool TryFocusActiveInspector(object target) private static bool TryFocusActiveInspector(object target)
{ {
foreach (var inspector in Inspectors) foreach (var inspector in Inspectors)
@ -64,11 +56,6 @@ namespace UnityExplorer
return false; return false;
} }
public static void Inspect(Type type)
{
CreateInspector<ReflectionInspector>(type, true);
}
public static void SetInspectorActive(InspectorBase inspector) public static void SetInspectorActive(InspectorBase inspector)
{ {
UnsetActiveInspector(); UnsetActiveInspector();
@ -87,7 +74,20 @@ namespace UnityExplorer
} }
} }
private static void CreateInspector<T>(object target, bool staticReflection = false, internal static void CloseAllTabs()
{
if (Inspectors.Any())
{
for (int i = Inspectors.Count - 1; i >= 0; i--)
Inspectors[i].CloseInspector();
Inspectors.Clear();
}
UIManager.SetPanelActive(UIManager.Panels.Inspector, false);
}
private static void CreateInspector<T>(object target, bool staticReflection = false,
CacheObjectBase sourceCache = null) where T : InspectorBase CacheObjectBase sourceCache = null) where T : InspectorBase
{ {
var inspector = Pool<T>.Borrow(); var inspector = Pool<T>.Borrow();

View File

@ -4,10 +4,11 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors namespace UnityExplorer.Inspectors
{ {
public class InspectorTab : IPooledObject public class InspectorTab : IPooledObject
{ {
@ -33,7 +34,7 @@ namespace UnityExplorer.UI.Inspectors
public GameObject CreateContent(GameObject parent) public GameObject CreateContent(GameObject parent)
{ {
UIRoot = UIFactory.CreateHorizontalGroup(parent, "TabObject", false, true, true, true, 0, UIRoot = UIFactory.CreateHorizontalGroup(parent, "TabObject", false, true, true, true, 0,
default, new Color(0.13f, 0.13f, 0.13f), childAlignment: TextAnchor.MiddleLeft); default, new Color(0.13f, 0.13f, 0.13f), childAlignment: TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(UIRoot, minWidth: 200, flexibleWidth: 0); UIFactory.SetLayoutElement(UIRoot, minWidth: 200, flexibleWidth: 0);
UIRoot.AddComponent<Mask>(); UIRoot.AddComponent<Mask>();

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityExplorer.Inspectors.MouseInspectors
{
public abstract class MouseInspectorBase
{
public abstract void OnBeginMouseInspect();
public abstract void UpdateMouseInspect(Vector2 mousePos);
public abstract void OnSelectMouseInspect();
public abstract void ClearHitData();
public abstract void OnEndInspect();
}
}

Some files were not shown because too many files have changed in this diff Show More