Compare commits

..

25 Commits

Author SHA1 Message Date
d1fbbfa62d Bump version 2022-05-15 19:12:00 +10:00
56a3cef245 Add C# Console Assembly blacklist 2022-05-15 18:16:04 +10:00
61e7915a55 Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2022-05-14 01:55:13 +10:00
88e63c8d6a Fix logic after changes in UniverseLib 2022-05-14 01:55:10 +10:00
048e5980a0 Update README.md 2022-05-13 20:44:51 +10:00
3b851b6e08 Use 7zip instead of Compress-Archive 2022-05-13 17:11:25 +10:00
4c029dad90 Update TimeScaleWidget.cs 2022-05-11 22:49:28 +10:00
3d61011e59 Move Time Scale Widget into separate class
And change "pause" to "lock" behaviour. Added patch to implement locking feature.
2022-05-10 01:44:08 +10:00
5285239bc5 Update hooks list after edit save 2022-05-10 01:43:41 +10:00
57d3a3f52e Fix UI Mouse Inspect hotkey not being implemented 2022-05-10 01:02:37 +10:00
c88182c831 Bump version 2022-05-08 18:06:56 +10:00
02e0102041 Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2022-05-07 05:19:46 +10:00
6adecef785 Fix generated patch code for static void methods
And always show the patch even if it failed to apply
2022-05-07 05:19:43 +10:00
ff6c03e1f3 Update README.md 2022-05-06 15:46:49 +10:00
1aedc505b2 Bump UniverseLib 2022-05-05 23:13:51 +10:00
dbe993a7c7 Fix some casts for IL2CPP 2022-05-05 22:18:19 +10:00
5a1676fb84 Update README.md 2022-05-05 19:56:29 +10:00
a7a663aefa Prevent update overwriting input field changes 2022-05-05 19:53:02 +10:00
76c77fb082 Add button to inspect scene of a GameObject 2022-05-05 19:52:25 +10:00
a25017df69 Add wider Texture2DWidget support 2022-05-05 19:52:13 +10:00
a1fab0c4a7 Add support for opening inspected Type in dnSpy 2022-05-05 19:50:52 +10:00
4599747bfe Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2022-05-05 02:45:43 +10:00
3856e84c08 Bump version 2022-05-05 02:45:41 +10:00
104288a912 Add support for Cubemaps in Texture2DWidget 2022-05-05 02:45:24 +10:00
41e8a5ae33 Update README.md 2022-05-03 06:10:22 +10:00
23 changed files with 850 additions and 331 deletions

View File

@ -38,10 +38,10 @@ Nightly builds can be found [here](https://github.com/sinai-dev/UnityExplorer/ac
## MelonLoader ## MelonLoader
| Release | IL2CPP | Mono | | Release | IL2CPP | Mono |
| ---------- | ------ | ---- | | ------- | ------ | ---- |
| ML 0.4/0.5 | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.IL2CPP.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Mono.zip) | | ML 0.5 | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.IL2CPP.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Mono.zip) |
| ML 0.6 | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.IL2CPP.net6preview.zip) | ✖️ | | ML 0.6 | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.IL2CPP.net6preview.zip) | ✖️ |
1. Unzip the release file into a folder 1. Unzip the release file into a folder
2. Copy the DLL inside the `Mods` folder into your MelonLoader `Mods` folder 2. Copy the DLL inside the `Mods` folder into your MelonLoader `Mods` folder
@ -90,6 +90,20 @@ If these fixes do not work, please create an issue in this repo and I'll do my b
</a> </a>
</p> </p>
### Inspector API
If you want to inspect an object or Type from outside the C# console, use the `InspectorManager` class:
**To inspect an object:**
```csharp
UnityExplorer.InspectorManager.Inspect(theObject);
```
**To inspect a Type:**
```cs
UnityExplorer.InspectorManager.Inspect(typeof(SomeClass));
```
### Object Explorer ### Object Explorer
* Use the <b>Scene Explorer</b> tab to traverse the active scenes, as well as the DontDestroyOnLoad and HideAndDontSave objects. * Use the <b>Scene Explorer</b> tab to traverse the active scenes, as well as the DontDestroyOnLoad and HideAndDontSave objects.
@ -110,7 +124,8 @@ The inspector is used to see detailed information on objects of any type and man
* Automatic updating is not enabled by default, and you must press Apply for any changes you make to take effect. * 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 * 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 * 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 * For `Texture2D`, `Image`, `Sprite` and `Material` objects, there is a `View Texture` button at the top of the inspector which lets you view the Texture(s) and save them as a PNG file.
* For `AudioClip` objects there is a `Show Player` button which opens an audio player widget. For clips which are loaded as `DecompressOnLoad`, there is also a button to save them to a `.wav` file.
### C# Console ### C# Console

161
build.ps1
View File

