├── .gitattributes
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.yaml
│ ├── config.yml
│ ├── enhancement.yaml
│ └── other.yaml
└── workflows
│ └── dotnet.yml
├── .gitignore
├── LICENSE
├── README.md
├── THIRDPARTY_LICENSES.md
├── UnityEditorPackage
├── LICENSE.md
├── README.md
├── Runtime
│ ├── 0Harmony.dll
│ ├── Mono.Cecil.Mdb.dll
│ ├── Mono.Cecil.Pdb.dll
│ ├── Mono.Cecil.Rocks.dll
│ ├── Mono.Cecil.dll
│ ├── MonoMod.RuntimeDetour.dll
│ ├── MonoMod.Utils.dll
│ ├── Tomlet.dll
│ ├── UnityExplorer.STANDALONE.Mono.dll
│ ├── UnityExplorer.prefab
│ ├── UniverseLib.Mono.dll
│ └── mcs.dll
├── Third Party Notices.md
└── package.json
├── build.ps1
├── img
├── icon.png
└── preview.png
├── lib
├── ILRepack.exe
├── interop
│ ├── Il2CppSystem.Core.dll
│ ├── Il2Cppmscorlib.dll
│ ├── UnityEngine.AssetBundleModule.dll
│ ├── UnityEngine.AudioModule.dll
│ ├── UnityEngine.CoreModule.dll
│ ├── UnityEngine.IMGUIModule.dll
│ ├── UnityEngine.PhysicsModule.dll
│ ├── UnityEngine.TextRenderingModule.dll
│ ├── UnityEngine.UI.dll
│ ├── UnityEngine.UIModule.dll
│ └── UnityEngine.dll
├── net35
│ ├── BepInEx.Core.dll
│ ├── BepInEx.Unity.dll
│ ├── BepInEx.dll
│ ├── MelonLoader.dll
│ ├── UnityEngine.UI.dll
│ ├── UnityEngine.dll
│ └── mcs.dll
├── net472
│ ├── BepInEx.Core.dll
│ └── BepInEx.IL2CPP.dll
├── net6
│ ├── MelonLoader.dll
│ ├── System.Runtime.dll
│ └── mcs.dll
└── unhollowed
│ ├── Il2CppSystem.Core.dll
│ ├── Il2Cppmscorlib.dll
│ ├── UnityEngine.AssetBundleModule.dll
│ ├── UnityEngine.AudioModule.dll
│ ├── UnityEngine.CoreModule.dll
│ ├── UnityEngine.IMGUIModule.dll
│ ├── UnityEngine.PhysicsModule.dll
│ ├── UnityEngine.TextRenderingModule.dll
│ ├── UnityEngine.UI.dll
│ ├── UnityEngine.UIModule.dll
│ └── UnityEngine.dll
└── src
├── CSConsole
├── CSAutoCompleter.cs
├── ConsoleController.cs
├── LexerBuilder.cs
├── Lexers
│ ├── CommentLexer.cs
│ ├── KeywordLexer.cs
│ ├── Lexer.cs
│ ├── NumberLexer.cs
│ ├── StringLexer.cs
│ └── SymbolLexer.cs
├── ScriptEvaluator.cs
└── ScriptInteraction.cs
├── CacheObject
├── CacheConfigEntry.cs
├── CacheConstructor.cs
├── CacheField.cs
├── CacheKeyValuePair.cs
├── CacheListEntry.cs
├── CacheMember.cs
├── CacheMemberFactory.cs
├── CacheMethod.cs
├── CacheObjectBase.cs
├── CacheProperty.cs
├── ICacheObjectController.cs
├── IValues
│ ├── InteractiveColor.cs
│ ├── InteractiveDictionary.cs
│ ├── InteractiveEnum.cs
│ ├── InteractiveList.cs
│ ├── InteractiveString.cs
│ ├── InteractiveValue.cs
│ └── InteractiveValueStruct.cs
└── Views
│ ├── CacheConfigCell.cs
│ ├── CacheKeyValuePairCell.cs
│ ├── CacheListEntryCell.cs
│ ├── CacheMemberCell.cs
│ └── CacheObjectCell.cs
├── Config
├── ConfigElement.cs
├── ConfigHandler.cs
├── ConfigManager.cs
├── IConfigElement.cs
└── InternalConfigHandler.cs
├── ExplorerBehaviour.cs
├── ExplorerCore.cs
├── Hooks
├── AddHookCell.cs
├── HookCell.cs
├── HookCreator.cs
├── HookInstance.cs
└── HookList.cs
├── Inspectors
├── GameObjectInspector.cs
├── InspectorBase.cs
├── InspectorManager.cs
├── InspectorTab.cs
├── MouseInspector.cs
├── MouseInspectors
│ ├── MouseInspectorBase.cs
│ ├── UiInspector.cs
│ └── WorldInspector.cs
└── ReflectionInspector.cs
├── Loader
├── BepInEx
│ ├── BepInExConfigHandler.cs
│ └── ExplorerBepInPlugin.cs
├── IExplorerLoader.cs
├── MelonLoader
│ ├── ExplorerMelonMod.cs
│ └── MelonLoaderConfigHandler.cs
└── Standalone
│ ├── Editor
│ ├── ExplorerEditorBehaviour.cs
│ └── ExplorerEditorLoader.cs
│ ├── ExplorerStandalone.cs
│ └── StandaloneConfigHandler.cs
├── ObjectExplorer
├── ObjectSearch.cs
├── SceneExplorer.cs
├── SceneHandler.cs
└── SearchProvider.cs
├── Properties
└── AssemblyInfo.cs
├── Runtime
├── Il2CppHelper.cs
├── MonoHelper.cs
├── UERuntimeHelper.cs
└── UnityCrashPrevention.cs
├── Tests
└── TestClass.cs
├── UI
├── DisplayManager.cs
├── ExplorerUIBase.cs
├── Notification.cs
├── Panels
│ ├── AutoCompleteModal.cs
│ ├── CSConsolePanel.cs
│ ├── ClipboardPanel.cs
│ ├── FreeCamPanel.cs
│ ├── HookManagerPanel.cs
│ ├── InspectorPanel.cs
│ ├── LogPanel.cs
│ ├── MouseInspectorResultsPanel.cs
│ ├── ObjectExplorerPanel.cs
│ ├── OptionsPanel.cs
│ ├── UEPanel.cs
│ └── UEPanelDragger.cs
├── UEPanelManager.cs
├── UIManager.cs
└── Widgets
│ ├── AutoComplete
│ ├── EnumCompleter.cs
│ ├── ISuggestionProvider.cs
│ ├── Suggestion.cs
│ └── TypeCompleter.cs
│ ├── EvaluateWidget
│ ├── BaseArgumentHandler.cs
│ ├── EvaluateWidget.cs
│ ├── GenericArgumentHandler.cs
│ ├── GenericConstructorWidget.cs
│ └── ParameterHandler.cs
│ ├── GameObjects
│ ├── AxisControl.cs
│ ├── ComponentCell.cs
│ ├── ComponentList.cs
│ ├── GameObjectControls.cs
│ ├── GameObjectInfoPanel.cs
│ ├── TransformControls.cs
│ ├── TransformType.cs
│ └── Vector3Control.cs
│ ├── TimeScaleWidget.cs
│ └── UnityObjects
│ ├── AudioClipWidget.cs
│ ├── MaterialWidget.cs
│ ├── Texture2DWidget.cs
│ └── UnityObjectWidget.cs
├── UnityExplorer.csproj
├── UnityExplorer.sln
└── nuget.config
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | ko_fi: sinaidev
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yaml:
--------------------------------------------------------------------------------
1 | name: Bug Report
2 | description: File a bug or crash report
3 | title: "[Bug]: "
4 | labels: [bug]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for submitting a bug report, please fill out as much detail as possible.
10 | - type: checkboxes
11 | id: latestversion
12 | attributes:
13 | label: Are you on the latest version of UnityExplorer?
14 | description: If not, you must update first.
15 | options:
16 | - label: Yes, I'm on the latest version of UnityExplorer.
17 | required: true
18 | - type: dropdown
19 | id: version
20 | attributes:
21 | label: Which release are you using?
22 | description: Please select your environment for UnityExplorer.
23 | options:
24 | - BepInEx IL2CPP
25 | - BepInEx 6.X Mono
26 | - BepInEx 5.X Mono
27 | - MelonLoader IL2CPP
28 | - MelonLoader Mono
29 | - Standalone IL2CPP
30 | - Standalone Mono
31 | validations:
32 | required: true
33 | - type: textarea
34 | id: game
35 | attributes:
36 | label: Which game did this occur on?
37 | description: Please tell us the name of the game. If it's a personal or private project, just let us know the Unity version.
38 | validations:
39 | required: true
40 | - type: textarea
41 | id: what-happened
42 | attributes:
43 | label: Describe the issue.
44 | description: What happened? Should something else have happened instead? Please provide steps to reproduce the issue if possible.
45 | placeholder: Tell us what you see!
46 | validations:
47 | required: true
48 | - type: textarea
49 | id: logs
50 | attributes:
51 | label: Relevant log output
52 | description: |
53 | Please copy and paste any relevant logs and stack traces.
54 | * Unity log: `%userprofile%\AppData\LocalLow\{Company}\{Game}\Player.log` or `output_log.txt`
55 | * BepInEx: `BepInEx\LogOutput.log`
56 | * MelonLoader: `MelonLoader\latest.log`
57 | * Standalone: `{DLL_Location}\UnityExplorer\Logs\` (pick the most recent one)
58 | render: shell
59 | validations:
60 | required: false
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/enhancement.yaml:
--------------------------------------------------------------------------------
1 | name: New feature or enhancement
2 | description: Suggest or discuss a feature or enhancement for UnityExplorer
3 | title: "[Enhancement]: "
4 | labels: [enhancement]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to discuss UnityExplorer, please provide as much detail as possible.
10 | - type: textarea
11 | id: description
12 | attributes:
13 | label: Describe the new feature or enhancement
14 | description: |
15 | Please go into as much detail as necessary in describing the new feature or enhancement.
16 | If providing examples or suggestions for the required C# code, please use syntax-highlighted code blocks.
17 | validations:
18 | required: true
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/other.yaml:
--------------------------------------------------------------------------------
1 | name: Other
2 | description: Something else?
3 | title: "[Other]: "
4 | labels: [Other]
5 | body:
6 | - type: textarea
7 | id: description
8 | attributes:
9 | label: Describe the issue
10 | description: |
11 | Please describe the issue in as much detail as possible.
12 | validations:
13 | required: true
14 |
--------------------------------------------------------------------------------
/.github/workflows/dotnet.yml:
--------------------------------------------------------------------------------
1 | name: Build UnityExplorer
2 |
3 | # Controls when the action will run.
4 | on:
5 | push:
6 | branches: [master]
7 | # Allows you to run this workflow manually from the Actions tab
8 | workflow_dispatch:
9 |
10 | jobs:
11 | build:
12 | runs-on: windows-latest
13 | if: "!contains(github.event.head_commit.message, '-noci')"
14 |
15 | steps:
16 | # Setup
17 |
18 | - name: Checkout latest
19 | uses: actions/checkout@v2
20 |
21 | - name: Setup dotnet
22 | uses: actions/setup-dotnet@v2
23 | with:
24 | dotnet-version: '6.0.x'
25 | include-prerelease: true
26 |
27 | # Run build script
28 | - run: |
29 | ./build.ps1
30 |
31 | # Upload artifacts
32 |
33 | # BepInEx IL2CPP
34 | - uses: actions/upload-artifact@v2
35 | with:
36 | name: UnityExplorer.BepInEx.IL2CPP.zip
37 | path: ./Release/UnityExplorer.BepInEx.IL2CPP/
38 |
39 | # BepInEx IL2CPP CoreCLR
40 | - uses: actions/upload-artifact@v2
41 | with:
42 | name: UnityExplorer.BepInEx.IL2CPP.CoreCLR.zip
43 | path: ./Release/UnityExplorer.BepInEx.IL2CPP.CoreCLR/
44 |
45 | # BepInEx 5 Mono
46 | - uses: actions/upload-artifact@v2
47 | with:
48 | name: UnityExplorer.BepInEx5.Mono.zip
49 | path: ./Release/UnityExplorer.BepInEx5.Mono/
50 |
51 | # BepInEx 6 Mono
52 | - uses: actions/upload-artifact@v2
53 | with:
54 | name: UnityExplorer.BepInEx6.Mono.zip
55 | path: ./Release/UnityExplorer.BepInEx6.Mono/
56 |
57 | # Editor
58 | - uses: actions/upload-artifact@v2
59 | with:
60 | name: UnityExplorer.Editor.zip
61 | path: ./UnityEditorPackage/
62 |
63 | # MelonLoader IL2CPP net6preview
64 | - uses: actions/upload-artifact@v2
65 | with:
66 | name: UnityExplorer.MelonLoader.IL2CPP.net6preview.zip
67 | path: ./Release/UnityExplorer.MelonLoader.IL2CPP.net6preview/
68 |
69 | # MelonLoader IL2CPP net472
70 | - uses: actions/upload-artifact@v2
71 | with:
72 | name: UnityExplorer.MelonLoader.IL2CPP.zip
73 | path: ./Release/UnityExplorer.MelonLoader.IL2CPP/
74 |
75 | # MelonLoader Mono
76 | - uses: actions/upload-artifact@v2
77 | with:
78 | name: UnityExplorer.MelonLoader.Mono.zip
79 | path: ./Release/UnityExplorer.MelonLoader.Mono/
80 |
81 | # Standalone Il2Cpp
82 | - uses: actions/upload-artifact@v2
83 | with:
84 | name: UnityExplorer.Standalone.IL2CPP.zip
85 | path: ./Release/UnityExplorer.Standalone.IL2CPP/
86 |
87 | # Standalone Mono
88 | - uses: actions/upload-artifact@v2
89 | with:
90 | name: UnityExplorer.Standalone.Mono.zip
91 | path: ./Release/UnityExplorer.Standalone.Mono/
92 |
93 |
--------------------------------------------------------------------------------
/UnityEditorPackage/README.md:
--------------------------------------------------------------------------------
1 | # UnityExplorer
2 |
3 | 🔍 An in-game UI for exploring, debugging and modifying Unity games.
4 | ✔️ Supports most Unity versions from 5.2 to 2021+ (IL2CPP and Mono).
5 | ✨ Powered by [UniverseLib](https://github.com/sinai-dev/UniverseLib)
6 |
7 | # Setup
8 |
9 | * Install this package, either by using the Package Manager and importing the `package.json` file, or by manually dragging this folder into your Assets folder.
10 | * Drag the `Runtime/UnityExplorer` prefab into your scene, or create a GameObject and add the `Explorer Editor Behaviour` script to it.
11 |
12 | # Features
13 |
14 | ### Object Explorer
15 |
16 | * Use the Scene Explorer tab to traverse the active scenes, as well as the DontDestroyOnLoad and HideAndDontSave objects.
17 | * 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.
18 | * You can use the Scene Loader to easily load any of the scenes in the build (may not work for Unity 5.X games)
19 | * Use the Object Search tab to search for Unity objects (including GameObjects, Components, etc), C# Singletons or Static Classes.
20 | * Use the UnityObject search to look for any objects which derive from `UnityEngine.Object`, with optional filters
21 | * 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.
22 |
23 | ### Inspector
24 |
25 | 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.
26 |
27 | * The GameObject Inspector (tab prefix `[G]`) is used to inspect a `GameObject`, and to see and manipulate its Transform and Components.
28 | * You can edit any of the input fields in the inspector (excluding readonly fields) and press Enter to apply your changes. You can also do this to the GameObject path as a way to change the GameObject's parent. Press the Escape key to cancel your edits.
29 | * note: When inspecting a GameObject with a Canvas, the transform controls may be overridden by the RectTransform anchors.
30 | * The Reflection Inspectors (tab prefix `[R]` and `[S]`) are used for everything else
31 | * Automatic updating is not enabled by default, and you must press Apply for any changes you make to take effect.
32 | * Press the `▼` button to expand certain values such as strings, enums, lists, dictionaries, some structs, etc
33 | * Use the filters at the top to quickly find the members you are looking for
34 | * 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
35 |
36 | ### C# Console
37 |
38 | * The C# Console uses the `Mono.CSharp.Evaluator` to define temporary classes or run immediate REPL code.
39 | * 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).
40 | * See the "Help" dropdown in the C# console menu for more detailed information.
41 |
42 | ### Hook Manager
43 |
44 | * The Hooks panel allows you to hook methods at the click of a button for debugging purposes.
45 | * Simply enter any class (generic types not yet supported) and hook the methods you want from the menu.
46 | * 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`). You can define multiple patches if you wish.
47 |
48 | ### Mouse-Inspect
49 |
50 | * The "Mouse Inspect" dropdown in the "Inspector" panel allows you to inspect objects under the mouse.
51 | * World: uses Physics.Raycast to look for Colliders
52 | * UI: uses GraphicRaycasters to find UI objects
53 |
54 | ### Clipboard
55 |
56 | * The "Clipboard" panel allows you to see your current paste value, or clear it (resets it to `null`)
57 | * Can copy the value from any member in a Reflection Inspector, Enumerable or Dictionary, and from the target of any Inspector tab
58 | * Can paste values onto any member in a Reflection Inspector
59 | * Non-parsable arguments in Method/Property Evaluators allow pasting values
60 | * The C# Console has helper methods `Copy(obj)` and `Paste()` for accessing the Clipboard
61 |
62 | ### Settings
63 |
64 | * You can change the settings via the "Options" tab of the menu, or directly from the config file.
65 | * /sinai-dev-UnityExplorer~/config.cfg
66 |
67 | # Acknowledgments
68 |
69 | * [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.
70 | * [Geoffrey Horsington](https://github.com/ghorsington) for [mcs-unity](https://github.com/sinai-dev/mcs-unity) \[no license\], used as the `Mono.CSharp` reference for the C# Console.
71 |
72 | ### Disclaimer
73 |
74 | UnityExplorer is in no way associated with Unity Technologies. "Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere.
75 |
--------------------------------------------------------------------------------
/UnityEditorPackage/Runtime/0Harmony.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/UnityEditorPackage/Runtime/0Harmony.dll
--------------------------------------------------------------------------------
/UnityEditorPackage/Runtime/Mono.Cecil.Mdb.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/UnityEditorPackage/Runtime/Mono.Cecil.Mdb.dll
--------------------------------------------------------------------------------
/UnityEditorPackage/Runtime/Mono.Cecil.Pdb.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/UnityEditorPackage/Runtime/Mono.Cecil.Pdb.dll
--------------------------------------------------------------------------------
/UnityEditorPackage/Runtime/Mono.Cecil.Rocks.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/UnityEditorPackage/Runtime/Mono.Cecil.Rocks.dll
--------------------------------------------------------------------------------
/UnityEditorPackage/Runtime/Mono.Cecil.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/UnityEditorPackage/Runtime/Mono.Cecil.dll
--------------------------------------------------------------------------------
/UnityEditorPackage/Runtime/MonoMod.RuntimeDetour.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/UnityEditorPackage/Runtime/MonoMod.RuntimeDetour.dll
--------------------------------------------------------------------------------
/UnityEditorPackage/Runtime/MonoMod.Utils.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/UnityEditorPackage/Runtime/MonoMod.Utils.dll
--------------------------------------------------------------------------------
/UnityEditorPackage/Runtime/Tomlet.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/UnityEditorPackage/Runtime/Tomlet.dll
--------------------------------------------------------------------------------
/UnityEditorPackage/Runtime/UnityExplorer.STANDALONE.Mono.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/UnityEditorPackage/Runtime/UnityExplorer.STANDALONE.Mono.dll
--------------------------------------------------------------------------------
/UnityEditorPackage/Runtime/UnityExplorer.prefab:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!1 &2342243352467007562
4 | GameObject:
5 | m_ObjectHideFlags: 0
6 | m_CorrespondingSourceObject: {fileID: 0}
7 | m_PrefabInstance: {fileID: 0}
8 | m_PrefabAsset: {fileID: 0}
9 | serializedVersion: 6
10 | m_Component:
11 | - component: {fileID: 2342243352467007560}
12 | - component: {fileID: 2342243352467007563}
13 | m_Layer: 0
14 | m_Name: UnityExplorer
15 | m_TagString: Untagged
16 | m_Icon: {fileID: 0}
17 | m_NavMeshLayer: 0
18 | m_StaticEditorFlags: 0
19 | m_IsActive: 1
20 | --- !u!4 &2342243352467007560
21 | Transform:
22 | m_ObjectHideFlags: 0
23 | m_CorrespondingSourceObject: {fileID: 0}
24 | m_PrefabInstance: {fileID: 0}
25 | m_PrefabAsset: {fileID: 0}
26 | m_GameObject: {fileID: 2342243352467007562}
27 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
28 | m_LocalPosition: {x: 0, y: 0, z: 0}
29 | m_LocalScale: {x: 1, y: 1, z: 1}
30 | m_Children: []
31 | m_Father: {fileID: 0}
32 | m_RootOrder: 0
33 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
34 | --- !u!114 &2342243352467007563
35 | MonoBehaviour:
36 | m_ObjectHideFlags: 0
37 | m_CorrespondingSourceObject: {fileID: 0}
38 | m_PrefabInstance: {fileID: 0}
39 | m_PrefabAsset: {fileID: 0}
40 | m_GameObject: {fileID: 2342243352467007562}
41 | m_Enabled: 1
42 | m_EditorHideFlags: 0
43 | m_Script: {fileID: 255528132, guid: 7e940c577136f52428020c8e128f06f3, type: 3}
44 | m_Name:
45 | m_EditorClassIdentifier:
46 |
--------------------------------------------------------------------------------
/UnityEditorPackage/Runtime/UniverseLib.Mono.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/UnityEditorPackage/Runtime/UniverseLib.Mono.dll
--------------------------------------------------------------------------------
/UnityEditorPackage/Runtime/mcs.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/UnityEditorPackage/Runtime/mcs.dll
--------------------------------------------------------------------------------
/UnityEditorPackage/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.sinai-dev.unityexplorer",
3 | "version": "4.7.12",
4 | "displayName": "UnityExplorer",
5 | "description": "An in-game UI for exploring, debugging and modifying Unity games.",
6 | "unity": "2017.1",
7 | "documentationUrl": "https://github.com/sinai-dev/UnityExplorer",
8 | "changelogUrl": "https://github.com/sinai-dev/UnityExplorer/releases",
9 | "licensesUrl": "https://github.com/sinai-dev/UnityExplorer/blob/master/LICENSE",
10 | "keywords": [
11 | "inspector",
12 | "debug",
13 | "runtime"
14 | ],
15 | "author": {
16 | "name": "Sinai",
17 | "url": "https://github.com/sinai-dev"
18 | }
19 | }
--------------------------------------------------------------------------------
/img/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/img/icon.png
--------------------------------------------------------------------------------
/img/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/img/preview.png
--------------------------------------------------------------------------------
/lib/ILRepack.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/ILRepack.exe
--------------------------------------------------------------------------------
/lib/interop/Il2CppSystem.Core.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/interop/Il2CppSystem.Core.dll
--------------------------------------------------------------------------------
/lib/interop/Il2Cppmscorlib.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/interop/Il2Cppmscorlib.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.AssetBundleModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/interop/UnityEngine.AssetBundleModule.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.AudioModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/interop/UnityEngine.AudioModule.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.CoreModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/interop/UnityEngine.CoreModule.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.IMGUIModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/interop/UnityEngine.IMGUIModule.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.PhysicsModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/interop/UnityEngine.PhysicsModule.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.TextRenderingModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/interop/UnityEngine.TextRenderingModule.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.UI.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/interop/UnityEngine.UI.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.UIModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/interop/UnityEngine.UIModule.dll
--------------------------------------------------------------------------------
/lib/interop/UnityEngine.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/interop/UnityEngine.dll
--------------------------------------------------------------------------------
/lib/net35/BepInEx.Core.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/net35/BepInEx.Core.dll
--------------------------------------------------------------------------------
/lib/net35/BepInEx.Unity.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/net35/BepInEx.Unity.dll
--------------------------------------------------------------------------------
/lib/net35/BepInEx.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/net35/BepInEx.dll
--------------------------------------------------------------------------------
/lib/net35/MelonLoader.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/net35/MelonLoader.dll
--------------------------------------------------------------------------------
/lib/net35/UnityEngine.UI.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/net35/UnityEngine.UI.dll
--------------------------------------------------------------------------------
/lib/net35/UnityEngine.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/net35/UnityEngine.dll
--------------------------------------------------------------------------------
/lib/net35/mcs.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/net35/mcs.dll
--------------------------------------------------------------------------------
/lib/net472/BepInEx.Core.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/net472/BepInEx.Core.dll
--------------------------------------------------------------------------------
/lib/net472/BepInEx.IL2CPP.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/net472/BepInEx.IL2CPP.dll
--------------------------------------------------------------------------------
/lib/net6/MelonLoader.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/net6/MelonLoader.dll
--------------------------------------------------------------------------------
/lib/net6/System.Runtime.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/net6/System.Runtime.dll
--------------------------------------------------------------------------------
/lib/net6/mcs.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/net6/mcs.dll
--------------------------------------------------------------------------------
/lib/unhollowed/Il2CppSystem.Core.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/unhollowed/Il2CppSystem.Core.dll
--------------------------------------------------------------------------------
/lib/unhollowed/Il2Cppmscorlib.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/unhollowed/Il2Cppmscorlib.dll
--------------------------------------------------------------------------------
/lib/unhollowed/UnityEngine.AssetBundleModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/unhollowed/UnityEngine.AssetBundleModule.dll
--------------------------------------------------------------------------------
/lib/unhollowed/UnityEngine.AudioModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/unhollowed/UnityEngine.AudioModule.dll
--------------------------------------------------------------------------------
/lib/unhollowed/UnityEngine.CoreModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/unhollowed/UnityEngine.CoreModule.dll
--------------------------------------------------------------------------------
/lib/unhollowed/UnityEngine.IMGUIModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/unhollowed/UnityEngine.IMGUIModule.dll
--------------------------------------------------------------------------------
/lib/unhollowed/UnityEngine.PhysicsModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/unhollowed/UnityEngine.PhysicsModule.dll
--------------------------------------------------------------------------------
/lib/unhollowed/UnityEngine.TextRenderingModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/unhollowed/UnityEngine.TextRenderingModule.dll
--------------------------------------------------------------------------------
/lib/unhollowed/UnityEngine.UI.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/unhollowed/UnityEngine.UI.dll
--------------------------------------------------------------------------------
/lib/unhollowed/UnityEngine.UIModule.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/unhollowed/UnityEngine.UIModule.dll
--------------------------------------------------------------------------------
/lib/unhollowed/UnityEngine.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GrahamKracker/UnityExplorer/5b72210c3923a2776b14fc866c9cf46dc586a346/lib/unhollowed/UnityEngine.dll
--------------------------------------------------------------------------------
/src/CSConsole/CSAutoCompleter.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using UnityExplorer.CSConsole.Lexers;
3 | using UnityExplorer.UI.Panels;
4 | using UnityExplorer.UI.Widgets.AutoComplete;
5 | using UniverseLib.UI.Models;
6 |
7 | namespace UnityExplorer.CSConsole
8 | {
9 | public class CSAutoCompleter : ISuggestionProvider
10 | {
11 | public InputFieldRef InputField => ConsoleController.Input;
12 |
13 | public bool AnchorToCaretPosition => true;
14 |
15 | bool ISuggestionProvider.AllowNavigation => true;
16 |
17 | public void OnSuggestionClicked(Suggestion suggestion)
18 | {
19 | ConsoleController.InsertSuggestionAtCaret(suggestion.UnderlyingValue);
20 | AutoCompleteModal.Instance.ReleaseOwnership(this);
21 | }
22 |
23 | private readonly HashSet delimiters = new()
24 | {
25 | '{',
26 | '}',
27 | ',',
28 | ';',
29 | '<',
30 | '>',
31 | '(',
32 | ')',
33 | '[',
34 | ']',
35 | '=',
36 | '|',
37 | '&',
38 | '?'
39 | };
40 |
41 | private readonly List suggestions = new();
42 |
43 | public void CheckAutocompletes()
44 | {
45 | if (string.IsNullOrEmpty(InputField.Text))
46 | {
47 | AutoCompleteModal.Instance.ReleaseOwnership(this);
48 | return;
49 | }
50 |
51 | suggestions.Clear();
52 |
53 | int caret = Math.Max(0, Math.Min(InputField.Text.Length - 1, InputField.Component.caretPosition - 1));
54 | int startIdx = caret;
55 |
56 | // If the character at the caret index is whitespace or delimiter,
57 | // or if the next character (if it exists) is not whitespace,
58 | // then we don't want to provide suggestions.
59 | if (char.IsWhiteSpace(InputField.Text[caret])
60 | || delimiters.Contains(InputField.Text[caret])
61 | || (InputField.Text.Length > caret + 1 && !char.IsWhiteSpace(InputField.Text[caret + 1])))
62 | {
63 | AutoCompleteModal.Instance.ReleaseOwnership(this);
64 | return;
65 | }
66 |
67 | // get the current composition string (from caret back to last delimiter)
68 | while (startIdx > 0)
69 | {
70 | startIdx--;
71 | char c = InputField.Text[startIdx];
72 | if (delimiters.Contains(c) || char.IsWhiteSpace(c))
73 | {
74 | startIdx++;
75 | break;
76 | }
77 | }
78 | string input = InputField.Text.Substring(startIdx, caret - startIdx + 1);
79 |
80 | // Get MCS completions
81 |
82 | string[] evaluatorCompletions = ConsoleController.Evaluator.GetCompletions(input, out string prefix);
83 |
84 | if (evaluatorCompletions != null && evaluatorCompletions.Any())
85 | {
86 | suggestions.AddRange(from completion in evaluatorCompletions
87 | select new Suggestion(GetHighlightString(prefix, completion), completion));
88 | }
89 |
90 | // Get manual namespace completions
91 |
92 | foreach (string ns in ReflectionUtility.AllNamespaces)
93 | {
94 | if (ns.StartsWith(input))
95 | {
96 | if (!namespaceHighlights.ContainsKey(ns))
97 | namespaceHighlights.Add(ns, $"{ns}");
98 |
99 | string completion = ns.Substring(input.Length, ns.Length - input.Length);
100 | suggestions.Add(new Suggestion(namespaceHighlights[ns], completion));
101 | }
102 | }
103 |
104 | // Get manual keyword completions
105 |
106 | foreach (string kw in KeywordLexer.keywords)
107 | {
108 | if (kw.StartsWith(input))// && kw.Length > input.Length)
109 | {
110 | if (!keywordHighlights.ContainsKey(kw))
111 | keywordHighlights.Add(kw, $"{kw}");
112 |
113 | string completion = kw.Substring(input.Length, kw.Length - input.Length);
114 | suggestions.Add(new Suggestion(keywordHighlights[kw], completion));
115 | }
116 | }
117 |
118 | if (suggestions.Any())
119 | {
120 | AutoCompleteModal.TakeOwnership(this);
121 | AutoCompleteModal.Instance.SetSuggestions(suggestions);
122 | }
123 | else
124 | {
125 | AutoCompleteModal.Instance.ReleaseOwnership(this);
126 | }
127 | }
128 |
129 |
130 | private readonly Dictionary namespaceHighlights = new();
131 |
132 | private readonly Dictionary keywordHighlights = new();
133 |
134 | private readonly StringBuilder highlightBuilder = new();
135 | private const string OPEN_HIGHLIGHT = "";
136 |
137 | private string GetHighlightString(string prefix, string completion)
138 | {
139 | highlightBuilder.Clear();
140 | highlightBuilder.Append(OPEN_HIGHLIGHT);
141 | highlightBuilder.Append(prefix);
142 | highlightBuilder.Append(SignatureHighlighter.CLOSE_COLOR);
143 | highlightBuilder.Append(completion);
144 | return highlightBuilder.ToString();
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/CSConsole/Lexers/CommentLexer.cs:
--------------------------------------------------------------------------------
1 | namespace UnityExplorer.CSConsole.Lexers
2 | {
3 | public class CommentLexer : Lexer
4 | {
5 | private enum CommentType
6 | {
7 | Line,
8 | Block
9 | }
10 |
11 | // forest green
12 | protected override Color HighlightColor => new(0.34f, 0.65f, 0.29f, 1.0f);
13 |
14 | public override bool TryMatchCurrent(LexerBuilder lexer)
15 | {
16 | if (lexer.Current == '/')
17 | {
18 | lexer.PeekNext();
19 | if (lexer.Current == '/')
20 | {
21 | // line comment. read to end of line or file.
22 | do
23 | {
24 | lexer.Commit();
25 | lexer.PeekNext();
26 | }
27 | while (!lexer.EndOrNewLine);
28 |
29 | return true;
30 | }
31 | else if (lexer.Current == '*')
32 | {
33 | // block comment, read until end of file or closing '*/'
34 | lexer.PeekNext();
35 | do
36 | {
37 | lexer.PeekNext();
38 | lexer.Commit();
39 | }
40 | while (!lexer.EndOfInput && !(lexer.Current == '/' && lexer.Previous == '*'));
41 |
42 | return true;
43 | }
44 | }
45 |
46 | return false;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/CSConsole/Lexers/KeywordLexer.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 |
3 | namespace UnityExplorer.CSConsole.Lexers
4 | {
5 | public class KeywordLexer : Lexer
6 | {
7 | // system blue
8 | protected override Color HighlightColor => new(0.33f, 0.61f, 0.83f, 1.0f);
9 |
10 | public static readonly HashSet keywords = new()
11 | {
12 | // reserved keywords
13 | "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue",
14 | "decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally",
15 | "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock",
16 | "long", "namespace", "new", "null", "object", "operator", "out", "override", "params", "private", "protected", "public",
17 | "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch",
18 | "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void",
19 | "volatile", "while",
20 | // contextual keywords
21 | "add", "and", "alias", "ascending", "async", "await", "by", "descending", "dynamic", "equals", "from", "get",
22 | "global", "group", "init", "into", "join", "let", "managed", "nameof", "not", "notnull", "on",
23 | "or", "orderby", "partial", "record", "remove", "select", "set", "unmanaged", "value", "var", "when", "where",
24 | "where", "with", "yield", "nint", "nuint"
25 | };
26 |
27 | public override bool TryMatchCurrent(LexerBuilder lexer)
28 | {
29 | char prev = lexer.Previous;
30 | char first = lexer.Current;
31 |
32 | // check for keywords
33 | if (lexer.IsDelimiter(prev, true) && char.IsLetter(first))
34 | {
35 | // can be a keyword...
36 |
37 | StringBuilder sb = new();
38 | sb.Append(lexer.Current);
39 | while (!lexer.EndOfInput && char.IsLetter(lexer.PeekNext()))
40 | sb.Append(lexer.Current);
41 |
42 | // next must be whitespace or delimiter
43 | if (!lexer.EndOfInput && !(char.IsWhiteSpace(lexer.Current) || lexer.IsDelimiter(lexer.Current)))
44 | return false;
45 |
46 | if (keywords.Contains(sb.ToString()))
47 | {
48 | if (!lexer.EndOfInput)
49 | lexer.RollbackBy(1);
50 | lexer.Commit();
51 | return true;
52 | }
53 |
54 | return false;
55 |
56 | }
57 | else
58 | return false;
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/CSConsole/Lexers/Lexer.cs:
--------------------------------------------------------------------------------
1 | namespace UnityExplorer.CSConsole.Lexers
2 | {
3 | public abstract class Lexer
4 | {
5 | public virtual IEnumerable Delimiters => Enumerable.Empty();
6 |
7 | protected abstract Color HighlightColor { get; }
8 |
9 | public string ColorTag => colorTag ?? (colorTag = "");
10 | private string colorTag;
11 |
12 | public abstract bool TryMatchCurrent(LexerBuilder lexer);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/CSConsole/Lexers/NumberLexer.cs:
--------------------------------------------------------------------------------
1 | namespace UnityExplorer.CSConsole.Lexers
2 | {
3 | public class NumberLexer : Lexer
4 | {
5 | // Maroon
6 | protected override Color HighlightColor => new(0.58f, 0.33f, 0.33f, 1.0f);
7 |
8 | private bool IsNumeric(char c) => char.IsNumber(c) || c == '.';
9 |
10 | public override bool TryMatchCurrent(LexerBuilder lexer)
11 | {
12 | // previous character must be whitespace or delimiter
13 | if (!lexer.IsDelimiter(lexer.Previous, true))
14 | return false;
15 |
16 | if (!IsNumeric(lexer.Current))
17 | return false;
18 |
19 | while (!lexer.EndOfInput)
20 | {
21 | lexer.Commit();
22 | if (!IsNumeric(lexer.PeekNext()))
23 | break;
24 | }
25 |
26 | return true;
27 | }
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/CSConsole/Lexers/StringLexer.cs:
--------------------------------------------------------------------------------
1 | namespace UnityExplorer.CSConsole.Lexers
2 | {
3 | public class StringLexer : Lexer
4 | {
5 | public override IEnumerable Delimiters => new[] { '"', '\'', };
6 |
7 | // orange
8 | protected override Color HighlightColor => new(0.79f, 0.52f, 0.32f, 1.0f);
9 |
10 | public override bool TryMatchCurrent(LexerBuilder lexer)
11 | {
12 | if (lexer.Current == '"')
13 | {
14 | if (lexer.Previous == '@')
15 | {
16 | // verbatim string, continue until un-escaped quote.
17 | while (!lexer.EndOfInput)
18 | {
19 | lexer.Commit();
20 | if (lexer.PeekNext() == '"')
21 | {
22 | lexer.Commit();
23 | // possibly the end, check for escaped quotes.
24 | // commit the character and flip the escape bool for each quote.
25 | bool escaped = false;
26 | while (lexer.PeekNext() == '"')
27 | {
28 | lexer.Commit();
29 | escaped = !escaped;
30 | }
31 | // if the last quote wasnt escaped, that was the end of the string.
32 | if (!escaped)
33 | break;
34 | }
35 | }
36 | }
37 | else
38 | {
39 | // normal string
40 | // continue until a quote which is not escaped, or end of input
41 |
42 | while (!lexer.EndOfInput)
43 | {
44 | lexer.Commit();
45 | lexer.PeekNext();
46 | if ((lexer.Current == '"') && lexer.Previous != '\\')
47 | {
48 | lexer.Commit();
49 | break;
50 | }
51 | }
52 | }
53 |
54 | return true;
55 | }
56 | else if (lexer.Current == '\'')
57 | {
58 | // char
59 |
60 | while (!lexer.EndOfInput)
61 | {
62 | lexer.Commit();
63 | lexer.PeekNext();
64 | if ((lexer.Current == '\'') && lexer.Previous != '\\')
65 | {
66 | lexer.Commit();
67 | break;
68 | }
69 | }
70 |
71 | return true;
72 | }
73 | else
74 | return false;
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/CSConsole/Lexers/SymbolLexer.cs:
--------------------------------------------------------------------------------
1 | namespace UnityExplorer.CSConsole.Lexers
2 | {
3 | public class SymbolLexer : Lexer
4 | {
5 | // silver
6 | protected override Color HighlightColor => new(0.6f, 0.6f, 0.6f);
7 |
8 | // all symbols are delimiters
9 | public override IEnumerable Delimiters => symbols.Where(it => it != '.'); // '.' is not a delimiter, only a separator.
10 |
11 | public static bool IsSymbol(char c) => symbols.Contains(c);
12 |
13 | public static readonly HashSet symbols = new()
14 | {
15 | '[', '{', '(', // open
16 | ']', '}', ')', // close
17 | '.', ',', ';', ':', '?', '@', // special
18 |
19 | // operators
20 | '+', '-', '*', '/', '%', '&', '|', '^', '~', '=', '<', '>', '!',
21 | };
22 |
23 | public override bool TryMatchCurrent(LexerBuilder lexer)
24 | {
25 | // previous character must be delimiter, whitespace, or alphanumeric.
26 | if (!lexer.IsDelimiter(lexer.Previous, true, true))
27 | return false;
28 |
29 | if (IsSymbol(lexer.Current))
30 | {
31 | lexer.Commit();
32 | return true;
33 | }
34 |
35 | return false;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/CSConsole/ScriptEvaluator.cs:
--------------------------------------------------------------------------------
1 | using Mono.CSharp;
2 | using UnityExplorer.Config;
3 |
4 | // Thanks to ManlyMarco for this
5 |
6 | namespace UnityExplorer.CSConsole
7 | {
8 | public class ScriptEvaluator : Evaluator, IDisposable
9 | {
10 | internal TextWriter _textWriter;
11 | internal static StreamReportPrinter _reportPrinter;
12 |
13 | private static readonly HashSet StdLib = new(StringComparer.InvariantCultureIgnoreCase)
14 | {
15 | "mscorlib",
16 | "System.Core",
17 | "System",
18 | "System.Xml"
19 | };
20 |
21 | public ScriptEvaluator(TextWriter tw) : base(BuildContext(tw))
22 | {
23 | _textWriter = tw;
24 |
25 | ImportAppdomainAssemblies();
26 | AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
27 | }
28 |
29 | public void Dispose()
30 | {
31 | AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
32 | _textWriter.Dispose();
33 | }
34 |
35 | private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
36 | {
37 | string name = args.LoadedAssembly.GetName().Name;
38 |
39 | if (StdLib.Contains(name))
40 | return;
41 |
42 | Reference(args.LoadedAssembly);
43 | }
44 |
45 | private void Reference(Assembly asm)
46 | {
47 | string name = asm.GetName().Name;
48 |
49 | if (name == "completions") // ignore assemblies generated by mcs' autocomplete.
50 | return;
51 |
52 | foreach (string blacklisted in ConfigManager.CSConsole_Assembly_Blacklist.Value.Split(';'))
53 | {
54 | string bl = blacklisted;
55 | if (bl.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
56 | bl = blacklisted.Substring(0, bl.Length - 4);
57 | if (string.Equals(bl, name, StringComparison.OrdinalIgnoreCase))
58 | return;
59 | }
60 |
61 | ReferenceAssembly(asm);
62 | }
63 |
64 | private static CompilerContext BuildContext(TextWriter tw)
65 | {
66 | _reportPrinter = new StreamReportPrinter(tw);
67 |
68 | CompilerSettings settings = new()
69 | {
70 | Version = LanguageVersion.Experimental,
71 | GenerateDebugInfo = false,
72 | StdLib = true,
73 | Target = Target.Library,
74 | WarningLevel = 0,
75 | EnhancedWarnings = false
76 | };
77 |
78 | return new CompilerContext(settings, _reportPrinter);
79 | }
80 |
81 | private void ImportAppdomainAssemblies()
82 | {
83 | foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
84 | {
85 | string name = assembly.GetName().Name;
86 | if (StdLib.Contains(name))
87 | continue;
88 |
89 | try
90 | {
91 | Reference(assembly);
92 | }
93 | catch // (Exception ex)
94 | {
95 | //ExplorerCore.LogWarning($"Excepting referencing '{name}': {ex.GetType()}.{ex.Message}");
96 | }
97 | }
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/CSConsole/ScriptInteraction.cs:
--------------------------------------------------------------------------------
1 | using HarmonyLib;
2 | using Mono.CSharp;
3 | using System.Collections;
4 | using System.Text;
5 | using UnityExplorer.UI.Panels;
6 |
7 | namespace UnityExplorer.CSConsole
8 | {
9 | public class ScriptInteraction : InteractiveBase
10 | {
11 | public static object CurrentTarget
12 | => InspectorManager.ActiveInspector?.Target;
13 |
14 | public static object[] AllTargets
15 | => InspectorManager.Inspectors.Select(it => it.Target).ToArray();
16 |
17 | public static void Log(object message)
18 | => ExplorerCore.Log(message);
19 |
20 | public static void Inspect(object obj)
21 | => InspectorManager.Inspect(obj);
22 |
23 | public static void Inspect(Type type)
24 | => InspectorManager.Inspect(type);
25 |
26 | public static Coroutine Start(IEnumerator ienumerator)
27 | => RuntimeHelper.StartCoroutine(ienumerator);
28 |
29 | public static void Stop(Coroutine coro)
30 | => RuntimeHelper.StopCoroutine(coro);
31 |
32 | public static void Copy(object obj)
33 | => ClipboardPanel.Copy(obj);
34 |
35 | public static object Paste()
36 | => ClipboardPanel.Current;
37 |
38 | public static void GetUsing()
39 | => Log(Evaluator.GetUsing());
40 |
41 | public static void GetVars()
42 | {
43 | string vars = Evaluator.GetVars()?.Trim();
44 | if (string.IsNullOrEmpty(vars))
45 | ExplorerCore.LogWarning("No variables seem to be defined!");
46 | else
47 | Log(vars);
48 | }
49 |
50 | public static void GetClasses()
51 | {
52 | if (AccessTools.Field(typeof(Evaluator), "source_file")
53 | .GetValue(Evaluator) is CompilationSourceFile sourceFile
54 | && sourceFile.Containers.Any())
55 | {
56 | StringBuilder sb = new();
57 | sb.Append($"There are {sourceFile.Containers.Count} defined classes:");
58 | foreach (TypeDefinition type in sourceFile.Containers.Where(it => it is TypeDefinition))
59 | {
60 | sb.Append($"\n\n{type.MemberName.Name}:");
61 | foreach (MemberCore member in type.Members)
62 | sb.Append($"\n\t- {member.AttributeTargets}: \"{member.MemberName.Name}\" ({member.ModFlags})");
63 | }
64 | Log(sb.ToString());
65 | }
66 | else
67 | ExplorerCore.LogWarning("No classes seem to be defined.");
68 |
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/CacheObject/CacheConfigEntry.cs:
--------------------------------------------------------------------------------
1 | using UnityExplorer.CacheObject.Views;
2 | using UnityExplorer.Config;
3 |
4 | namespace UnityExplorer.CacheObject
5 | {
6 | public class CacheConfigEntry : CacheObjectBase
7 | {
8 | public CacheConfigEntry(IConfigElement configElement)
9 | {
10 | this.RefConfigElement = configElement;
11 | this.FallbackType = configElement.ElementType;
12 |
13 | this.NameLabelText = $"{configElement.Name}" +
14 | $"\r\n{configElement.Description}";
15 | this.NameLabelTextRaw = string.Empty;
16 |
17 | configElement.OnValueChangedNotify += UpdateValueFromSource;
18 | }
19 |
20 | public IConfigElement RefConfigElement;
21 |
22 | public override bool ShouldAutoEvaluate => true;
23 | public override bool HasArguments => false;
24 | public override bool CanWrite => true;
25 |
26 | public void UpdateValueFromSource()
27 | {
28 | //if (RefConfigElement.BoxedValue.Equals(this.Value))
29 | // return;
30 |
31 | SetValueFromSource(RefConfigElement.BoxedValue);
32 |
33 | if (this.CellView != null)
34 | this.SetDataToCell(CellView);
35 | }
36 |
37 | public override void TrySetUserValue(object value)
38 | {
39 | this.Value = value;
40 | RefConfigElement.BoxedValue = value;
41 | }
42 |
43 | protected override bool TryAutoEvaluateIfUnitialized(CacheObjectCell cell) => true;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/CacheObject/CacheConstructor.cs:
--------------------------------------------------------------------------------
1 | using UnityExplorer.Inspectors;
2 |
3 | namespace UnityExplorer.CacheObject
4 | {
5 | public class CacheConstructor : CacheMember
6 | {
7 | public ConstructorInfo CtorInfo { get; }
8 | readonly Type typeForStructConstructor;
9 |
10 | public override Type DeclaringType => typeForStructConstructor ?? CtorInfo.DeclaringType;
11 | public override bool IsStatic => true;
12 | public override bool ShouldAutoEvaluate => false;
13 | public override bool CanWrite => false;
14 |
15 | public CacheConstructor(ConstructorInfo ci)
16 | {
17 | this.CtorInfo = ci;
18 | }
19 |
20 | public CacheConstructor(Type typeForStructConstructor)
21 | {
22 | this.typeForStructConstructor = typeForStructConstructor;
23 | }
24 |
25 | public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
26 | {
27 | Type ctorReturnType;
28 | // if is parameterless struct ctor
29 | if (typeForStructConstructor != null)
30 | {
31 | ctorReturnType = typeForStructConstructor;
32 | this.Owner = inspector;
33 |
34 | // eg. Vector3.Vector3()
35 | this.NameLabelText = SignatureHighlighter.Parse(typeForStructConstructor, false);
36 | NameLabelText += $".{NameLabelText}()";
37 |
38 | this.NameForFiltering = SignatureHighlighter.RemoveHighlighting(NameLabelText);
39 | this.NameLabelTextRaw = NameForFiltering;
40 | return;
41 | }
42 | else
43 | {
44 | base.SetInspectorOwner(inspector, member);
45 |
46 | Arguments = CtorInfo.GetParameters();
47 | ctorReturnType = CtorInfo.DeclaringType;
48 | }
49 |
50 | if (ctorReturnType.IsGenericTypeDefinition)
51 | GenericArguments = ctorReturnType.GetGenericArguments();
52 | }
53 |
54 | protected override object TryEvaluate()
55 | {
56 | try
57 | {
58 | Type returnType = DeclaringType;
59 |
60 | if (returnType.IsGenericTypeDefinition)
61 | returnType = DeclaringType.MakeGenericType(Evaluator.TryParseGenericArguments());
62 |
63 | object ret;
64 | if (HasArguments)
65 | ret = Activator.CreateInstance(returnType, Evaluator.TryParseArguments());
66 | else
67 | ret = Activator.CreateInstance(returnType, ArgumentUtility.EmptyArgs);
68 |
69 | LastException = null;
70 | return ret;
71 | }
72 | catch (Exception ex)
73 | {
74 | LastException = ex;
75 | return null;
76 | }
77 | }
78 |
79 | protected override void TrySetValue(object value) => throw new NotImplementedException("You can't set a constructor");
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/CacheObject/CacheField.cs:
--------------------------------------------------------------------------------
1 | using UnityExplorer.Inspectors;
2 |
3 | namespace UnityExplorer.CacheObject
4 | {
5 | public class CacheField : CacheMember
6 | {
7 | public FieldInfo FieldInfo { get; internal set; }
8 | public override Type DeclaringType => FieldInfo.DeclaringType;
9 | public override bool IsStatic => FieldInfo.IsStatic;
10 | public override bool CanWrite => m_canWrite ?? (bool)(m_canWrite = !(FieldInfo.IsLiteral && !FieldInfo.IsInitOnly));
11 | private bool? m_canWrite;
12 |
13 | public override bool ShouldAutoEvaluate => true;
14 |
15 | public CacheField(FieldInfo fi)
16 | {
17 | this.FieldInfo = fi;
18 | }
19 |
20 | public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
21 | {
22 | base.SetInspectorOwner(inspector, member);
23 | }
24 |
25 | protected override object TryEvaluate()
26 | {
27 | try
28 | {
29 | object ret = FieldInfo.GetValue(DeclaringInstance);
30 | LastException = null;
31 | return ret;
32 | }
33 | catch (Exception ex)
34 | {
35 | LastException = ex;
36 | return null;
37 | }
38 | }
39 |
40 | protected override void TrySetValue(object value)
41 | {
42 | try
43 | {
44 | FieldInfo.SetValue(DeclaringInstance, value);
45 | }
46 | catch (Exception ex)
47 | {
48 | ExplorerCore.LogWarning(ex);
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/CacheObject/CacheKeyValuePair.cs:
--------------------------------------------------------------------------------
1 | using UnityExplorer.CacheObject.IValues;
2 | using UnityExplorer.CacheObject.Views;
3 |
4 | namespace UnityExplorer.CacheObject
5 | {
6 | public class CacheKeyValuePair : CacheObjectBase
7 | {
8 | //public InteractiveList CurrentList { get; set; }
9 |
10 | public int DictIndex;
11 | public object DictKey;
12 | public object DisplayedKey;
13 |
14 | public bool KeyInputWanted;
15 | public bool InspectWanted;
16 | public string KeyLabelText;
17 | public string KeyInputText;
18 | public string KeyInputTypeText;
19 |
20 | public float DesiredKeyWidth;
21 | public float DesiredValueWidth;
22 |
23 | public override bool ShouldAutoEvaluate => true;
24 | public override bool HasArguments => false;
25 | public override bool CanWrite => Owner.CanWrite;
26 |
27 | public void SetDictOwner(InteractiveDictionary dict, int index)
28 | {
29 | this.Owner = dict;
30 | this.DictIndex = index;
31 | }
32 |
33 | public void SetKey(object key)
34 | {
35 | this.DictKey = key;
36 | this.DisplayedKey = key.TryCast();
37 |
38 | Type type = DisplayedKey.GetType();
39 | if (ParseUtility.CanParse(type))
40 | {
41 | KeyInputWanted = true;
42 | KeyInputText = ParseUtility.ToStringForInput(DisplayedKey, type);
43 | KeyInputTypeText = SignatureHighlighter.Parse(type, false);
44 | }
45 | else
46 | {
47 | KeyInputWanted = false;
48 | InspectWanted = type != typeof(bool) && !type.IsEnum;
49 | KeyLabelText = ToStringUtility.ToStringWithType(DisplayedKey, type, true);
50 | }
51 | }
52 |
53 | public override void SetDataToCell(CacheObjectCell cell)
54 | {
55 | base.SetDataToCell(cell);
56 |
57 | CacheKeyValuePairCell kvpCell = cell as CacheKeyValuePairCell;
58 |
59 | kvpCell.NameLabel.text = $"{DictIndex}:";
60 | kvpCell.HiddenNameLabel.Text = "";
61 | kvpCell.Image.color = DictIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor;
62 |
63 | if (KeyInputWanted)
64 | {
65 | kvpCell.KeyInputField.UIRoot.SetActive(true);
66 | kvpCell.KeyInputTypeLabel.gameObject.SetActive(true);
67 | kvpCell.KeyLabel.gameObject.SetActive(false);
68 | kvpCell.KeyInspectButton.Component.gameObject.SetActive(false);
69 |
70 | kvpCell.KeyInputField.Text = KeyInputText;
71 | kvpCell.KeyInputTypeLabel.text = KeyInputTypeText;
72 | }
73 | else
74 | {
75 | kvpCell.KeyInputField.UIRoot.SetActive(false);
76 | kvpCell.KeyInputTypeLabel.gameObject.SetActive(false);
77 | kvpCell.KeyLabel.gameObject.SetActive(true);
78 | kvpCell.KeyInspectButton.Component.gameObject.SetActive(InspectWanted);
79 |
80 | kvpCell.KeyLabel.text = KeyLabelText;
81 | }
82 | }
83 |
84 | public override void TrySetUserValue(object value)
85 | {
86 | (Owner as InteractiveDictionary).TrySetValueToKey(DictKey, value, DictIndex);
87 | }
88 |
89 |
90 | protected override bool TryAutoEvaluateIfUnitialized(CacheObjectCell cell) => true;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/CacheObject/CacheListEntry.cs:
--------------------------------------------------------------------------------
1 | using UnityExplorer.CacheObject.IValues;
2 | using UnityExplorer.CacheObject.Views;
3 |
4 | namespace UnityExplorer.CacheObject
5 | {
6 | public class CacheListEntry : CacheObjectBase
7 | {
8 | public int ListIndex;
9 |
10 | public override bool ShouldAutoEvaluate => true;
11 | public override bool HasArguments => false;
12 | public override bool CanWrite => Owner?.CanWrite ?? false;
13 |
14 | public void SetListOwner(InteractiveList list, int listIndex)
15 | {
16 | this.Owner = list;
17 | this.ListIndex = listIndex;
18 | }
19 |
20 | public override void SetDataToCell(CacheObjectCell cell)
21 | {
22 | base.SetDataToCell(cell);
23 |
24 | CacheListEntryCell listCell = cell as CacheListEntryCell;
25 |
26 | listCell.NameLabel.text = $"{ListIndex}:";
27 | listCell.HiddenNameLabel.Text = "";
28 | listCell.Image.color = ListIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor;
29 | }
30 |
31 | public override void TrySetUserValue(object value)
32 | {
33 | (Owner as InteractiveList).TrySetValueToIndex(value, this.ListIndex);
34 | }
35 |
36 | protected override bool TryAutoEvaluateIfUnitialized(CacheObjectCell cell) => true;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/CacheObject/CacheMethod.cs:
--------------------------------------------------------------------------------
1 | using UnityExplorer.Inspectors;
2 |
3 | namespace UnityExplorer.CacheObject
4 | {
5 | public class CacheMethod : CacheMember
6 | {
7 | public MethodInfo MethodInfo { get; }
8 | public override Type DeclaringType => MethodInfo.DeclaringType;
9 | public override bool CanWrite => false;
10 | public override bool IsStatic => MethodInfo.IsStatic;
11 |
12 | public override bool ShouldAutoEvaluate => false;
13 |
14 | public CacheMethod(MethodInfo mi)
15 | {
16 | this.MethodInfo = mi;
17 | }
18 |
19 | public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
20 | {
21 | base.SetInspectorOwner(inspector, member);
22 |
23 | Arguments = MethodInfo.GetParameters();
24 | if (MethodInfo.IsGenericMethod)
25 | GenericArguments = MethodInfo.GetGenericArguments();
26 | }
27 |
28 | protected override object TryEvaluate()
29 | {
30 | try
31 | {
32 | MethodInfo methodInfo = MethodInfo;
33 | if (methodInfo.IsGenericMethod)
34 | methodInfo = MethodInfo.MakeGenericMethod(Evaluator.TryParseGenericArguments());
35 |
36 | object ret;
37 | if (HasArguments)
38 | ret = methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments());
39 | else
40 | ret = methodInfo.Invoke(DeclaringInstance, ArgumentUtility.EmptyArgs);
41 | LastException = null;
42 | return ret;
43 | }
44 | catch (Exception ex)
45 | {
46 | LastException = ex;
47 | return null;
48 | }
49 | }
50 |
51 | protected override void TrySetValue(object value) => throw new NotImplementedException("You can't set a method");
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/CacheObject/CacheProperty.cs:
--------------------------------------------------------------------------------
1 | using UnityExplorer.Inspectors;
2 |
3 | namespace UnityExplorer.CacheObject
4 | {
5 | public class CacheProperty : CacheMember
6 | {
7 | public PropertyInfo PropertyInfo { get; internal set; }
8 | public override Type DeclaringType => PropertyInfo.DeclaringType;
9 | public override bool CanWrite => PropertyInfo.CanWrite;
10 | public override bool IsStatic => m_isStatic ?? (bool)(m_isStatic = PropertyInfo.GetAccessors(true)[0].IsStatic);
11 | private bool? m_isStatic;
12 |
13 | public override bool ShouldAutoEvaluate => !HasArguments;
14 |
15 | public CacheProperty(PropertyInfo pi)
16 | {
17 | this.PropertyInfo = pi;
18 | }
19 |
20 | public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
21 | {
22 | base.SetInspectorOwner(inspector, member);
23 |
24 | Arguments = PropertyInfo.GetIndexParameters();
25 | }
26 |
27 | protected override object TryEvaluate()
28 | {
29 | try
30 | {
31 | object ret;
32 | if (HasArguments)
33 | ret = PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments());
34 | else
35 | ret = PropertyInfo.GetValue(DeclaringInstance, null);
36 | LastException = null;
37 | return ret;
38 | }
39 | catch (Exception ex)
40 | {
41 | LastException = ex;
42 | return null;
43 | }
44 | }
45 |
46 | protected override void TrySetValue(object value)
47 | {
48 | if (!CanWrite)
49 | return;
50 |
51 | try
52 | {
53 | bool _static = PropertyInfo.GetAccessors(true)[0].IsStatic;
54 |
55 | if (HasArguments)
56 | PropertyInfo.SetValue(DeclaringInstance, value, Evaluator.TryParseArguments());
57 | else
58 | PropertyInfo.SetValue(DeclaringInstance, value, null);
59 | }
60 | catch (Exception ex)
61 | {
62 | ExplorerCore.LogWarning(ex);
63 | }
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/CacheObject/ICacheObjectController.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using UnityExplorer.CacheObject.Views;
3 |
4 | namespace UnityExplorer.CacheObject
5 | {
6 | public interface ICacheObjectController
7 | {
8 | CacheObjectBase ParentCacheObject { get; }
9 |
10 | object Target { get; }
11 | Type TargetType { get; }
12 |
13 | bool CanWrite { get; }
14 | }
15 |
16 | public static class CacheObjectControllerHelper
17 | {
18 | // Helper so that this doesn't need to be copy+pasted between each implementation of the interface
19 |
20 | public static void SetCell(CacheObjectCell cell, int index, IList cachedEntries, Action onDataSetToCell)
21 | {
22 | if (index < 0 || index >= cachedEntries.Count)
23 | {
24 | if (cell.Occupant != null)
25 | cell.Occupant.UnlinkFromView();
26 |
27 | cell.Disable();
28 | return;
29 | }
30 |
31 | CacheObjectBase entry = (CacheObjectBase)cachedEntries[index];
32 |
33 | if (entry.CellView != null && entry.CellView != cell)
34 | entry.UnlinkFromView();
35 |
36 | if (cell.Occupant != null && cell.Occupant != entry)
37 | cell.Occupant.UnlinkFromView();
38 |
39 | if (entry.CellView != cell)
40 | entry.SetView(cell);
41 |
42 | entry.SetDataToCell(cell);
43 |
44 | onDataSetToCell?.Invoke(cell);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/CacheObject/IValues/InteractiveString.cs:
--------------------------------------------------------------------------------
1 | using UnityExplorer.Config;
2 | using UniverseLib.UI;
3 | using UniverseLib.UI.Models;
4 |
5 | namespace UnityExplorer.CacheObject.IValues
6 | {
7 | public class InteractiveString : InteractiveValue
8 | {
9 | private string RealValue;
10 | public string EditedValue = "";
11 |
12 | public InputFieldRef inputField;
13 | public ButtonRef ApplyButton;
14 |
15 | public GameObject SaveFileRow;
16 | public InputFieldRef SaveFilePath;
17 |
18 | public override void OnBorrowed(CacheObjectBase owner)
19 | {
20 | base.OnBorrowed(owner);
21 |
22 | bool canWrite = owner.CanWrite && owner.State != ValueState.Exception;
23 | inputField.Component.readOnly = !canWrite;
24 | ApplyButton.Component.gameObject.SetActive(canWrite);
25 |
26 | SaveFilePath.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, "untitled.txt");
27 | }
28 |
29 | private bool IsStringTooLong(string s)
30 | {
31 | if (s == null)
32 | return false;
33 |
34 | return s.Length >= UniversalUI.MAX_INPUTFIELD_CHARS;
35 | }
36 |
37 | public override void SetValue(object value)
38 | {
39 | if (CurrentOwner.State == ValueState.Exception)
40 | value = CurrentOwner.LastException.ToString();
41 |
42 | RealValue = value as string;
43 | SaveFileRow.SetActive(IsStringTooLong(RealValue));
44 |
45 | if (value == null)
46 | {
47 | inputField.Text = "";
48 | EditedValue = "";
49 | }
50 | else
51 | {
52 | EditedValue = (string)value;
53 | inputField.Text = EditedValue;
54 | }
55 | }
56 |
57 | private void OnApplyClicked()
58 | {
59 | CurrentOwner.SetUserValue(EditedValue);
60 | }
61 |
62 | private void OnInputChanged(string input)
63 | {
64 | EditedValue = input;
65 | SaveFileRow.SetActive(IsStringTooLong(EditedValue));
66 | }
67 |
68 | private void OnSaveFileClicked()
69 | {
70 | if (RealValue == null)
71 | return;
72 |
73 | if (string.IsNullOrEmpty(SaveFilePath.Text))
74 | {
75 | ExplorerCore.LogWarning("Cannot save an empty file path!");
76 | return;
77 | }
78 |
79 | string path = IOUtility.EnsureValidFilePath(SaveFilePath.Text);
80 |
81 | if (File.Exists(path))
82 | File.Delete(path);
83 |
84 | File.WriteAllText(path, RealValue);
85 | }
86 |
87 | public override GameObject CreateContent(GameObject parent)
88 | {
89 | UIRoot = UIFactory.CreateVerticalGroup(parent, "InteractiveString", false, false, true, true, 3, new Vector4(4, 4, 4, 4),
90 | new Color(0.06f, 0.06f, 0.06f));
91 |
92 | // Save to file helper
93 |
94 | SaveFileRow = UIFactory.CreateUIObject("SaveFileRow", UIRoot);
95 | UIFactory.SetLayoutElement(SaveFileRow, flexibleWidth: 9999);
96 | UIFactory.SetLayoutGroup(SaveFileRow, false, true, true, true, 3);
97 |
98 | UIFactory.CreateLabel(SaveFileRow, "Info", "String is too long! Save to file if you want to see the full string.",
99 | TextAnchor.MiddleLeft);
100 |
101 | GameObject horizRow = UIFactory.CreateUIObject("Horiz", SaveFileRow);
102 | UIFactory.SetLayoutGroup(horizRow, false, false, true, true, 4);
103 |
104 | ButtonRef saveButton = UIFactory.CreateButton(horizRow, "SaveButton", "Save file");
105 | UIFactory.SetLayoutElement(saveButton.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
106 | saveButton.OnClick += OnSaveFileClicked;
107 |
108 | SaveFilePath = UIFactory.CreateInputField(horizRow, "SaveInput", "...");
109 | UIFactory.SetLayoutElement(SaveFilePath.UIRoot, minHeight: 25, flexibleWidth: 9999);
110 |
111 | // Main Input / apply
112 |
113 | ApplyButton = UIFactory.CreateButton(UIRoot, "ApplyButton", "Apply", new Color(0.2f, 0.27f, 0.2f));
114 | UIFactory.SetLayoutElement(ApplyButton.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
115 | ApplyButton.OnClick += OnApplyClicked;
116 |
117 | inputField = UIFactory.CreateInputField(UIRoot, "InputField", "empty");
118 | inputField.UIRoot.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
119 | UIFactory.SetLayoutElement(inputField.UIRoot, minHeight: 25, flexibleHeight: 500, flexibleWidth: 9999);
120 | inputField.Component.lineType = InputField.LineType.MultiLineNewline;
121 | inputField.OnValueChanged += OnInputChanged;
122 |
123 | return UIRoot;
124 | }
125 |
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/CacheObject/IValues/InteractiveValue.cs:
--------------------------------------------------------------------------------
1 | using UniverseLib.UI.ObjectPool;
2 |
3 | namespace UnityExplorer.CacheObject.IValues
4 | {
5 | public abstract class InteractiveValue : IPooledObject
6 | {
7 | public static Type GetIValueTypeForState(ValueState state)
8 | {
9 | return state switch
10 | {
11 | ValueState.Exception or ValueState.String => typeof(InteractiveString),
12 | ValueState.Enum => typeof(InteractiveEnum),
13 | ValueState.Collection => typeof(InteractiveList),
14 | ValueState.Dictionary => typeof(InteractiveDictionary),
15 | ValueState.ValueStruct => typeof(InteractiveValueStruct),
16 | ValueState.Color => typeof(InteractiveColor),
17 | _ => null,
18 | };
19 | }
20 |
21 | public GameObject UIRoot { get; set; }
22 | public float DefaultHeight => -1f;
23 |
24 | public virtual bool CanWrite => this.CurrentOwner.CanWrite;
25 |
26 | public CacheObjectBase CurrentOwner => owner;
27 | private CacheObjectBase owner;
28 |
29 | public bool PendingValueWanted;
30 |
31 | public virtual void OnBorrowed(CacheObjectBase owner)
32 | {
33 | if (this.owner != null)
34 | {
35 | ExplorerCore.LogWarning("Setting an IValue's owner but there is already one set. Maybe it wasn't cleaned up?");
36 | ReleaseFromOwner();
37 | }
38 |
39 | this.owner = owner;
40 | }
41 |
42 | public virtual void ReleaseFromOwner()
43 | {
44 | if (this.owner == null)
45 | return;
46 |
47 | this.owner = null;
48 | }
49 |
50 | public abstract void SetValue(object value);
51 |
52 | public virtual void SetLayout() { }
53 |
54 | public abstract GameObject CreateContent(GameObject parent);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/CacheObject/Views/CacheConfigCell.cs:
--------------------------------------------------------------------------------
1 | using UniverseLib.UI;
2 |
3 | namespace UnityExplorer.CacheObject.Views
4 | {
5 | public class ConfigEntryCell : CacheObjectCell
6 | {
7 | public override GameObject CreateContent(GameObject parent)
8 | {
9 | // Main layout
10 |
11 | UIRoot = UIFactory.CreateUIObject(this.GetType().Name, parent, new Vector2(100, 30));
12 | Rect = UIRoot.GetComponent();
13 | UIFactory.SetLayoutGroup(UIRoot, false, false, true, true, 4, 4, 4, 4, 4, childAlignment: TextAnchor.UpperLeft);
14 | UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
15 | UIRoot.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
16 |
17 | // Left label
18 |
19 | NameLabel = UIFactory.CreateLabel(UIRoot, "NameLabel", "", TextAnchor.MiddleLeft);
20 | NameLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
21 | UIFactory.SetLayoutElement(NameLabel.gameObject, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 300);
22 | NameLayout = NameLabel.GetComponent();
23 |
24 | // horizontal group
25 |
26 | GameObject horiGroup = UIFactory.CreateUIObject("RightHoriGroup", UIRoot);
27 | UIFactory.SetLayoutGroup(horiGroup, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft);
28 | UIFactory.SetLayoutElement(horiGroup, minHeight: 25, minWidth: 200, flexibleWidth: 9999, flexibleHeight: 800);
29 |
30 | SubContentButton = UIFactory.CreateButton(horiGroup, "SubContentButton", "▲", subInactiveColor);
31 | UIFactory.SetLayoutElement(SubContentButton.Component.gameObject, minWidth: 25, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
32 | SubContentButton.OnClick += SubContentClicked;
33 |
34 | // Type label
35 |
36 | TypeLabel = UIFactory.CreateLabel(horiGroup, "TypeLabel", "", TextAnchor.MiddleLeft);
37 | TypeLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
38 | UIFactory.SetLayoutElement(TypeLabel.gameObject, minHeight: 25, flexibleHeight: 150, minWidth: 60, flexibleWidth: 0);
39 |
40 | // Bool and number value interaction
41 |
42 | GameObject toggleObj = UIFactory.CreateToggle(horiGroup, "Toggle", out Toggle, out ToggleText);
43 | UIFactory.SetLayoutElement(toggleObj, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
44 | ToggleText.color = SignatureHighlighter.KeywordBlue;
45 | Toggle.onValueChanged.AddListener(ToggleClicked);
46 |
47 | InputField = UIFactory.CreateInputField(horiGroup, "InputField", "...");
48 | UIFactory.SetLayoutElement(InputField.UIRoot, minWidth: 150, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
49 |
50 | // Apply
51 |
52 | ApplyButton = UIFactory.CreateButton(horiGroup, "ApplyButton", "Apply", new Color(0.15f, 0.19f, 0.15f));
53 | UIFactory.SetLayoutElement(ApplyButton.Component.gameObject, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
54 | ApplyButton.OnClick += ApplyClicked;
55 |
56 | // Main value label
57 |
58 | ValueLabel = UIFactory.CreateLabel(horiGroup, "ValueLabel", "Value goes here", TextAnchor.MiddleLeft);
59 | ValueLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
60 | UIFactory.SetLayoutElement(ValueLabel.gameObject, minHeight: 25, flexibleHeight: 150, flexibleWidth: 9999);
61 |
62 | // Subcontent
63 |
64 | SubContentHolder = UIFactory.CreateUIObject("SubContent", UIRoot);
65 | UIFactory.SetLayoutElement(SubContentHolder.gameObject, minHeight: 30, flexibleHeight: 600, minWidth: 100, flexibleWidth: 9999);
66 | UIFactory.SetLayoutGroup(SubContentHolder, true, true, true, true, 2, childAlignment: TextAnchor.UpperLeft);
67 | //SubContentHolder.AddComponent().verticalFit = ContentSizeFitter.FitMode.MinSize;
68 | SubContentHolder.SetActive(false);
69 |
70 | // Bottom separator
71 | GameObject separator = UIFactory.CreateUIObject("BottomSeperator", UIRoot);
72 | UIFactory.SetLayoutElement(separator, minHeight: 1, flexibleHeight: 0, flexibleWidth: 9999);
73 | separator.AddComponent().color = Color.black;
74 |
75 | return UIRoot;
76 | }
77 |
78 | protected override void ConstructEvaluateHolder(GameObject parent) { }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/CacheObject/Views/CacheKeyValuePairCell.cs:
--------------------------------------------------------------------------------
1 | using UnityExplorer.CacheObject.IValues;
2 | using UniverseLib.UI;
3 | using UniverseLib.UI.Models;
4 |
5 | namespace UnityExplorer.CacheObject.Views
6 | {
7 | public class CacheKeyValuePairCell : CacheObjectCell
8 | {
9 | public Image Image { get; private set; }
10 | public InteractiveDictionary DictOwner => Occupant.Owner as InteractiveDictionary;
11 |
12 | public LayoutElement KeyGroupLayout;
13 | public Text KeyLabel;
14 | public ButtonRef KeyInspectButton;
15 | public InputFieldRef KeyInputField;
16 | public Text KeyInputTypeLabel;
17 |
18 | public static Color EvenColor = new(0.07f, 0.07f, 0.07f);
19 | public static Color OddColor = new(0.063f, 0.063f, 0.063f);
20 |
21 | public int AdjustedWidth => (int)Rect.rect.width - 70;
22 |
23 | //public int HalfWidth => (int)(0.5f * Rect.rect.width) - 75;
24 | //public int AdjustedKeyWidth => HalfWidth - 50;
25 | //public int AdjustedRightWidth => HalfWidth;
26 |
27 | private void KeyInspectClicked()
28 | {
29 | InspectorManager.Inspect((Occupant as CacheKeyValuePair).DictKey, this.Occupant);
30 | }
31 |
32 | public override GameObject CreateContent(GameObject parent)
33 | {
34 | GameObject root = base.CreateContent(parent);
35 |
36 | Image = root.AddComponent();
37 |
38 | this.NameLayout.minWidth = 70;
39 | this.NameLayout.flexibleWidth = 0;
40 | this.NameLayout.minHeight = 30;
41 | this.NameLayout.flexibleHeight = 0;
42 | this.NameLabel.alignment = TextAnchor.MiddleRight;
43 |
44 | this.RightGroupLayout.minWidth = AdjustedWidth * 0.55f;
45 |
46 | // Key area
47 | GameObject keyGroup = UIFactory.CreateUIObject("KeyHolder", root.transform.Find("HoriGroup").gameObject);
48 | UIFactory.SetLayoutGroup(keyGroup, false, false, true, true, 2, 0, 0, 4, 4, childAlignment: TextAnchor.MiddleLeft);
49 | KeyGroupLayout = UIFactory.SetLayoutElement(keyGroup, minHeight: 30, minWidth: (int)(AdjustedWidth * 0.44f), flexibleWidth: 0);
50 |
51 | // set to be after the NameLabel (our index label), and before the main horizontal group.
52 | keyGroup.transform.SetSiblingIndex(1);
53 |
54 | // key Inspect
55 |
56 | KeyInspectButton = UIFactory.CreateButton(keyGroup, "KeyInspectButton", "Inspect", new Color(0.15f, 0.15f, 0.15f));
57 | UIFactory.SetLayoutElement(KeyInspectButton.Component.gameObject, minWidth: 60, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
58 | KeyInspectButton.OnClick += KeyInspectClicked;
59 |
60 | // label
61 |
62 | KeyLabel = UIFactory.CreateLabel(keyGroup, "KeyLabel", "empty", TextAnchor.MiddleLeft);
63 | UIFactory.SetLayoutElement(KeyLabel.gameObject, minWidth: 50, flexibleWidth: 999, minHeight: 25);
64 |
65 | // Type label for input field
66 |
67 | KeyInputTypeLabel = UIFactory.CreateLabel(keyGroup, "InputTypeLabel", "null", TextAnchor.MiddleLeft);
68 | UIFactory.SetLayoutElement(KeyInputTypeLabel.gameObject, minWidth: 55, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
69 |
70 | // input field
71 |
72 | KeyInputField = UIFactory.CreateInputField(keyGroup, "KeyInput", "empty");
73 | UIFactory.SetLayoutElement(KeyInputField.UIRoot, minHeight: 25, flexibleHeight: 0, flexibleWidth: 0, preferredWidth: 200);
74 | //KeyInputField.lineType = InputField.LineType.MultiLineNewline;
75 | KeyInputField.Component.readOnly = true;
76 |
77 | return root;
78 | }
79 |
80 | protected override void ConstructEvaluateHolder(GameObject parent)
81 | {
82 | // not used
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/CacheObject/Views/CacheListEntryCell.cs:
--------------------------------------------------------------------------------
1 | using UnityExplorer.CacheObject.IValues;
2 |
3 | namespace UnityExplorer.CacheObject.Views
4 | {
5 | public class CacheListEntryCell : CacheObjectCell
6 | {
7 | public Image Image { get; private set; }
8 | public InteractiveList ListOwner => Occupant.Owner as InteractiveList;
9 |
10 | public static Color EvenColor = new(0.12f, 0.12f, 0.12f);
11 | public static Color OddColor = new(0.1f, 0.1f, 0.1f);
12 |
13 | public override GameObject CreateContent(GameObject parent)
14 | {
15 | GameObject root = base.CreateContent(parent);
16 |
17 | Image = root.AddComponent();
18 |
19 | this.NameLayout.minWidth = 40;
20 | this.NameLayout.flexibleWidth = 50;
21 | this.NameLayout.minHeight = 25;
22 | this.NameLayout.flexibleHeight = 0;
23 | this.NameLabel.alignment = TextAnchor.MiddleRight;
24 |
25 | return root;
26 | }
27 |
28 | protected override void ConstructEvaluateHolder(GameObject parent)
29 | {
30 | // not used
31 | }
32 |
33 | //protected override void ConstructUpdateToggle(GameObject parent)
34 | //{
35 | // // not used
36 | //}
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/CacheObject/Views/CacheMemberCell.cs:
--------------------------------------------------------------------------------
1 | using UniverseLib.UI;
2 | using UniverseLib.UI.Models;
3 |
4 | namespace UnityExplorer.CacheObject.Views
5 | {
6 | public class CacheMemberCell : CacheObjectCell
7 | {
8 | public CacheMember MemberOccupant => Occupant as CacheMember;
9 |
10 | public GameObject EvaluateHolder;
11 | public ButtonRef EvaluateButton;
12 |
13 | protected virtual void EvaluateClicked()
14 | {
15 | this.MemberOccupant.OnEvaluateClicked();
16 | }
17 |
18 | protected override void ConstructEvaluateHolder(GameObject parent)
19 | {
20 | // Evaluate vert group
21 |
22 | EvaluateHolder = UIFactory.CreateUIObject("EvalGroup", parent);
23 | UIFactory.SetLayoutGroup(EvaluateHolder, false, false, true, true, 3);
24 | UIFactory.SetLayoutElement(EvaluateHolder, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 775);
25 |
26 | EvaluateButton = UIFactory.CreateButton(EvaluateHolder, "EvaluateButton", "Evaluate", new Color(0.15f, 0.15f, 0.15f));
27 | UIFactory.SetLayoutElement(EvaluateButton.Component.gameObject, minWidth: 100, minHeight: 25);
28 | EvaluateButton.OnClick += EvaluateClicked;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Config/ConfigElement.cs:
--------------------------------------------------------------------------------
1 | namespace UnityExplorer.Config
2 | {
3 | public class ConfigElement : IConfigElement
4 | {
5 | public string Name { get; }
6 | public string Description { get; }
7 |
8 | public bool IsInternal { get; }
9 | public Type ElementType => typeof(T);
10 |
11 | public Action OnValueChanged;
12 | public Action OnValueChangedNotify { get; set; }
13 |
14 | public object DefaultValue { get; }
15 |
16 | public ConfigHandler Handler => IsInternal
17 | ? ConfigManager.InternalHandler
18 | : ConfigManager.Handler;
19 |
20 | public T Value
21 | {
22 | get => m_value;
23 | set => SetValue(value);
24 | }
25 | private T m_value;
26 |
27 | object IConfigElement.BoxedValue
28 | {
29 | get => m_value;
30 | set => SetValue((T)value);
31 | }
32 |
33 | public ConfigElement(string name, string description, T defaultValue, bool isInternal = false)
34 | {
35 | Name = name;
36 | Description = description;
37 |
38 | m_value = defaultValue;
39 | DefaultValue = defaultValue;
40 |
41 | IsInternal = isInternal;
42 |
43 | ConfigManager.RegisterConfigElement(this);
44 | }
45 |
46 | private void SetValue(T value)
47 | {
48 | if ((m_value == null && value == null) || (m_value != null && m_value.Equals(value)))
49 | return;
50 |
51 | m_value = value;
52 |
53 | Handler.SetConfigValue(this, value);
54 |
55 | OnValueChanged?.Invoke(value);
56 | OnValueChangedNotify?.Invoke();
57 |
58 | Handler.OnAnyConfigChanged();
59 | }
60 |
61 | object IConfigElement.GetLoaderConfigValue() => GetLoaderConfigValue();
62 |
63 | public T GetLoaderConfigValue()
64 | {
65 | return Handler.GetConfigValue(this);
66 | }
67 |
68 | public void RevertToDefaultValue()
69 | {
70 | Value = (T)DefaultValue;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Config/ConfigHandler.cs:
--------------------------------------------------------------------------------
1 | namespace UnityExplorer.Config
2 | {
3 | public abstract class ConfigHandler
4 | {
5 | public abstract void RegisterConfigElement(ConfigElement element);
6 |
7 | public abstract void SetConfigValue(ConfigElement element, T value);
8 |
9 | public abstract T GetConfigValue(ConfigElement element);
10 |
11 | public abstract void Init();
12 |
13 | public abstract void LoadConfig();
14 |
15 | public abstract void SaveConfig();
16 |
17 | public virtual void OnAnyConfigChanged() { }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Config/IConfigElement.cs:
--------------------------------------------------------------------------------
1 | namespace UnityExplorer.Config
2 | {
3 | public interface IConfigElement
4 | {
5 | string Name { get; }
6 | string Description { get; }
7 |
8 | bool IsInternal { get; }
9 | Type ElementType { get; }
10 |
11 | object BoxedValue { get; set; }
12 | object DefaultValue { get; }
13 |
14 | object GetLoaderConfigValue();
15 |
16 | void RevertToDefaultValue();
17 |
18 | Action OnValueChangedNotify { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Config/InternalConfigHandler.cs:
--------------------------------------------------------------------------------
1 | using Tomlet;
2 | using Tomlet.Models;
3 | using UnityExplorer.UI;
4 |
5 | namespace UnityExplorer.Config
6 | {
7 | public class InternalConfigHandler : ConfigHandler
8 | {
9 | internal static string CONFIG_PATH;
10 |
11 | public override void Init()
12 | {
13 | CONFIG_PATH = Path.Combine(ExplorerCore.ExplorerFolder, "data.cfg");
14 | }
15 |
16 | public override void LoadConfig()
17 | {
18 | if (!TryLoadConfig())
19 | SaveConfig();
20 | }
21 |
22 | public override void RegisterConfigElement(ConfigElement element)
23 | {
24 | // Not necessary
25 | }
26 |
27 | public override void SetConfigValue(ConfigElement element, T value)
28 | {
29 | // Not necessary
30 | }
31 |
32 | // Not necessary, just return the value.
33 | public override T GetConfigValue(ConfigElement element) => element.Value;
34 |
35 | // Always just auto-save.
36 | public override void OnAnyConfigChanged() => SaveConfig();
37 |
38 | public bool TryLoadConfig()
39 | {
40 | try
41 | {
42 | if (!File.Exists(CONFIG_PATH))
43 | return false;
44 |
45 | TomlDocument document = TomlParser.ParseFile(CONFIG_PATH);
46 | foreach (string key in document.Keys)
47 | {
48 | if (!Enum.IsDefined(typeof(UIManager.Panels), key))
49 | continue;
50 |
51 | UIManager.Panels panelKey = (UIManager.Panels)Enum.Parse(typeof(UIManager.Panels), key);
52 | ConfigManager.GetPanelSaveData(panelKey).Value = document.GetString(key);
53 | }
54 |
55 | return true;
56 | }
57 | catch (Exception ex)
58 | {
59 | ExplorerCore.LogWarning("Error loading internal data: " + ex.ToString());
60 | return false;
61 | }
62 | }
63 |
64 | public override void SaveConfig()
65 | {
66 | if (UIManager.Initializing)
67 | return;
68 |
69 | TomlDocument tomlDocument = TomlDocument.CreateEmpty();
70 | foreach (KeyValuePair entry in ConfigManager.InternalConfigs)
71 | tomlDocument.Put(entry.Key, entry.Value.BoxedValue as string, false);
72 |
73 | File.WriteAllText(CONFIG_PATH, tomlDocument.SerializedValue);
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/ExplorerBehaviour.cs:
--------------------------------------------------------------------------------
1 | using UnityExplorer.UI;
2 | #if CPP
3 | #if UNHOLLOWER
4 | using UnhollowerRuntimeLib;
5 | #else
6 | using Il2CppInterop.Runtime.Injection;
7 | #endif
8 | #endif
9 |
10 | namespace UnityExplorer
11 | {
12 | public class ExplorerBehaviour : MonoBehaviour
13 | {
14 | internal static ExplorerBehaviour Instance { get; private set; }
15 |
16 | #if CPP
17 | public ExplorerBehaviour(System.IntPtr ptr) : base(ptr) { }
18 | #endif
19 |
20 | internal static void Setup()
21 | {
22 | #if CPP
23 | ClassInjector.RegisterTypeInIl2Cpp();
24 | #endif
25 |
26 | GameObject obj = new("ExplorerBehaviour");
27 | DontDestroyOnLoad(obj);
28 | obj.hideFlags = HideFlags.HideAndDontSave;
29 | Instance = obj.AddComponent();
30 | }
31 |
32 | internal void Update()
33 | {
34 | ExplorerCore.Update();
35 | }
36 |
37 | // For editor, to clean up objects
38 |
39 | internal void OnDestroy()
40 | {
41 | OnApplicationQuit();
42 | }
43 |
44 | internal bool quitting;
45 |
46 | internal void OnApplicationQuit()
47 | {
48 | if (quitting) return;
49 | quitting = true;
50 |
51 | TryDestroy(UIManager.UIRoot?.transform.root.gameObject);
52 |
53 | TryDestroy((typeof(Universe).Assembly.GetType("UniverseLib.UniversalBehaviour")
54 | .GetProperty("Instance", BindingFlags.Static | BindingFlags.NonPublic)
55 | .GetValue(null, null)
56 | as Component).gameObject);
57 |
58 | TryDestroy(this.gameObject);
59 | }
60 |
61 | internal void TryDestroy(GameObject obj)
62 | {
63 | try
64 | {
65 | if (obj)
66 | Destroy(obj);
67 | }
68 | catch { }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Hooks/AddHookCell.cs:
--------------------------------------------------------------------------------
1 | using UniverseLib.UI;
2 | using UniverseLib.UI.Models;
3 | using UniverseLib.UI.Widgets.ScrollView;
4 |
5 | namespace UnityExplorer.Hooks
6 | {
7 | public class AddHookCell : ICell
8 | {
9 | public bool Enabled => UIRoot.activeSelf;
10 |
11 | public RectTransform Rect { get; set; }
12 | public GameObject UIRoot { get; set; }
13 |
14 | public float DefaultHeight => 30;
15 |
16 | public Text MethodNameLabel;
17 | public ButtonRef HookButton;
18 |
19 | public int CurrentDisplayedIndex;
20 |
21 | private void OnHookClicked()
22 | {
23 | HookCreator.AddHookClicked(CurrentDisplayedIndex);
24 | }
25 |
26 | public void Enable()
27 | {
28 | this.UIRoot.SetActive(true);
29 | }
30 |
31 | public void Disable()
32 | {
33 | this.UIRoot.SetActive(false);
34 | }
35 |
36 | public GameObject CreateContent(GameObject parent)
37 | {
38 | UIRoot = UIFactory.CreateUIObject(this.GetType().Name, parent, new Vector2(100, 30));
39 | Rect = UIRoot.GetComponent();
40 | UIFactory.SetLayoutGroup(UIRoot, false, false, true, true, 5, childAlignment: TextAnchor.UpperLeft);
41 | UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
42 | UIRoot.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
43 |
44 | HookButton = UIFactory.CreateButton(UIRoot, "HookButton", "Hook", new Color(0.2f, 0.25f, 0.2f));
45 | UIFactory.SetLayoutElement(HookButton.Component.gameObject, minHeight: 25, minWidth: 100);
46 | HookButton.OnClick += OnHookClicked;
47 |
48 | MethodNameLabel = UIFactory.CreateLabel(UIRoot, "MethodName", "NOT SET", TextAnchor.MiddleLeft);
49 | UIFactory.SetLayoutElement(MethodNameLabel.gameObject, minHeight: 25, flexibleWidth: 9999);
50 |
51 | return UIRoot;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Hooks/HookCell.cs:
--------------------------------------------------------------------------------
1 | using UniverseLib.UI;
2 | using UniverseLib.UI.Models;
3 | using UniverseLib.UI.Widgets.ScrollView;
4 |
5 | namespace UnityExplorer.Hooks
6 | {
7 | public class HookCell : ICell
8 | {
9 | public bool Enabled => UIRoot.activeSelf;
10 |
11 | public RectTransform Rect { get; set; }
12 | public GameObject UIRoot { get; set; }
13 |
14 | public float DefaultHeight => 30;
15 |
16 | public Text MethodNameLabel;
17 | public ButtonRef EditPatchButton;
18 | public ButtonRef ToggleActiveButton;
19 | public ButtonRef DeleteButton;
20 |
21 | public int CurrentDisplayedIndex;
22 |
23 | private void OnToggleActiveClicked()
24 | {
25 | HookList.EnableOrDisableHookClicked(CurrentDisplayedIndex);
26 | }
27 |
28 | private void OnDeleteClicked()
29 | {
30 | HookList.DeleteHookClicked(CurrentDisplayedIndex);
31 | HookCreator.AddHooksScrollPool.Refresh(true, false);
32 | }
33 |
34 | private void OnEditPatchClicked()
35 | {
36 | HookList.EditPatchClicked(CurrentDisplayedIndex);
37 | }
38 |
39 | public GameObject CreateContent(GameObject parent)
40 | {
41 | UIRoot = UIFactory.CreateUIObject(this.GetType().Name, parent, new Vector2(100, 30));
42 | Rect = UIRoot.GetComponent();
43 | UIFactory.SetLayoutGroup(UIRoot, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft);
44 | UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
45 | UIRoot.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
46 |
47 | MethodNameLabel = UIFactory.CreateLabel(UIRoot, "MethodName", "NOT SET", TextAnchor.MiddleLeft);
48 | UIFactory.SetLayoutElement(MethodNameLabel.gameObject, minHeight: 25, flexibleWidth: 9999);
49 |
50 | ToggleActiveButton = UIFactory.CreateButton(UIRoot, "ToggleActiveBtn", "On", new Color(0.15f, 0.2f, 0.15f));
51 | UIFactory.SetLayoutElement(ToggleActiveButton.Component.gameObject, minHeight: 25, minWidth: 35);
52 | ToggleActiveButton.OnClick += OnToggleActiveClicked;
53 |
54 | EditPatchButton = UIFactory.CreateButton(UIRoot, "EditButton", "Edit", new Color(0.15f, 0.15f, 0.15f));
55 | UIFactory.SetLayoutElement(EditPatchButton.Component.gameObject, minHeight: 25, minWidth: 35);
56 | EditPatchButton.OnClick += OnEditPatchClicked;
57 |
58 | DeleteButton = UIFactory.CreateButton(UIRoot, "DeleteButton", "X", new Color(0.2f, 0.15f, 0.15f));
59 | UIFactory.SetLayoutElement(DeleteButton.Component.gameObject, minHeight: 25, minWidth: 35);
60 | DeleteButton.OnClick += OnDeleteClicked;
61 |
62 | return UIRoot;
63 | }
64 |
65 | public void Disable()
66 | {
67 | UIRoot.SetActive(false);
68 | }
69 |
70 | public void Enable()
71 | {
72 | UIRoot.SetActive(true);
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Hooks/HookList.cs:
--------------------------------------------------------------------------------
1 | using HarmonyLib;
2 | using System.Collections.Specialized;
3 | using UnityExplorer.UI.Panels;
4 | using UniverseLib.UI;
5 | using UniverseLib.UI.Widgets.ScrollView;
6 |
7 | namespace UnityExplorer.Hooks
8 | {
9 | public class HookList : ICellPoolDataSource
10 | {
11 | public int ItemCount => currentHooks.Count;
12 |
13 | internal static readonly HashSet hookedSignatures = new();
14 | internal static readonly OrderedDictionary currentHooks = new();
15 |
16 | internal static GameObject UIRoot;
17 | internal static ScrollPool HooksScrollPool;
18 |
19 | public static void EnableOrDisableHookClicked(int index)
20 | {
21 | HookInstance hook = (HookInstance)currentHooks[index];
22 | hook.TogglePatch();
23 |
24 | HooksScrollPool.Refresh(true, false);
25 | }
26 |
27 | public static void DeleteHookClicked(int index)
28 | {
29 | HookInstance hook = (HookInstance)currentHooks[index];
30 |
31 | if (HookCreator.CurrentEditedHook == hook)
32 | HookCreator.EditorInputCancel();
33 |
34 | hook.Unpatch();
35 | currentHooks.RemoveAt(index);
36 | hookedSignatures.Remove(hook.TargetMethod.FullDescription());
37 |
38 | HooksScrollPool.Refresh(true, false);
39 | }
40 |
41 | public static void EditPatchClicked(int index)
42 | {
43 | if (HookCreator.PendingGeneric)
44 | HookManagerPanel.genericArgsHandler.Cancel();
45 |
46 | HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.HookSourceEditor);
47 | HookInstance hook = (HookInstance)currentHooks[index];
48 | HookCreator.SetEditedHook(hook);
49 | }
50 |
51 | // Set current hook cell
52 |
53 | public void OnCellBorrowed(HookCell cell) { }
54 |
55 | public void SetCell(HookCell cell, int index)
56 | {
57 | if (index >= currentHooks.Count)
58 | {
59 | cell.Disable();
60 | return;
61 | }
62 |
63 | cell.CurrentDisplayedIndex = index;
64 | HookInstance hook = (HookInstance)currentHooks[index];
65 |
66 | cell.MethodNameLabel.text = SignatureHighlighter.ParseMethod(hook.TargetMethod);
67 |
68 | cell.ToggleActiveButton.ButtonText.text = hook.Enabled ? "On" : "Off";
69 | RuntimeHelper.SetColorBlockAuto(cell.ToggleActiveButton.Component,
70 | hook.Enabled ? new Color(0.15f, 0.2f, 0.15f) : new Color(0.2f, 0.2f, 0.15f));
71 | }
72 |
73 | // UI
74 |
75 | internal void ConstructUI(GameObject leftGroup)
76 | {
77 | UIRoot = UIFactory.CreateUIObject("CurrentHooksPanel", leftGroup);
78 | UIFactory.SetLayoutElement(UIRoot, preferredHeight: 150, flexibleHeight: 0, flexibleWidth: 9999);
79 | UIFactory.SetLayoutGroup(UIRoot, true, true, true, true);
80 |
81 | Text hooksLabel = UIFactory.CreateLabel(UIRoot, "HooksLabel", "Current Hooks", TextAnchor.MiddleCenter);
82 | UIFactory.SetLayoutElement(hooksLabel.gameObject, minHeight: 30, flexibleWidth: 9999);
83 |
84 | HooksScrollPool = UIFactory.CreateScrollPool(UIRoot, "HooksScrollPool",
85 | out GameObject hooksScroll, out GameObject hooksContent);
86 | UIFactory.SetLayoutElement(hooksScroll, flexibleHeight: 9999);
87 | HooksScrollPool.Initialize(this);
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/Inspectors/InspectorBase.cs:
--------------------------------------------------------------------------------
1 | using UnityExplorer.UI.Panels;
2 | using UniverseLib.UI.ObjectPool;
3 |
4 | namespace UnityExplorer.Inspectors
5 | {
6 | public abstract class InspectorBase : IPooledObject
7 | {
8 | public bool IsActive { get; internal set; }
9 | public object Target { get; set; }
10 | public Type TargetType { get; protected set; }
11 |
12 | public InspectorTab Tab { get; internal set; }
13 |
14 | public GameObject UIRoot { get; set; }
15 |
16 | public float DefaultHeight => -1f;
17 | public abstract GameObject CreateContent(GameObject parent);
18 |
19 | public abstract void Update();
20 |
21 | public abstract void CloseInspector();
22 |
23 | public virtual void OnBorrowedFromPool(object target)
24 | {
25 | this.Target = target;
26 | this.TargetType = target is Type type ? type : target.GetActualType();
27 |
28 | Tab = Pool.Borrow();
29 | Tab.UIRoot.transform.SetParent(InspectorPanel.Instance.NavbarHolder.transform, false);
30 |
31 | Tab.TabButton.OnClick += OnTabButtonClicked;
32 | Tab.CloseButton.OnClick += CloseInspector;
33 | }
34 |
35 | public virtual void OnReturnToPool()
36 | {
37 | Pool.Return(Tab);
38 |
39 | this.Target = null;
40 |
41 | Tab.TabButton.OnClick -= OnTabButtonClicked;
42 | Tab.CloseButton.OnClick -= CloseInspector;
43 | }
44 |
45 | public virtual void OnSetActive()
46 | {
47 | Tab.SetTabColor(true);
48 | UIRoot.SetActive(true);
49 | IsActive = true;
50 | LayoutRebuilder.ForceRebuildLayoutImmediate(UIRoot.GetComponent());
51 | }
52 |
53 | public virtual void OnSetInactive()
54 | {
55 | Tab.SetTabColor(false);
56 | UIRoot.SetActive(false);
57 | IsActive = false;
58 | }
59 |
60 | private void OnTabButtonClicked()
61 | {
62 | InspectorManager.SetInspectorActive(this);
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Inspectors/InspectorTab.cs:
--------------------------------------------------------------------------------
1 | using UniverseLib.UI;
2 | using UniverseLib.UI.Models;
3 | using UniverseLib.UI.ObjectPool;
4 |
5 | namespace UnityExplorer.Inspectors
6 | {
7 | public class InspectorTab : IPooledObject
8 | {
9 | public GameObject UIRoot { get; set; }
10 | public float DefaultHeight => 25f;
11 |
12 | public ButtonRef TabButton;
13 | public Text TabText;
14 | public ButtonRef CloseButton;
15 |
16 | private static readonly Color enabledTabColor = new(0.15f, 0.22f, 0.15f);
17 | private static readonly Color disabledTabColor = new(0.13f, 0.13f, 0.13f);
18 |
19 | public void SetTabColor(bool active)
20 | {
21 | Color color = active ? enabledTabColor : disabledTabColor;
22 | RuntimeHelper.SetColorBlock(TabButton.Component, color, color * 1.2f);
23 | }
24 |
25 | public GameObject CreateContent(GameObject parent)
26 | {
27 | UIRoot = UIFactory.CreateHorizontalGroup(parent, "TabObject", false, true, true, true, 1,
28 | default, new Color(0.13f, 0.13f, 0.13f), childAlignment: TextAnchor.MiddleLeft);
29 | UIFactory.SetLayoutElement(UIRoot, minWidth: 200, flexibleWidth: 0);
30 | UIRoot.AddComponent();
31 | UIRoot.AddComponent();
32 |
33 | TabButton = UIFactory.CreateButton(UIRoot, "TabButton", "");
34 | UIFactory.SetLayoutElement(TabButton.Component.gameObject, minWidth: 173, flexibleWidth: 0);
35 | UIFactory.SetLayoutGroup(TabButton.Component.gameObject, false, false, true, true, 0, 0, 0, 3);
36 | TabButton.GameObject.AddComponent();
37 |
38 | TabText = TabButton.ButtonText;
39 | UIFactory.SetLayoutElement(TabText.gameObject, minHeight: 25, minWidth: 150, flexibleWidth: 0);
40 | TabText.alignment = TextAnchor.MiddleLeft;
41 | TabText.fontSize = 12;
42 | TabText.horizontalOverflow = HorizontalWrapMode.Overflow;
43 |
44 | CloseButton = UIFactory.CreateButton(UIRoot, "CloseButton", "X", new Color(0.15f, 0.15f, 0.15f, 1));
45 | UIFactory.SetLayoutElement(CloseButton.Component.gameObject, minHeight: 25, minWidth: 25, flexibleWidth: 0);
46 | CloseButton.ButtonText.color = Color.red;
47 |
48 | return UIRoot;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Inspectors/MouseInspectors/MouseInspectorBase.cs:
--------------------------------------------------------------------------------
1 | namespace UnityExplorer.Inspectors.MouseInspectors
2 | {
3 | public abstract class MouseInspectorBase
4 | {
5 | public abstract void OnBeginMouseInspect();
6 |
7 | public abstract void UpdateMouseInspect(Vector2 mousePos);
8 |
9 | public abstract void OnSelectMouseInspect();
10 |
11 | public abstract void ClearHitData();
12 |
13 | public abstract void OnEndInspect();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Inspectors/MouseInspectors/WorldInspector.cs:
--------------------------------------------------------------------------------
1 | namespace UnityExplorer.Inspectors.MouseInspectors
2 | {
3 | public class WorldInspector : MouseInspectorBase
4 | {
5 | private static Camera MainCamera;
6 | private static GameObject lastHitObject;
7 |
8 | public override void OnBeginMouseInspect()
9 | {
10 | MainCamera = Camera.main;
11 |
12 | if (!MainCamera)
13 | {
14 | ExplorerCore.LogWarning("No MainCamera found! Cannot inspect world!");
15 | return;
16 | }
17 | }
18 |
19 | public override void ClearHitData()
20 | {
21 | lastHitObject = null;
22 | }
23 |
24 | public override void OnSelectMouseInspect()
25 | {
26 | InspectorManager.Inspect(lastHitObject);
27 | }
28 |
29 | public override void UpdateMouseInspect(Vector2 mousePos)
30 | {
31 | if (!MainCamera)
32 | MainCamera = Camera.main;
33 | if (!MainCamera)
34 | {
35 | ExplorerCore.LogWarning("No Main Camera was found, unable to inspect world!");
36 | MouseInspector.Instance.StopInspect();
37 | return;
38 | }
39 |
40 | Ray ray = MainCamera.ScreenPointToRay(mousePos);
41 | Physics.Raycast(ray, out RaycastHit hit, 1000f);
42 |
43 | if (hit.transform)
44 | OnHitGameObject(hit.transform.gameObject);
45 | else if (lastHitObject)
46 | MouseInspector.Instance.ClearHitData();
47 | }
48 |
49 | internal void OnHitGameObject(GameObject obj)
50 | {
51 | if (obj != lastHitObject)
52 | {
53 | lastHitObject = obj;
54 | MouseInspector.Instance.objNameLabel.text = $"Click to Inspect: {obj.name}";
55 | MouseInspector.Instance.objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}";
56 | }
57 | }
58 |
59 | public override void OnEndInspect()
60 | {
61 | // not needed
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Loader/BepInEx/BepInExConfigHandler.cs:
--------------------------------------------------------------------------------
1 | #if BIE
2 | using BepInEx.Configuration;
3 | using UnityExplorer.Config;
4 |
5 | namespace UnityExplorer.Loader.BIE
6 | {
7 | public class BepInExConfigHandler : ConfigHandler
8 | {
9 | private ConfigFile Config => ExplorerBepInPlugin.Instance.Config;
10 |
11 | private const string CTG_NAME = "UnityExplorer";
12 |
13 | public override void Init()
14 | {
15 | // Not necessary
16 | }
17 |
18 | public override void RegisterConfigElement(ConfigElement config)
19 | {
20 | ConfigEntry entry = Config.Bind(CTG_NAME, config.Name, config.Value, config.Description);
21 |
22 | entry.SettingChanged += (object o, EventArgs e) =>
23 | {
24 | config.Value = entry.Value;
25 | };
26 | }
27 |
28 | public override T GetConfigValue(ConfigElement element)
29 | {
30 | if (Config.TryGetEntry(CTG_NAME, element.Name, out ConfigEntry configEntry))
31 | return configEntry.Value;
32 | else
33 | throw new Exception("Could not get config entry '" + element.Name + "'");
34 | }
35 |
36 | public override void SetConfigValue(ConfigElement element, T value)
37 | {
38 | if (Config.TryGetEntry(CTG_NAME, element.Name, out ConfigEntry configEntry))
39 | configEntry.Value = value;
40 | else
41 | ExplorerCore.Log("Could not get config entry '" + element.Name + "'");
42 | }
43 |
44 | public override void LoadConfig()
45 | {
46 | foreach (KeyValuePair entry in ConfigManager.ConfigElements)
47 | {
48 | string key = entry.Key;
49 | ConfigDefinition def = new(CTG_NAME, key);
50 | if (Config.ContainsKey(def) && Config[def] is ConfigEntryBase configEntry)
51 | {
52 | IConfigElement config = entry.Value;
53 | config.BoxedValue = configEntry.BoxedValue;
54 | }
55 | }
56 | }
57 |
58 | public override void SaveConfig()
59 | {
60 | Config.Save();
61 | }
62 | }
63 | }
64 |
65 | #endif
--------------------------------------------------------------------------------
/src/Loader/BepInEx/ExplorerBepInPlugin.cs:
--------------------------------------------------------------------------------
1 | #if BIE
2 | using BepInEx;
3 | using BepInEx.Logging;
4 | using HarmonyLib;
5 | using UnityExplorer.Config;
6 | using UnityExplorer.Loader.BIE;
7 | #if CPP
8 | using BepInEx.IL2CPP;
9 | #endif
10 |
11 | namespace UnityExplorer
12 | {
13 | [BepInPlugin(ExplorerCore.GUID, "UnityExplorer", ExplorerCore.VERSION)]
14 |
15 | public class ExplorerBepInPlugin :
16 | #if MONO
17 | BaseUnityPlugin
18 | #else
19 | BasePlugin
20 | #endif
21 | , IExplorerLoader
22 | {
23 | public static ExplorerBepInPlugin Instance;
24 |
25 | public ManualLogSource LogSource
26 | #if MONO
27 | => Logger;
28 | #else
29 | => Log;
30 | #endif
31 | const string IL2CPP_LIBS_FOLDER =
32 | #if UNHOLLOWER
33 | "unhollowed"
34 | #else
35 | "interop"
36 | #endif
37 | ;
38 | public string UnhollowedModulesFolder => Path.Combine(Paths.BepInExRootPath, IL2CPP_LIBS_FOLDER);
39 |
40 | public ConfigHandler ConfigHandler => _configHandler;
41 | private BepInExConfigHandler _configHandler;
42 |
43 | public Harmony HarmonyInstance => s_harmony;
44 | private static readonly Harmony s_harmony = new(ExplorerCore.GUID);
45 |
46 | public string ExplorerFolderName => ExplorerCore.DEFAULT_EXPLORER_FOLDER_NAME;
47 | public string ExplorerFolderDestination => Paths.PluginPath;
48 |
49 | public Action