mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-22 16:42:38 +08:00
Compare commits
123 Commits
Author | SHA1 | Date | |
---|---|---|---|
93181f02be | |||
371054d6df | |||
427f23b80a | |||
a0d5ab8792 | |||
297034e38b | |||
57f59d1295 | |||
fbdb84eefa | |||
6989ea1b19 | |||
fcdfeb2dec | |||
a1d0b6432e | |||
0b84405e57 | |||
c31e0949d3 | |||
5f0495a7ea | |||
28de6779d8 | |||
fae85c2968 | |||
fc8fa9aa7a | |||
bcf9a801a9 | |||
20298aa47b | |||
e2c1c186c3 | |||
9ce6508828 | |||
506e75c5fe | |||
a2f22051f0 | |||
4e3203a91b | |||
a411ce2dba | |||
48132b3d46 | |||
e49ed3028f | |||
31dd54d25c | |||
3cfdd6fa43 | |||
1fa1283a68 | |||
afa4135b67 | |||
48d1cf574d | |||
df0abbc847 | |||
5c9dcb1d43 | |||
602770d980 | |||
f26371f95f | |||
a673c39f4a | |||
40583cae3d | |||
20018a9ba9 | |||
dabf92a1a5 | |||
b7e275f02c | |||
f393e0d706 | |||
ab8acc9e84 | |||
66c30ee70e | |||
ddd271c00d | |||
30fe9e4dde | |||
fa3a436037 | |||
ca27d2b20f | |||
4315e0c547 | |||
3e19e74329 | |||
5a3ffebadc | |||
6e6b6239d8 | |||
03c8e5a8bd | |||
44f7209843 | |||
734e45cf9f | |||
97838e0b3a | |||
304b47f898 | |||
3d1fcbcd9f | |||
36fc17aa43 | |||
adfa29e63c | |||
3ffdcea73b | |||
dfd55260a8 | |||
29c78dc5a6 | |||
bf59d9d6cd | |||
bb0c59534a | |||
802bb722bc | |||
9ca992b0d7 | |||
5f3b3a6870 | |||
f5bce439cb | |||
e2b2c9038a | |||
fbefccd6b7 | |||
271c91f0d0 | |||
eb221bd868 | |||
5b516eb4cc | |||
601567f9d2 | |||
7ff508b874 | |||
09a7cd35cf | |||
db4a338d26 | |||
c08e02057c | |||
362fcdc51a | |||
73bd172e4d | |||
454d3bd0b4 | |||
f815a13d9a | |||
65c4d49274 | |||
d99137526e | |||
92447b55cd | |||
87d5d5a2de | |||
08cff3386b | |||
6033200579 | |||
94ec1c4908 | |||
7a400e762c | |||
67f9f744bb | |||
7a59f9a2a1 | |||
9b42eef1b9 | |||
830000b019 | |||
34910ab273 | |||
86b036095e | |||
b57e5be2e6 | |||
2d8ae45814 | |||
66dc262a68 | |||
4342901206 | |||
58b7c72a5c | |||
623dc7b7be | |||
e6f4939cc9 | |||
7a539ba78b | |||
0d10f94eb5 | |||
91671bf243 | |||
a72877befb | |||
16335c1bc4 | |||
8fab9e6268 | |||
fdd9039cca | |||
dcca980635 | |||
bfcab8248e | |||
97c20144f1 | |||
aaab10a0a0 | |||
b42a8dbe6a | |||
d7008db22e | |||
8c1913fe80 | |||
8d8c9ac7c9 | |||
f35beeaf58 | |||
d150ff3455 | |||
c4fa0d6bcd | |||
a5f56cf5a3 | |||
8c822b2ee9 |
60
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
60
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal 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
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
blank_issues_enabled: false
|
18
.github/ISSUE_TEMPLATE/enhancement.yaml
vendored
Normal file
18
.github/ISSUE_TEMPLATE/enhancement.yaml
vendored
Normal 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
13
.github/ISSUE_TEMPLATE/other.yaml
vendored
Normal 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
|
102
.github/workflows/dotnet.yml
vendored
102
.github/workflows/dotnet.yml
vendored
@ -7,21 +7,18 @@ on:
|
|||||||
# Allows you to run this workflow manually from the Actions tab
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
|
||||||
jobs:
|
jobs:
|
||||||
# This workflow contains a single job called "build"
|
|
||||||
build:
|
build:
|
||||||
# The type of runner that the job will run on
|
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
if: "!contains(github.event.head_commit.message, '-noci')"
|
if: "!contains(github.event.head_commit.message, '-noci')"
|
||||||
|
|
||||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
||||||
steps:
|
steps:
|
||||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
# Checkout latest with submodules
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
|
# Setup tools
|
||||||
- name: Setup msbuild
|
- name: Setup msbuild
|
||||||
uses: microsoft/setup-msbuild@v1
|
uses: microsoft/setup-msbuild@v1
|
||||||
|
|
||||||
@ -32,113 +29,68 @@ jobs:
|
|||||||
nuget-version: '5.x'
|
nuget-version: '5.x'
|
||||||
|
|
||||||
# Build Il2CppAssemblyUnhollower
|
# Build Il2CppAssemblyUnhollower
|
||||||
- name: Build Il2CppAssemblyUnhollower
|
- run: msbuild lib\Il2CppAssemblyUnhollower\UnhollowerBaseLib\UnhollowerBaseLib.csproj -t:Restore -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release
|
||||||
shell: cmd
|
|
||||||
run: msbuild lib\Il2CppAssemblyUnhollower\UnhollowerBaseLib\UnhollowerBaseLib.csproj -t:Restore -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release
|
|
||||||
|
|
||||||
# Restore mcs nuget
|
|
||||||
- name: Restore mcs nuget
|
|
||||||
run: nuget restore lib\mcs-unity\mcs.sln
|
|
||||||
|
|
||||||
# Build mcs
|
# Build mcs
|
||||||
- name: Build mcs
|
- run: nuget restore lib\mcs-unity\mcs.sln
|
||||||
shell: cmd
|
- run: msbuild lib\mcs-unity\mcs\mcs.csproj -t:Restore -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release
|
||||||
run: msbuild lib\mcs-unity\mcs\mcs.csproj -t:Restore -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release
|
|
||||||
|
|
||||||
# Restore NuGet packages
|
# Build UnityExplorer releases, and upload artifacts
|
||||||
- name: Restore UnityExplorer nuget
|
|
||||||
run: nuget restore src\UnityExplorer.sln
|
|
||||||
|
|
||||||
# Build UnityExplorer Releases
|
|
||||||
|
|
||||||
- name: Build UnityExplorer BepInEx Il2Cpp
|
- run: nuget restore src\UnityExplorer.sln
|
||||||
shell: cmd
|
|
||||||
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE_Cpp
|
|
||||||
|
|
||||||
- name: Build UnityExplorer BepInEx 5 Mono
|
# BepInEx Il2Cpp
|
||||||
shell: cmd
|
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE_Cpp
|
||||||
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE5_Mono
|
|
||||||
|
|
||||||
- name: Build UnityExplorer BepInEx 6 Mono
|
|
||||||
shell: cmd
|
|
||||||
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE6_Mono
|
|
||||||
|
|
||||||
- name: Build UnityExplorer MelonLoader Il2Cpp
|
|
||||||
shell: cmd
|
|
||||||
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_ML_Cpp
|
|
||||||
|
|
||||||
- name: Build UnityExplorer MelonLoader Mono
|
|
||||||
shell: cmd
|
|
||||||
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_ML_Mono
|
|
||||||
|
|
||||||
- name: Build UnityExplorer MelonLoader Legacy Il2Cpp
|
|
||||||
shell: cmd
|
|
||||||
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_MLLegacy_Cpp
|
|
||||||
|
|
||||||
- name: Build UnityExplorer MelonLoader Legacy Mono
|
|
||||||
shell: cmd
|
|
||||||
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_MLLegacy_Mono
|
|
||||||
|
|
||||||
- name: Build UnityExplorer Standalone Il2Cpp
|
|
||||||
shell: cmd
|
|
||||||
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_STANDALONE_Cpp
|
|
||||||
|
|
||||||
- name: Build UnityExplorer Standalone Mono
|
|
||||||
shell: cmd
|
|
||||||
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_STANDALONE_Mono
|
|
||||||
|
|
||||||
# Upload artifacts
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
name: Upload BepInEx Il2Cpp zip
|
|
||||||
with:
|
with:
|
||||||
name: UnityExplorer.BepInEx.Il2Cpp
|
name: UnityExplorer.BepInEx.Il2Cpp
|
||||||
path: ./Release/UnityExplorer.BepInEx.Il2Cpp/*
|
path: ./Release/UnityExplorer.BepInEx.Il2Cpp/*
|
||||||
|
|
||||||
|
# BepInEx 5 Mono
|
||||||
|
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE5_Mono
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
name: Upload BepInEx 5 Mono zip
|
|
||||||
with:
|
with:
|
||||||
name: UnityExplorer.BepInEx5.Mono
|
name: UnityExplorer.BepInEx5.Mono
|
||||||
path: ./Release/UnityExplorer.BepInEx5.Mono/*
|
path: ./Release/UnityExplorer.BepInEx5.Mono/*
|
||||||
|
|
||||||
|
# BepInEx 6 Mono
|
||||||
|
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE6_Mono
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
name: Upload BepInEx 6 Mono zip
|
|
||||||
with:
|
with:
|
||||||
name: UnityExplorer.BepInEx6.Mono
|
name: UnityExplorer.BepInEx6.Mono
|
||||||
path: ./Release/UnityExplorer.BepInEx6.Mono/*
|
path: ./Release/UnityExplorer.BepInEx6.Mono/*
|
||||||
|
|
||||||
|
# MelonLoader Il2Cpp
|
||||||
|
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_ML_Cpp
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
name: Upload MelonLoader Il2Cpp zip
|
|
||||||
with:
|
with:
|
||||||
name: UnityExplorer.MelonLoader.Il2Cpp
|
name: UnityExplorer.MelonLoader.Il2Cpp
|
||||||
path: ./Release/UnityExplorer.MelonLoader.Il2Cpp/*
|
path: ./Release/UnityExplorer.MelonLoader.Il2Cpp/*
|
||||||
|
|
||||||
|
# MelonLoader Mono
|
||||||
|
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_ML_Mono
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
name: Upload MelonLoader Mono zip
|
|
||||||
with:
|
with:
|
||||||
name: UnityExplorer.MelonLoader.Mono
|
name: UnityExplorer.MelonLoader.Mono
|
||||||
path: ./Release/UnityExplorer.MelonLoader.Mono/*
|
path: ./Release/UnityExplorer.MelonLoader.Mono/*
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
# Standalone Il2Cpp
|
||||||
name: Upload MelonLoader Legacy Il2Cpp zip
|
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_STANDALONE_Cpp
|
||||||
with:
|
|
||||||
name: UnityExplorer.MelonLoader_Legacy.Il2Cpp
|
|
||||||
path: ./Release/UnityExplorer.MelonLoader_Legacy.Il2Cpp/*
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
name: Upload MelonLoader Legacy Mono zip
|
|
||||||
with:
|
|
||||||
name: UnityExplorer.MelonLoader_Legacy.Mono
|
|
||||||
path: ./Release/UnityExplorer.MelonLoader_Legacy.Mono/*
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
name: Upload Standalone Il2Cpp zip
|
|
||||||
with:
|
with:
|
||||||
name: UnityExplorer.Standalone.Il2Cpp
|
name: UnityExplorer.Standalone.Il2Cpp
|
||||||
path: ./Release/UnityExplorer.Standalone.Il2Cpp/*
|
path: ./Release/UnityExplorer.Standalone.Il2Cpp/*
|
||||||
|
|
||||||
|
# Standalone Mono
|
||||||
|
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_STANDALONE_Mono
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
name: Upload Standalone Mono zip
|
|
||||||
with:
|
with:
|
||||||
name: UnityExplorer.Standalone.Mono
|
name: UnityExplorer.Standalone.Mono
|
||||||
path: ./Release/UnityExplorer.Standalone.Mono/*
|
path: ./Release/UnityExplorer.Standalone.Mono/*
|
||||||
|
115
README.md
115
README.md
@ -3,47 +3,43 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
An in-game explorer and a suite of debugging tools for <a href="https://docs.unity3d.com/Manual/IL2CPP.html">IL2CPP</a> and <b>Mono</b> Unity games, to aid with modding development.
|
🔍 An in-game UI for exploring, debugging and modifying Unity games.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
Supports most Unity games from versions 5.2 to 2020+.
|
✔️ Supports most Unity versions from 5.2 to 2021+ (IL2CPP and Mono).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Releases [](../../releases)
|
# Releases [](../../releases)
|
||||||
|
|
||||||
[](../../releases/latest) [](../../releases/latest)
|
[](../../releases/latest) [](https://github.com/sinai-dev/UnityExplorer/actions) [](../../releases/latest)
|
||||||
| Mod Loader | IL2CPP | Mono |
|
|
||||||
| ----------- | ------ | ---- |
|
|
||||||
| [BepInEx](https://github.com/BepInEx/BepInEx) 6.X | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx6.Mono.zip) |
|
|
||||||
| [BepInEx](https://github.com/BepInEx/BepInEx) 5.X | ✖️ n/a | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip) |
|
|
||||||
| [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3.1 | ✅ [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) |
|
|
||||||
| [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 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) |
|
|
||||||
| Standalone | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Mono.zip) |
|
|
||||||
|
|
||||||
* [Click here for Bleeding Edge releases](https://github.com/sinai-dev/UnityExplorer/actions) (click on the latest workflow and scroll down to Artifacts)
|
⚡ 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)
|
||||||
|
|
||||||
### Known issues
|
## BepInEx
|
||||||
* 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.
|
|
||||||
* 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.
|
|
||||||
|
|
||||||
## How to install
|
| Release | IL2CPP | Mono |
|
||||||
|
| ------- | ------ | ---- |
|
||||||
|
| BIE 6.X | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx6.Mono.zip) |
|
||||||
|
| BIE 5.X | ✖️ n/a | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip) |
|
||||||
|
|
||||||
### BepInEx
|
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.
|
||||||
|
|
||||||
1. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game. IL2CPP currently requires a [Bleeding Edge](https://builds.bepis.io/projects/bepinex_be) release.
|
<i>Note: BepInEx 6 is obtainable via [BepisBuilds](https://builds.bepis.io/projects/bepinex_be)</i>
|
||||||
2. Download the UnityExplorer release for BepInEx IL2CPP or Mono above.
|
|
||||||
3. Take the `UnityExplorer.BIE.___.dll` file and put it in `[GameFolder]\BepInEx\plugins\`
|
|
||||||
4. In IL2CPP, you will need to download the [Unity libs](https://github.com/LavaGang/Unity-Runtime-Libraries) for the game's Unity version and put them in the `BepInEx\unity-libs\` folder.
|
|
||||||
|
|
||||||
### MelonLoader
|
## MelonLoader
|
||||||
|
|
||||||
1. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3.1+ for your game (or use `MelonLoader_Legacy` for `0.3.0`). This version can currently be obtained from [here](https://github.com/LavaGang/MelonLoader/actions).
|
| Release | IL2CPP | Mono |
|
||||||
2. Download the UnityExplorer release for MelonLoader IL2CPP or Mono above.
|
| ------- | ------ | ---- |
|
||||||
3. Take the `UnityExplorer.ML.___.dll` file and put it in the `[GameFolder]\Mods\` folder.
|
| 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) |
|
||||||
|
|
||||||
### Standalone
|
1. Take the `UnityExplorer.ML.[version].dll` file and put it in the `Mods\` folder created by MelonLoader.
|
||||||
|
|
||||||
|
## Standalone
|
||||||
|
|
||||||
|
| IL2CPP | Mono |
|
||||||
|
| ------ | ---- |
|
||||||
|
| ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Mono.zip) |
|
||||||
|
|
||||||
The standalone release can be used with any injector or loader of your choice, but it requires you to load the dependencies manually: HarmonyX, and the IL2CPP version also requires that you set up an [Il2CppAssemblyUnhollower runtime](https://github.com/knah/Il2CppAssemblyUnhollower#required-external-setup).
|
The standalone release can be used with any injector or loader of your choice, but it requires you to load the dependencies manually: HarmonyX, and the IL2CPP version also requires that you set up an [Il2CppAssemblyUnhollower runtime](https://github.com/knah/Il2CppAssemblyUnhollower#required-external-setup).
|
||||||
|
|
||||||
@ -52,7 +48,7 @@ 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
|
||||||
|
|
||||||
## Features
|
# Features
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://raw.githubusercontent.com/sinai-dev/UnityExplorer/master/img/preview.png">
|
<a href="https://raw.githubusercontent.com/sinai-dev/UnityExplorer/master/img/preview.png">
|
||||||
@ -62,39 +58,59 @@ The standalone release can be used with any injector or loader of your choice, b
|
|||||||
|
|
||||||
### Object Explorer
|
### Object Explorer
|
||||||
|
|
||||||
* Use the <b>Scene Explorer</b> tab to traverse the active scenes, as well as the DontDestroyOnLoad scene and the HideAndDontSave "scene" (assets and hidden objects).
|
* Use the <b>Scene Explorer</b> tab to traverse the active scenes, as well as the DontDestroyOnLoad and HideAndDontSave objects.
|
||||||
|
* The "HideAndDontSave" scene contains objects with that flag, as well as Assets and Resources which are not in any scene but behave the same way.
|
||||||
|
* You can use the Scene Loader to easily load any of the scenes in the build (may not work for Unity 5.X games)
|
||||||
* Use the <b>Object Search</b> tab to search for Unity objects (including GameObjects, Components, etc), C# Singletons or Static Classes.
|
* Use the <b>Object Search</b> tab to search for Unity objects (including GameObjects, Components, etc), C# Singletons or Static Classes.
|
||||||
|
* Use the UnityObject search to look for any objects which derive from `UnityEngine.Object`, with optional filters
|
||||||
|
* The singleton search will look for any classes with a typical "Instance" field, and check it for a current value. This may cause unexpected behaviour in some IL2CPP games as we cannot distinguish between true properties and field-properties, so some property accessors will be invoked.
|
||||||
|
|
||||||
### Inspector
|
### Inspector
|
||||||
|
|
||||||
The inspector is used to see detailed information on GameObjects (GameObject Inspector), C# objects (Reflection Inspector) and C# classes (Static Inspector).
|
The inspector is used to see detailed information on objects of any type and manipulate their values, as well as to inspect C# Classes with static reflection.
|
||||||
|
|
||||||
* In the GameObject Inspector, you can edit any of the input fields in the inspector (excluding readonly fields) and press <b>Enter</b> to apply your changes. You can also do this to the GameObject path as a way to change the GameObject's parent. Press the <b>Escape</b> key to cancel your edits.
|
* The <b>GameObject Inspector</b> (tab prefix `[G]`) is used to inspect a `GameObject`, and to see and manipulate its Transform and Components.
|
||||||
* In the Reflection Inspectors, automatic updating is not enabled by default, and you must press Apply for any changes you make to take effect.
|
* You can edit any of the input fields in the inspector (excluding readonly fields) and press <b>Enter</b> to apply your changes. You can also do this to the GameObject path as a way to change the GameObject's parent. Press the <b>Escape</b> key to cancel your edits.
|
||||||
|
* <i>note: When inspecting a GameObject with a Canvas, the transform controls may be overridden by the RectTransform anchors.</i>
|
||||||
|
* The <b>Reflection Inspectors</b> (tab prefix `[R]` and `[S]`) are used for everything else
|
||||||
|
* Automatic updating is not enabled by default, and you must press Apply for any changes you make to take effect.
|
||||||
|
* Press the `▼` button to expand certain values such as strings, enums, lists, dictionaries, some structs, etc
|
||||||
|
* Use the filters at the top to quickly find the members you are looking for
|
||||||
|
* For `Texture2D` objects, there is a `View Texture` button at the top of the inspector which lets you view it and save it as a PNG file. Currently there are no other similar helpers yet, but I may add more at some point for Mesh, Sprite, Material, etc
|
||||||
|
|
||||||
### 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.
|
||||||
|
* <b>World</b>: uses Physics.Raycast to look for Colliders
|
||||||
* <b>World</b>: uses Physics.Raycast to look for Colliders
|
* <b>UI</b>: uses GraphicRaycasters to find UI objects
|
||||||
* <b>UI</b>: uses GraphicRaycasters to find UI objects
|
|
||||||
|
|
||||||
### Settings
|
### Settings
|
||||||
|
|
||||||
You can change the settings via the "Options" page of the main menu, or directly from the config file.
|
* You can change the settings via the "Options" tab of the menu, or directly from the config file.
|
||||||
|
* BepInEx: `BepInEx\config\com.sinai.unityexplorer.cfg`
|
||||||
|
* MelonLoader: `UserData\MelonPreferences.cfg`
|
||||||
|
* Standalone `{DLL_location}\UnityExplorer\config.ini`
|
||||||
|
|
||||||
Depending on the release you are using, the config file will be found at:
|
# Building
|
||||||
* BepInEx: `BepInEx\config\com.sinai.unityexplorer.cfg`
|
|
||||||
* MelonLoader: `UserData\MelonPreferences.cfg`
|
|
||||||
* Standalone `{DLL_location}\UnityExplorer\config.ini`
|
|
||||||
|
|
||||||
## 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):
|
||||||
|
|
||||||
@ -102,14 +118,7 @@ If you fork the repository on GitHub you can build using the [dotnet workflow](h
|
|||||||
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:
|
# Acknowledgments
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
* [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.
|
||||||
* [denikson](https://github.com/denikson) (aka Horse) for [mcs-unity](https://github.com/denikson/mcs-unity) \[no license\], used as the `Mono.CSharp` reference for the C# Console.
|
* [denikson](https://github.com/denikson) (aka Horse) for [mcs-unity](https://github.com/denikson/mcs-unity) \[no license\], used as the `Mono.CSharp` reference for the C# Console.
|
||||||
|
Binary file not shown.
Submodule lib/Il2CppAssemblyUnhollower updated: 0911fdaca6...0099c25069
Binary file not shown.
Binary file not shown.
@ -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>
|
||||||
{
|
{
|
||||||
'{', '}', ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&', '?'
|
'{', '}', ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&', '?'
|
||||||
@ -41,7 +41,7 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
suggestions.Clear();
|
suggestions.Clear();
|
||||||
|
|
||||||
int caret = Math.Max(0, Math.Min(InputField.Text.Length - 1, InputField.Component.caretPosition - 1));
|
int caret = Math.Max(0, Math.Min(InputField.Text.Length - 1, InputField.Component.caretPosition - 1));
|
||||||
int start = caret;
|
int startIdx = caret;
|
||||||
|
|
||||||
// If the character at the caret index is whitespace or delimiter,
|
// If the character at the caret index is whitespace or delimiter,
|
||||||
// or if the next character (if it exists) is not whitespace,
|
// or if the next character (if it exists) is not whitespace,
|
||||||
@ -55,22 +55,22 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get the current composition string (from caret back to last delimiter)
|
// get the current composition string (from caret back to last delimiter)
|
||||||
while (start > 0)
|
while (startIdx > 0)
|
||||||
{
|
{
|
||||||
start--;
|
startIdx--;
|
||||||
char c = InputField.Text[start];
|
char c = InputField.Text[startIdx];
|
||||||
if (delimiters.Contains(c))
|
if (delimiters.Contains(c) || char.IsWhiteSpace(c))
|
||||||
{
|
{
|
||||||
start++;
|
startIdx++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
string input = InputField.Text.Substring(start, caret - start + 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
|
||||||
@ -99,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));
|
||||||
}
|
}
|
||||||
@ -123,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();
|
@ -1,19 +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;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CSConsole
|
namespace UnityExplorer.CSConsole
|
||||||
{
|
{
|
||||||
public static class ConsoleController
|
public static class ConsoleController
|
||||||
{
|
{
|
||||||
@ -35,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",
|
||||||
@ -50,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);
|
||||||
@ -62,19 +67,41 @@ 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}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -150,15 +177,16 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Try to "Compile" the code (tries to interpret it as REPL)
|
// Compile the code. If it returned a CompiledMethod, it is REPL.
|
||||||
var evaluation = Evaluator.Compile(input);
|
CompiledMethod repl = Evaluator.Compile(input);
|
||||||
if (evaluation != null)
|
|
||||||
|
if (repl != null)
|
||||||
{
|
{
|
||||||
// Valid REPL, we have a delegate to the evaluation.
|
// Valid REPL, we have a delegate to the evaluation.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
object ret = null;
|
object ret = null;
|
||||||
evaluation.Invoke(ref ret);
|
repl.Invoke(ref ret);
|
||||||
var result = ret?.ToString();
|
var result = ret?.ToString();
|
||||||
if (!string.IsNullOrEmpty(result))
|
if (!string.IsNullOrEmpty(result))
|
||||||
ExplorerCore.Log($"Invoked REPL, result: {ret}");
|
ExplorerCore.Log($"Invoked REPL, result: {ret}");
|
||||||
@ -172,11 +200,9 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// The input was not recognized as an evaluation. Compile the code.
|
// The compiled code was not REPL, so it was a using directive or it defined classes.
|
||||||
|
|
||||||
Evaluator.Run(input);
|
string output = Evaluator._textWriter.ToString();
|
||||||
|
|
||||||
string output = ScriptEvaluator._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 +247,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();
|
||||||
|
|
||||||
@ -231,19 +257,26 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
previousInput = value;
|
previousInput = value;
|
||||||
|
|
||||||
if (EnableSuggestions && AutoCompleteModal.CheckEnter(Completer))
|
if (EnableSuggestions && AutoCompleteModal.CheckEnter(Completer))
|
||||||
{
|
|
||||||
OnAutocompleteEnter();
|
OnAutocompleteEnter();
|
||||||
}
|
|
||||||
else if (!settingCaretCoroutine)
|
|
||||||
{
|
|
||||||
if (EnableSuggestions)
|
|
||||||
Completer.CheckAutocompletes();
|
|
||||||
|
|
||||||
|
if (!settingCaretCoroutine)
|
||||||
|
{
|
||||||
if (EnableAutoIndent)
|
if (EnableAutoIndent)
|
||||||
DoAutoIndent();
|
DoAutoIndent();
|
||||||
}
|
}
|
||||||
|
|
||||||
HighlightVisibleInput();
|
var inStringOrComment = HighlightVisibleInput();
|
||||||
|
|
||||||
|
if (!settingCaretCoroutine)
|
||||||
|
{
|
||||||
|
if (EnableSuggestions)
|
||||||
|
{
|
||||||
|
if (inStringOrComment)
|
||||||
|
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
|
||||||
|
else
|
||||||
|
Completer.CheckAutocompletes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UpdateCaret(out _);
|
UpdateCaret(out _);
|
||||||
}
|
}
|
||||||
@ -257,16 +290,16 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
|
|
||||||
UpdateCaret(out bool caretMoved);
|
UpdateCaret(out bool caretMoved);
|
||||||
|
|
||||||
if (!settingCaretCoroutine && EnableSuggestions && AutoCompleteModal.CheckEscape(Completer))
|
if (!settingCaretCoroutine && EnableSuggestions)
|
||||||
{
|
{
|
||||||
OnAutocompleteEscaped();
|
if (AutoCompleteModal.CheckEscape(Completer))
|
||||||
return;
|
{
|
||||||
}
|
OnAutocompleteEscaped();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!settingCaretCoroutine && EnableSuggestions && caretMoved)
|
if (caretMoved)
|
||||||
{
|
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
|
||||||
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
|
|
||||||
//Completer.CheckAutocompletes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EnableCtrlRShortcut
|
if (EnableCtrlRShortcut
|
||||||
@ -310,7 +343,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)
|
||||||
@ -330,7 +363,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();
|
||||||
@ -345,7 +378,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;
|
||||||
@ -369,48 +402,91 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
settingCaretCoroutine = false;
|
settingCaretCoroutine = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#region Lexer Highlighting
|
#region Lexer Highlighting
|
||||||
|
|
||||||
private static void HighlightVisibleInput()
|
/// <summary>
|
||||||
|
/// Returns true if caret is inside string or comment, false otherwise
|
||||||
|
/// </summary>
|
||||||
|
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);
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -544,7 +620,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.
|
@ -5,14 +5,16 @@ 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 bool isStringOrComment;
|
||||||
|
public bool matchToEndOfLine;
|
||||||
public string htmlColorTag;
|
public string htmlColorTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,8 +84,10 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
/// <param name="endIdx">The last character you want to highlight</param>
|
/// <param name="endIdx">The last character you want to highlight</param>
|
||||||
/// <param name="leadingLines">The amount of leading empty lines you want before the first character in the return string.</param>
|
/// <param name="leadingLines">The amount of leading empty lines you want before the first character in the return string.</param>
|
||||||
/// <returns>A string which contains the amount of leading lines specified, as well as the rich-text highlighted section.</returns>
|
/// <returns>A string which contains the amount of leading lines specified, as well as the rich-text highlighted section.</returns>
|
||||||
public string BuildHighlightedString(string input, int startIdx, int endIdx, int leadingLines)
|
public string BuildHighlightedString(string input, int startIdx, int endIdx, int leadingLines, int caretIdx, out bool caretInStringOrComment)
|
||||||
{
|
{
|
||||||
|
caretInStringOrComment = false;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(input) || endIdx <= startIdx)
|
if (string.IsNullOrEmpty(input) || endIdx <= startIdx)
|
||||||
return input;
|
return input;
|
||||||
|
|
||||||
@ -105,14 +109,27 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
|
|
||||||
// append the highlighted match
|
// append the highlighted match
|
||||||
sb.Append(match.htmlColorTag);
|
sb.Append(match.htmlColorTag);
|
||||||
|
|
||||||
for (int i = match.startIndex; i <= match.endIndex && i <= currentEndIdx; i++)
|
for (int i = match.startIndex; i <= match.endIndex && i <= currentEndIdx; i++)
|
||||||
sb.Append(input[i]);
|
sb.Append(input[i]);
|
||||||
|
|
||||||
sb.Append(SignatureHighlighter.CLOSE_COLOR);
|
sb.Append(SignatureHighlighter.CLOSE_COLOR);
|
||||||
|
|
||||||
// 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
|
||||||
@ -150,6 +167,7 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
startIndex = startIndex,
|
startIndex = startIndex,
|
||||||
endIndex = CommittedIndex,
|
endIndex = CommittedIndex,
|
||||||
htmlColorTag = lexer.ColorTag,
|
htmlColorTag = lexer.ColorTag,
|
||||||
|
isStringOrComment = lexer is StringLexer || lexer is CommentLexer,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
@ -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;
|
@ -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",
|
||||||
@ -41,6 +41,10 @@ namespace UnityExplorer.UI.CSConsole.Lexers
|
|||||||
while (!lexer.EndOfInput && char.IsLetter(lexer.PeekNext()))
|
while (!lexer.EndOfInput && char.IsLetter(lexer.PeekNext()))
|
||||||
sb.Append(lexer.Current);
|
sb.Append(lexer.Current);
|
||||||
|
|
||||||
|
// next must be whitespace or delimiter
|
||||||
|
if (!lexer.EndOfInput && !(char.IsWhiteSpace(lexer.Current) || lexer.IsDelimiter(lexer.Current)))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (keywords.Contains(sb.ToString()))
|
if (keywords.Contains(sb.ToString()))
|
||||||
{
|
{
|
||||||
if (!lexer.EndOfInput)
|
if (!lexer.EndOfInput)
|
@ -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
|
||||||
{
|
{
|
@ -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
|
||||||
{
|
{
|
@ -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
|
||||||
{
|
{
|
@ -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
|
||||||
{
|
{
|
||||||
@ -11,7 +11,7 @@ namespace UnityExplorer.UI.CSConsole.Lexers
|
|||||||
protected override Color HighlightColor => new Color(0.6f, 0.6f, 0.6f);
|
protected override Color HighlightColor => new Color(0.6f, 0.6f, 0.6f);
|
||||||
|
|
||||||
// all symbols are delimiters
|
// all symbols are delimiters
|
||||||
public override IEnumerable<char> Delimiters => symbols;
|
public override IEnumerable<char> Delimiters => symbols.Where(it => it != '.'); // '.' is not a delimiter, only a separator.
|
||||||
|
|
||||||
public static bool IsSymbol(char c) => symbols.Contains(c);
|
public static bool IsSymbol(char c) => symbols.Contains(c);
|
||||||
|
|
@ -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)
|
@ -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
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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;
|
||||||
}
|
}
|
@ -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
|
||||||
{
|
{
|
@ -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)
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
@ -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)
|
@ -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;
|
@ -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();
|
||||||
}
|
}
|
@ -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;
|
@ -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
|
||||||
{
|
{
|
@ -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
|
||||||
{
|
{
|
@ -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()
|
||||||
{
|
{
|
@ -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>();
|
@ -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);
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +84,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
|
|||||||
|
|
||||||
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);
|
@ -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
|
||||||
{
|
{
|
@ -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()
|
@ -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
|
||||||
{
|
{
|
@ -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
|
||||||
{
|
{
|
@ -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
|
||||||
{
|
{
|
@ -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
|
||||||
{
|
{
|
@ -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
|
||||||
|
|
@ -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();
|
||||||
};
|
};
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
@ -149,18 +148,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)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using UnityEngine;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using UnityEngine;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
|
|
||||||
namespace UnityExplorer.Core.Input
|
namespace UnityExplorer.Core.Input
|
||||||
@ -74,7 +74,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.
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -14,8 +14,8 @@ 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;
|
||||||
|
|
||||||
namespace UnityExplorer
|
namespace UnityExplorer
|
||||||
{
|
{
|
||||||
@ -25,13 +25,18 @@ namespace UnityExplorer
|
|||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
|
float start = Time.realtimeSinceStartup;
|
||||||
TryLoadGameModules();
|
TryLoadGameModules();
|
||||||
|
ExplorerCore.Log($"Loaded Unhollowed modules in {Time.realtimeSinceStartup - start} seconds");
|
||||||
|
|
||||||
|
start = Time.realtimeSinceStartup;
|
||||||
BuildDeobfuscationCache();
|
BuildDeobfuscationCache();
|
||||||
OnTypeLoaded += TryCacheDeobfuscatedType;
|
OnTypeLoaded += TryCacheDeobfuscatedType;
|
||||||
|
ExplorerCore.Log($"Setup IL2CPP reflection in {Time.realtimeSinceStartup - start} seconds, " +
|
||||||
|
$"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)]
|
||||||
@ -57,29 +62,21 @@ 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>();
|
||||||
|
|
||||||
private static void BuildDeobfuscationCache()
|
private static void BuildDeobfuscationCache()
|
||||||
{
|
{
|
||||||
float start = UnityEngine.Time.realtimeSinceStartup;
|
|
||||||
|
|
||||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
{
|
{
|
||||||
foreach (var type in asm.TryGetTypes())
|
foreach (var type in asm.TryGetTypes())
|
||||||
TryCacheDeobfuscatedType(type);
|
TryCacheDeobfuscatedType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DeobfuscatedTypes.Count > 0)
|
|
||||||
{
|
|
||||||
ExplorerCore.Log($"Built deobfuscation cache in {UnityEngine.Time.realtimeSinceStartup - start} seconds, " +
|
|
||||||
$"initial count: {DeobfuscatedTypes.Count} ");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TryCacheDeobfuscatedType(Type type)
|
private static void TryCacheDeobfuscatedType(Type type)
|
||||||
@ -113,7 +110,7 @@ namespace UnityExplorer
|
|||||||
return theString;
|
return theString;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
// Get type by name
|
// Get type by name
|
||||||
@ -126,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)
|
||||||
{
|
{
|
||||||
@ -134,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);
|
||||||
|
|
||||||
@ -181,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>();
|
||||||
|
|
||||||
@ -218,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;
|
||||||
@ -280,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>();
|
||||||
@ -297,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;
|
||||||
|
|
||||||
@ -386,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";
|
||||||
@ -430,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)
|
||||||
{
|
{
|
||||||
@ -455,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.
|
||||||
|
|
||||||
@ -476,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();
|
||||||
|
|
||||||
@ -562,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",
|
||||||
@ -667,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)
|
||||||
{
|
{
|
||||||
@ -782,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -805,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;
|
||||||
|
|
||||||
@ -859,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))
|
||||||
{
|
{
|
||||||
@ -869,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -893,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);
|
||||||
|
57
src/Core/Reflection/Patches.cs
Normal file
57
src/Core/Reflection/Patches.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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>();
|
||||||
@ -65,10 +67,14 @@ namespace UnityExplorer
|
|||||||
|
|
||||||
private static void SetupTypeCache()
|
private static void SetupTypeCache()
|
||||||
{
|
{
|
||||||
|
float start = Time.realtimeSinceStartup;
|
||||||
|
|
||||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
CacheTypes(asm);
|
CacheTypes(asm);
|
||||||
|
|
||||||
AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
|
AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
|
||||||
|
|
||||||
|
ExplorerCore.Log($"Cached AppDomain assemblies in {Time.realtimeSinceStartup - start} seconds");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssemblyLoaded(object sender, AssemblyLoadEventArgs args)
|
private static void AssemblyLoaded(object sender, AssemblyLoadEventArgs args)
|
||||||
@ -83,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);
|
||||||
@ -96,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
|
||||||
@ -150,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)
|
||||||
@ -221,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
|
||||||
@ -251,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))
|
||||||
{
|
{
|
||||||
@ -287,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")
|
||||||
@ -356,7 +355,7 @@ namespace UnityExplorer
|
|||||||
return genericParameterInheritance[key];
|
return genericParameterInheritance[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
#region Internal MemberInfo Cache
|
#region Internal MemberInfo Cache
|
||||||
@ -489,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);
|
||||||
@ -501,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)
|
||||||
@ -542,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);
|
||||||
@ -554,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)
|
||||||
|
@ -32,16 +32,6 @@ namespace UnityExplorer
|
|||||||
return new AssetBundle(ptr);
|
return new AssetBundle(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static void UnloadAllAssetBundles(bool unloadAllObjects);
|
|
||||||
|
|
||||||
internal delegate void d_UnloadAllAssetBundles(bool unloadAllObjects);
|
|
||||||
|
|
||||||
public static void UnloadAllAssetBundles(bool unloadAllObjects)
|
|
||||||
{
|
|
||||||
var iCall = ICallManager.GetICall<d_UnloadAllAssetBundles>("UnityEngine.AssetBundle::UnloadAllAssetBundles");
|
|
||||||
iCall.Invoke(unloadAllObjects);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ~~~~~~~~~~~~ Instance ~~~~~~~~~~~~
|
// ~~~~~~~~~~~~ Instance ~~~~~~~~~~~~
|
||||||
|
|
||||||
private readonly IntPtr m_bundlePtr = IntPtr.Zero;
|
private readonly IntPtr m_bundlePtr = IntPtr.Zero;
|
||||||
@ -78,7 +68,8 @@ 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)
|
public void Unload(bool unloadAssets = true)
|
||||||
|
@ -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)]
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
@ -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()
|
||||||
|
@ -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,
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
@ -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}")
|
||||||
{
|
{
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
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
|
// Project-wide namespace for accessibility
|
||||||
@ -107,5 +109,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>>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.0";
|
public const string VERSION = "4.3.1";
|
||||||
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
63
src/Hooks/AddHookCell.cs
Normal 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
79
src/Hooks/HookCell.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
225
src/Hooks/HookInstance.cs
Normal file
225
src/Hooks/HookInstance.cs
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
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));
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
|
||||||
|
int paramIdx = 0;
|
||||||
|
var parameters = targetMethod.GetParameters();
|
||||||
|
foreach (var param in parameters)
|
||||||
|
{
|
||||||
|
Type pType = param.ParameterType;
|
||||||
|
if (pType.IsByRef) pType = pType.GetElementType();
|
||||||
|
codeBuilder.Append($", {pType.FullName} __{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("}");
|
||||||
|
|
||||||
|
return PatchSourceCode = codeBuilder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TogglePatch()
|
||||||
|
{
|
||||||
|
Enabled = !Enabled;
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
272
src/Hooks/HookManager.cs
Normal file
272
src/Hooks/HookManager.cs
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
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 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~ 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnBorrow methods not needed
|
||||||
|
public void OnCellBorrowed(HookCell cell) { }
|
||||||
|
public void OnCellBorrowed(AddHookCell cell) { }
|
||||||
|
|
||||||
|
|
||||||
|
// Set eligable method 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// private static readonly string VOID_HIGHLIGHT = $"<color=#{SignatureHighlighter.keywordBlueHex}>void</color> ";
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,20 +8,10 @@
|
|||||||
<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\" />
|
||||||
|
@ -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;
|
@ -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
|
||||||
{
|
{
|
@ -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.
|
@ -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);
|
||||||
|
|
215
src/Inspectors/InspectUnderMouse.cs
Normal file
215
src/Inspectors/InspectUnderMouse.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
{
|
{
|
@ -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();
|
@ -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>();
|
21
src/Inspectors/MouseInspectors/MouseInspectorBase.cs
Normal file
21
src/Inspectors/MouseInspectors/MouseInspectorBase.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
142
src/Inspectors/MouseInspectors/UiInspector.cs
Normal file
142
src/Inspectors/MouseInspectors/UiInspector.cs
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.UI;
|
||||||
|
using UnityExplorer.UI.Panels;
|
||||||
|
|
||||||
|
namespace UnityExplorer.Inspectors.MouseInspectors
|
||||||
|
{
|
||||||
|
public class UiInspector : MouseInspectorBase
|
||||||
|
{
|
||||||
|
public static readonly List<GameObject> LastHitObjects = new List<GameObject>();
|
||||||
|
|
||||||
|
private static GraphicRaycaster[] graphicRaycasters;
|
||||||
|
|
||||||
|
private static readonly List<GameObject> currentHitObjects = new List<GameObject>();
|
||||||
|
|
||||||
|
private static readonly List<Graphic> wasDisabledGraphics = new List<Graphic>();
|
||||||
|
private static readonly List<CanvasGroup> wasDisabledCanvasGroups = new List<CanvasGroup>();
|
||||||
|
private static readonly List<GameObject> objectsAddedCastersTo = new List<GameObject>();
|
||||||
|
|
||||||
|
public override void OnBeginMouseInspect()
|
||||||
|
{
|
||||||
|
SetupUIRaycast();
|
||||||
|
InspectUnderMouse.Instance.objPathLabel.text = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ClearHitData()
|
||||||
|
{
|
||||||
|
currentHitObjects.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnSelectMouseInspect()
|
||||||
|
{
|
||||||
|
LastHitObjects.Clear();
|
||||||
|
LastHitObjects.AddRange(currentHitObjects);
|
||||||
|
var panel = UIManager.GetPanel<UiInspectorResultsPanel>(UIManager.Panels.UIInspectorResults);
|
||||||
|
panel.SetActive(true);
|
||||||
|
panel.ShowResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateMouseInspect(Vector2 mousePos)
|
||||||
|
{
|
||||||
|
currentHitObjects.Clear();
|
||||||
|
|
||||||
|
var ped = new PointerEventData(null)
|
||||||
|
{
|
||||||
|
position = mousePos
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var gr in graphicRaycasters)
|
||||||
|
{
|
||||||
|
if (!gr || !gr.canvas)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var list = new List<RaycastResult>();
|
||||||
|
RuntimeProvider.Instance.GraphicRaycast(gr, ped, list);
|
||||||
|
if (list.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var hit in list)
|
||||||
|
{
|
||||||
|
if (hit.gameObject)
|
||||||
|
currentHitObjects.Add(hit.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentHitObjects.Any())
|
||||||
|
InspectUnderMouse.Instance.objNameLabel.text = $"Click to view UI Objects under mouse: {currentHitObjects.Count}";
|
||||||
|
else
|
||||||
|
InspectUnderMouse.Instance.objNameLabel.text = $"No UI objects under mouse.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetupUIRaycast()
|
||||||
|
{
|
||||||
|
foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(Canvas)))
|
||||||
|
{
|
||||||
|
var canvas = obj.TryCast<Canvas>();
|
||||||
|
if (!canvas || !canvas.enabled || !canvas.gameObject.activeInHierarchy)
|
||||||
|
continue;
|
||||||
|
if (!canvas.GetComponent<GraphicRaycaster>())
|
||||||
|
{
|
||||||
|
canvas.gameObject.AddComponent<GraphicRaycaster>();
|
||||||
|
//ExplorerCore.Log("Added raycaster to " + canvas.name);
|
||||||
|
objectsAddedCastersTo.Add(canvas.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// recache Graphic Raycasters each time we start
|
||||||
|
var casters = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GraphicRaycaster));
|
||||||
|
graphicRaycasters = new GraphicRaycaster[casters.Length];
|
||||||
|
for (int i = 0; i < casters.Length; i++)
|
||||||
|
{
|
||||||
|
graphicRaycasters[i] = casters[i].TryCast<GraphicRaycaster>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable raycastTarget on Graphics
|
||||||
|
foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(Graphic)))
|
||||||
|
{
|
||||||
|
var graphic = obj.TryCast<Graphic>();
|
||||||
|
if (!graphic || !graphic.enabled || graphic.raycastTarget || !graphic.gameObject.activeInHierarchy)
|
||||||
|
continue;
|
||||||
|
graphic.raycastTarget = true;
|
||||||
|
//ExplorerCore.Log("Enabled raycastTarget on " + graphic.name);
|
||||||
|
wasDisabledGraphics.Add(graphic);
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable blocksRaycasts on CanvasGroups
|
||||||
|
foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(CanvasGroup)))
|
||||||
|
{
|
||||||
|
var canvas = obj.TryCast<CanvasGroup>();
|
||||||
|
if (!canvas || !canvas.gameObject.activeInHierarchy || canvas.blocksRaycasts)
|
||||||
|
continue;
|
||||||
|
canvas.blocksRaycasts = true;
|
||||||
|
//ExplorerCore.Log("Enabled raycasts on " + canvas.name);
|
||||||
|
wasDisabledCanvasGroups.Add(canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnEndInspect()
|
||||||
|
{
|
||||||
|
foreach (var obj in objectsAddedCastersTo)
|
||||||
|
{
|
||||||
|
if (obj.GetComponent<GraphicRaycaster>() is GraphicRaycaster raycaster)
|
||||||
|
GameObject.Destroy(raycaster);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var graphic in wasDisabledGraphics)
|
||||||
|
graphic.raycastTarget = false;
|
||||||
|
|
||||||
|
foreach (var canvas in wasDisabledCanvasGroups)
|
||||||
|
canvas.blocksRaycasts = false;
|
||||||
|
|
||||||
|
objectsAddedCastersTo.Clear();
|
||||||
|
wasDisabledCanvasGroups.Clear();
|
||||||
|
wasDisabledGraphics.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
src/Inspectors/MouseInspectors/WorldInspector.cs
Normal file
61
src/Inspectors/MouseInspectors/WorldInspector.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UnityExplorer.Inspectors.MouseInspectors
|
||||||
|
{
|
||||||
|
public class WorldInspector : MouseInspectorBase
|
||||||
|
{
|
||||||
|
private static Camera MainCamera;
|
||||||
|
private static GameObject lastHitObject;
|
||||||
|
|
||||||
|
public override void OnBeginMouseInspect()
|
||||||
|
{
|
||||||
|
MainCamera = Camera.main;
|
||||||
|
|
||||||
|
if (!MainCamera)
|
||||||
|
{
|
||||||
|
ExplorerCore.LogWarning("No MainCamera found! Cannot inspect world!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ClearHitData()
|
||||||
|
{
|
||||||
|
lastHitObject = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnSelectMouseInspect()
|
||||||
|
{
|
||||||
|
InspectorManager.Inspect(lastHitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateMouseInspect(Vector2 mousePos)
|
||||||
|
{
|
||||||
|
var ray = MainCamera.ScreenPointToRay(mousePos);
|
||||||
|
Physics.Raycast(ray, out RaycastHit hit, 1000f);
|
||||||
|
|
||||||
|
if (hit.transform)
|
||||||
|
OnHitGameObject(hit.transform.gameObject);
|
||||||
|
else if (lastHitObject)
|
||||||
|
InspectUnderMouse.Instance.ClearHitData();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void OnHitGameObject(GameObject obj)
|
||||||
|
{
|
||||||
|
if (obj != lastHitObject)
|
||||||
|
{
|
||||||
|
lastHitObject = obj;
|
||||||
|
InspectUnderMouse.Instance.objNameLabel.text = $"<b>Click to Inspect:</b> <color=cyan>{obj.name}</color>";
|
||||||
|
InspectUnderMouse.Instance.objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnEndInspect()
|
||||||
|
{
|
||||||
|
// not needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,12 +10,13 @@ using UnityEngine;
|
|||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityExplorer.Core.Config;
|
using UnityExplorer.Core.Config;
|
||||||
using UnityExplorer.Core.Runtime;
|
using UnityExplorer.Core.Runtime;
|
||||||
using UnityExplorer.UI.CacheObject;
|
using UnityExplorer.CacheObject;
|
||||||
using UnityExplorer.UI.CacheObject.Views;
|
using UnityExplorer.CacheObject.Views;
|
||||||
using UnityExplorer.UI.Panels;
|
using UnityExplorer.UI.Panels;
|
||||||
using UnityExplorer.UI.Widgets;
|
using UnityExplorer.UI.Widgets;
|
||||||
|
using UnityExplorer.UI;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Inspectors
|
namespace UnityExplorer.Inspectors
|
||||||
{
|
{
|
||||||
public class ReflectionInspector : InspectorBase, ICellPoolDataSource<CacheMemberCell>, ICacheObjectController
|
public class ReflectionInspector : InspectorBase, ICellPoolDataSource<CacheMemberCell>, ICacheObjectController
|
||||||
{
|
{
|
||||||
@ -46,6 +47,7 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
|
|
||||||
public ScrollPool<CacheMemberCell> MemberScrollPool { get; private set; }
|
public ScrollPool<CacheMemberCell> MemberScrollPool { get; private set; }
|
||||||
|
|
||||||
|
public InputFieldRef HiddenNameText;
|
||||||
public Text NameText;
|
public Text NameText;
|
||||||
public Text AssemblyText;
|
public Text AssemblyText;
|
||||||
private Toggle autoUpdateToggle;
|
private Toggle autoUpdateToggle;
|
||||||
@ -54,6 +56,7 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
|
|
||||||
private readonly Color disabledButtonColor = new Color(0.24f, 0.24f, 0.24f);
|
private readonly Color disabledButtonColor = new Color(0.24f, 0.24f, 0.24f);
|
||||||
private readonly Color enabledButtonColor = new Color(0.2f, 0.27f, 0.2f);
|
private readonly Color enabledButtonColor = new Color(0.2f, 0.27f, 0.2f);
|
||||||
|
|
||||||
private readonly Dictionary<BindingFlags, ButtonRef> scopeFilterButtons = new Dictionary<BindingFlags, ButtonRef>();
|
private readonly Dictionary<BindingFlags, ButtonRef> scopeFilterButtons = new Dictionary<BindingFlags, ButtonRef>();
|
||||||
private readonly List<Toggle> memberTypeToggles = new List<Toggle>();
|
private readonly List<Toggle> memberTypeToggles = new List<Toggle>();
|
||||||
private InputFieldRef filterInputField;
|
private InputFieldRef filterInputField;
|
||||||
@ -73,7 +76,6 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
private IEnumerator InitCoroutine()
|
private IEnumerator InitCoroutine()
|
||||||
{
|
{
|
||||||
yield return null;
|
yield return null;
|
||||||
|
|
||||||
LayoutRebuilder.ForceRebuildLayoutImmediate(InspectorPanel.Instance.ContentRect);
|
LayoutRebuilder.ForceRebuildLayoutImmediate(InspectorPanel.Instance.ContentRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,6 +126,7 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
currentBaseTabText = $"{prefix} {SignatureHighlighter.Parse(TargetType, false)}";
|
currentBaseTabText = $"{prefix} {SignatureHighlighter.Parse(TargetType, false)}";
|
||||||
Tab.TabText.text = currentBaseTabText;
|
Tab.TabText.text = currentBaseTabText;
|
||||||
NameText.text = SignatureHighlighter.Parse(TargetType, true);
|
NameText.text = SignatureHighlighter.Parse(TargetType, true);
|
||||||
|
HiddenNameText.Text = TargetType.FullName;
|
||||||
|
|
||||||
string asmText;
|
string asmText;
|
||||||
if (TargetType.Assembly is AssemblyBuilder || string.IsNullOrEmpty(TargetType.Assembly.Location))
|
if (TargetType.Assembly is AssemblyBuilder || string.IsNullOrEmpty(TargetType.Assembly.Location))
|
||||||
@ -142,7 +145,7 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
// reset filters
|
// reset filters
|
||||||
|
|
||||||
this.filterInputField.Text = "";
|
this.filterInputField.Text = "";
|
||||||
|
|
||||||
SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Instance);
|
SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Instance);
|
||||||
scopeFilterButtons[BindingFlags.Default].Component.gameObject.SetActive(!StaticOnly);
|
scopeFilterButtons[BindingFlags.Default].Component.gameObject.SetActive(!StaticOnly);
|
||||||
scopeFilterButtons[BindingFlags.Instance].Component.gameObject.SetActive(!StaticOnly);
|
scopeFilterButtons[BindingFlags.Instance].Component.gameObject.SetActive(!StaticOnly);
|
||||||
@ -307,11 +310,8 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
|
|
||||||
private void CalculateLayouts()
|
private void CalculateLayouts()
|
||||||
{
|
{
|
||||||
// Calculate sizes
|
LeftGroupWidth = (int)Math.Max(200, (0.4f * InspectorManager.PanelWidth) - 5);
|
||||||
LeftGroupWidth = (int)Math.Max(200, (0.4f * InspectorManager.PanelWidth) - 5);// Math.Min(450f, 0.4f * InspectorManager.PanelWidth - 5));
|
|
||||||
RightGroupWidth = (int)Math.Max(200, InspectorManager.PanelWidth - LeftGroupWidth - 65);
|
RightGroupWidth = (int)Math.Max(200, InspectorManager.PanelWidth - LeftGroupWidth - 65);
|
||||||
|
|
||||||
//memberTitleLayout.minWidth = LeftGroupWidth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetCellLayout(CacheObjectCell cell)
|
private void SetCellLayout(CacheObjectCell cell)
|
||||||
@ -334,15 +334,34 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
|
|
||||||
// Class name, assembly
|
// Class name, assembly
|
||||||
|
|
||||||
NameText = UIFactory.CreateLabel(UIRoot, "Title", "not set", TextAnchor.MiddleLeft, fontSize: 17);
|
var titleHolder = UIFactory.CreateUIObject("TitleHolder", UIRoot);
|
||||||
UIFactory.SetLayoutElement(NameText.gameObject, minHeight: 25, flexibleHeight: 0);
|
UIFactory.SetLayoutElement(titleHolder, minHeight: 35, flexibleHeight: 0, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
NameText = UIFactory.CreateLabel(titleHolder, "VisibleTitle", "NotSet", TextAnchor.MiddleLeft);
|
||||||
|
var namerect = NameText.GetComponent<RectTransform>();
|
||||||
|
namerect.anchorMin = new Vector2(0, 0);
|
||||||
|
namerect.anchorMax = new Vector2(1, 1);
|
||||||
|
NameText.fontSize = 17;
|
||||||
|
UIFactory.SetLayoutElement(NameText.gameObject, minHeight: 35, flexibleHeight: 0, minWidth: 300, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
HiddenNameText = UIFactory.CreateInputField(titleHolder, "Title", "not set");
|
||||||
|
var hiddenrect = HiddenNameText.Component.gameObject.GetComponent<RectTransform>();
|
||||||
|
hiddenrect.anchorMin = new Vector2(0, 0);
|
||||||
|
hiddenrect.anchorMax = new Vector2(1, 1);
|
||||||
|
HiddenNameText.Component.readOnly = true;
|
||||||
|
HiddenNameText.Component.lineType = InputField.LineType.MultiLineNewline;
|
||||||
|
HiddenNameText.Component.gameObject.GetComponent<Image>().color = Color.clear;
|
||||||
|
HiddenNameText.Component.textComponent.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||||
|
HiddenNameText.Component.textComponent.fontSize = 17;
|
||||||
|
HiddenNameText.Component.textComponent.color = Color.clear;
|
||||||
|
UIFactory.SetLayoutElement(HiddenNameText.Component.gameObject, minHeight: 35, flexibleHeight: 0, flexibleWidth: 9999);
|
||||||
|
|
||||||
AssemblyText = UIFactory.CreateLabel(UIRoot, "AssemblyLabel", "not set", TextAnchor.MiddleLeft);
|
AssemblyText = UIFactory.CreateLabel(UIRoot, "AssemblyLabel", "not set", TextAnchor.MiddleLeft);
|
||||||
UIFactory.SetLayoutElement(AssemblyText.gameObject, minHeight: 25, flexibleWidth: 9999);
|
UIFactory.SetLayoutElement(AssemblyText.gameObject, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
|
||||||
ConstructUnityObjectRow();
|
ConstructUnityObjectRow();
|
||||||
|
|
||||||
mainContentHolder = UIFactory.CreateVerticalGroup(UIRoot, "MemberHolder", false, false, true, true, 5, new Vector4(2,2,2,2),
|
mainContentHolder = UIFactory.CreateVerticalGroup(UIRoot, "MemberHolder", false, false, true, true, 5, new Vector4(2, 2, 2, 2),
|
||||||
new Color(0.12f, 0.12f, 0.12f));
|
new Color(0.12f, 0.12f, 0.12f));
|
||||||
UIFactory.SetLayoutElement(mainContentHolder, flexibleWidth: 9999, flexibleHeight: 9999);
|
UIFactory.SetLayoutElement(mainContentHolder, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
|
|
||||||
@ -352,7 +371,7 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
|
|
||||||
// Member scroll pool
|
// Member scroll pool
|
||||||
|
|
||||||
var memberBorder = UIFactory.CreateVerticalGroup(mainContentHolder, "ScrollPoolHolder", false, false, true, true, padding: new Vector4(2,2,2,2),
|
var memberBorder = UIFactory.CreateVerticalGroup(mainContentHolder, "ScrollPoolHolder", false, false, true, true, padding: new Vector4(2, 2, 2, 2),
|
||||||
bgColor: new Color(0.05f, 0.05f, 0.05f));
|
bgColor: new Color(0.05f, 0.05f, 0.05f));
|
||||||
UIFactory.SetLayoutElement(memberBorder, flexibleWidth: 9999, flexibleHeight: 9999);
|
UIFactory.SetLayoutElement(memberBorder, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
|
|
||||||
@ -546,7 +565,7 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
textureButton.ButtonText.text = "Hide Texture";
|
textureButton.ButtonText.text = "Hide Texture";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UI construction
|
// UI construction
|
||||||
|
|
||||||
private void ConstructUnityObjectRow()
|
private void ConstructUnityObjectRow()
|
||||||
@ -617,12 +636,15 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
|
|
||||||
// Actual texture viewer
|
// Actual texture viewer
|
||||||
|
|
||||||
var imageObj = UIFactory.CreateUIObject("TextureViewerImage", textureViewer);
|
var imageViewport = UIFactory.CreateVerticalGroup(textureViewer, "Viewport", false, false, true, true);
|
||||||
textureImage = imageObj.AddComponent<Image>();
|
imageViewport.GetComponent<Image>().color = Color.white;
|
||||||
textureImageLayout = textureImage.gameObject.AddComponent<LayoutElement>();
|
imageViewport.AddComponent<Mask>().showMaskGraphic = false;
|
||||||
|
|
||||||
|
var imageObj = UIFactory.CreateUIObject("Image", imageViewport);
|
||||||
var fitter = imageObj.AddComponent<ContentSizeFitter>();
|
var fitter = imageObj.AddComponent<ContentSizeFitter>();
|
||||||
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||||
|
textureImage = imageObj.AddComponent<Image>();
|
||||||
|
textureImageLayout = UIFactory.SetLayoutElement(imageObj, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
|
|
||||||
textureViewer.SetActive(false);
|
textureViewer.SetActive(false);
|
||||||
}
|
}
|
@ -1,119 +0,0 @@
|
|||||||
#if BIE
|
|
||||||
using BepInEx;
|
|
||||||
using BepInEx.Logging;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
using UnityExplorer.Loader.BIE;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityEngine.EventSystems;
|
|
||||||
using UnityExplorer.Core.Input;
|
|
||||||
#if CPP
|
|
||||||
using BepInEx.IL2CPP;
|
|
||||||
using UnhollowerRuntimeLib;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace UnityExplorer
|
|
||||||
{
|
|
||||||
[BepInPlugin(ExplorerCore.GUID, "UnityExplorer", ExplorerCore.VERSION)]
|
|
||||||
|
|
||||||
public class ExplorerBepInPlugin :
|
|
||||||
#if MONO
|
|
||||||
BaseUnityPlugin
|
|
||||||
#else
|
|
||||||
BasePlugin
|
|
||||||
#endif
|
|
||||||
, IExplorerLoader
|
|
||||||
{
|
|
||||||
public static ExplorerBepInPlugin Instance;
|
|
||||||
|
|
||||||
public ManualLogSource LogSource
|
|
||||||
#if MONO
|
|
||||||
=> Logger;
|
|
||||||
#else
|
|
||||||
=> Log;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public ConfigHandler ConfigHandler => _configHandler;
|
|
||||||
private BepInExConfigHandler _configHandler;
|
|
||||||
|
|
||||||
public Harmony HarmonyInstance => s_harmony;
|
|
||||||
private static readonly Harmony s_harmony = new Harmony(ExplorerCore.GUID);
|
|
||||||
|
|
||||||
public string ExplorerFolder => Path.Combine(Paths.PluginPath, ExplorerCore.NAME);
|
|
||||||
public string ConfigFolder => Path.Combine(Paths.ConfigPath, ExplorerCore.NAME);
|
|
||||||
|
|
||||||
public Action<object> OnLogMessage => LogSource.LogMessage;
|
|
||||||
public Action<object> OnLogWarning => LogSource.LogWarning;
|
|
||||||
public Action<object> OnLogError => LogSource.LogError;
|
|
||||||
|
|
||||||
// Init common to Mono and Il2Cpp
|
|
||||||
internal void UniversalInit()
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
_configHandler = new BepInExConfigHandler();
|
|
||||||
ExplorerCore.Init(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MONO // Mono
|
|
||||||
internal void Awake()
|
|
||||||
{
|
|
||||||
UniversalInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
#else // Il2Cpp
|
|
||||||
public override void Load()
|
|
||||||
{
|
|
||||||
UniversalInit();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public void SetupCursorPatches()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.HarmonyInstance.PatchAll();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(EventSystem), "current", MethodType.Setter)]
|
|
||||||
public class PATCH_EventSystem_current
|
|
||||||
{
|
|
||||||
[HarmonyPrefix]
|
|
||||||
public static void Prefix_EventSystem_set_current(ref EventSystem value)
|
|
||||||
{
|
|
||||||
CursorUnlocker.Prefix_EventSystem_set_current(ref value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), "lockState", MethodType.Setter)]
|
|
||||||
public class PATCH_Cursor_lockState
|
|
||||||
{
|
|
||||||
[HarmonyPrefix]
|
|
||||||
public static void Prefix_set_lockState(ref CursorLockMode value)
|
|
||||||
{
|
|
||||||
CursorUnlocker.Prefix_set_lockState(ref value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), "visible", MethodType.Setter)]
|
|
||||||
public class PATCH_Cursor_visible
|
|
||||||
{
|
|
||||||
[HarmonyPrefix]
|
|
||||||
public static void Prefix_set_visible(ref bool value)
|
|
||||||
{
|
|
||||||
CursorUnlocker.Prefix_set_visible(ref value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -21,13 +21,9 @@ namespace UnityExplorer.Loader.BIE
|
|||||||
|
|
||||||
public override void RegisterConfigElement<T>(ConfigElement<T> config)
|
public override void RegisterConfigElement<T>(ConfigElement<T> config)
|
||||||
{
|
{
|
||||||
object[] tags = null;
|
var entry = Config.Bind(CTG_NAME, config.Name, config.Value, config.Description);
|
||||||
if (config.IsInternal)
|
|
||||||
tags = new[] { "Advanced" };
|
|
||||||
|
|
||||||
var entry = Config.Bind(CTG_NAME, config.Name, config.Value, new ConfigDescription(config.Description, null, tags));
|
entry.SettingChanged += (object o, EventArgs e) =>
|
||||||
|
|
||||||
entry.SettingChanged += (object o, EventArgs e) =>
|
|
||||||
{
|
{
|
||||||
config.Value = entry.Value;
|
config.Value = entry.Value;
|
||||||
};
|
};
|
77
src/Loader/BepInEx/ExplorerBepInPlugin.cs
Normal file
77
src/Loader/BepInEx/ExplorerBepInPlugin.cs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#if BIE
|
||||||
|
using BepInEx;
|
||||||
|
using BepInEx.Logging;
|
||||||
|
using HarmonyLib;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using UnityExplorer.Core;
|
||||||
|
using UnityExplorer.Core.Config;
|
||||||
|
using UnityExplorer.Core.Input;
|
||||||
|
using UnityExplorer.Loader.BIE;
|
||||||
|
#if CPP
|
||||||
|
using BepInEx.IL2CPP;
|
||||||
|
using UnhollowerRuntimeLib;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace UnityExplorer
|
||||||
|
{
|
||||||
|
[BepInPlugin(ExplorerCore.GUID, "UnityExplorer", ExplorerCore.VERSION)]
|
||||||
|
|
||||||
|
public class ExplorerBepInPlugin :
|
||||||
|
#if MONO
|
||||||
|
BaseUnityPlugin
|
||||||
|
#else
|
||||||
|
BasePlugin
|
||||||
|
#endif
|
||||||
|
, IExplorerLoader
|
||||||
|
{
|
||||||
|
public static ExplorerBepInPlugin Instance;
|
||||||
|
|
||||||
|
public ManualLogSource LogSource
|
||||||
|
#if MONO
|
||||||
|
=> Logger;
|
||||||
|
#else
|
||||||
|
=> Log;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public string UnhollowedModulesFolder => Path.Combine(Paths.BepInExRootPath, "unhollowed");
|
||||||
|
|
||||||
|
public ConfigHandler ConfigHandler => _configHandler;
|
||||||
|
private BepInExConfigHandler _configHandler;
|
||||||
|
|
||||||
|
public Harmony HarmonyInstance => s_harmony;
|
||||||
|
private static readonly Harmony s_harmony = new Harmony(ExplorerCore.GUID);
|
||||||
|
|
||||||
|
public string ExplorerFolder => Path.Combine(Paths.PluginPath, ExplorerCore.NAME);
|
||||||
|
|
||||||
|
public Action<object> OnLogMessage => LogSource.LogMessage;
|
||||||
|
public Action<object> OnLogWarning => LogSource.LogWarning;
|
||||||
|
public Action<object> OnLogError => LogSource.LogError;
|
||||||
|
|
||||||
|
private void Init()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
_configHandler = new BepInExConfigHandler();
|
||||||
|
ExplorerCore.Init(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if MONO // Mono
|
||||||
|
internal void Awake()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // Il2Cpp
|
||||||
|
public override void Load()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -9,14 +9,12 @@ namespace UnityExplorer
|
|||||||
public interface IExplorerLoader
|
public interface IExplorerLoader
|
||||||
{
|
{
|
||||||
string ExplorerFolder { get; }
|
string ExplorerFolder { get; }
|
||||||
|
string UnhollowedModulesFolder { get; }
|
||||||
|
|
||||||
string ConfigFolder { get; }
|
|
||||||
ConfigHandler ConfigHandler { get; }
|
ConfigHandler ConfigHandler { get; }
|
||||||
|
|
||||||
Action<object> OnLogMessage { get; }
|
Action<object> OnLogMessage { get; }
|
||||||
Action<object> OnLogWarning { get; }
|
Action<object> OnLogWarning { get; }
|
||||||
Action<object> OnLogError { get; }
|
Action<object> OnLogError { get; }
|
||||||
|
|
||||||
void SetupCursorPatches();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
#if ML
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using MelonLoader;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.EventSystems;
|
|
||||||
using UnityExplorer;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
using UnityExplorer.Core.Input;
|
|
||||||
using UnityExplorer.Loader.ML;
|
|
||||||
#if ML_LEGACY
|
|
||||||
using Harmony;
|
|
||||||
#else
|
|
||||||
using HarmonyLib;
|
|
||||||
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.UNIVERSAL)]
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[assembly: MelonInfo(typeof(ExplorerMelonMod), ExplorerCore.NAME, ExplorerCore.VERSION, ExplorerCore.AUTHOR)]
|
|
||||||
[assembly: MelonGame(null, null)]
|
|
||||||
[assembly: MelonColor(ConsoleColor.DarkCyan)]
|
|
||||||
|
|
||||||
namespace UnityExplorer
|
|
||||||
{
|
|
||||||
public class ExplorerMelonMod : MelonMod, IExplorerLoader
|
|
||||||
{
|
|
||||||
public static ExplorerMelonMod Instance;
|
|
||||||
|
|
||||||
public string ExplorerFolder => Path.Combine("Mods", ExplorerCore.NAME);
|
|
||||||
public string ConfigFolder => ExplorerFolder;
|
|
||||||
|
|
||||||
public ConfigHandler ConfigHandler => _configHandler;
|
|
||||||
public MelonLoaderConfigHandler _configHandler;
|
|
||||||
|
|
||||||
public Action<object> OnLogMessage => MelonLogger.Msg;
|
|
||||||
public Action<object> OnLogWarning => MelonLogger.Warning;
|
|
||||||
public Action<object> OnLogError => MelonLogger.Error;
|
|
||||||
|
|
||||||
public override void OnApplicationStart()
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
_configHandler = new MelonLoaderConfigHandler();
|
|
||||||
|
|
||||||
ExplorerCore.Init(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetupCursorPatches()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
PrefixProperty(typeof(Cursor),
|
|
||||||
"lockState",
|
|
||||||
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_lockState))));
|
|
||||||
|
|
||||||
PrefixProperty(typeof(Cursor),
|
|
||||||
"visible",
|
|
||||||
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_visible))));
|
|
||||||
|
|
||||||
PrefixProperty(typeof(EventSystem),
|
|
||||||
"current",
|
|
||||||
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_EventSystem_set_current))));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PrefixProperty(Type type, string property, HarmonyMethod prefix)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var prop = type.GetProperty(property);
|
|
||||||
#if ML_LEGACY
|
|
||||||
this.Harmony.Patch(prop.GetSetMethod(), prefix: prefix);
|
|
||||||
#else
|
|
||||||
HarmonyInstance.Patch(prop.GetSetMethod(), prefix: prefix);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
ExplorerCore.Log($"Unable to patch {type.Name}.set_{property}: {e.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,206 +0,0 @@
|
|||||||
#if ML
|
|
||||||
|
|
||||||
#if !ML_LEGACY // ML 0.3.1+ config handler
|
|
||||||
|
|
||||||
using MelonLoader;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
|
|
||||||
namespace UnityExplorer.Loader.ML
|
|
||||||
{
|
|
||||||
public class MelonLoaderConfigHandler : ConfigHandler
|
|
||||||
{
|
|
||||||
internal const string CTG_NAME = "UnityExplorer";
|
|
||||||
|
|
||||||
internal MelonPreferences_Category prefCategory;
|
|
||||||
|
|
||||||
public override void Init()
|
|
||||||
{
|
|
||||||
prefCategory = MelonPreferences.CreateCategory(CTG_NAME, $"{CTG_NAME} Settings", false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void LoadConfig()
|
|
||||||
{
|
|
||||||
foreach (var entry in ConfigManager.ConfigElements)
|
|
||||||
{
|
|
||||||
var key = entry.Key;
|
|
||||||
if (prefCategory.GetEntry(key) is MelonPreferences_Entry)
|
|
||||||
{
|
|
||||||
var config = entry.Value;
|
|
||||||
config.BoxedValue = config.GetLoaderConfigValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void RegisterConfigElement<T>(ConfigElement<T> config)
|
|
||||||
{
|
|
||||||
var entry = prefCategory.CreateEntry(config.Name, config.Value, null, config.Description, config.IsInternal, false);
|
|
||||||
|
|
||||||
entry.OnValueChangedUntyped += () =>
|
|
||||||
{
|
|
||||||
if ((entry.Value == null && config.Value == null) || config.Value.Equals(entry.Value))
|
|
||||||
return;
|
|
||||||
|
|
||||||
config.Value = entry.Value;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetConfigValue<T>(ConfigElement<T> config, T value)
|
|
||||||
{
|
|
||||||
if (prefCategory.GetEntry<T>(config.Name) is MelonPreferences_Entry<T> entry)
|
|
||||||
{
|
|
||||||
entry.Value = value;
|
|
||||||
//entry.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override T GetConfigValue<T>(ConfigElement<T> config)
|
|
||||||
{
|
|
||||||
if (prefCategory.GetEntry<T>(config.Name) is MelonPreferences_Entry<T> entry)
|
|
||||||
return entry.Value;
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnAnyConfigChanged()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SaveConfig()
|
|
||||||
{
|
|
||||||
MelonPreferences.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#else // ML 0.3.0 config handler
|
|
||||||
|
|
||||||
using MelonLoader;
|
|
||||||
using MelonLoader.Tomlyn.Model;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
|
|
||||||
namespace UnityExplorer.Loader.ML
|
|
||||||
{
|
|
||||||
public class MelonLoaderConfigHandler : ConfigHandler
|
|
||||||
{
|
|
||||||
internal const string CTG_NAME = "UnityExplorer";
|
|
||||||
|
|
||||||
internal MelonPreferences_Category prefCategory;
|
|
||||||
|
|
||||||
public override void Init()
|
|
||||||
{
|
|
||||||
prefCategory = MelonPreferences.CreateCategory(CTG_NAME, $"{CTG_NAME} Settings");
|
|
||||||
|
|
||||||
try { MelonPreferences.Mapper.RegisterMapper(KeycodeReader, KeycodeWriter); } catch { }
|
|
||||||
try { MelonPreferences.Mapper.RegisterMapper(AnchorReader, AnchorWriter); } catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void LoadConfig()
|
|
||||||
{
|
|
||||||
foreach (var entry in ConfigManager.ConfigElements)
|
|
||||||
{
|
|
||||||
var key = entry.Key;
|
|
||||||
if (prefCategory.GetEntry(key) is MelonPreferences_Entry)
|
|
||||||
{
|
|
||||||
var config = entry.Value;
|
|
||||||
config.BoxedValue = config.GetLoaderConfigValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void RegisterConfigElement<T>(ConfigElement<T> config)
|
|
||||||
{
|
|
||||||
var entry = prefCategory.CreateEntry(config.Name, config.Value, null, config.IsInternal) as MelonPreferences_Entry<T>;
|
|
||||||
|
|
||||||
entry.OnValueChangedUntyped += () =>
|
|
||||||
{
|
|
||||||
if ((entry.Value == null && config.Value == null) || config.Value.Equals(entry.Value))
|
|
||||||
return;
|
|
||||||
|
|
||||||
config.Value = entry.Value;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetConfigValue<T>(ConfigElement<T> config, T value)
|
|
||||||
{
|
|
||||||
if (prefCategory.GetEntry<T>(config.Name) is MelonPreferences_Entry<T> entry)
|
|
||||||
{
|
|
||||||
entry.Value = value;
|
|
||||||
entry.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override T GetConfigValue<T>(ConfigElement<T> config)
|
|
||||||
{
|
|
||||||
if (prefCategory.GetEntry<T>(config.Name) is MelonPreferences_Entry<T> entry)
|
|
||||||
return entry.Value;
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnAnyConfigChanged()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SaveConfig()
|
|
||||||
{
|
|
||||||
MelonPreferences.Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enum config handlers
|
|
||||||
|
|
||||||
public static KeyCode KeycodeReader(TomlObject value)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
KeyCode kc = (KeyCode)Enum.Parse(typeof(KeyCode), (value as TomlString).Value);
|
|
||||||
|
|
||||||
if (kc == default)
|
|
||||||
throw new Exception();
|
|
||||||
|
|
||||||
return kc;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return KeyCode.F7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TomlObject KeycodeWriter(KeyCode value)
|
|
||||||
{
|
|
||||||
return MelonPreferences.Mapper.ToToml(value.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UI.UIManager.VerticalAnchor AnchorReader(TomlObject value)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return (UI.UIManager.VerticalAnchor)Enum.Parse(typeof(UI.UIManager.VerticalAnchor), (value as TomlString).Value);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return UI.UIManager.VerticalAnchor.Top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TomlObject AnchorWriter(UI.UIManager.VerticalAnchor anchor)
|
|
||||||
{
|
|
||||||
return MelonPreferences.Mapper.ToToml(anchor.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
42
src/Loader/MelonLoader/ExplorerMelonMod.cs
Normal file
42
src/Loader/MelonLoader/ExplorerMelonMod.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#if ML
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using MelonLoader;
|
||||||
|
using UnityExplorer;
|
||||||
|
using UnityExplorer.Core.Config;
|
||||||
|
using UnityExplorer.Loader.ML;
|
||||||
|
|
||||||
|
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.UNIVERSAL)]
|
||||||
|
[assembly: MelonInfo(typeof(ExplorerMelonMod), ExplorerCore.NAME, ExplorerCore.VERSION, ExplorerCore.AUTHOR)]
|
||||||
|
[assembly: MelonGame(null, null)]
|
||||||
|
[assembly: MelonColor(ConsoleColor.DarkCyan)]
|
||||||
|
|
||||||
|
namespace UnityExplorer
|
||||||
|
{
|
||||||
|
public class ExplorerMelonMod : MelonMod, IExplorerLoader
|
||||||
|
{
|
||||||
|
public static ExplorerMelonMod Instance;
|
||||||
|
|
||||||
|
public string ExplorerFolder => Path.Combine(MelonHandler.ModsDirectory, ExplorerCore.NAME);
|
||||||
|
|
||||||
|
public string UnhollowedModulesFolder => Path.Combine(
|
||||||
|
Path.GetDirectoryName(MelonHandler.ModsDirectory),
|
||||||
|
Path.Combine("MelonLoader", "Managed"));
|
||||||
|
|
||||||
|
public ConfigHandler ConfigHandler => _configHandler;
|
||||||
|
public MelonLoaderConfigHandler _configHandler;
|
||||||
|
|
||||||
|
public Action<object> OnLogMessage => MelonLogger.Msg;
|
||||||
|
public Action<object> OnLogWarning => MelonLogger.Warning;
|
||||||
|
public Action<object> OnLogError => MelonLogger.Error;
|
||||||
|
|
||||||
|
public override void OnApplicationStart()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
_configHandler = new MelonLoaderConfigHandler();
|
||||||
|
|
||||||
|
ExplorerCore.Init(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
94
src/Loader/MelonLoader/MelonLoaderConfigHandler.cs
Normal file
94
src/Loader/MelonLoader/MelonLoaderConfigHandler.cs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#if ML
|
||||||
|
using MelonLoader;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityExplorer.Core;
|
||||||
|
using UnityExplorer.Core.Config;
|
||||||
|
|
||||||
|
namespace UnityExplorer.Loader.ML
|
||||||
|
{
|
||||||
|
public class MelonLoaderConfigHandler : ConfigHandler
|
||||||
|
{
|
||||||
|
internal const string CTG_NAME = "UnityExplorer";
|
||||||
|
|
||||||
|
internal MelonPreferences_Category prefCategory;
|
||||||
|
|
||||||
|
public override void Init()
|
||||||
|
{
|
||||||
|
prefCategory = MelonPreferences.CreateCategory(CTG_NAME, $"{CTG_NAME} Settings", false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void LoadConfig()
|
||||||
|
{
|
||||||
|
foreach (var entry in ConfigManager.ConfigElements)
|
||||||
|
{
|
||||||
|
var key = entry.Key;
|
||||||
|
if (prefCategory.GetEntry(key) is MelonPreferences_Entry)
|
||||||
|
{
|
||||||
|
var config = entry.Value;
|
||||||
|
config.BoxedValue = config.GetLoaderConfigValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This wrapper exists to handle the arbitrary "LemonAction" delegates which ML now uses in 0.4.4+.
|
||||||
|
// Reflection is required since the delegate type changed between 0.4.3 and 0.4.4.
|
||||||
|
// A wrapper class is required to link the MelonPreferences_Entry and the delegate instance.
|
||||||
|
public class EntryDelegateWrapper<T>
|
||||||
|
{
|
||||||
|
public MelonPreferences_Entry<T> entry;
|
||||||
|
public ConfigElement<T> config;
|
||||||
|
|
||||||
|
public EntryDelegateWrapper(MelonPreferences_Entry<T> entry, ConfigElement<T> config)
|
||||||
|
{
|
||||||
|
this.entry = entry;
|
||||||
|
this.config = config;
|
||||||
|
var evt = entry.GetType().GetEvent("OnValueChangedUntyped");
|
||||||
|
evt.AddEventHandler(entry, Delegate.CreateDelegate(evt.EventHandlerType, this, GetType().GetMethod("OnChanged")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnChanged()
|
||||||
|
{
|
||||||
|
if ((entry.Value == null && config.Value == null) || config.Value.Equals(entry.Value))
|
||||||
|
return;
|
||||||
|
config.Value = entry.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RegisterConfigElement<T>(ConfigElement<T> config)
|
||||||
|
{
|
||||||
|
var entry = prefCategory.CreateEntry(config.Name, config.Value, null, config.Description, config.IsInternal, false);
|
||||||
|
new EntryDelegateWrapper<T>(entry, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetConfigValue<T>(ConfigElement<T> config, T value)
|
||||||
|
{
|
||||||
|
if (prefCategory.GetEntry<T>(config.Name) is MelonPreferences_Entry<T> entry)
|
||||||
|
{
|
||||||
|
entry.Value = value;
|
||||||
|
//entry.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override T GetConfigValue<T>(ConfigElement<T> config)
|
||||||
|
{
|
||||||
|
if (prefCategory.GetEntry<T>(config.Name) is MelonPreferences_Entry<T> entry)
|
||||||
|
return entry.Value;
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnAnyConfigChanged()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SaveConfig()
|
||||||
|
{
|
||||||
|
MelonPreferences.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -18,26 +18,43 @@ namespace UnityExplorer
|
|||||||
public class ExplorerStandalone : IExplorerLoader
|
public class ExplorerStandalone : IExplorerLoader
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Call this to initialize UnityExplorer without adding a log listener.
|
/// Call this to initialize UnityExplorer without adding a log listener or Unhollowed modules path.
|
||||||
|
/// The default Unhollowed path "UnityExplorer\Modules\" will be used.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The new (or active, if one exists) instance of ExplorerStandalone.</returns>
|
/// <returns>The new (or active, if one exists) instance of ExplorerStandalone.</returns>
|
||||||
public static ExplorerStandalone CreateInstance()
|
public static ExplorerStandalone CreateInstance() => CreateInstance(null, null);
|
||||||
=> CreateInstance(null);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Call this to initialize UnityExplorer and add a listener for UnityExplorer's log messages.
|
/// Call this to initialize UnityExplorer and add a listener for UnityExplorer's log messages, without specifying an Unhollowed modules path.
|
||||||
|
/// The default Unhollowed path "UnityExplorer\Modules\" will be used.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="logListener">Your log listener to handle UnityExplorer logs.</param>
|
/// <param name="logListener">Your log listener to handle UnityExplorer logs.</param>
|
||||||
/// <returns>The new (or active, if one exists) instance of ExplorerStandalone.</returns>
|
/// <returns>The new (or active, if one exists) instance of ExplorerStandalone.</returns>
|
||||||
public static ExplorerStandalone CreateInstance(Action<string, LogType> logListener)
|
public static ExplorerStandalone CreateInstance(Action<string, LogType> logListener) => CreateInstance(logListener, null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Call this to initialize UnityExplorer with the provided log listener and Unhollowed modules path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logListener">Your log listener to handle UnityExplorer logs.</param>
|
||||||
|
/// <param name="unhollowedModulesPath">The path of the Unhollowed modules, either relative or absolute.</param>
|
||||||
|
/// <returns>The new (or active, if one exists) instance of ExplorerStandalone.</returns>
|
||||||
|
public static ExplorerStandalone CreateInstance(Action<string, LogType> logListener, string unhollowedModulesPath)
|
||||||
{
|
{
|
||||||
if (Instance != null)
|
if (Instance != null)
|
||||||
return Instance;
|
return Instance;
|
||||||
|
|
||||||
OnLog += logListener;
|
|
||||||
|
|
||||||
var instance = new ExplorerStandalone();
|
var instance = new ExplorerStandalone();
|
||||||
instance.Init();
|
instance.Init();
|
||||||
|
instance.CheckExplorerFolder();
|
||||||
|
|
||||||
|
if (logListener != null)
|
||||||
|
OnLog += logListener;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(unhollowedModulesPath) || !Directory.Exists(unhollowedModulesPath))
|
||||||
|
instance._unhollowedPath = Path.Combine(instance.ExplorerFolder, "Modules");
|
||||||
|
else
|
||||||
|
instance._unhollowedPath = unhollowedModulesPath;
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,8 +65,8 @@ namespace UnityExplorer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static event Action<string, LogType> OnLog;
|
public static event Action<string, LogType> OnLog;
|
||||||
|
|
||||||
public Harmony HarmonyInstance => s_harmony;
|
public string UnhollowedModulesFolder => _unhollowedPath;
|
||||||
public static readonly Harmony s_harmony = new Harmony(ExplorerCore.GUID);
|
private string _unhollowedPath;
|
||||||
|
|
||||||
public ConfigHandler ConfigHandler => _configHandler;
|
public ConfigHandler ConfigHandler => _configHandler;
|
||||||
private StandaloneConfigHandler _configHandler;
|
private StandaloneConfigHandler _configHandler;
|
||||||
@ -58,26 +75,12 @@ namespace UnityExplorer
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (s_explorerFolder == null)
|
CheckExplorerFolder();
|
||||||
{
|
|
||||||
s_explorerFolder =
|
|
||||||
Path.Combine(
|
|
||||||
Path.GetDirectoryName(
|
|
||||||
Uri.UnescapeDataString(new Uri(Assembly.GetExecutingAssembly().CodeBase)
|
|
||||||
.AbsolutePath)),
|
|
||||||
"UnityExplorer");
|
|
||||||
|
|
||||||
if (!Directory.Exists(s_explorerFolder))
|
|
||||||
Directory.CreateDirectory(s_explorerFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
return s_explorerFolder;
|
return s_explorerFolder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static string s_explorerFolder;
|
private static string s_explorerFolder;
|
||||||
|
|
||||||
public string ConfigFolder => ExplorerFolder;
|
|
||||||
|
|
||||||
Action<object> IExplorerLoader.OnLogMessage => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Log); };
|
Action<object> IExplorerLoader.OnLogMessage => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Log); };
|
||||||
Action<object> IExplorerLoader.OnLogWarning => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Warning); };
|
Action<object> IExplorerLoader.OnLogWarning => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Warning); };
|
||||||
Action<object> IExplorerLoader.OnLogError => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Error); };
|
Action<object> IExplorerLoader.OnLogError => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Error); };
|
||||||
@ -90,45 +93,18 @@ namespace UnityExplorer
|
|||||||
ExplorerCore.Init(this);
|
ExplorerCore.Init(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetupCursorPatches()
|
private void CheckExplorerFolder()
|
||||||
{
|
{
|
||||||
try
|
if (s_explorerFolder == null)
|
||||||
{
|
{
|
||||||
this.HarmonyInstance.PatchAll();
|
s_explorerFolder =
|
||||||
}
|
Path.Combine(
|
||||||
catch (Exception ex)
|
Path.GetDirectoryName(
|
||||||
{
|
Uri.UnescapeDataString(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath)),
|
||||||
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
|
"UnityExplorer");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(EventSystem), "current", MethodType.Setter)]
|
if (!Directory.Exists(s_explorerFolder))
|
||||||
public class PATCH_EventSystem_current
|
Directory.CreateDirectory(s_explorerFolder);
|
||||||
{
|
|
||||||
[HarmonyPrefix]
|
|
||||||
public static void Prefix_EventSystem_set_current(ref EventSystem value)
|
|
||||||
{
|
|
||||||
CursorUnlocker.Prefix_EventSystem_set_current(ref value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), "lockState", MethodType.Setter)]
|
|
||||||
public class PATCH_Cursor_lockState
|
|
||||||
{
|
|
||||||
[HarmonyPrefix]
|
|
||||||
public static void Prefix_set_lockState(ref CursorLockMode value)
|
|
||||||
{
|
|
||||||
CursorUnlocker.Prefix_set_lockState(ref value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), "visible", MethodType.Setter)]
|
|
||||||
public class PATCH_Cursor_visible
|
|
||||||
{
|
|
||||||
[HarmonyPrefix]
|
|
||||||
public static void Prefix_set_visible(ref bool value)
|
|
||||||
{
|
|
||||||
CursorUnlocker.Prefix_set_visible(ref value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,11 +13,11 @@ namespace UnityExplorer.Loader.STANDALONE
|
|||||||
public class StandaloneConfigHandler : ConfigHandler
|
public class StandaloneConfigHandler : ConfigHandler
|
||||||
{
|
{
|
||||||
internal static IniDataParser _parser;
|
internal static IniDataParser _parser;
|
||||||
internal static string INI_PATH;
|
internal static string CONFIG_PATH;
|
||||||
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
INI_PATH = Path.Combine(ExplorerCore.Loader.ConfigFolder, "config.ini");
|
CONFIG_PATH = Path.Combine(ExplorerCore.Loader.ExplorerFolder, "config.ini");
|
||||||
_parser = new IniDataParser();
|
_parser = new IniDataParser();
|
||||||
_parser.Configuration.CommentString = "#";
|
_parser.Configuration.CommentString = "#";
|
||||||
}
|
}
|
||||||
@ -49,10 +49,10 @@ namespace UnityExplorer.Loader.STANDALONE
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!File.Exists(INI_PATH))
|
if (!File.Exists(CONFIG_PATH))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
string ini = File.ReadAllText(INI_PATH);
|
string ini = File.ReadAllText(CONFIG_PATH);
|
||||||
|
|
||||||
var data = _parser.Parse(ini);
|
var data = _parser.Parse(ini);
|
||||||
|
|
||||||
@ -97,10 +97,10 @@ namespace UnityExplorer.Loader.STANDALONE
|
|||||||
foreach (var entry in ConfigManager.ConfigElements)
|
foreach (var entry in ConfigManager.ConfigElements)
|
||||||
sec.AddKey(entry.Key, entry.Value.BoxedValue.ToString());
|
sec.AddKey(entry.Key, entry.Value.BoxedValue.ToString());
|
||||||
|
|
||||||
if (!Directory.Exists(ExplorerCore.Loader.ConfigFolder))
|
if (!Directory.Exists(ExplorerCore.Loader.ExplorerFolder))
|
||||||
Directory.CreateDirectory(ExplorerCore.Loader.ConfigFolder);
|
Directory.CreateDirectory(ExplorerCore.Loader.ExplorerFolder);
|
||||||
|
|
||||||
File.WriteAllText(INI_PATH, data.ToString());
|
File.WriteAllText(CONFIG_PATH, data.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,12 +4,13 @@ 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;
|
||||||
using UnityExplorer.UI.Widgets;
|
using UnityExplorer.UI.Widgets;
|
||||||
using UnityExplorer.UI.Widgets.AutoComplete;
|
using UnityExplorer.UI.Widgets.AutoComplete;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.ObjectExplorer
|
namespace UnityExplorer.ObjectExplorer
|
||||||
{
|
{
|
||||||
public class ObjectSearch : UIModel
|
public class ObjectSearch : UIModel
|
||||||
{
|
{
|
||||||
@ -25,7 +26,7 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
private ChildFilter m_childFilter = ChildFilter.Any;
|
private ChildFilter m_childFilter = ChildFilter.Any;
|
||||||
private string desiredTypeInput;
|
private string desiredTypeInput;
|
||||||
private string lastCheckedTypeInput;
|
private string lastCheckedTypeInput;
|
||||||
private bool lastTypeCanHaveGO;
|
private bool lastTypeCanHaveGO;
|
||||||
|
|
||||||
public ButtonListHandler<object, ButtonCell> dataHandler;
|
public ButtonListHandler<object, ButtonCell> dataHandler;
|
||||||
|
|
||||||
@ -52,16 +53,10 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
|
|
||||||
if (m_context == SearchContext.Singleton)
|
if (m_context == SearchContext.Singleton)
|
||||||
currentResults = SearchProvider.SingletonSearch(nameInputField.Text);
|
currentResults = SearchProvider.SingletonSearch(nameInputField.Text);
|
||||||
else if (m_context == SearchContext.StaticClass)
|
else if (m_context == SearchContext.Class)
|
||||||
currentResults = SearchProvider.StaticClassSearch(nameInputField.Text);
|
currentResults = SearchProvider.ClassSearch(nameInputField.Text);
|
||||||
else
|
else
|
||||||
{
|
currentResults = SearchProvider.UnityObjectSearch(nameInputField.Text, desiredTypeInput, m_context, m_childFilter, m_sceneFilter);
|
||||||
string compType = "";
|
|
||||||
if (m_context == SearchContext.UnityObject)
|
|
||||||
compType = this.desiredTypeInput;
|
|
||||||
|
|
||||||
currentResults = SearchProvider.UnityObjectSearch(nameInputField.Text, compType, m_context, m_childFilter, m_sceneFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
dataHandler.RefreshData();
|
dataHandler.RefreshData();
|
||||||
resultsScrollPool.Refresh(true);
|
resultsScrollPool.Refresh(true);
|
||||||
@ -76,7 +71,7 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
lastCheckedTypeInput = desiredTypeInput;
|
lastCheckedTypeInput = desiredTypeInput;
|
||||||
|
|
||||||
//var type = ReflectionUtility.GetTypeByName(desiredTypeInput);
|
//var type = ReflectionUtility.GetTypeByName(desiredTypeInput);
|
||||||
if (ReflectionUtility.AllTypes.TryGetValue(desiredTypeInput, out var cachedType))
|
if (ReflectionUtility.GetTypeByName(desiredTypeInput) is Type cachedType)
|
||||||
{
|
{
|
||||||
var type = cachedType;
|
var type = cachedType;
|
||||||
lastTypeCanHaveGO = typeof(Component).IsAssignableFrom(type) || type == typeof(GameObject);
|
lastTypeCanHaveGO = typeof(Component).IsAssignableFrom(type) || type == typeof(GameObject);
|
||||||
@ -129,8 +124,11 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
if (!cachedCellTexts.ContainsKey(index))
|
if (!cachedCellTexts.ContainsKey(index))
|
||||||
{
|
{
|
||||||
string text;
|
string text;
|
||||||
if (m_context == SearchContext.StaticClass)
|
if (m_context == SearchContext.Class)
|
||||||
text = SignatureHighlighter.Parse(currentResults[index] as Type, true);
|
{
|
||||||
|
var type = currentResults[index] as Type;
|
||||||
|
text = $"{SignatureHighlighter.Parse(type, true)} <color=grey><i>({type.Assembly.GetName().Name})</i></color>";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
text = ToStringUtility.ToStringWithType(currentResults[index], currentResults[index]?.GetActualType());
|
text = ToStringUtility.ToStringWithType(currentResults[index], currentResults[index]?.GetActualType());
|
||||||
|
|
||||||
@ -142,7 +140,7 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
|
|
||||||
private void OnCellClicked(int dataIndex)
|
private void OnCellClicked(int dataIndex)
|
||||||
{
|
{
|
||||||
if (m_context == SearchContext.StaticClass)
|
if (m_context == SearchContext.Class)
|
||||||
InspectorManager.Inspect(currentResults[dataIndex] as Type);
|
InspectorManager.Inspect(currentResults[dataIndex] as Type);
|
||||||
else
|
else
|
||||||
InspectorManager.Inspect(currentResults[dataIndex]);
|
InspectorManager.Inspect(currentResults[dataIndex]);
|
||||||
@ -241,7 +239,7 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
// RESULTS SCROLL POOL
|
// RESULTS SCROLL POOL
|
||||||
|
|
||||||
dataHandler = new ButtonListHandler<object, ButtonCell>(resultsScrollPool, GetEntries, SetCell, ShouldDisplayCell, OnCellClicked);
|
dataHandler = new ButtonListHandler<object, ButtonCell>(resultsScrollPool, GetEntries, SetCell, ShouldDisplayCell, OnCellClicked);
|
||||||
resultsScrollPool = UIFactory.CreateScrollPool<ButtonCell>(uiRoot, "ResultsList", out GameObject scrollObj,
|
resultsScrollPool = UIFactory.CreateScrollPool<ButtonCell>(uiRoot, "ResultsList", out GameObject scrollObj,
|
||||||
out GameObject scrollContent);
|
out GameObject scrollContent);
|
||||||
|
|
||||||
resultsScrollPool.Initialize(dataHandler);
|
resultsScrollPool.Initialize(dataHandler);
|
@ -8,11 +8,12 @@ using UnityEngine;
|
|||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityExplorer.Core;
|
using UnityExplorer.Core;
|
||||||
|
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;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.ObjectExplorer
|
namespace UnityExplorer.ObjectExplorer
|
||||||
{
|
{
|
||||||
public class SceneExplorer : UIModel
|
public class SceneExplorer : UIModel
|
||||||
{
|
{
|
||||||
@ -113,7 +114,7 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
refreshRow.SetActive(!scene.IsValid());
|
refreshRow.SetActive(!scene.IsValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SceneHandler_OnLoadedScenesChanged(ReadOnlyCollection<Scene> loadedScenes)
|
private void SceneHandler_OnLoadedScenesChanged(List<Scene> loadedScenes)
|
||||||
{
|
{
|
||||||
PopulateSceneDropdown();
|
PopulateSceneDropdown();
|
||||||
}
|
}
|
||||||
@ -125,6 +126,9 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
|
|
||||||
foreach (var scene in SceneHandler.LoadedScenes)
|
foreach (var scene in SceneHandler.LoadedScenes)
|
||||||
{
|
{
|
||||||
|
if (sceneToDropdownOption.ContainsKey(scene))
|
||||||
|
continue;
|
||||||
|
|
||||||
string name = scene.name?.Trim();
|
string name = scene.name?.Trim();
|
||||||
|
|
||||||
if (!scene.IsValid())
|
if (!scene.IsValid())
|
||||||
@ -201,7 +205,7 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
//Filter input field
|
//Filter input field
|
||||||
var inputField = UIFactory.CreateInputField(filterRow, "FilterInput", "Search...");
|
var inputField = UIFactory.CreateInputField(filterRow, "FilterInput", "Search...");
|
||||||
inputField.Component.targetGraphic.color = new Color(0.2f, 0.2f, 0.2f);
|
inputField.Component.targetGraphic.color = new Color(0.2f, 0.2f, 0.2f);
|
||||||
RuntimeProvider.Instance.SetColorBlock(inputField.Component, new Color(0.4f, 0.4f, 0.4f), new Color(0.2f, 0.2f, 0.2f),
|
RuntimeProvider.Instance.SetColorBlock(inputField.Component, new Color(0.4f, 0.4f, 0.4f), new Color(0.2f, 0.2f, 0.2f),
|
||||||
new Color(0.08f, 0.08f, 0.08f));
|
new Color(0.08f, 0.08f, 0.08f));
|
||||||
UIFactory.SetLayoutElement(inputField.UIRoot, minHeight: 25);
|
UIFactory.SetLayoutElement(inputField.UIRoot, minHeight: 25);
|
||||||
inputField.OnValueChanged += OnFilterInput;
|
inputField.OnValueChanged += OnFilterInput;
|
@ -6,14 +6,14 @@ using System.Text;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.ObjectExplorer
|
namespace UnityExplorer.ObjectExplorer
|
||||||
{
|
{
|
||||||
public static class SceneHandler
|
public static class SceneHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently inspected Scene.
|
/// The currently inspected Scene.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Scene? SelectedScene
|
public static Scene? SelectedScene
|
||||||
{
|
{
|
||||||
get => selectedScene;
|
get => selectedScene;
|
||||||
internal set
|
internal set
|
||||||
@ -29,27 +29,23 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The GameObjects in the currently inspected scene.
|
/// The GameObjects in the currently inspected scene.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ReadOnlyCollection<GameObject> CurrentRootObjects => new ReadOnlyCollection<GameObject>(rootObjects);
|
public static GameObject[] CurrentRootObjects { get; private set; } = new GameObject[0];
|
||||||
private static GameObject[] rootObjects = new GameObject[0];
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All currently loaded Scenes.
|
/// All currently loaded Scenes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ReadOnlyCollection<Scene> LoadedScenes => new ReadOnlyCollection<Scene>(allLoadedScenes);
|
public static List<Scene> LoadedScenes { get; private set; } = new List<Scene>();
|
||||||
private static readonly List<Scene> allLoadedScenes = new List<Scene>();
|
|
||||||
private static HashSet<Scene> previousLoadedScenes;
|
private static HashSet<Scene> previousLoadedScenes;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The names of all scenes in the build settings, if they could be retrieved.
|
/// The names of all scenes in the build settings, if they could be retrieved.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ReadOnlyCollection<string> AllSceneNames => new ReadOnlyCollection<string>(allScenesInBuild);
|
public static readonly List<string> AllSceneNames = new List<string>();
|
||||||
private static readonly List<string> allScenesInBuild = new List<string>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not we successfuly retrieved the names of the scenes in the build settings.
|
/// Whether or not we successfuly retrieved the names of the scenes in the build settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool WasAbleToGetScenesInBuild => gotAllScenesInBuild;
|
public static bool WasAbleToGetScenesInBuild { get; private set; }
|
||||||
private static bool gotAllScenesInBuild = true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when the currently inspected Scene changes. The argument is the new scene.
|
/// Invoked when the currently inspected Scene changes. The argument is the new scene.
|
||||||
@ -59,7 +55,7 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked whenever the list of currently loaded Scenes changes. The argument contains all loaded scenes after the change.
|
/// Invoked whenever the list of currently loaded Scenes changes. The argument contains all loaded scenes after the change.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static event Action<ReadOnlyCollection<Scene>> OnLoadedScenesChanged;
|
public static event Action<List<Scene>> OnLoadedScenesChanged;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Equivalent to <see cref="SceneManager.sceneCount"/> + 2, to include 'DontDestroyOnLoad' and the 'None' scene.
|
/// Equivalent to <see cref="SceneManager.sceneCount"/> + 2, to include 'DontDestroyOnLoad' and the 'None' scene.
|
||||||
@ -93,18 +89,18 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
Type sceneUtil = ReflectionUtility.GetTypeByName("UnityEngine.SceneManagement.SceneUtility");
|
Type sceneUtil = ReflectionUtility.GetTypeByName("UnityEngine.SceneManagement.SceneUtility");
|
||||||
if (sceneUtil == null)
|
if (sceneUtil == null)
|
||||||
throw new Exception("This version of Unity does not ship with the 'SceneUtility' class, or it was not unstripped.");
|
throw new Exception("This version of Unity does not ship with the 'SceneUtility' class, or it was not unstripped.");
|
||||||
|
|
||||||
var method = sceneUtil.GetMethod("GetScenePathByBuildIndex", ReflectionUtility.FLAGS);
|
var method = sceneUtil.GetMethod("GetScenePathByBuildIndex", ReflectionUtility.FLAGS);
|
||||||
int sceneCount = SceneManager.sceneCountInBuildSettings;
|
int sceneCount = SceneManager.sceneCountInBuildSettings;
|
||||||
for (int i = 0; i < sceneCount; i++)
|
for (int i = 0; i < sceneCount; i++)
|
||||||
{
|
{
|
||||||
var scenePath = (string)method.Invoke(null, new object[] { i });
|
var scenePath = (string)method.Invoke(null, new object[] { i });
|
||||||
allScenesInBuild.Add(scenePath);
|
AllSceneNames.Add(scenePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
gotAllScenesInBuild = false;
|
WasAbleToGetScenesInBuild = false;
|
||||||
ExplorerCore.LogWarning($"Unable to generate list of all Scenes in the build: {ex}");
|
ExplorerCore.LogWarning($"Unable to generate list of all Scenes in the build: {ex}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,7 +111,7 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
int confirmedCount = 2;
|
int confirmedCount = 2;
|
||||||
bool inspectedExists = SelectedScene == DontDestroyScene || (SelectedScene.HasValue && SelectedScene.Value == default);
|
bool inspectedExists = SelectedScene == DontDestroyScene || (SelectedScene.HasValue && SelectedScene.Value == default);
|
||||||
|
|
||||||
allLoadedScenes.Clear();
|
LoadedScenes.Clear();
|
||||||
|
|
||||||
for (int i = 0; i < SceneManager.sceneCount; i++)
|
for (int i = 0; i < SceneManager.sceneCount; i++)
|
||||||
{
|
{
|
||||||
@ -131,30 +127,26 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
if (!inspectedExists && scene == SelectedScene)
|
if (!inspectedExists && scene == SelectedScene)
|
||||||
inspectedExists = true;
|
inspectedExists = true;
|
||||||
|
|
||||||
allLoadedScenes.Add(scene);
|
LoadedScenes.Add(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool anyChange = confirmedCount != allLoadedScenes.Count;
|
bool anyChange = confirmedCount != LoadedScenes.Count;
|
||||||
|
|
||||||
allLoadedScenes.Add(DontDestroyScene);
|
LoadedScenes.Add(DontDestroyScene);
|
||||||
allLoadedScenes.Add(default);
|
LoadedScenes.Add(default);
|
||||||
previousLoadedScenes = new HashSet<Scene>(allLoadedScenes);
|
previousLoadedScenes = new HashSet<Scene>(LoadedScenes);
|
||||||
|
|
||||||
// Default to first scene if none selected or previous selection no longer exists.
|
// Default to first scene if none selected or previous selection no longer exists.
|
||||||
if (!inspectedExists)
|
if (!inspectedExists)
|
||||||
{
|
SelectedScene = LoadedScenes.First();
|
||||||
SelectedScene = allLoadedScenes.First();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify on the list changing at all
|
// Notify on the list changing at all
|
||||||
if (anyChange)
|
if (anyChange)
|
||||||
{
|
|
||||||
OnLoadedScenesChanged?.Invoke(LoadedScenes);
|
OnLoadedScenesChanged?.Invoke(LoadedScenes);
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, update the root objects list.
|
// Finally, update the root objects list.
|
||||||
if (SelectedScene != null && ((Scene)SelectedScene).IsValid())
|
if (SelectedScene != null && ((Scene)SelectedScene).IsValid())
|
||||||
rootObjects = RuntimeProvider.Instance.GetRootGameObjects((Scene)SelectedScene);
|
CurrentRootObjects = RuntimeProvider.Instance.GetRootGameObjects((Scene)SelectedScene);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GameObject));
|
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GameObject));
|
||||||
@ -165,7 +157,7 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
if (go.transform.parent == null && !go.scene.IsValid())
|
if (go.transform.parent == null && !go.scene.IsValid())
|
||||||
objects.Add(go);
|
objects.Add(go);
|
||||||
}
|
}
|
||||||
rootObjects = objects.ToArray();
|
CurrentRootObjects = objects.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,14 +8,13 @@ using UnityEngine.SceneManagement;
|
|||||||
using UnityExplorer.Core;
|
using UnityExplorer.Core;
|
||||||
using UnityExplorer.Core.Runtime;
|
using UnityExplorer.Core.Runtime;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.ObjectExplorer
|
namespace UnityExplorer.ObjectExplorer
|
||||||
{
|
{
|
||||||
public enum SearchContext
|
public enum SearchContext
|
||||||
{
|
{
|
||||||
UnityObject,
|
UnityObject,
|
||||||
// GameObject,
|
|
||||||
Singleton,
|
Singleton,
|
||||||
StaticClass
|
Class
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ChildFilter
|
public enum ChildFilter
|
||||||
@ -35,7 +34,7 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
|
|
||||||
public static class SearchProvider
|
public static class SearchProvider
|
||||||
{
|
{
|
||||||
|
|
||||||
private static bool Filter(Scene scene, SceneFilter filter)
|
private static bool Filter(Scene scene, SceneFilter filter)
|
||||||
{
|
{
|
||||||
switch (filter)
|
switch (filter)
|
||||||
@ -53,7 +52,7 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static List<object> UnityObjectSearch(string input, string customTypeInput, SearchContext context,
|
internal static List<object> UnityObjectSearch(string input, string customTypeInput, SearchContext context,
|
||||||
ChildFilter childFilter, SceneFilter sceneFilter)
|
ChildFilter childFilter, SceneFilter sceneFilter)
|
||||||
{
|
{
|
||||||
var results = new List<object>();
|
var results = new List<object>();
|
||||||
@ -134,7 +133,7 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static List<object> StaticClassSearch(string input)
|
internal static List<object> ClassSearch(string input)
|
||||||
{
|
{
|
||||||
var list = new List<object>();
|
var list = new List<object>();
|
||||||
|
|
||||||
@ -144,11 +143,10 @@ namespace UnityExplorer.UI.ObjectExplorer
|
|||||||
|
|
||||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
{
|
{
|
||||||
foreach (var type in asm.TryGetTypes().Where(it => it.IsSealed && it.IsAbstract))
|
foreach (var type in asm.TryGetTypes())
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ContainsIgnoreCase(nameFilter))
|
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ContainsIgnoreCase(nameFilter))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
list.Add(type);
|
list.Add(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user