@ -1,125 +1,134 @@
# ----------- MelonLoader IL2CPP (net6) ----------- # ----------- MelonLoader IL2CPP (net6) -----------
dotnet build src\UnityExplorer.sln -c Release_ML_Cpp_net6 dotnet build src/UnityExplorer.sln -c Release_ML_Cpp_net6
$Path = "Release\UnityExplorer.MelonLoader.IL2CPP.net6preview" $Path = "Release\UnityExplorer.MelonLoader.IL2CPP.net6preview"
# ILRepack # ILRepack
lib/ILRepack.exe /target:library /lib:lib\net6 /lib:lib\unhollowed /lib:$Path /internalize /out:$Path\UnityExplorer.ML.IL2CPP.net6preview.dll $Path\UnityExplorer.ML.IL2CPP.net6preview.dll $Path\mcs.dll lib/ILRepack.exe /target:library /lib:lib/net6 /lib:lib/unhollowed /lib:$Path /internalize /out:$Path/UnityExplorer.ML.IL2CPP.net6preview.dll $Path/UnityExplorer.ML.IL2CPP.net6preview.dll $Path/mcs.dll
# (cleanup and move files) # (cleanup and move files)
Remove-Item $Path\UnityExplorer.ML.IL2CPP.net6preview.deps.json Remove-Item $Path/UnityExplorer.ML.IL2CPP.net6preview.deps.json
Remove-Item $Path\Tomlet.dll Remove-Item $Path/Tomlet.dll
Remove-Item $Path\mcs.dll Remove-Item $Path/mcs.dll
Remove-Item $Path\Iced.dll Remove-Item $Path/Iced.dll
Remove-Item $Path\UnhollowerBaseLib.dll Remove-Item $Path/UnhollowerBaseLib.dll
New-Item -Path "$Path" -Name "Mods" -ItemType "directory" -Force New-Item -Path "$Path" -Name "Mods" -ItemType "directory" -Force
Move-Item -Path $Path\UnityExplorer.ML.IL2CPP.net6preview.dll -Destination $Path\Mods -Force Move-Item -Path $Path/UnityExplorer.ML.IL2CPP.net6preview.dll -Destination $Path/Mods -Force
New-Item -Path "$Path" -Name "UserLibs" -ItemType "directory" -Force New-Item -Path "$Path" -Name "UserLibs" -ItemType "directory" -Force
Move-Item -Path $Path\UniverseLib.IL2CPP.dll -Destination $Path\UserLibs -Force Move-Item -Path $Path/UniverseLib.IL2CPP.dll -Destination $Path/UserLibs -Force
# (create zip archive) # (create zip archive)
Compress-Archive -Path $Path\* -CompressionLevel Fastest -DestinationPath $Path\..\UnityExplorer.MelonLoader.IL2CPP.net6preview.zip -Force Remove-Item $Path/../UnityExplorer.MelonLoader.IL2CPP.net6preview.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.MelonLoader.IL2CPP.net6preview.zip .\$Path\*
# ----------- MelonLoader IL2CPP (net472) ----------- # ----------- MelonLoader IL2CPP (net472) -----------
dotnet build src\UnityExplorer.sln -c Release_ML_Cpp_net472 dotnet build src/UnityExplorer.sln -c Release_ML_Cpp_net472
$Path = "Release\UnityExplorer.MelonLoader.IL2CPP" $Path = "Release/UnityExplorer.MelonLoader.IL2CPP"
# ILRepack # ILRepack
lib/ILRepack.exe /target:library /lib:lib\net472 /lib:lib\net35 /lib:lib\unhollowed /lib:$Path /internalize /out:$Path\UnityExplorer.ML.IL2CPP.dll $Path\UnityExplorer.ML.IL2CPP.dll $Path\mcs.dll lib/ILRepack.exe /target:library /lib:lib/net472 /lib:lib/net35 /lib:lib/unhollowed /lib:$Path /internalize /out:$Path/UnityExplorer.ML.IL2CPP.dll $Path/UnityExplorer.ML.IL2CPP.dll $Path/mcs.dll
# (cleanup and move files) # (cleanup and move files)
Remove-Item $Path\Tomlet.dll Remove-Item $Path/Tomlet.dll
Remove-Item $Path\mcs.dll Remove-Item $Path/mcs.dll
Remove-Item $Path\Iced.dll Remove-Item $Path/Iced.dll
Remove-Item $Path\UnhollowerBaseLib.dll Remove-Item $Path/UnhollowerBaseLib.dll
New-Item -Path "$Path" -Name "Mods" -ItemType "directory" -Force New-Item -Path "$Path" -Name "Mods" -ItemType "directory" -Force
Move-Item -Path $Path\UnityExplorer.ML.IL2CPP.dll -Destination $Path\Mods -Force Move-Item -Path $Path/UnityExplorer.ML.IL2CPP.dll -Destination $Path/Mods -Force
New-Item -Path "$Path" -Name "UserLibs" -ItemType "directory" -Force New-Item -Path "$Path" -Name "UserLibs" -ItemType "directory" -Force
Move-Item -Path $Path\UniverseLib.IL2CPP.dll -Destination $Path\UserLibs -Force Move-Item -Path $Path/UniverseLib.IL2CPP.dll -Destination $Path/UserLibs -Force
# (create zip archive) # (create zip archive)
Compress-Archive -Path $Path\* -CompressionLevel Fastest -DestinationPath $Path\..\UnityExplorer.MelonLoader.IL2CPP.zip -Force Remove-Item $Path/../UnityExplorer.MelonLoader.IL2CPP.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.MelonLoader.IL2CPP.zip .\$Path\*
# ----------- MelonLoader Mono ----------- # ----------- MelonLoader Mono -----------
dotnet build src\UnityExplorer.sln -c Release_ML_Mono dotnet build src/UnityExplorer.sln -c Release_ML_Mono
$Path = "Release\UnityExplorer.MelonLoader.Mono" $Path = "Release/UnityExplorer.MelonLoader.Mono"
# ILRepack # ILRepack
lib/ILRepack.exe /target:library /lib:lib\net35 /lib:$Path /internalize /out:$Path\UnityExplorer.ML.Mono.dll $Path\UnityExplorer.ML.Mono.dll $Path\mcs.dll lib/ILRepack.exe /target:library /lib:lib/net35 /lib:$Path /internalize /out:$Path/UnityExplorer.ML.Mono.dll $Path/UnityExplorer.ML.Mono.dll $Path/mcs.dll
# (cleanup and move files) # (cleanup and move files)
Remove-Item $Path\Tomlet.dll Remove-Item $Path/Tomlet.dll
Remove-Item $Path\mcs.dll Remove-Item $Path/mcs.dll
New-Item -Path "$Path" -Name "Mods" -ItemType "directory" -Force New-Item -Path "$Path" -Name "Mods" -ItemType "directory" -Force
Move-Item -Path $Path\UnityExplorer.ML.Mono.dll -Destination $Path\Mods -Force Move-Item -Path $Path/UnityExplorer.ML.Mono.dll -Destination $Path/Mods -Force
New-Item -Path "$Path" -Name "UserLibs" -ItemType "directory" -Force New-Item -Path "$Path" -Name "UserLibs" -ItemType "directory" -Force
Move-Item -Path $Path\UniverseLib.Mono.dll -Destination $Path\UserLibs -Force Move-Item -Path $Path/UniverseLib.Mono.dll -Destination $Path/UserLibs -Force
# (create zip archive) # (create zip archive)
Compress-Archive -Path $Path\* -CompressionLevel Fastest -DestinationPath $Path\..\UnityExplorer.MelonLoader.Mono.zip -Force Remove-Item $Path/../UnityExplorer.MelonLoader.Mono.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.MelonLoader.Mono.zip .\$Path\*
# ----------- BepInEx IL2CPP ----------- # ----------- BepInEx IL2CPP -----------
dotnet build src\UnityExplorer.sln -c Release_BIE_Cpp dotnet build src/UnityExplorer.sln -c Release_BIE_Cpp
$Path = "Release\UnityExplorer.BepInEx.IL2CPP" $Path = "Release/UnityExplorer.BepInEx.IL2CPP"
# ILRepack # ILRepack
lib/ILRepack.exe /target:library /lib:lib\net472 /lib:lib\unhollowed /lib:$Path /internalize /out:$Path\UnityExplorer.BIE.IL2CPP.dll $Path\UnityExplorer.BIE.IL2CPP.dll $Path\mcs.dll $Path\Tomlet.dll lib/ILRepack.exe /target:library /lib:lib/net472 /lib:lib/unhollowed /lib:$Path /internalize /out:$Path/UnityExplorer.BIE.IL2CPP.dll $Path/UnityExplorer.BIE.IL2CPP.dll $Path/mcs.dll $Path/Tomlet.dll
# (cleanup and move files) # (cleanup and move files)
Remove-Item $Path\Tomlet.dll Remove-Item $Path/Tomlet.dll
Remove-Item $Path\mcs.dll Remove-Item $Path/mcs.dll
Remove-Item $Path\Iced.dll Remove-Item $Path/Iced.dll
Remove-Item $Path\UnhollowerBaseLib.dll Remove-Item $Path/UnhollowerBaseLib.dll
New-Item -Path "$Path" -Name "plugins" -ItemType "directory" -Force New-Item -Path "$Path" -Name "plugins" -ItemType "directory" -Force
New-Item -Path "$Path" -Name "plugins\sinai-dev-UnityExplorer" -ItemType "directory" -Force New-Item -Path "$Path" -Name "plugins/sinai-dev-UnityExplorer" -ItemType "directory" -Force
Move-Item -Path $Path\UnityExplorer.BIE.IL2CPP.dll -Destination $Path\plugins\sinai-dev-UnityExplorer -Force Move-Item -Path $Path/UnityExplorer.BIE.IL2CPP.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
Move-Item -Path $Path\UniverseLib.IL2CPP.dll -Destination $Path\plugins\sinai-dev-UnityExplorer -Force Move-Item -Path $Path/UniverseLib.IL2CPP.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
# (create zip archive) # (create zip archive)
Compress-Archive -Path $Path\* -CompressionLevel Fastest -DestinationPath $Path\..\UnityExplorer.BepInEx.IL2CPP.zip -Force Remove-Item $Path/../UnityExplorer.BepInEx.IL2CPP.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.BepInEx.IL2CPP.zip .\$Path\*
# ----------- BepInEx 5 Mono ----------- # ----------- BepInEx 5 Mono -----------
dotnet build src\UnityExplorer.sln -c Release_BIE5_Mono dotnet build src/UnityExplorer.sln -c Release_BIE5_Mono
$Path = "Release\UnityExplorer.BepInEx5.Mono" $Path = "Release/UnityExplorer.BepInEx5.Mono"
# ILRepack # ILRepack
lib/ILRepack.exe /target:library /lib:lib\net35 /lib:$Path /internalize /out:$Path\UnityExplorer.BIE5.Mono.dll $Path\UnityExplorer.BIE5.Mono.dll $Path\mcs.dll $Path\Tomlet.dll lib/ILRepack.exe /target:library /lib:lib/net35 /lib:$Path /internalize /out:$Path/UnityExplorer.BIE5.Mono.dll $Path/UnityExplorer.BIE5.Mono.dll $Path/mcs.dll $Path/Tomlet.dll
# (cleanup and move files) # (cleanup and move files)
Remove-Item $Path\Tomlet.dll Remove-Item $Path/Tomlet.dll
Remove-Item $Path\mcs.dll Remove-Item $Path/mcs.dll
New-Item -Path "$Path" -Name "plugins" -ItemType "directory" -Force New-Item -Path "$Path" -Name "plugins" -ItemType "directory" -Force
New-Item -Path "$Path" -Name "plugins\sinai-dev-UnityExplorer" -ItemType "directory" -Force New-Item -Path "$Path" -Name "plugins/sinai-dev-UnityExplorer" -ItemType "directory" -Force
Move-Item -Path $Path\UnityExplorer.BIE5.Mono.dll -Destination $Path\plugins\sinai-dev-UnityExplorer -Force Move-Item -Path $Path/UnityExplorer.BIE5.Mono.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
Move-Item -Path $Path\UniverseLib.Mono.dll -Destination $Path\plugins\sinai-dev-UnityExplorer -Force Move-Item -Path $Path/UniverseLib.Mono.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
# (create zip archive) # (create zip archive)
Compress-Archive -Path $Path\* -CompressionLevel Fastest -DestinationPath $Path\..\UnityExplorer.BepInEx5.Mono.zip -Force Remove-Item $Path/../UnityExplorer.BepInEx5.Mono.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.BepInEx5.Mono.zip .\$Path\*
# ----------- BepInEx 6 Mono ----------- # ----------- BepInEx 6 Mono -----------
dotnet build src\UnityExplorer.sln -c Release_BIE6_Mono dotnet build src/UnityExplorer.sln -c Release_BIE6_Mono
$Path = "Release\UnityExplorer.BepInEx6.Mono" $Path = "Release/UnityExplorer.BepInEx6.Mono"
# ILRepack # ILRepack
lib/ILRepack.exe /target:library /lib:lib\net35 /lib:$Path /internalize /out:$Path\UnityExplorer.BIE6.Mono.dll $Path\UnityExplorer.BIE6.Mono.dll $Path\mcs.dll $Path\Tomlet.dll lib/ILRepack.exe /target:library /lib:lib/net35 /lib:$Path /internalize /out:$Path/UnityExplorer.BIE6.Mono.dll $Path/UnityExplorer.BIE6.Mono.dll $Path/mcs.dll $Path/Tomlet.dll
# (cleanup and move files) # (cleanup and move files)
Remove-Item $Path\Tomlet.dll Remove-Item $Path/Tomlet.dll
Remove-Item $Path\mcs.dll Remove-Item $Path/mcs.dll
New-Item -Path "$Path" -Name "plugins" -ItemType "directory" -Force New-Item -Path "$Path" -Name "plugins" -ItemType "directory" -Force
New-Item -Path "$Path" -Name "plugins\sinai-dev-UnityExplorer" -ItemType "directory" -Force New-Item -Path "$Path" -Name "plugins/sinai-dev-UnityExplorer" -ItemType "directory" -Force
Move-Item -Path $Path\UnityExplorer.BIE6.Mono.dll -Destination $Path\plugins\sinai-dev-UnityExplorer -Force Move-Item -Path $Path/UnityExplorer.BIE6.Mono.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
Move-Item -Path $Path\UniverseLib.Mono.dll -Destination $Path\plugins\sinai-dev-UnityExplorer -Force Move-Item -Path $Path/UniverseLib.Mono.dll -Destination $Path/plugins/sinai-dev-UnityExplorer -Force
# (create zip archive) # (create zip archive)
Compress-Archive -Path $Path\* -CompressionLevel Fastest -DestinationPath $Path\..\UnityExplorer.BepInEx6.Mono.zip -Force Remove-Item $Path/../UnityExplorer.BepInEx6.Mono.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.BepInEx6.Mono.zip .\$Path\*
# ----------- Standalone Mono ----------- # ----------- Standalone Mono -----------
dotnet build src\UnityExplorer.sln -c Release_STANDALONE_Mono dotnet build src/UnityExplorer.sln -c Release_STANDALONE_Mono
$Path = "Release\UnityExplorer.Standalone.Mono" $Path = "Release/UnityExplorer.Standalone.Mono"
# ILRepack # ILRepack
lib/ILRepack.exe /target:library /lib:lib\net35 /lib:$Path /internalize /out:$Path\UnityExplorer.Standalone.Mono.dll $Path\UnityExplorer.Standalone.Mono.dll $Path\mcs.dll $Path\Tomlet.dll lib/ILRepack.exe /target:library /lib:lib/net35 /lib:$Path /internalize /out:$Path/UnityExplorer.Standalone.Mono.dll $Path/UnityExplorer.Standalone.Mono.dll $Path/mcs.dll $Path/Tomlet.dll
# (cleanup and move files) # (cleanup and move files)
Remove-Item $Path\Tomlet.dll Remove-Item $Path/Tomlet.dll
Remove-Item $Path\mcs.dll Remove-Item $Path/mcs.dll
Compress-Archive -Path $Path\* -CompressionLevel Fastest -DestinationPath $Path\..\UnityExplorer.Standalone.Mono.zip -Force Remove-Item $Path/../UnityExplorer.Standalone.Mono.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.Standalone.Mono.zip .\$Path\*
# ----------- Standalone IL2CPP ----------- # ----------- Standalone IL2CPP -----------
dotnet build src\UnityExplorer.sln -c Release_STANDALONE_Cpp dotnet build src/UnityExplorer.sln -c Release_STANDALONE_Cpp
$Path = "Release\UnityExplorer.Standalone.IL2CPP" $Path = "Release/UnityExplorer.Standalone.IL2CPP"
# ILRepack # ILRepack
lib/ILRepack.exe /target:library /lib:lib\net472 /lib:lib\unhollowed /lib:$Path /internalize /out:$Path\UnityExplorer.Standalone.IL2CPP.dll $Path\UnityExplorer.Standalone.IL2CPP.dll $Path\mcs.dll $Path\Tomlet.dll lib/ILRepack.exe /target:library /lib:lib/net472 /lib:lib/unhollowed /lib:$Path /internalize /out:$Path/UnityExplorer.Standalone.IL2CPP.dll $Path/UnityExplorer.Standalone.IL2CPP.dll $Path/mcs.dll $Path/Tomlet.dll
# (cleanup and move files) # (cleanup and move files)
Remove-Item $Path\Tomlet.dll Remove-Item $Path/Tomlet.dll
Remove-Item $Path\mcs.dll Remove-Item $Path/mcs.dll
Remove-Item $Path\Iced.dll Remove-Item $Path/Iced.dll
Remove-Item $Path\UnhollowerBaseLib.dll Remove-Item $Path/UnhollowerBaseLib.dll
Compress-Archive -Path $Path\* -CompressionLevel Fastest -DestinationPath $Path\..\UnityExplorer.Standalone.IL2CPP.zip -Force Remove-Item $Path/../UnityExplorer.Standalone.IL2CPP.zip -ErrorAction SilentlyContinue
7z a $Path/../UnityExplorer.Standalone.IL2CPP.zip .\$Path\*
# ----------- Editor (mono) ----------- # ----------- Editor (mono) -----------
$Path1 = "Release\UnityExplorer.Standalone.Mono" $Path1 = "Release/UnityExplorer.Standalone.Mono"
$Path2 = "UnityEditorPackage\Runtime" $Path2 = "UnityEditorPackage/Runtime"
Copy-Item $Path1\UnityExplorer.STANDALONE.Mono.dll -Destination $Path2 Copy-Item $Path1/UnityExplorer.STANDALONE.Mono.dll -Destination $Path2
Copy-Item $Path1\UniverseLib.Mono.dll -Destination $Path2 Copy-Item $Path1/UniverseLib.Mono.dll -Destination $Path2
Compress-Archive -Path UnityEditorPackage\* -CompressionLevel Fastest -DestinationPath Release\UnityExplorer.Editor.zip -Force Remove-Item Release/UnityExplorer.Editor.zip -ErrorAction SilentlyContinue
7z a Release/UnityExplorer.Editor.zip .\UnityEditorPackage\*

View File

@ -56,6 +56,7 @@ namespace UnityExplorer.CSConsole
"System.Text", "System.Text",
"System.Collections", "System.Collections",
"System.Collections.Generic", "System.Collections.Generic",
"System.Reflection",
"UnityEngine", "UnityEngine",
"UniverseLib", "UniverseLib",
#if CPP #if CPP

View File

@ -3,6 +3,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using UnityExplorer.Config;
// Thanks to ManlyMarco for this // Thanks to ManlyMarco for this
@ -10,6 +11,9 @@ namespace UnityExplorer.CSConsole
{ {
public class ScriptEvaluator : Evaluator, IDisposable public class ScriptEvaluator : Evaluator, IDisposable
{ {
internal TextWriter _textWriter;
internal static StreamReportPrinter _reportPrinter;
private static readonly HashSet<string> StdLib = new(StringComparer.InvariantCultureIgnoreCase) private static readonly HashSet<string> StdLib = new(StringComparer.InvariantCultureIgnoreCase)
{ {
"mscorlib", "mscorlib",
@ -18,9 +22,6 @@ namespace UnityExplorer.CSConsole
"System.Xml" "System.Xml"
}; };
internal TextWriter _textWriter;
internal static StreamReportPrinter _reportPrinter;
public ScriptEvaluator(TextWriter tw) : base(BuildContext(tw)) public ScriptEvaluator(TextWriter tw) : base(BuildContext(tw))
{ {
_textWriter = tw; _textWriter = tw;
@ -48,8 +49,19 @@ namespace UnityExplorer.CSConsole
private void Reference(Assembly asm) private void Reference(Assembly asm)
{ {
string name = asm.GetName().Name; string name = asm.GetName().Name;
if (name == "completions")
if (name == "completions") // ignore assemblies generated by mcs' autocomplete.
return; return;
foreach (string blacklisted in ConfigManager.CSConsole_Assembly_Blacklist.Value.Split(';'))
{
string bl = blacklisted;
if (bl.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
bl = blacklisted.Substring(0, bl.Length - 4);
if (string.Equals(bl, name, StringComparison.OrdinalIgnoreCase))
return;
}
ReferenceAssembly(asm); ReferenceAssembly(asm);
} }

View File

@ -16,19 +16,20 @@ namespace UnityExplorer.Config
// Actual UE Settings // Actual UE Settings
public static ConfigElement<KeyCode> Master_Toggle; public static ConfigElement<KeyCode> Master_Toggle;
public static ConfigElement<int> Target_Display;
public static ConfigElement<UIManager.VerticalAnchor> Main_Navbar_Anchor;
public static ConfigElement<bool> Force_Unlock_Mouse;
public static ConfigElement<KeyCode> Force_Unlock_Toggle;
public static ConfigElement<bool> Aggressive_Mouse_Unlock;
public static ConfigElement<bool> Disable_EventSystem_Override;
public static ConfigElement<string> Default_Output_Path;
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<bool> Disable_EventSystem_Override;
public static ConfigElement<int> Target_Display;
public static ConfigElement<bool> Force_Unlock_Mouse;
public static ConfigElement<KeyCode> Force_Unlock_Toggle;
public static ConfigElement<string> Default_Output_Path;
public static ConfigElement<string> DnSpy_Path;
public static ConfigElement<bool> Log_Unity_Debug;
public static ConfigElement<UIManager.VerticalAnchor> Main_Navbar_Anchor;
public static ConfigElement<KeyCode> World_MouseInspect_Keybind; public static ConfigElement<KeyCode> World_MouseInspect_Keybind;
public static ConfigElement<KeyCode> UI_MouseInspect_Keybind; public static ConfigElement<KeyCode> UI_MouseInspect_Keybind;
public static ConfigElement<string> CSConsole_Assembly_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; }
@ -57,8 +58,6 @@ namespace UnityExplorer.Config
#if STANDALONE #if STANDALONE
Loader.Standalone.ExplorerEditorBehaviour.Instance?.LoadConfigs(); Loader.Standalone.ExplorerEditorBehaviour.Instance?.LoadConfigs();
#endif #endif
//InitConsoleCallback();
} }
internal static void RegisterConfigElement<T>(ConfigElement<T> configElement) internal static void RegisterConfigElement<T>(ConfigElement<T> configElement)
@ -77,23 +76,53 @@ namespace UnityExplorer.Config
private static void CreateConfigElements() private static void CreateConfigElements()
{ {
Master_Toggle = new ConfigElement<KeyCode>("UnityExplorer Toggle", Master_Toggle = new("UnityExplorer Toggle",
"The key to enable or disable UnityExplorer's menu and features.", "The key to enable or disable UnityExplorer's menu and features.",
KeyCode.F7); KeyCode.F7);
Hide_On_Startup = new ConfigElement<bool>("Hide On Startup", Hide_On_Startup = new("Hide On Startup",
"Should UnityExplorer be hidden on startup?", "Should UnityExplorer be hidden on startup?",
false); false);
Target_Display = new ConfigElement<int>("Target Display", Startup_Delay_Time = new("Startup Delay Time",
"The delay on startup before the UI is created.",
1f);
Target_Display = new("Target Display",
"The monitor index for UnityExplorer to use, if you have multiple. 0 is the default display, 1 is secondary, etc. " + "The monitor index for UnityExplorer to use, if you have multiple. 0 is the default display, 1 is secondary, etc. " +
"Restart recommended when changing this setting. Make sure your extra monitors are the same resolution as your primary monitor.", "Restart recommended when changing this setting. Make sure your extra monitors are the same resolution as your primary monitor.",
0); 0);
Main_Navbar_Anchor = new ConfigElement<UIManager.VerticalAnchor>("Main Navbar Anchor", Force_Unlock_Mouse = new("Force Unlock Mouse",
"Force the Cursor to be unlocked (visible) when the UnityExplorer menu is open.",
true);
Force_Unlock_Mouse.OnValueChanged += (bool value) => UniverseLib.Config.ConfigManager.Force_Unlock_Mouse = value;
Force_Unlock_Toggle = new("Force Unlock Toggle Key",
"The keybind to toggle the 'Force Unlock Mouse' setting. Only usable when UnityExplorer is open.",
KeyCode.None);
Disable_EventSystem_Override = new("Disable EventSystem override",
"If enabled, UnityExplorer will not override the EventSystem from the game.\n<b>May require restart to take effect.</b>",
false);
Disable_EventSystem_Override.OnValueChanged += (bool value) => UniverseLib.Config.ConfigManager.Disable_EventSystem_Override = value;
Default_Output_Path = new("Default Output Path",
"The default output path when exporting things from UnityExplorer.",
Path.Combine(ExplorerCore.ExplorerFolder, "Output"));
DnSpy_Path = new("dnSpy Path",
"The full path to dnSpy.exe (64-bit).",
@"C:/Program Files/dnspy/dnSpy.exe");
Main_Navbar_Anchor = new("Main Navbar Anchor",
"The vertical anchor of the main UnityExplorer Navbar, in case you want to move it.", "The vertical anchor of the main UnityExplorer Navbar, in case you want to move it.",
UIManager.VerticalAnchor.Top); UIManager.VerticalAnchor.Top);
Log_Unity_Debug = new("Log Unity Debug",
"Should UnityEngine.Debug.Log messages be printed to UnityExplorer's log?",
false);
World_MouseInspect_Keybind = new("World Mouse-Inspect Keybind", World_MouseInspect_Keybind = new("World Mouse-Inspect Keybind",
"Optional keybind to being a World-mode Mouse Inspect.", "Optional keybind to being a World-mode Mouse Inspect.",
KeyCode.None); KeyCode.None);
@ -102,33 +131,13 @@ namespace UnityExplorer.Config
"Optional keybind to begin a UI-mode Mouse Inspect.", "Optional keybind to begin a UI-mode Mouse Inspect.",
KeyCode.None); KeyCode.None);
Force_Unlock_Mouse = new ConfigElement<bool>("Force Unlock Mouse", CSConsole_Assembly_Blacklist = new("CSharp Console Assembly Blacklist",
"Force the Cursor to be unlocked (visible) when the UnityExplorer menu is open.", "Use this to blacklist Assembly names from being referenced by the C# Console. Requires a Reset of the C# Console.\n" +
true); "Separate each Assembly with a semicolon ';'." +
Force_Unlock_Mouse.OnValueChanged += (bool value) => UniverseLib.Config.ConfigManager.Force_Unlock_Mouse = value; "For example, to blacklist Assembly-CSharp, you would add 'Assembly-CSharp;'",
"");
Force_Unlock_Toggle = new ConfigElement<KeyCode>("Force Unlock Toggle Key", Reflection_Signature_Blacklist = new("Member Signature Blacklist",
"The keybind to toggle the 'Force Unlock Mouse' setting. Only usable when UnityExplorer is open.",
KeyCode.None);
Disable_EventSystem_Override = new ConfigElement<bool>("Disable EventSystem override",
"If enabled, UnityExplorer will not override the EventSystem from the game.\n<b>May require restart to take effect.</b>",
false);
Disable_EventSystem_Override.OnValueChanged += (bool value) => UniverseLib.Config.ConfigManager.Disable_EventSystem_Override = value;
Log_Unity_Debug = new ConfigElement<bool>("Log Unity Debug",
"Should UnityEngine.Debug.Log messages be printed to UnityExplorer's log?",
false);
Default_Output_Path = new ConfigElement<string>("Default Output Path",
"The default output path when exporting things from UnityExplorer.",
Path.Combine(ExplorerCore.ExplorerFolder, "Output"));
Startup_Delay_Time = new ConfigElement<float>("Startup Delay Time",
"The delay on startup before the UI is created.",
1f);
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 'UnityEngine.Camera.main;'", "For example, to blacklist Camera.main, you would add 'UnityEngine.Camera.main;'",

View File

@ -14,7 +14,7 @@ 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.7.12"; public const string VERSION = "4.8.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";

View File

@ -158,11 +158,8 @@ namespace UnityExplorer.Hooks
} }
HookInstance hook = new(method); HookInstance hook = new(method);
if (hook.Enabled) HookList.hookedSignatures.Add(sig);
{ HookList.currentHooks.Add(sig, hook);
HookList.hookedSignatures.Add(sig);
HookList.currentHooks.Add(sig, hook);
}
AddHooksScrollPool.Refresh(true, false); AddHooksScrollPool.Refresh(true, false);
HookList.HooksScrollPool.Refresh(true, false); HookList.HooksScrollPool.Refresh(true, false);
@ -239,6 +236,8 @@ namespace UnityExplorer.Hooks
CurrentEditedHook = null; CurrentEditedHook = null;
HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.ClassMethodSelector); HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.ClassMethodSelector);
} }
HookList.HooksScrollPool.Refresh(true, false);
} }
// UI Construction // UI Construction

View File

@ -15,7 +15,6 @@ namespace UnityExplorer.Hooks
{ {
// Static // Static
//static readonly StringBuilder evalOutput = new();
static readonly StringBuilder evaluatorOutput; static readonly StringBuilder evaluatorOutput;
static readonly ScriptEvaluator scriptEvaluator = new(new StringWriter(evaluatorOutput = new StringBuilder())); static readonly ScriptEvaluator scriptEvaluator = new(new StringWriter(evaluatorOutput = new StringBuilder()));
@ -31,21 +30,22 @@ namespace UnityExplorer.Hooks
// Instance // Instance
public bool Enabled; public bool Enabled;
public MethodInfo TargetMethod; public MethodInfo TargetMethod;
public string PatchSourceCode; public string PatchSourceCode;
private readonly string shortSignature; readonly string signature;
private PatchProcessor patchProcessor; PatchProcessor patchProcessor;
private MethodInfo postfix; MethodInfo postfix;
private MethodInfo prefix; MethodInfo prefix;
private MethodInfo finalizer; MethodInfo finalizer;
private MethodInfo transpiler; MethodInfo transpiler;
public HookInstance(MethodInfo targetMethod) public HookInstance(MethodInfo targetMethod)
{ {
this.TargetMethod = targetMethod; this.TargetMethod = targetMethod;
this.shortSignature = TargetMethod.FullDescription(); this.signature = TargetMethod.FullDescription();
GenerateDefaultPatchSourceCode(targetMethod); GenerateDefaultPatchSourceCode(targetMethod);
@ -144,28 +144,29 @@ namespace UnityExplorer.Hooks
{ {
StringBuilder codeBuilder = new(); StringBuilder codeBuilder = new();
codeBuilder.Append("static void Postfix("); // System.Reflection.MethodBase __originalMethod codeBuilder.Append("static void Postfix(");
bool isStatic = targetMethod.IsStatic; bool isStatic = targetMethod.IsStatic;
List<string> arguments = new();
if (!isStatic) if (!isStatic)
codeBuilder.Append($"{FullDescriptionClean(targetMethod.DeclaringType)} __instance"); arguments.Add($"{FullDescriptionClean(targetMethod.DeclaringType)} __instance");
if (targetMethod.ReturnType != typeof(void)) if (targetMethod.ReturnType != typeof(void))
{ arguments.Add($"{FullDescriptionClean(targetMethod.ReturnType)} __result");
if (!isStatic)
codeBuilder.Append(", ");
codeBuilder.Append($"{FullDescriptionClean(targetMethod.ReturnType)} __result");
}
ParameterInfo[] parameters = targetMethod.GetParameters(); ParameterInfo[] parameters = targetMethod.GetParameters();
int paramIdx = 0; int paramIdx = 0;
foreach (ParameterInfo param in parameters) foreach (ParameterInfo param in parameters)
{ {
codeBuilder.Append($", {FullDescriptionClean(param.ParameterType)} __{paramIdx}"); arguments.Add($"{FullDescriptionClean(param.ParameterType)} __{paramIdx}");
paramIdx++; paramIdx++;
} }
codeBuilder.Append(string.Join(", ", arguments.ToArray()));
codeBuilder.Append(")\n"); codeBuilder.Append(")\n");
// Patch body // Patch body
@ -173,8 +174,8 @@ namespace UnityExplorer.Hooks
codeBuilder.AppendLine("{"); codeBuilder.AppendLine("{");
codeBuilder.AppendLine(" try {"); codeBuilder.AppendLine(" try {");
codeBuilder.AppendLine(" StringBuilder sb = new StringBuilder();"); codeBuilder.AppendLine(" StringBuilder sb = new StringBuilder();");
codeBuilder.AppendLine($" sb.AppendLine(\"---- Patched called ----\");"); codeBuilder.AppendLine($" sb.AppendLine(\"--------------------\");");
codeBuilder.AppendLine($" sb.AppendLine(\"{shortSignature}\");"); codeBuilder.AppendLine($" sb.AppendLine(\"{signature}\");");
if (!targetMethod.IsStatic) if (!targetMethod.IsStatic)
codeBuilder.AppendLine($" sb.Append(\"- __instance: \").AppendLine(__instance.ToString());"); codeBuilder.AppendLine($" sb.Append(\"- __instance: \").AppendLine(__instance.ToString());");
@ -206,15 +207,11 @@ namespace UnityExplorer.Hooks
codeBuilder.AppendLine($" UnityExplorer.ExplorerCore.Log(sb.ToString());"); codeBuilder.AppendLine($" UnityExplorer.ExplorerCore.Log(sb.ToString());");
codeBuilder.AppendLine(" }"); codeBuilder.AppendLine(" }");
codeBuilder.AppendLine(" catch (System.Exception ex) {"); codeBuilder.AppendLine(" catch (System.Exception ex) {");
codeBuilder.AppendLine($" UnityExplorer.ExplorerCore.LogWarning($\"Exception in patch of {shortSignature}:\\n{{ex}}\");"); codeBuilder.AppendLine($" UnityExplorer.ExplorerCore.LogWarning($\"Exception in patch of {signature}:\\n{{ex}}\");");
codeBuilder.AppendLine(" }"); codeBuilder.AppendLine(" }");
// End patch body
codeBuilder.AppendLine("}"); codeBuilder.AppendLine("}");
//ExplorerCore.Log(codeBuilder.ToString());
return PatchSourceCode = codeBuilder.ToString(); return PatchSourceCode = codeBuilder.ToString();
} }

View File

@ -113,17 +113,11 @@ namespace UnityExplorer.Inspectors
public bool TryUpdate() public bool TryUpdate()
{ {
if (ConfigManager.World_MouseInspect_Keybind.Value != KeyCode.None) if (InputManager.GetKeyDown(ConfigManager.World_MouseInspect_Keybind.Value))
{ Instance.StartInspect(MouseInspectMode.World);
if (InputManager.GetKeyDown(ConfigManager.World_MouseInspect_Keybind.Value))
Instance.StartInspect(MouseInspectMode.World);
}
if (ConfigManager.World_MouseInspect_Keybind.Value != KeyCode.None) if (InputManager.GetKeyDown(ConfigManager.UI_MouseInspect_Keybind.Value))
{ Instance.StartInspect(MouseInspectMode.UI);
if (InputManager.GetKeyDown(ConfigManager.World_MouseInspect_Keybind.Value))
Instance.StartInspect(MouseInspectMode.World);
}
if (Inspecting) if (Inspecting)
UpdateInspect(); UpdateInspect();

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
@ -8,6 +9,8 @@ using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.CacheObject; using UnityExplorer.CacheObject;
using UnityExplorer.CacheObject.Views; using UnityExplorer.CacheObject.Views;
using UnityExplorer.Config;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
using UniverseLib; using UniverseLib;
@ -33,7 +36,6 @@ namespace UnityExplorer.Inspectors
public class ReflectionInspector : InspectorBase, ICellPoolDataSource<CacheMemberCell>, ICacheObjectController public class ReflectionInspector : InspectorBase, ICellPoolDataSource<CacheMemberCell>, ICacheObjectController
{ {
public CacheObjectBase ParentCacheObject { get; set; } public CacheObjectBase ParentCacheObject { get; set; }
//public Type TargetType { get; private set; }
public bool StaticOnly { get; internal set; } public bool StaticOnly { get; internal set; }
public bool CanWrite => true; public bool CanWrite => true;
@ -73,6 +75,8 @@ namespace UnityExplorer.Inspectors
Text assemblyText; Text assemblyText;
Toggle autoUpdateToggle; Toggle autoUpdateToggle;
ButtonRef dnSpyButton;
ButtonRef makeGenericButton; ButtonRef makeGenericButton;
GenericConstructorWidget genericConstructor; GenericConstructorWidget genericConstructor;
@ -155,9 +159,15 @@ namespace UnityExplorer.Inspectors
string asmText; string asmText;
if (TargetType.Assembly is AssemblyBuilder || string.IsNullOrEmpty(TargetType.Assembly.Location)) if (TargetType.Assembly is AssemblyBuilder || string.IsNullOrEmpty(TargetType.Assembly.Location))
{
asmText = $"{TargetType.Assembly.GetName().Name} <color=grey><i>(in memory)</i></color>"; asmText = $"{TargetType.Assembly.GetName().Name} <color=grey><i>(in memory)</i></color>";
dnSpyButton.GameObject.SetActive(false);
}
else else
{
asmText = Path.GetFileName(TargetType.Assembly.Location); asmText = Path.GetFileName(TargetType.Assembly.Location);
dnSpyButton.GameObject.SetActive(true);
}
assemblyText.text = $"<color=grey>Assembly:</color> {asmText}"; assemblyText.text = $"<color=grey>Assembly:</color> {asmText}";
// Unity object helper widget // Unity object helper widget
@ -350,6 +360,25 @@ namespace UnityExplorer.Inspectors
ClipboardPanel.Copy(this.Target ?? this.TargetType); ClipboardPanel.Copy(this.Target ?? this.TargetType);
} }
void OnDnSpyButtonClicked()
{
string path = ConfigManager.DnSpy_Path.Value;
if (File.Exists(path) && path.EndsWith("dnspy.exe", StringComparison.OrdinalIgnoreCase))
{
Type type = TargetType;
// if constructed generic type, use the generic type definition
if (type.IsGenericType && !type.IsGenericTypeDefinition)
type = type.GetGenericTypeDefinition();
string args = $"\"{type.Assembly.Location}\" --select T:{type.FullName}";
Process.Start(path, args);
}
else
{
Notification.ShowMessage($"Please set a valid dnSpy path in UnityExplorer Settings.");
}
}
void OnMakeGenericClicked() void OnMakeGenericClicked()
{ {
ContentRoot.SetActive(false); ContentRoot.SetActive(false);
@ -425,10 +454,21 @@ namespace UnityExplorer.Inspectors
UIFactory.SetLayoutElement(copyButton.Component.gameObject, minHeight: 25, minWidth: 120, flexibleWidth: 0); UIFactory.SetLayoutElement(copyButton.Component.gameObject, minHeight: 25, minWidth: 120, flexibleWidth: 0);
copyButton.OnClick += OnCopyClicked; copyButton.OnClick += OnCopyClicked;
assemblyText = UIFactory.CreateLabel(UIRoot, "AssemblyLabel", "not set", TextAnchor.MiddleLeft); // Assembly row
GameObject asmRow = UIFactory.CreateHorizontalGroup(UIRoot, "AssemblyRow", false, false, true, true, 5, default, new(1, 1, 1, 0));
UIFactory.SetLayoutElement(asmRow, flexibleWidth: 9999, minHeight: 25);
assemblyText = UIFactory.CreateLabel(asmRow, "AssemblyLabel", "not set", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(assemblyText.gameObject, minHeight: 25, flexibleWidth: 9999); UIFactory.SetLayoutElement(assemblyText.gameObject, minHeight: 25, flexibleWidth: 9999);
ContentRoot = UIFactory.CreateVerticalGroup(UIRoot, "MemberHolder", false, false, true, true, 5, new Vector4(2, 2, 2, 2), dnSpyButton = UIFactory.CreateButton(asmRow, "DnSpyButton", "View in dnSpy");
UIFactory.SetLayoutElement(dnSpyButton.GameObject, minWidth: 120, minHeight: 25);
dnSpyButton.OnClick += OnDnSpyButtonClicked;
// Content
ContentRoot = UIFactory.CreateVerticalGroup(UIRoot, "ContentRoot", 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(ContentRoot, flexibleWidth: 9999, flexibleHeight: 9999); UIFactory.SetLayoutElement(ContentRoot, flexibleWidth: 9999, flexibleHeight: 9999);

View File

@ -39,7 +39,6 @@ namespace UnityExplorer.UI
private static void ConstructUI() private static void ConstructUI()
{ {
popupLabel = UIFactory.CreateLabel(UIManager.UIRoot, "ClipboardNotification", "", TextAnchor.MiddleCenter); popupLabel = UIFactory.CreateLabel(UIManager.UIRoot, "ClipboardNotification", "", TextAnchor.MiddleCenter);
popupLabel.rectTransform.sizeDelta = new(500, 100); popupLabel.rectTransform.sizeDelta = new(500, 100);
popupLabel.gameObject.AddComponent<Outline>(); popupLabel.gameObject.AddComponent<Outline>();

View File

@ -171,6 +171,9 @@ namespace UnityExplorer.UI.Panels
if (!ourCamera) if (!ourCamera)
return; return;
if (positionInput.Component.isFocused)
return;
lastSetCameraPosition = ourCamera.transform.position; lastSetCameraPosition = ourCamera.transform.position;
positionInput.Text = ParseUtility.ToStringForInput<Vector3>(lastSetCameraPosition); positionInput.Text = ParseUtility.ToStringForInput<Vector3>(lastSetCameraPosition);
} }

View File

@ -71,6 +71,17 @@ namespace UnityExplorer.UI.Panels
// Save Data // Save Data
bool setDefault = false;
public override void SetDefaultSizeAndPosition()
{
if (setDefault)
return;
setDefault = true;
base.SetDefaultSizeAndPosition();
}
public bool ApplyingSaveData { get; set; } public bool ApplyingSaveData { get; set; }
public void SaveInternalData() public void SaveInternalData()

View File

@ -5,6 +5,7 @@ using UnityExplorer.Config;
using UnityExplorer.CSConsole; using UnityExplorer.CSConsole;
using UnityExplorer.Inspectors; using UnityExplorer.Inspectors;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete; using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib; using UniverseLib;
using UniverseLib.Input; using UniverseLib.Input;
@ -54,10 +55,7 @@ namespace UnityExplorer.UI
private static readonly Vector2 NAVBAR_DIMENSIONS = new(1020f, 35f); private static readonly Vector2 NAVBAR_DIMENSIONS = new(1020f, 35f);
private static ButtonRef closeBtn; private static ButtonRef closeBtn;
private static ButtonRef pauseBtn; private static TimeScaleWidget timeScaleWidget;
private static InputFieldRef timeInput;
private static bool pauseButtonPausing;
private static float lastTimeScale;
private static int lastScreenWidth; private static int lastScreenWidth;
private static int lastScreenHeight; private static int lastScreenHeight;
@ -141,20 +139,7 @@ namespace UnityExplorer.UI
UniverseLib.Config.ConfigManager.Force_Unlock_Mouse = !UniverseLib.Config.ConfigManager.Force_Unlock_Mouse; UniverseLib.Config.ConfigManager.Force_Unlock_Mouse = !UniverseLib.Config.ConfigManager.Force_Unlock_Mouse;
// update the timescale value // update the timescale value
if (!timeInput.Component.isFocused && lastTimeScale != Time.timeScale) timeScaleWidget.Update();
{
if (pauseButtonPausing && Time.timeScale != 0.0f)
{
pauseButtonPausing = false;
OnPauseButtonToggled();
}
if (!pauseButtonPausing)
{
timeInput.Text = Time.timeScale.ToString("F2");
lastTimeScale = Time.timeScale;
}
}
// check screen dimension change // check screen dimension change
Display display = DisplayManager.ActiveDisplay; Display display = DisplayManager.ActiveDisplay;
@ -232,41 +217,7 @@ namespace UnityExplorer.UI
closeBtn.ButtonText.text = val.ToString(); closeBtn.ButtonText.text = val.ToString();
} }
// Time controls
private static void OnTimeInputEndEdit(string val)
{
if (pauseButtonPausing)
return;
if (float.TryParse(val, out float f))
{
Time.timeScale = f;
lastTimeScale = f;
}
timeInput.Text = Time.timeScale.ToString("F2");
}
private static void OnPauseButtonClicked()
{
pauseButtonPausing = !pauseButtonPausing;
Time.timeScale = pauseButtonPausing ? 0f : lastTimeScale;
OnPauseButtonToggled();
}
private static void OnPauseButtonToggled()
{
timeInput.Component.text = Time.timeScale.ToString("F2");
timeInput.Component.readOnly = pauseButtonPausing;
timeInput.Component.textComponent.color = pauseButtonPausing ? Color.grey : Color.white;
Color color = pauseButtonPausing ? new Color(0.3f, 0.3f, 0.2f) : new Color(0.2f, 0.2f, 0.2f);
RuntimeHelper.SetColorBlock(pauseBtn.Component, color, color * 1.2f, color * 0.7f);
pauseBtn.ButtonText.text = pauseButtonPausing ? "►" : "||";
}
// UI Construction // UI Construction
@ -298,26 +249,17 @@ namespace UnityExplorer.UI
UIFactory.SetLayoutElement(NavbarTabButtonHolder, minHeight: 25, flexibleHeight: 999, flexibleWidth: 999); UIFactory.SetLayoutElement(NavbarTabButtonHolder, minHeight: 25, flexibleHeight: 999, flexibleWidth: 999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(NavbarTabButtonHolder, false, true, true, true, 4, 2, 2, 2, 2); UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(NavbarTabButtonHolder, false, true, true, true, 4, 2, 2, 2, 2);
// Time controls // Time scale widget
timeScaleWidget = new(navbarPanel);
Text timeLabel = UIFactory.CreateLabel(navbarPanel, "TimeLabel", "Time:", TextAnchor.MiddleRight, Color.grey); //spacer
UIFactory.SetLayoutElement(timeLabel.gameObject, minHeight: 25, minWidth: 50); GameObject spacer = UIFactory.CreateUIObject("Spacer", navbarPanel);
UIFactory.SetLayoutElement(spacer, minWidth: 15);
timeInput = UIFactory.CreateInputField(navbarPanel, "TimeInput", "timeScale");
UIFactory.SetLayoutElement(timeInput.Component.gameObject, minHeight: 25, minWidth: 40);
timeInput.Component.GetOnEndEdit().AddListener(OnTimeInputEndEdit);
timeInput.Text = string.Empty;
timeInput.Text = Time.timeScale.ToString();
pauseBtn = UIFactory.CreateButton(navbarPanel, "PauseButton", "||", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(pauseBtn.Component.gameObject, minHeight: 25, minWidth: 25);
pauseBtn.OnClick += OnPauseButtonClicked;
// Hide menu button // Hide menu button
closeBtn = UIFactory.CreateButton(navbarPanel, "CloseButton", ConfigManager.Master_Toggle.Value.ToString()); closeBtn = UIFactory.CreateButton(navbarPanel, "CloseButton", ConfigManager.Master_Toggle.Value.ToString());
UIFactory.SetLayoutElement(closeBtn.Component.gameObject, minHeight: 25, minWidth: 80, flexibleWidth: 0); UIFactory.SetLayoutElement(closeBtn.Component.gameObject, minHeight: 25, minWidth: 60, flexibleWidth: 0);
RuntimeHelper.SetColorBlock(closeBtn.Component, new Color(0.63f, 0.32f, 0.31f), RuntimeHelper.SetColorBlock(closeBtn.Component, new Color(0.63f, 0.32f, 0.31f),
new Color(0.81f, 0.25f, 0.2f), new Color(0.6f, 0.18f, 0.16f)); new Color(0.81f, 0.25f, 0.2f), new Color(0.6f, 0.18f, 0.16f));

View File

@ -33,7 +33,8 @@ namespace UnityExplorer.UI.Widgets
Text ActiveSelfText; Text ActiveSelfText;
Toggle IsStaticToggle; Toggle IsStaticToggle;
InputFieldRef SceneInput; ButtonRef SceneButton;
InputFieldRef InstanceIDInput; InputFieldRef InstanceIDInput;
InputFieldRef TagInput; InputFieldRef TagInput;
@ -100,7 +101,7 @@ namespace UnityExplorer.UI.Widgets
if (force || Target.scene.handle != lastSceneHandle) if (force || Target.scene.handle != lastSceneHandle)
{ {
lastSceneHandle = Target.scene.handle; lastSceneHandle = Target.scene.handle;
SceneInput.Text = Target.scene.IsValid() ? Target.scene.name : "None (Asset/Resource)"; SceneButton.ButtonText.text = Target.scene.IsValid() ? Target.scene.name : "None (Asset/Resource)";
} }
if (force || (!TagInput.Component.isFocused && Target.tag != lastTag)) if (force || (!TagInput.Component.isFocused && Target.tag != lastTag))
@ -228,6 +229,11 @@ namespace UnityExplorer.UI.Widgets
ExplorerCore.LogWarning($"Exception setting tag! {ex.ReflectionExToString()}"); ExplorerCore.LogWarning($"Exception setting tag! {ex.ReflectionExToString()}");
} }
} }
void OnSceneButtonClicked()
{
InspectorManager.Inspect(Target.scene);
}
void OnExploreButtonClicked() void OnExploreButtonClicked()
{ {
@ -383,10 +389,9 @@ namespace UnityExplorer.UI.Widgets
Text sceneLabel = UIFactory.CreateLabel(thirdrow, "SceneLabel", "Scene:", TextAnchor.MiddleLeft, Color.grey); Text sceneLabel = UIFactory.CreateLabel(thirdrow, "SceneLabel", "Scene:", TextAnchor.MiddleLeft, Color.grey);
UIFactory.SetLayoutElement(sceneLabel.gameObject, minHeight: 25, minWidth: 50); UIFactory.SetLayoutElement(sceneLabel.gameObject, minHeight: 25, minWidth: 50);
SceneInput = UIFactory.CreateInputField(thirdrow, "SceneInput", "untitled"); SceneButton = UIFactory.CreateButton(thirdrow, "SceneButton", "untitled");
UIFactory.SetLayoutElement(SceneInput.Component.gameObject, minHeight: 25, minWidth: 120, flexibleWidth: 999); UIFactory.SetLayoutElement(SceneButton.Component.gameObject, minHeight: 25, minWidth: 120, flexibleWidth: 999);
SceneInput.Component.readOnly = true; SceneButton.OnClick += OnSceneButtonClicked;
SceneInput.Component.textComponent.color = new Color(0.7f, 0.7f, 0.7f);
// Layer // Layer
Text layerLabel = UIFactory.CreateLabel(thirdrow, "LayerLabel", "Layer:", TextAnchor.MiddleLeft, Color.grey); Text layerLabel = UIFactory.CreateLabel(thirdrow, "LayerLabel", "Layer:", TextAnchor.MiddleLeft, Color.grey);

View File

@ -0,0 +1,116 @@
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets
{
internal class TimeScaleWidget
{
public TimeScaleWidget(GameObject parent)
{
Instance = this;
ConstructUI(parent);
InitPatch();
}
static TimeScaleWidget Instance;
ButtonRef lockBtn;
bool locked;
InputFieldRef timeInput;
float desiredTime;
bool settingTimeScale;
public void Update()
{
// Fallback in case Time.timeScale patch failed for whatever reason
if (locked)
SetTimeScale(desiredTime);
if (!timeInput.Component.isFocused)
timeInput.Text = Time.timeScale.ToString("F2");
}
void SetTimeScale(float time)
{
settingTimeScale = true;
Time.timeScale = time;
settingTimeScale = false;
}
// UI event listeners
void OnTimeInputEndEdit(string val)
{
if (float.TryParse(val, out float f))
{
SetTimeScale(f);
desiredTime = f;
}
}
void OnPauseButtonClicked()
{
OnTimeInputEndEdit(timeInput.Text);
locked = !locked;
Color color = locked ? new Color(0.3f, 0.3f, 0.2f) : new Color(0.2f, 0.2f, 0.2f);
RuntimeHelper.SetColorBlock(lockBtn.Component, color, color * 1.2f, color * 0.7f);
lockBtn.ButtonText.text = locked ? "Unlock" : "Lock";
}
// UI Construction
void ConstructUI(GameObject parent)
{
Text timeLabel = UIFactory.CreateLabel(parent, "TimeLabel", "Time:", TextAnchor.MiddleRight, Color.grey);
UIFactory.SetLayoutElement(timeLabel.gameObject, minHeight: 25, minWidth: 35);
timeInput = UIFactory.CreateInputField(parent, "TimeInput", "timeScale");
UIFactory.SetLayoutElement(timeInput.Component.gameObject, minHeight: 25, minWidth: 40);
timeInput.Component.GetOnEndEdit().AddListener(OnTimeInputEndEdit);
timeInput.Text = string.Empty;
timeInput.Text = Time.timeScale.ToString();
lockBtn = UIFactory.CreateButton(parent, "PauseButton", "Lock", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(lockBtn.Component.gameObject, minHeight: 25, minWidth: 50);
lockBtn.OnClick += OnPauseButtonClicked;
}
// Only allow Time.timeScale to be set if the user hasn't "locked" it or if we are setting the value internally.
static void InitPatch()
{
try
{
MethodInfo target = AccessTools.Method(typeof(Time), nameof(Time.timeScale));
#if CPP
if (UnhollowerBaseLib.UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(target) == null)
return;
#endif
ExplorerCore.Harmony.Patch(target,
prefix: new(AccessTools.Method(typeof(TimeScaleWidget), nameof(Prefix_Time_set_timeScale))));
}
catch { }
}
static bool Prefix_Time_set_timeScale()
{
return !Instance.locked || Instance.settingTimeScale;
}
}
}

View File

@ -22,18 +22,18 @@ namespace UnityExplorer.UI.Widgets
static Coroutine CurrentlyPlayingCoroutine; static Coroutine CurrentlyPlayingCoroutine;
static readonly string zeroLengthString = GetLengthString(0f); static readonly string zeroLengthString = GetLengthString(0f);
public AudioClip RefAudioClip; public AudioClip audioClip;
private string fullLengthText; string fullLengthText;
private ButtonRef toggleButton; ButtonRef toggleButton;
private bool audioPlayerWanted; bool audioPlayerWanted;
private GameObject audioPlayerRoot; GameObject audioPlayerRoot;
private ButtonRef playStopButton; ButtonRef playStopButton;
private Text progressLabel; Text progressLabel;
private GameObject saveObjectRow; GameObject saveObjectRow;
private InputFieldRef savePathInput; InputFieldRef savePathInput;
private GameObject cantSaveRow; GameObject cantSaveRow;
public override void OnBorrowed(object target, Type targetType, ReflectionInspector inspector) public override void OnBorrowed(object target, Type targetType, ReflectionInspector inspector)
{ {
@ -42,10 +42,10 @@ namespace UnityExplorer.UI.Widgets
this.audioPlayerRoot.transform.SetParent(inspector.UIRoot.transform); this.audioPlayerRoot.transform.SetParent(inspector.UIRoot.transform);
this.audioPlayerRoot.transform.SetSiblingIndex(inspector.UIRoot.transform.childCount - 2); this.audioPlayerRoot.transform.SetSiblingIndex(inspector.UIRoot.transform.childCount - 2);
RefAudioClip = target.TryCast<AudioClip>(); audioClip = target.TryCast<AudioClip>();
this.fullLengthText = GetLengthString(RefAudioClip.length); this.fullLengthText = GetLengthString(audioClip.length);
if (RefAudioClip.loadType == AudioClipLoadType.DecompressOnLoad) if (audioClip.loadType == AudioClipLoadType.DecompressOnLoad)
{ {
cantSaveRow.SetActive(false); cantSaveRow.SetActive(false);
saveObjectRow.SetActive(true); saveObjectRow.SetActive(true);
@ -62,7 +62,7 @@ namespace UnityExplorer.UI.Widgets
public override void OnReturnToPool() public override void OnReturnToPool()
{ {
RefAudioClip = null; audioClip = null;
if (audioPlayerWanted) if (audioPlayerWanted)
ToggleAudioWidget(); ToggleAudioWidget();
@ -95,7 +95,7 @@ namespace UnityExplorer.UI.Widgets
void SetDefaultSavePath() void SetDefaultSavePath()
{ {
string name = RefAudioClip.name; string name = audioClip.name;
if (string.IsNullOrEmpty(name)) if (string.IsNullOrEmpty(name))
name = "untitled"; name = "untitled";
savePathInput.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.wav"); savePathInput.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.wav");
@ -163,7 +163,7 @@ namespace UnityExplorer.UI.Widgets
{ {
playStopButton.ButtonText.text = "Stop Clip"; playStopButton.ButtonText.text = "Stop Clip";
CurrentlyPlaying = this; CurrentlyPlaying = this;
Source.clip = this.RefAudioClip; Source.clip = this.audioClip;
Source.Play(); Source.Play();
while (Source.isPlaying) while (Source.isPlaying)
@ -191,7 +191,7 @@ namespace UnityExplorer.UI.Widgets
public void OnSaveClipClicked() public void OnSaveClipClicked()
{ {
if (!RefAudioClip) if (!audioClip)
{ {
ExplorerCore.LogWarning("AudioClip is null, maybe it was destroyed?"); ExplorerCore.LogWarning("AudioClip is null, maybe it was destroyed?");
return; return;
@ -212,7 +212,7 @@ namespace UnityExplorer.UI.Widgets
if (File.Exists(path)) if (File.Exists(path))
File.Delete(path); File.Delete(path);
SavWav.Save(RefAudioClip, path); SavWav.Save(audioClip, path);
} }
public override GameObject CreateContent(GameObject uiRoot) public override GameObject CreateContent(GameObject uiRoot)

View File

@ -0,0 +1,344 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Config;
using UnityExplorer.Inspectors;
using UnityExplorer.UI.Panels;
using UniverseLib;
using UniverseLib.Runtime;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.ObjectPool;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets
{
public class MaterialWidget : UnityObjectWidget
{
static MaterialWidget()
{
mi_GetTexturePropertyNames = typeof(Material).GetMethod("GetTexturePropertyNames", ArgumentUtility.EmptyTypes);
MaterialWidgetSupported = mi_GetTexturePropertyNames != null;
}
internal static bool MaterialWidgetSupported { get; }
static readonly MethodInfo mi_GetTexturePropertyNames;
Material material;
Texture2D activeTexture;
readonly Dictionary<string, Texture> textures = new();
readonly HashSet<Texture2D> texturesToDestroy = new();
bool textureViewerWanted;
ButtonRef toggleButton;
GameObject textureViewerRoot;
Dropdown textureDropdown;
InputFieldRef savePathInput;
Image image;
LayoutElement imageLayout;
public override void OnBorrowed(object target, Type targetType, ReflectionInspector inspector)
{
base.OnBorrowed(target, targetType, inspector);
material = target.TryCast<Material>();
if (material.mainTexture)
SetActiveTexture(material.mainTexture);
if (mi_GetTexturePropertyNames.Invoke(material, ArgumentUtility.EmptyArgs) is IEnumerable<string> propNames)
{
foreach (string property in propNames)
{
if (material.GetTexture(property) is Texture texture)
{
if (texture.TryCast<Texture2D>() is null && texture.TryCast<Cubemap>() is null)
continue;
textures.Add(property, texture);
if (!activeTexture)
SetActiveTexture(texture);
}
}
}
if (textureViewerRoot)
{
textureViewerRoot.transform.SetParent(inspector.UIRoot.transform);
RefreshTextureDropdown();
}
InspectorPanel.Instance.Dragger.OnFinishResize += OnInspectorFinishResize;
}
void SetActiveTexture(Texture texture)
{
if (texture.TryCast<Texture2D>() is Texture2D tex2D)
activeTexture = tex2D;
else if (texture.TryCast<Cubemap>() is Cubemap cubemap)
{
activeTexture = TextureHelper.UnwrapCubemap(cubemap);
texturesToDestroy.Add(activeTexture);
}
}
public override void OnReturnToPool()
{
InspectorPanel.Instance.Dragger.OnFinishResize -= OnInspectorFinishResize;
if (texturesToDestroy.Any())
{
foreach (Texture2D tex in texturesToDestroy)
UnityEngine.Object.Destroy(tex);
texturesToDestroy.Clear();
}
material = null;
activeTexture = null;
textures.Clear();
if (image.sprite)
UnityEngine.Object.Destroy(image.sprite);
if (textureViewerWanted)
ToggleTextureViewer();
if (textureViewerRoot)
textureViewerRoot.transform.SetParent(Pool<Texture2DWidget>.Instance.InactiveHolder.transform);
base.OnReturnToPool();
}
void ToggleTextureViewer()
{
if (textureViewerWanted)
{
// disable
textureViewerWanted = false;
textureViewerRoot.SetActive(false);
toggleButton.ButtonText.text = "View Material";
owner.ContentRoot.SetActive(true);
}
else
{
// enable
if (!image.sprite)
{
RefreshTextureViewer();
RefreshTextureDropdown();
}
SetImageSize();
textureViewerWanted = true;
textureViewerRoot.SetActive(true);
toggleButton.ButtonText.text = "Hide Material";
owner.ContentRoot.gameObject.SetActive(false);
}
}
void RefreshTextureViewer()
{
if (!this.activeTexture)
{
ExplorerCore.LogWarning($"Material has no active textures!");
savePathInput.Text = string.Empty;
return;
}
if (image.sprite)
UnityEngine.Object.Destroy(image.sprite);
string name = activeTexture.name;
if (string.IsNullOrEmpty(name))
name = "untitled";
savePathInput.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.png");
Sprite sprite = TextureHelper.CreateSprite(activeTexture);
image.sprite = sprite;
}
void RefreshTextureDropdown()
{
if (!textureDropdown)
return;
textureDropdown.options.Clear();
foreach (string key in textures.Keys)
textureDropdown.options.Add(new(key));
int i = 0;
foreach (Texture value in textures.Values)
{
if (activeTexture.ReferenceEqual(value))
{
textureDropdown.value = i;
break;
}
i++;
}
textureDropdown.RefreshShownValue();
}
void OnTextureDropdownChanged(int value)
{
Texture tex = textures.ElementAt(value).Value;
if (activeTexture.ReferenceEqual(tex))
return;
SetActiveTexture(tex);
RefreshTextureViewer();
}
void OnInspectorFinishResize()
{
SetImageSize();
}
void SetImageSize()
{
if (!imageLayout)
return;
RuntimeHelper.StartCoroutine(SetImageSizeCoro());
}
IEnumerator SetImageSizeCoro()
{
if (!activeTexture)
yield break;
// let unity rebuild layout etc
yield return null;
RectTransform imageRect = InspectorPanel.Instance.Rect;
float rectWidth = imageRect.rect.width - 25;
float rectHeight = imageRect.rect.height - 196;
// If our image is smaller than the viewport, just use 100% scaling
if (activeTexture.width < rectWidth && activeTexture.height < rectHeight)
{
imageLayout.minWidth = activeTexture.width;
imageLayout.minHeight = activeTexture.height;
}
else // we will need to scale down the image to fit
{
// get the ratio of our viewport dimensions to width and height
float viewWidthRatio = (float)((decimal)rectWidth / (decimal)activeTexture.width);
float viewHeightRatio = (float)((decimal)rectHeight / (decimal)activeTexture.height);
// if width needs to be scaled more than height
if (viewWidthRatio < viewHeightRatio)
{
imageLayout.minWidth = activeTexture.width * viewWidthRatio;
imageLayout.minHeight = activeTexture.height * viewWidthRatio;
}
else // if height needs to be scaled more than width
{
imageLayout.minWidth = activeTexture.width * viewHeightRatio;
imageLayout.minHeight = activeTexture.height * viewHeightRatio;
}
}
}
void OnSaveTextureClicked()
{
if (!activeTexture)
{
ExplorerCore.LogWarning("Texture is null, maybe it was destroyed?");
return;
}
if (string.IsNullOrEmpty(savePathInput.Text))
{
ExplorerCore.LogWarning("Save path cannot be empty!");
return;
}
string path = savePathInput.Text;
if (!path.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase))
path += ".png";
path = IOUtility.EnsureValidFilePath(path);
if (File.Exists(path))
File.Delete(path);
TextureHelper.SaveTextureAsPNG(activeTexture, path);
}
public override GameObject CreateContent(GameObject uiRoot)
{
GameObject ret = base.CreateContent(uiRoot);
// Button
toggleButton = UIFactory.CreateButton(UIRoot, "MaterialButton", "View Material", new Color(0.2f, 0.3f, 0.2f));
toggleButton.Transform.SetSiblingIndex(0);
UIFactory.SetLayoutElement(toggleButton.Component.gameObject, minHeight: 25, minWidth: 150);
toggleButton.OnClick += ToggleTextureViewer;
// Texture viewer
textureViewerRoot = UIFactory.CreateVerticalGroup(uiRoot, "MaterialViewer", false, false, true, true, 2, new Vector4(5, 5, 5, 5),
new Color(0.1f, 0.1f, 0.1f), childAlignment: TextAnchor.UpperLeft);
UIFactory.SetLayoutElement(textureViewerRoot, flexibleWidth: 9999, flexibleHeight: 9999);
// Buttons holder
GameObject dropdownRow = UIFactory.CreateHorizontalGroup(textureViewerRoot, "DropdownRow", false, true, true, true, 5, new(3, 3, 3, 3));
UIFactory.SetLayoutElement(dropdownRow, minHeight: 30, flexibleWidth: 9999);
Text dropdownLabel = UIFactory.CreateLabel(dropdownRow, "DropdownLabel", "Texture:");
UIFactory.SetLayoutElement(dropdownLabel.gameObject, minWidth: 75, minHeight: 25);
GameObject dropdownObj = UIFactory.CreateDropdown(dropdownRow, "TextureDropdown", out textureDropdown, "NOT SET", 13, OnTextureDropdownChanged);
UIFactory.SetLayoutElement(dropdownObj, minWidth: 350, minHeight: 25);
// Save helper
GameObject saveRowObj = UIFactory.CreateHorizontalGroup(textureViewerRoot, "SaveRow", false, false, true, true, 2, new Vector4(2, 2, 2, 2),
new Color(0.1f, 0.1f, 0.1f));
ButtonRef saveBtn = UIFactory.CreateButton(saveRowObj, "SaveButton", "Save .PNG", new Color(0.2f, 0.25f, 0.2f));
UIFactory.SetLayoutElement(saveBtn.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
saveBtn.OnClick += OnSaveTextureClicked;
savePathInput = UIFactory.CreateInputField(saveRowObj, "SaveInput", "...");
UIFactory.SetLayoutElement(savePathInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 9999);
// Actual texture viewer
GameObject imageViewport = UIFactory.CreateVerticalGroup(textureViewerRoot, "ImageViewport", false, false, true, true,
bgColor: new(1, 1, 1, 0), childAlignment: TextAnchor.MiddleCenter);
UIFactory.SetLayoutElement(imageViewport, flexibleWidth: 9999, flexibleHeight: 9999);
GameObject imageHolder = UIFactory.CreateUIObject("ImageHolder", imageViewport);
imageLayout = UIFactory.SetLayoutElement(imageHolder, 1, 1, 0, 0);
GameObject actualImageObj = UIFactory.CreateUIObject("ActualImage", imageHolder);
RectTransform actualRect = actualImageObj.GetComponent<RectTransform>();
actualRect.anchorMin = new(0, 0);
actualRect.anchorMax = new(1, 1);
image = actualImageObj.AddComponent<Image>();
textureViewerRoot.SetActive(false);
return ret;
}
}
}

View File

@ -17,29 +17,51 @@ namespace UnityExplorer.UI.Widgets
{ {
public class Texture2DWidget : UnityObjectWidget public class Texture2DWidget : UnityObjectWidget
{ {
private Texture2D TextureRef; Texture2D texture;
private float realWidth; bool shouldDestroyTexture;
private float realHeight;
private bool textureViewerWanted; bool textureViewerWanted;
private ButtonRef toggleButton; ButtonRef toggleButton;
private GameObject textureViewerRoot; GameObject textureViewerRoot;
private InputFieldRef savePathInput; InputFieldRef savePathInput;
private Image image; Image image;
private LayoutElement imageLayout; LayoutElement imageLayout;
public override void OnBorrowed(object target, Type targetType, ReflectionInspector inspector) public override void OnBorrowed(object target, Type targetType, ReflectionInspector inspector)
{ {
base.OnBorrowed(target, targetType, inspector); base.OnBorrowed(target, targetType, inspector);
TextureRef = target.TryCast<Texture2D>(); if (target.TryCast<Cubemap>() is Cubemap cubemap)
{
texture = TextureHelper.UnwrapCubemap(cubemap);
shouldDestroyTexture = true;
}
else if (target.TryCast<Sprite>() is Sprite sprite)
{
if (sprite.packingMode == SpritePackingMode.Tight)
texture = sprite.texture;
else
{
texture = TextureHelper.CopyTexture(sprite.texture, sprite.textureRect);
shouldDestroyTexture = true;
}
}
else if (target.TryCast<Image>() is Image image)
{
if (image.sprite.packingMode == SpritePackingMode.Tight)
texture = image.sprite.texture;
else
{
texture = TextureHelper.CopyTexture(image.sprite.texture, image.sprite.textureRect);
shouldDestroyTexture = true;
}
}
else
texture = target.TryCast<Texture2D>();
realWidth = TextureRef.width; if (textureViewerRoot)
realHeight = TextureRef.height; textureViewerRoot.transform.SetParent(inspector.UIRoot.transform);
if (this.textureViewerRoot)
this.textureViewerRoot.transform.SetParent(inspector.UIRoot.transform);
InspectorPanel.Instance.Dragger.OnFinishResize += OnInspectorFinishResize; InspectorPanel.Instance.Dragger.OnFinishResize += OnInspectorFinishResize;
} }
@ -48,21 +70,25 @@ namespace UnityExplorer.UI.Widgets
{ {
InspectorPanel.Instance.Dragger.OnFinishResize -= OnInspectorFinishResize; InspectorPanel.Instance.Dragger.OnFinishResize -= OnInspectorFinishResize;
TextureRef = null; if (shouldDestroyTexture)
UnityEngine.Object.Destroy(texture);
texture = null;
shouldDestroyTexture = false;
if (image.sprite) if (image.sprite)
GameObject.Destroy(image.sprite); UnityEngine.Object.Destroy(image.sprite);
if (textureViewerWanted) if (textureViewerWanted)
ToggleTextureViewer(); ToggleTextureViewer();
if (this.textureViewerRoot) if (textureViewerRoot)
this.textureViewerRoot.transform.SetParent(Pool<Texture2DWidget>.Instance.InactiveHolder.transform); textureViewerRoot.transform.SetParent(Pool<Texture2DWidget>.Instance.InactiveHolder.transform);
base.OnReturnToPool(); base.OnReturnToPool();
} }
private void ToggleTextureViewer() void ToggleTextureViewer()
{ {
if (textureViewerWanted) if (textureViewerWanted)
{ {
@ -71,7 +97,7 @@ namespace UnityExplorer.UI.Widgets
textureViewerRoot.SetActive(false); textureViewerRoot.SetActive(false);
toggleButton.ButtonText.text = "View Texture"; toggleButton.ButtonText.text = "View Texture";
ParentInspector.ContentRoot.SetActive(true); owner.ContentRoot.SetActive(true);
} }
else else
{ {
@ -85,30 +111,30 @@ namespace UnityExplorer.UI.Widgets
textureViewerRoot.SetActive(true); textureViewerRoot.SetActive(true);
toggleButton.ButtonText.text = "Hide Texture"; toggleButton.ButtonText.text = "Hide Texture";
ParentInspector.ContentRoot.gameObject.SetActive(false); owner.ContentRoot.gameObject.SetActive(false);
} }
} }
private void SetupTextureViewer() void SetupTextureViewer()
{ {
if (!this.TextureRef) if (!this.texture)
return; return;
string name = TextureRef.name; string name = texture.name;
if (string.IsNullOrEmpty(name)) if (string.IsNullOrEmpty(name))
name = "untitled"; name = "untitled";
savePathInput.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.png"); savePathInput.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.png");
Sprite sprite = TextureHelper.CreateSprite(TextureRef); Sprite sprite = TextureHelper.CreateSprite(texture);
image.sprite = sprite; image.sprite = sprite;
} }
private void OnInspectorFinishResize() void OnInspectorFinishResize()
{ {
SetImageSize(); SetImageSize();
} }
private void SetImageSize() void SetImageSize()
{ {
if (!imageLayout) if (!imageLayout)
return; return;
@ -127,34 +153,34 @@ namespace UnityExplorer.UI.Widgets
float rectHeight = imageRect.rect.height - 196; float rectHeight = imageRect.rect.height - 196;
// If our image is smaller than the viewport, just use 100% scaling // If our image is smaller than the viewport, just use 100% scaling
if (realWidth < rectWidth && realHeight < rectHeight) if (texture.width < rectWidth && texture.height < rectHeight)
{ {
imageLayout.minWidth = realWidth; imageLayout.minWidth = texture.width;
imageLayout.minHeight = realHeight; imageLayout.minHeight = texture.height;
} }
else // we will need to scale down the image to fit else // we will need to scale down the image to fit
{ {
// get the ratio of our viewport dimensions to width and height // get the ratio of our viewport dimensions to width and height
float viewWidthRatio = (float)((decimal)rectWidth / (decimal)realWidth); float viewWidthRatio = (float)((decimal)rectWidth / (decimal)texture.width);
float viewHeightRatio = (float)((decimal)rectHeight / (decimal)realHeight); float viewHeightRatio = (float)((decimal)rectHeight / (decimal)texture.height);
// if width needs to be scaled more than height // if width needs to be scaled more than height
if (viewWidthRatio < viewHeightRatio) if (viewWidthRatio < viewHeightRatio)
{ {
imageLayout.minWidth = realWidth * viewWidthRatio; imageLayout.minWidth = texture.width * viewWidthRatio;
imageLayout.minHeight = realHeight * viewWidthRatio; imageLayout.minHeight = texture.height * viewWidthRatio;
} }
else // if height needs to be scaled more than width else // if height needs to be scaled more than width
{ {
imageLayout.minWidth = realWidth * viewHeightRatio; imageLayout.minWidth = texture.width * viewHeightRatio;
imageLayout.minHeight = realHeight * viewHeightRatio; imageLayout.minHeight = texture.height * viewHeightRatio;
} }
} }
} }
private void OnSaveTextureClicked() void OnSaveTextureClicked()
{ {
if (!TextureRef) if (!texture)
{ {
ExplorerCore.LogWarning("Texture is null, maybe it was destroyed?"); ExplorerCore.LogWarning("Texture is null, maybe it was destroyed?");
return; return;
@ -175,18 +201,7 @@ namespace UnityExplorer.UI.Widgets
if (File.Exists(path)) if (File.Exists(path))
File.Delete(path); File.Delete(path);
Texture2D tex = TextureRef; TextureHelper.SaveTextureAsPNG(texture, path);
if (!TextureHelper.IsReadable(tex))
tex = TextureHelper.ForceReadTexture(tex);
byte[] data = TextureHelper.EncodeToPNG(tex);
File.WriteAllBytes(path, data);
if (tex != TextureRef)
{
// cleanup temp texture if we had to force-read it.
GameObject.Destroy(tex);
}
} }
public override GameObject CreateContent(GameObject uiRoot) public override GameObject CreateContent(GameObject uiRoot)

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.Inspectors; using UnityExplorer.Inspectors;
@ -11,9 +12,9 @@ namespace UnityExplorer.UI.Widgets
{ {
public class UnityObjectWidget : IPooledObject public class UnityObjectWidget : IPooledObject
{ {
public UnityEngine.Object UnityObjectRef; public UnityEngine.Object unityObject;
public Component ComponentRef; public Component component;
public ReflectionInspector ParentInspector; public ReflectionInspector owner;
protected ButtonRef gameObjectButton; protected ButtonRef gameObjectButton;
protected InputFieldRef nameInput; protected InputFieldRef nameInput;
@ -30,8 +31,14 @@ namespace UnityExplorer.UI.Widgets
UnityObjectWidget widget = target switch UnityObjectWidget widget = target switch
{ {
Texture2D => Pool<Texture2DWidget>.Borrow(), Texture2D or Cubemap => Pool<Texture2DWidget>.Borrow(),
Sprite s when s.texture => Pool<Texture2DWidget>.Borrow(),
Image i when i.sprite?.texture => Pool<Texture2DWidget>.Borrow(),
Material when MaterialWidget.MaterialWidgetSupported => Pool<MaterialWidget>.Borrow(),
AudioClip => Pool<AudioClipWidget>.Borrow(), AudioClip => Pool<AudioClipWidget>.Borrow(),
_ => Pool<UnityObjectWidget>.Borrow() _ => Pool<UnityObjectWidget>.Borrow()
}; };
@ -42,7 +49,7 @@ namespace UnityExplorer.UI.Widgets
public virtual void OnBorrowed(object target, Type targetType, ReflectionInspector inspector) public virtual void OnBorrowed(object target, Type targetType, ReflectionInspector inspector)
{ {
this.ParentInspector = inspector ?? throw new ArgumentNullException(nameof(inspector)); this.owner = inspector;
if (!this.UIRoot) if (!this.UIRoot)
CreateContent(inspector.UIRoot); CreateContent(inspector.UIRoot);
@ -51,15 +58,15 @@ namespace UnityExplorer.UI.Widgets
this.UIRoot.transform.SetSiblingIndex(inspector.UIRoot.transform.childCount - 2); this.UIRoot.transform.SetSiblingIndex(inspector.UIRoot.transform.childCount - 2);
UnityObjectRef = target.TryCast<UnityEngine.Object>(); unityObject = target.TryCast<UnityEngine.Object>();
UIRoot.SetActive(true); UIRoot.SetActive(true);
nameInput.Text = UnityObjectRef.name; nameInput.Text = unityObject.name;
instanceIdInput.Text = UnityObjectRef.GetInstanceID().ToString(); instanceIdInput.Text = unityObject.GetInstanceID().ToString();
if (typeof(Component).IsAssignableFrom(targetType)) if (typeof(Component).IsAssignableFrom(targetType))
{ {
ComponentRef = (Component)target.TryCast(typeof(Component)); component = (Component)target.TryCast(typeof(Component));
gameObjectButton.Component.gameObject.SetActive(true); gameObjectButton.Component.gameObject.SetActive(true);
} }
else else
@ -68,19 +75,20 @@ namespace UnityExplorer.UI.Widgets
public virtual void OnReturnToPool() public virtual void OnReturnToPool()
{ {
UnityObjectRef = null; unityObject = null;
ComponentRef = null; component = null;
ParentInspector = null; owner = null;
} }
// Update // Update
public virtual void Update() public virtual void Update()
{ {
if (this.UnityObjectRef) if (this.unityObject)
{ {
nameInput.Text = UnityObjectRef.name; nameInput.Text = unityObject.name;
ParentInspector.Tab.TabText.text = $"{ParentInspector.TabButtonText} \"{UnityObjectRef.name}\"";
owner.Tab.TabText.text = $"{owner.TabButtonText} \"{unityObject.name}\"";
} }
} }
@ -88,13 +96,13 @@ namespace UnityExplorer.UI.Widgets
private void OnGameObjectButtonClicked() private void OnGameObjectButtonClicked()
{ {
if (!ComponentRef) if (!component)
{ {
ExplorerCore.LogWarning("Component reference is null or destroyed!"); ExplorerCore.LogWarning("Component reference is null or destroyed!");
return; return;
} }
InspectorManager.Inspect(ComponentRef.gameObject); InspectorManager.Inspect(component.gameObject);
} }
// UI construction // UI construction

View File

@ -79,11 +79,11 @@
<!-- il2cpp nuget --> <!-- il2cpp nuget -->
<ItemGroup Condition="'$(Configuration)'=='ML_Cpp_net6' or '$(Configuration)'=='ML_Cpp_net472' or '$(Configuration)'=='STANDALONE_Cpp' or '$(Configuration)'=='BIE_Cpp'"> <ItemGroup Condition="'$(Configuration)'=='ML_Cpp_net6' or '$(Configuration)'=='ML_Cpp_net472' or '$(Configuration)'=='STANDALONE_Cpp' or '$(Configuration)'=='BIE_Cpp'">
<PackageReference Include="Il2CppAssemblyUnhollower.BaseLib" Version="0.4.22" /> <PackageReference Include="Il2CppAssemblyUnhollower.BaseLib" Version="0.4.22" />
<PackageReference Include="UniverseLib.IL2CPP" Version="1.3.14" /> <PackageReference Include="UniverseLib.IL2CPP" Version="1.4.2" />
</ItemGroup> </ItemGroup>
<!-- mono nuget --> <!-- mono nuget -->
<ItemGroup Condition="'$(Configuration)'=='BIE6_Mono' or '$(Configuration)'=='BIE5_Mono' or '$(Configuration)'=='ML_Mono' or '$(Configuration)'=='STANDALONE_Mono'"> <ItemGroup Condition="'$(Configuration)'=='BIE6_Mono' or '$(Configuration)'=='BIE5_Mono' or '$(Configuration)'=='ML_Mono' or '$(Configuration)'=='STANDALONE_Mono'">
<PackageReference Include="UniverseLib.Mono" Version="1.3.14" /> <PackageReference Include="UniverseLib.Mono" Version="1.4.2" />
</ItemGroup> </ItemGroup>
<!-- ~~~~~ ASSEMBLY REFERENCES ~~~~~ --> <!-- ~~~~~ ASSEMBLY REFERENCES ~~~~~ -->