Compare commits

...

45 Commits
1.8.0 ... 2.0.7

Author SHA1 Message Date
3c964cfef9 2.0.7
* More unstripping fixes. Explorer now works 100% on a blank Unity project (so should therefore work on any Unity game, regardless of stripping).
* Some cleanups
2020-10-18 21:41:04 +11:00
184b037523 Update ReflectionHelpers.cs 2020-10-18 04:46:50 +11:00
a49a918790 faster il2cpp cast, a few cleanups 2020-10-18 04:39:50 +11:00
e3a58bf675 Update CacheMember.cs 2020-10-17 22:00:53 +11:00
cc29dbda30 Update ReflectionInspector.cs 2020-10-16 20:20:36 +11:00
bc0ad5eab6 refactored icalls using icall helper 2020-10-16 19:40:01 +11:00
bdf86a7448 2.0.6 2020-10-14 20:55:44 +11:00
968546d43c 2.0.6
* Unstrip fixes and cleanups
2020-10-14 20:47:19 +11:00
680556d74e Update README.md 2020-10-12 20:23:21 +11:00
f490203b10 2.0.5
* Added Max Results option to Search (default 5000)
* Fixed a TypeInitializationException which can happen when inspecting some classes with Dictionary members
* Fixed an issue which could prevent Input support from initializating
* Improved and fixed the display of TextAsset objects
* A few other minor fixes
2020-10-12 20:15:41 +11:00
39d9585f1d 2.0.4
* Added ability to see and change the layer of a gameobject from the GameObject inspector more easily, and shows you the actual layer name (where possible).
* Fixed an issue related to the recently-added clickthrough prevention and resize drag
* Fixed write-only properties in the inspector
* A few other minor fixes
2020-10-11 22:57:46 +11:00
2d414e544b Update README.md 2020-10-11 20:52:08 +11:00
513fcaa534 Universal click-through prevention attempt 2020-10-11 20:49:14 +11:00
b41f7211e5 2.0.3
* Fixed a few issues related to the Texture2D support/export.
2020-10-11 20:07:23 +11:00
dd6cce1df1 2.0.2 2020-10-10 20:20:10 +11:00
ad54d2c76b 2.0.2
* Added support for viewing Texture2D (and Sprite) from the Inspector, and exporting them to PNG
* Fixed an issue with generic methods not showing their return value type
* Fixed an issue where destroyed UnityEngine.Objects would cause issues in the inspector
* Fixed an issue when caching a ValueCollection of a Dictionary (the generic argument for the Entry Type is the last arg, not the first as with other Enumerables)
2020-10-10 20:19:56 +11:00
867370ccee 2.0.1
* Added unstrip fix for GetRootSceneObjects using Il2CPP internal call
2020-10-09 21:11:15 +11:00
35eb78ca5d Update overview.png 2020-10-08 06:26:15 +11:00
f1c3771c24 2.0.0
lots, see release description
2020-10-08 06:15:42 +11:00
b012e2305c Cleanups 2020-10-07 16:20:34 +11:00
e309821743 Update README.md 2020-10-05 23:13:09 +11:00
56bedc9c6b Merging the two Mono builds, now just targets .NET 3.5 2020-10-05 23:08:59 +11:00
59c5b13a05 1.8.3.1 2020-10-05 20:25:51 +11:00
b8b6cc1605 1.8.0.1
* Added some internal caching for Enum Names, should vastly improve speed when inspecting certain classes (worst case scenario I found went from over 50 seconds to less than 1 second).
* ILRepack is now done as part of the build process, should simplify things if you are building the project yourself.
2020-10-05 20:25:25 +11:00
912b1b80ff using ILRepack MSBuild task, adding some base libs 2020-10-05 18:32:38 +11:00
6a9c64c2a1 Update README for new build process 2020-10-04 20:48:08 +11:00
54deecd312 Update README.md 2020-10-04 20:11:05 +11:00
403943a41f Update README.md 2020-10-04 20:09:45 +11:00
e7aa01ebc8 1.8.3
* Merging `mcs.dll` into the main `Explorer.dll` file, no longer needs to be in the Mods / Plugins folder.
2020-10-04 19:01:39 +11:00
bf6d526284 1.8.23
* Fixed an issue in Mono games when the target you are inspecting is destroyed (window would not close as it should).
* Cleaned up and refactored the Input support so it's easier to manage.
2020-10-03 20:19:44 +10:00
c991cb4b22 1.8.22
* Some performance improvements for the new InputSystem support (affects some 2019.3+ games)
* Fixed a small mistake with left/right mouse button checking.
2020-10-02 18:40:51 +10:00
3971e49ce1 Update README.md 2020-10-01 20:41:48 +10:00
6920ca1129 Update README.md 2020-10-01 20:26:25 +10:00
748e0cabcb 1.8.21
* Fixed a bug when editing a Text Field and the input string is `null`. Only affected Il2Cpp games, appeared in 1.8.0.
* Added a menu page for editing the Explorer Settings in-game, called `Options`.
* Added a new setting for default Items per Page Limit (for all "Pages" in Explorer).
2020-10-01 20:20:52 +10:00
b4b5f1ec93 1.8.2
* Added support for games which use the new InputSystem module and have disabled LegacyInputModule
2020-10-01 18:57:28 +10:00
5afaf85859 Update README.md 2020-10-01 17:16:00 +10:00
b65e417ecb Update GUIUnstrip.cs 2020-09-30 01:59:35 +10:00
23723a4ffd Some more unstrip fixes, and a few cleanups 2020-09-30 01:52:49 +10:00
dab7ecd441 Cleanups and refactorings, and some small UI fixes 2020-09-29 05:40:06 +10:00
f1406d016f Update README.md 2020-09-28 16:19:23 +10:00
99e801b3bd Update README.md 2020-09-28 02:11:35 +10:00
20133e123c Update README.md 2020-09-28 02:04:03 +10:00
142a2a4750 Update README.md 2020-09-27 23:04:42 +10:00
629403a74d 1.8.0 cleanup 2020-09-27 22:52:08 +10:00
873d0f277d Update README.md 2020-09-27 22:15:54 +10:00
99 changed files with 7893 additions and 4300 deletions

134
README.md
View File

@ -1,5 +1,3 @@
# Explorer [![Version](https://img.shields.io/badge/MelonLoader-0.2.7.1-green.svg)](https://github.com/HerpDerpinstine/MelonLoader) [![Version](https://img.shields.io/badge/BepInEx-5.3.0-green.svg)](https://github.com/BepInEx/BepInEx)
<p align="center">
<img align="center" src="icon.png">
</p>
@ -14,114 +12,82 @@
<img src="https://img.shields.io/github/downloads/sinai-dev/Explorer/total.svg" />
</p>
- [Current status](#current-status)
- [How to install](#how-to-install)
- [How to use](#how-to-use)
- [Mod Config](#mod-config)
- [Releases](#releases)
- [Features](#features)
- [Mouse Control](#mouse-control)
- [How to install](#how-to-install)
- [Mod Config](#mod-config)
- [Mouse Control](#mouse-control)
- [Building](#building)
- [Credits](#credits)
## Current status
## Releases
| Mod Loader | Il2Cpp | Mono |
| ----------- | ------ | ---- |
| MelonLoader | ✔ | ✔ |
| BepInEx | <b>?</b> (WIP) | ✔ |
| [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) | ✔️ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.MelonLoader.Il2Cpp.zip) | ✔️ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.MelonLoader.Mono.zip) |
| [BepInEx](https://github.com/BepInEx/BepInEx) | ❔ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.BepInEx.Il2Cpp.zip) | ✔️ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.BepInEx.Mono.zip) |
<b>IL2CPP Issues:</b>
* .NET 3.5 is not currently supported (Unity 5.6.1 and older), this might change in the future.
* Some methods may still fail with a `MissingMethodException`, please let me know if you experience this (with full MelonLoader log please).
<b>Il2Cpp Issues:</b>
* Some methods may still fail with a `MissingMethodException`, please let me know if you experience this (with full debug log please).
* Reflection may fail with certain types, see [here](https://github.com/knah/Il2CppAssemblyUnhollower#known-issues) for more details.
* Scrolling with mouse wheel in the Explorer menu may not work on all games at the moment.
## Features
<p align="center">
<img src="https://raw.githubusercontent.com/sinai-dev/Explorer/master/overview.png">
</p>
* <b>Scene Explorer</b>: Simple menu to traverse the Transform heirarchy of the scene.
* <b>GameObject Inspector</b>: Various helpful tools to see and manipulate the GameObject, similar to what you can do in the Editor.
* <b>Reflection Inspector</b>: Inspect Properties and Fields. Can also set primitive values and evaluate primitive methods.
* <b>Search</b>: Search for UnityEngine.Objects with various filters, or use the helpers for static Instances and Classes.
* <b>C# Console</b>: Interactive console for evaluating C# methods on the fly, with some basic helpers.
* <b>Inspect-under-mouse</b>: Hover over an object with a collider and inspect it by clicking on it.
## How to install
### MelonLoader
Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be installed for your game.
1. Download the relevant <b>Explorer_MelonLoader_.zip</b> from [Releases](https://github.com/sinai-dev/Explorer/releases).
1. Download the relevant release from above.
2. Unzip the file into the `Mods` folder in your game's installation directory, created by MelonLoader.
3. Make sure it's not in a sub-folder, `Explorer.dll` and `mcs.dll` should be directly in the `Mods\` folder.
3. Make sure it's not in a sub-folder, `Explorer.dll` should be directly in the `Mods\` folder.
### BepInEx
Requires [BepInEx](https://github.com/BepInEx/BepInEx) to be installed for your game.
1. Download the relevant <b>Explorer_BepInEx_.zip</b> from [Releases](https://github.com/sinai-dev/Explorer/releases).
2. Unzip the file into the `BepInEx\plugins\` folder in your game's installation directory, created by MelonLoader.
3. Make sure it's not in a sub-folder, `Explorer.dll` and `mcs.dll` should be directly in the `plugins\` folder.
1. Download the relevant release from above.
2. Unzip the file into the `BepInEx\plugins\` folder in your game's installation directory, created by BepInEx.
3. Make sure it's not in a sub-folder, `Explorer.dll` should be directly in the `plugins\` folder.
## How to use
## Mod Config
* Press F7 to show or hide the menu.
* Use the Scene Explorer or the Object Search to start Exploring, or the C# Console to test some code.
* See below for more specific details.
There is a simple Mod Config for the Explorer. You can access the settings via the "Options" page of the main menu.
### Mod Config
There is a simple Mod Config for the Explorer, which is generated the first time you run it.
This config is generated to `[Game_Directory]\Mods\Explorer\config.xml`. Edit the config while the game is closed if you wish to change it.
`Main_Menu_Toggle` (KeyCode)
* Sets the keybinding for the Main Menu toggle (show/hide all Explorer windows)
`Main Menu Toggle` (KeyCode) | Default: `F7`
* See [this article](https://docs.unity3d.com/ScriptReference/KeyCode.html) for a full list of all accepted KeyCodes.
* Default: `F7`
`Default_Window_Size` (Vector2)
`Default Window Size` (Vector2) | Default: `x: 550, y: 700`
* Sets the default width and height for all Explorer windows when created.
* `x` is width, `y` is height.
* Default: `<x>550</x> <y>700</y>`
## Features
[![](overview.png)](https://raw.githubusercontent.com/sinai-dev/Explorer/master/overview.png)
`Default Items per Page` (int) | Default: `20`
* Sets the default items per page when viewing lists or search results.
<i>An overview of the different Explorer menus.</i>
`Enable Bitwise Editing` (bool) | Default: `false`
* Whether or not to show the Bitwise Editing helper when inspecting integers
### Scene Explorer
`Enable Tab View` (bool) | Default: `true`
* Whether or not all inspector windows a grouped into a single window with tabs.
* A simple menu which allows you to traverse the Transform heirarchy of the scene.
* Click on a GameObject to set it as the current path, or <b>Inspect</b> it to send it to an Inspector Window.
`Default Output Path` (string) | Default: `Mods\Explorer`
* Where output is generated to, by default (for Texture PNG saving, etc).
### Inspectors
## Mouse Control
Explorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Reflection Inspector</b>.
Explorer can force the mouse to be visible and unlocked when the menu is open, if you have enabled "Force Unlock Mouse" (Left-Alt toggle). Explorer also attempts to prevent clicking-through onto the game behind the Explorer menu.
<b>Tips:</b>
* When in Tab View, GameObjects are denoted by a [G] prefix, and Reflection objects are denoted by a [R] prefix.
* Hold <b>Left Shift</b> when you click the Inspect button to force Reflection mode for GameObjects and Transforms.
### GameObject Inspector
* Allows you to see the children and components on a GameObject.
* Can use some basic GameObject Controls such as translating and rotating the object, destroy it, clone it, etc.
### Reflection Inspector
* The Reflection Inspector is used for all other supported objects.
* Allows you to inspect Properties, Fields and basic Methods, as well as set primitive values and evaluate primitive methods.
* Can search and filter members for the ones you are interested in.
### Object Search
* You can search for an `UnityEngine.Object` with the Object Search feature.
* Filter by name, type, etc.
* For GameObjects and Transforms you can filter which scene they are found in too.
### C# console
* A simple C# console, allows you to execute a method body on the fly.
### Inspect-under-mouse
* Press Shift+RMB (Right Mouse Button) while the Explorer menu is open to begin Inspect-Under-Mouse.
* Hover over your desired object, if you see the name appear then you can click on it to inspect it.
* Only objects with Colliders are supported.
### Mouse Control
Explorer can force the mouse to be visible and unlocked when the menu is open, if you have enabled "Force Unlock Mouse" (Left-Alt toggle). However, you may also want to prevent the mouse clicking-through onto the game behind Explorer, this is possible but it requires specific patches for that game.
If you need more mouse control:
* For VRChat, use [VRCExplorerMouseControl](https://github.com/sinai-dev/VRCExplorerMouseControl)
* For Hellpoint, use [HPExplorerMouseControl](https://github.com/sinai-dev/Hellpoint-Mods/tree/master/HPExplorerMouseControl/HPExplorerMouseControl)
@ -130,10 +96,11 @@ Explorer can force the mouse to be visible and unlocked when the menu is open, i
For example:
```csharp
using Explorer;
using Harmony;
using Harmony; // or 'using HarmonyLib;' for BepInEx
// ...
[HarmonyPatch(typeof(MyGame.MenuClass), nameof(MyGame.MenuClass.CursorUpdate)]
public class MenuClass_CursorUpdate
// You will need to figure out the relevant Class and Method for your game using dnSpy.
[HarmonyPatch(typeof(MyGame.InputManager), nameof(MyGame.InputManager.Update))]
public class InputManager_Update
{
[HarmonyPrefix]
public static bool Prefix()
@ -146,14 +113,15 @@ public class MenuClass_CursorUpdate
## Building
If you'd like to build this yourself, everything you need (other than MelonLoader) is included with this repository, there is no need for recursive cloning etc.
If you'd like to build this yourself, you will need to have installed BepInEx and/or MelonLoader for at least one Unity game. If you want to build all 4 versions, you will need at least one Il2Cpp and one Mono game, with BepInEx and MelonLoader installed for both.
1. Install MelonLoader for your game.
1. Install MelonLoader or BepInEx for your game.
2. Open the `src\Explorer.csproj` file in a text editor.
3. Set the relevant `GameFolder` value(s) for the version(s) you want to build, eg. set `MLCppGameFolder` if you want to build for a MelonLoader Il2Cpp game.
3. Set the relevant `GameFolder` values for the versions you want to build, eg. set `MLCppGameFolder` if you want to build for a MelonLoader Il2Cpp game.
4. Open the `src\Explorer.sln` project.
5. Select `Solution 'Explorer' (1 of 1 project)` in the Solution Explorer panel, and set the <b>Active config</b> to the version you want to build, then build it.
5. Select `Solution 'Explorer' (1 of 1 project)` in the Solution Explorer panel, and set the <b>Active config</b> property to the version you want to build, then build it.
5. The DLLs are built to the `Release\` folder in the root of the repository.
6. If ILRepack fails or is missing, use the NuGet package manager to re-install `ILRepack.Lib.MSBuild.Task`, then re-build.
## Credits

BIN
lib/0Harmony.dll Normal file

Binary file not shown.

BIN
lib/BepInEx.Core.dll Normal file

Binary file not shown.

BIN
lib/BepInEx.IL2CPP.dll Normal file

Binary file not shown.

BIN
lib/BepInEx.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 KiB

After

Width:  |  Height:  |  Size: 392 KiB

View File

@ -0,0 +1,26 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Explorer.UI;
namespace Explorer.CacheObject
{
public class CacheEnumerated : CacheObjectBase
{
public int Index { get; set; }
public IList RefIList { get; set; }
public InteractiveEnumerable ParentEnumeration { get; set; }
public override bool CanWrite => RefIList != null && ParentEnumeration.OwnerCacheObject.CanWrite;
public override void SetValue()
{
RefIList[Index] = IValue.Value;
ParentEnumeration.Value = RefIList;
ParentEnumeration.OwnerCacheObject.SetValue();
}
}
}

View File

@ -0,0 +1,76 @@
using System;
using System.Reflection;
using Explorer.CacheObject;
using UnityEngine;
using Explorer.Helpers;
namespace Explorer
{
public static class CacheFactory
{
public static CacheObjectBase GetCacheObject(object obj)
{
if (obj == null) return null;
return GetCacheObject(obj, ReflectionHelpers.GetActualType(obj));
}
public static CacheObjectBase GetCacheObject(object obj, Type type)
{
var ret = new CacheObjectBase();
ret.Init(obj, type);
return ret;
}
public static CacheMember GetCacheObject(MemberInfo member, object declaringInstance)
{
CacheMember ret;
if (member is MethodInfo mi && CanProcessArgs(mi.GetParameters()))
{
ret = new CacheMethod();
ret.InitMember(mi, declaringInstance);
}
else if (member is PropertyInfo pi && CanProcessArgs(pi.GetIndexParameters()))
{
ret = new CacheProperty();
ret.InitMember(pi, declaringInstance);
}
else if (member is FieldInfo fi)
{
ret = new CacheField();
ret.InitMember(fi, declaringInstance);
}
else
{
return null;
}
return ret;
}
public static bool CanProcessArgs(ParameterInfo[] parameters)
{
foreach (var param in parameters)
{
var pType = param.ParameterType;
if (pType.IsByRef && pType.HasElementType)
{
pType = pType.GetElementType();
}
if (pType.IsPrimitive || pType == typeof(string))
{
continue;
}
else
{
return false;
}
}
return true;
}
}
}

View File

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Explorer.UI;
using Explorer.Helpers;
namespace Explorer.CacheObject
{
public class CacheField : CacheMember
{
public override bool IsStatic => (MemInfo as FieldInfo).IsStatic;
public override void InitMember(MemberInfo member, object declaringInstance)
{
base.InitMember(member, declaringInstance);
base.Init(null, (member as FieldInfo).FieldType);
UpdateValue();
}
public override void UpdateValue()
{
if (IValue is InteractiveDictionary iDict)
{
if (!iDict.EnsureDictionaryIsSupported())
{
ReflectionException = "Not supported due to TypeInitializationException";
return;
}
}
try
{
var fi = MemInfo as FieldInfo;
IValue.Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance);
base.UpdateValue();
}
catch (Exception e)
{
ReflectionException = ReflectionHelpers.ExceptionToString(e);
}
}
public override void SetValue()
{
var fi = MemInfo as FieldInfo;
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, IValue.Value);
}
}
}

View File

@ -0,0 +1,226 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Explorer.UI;
using Explorer.UI.Shared;
namespace Explorer.CacheObject
{
public class CacheMember : CacheObjectBase
{
public MemberInfo MemInfo { get; set; }
public Type DeclaringType { get; set; }
public object DeclaringInstance { get; set; }
public virtual bool IsStatic { get; private set; }
public override bool HasParameters => m_arguments != null && m_arguments.Length > 0;
public override bool IsMember => true;
public string RichTextName => m_richTextName ?? GetRichTextName();
private string m_richTextName;
public override bool CanWrite => m_canWrite ?? GetCanWrite();
private bool? m_canWrite;
public string ReflectionException { get; set; }
public bool m_evaluated = false;
public bool m_isEvaluating;
public ParameterInfo[] m_arguments = new ParameterInfo[0];
public string[] m_argumentInput = new string[0];
public virtual void InitMember(MemberInfo member, object declaringInstance)
{
MemInfo = member;
DeclaringInstance = declaringInstance;
DeclaringType = member.DeclaringType;
}
public override void UpdateValue()
{
base.UpdateValue();
}
public override void SetValue()
{
// ...
}
public object[] ParseArguments()
{
if (m_arguments.Length < 1)
{
return new object[0];
}
var parsedArgs = new List<object>();
for (int i = 0; i < m_arguments.Length; i++)
{
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType;
if (type.IsByRef)
{
type = type.GetElementType();
}
if (!string.IsNullOrEmpty(input))
{
if (type == typeof(string))
{
parsedArgs.Add(input);
continue;
}
else
{
try
{
var arg = type.GetMethod("Parse", new Type[] { typeof(string) })
.Invoke(null, new object[] { input });
parsedArgs.Add(arg);
continue;
}
catch
{
ExplorerCore.Log($"Argument #{i} '{m_arguments[i].Name}' ({type.Name}), could not parse input '{input}'.");
}
}
}
// No input, see if there is a default value.
if (HasDefaultValue(m_arguments[i]))
{
parsedArgs.Add(m_arguments[i].DefaultValue);
continue;
}
// Try add a null arg I guess
parsedArgs.Add(null);
}
return parsedArgs.ToArray();
}
public static bool HasDefaultValue(ParameterInfo arg) => arg.DefaultValue != DBNull.Value;
public void DrawArgsInput()
{
GUILayout.Label($"<b><color=orange>Arguments:</color></b>", new GUILayoutOption[0]);
for (int i = 0; i < this.m_arguments.Length; i++)
{
var name = this.m_arguments[i].Name;
var input = this.m_argumentInput[i];
var type = this.m_arguments[i].ParameterType.Name;
var label = $"<color={Syntax.Class_Instance}>{type}</color> ";
label += $"<color={Syntax.Local}>{name}</color>";
if (HasDefaultValue(this.m_arguments[i]))
{
label = $"<i>[{label} = {this.m_arguments[i].DefaultValue ?? "null"}]</i>";
}
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(15) });
GUILayout.Label(label, new GUILayoutOption[] { GUILayout.ExpandWidth(false) });
this.m_argumentInput[i] = GUIHelper.TextField(input, new GUILayoutOption[] { GUILayout.ExpandWidth(true) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.EndHorizontal();
}
}
private bool GetCanWrite()
{
if (MemInfo is FieldInfo fi)
m_canWrite = !(fi.IsLiteral && !fi.IsInitOnly);
else if (MemInfo is PropertyInfo pi)
m_canWrite = pi.CanWrite;
else
m_canWrite = false;
return (bool)m_canWrite;
}
private string GetRichTextName()
{
string memberColor = "";
bool isStatic = false;
if (MemInfo is FieldInfo fi)
{
if (fi.IsStatic)
{
isStatic = true;
memberColor = Syntax.Field_Static;
}
else
memberColor = Syntax.Field_Instance;
}
else if (MemInfo is MethodInfo mi)
{
if (mi.IsStatic)
{
isStatic = true;
memberColor = Syntax.Method_Static;
}
else
memberColor = Syntax.Method_Instance;
}
else if (MemInfo is PropertyInfo pi)
{
if (pi.GetAccessors()[0].IsStatic)
{
isStatic = true;
memberColor = Syntax.Prop_Static;
}
else
memberColor = Syntax.Prop_Instance;
}
string classColor;
if (MemInfo.DeclaringType.IsValueType)
{
classColor = Syntax.StructGreen;
}
else if (MemInfo.DeclaringType.IsAbstract && MemInfo.DeclaringType.IsSealed)
{
classColor = Syntax.Class_Static;
}
else
{
classColor = Syntax.Class_Instance;
}
m_richTextName = $"<color={classColor}>{MemInfo.DeclaringType.Name}</color>.";
if (isStatic) m_richTextName += "<i>";
m_richTextName += $"<color={memberColor}>{MemInfo.Name}</color>";
if (isStatic) m_richTextName += "</i>";
// generic method args
if (this is CacheMethod cm && cm.GenericArgs.Length > 0)
{
m_richTextName += "<";
var args = "";
for (int i = 0; i < cm.GenericArgs.Length; i++)
{
if (args != "") args += ", ";
args += $"<color={Syntax.StructGreen}>{cm.GenericArgs[i].Name}</color>";
}
m_richTextName += args;
m_richTextName += ">";
}
return m_richTextName;
}
}
}

View File

@ -3,23 +3,29 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.Helpers;
namespace Explorer
namespace Explorer.CacheObject
{
public class CacheMethod : CacheObjectBase
public class CacheMethod : CacheMember
{
private CacheObjectBase m_cachedReturnValue;
public override bool HasParameters => base.HasParameters || GenericArgs.Length > 0;
public override bool IsStatic => (MemInfo as MethodInfo).IsStatic;
public Type[] GenericArgs { get; private set; }
public Type[][] GenericConstraints { get; private set; }
public string[] GenericArgInput = new string[0];
public override void Init()
public override void InitMember(MemberInfo member, object declaringInstance)
{
var mi = (MemInfo as MethodInfo);
base.InitMember(member, declaringInstance);
var mi = MemInfo as MethodInfo;
GenericArgs = mi.GetGenericArguments();
GenericConstraints = GenericArgs.Select(x => x.GetGenericParameterConstraints())
@ -27,12 +33,15 @@ namespace Explorer
GenericArgInput = new string[GenericArgs.Length];
ValueType = mi.ReturnType;
m_arguments = mi.GetParameters();
m_argumentInput = new string[m_arguments.Length];
base.Init(null, mi.ReturnType);
}
public override void UpdateValue()
{
//base.UpdateValue();
// CacheMethod cannot UpdateValue directly. Need to Evaluate.
}
public void Evaluate()
@ -64,7 +73,8 @@ namespace Explorer
if (ret != null)
{
m_cachedReturnValue = GetCacheObject(ret);
//m_cachedReturnValue = CacheFactory.GetTypeAndCacheObject(ret);
m_cachedReturnValue = CacheFactory.GetCacheObject(ret);
m_cachedReturnValue.UpdateValue();
}
else
@ -117,15 +127,20 @@ namespace Explorer
// ==== GUI DRAW ====
public override void DrawValue(Rect window, float width)
//public override void Draw(Rect window, float width)
//{
// base.Draw(window, width);
//}
public void DrawValue(Rect window, float width)
{
string typeLabel = $"<color={UIStyles.Syntax.Class_Instance}>{ValueType.FullName}</color>";
string typeLabel = $"<color={Syntax.Class_Instance}>{IValue.ValueType.FullName}</color>";
if (m_evaluated)
{
if (m_cachedReturnValue != null)
{
m_cachedReturnValue.DrawValue(window, width);
m_cachedReturnValue.IValue.DrawValue(window, width);
}
else
{
@ -137,5 +152,49 @@ namespace Explorer
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> ({typeLabel})", new GUILayoutOption[0]);
}
}
public void DrawGenericArgsInput()
{
GUILayout.Label($"<b><color=orange>Generic Arguments:</color></b>", new GUILayoutOption[0]);
for (int i = 0; i < this.GenericArgs.Length; i++)
{
string types = "";
if (this.GenericConstraints[i].Length > 0)
{
foreach (var constraint in this.GenericConstraints[i])
{
if (types != "") types += ", ";
string type;
if (constraint == null)
type = "Any";
else
type = constraint.ToString();
types += $"<color={Syntax.Class_Instance}>{type}</color>";
}
}
else
{
types = $"<color={Syntax.Class_Instance}>Any</color>";
}
var input = this.GenericArgInput[i];
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label(
$"<color={Syntax.StructGreen}>{this.GenericArgs[i].Name}</color>",
new GUILayoutOption[] { GUILayout.Width(15) }
);
this.GenericArgInput[i] = GUIHelper.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label(types, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
}
}
}
}

View File

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Explorer.UI;
using Explorer.UI.Shared;
using Explorer.Helpers;
namespace Explorer.CacheObject
{
public class CacheObjectBase
{
public InteractiveValue IValue;
public virtual bool CanWrite => false;
public virtual bool HasParameters => false;
public virtual bool IsMember => false;
public bool IsStaticClassSearchResult { get; set; }
public virtual void Init(object obj, Type valueType)
{
if (valueType == null && obj == null)
{
return;
}
//ExplorerCore.Log("Initializing InteractiveValue of type " + valueType.FullName);
InteractiveValue interactive;
if (valueType == typeof(GameObject) || valueType == typeof(Transform))
{
interactive = new InteractiveGameObject();
}
else if (valueType == typeof(Texture2D))
{
interactive = new InteractiveTexture2D();
}
else if (valueType == typeof(Texture))
{
interactive = new InteractiveTexture();
}
else if (valueType == typeof(Sprite))
{
interactive = new InteractiveSprite();
}
else if (valueType.IsPrimitive || valueType == typeof(string))
{
interactive = new InteractivePrimitive();
}
else if (valueType.IsEnum)
{
if (valueType.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] attributes && attributes.Length > 0)
{
interactive = new InteractiveFlags();
}
else
{
interactive = new InteractiveEnum();
}
}
else if (valueType == typeof(Vector2) || valueType == typeof(Vector3) || valueType == typeof(Vector4))
{
interactive = new InteractiveVector();
}
else if (valueType == typeof(Quaternion))
{
interactive = new InteractiveQuaternion();
}
else if (valueType == typeof(Color))
{
interactive = new InteractiveColor();
}
else if (valueType == typeof(Rect))
{
interactive = new InteractiveRect();
}
// must check this before IsEnumerable
else if (ReflectionHelpers.IsDictionary(valueType))
{
interactive = new InteractiveDictionary();
}
else if (ReflectionHelpers.IsEnumerable(valueType))
{
interactive = new InteractiveEnumerable();
}
else
{
interactive = new InteractiveValue();
}
interactive.Value = obj;
interactive.ValueType = valueType;
this.IValue = interactive;
this.IValue.OwnerCacheObject = this;
UpdateValue();
this.IValue.Init();
}
public virtual void Draw(Rect window, float width)
{
IValue.Draw(window, width);
}
public virtual void UpdateValue()
{
IValue.UpdateValue();
}
public virtual void SetValue() => throw new NotImplementedException();
}
}

View File

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Explorer.UI;
using Explorer.Helpers;
namespace Explorer.CacheObject
{
public class CacheProperty : CacheMember
{
public override bool IsStatic => (MemInfo as PropertyInfo).GetAccessors()[0].IsStatic;
public override void InitMember(MemberInfo member, object declaringInstance)
{
base.InitMember(member, declaringInstance);
var pi = member as PropertyInfo;
this.m_arguments = pi.GetIndexParameters();
this.m_argumentInput = new string[m_arguments.Length];
base.Init(null, pi.PropertyType);
UpdateValue();
}
public override void UpdateValue()
{
if (HasParameters && !m_isEvaluating)
{
// Need to enter parameters first.
return;
}
if (IValue is InteractiveDictionary iDict)
{
if (!iDict.EnsureDictionaryIsSupported())
{
ReflectionException = "Not supported due to TypeInitializationException";
return;
}
}
try
{
var pi = MemInfo as PropertyInfo;
if (pi.CanRead)
{
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
IValue.Value = pi.GetValue(target, ParseArguments());
base.UpdateValue();
}
else // create a dummy value for Write-Only properties.
{
if (IValue.ValueType == typeof(string))
{
IValue.Value = "";
}
else
{
IValue.Value = Activator.CreateInstance(IValue.ValueType);
}
}
}
catch (Exception e)
{
ReflectionException = ReflectionHelpers.ExceptionToString(e);
}
}
public override void SetValue()
{
var pi = MemInfo as PropertyInfo;
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
pi.SetValue(target, IValue.Value, ParseArguments());
}
}
}

View File

@ -1,630 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace Explorer
{
public abstract class CacheObjectBase
{
public object Value;
public Type ValueType;
public MemberInfo MemInfo { get; set; }
public Type DeclaringType { get; set; }
public object DeclaringInstance { get; set; }
public virtual bool HasParameters => m_arguments != null && m_arguments.Length > 0;
public bool m_evaluated = false;
public bool m_isEvaluating;
public ParameterInfo[] m_arguments = new ParameterInfo[0];
public string[] m_argumentInput = new string[0];
public string ReflectionException { get; set; }
public string RichTextName => m_richTextName ?? GetRichTextName();
private string m_richTextName;
public bool CanWrite
{
get
{
if (MemInfo is FieldInfo fi)
return !(fi.IsLiteral && !fi.IsInitOnly);
else if (MemInfo is PropertyInfo pi)
return pi.CanWrite;
else
return false;
}
}
public virtual void Init() { }
public abstract void DrawValue(Rect window, float width);
/// <summary>
/// Get CacheObject from only an object instance
/// Calls GetCacheObject(obj, memberInfo, declaringInstance) with (obj, null, null)</summary>
public static CacheObjectBase GetCacheObject(object obj)
{
return GetCacheObject(obj, null, null);
}
/// <summary>
/// Get CacheObject from an object instance and provide the value type
/// Calls GetCacheObjectImpl directly</summary>
public static CacheObjectBase GetCacheObject(object obj, Type valueType)
{
return GetCacheObjectImpl(obj, null, null, valueType);
}
/// <summary>
/// Get CacheObject from only a MemberInfo and declaring instance
/// Calls GetCacheObject(obj, memberInfo, declaringInstance) with (null, memberInfo, declaringInstance)</summary>
public static CacheObjectBase GetCacheObject(MemberInfo memberInfo, object declaringInstance)
{
return GetCacheObject(null, memberInfo, declaringInstance);
}
/// <summary>
/// Get CacheObject from either an object or MemberInfo, and don't provide the type.
/// This gets the type and then calls GetCacheObjectImpl</summary>
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance)
{
Type type = null;
if (memberInfo != null)
{
if (memberInfo is FieldInfo fi)
{
type = fi.FieldType;
}
else if (memberInfo is PropertyInfo pi)
{
type = pi.PropertyType;
}
else if (memberInfo is MethodInfo mi)
{
type = mi.ReturnType;
}
}
else if (obj != null)
{
type = ReflectionHelpers.GetActualType(obj);
}
if (type == null)
{
return null;
}
return GetCacheObjectImpl(obj, memberInfo, declaringInstance, type);
}
/// <summary>
/// Actual GetCacheObject implementation (private)
/// </summary>
private static CacheObjectBase GetCacheObjectImpl(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
{
CacheObjectBase holder;
var pi = memberInfo as PropertyInfo;
var mi = memberInfo as MethodInfo;
// Check if can process args
if ((pi != null && !CanProcessArgs(pi.GetIndexParameters()))
|| (mi != null && !CanProcessArgs(mi.GetParameters())))
{
return null;
}
if (mi != null)
{
holder = new CacheMethod();
}
else if (valueType == typeof(GameObject) || valueType == typeof(Transform))
{
holder = new CacheGameObject();
}
else if (valueType.IsPrimitive || valueType == typeof(string))
{
holder = new CachePrimitive();
}
else if (valueType.IsEnum)
{
if (valueType.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] attributes && attributes.Length > 0)
{
holder = new CacheEnumFlags();
}
else
{
holder = new CacheEnum();
}
}
else if (valueType == typeof(Vector2) || valueType == typeof(Vector3) || valueType == typeof(Vector4))
{
holder = new CacheVector();
}
else if (valueType == typeof(Quaternion))
{
holder = new CacheQuaternion();
}
else if (valueType == typeof(Color))
{
holder = new CacheColor();
}
else if (valueType == typeof(Rect))
{
holder = new CacheRect();
}
// must check this before IsEnumerable
else if (ReflectionHelpers.IsDictionary(valueType))
{
holder = new CacheDictionary();
}
else if (ReflectionHelpers.IsEnumerable(valueType))
{
holder = new CacheList();
}
else
{
holder = new CacheOther();
}
holder.Value = obj;
holder.ValueType = valueType;
if (memberInfo != null)
{
holder.MemInfo = memberInfo;
holder.DeclaringType = memberInfo.DeclaringType;
holder.DeclaringInstance = declaringInstance;
}
if (pi != null)
{
holder.m_arguments = pi.GetIndexParameters();
}
else if (mi != null)
{
holder.m_arguments = mi.GetParameters();
}
holder.m_argumentInput = new string[holder.m_arguments.Length];
holder.UpdateValue();
holder.Init();
return holder;
}
public static bool CanProcessArgs(ParameterInfo[] parameters)
{
foreach (var param in parameters)
{
var pType = param.ParameterType;
if (pType.IsByRef && pType.HasElementType)
{
pType = pType.GetElementType();
}
if (pType.IsPrimitive || pType == typeof(string))
{
continue;
}
else
{
return false;
}
}
return true;
}
public float CalcWhitespace(Rect window)
{
if (!(this is IExpandHeight)) return 0f;
float whitespace = (this as IExpandHeight).WhiteSpace;
if (whitespace > 0)
{
ClampLabelWidth(window, ref whitespace);
}
return whitespace;
}
public object[] ParseArguments()
{
var parsedArgs = new List<object>();
for (int i = 0; i < m_arguments.Length; i++)
{
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType;
if (type.IsByRef)
{
type = type.GetElementType();
}
if (!string.IsNullOrEmpty(input))
{
// strings can obviously just be used directly
if (type == typeof(string))
{
parsedArgs.Add(input);
continue;
}
else
{
// try to invoke the parse method and use that.
try
{
parsedArgs.Add(type.GetMethod("Parse", new Type[] { typeof(string) })
.Invoke(null, new object[] { input }));
continue;
}
catch
{
ExplorerCore.Log($"Argument #{i} '{m_arguments[i].Name}' ({type.Name}), could not parse input '{input}'.");
}
}
}
// Didn't use input, see if there is a default value.
if (HasDefaultValue(m_arguments[i]))
{
parsedArgs.Add(m_arguments[i].DefaultValue);
continue;
}
// Try add a null arg I guess
parsedArgs.Add(null);
}
return parsedArgs.ToArray();
}
public static bool HasDefaultValue(ParameterInfo arg)
{
return
#if NET35
arg.DefaultValue != null; // rip null default args in NET35
#else
arg.HasDefaultValue;
#endif
}
public virtual void UpdateValue()
{
if (MemInfo == null)
{
return;
}
if (HasParameters && !m_isEvaluating)
{
// Need to enter parameters first
return;
}
try
{
if (MemInfo.MemberType == MemberTypes.Field)
{
var fi = MemInfo as FieldInfo;
Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance);
}
else if (MemInfo.MemberType == MemberTypes.Property)
{
var pi = MemInfo as PropertyInfo;
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
Value = pi.GetValue(target, ParseArguments());
}
ReflectionException = null;
m_evaluated = true;
m_isEvaluating = false;
}
catch (Exception e)
{
ReflectionException = ReflectionHelpers.ExceptionToString(e);
}
}
public void SetValue()
{
try
{
if (MemInfo.MemberType == MemberTypes.Field)
{
var fi = MemInfo as FieldInfo;
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, Value);
}
else if (MemInfo.MemberType == MemberTypes.Property)
{
var pi = MemInfo as PropertyInfo;
if (HasParameters)
{
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value, ParseArguments());
}
else
{
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value, null);
}
}
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Error setting value: {e.GetType()}, {e.Message}");
}
}
// ========= Gui Draw ==========
public const float MAX_LABEL_WIDTH = 400f;
public const string EVALUATE_LABEL = "<color=lime>Evaluate</color>";
public static void ClampLabelWidth(Rect window, ref float labelWidth)
{
float min = window.width * 0.37f;
if (min > MAX_LABEL_WIDTH) min = MAX_LABEL_WIDTH;
labelWidth = Mathf.Clamp(labelWidth, min, MAX_LABEL_WIDTH);
}
public void Draw(Rect window, float labelWidth = 215f)
{
if (labelWidth > 0)
{
ClampLabelWidth(window, ref labelWidth);
}
if (MemInfo != null)
{
GUILayout.Label(RichTextName, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
}
else
{
GUIUnstrip.Space(labelWidth);
}
var cm = this as CacheMethod;
if (HasParameters)
{
GUILayout.BeginVertical(new GUILayoutOption[0]);
if (m_isEvaluating)
{
if (cm != null && cm.GenericArgs.Length > 0)
{
GUILayout.Label($"<b><color=orange>Generic Arguments:</color></b>", new GUILayoutOption[0]);
for (int i = 0; i < cm.GenericArgs.Length; i++)
{
string types = "";
if (cm.GenericConstraints[i].Length > 0)
{
foreach (var constraint in cm.GenericConstraints[i])
{
if (types != "") types += ", ";
string type;
if (constraint == null)
type = "Any";
else
type = constraint.ToString();
types += $"<color={UIStyles.Syntax.Class_Instance}>{type}</color>";
}
}
else
{
types = $"<color={UIStyles.Syntax.Class_Instance}>Any</color>";
}
var input = cm.GenericArgInput[i];
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label($"<color={UIStyles.Syntax.StructGreen}>{cm.GenericArgs[i].Name}</color>", new GUILayoutOption[] { GUILayout.Width(15) });
cm.GenericArgInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label(types, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
}
}
if (m_arguments.Length > 0)
{
GUILayout.Label($"<b><color=orange>Arguments:</color></b>", new GUILayoutOption[0]);
for (int i = 0; i < m_arguments.Length; i++)
{
var name = m_arguments[i].Name;
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType.Name;
var label = $"<color={UIStyles.Syntax.Class_Instance}>{type}</color> ";
label += $"<color={UIStyles.Syntax.Local}>{name}</color>";
if (HasDefaultValue(m_arguments[i]))
{
label = $"<i>[{label} = {m_arguments[i].DefaultValue ?? "null"}]</i>";
}
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(15) });
m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label(label, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
}
}
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{
if (cm != null)
{
cm.Evaluate();
}
else
{
UpdateValue();
}
}
if (GUILayout.Button("Cancel", new GUILayoutOption[] { GUILayout.Width(70) }))
{
m_isEvaluating = false;
}
GUILayout.EndHorizontal();
}
else
{
var lbl = $"Evaluate (";
int len = m_arguments.Length;
if (cm != null) len += cm.GenericArgs.Length;
lbl += len + " params)";
if (GUILayout.Button(lbl, new GUILayoutOption[] { GUILayout.Width(150) }))
{
m_isEvaluating = true;
}
}
GUILayout.EndVertical();
// new line and space
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(labelWidth);
}
else if (cm != null)
{
//GUILayout.BeginHorizontal(null);
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{
cm.Evaluate();
}
// new line and space
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(labelWidth);
}
string typeName = $"<color={UIStyles.Syntax.Class_Instance}>{ValueType.FullName}</color>";
if (!string.IsNullOrEmpty(ReflectionException))
{
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", new GUILayoutOption[0]);
}
else if ((HasParameters || this is CacheMethod) && !m_evaluated)
{
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> ({typeName})", new GUILayoutOption[0]);
}
else if (Value == null && !(this is CacheMethod))
{
GUILayout.Label($"<i>null ({typeName})</i>", new GUILayoutOption[0]);
}
else
{
DrawValue(window, window.width - labelWidth - 90);
}
}
private string GetRichTextName()
{
string memberColor = "";
bool isStatic = false;
if (MemInfo is FieldInfo fi)
{
if (fi.IsStatic)
{
isStatic = true;
memberColor = UIStyles.Syntax.Field_Static;
}
else
memberColor = UIStyles.Syntax.Field_Instance;
}
else if (MemInfo is MethodInfo mi)
{
if (mi.IsStatic)
{
isStatic = true;
memberColor = UIStyles.Syntax.Method_Static;
}
else
memberColor = UIStyles.Syntax.Method_Instance;
}
else if (MemInfo is PropertyInfo pi)
{
if (pi.GetAccessors()[0].IsStatic)
{
isStatic = true;
memberColor = UIStyles.Syntax.Prop_Static;
}
else
memberColor = UIStyles.Syntax.Prop_Instance;
}
string classColor = MemInfo.DeclaringType.IsAbstract && MemInfo.DeclaringType.IsSealed
? UIStyles.Syntax.Class_Static
: UIStyles.Syntax.Class_Instance;
m_richTextName = $"<color={classColor}>{MemInfo.DeclaringType.Name}</color>.";
if (isStatic) m_richTextName += "<i>";
m_richTextName += $"<color={memberColor}>{MemInfo.Name}</color>";
if (isStatic) m_richTextName += "</i>";
// generic method args
if (this is CacheMethod cm && cm.GenericArgs.Length > 0)
{
m_richTextName += "<";
var args = "";
for (int i = 0; i < cm.GenericArgs.Length; i++)
{
if (args != "") args += ", ";
args += $"<color={UIStyles.Syntax.StructGreen}>{cm.GenericArgs[i].Name}</color>";
}
m_richTextName += args;
m_richTextName += ">";
}
// Method / Property arguments
//if (m_arguments.Length > 0 || this is CacheMethod)
//{
// m_richTextName += "(";
// var args = "";
// foreach (var param in m_arguments)
// {
// if (args != "") args += ", ";
// args += $"<color={classColor}>{param.ParameterType.Name}</color> ";
// args += $"<color={UIStyles.Syntax.Local}>{param.Name}</color>";
// }
// m_richTextName += args;
// m_richTextName += ")";
//}
return m_richTextName;
}
}
}

View File

@ -1,17 +0,0 @@
using UnityEngine;
namespace Explorer
{
public class CacheGameObject : CacheObjectBase
{
public override void DrawValue(Rect window, float width)
{
UIHelpers.GOButton(Value, null, false, width);
}
public override void UpdateValue()
{
base.UpdateValue();
}
}
}

View File

@ -1,80 +0,0 @@
using System;
using System.Reflection;
using UnityEngine;
namespace Explorer
{
public class CacheOther : CacheObjectBase
{
public string ButtonLabel => m_btnLabel ?? GetButtonLabel();
private string m_btnLabel;
public MethodInfo ToStringMethod => m_toStringMethod ?? GetToStringMethod();
private MethodInfo m_toStringMethod;
public override void UpdateValue()
{
base.UpdateValue();
GetButtonLabel();
}
public override void DrawValue(Rect window, float width)
{
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button(ButtonLabel, new GUILayoutOption[] { GUILayout.Width(width - 15) }))
{
WindowManager.InspectObject(Value, out bool _);
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
}
private MethodInfo GetToStringMethod()
{
try
{
m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0])
?? typeof(object).GetMethod("ToString", new Type[0]);
// test invoke
m_toStringMethod.Invoke(Value, null);
}
catch
{
m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]);
}
return m_toStringMethod;
}
private string GetButtonLabel()
{
if (Value == null) return null;
string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
var classColor = ValueType.IsAbstract && ValueType.IsSealed
? UIStyles.Syntax.Class_Static
: UIStyles.Syntax.Class_Instance;
string typeLabel = $"<color={classColor}>{ValueType.FullName}</color>";
if (Value is UnityEngine.Object)
{
label = label.Replace($"({ValueType.FullName})", $"({typeLabel})");
}
else
{
if (!label.Contains(ValueType.FullName))
{
label += $" ({typeLabel})";
}
else
{
label = label.Replace(ValueType.FullName, typeLabel);
}
}
return m_btnLabel = label;
}
}
}

View File

@ -1,251 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
#if CPP
using UnhollowerRuntimeLib;
#endif
using UnityEngine;
namespace Explorer
{
public class CachePrimitive : CacheObjectBase
{
private bool m_isBool;
private bool m_isString;
private string m_valueToString;
public MethodInfo ParseMethod => m_parseMethod ?? (m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) }));
private MethodInfo m_parseMethod;
private bool m_canBitwiseOperate;
private bool m_inBitwiseMode;
private string m_bitwiseOperatorInput = "0";
private string m_bitwiseToString;
//private BitArray m_bitMask; // not needed I think
public override void Init()
{
if (ValueType == null)
{
ValueType = Value?.GetType();
// has to be a string at this point
if (ValueType == null)
{
ValueType = typeof(string);
}
}
if (ValueType == typeof(string))
{
m_isString = true;
}
else if (ValueType == typeof(bool))
{
m_isBool = true;
}
m_canBitwiseOperate = typeof(int).IsAssignableFrom(ValueType);
}
public override void UpdateValue()
{
base.UpdateValue();
RefreshToString();
}
public void RefreshToString()
{
m_valueToString = Value?.ToString();
if (m_inBitwiseMode)
{
var _int = (int)Value;
m_bitwiseToString = Convert.ToString(_int, toBase: 2);
}
}
public override void DrawValue(Rect window, float width)
{
// bool uses Toggle
if (m_isBool)
{
var b = (bool)Value;
var label = $"<color={(b ? "lime" : "red")}>{b}</color>";
if (CanWrite)
{
b = GUILayout.Toggle(b, label, new GUILayoutOption[0]);
if (b != (bool)Value)
{
SetValueFromInput(b.ToString());
}
}
else
{
GUILayout.Label(label, new GUILayoutOption[0]);
}
return;
}
// all other non-bool values use TextField
GUILayout.BeginVertical(new GUILayoutOption[0]);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
// using ValueType.Name instead of ValueTypeName, because we only want the short name.
GUILayout.Label("<color=#2df7b2><i>" + ValueType.Name + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
int dynSize = 25 + (m_valueToString.Length * 15);
var maxwidth = window.width - 310f;
if (CanWrite) maxwidth -= 60;
if (dynSize > maxwidth)
{
m_valueToString = GUIUnstrip.TextArea(m_valueToString, new GUILayoutOption[] { GUILayout.Width(maxwidth) });
}
else
{
m_valueToString = GUILayout.TextField(m_valueToString, new GUILayoutOption[] { GUILayout.Width(dynSize) });
}
if (CanWrite)
{
if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))
{
SetValueFromInput(m_valueToString);
RefreshToString();
}
}
if (m_canBitwiseOperate)
{
bool orig = m_inBitwiseMode;
m_inBitwiseMode = GUILayout.Toggle(m_inBitwiseMode, "Bitwise?", new GUILayoutOption[0]);
if (orig != m_inBitwiseMode)
{
RefreshToString();
}
}
GUIUnstrip.Space(10);
GUILayout.EndHorizontal();
if (m_inBitwiseMode)
{
if (CanWrite)
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("RHS:", new GUILayoutOption[] { GUILayout.Width(35) });
GUI.skin.label.alignment = TextAnchor.UpperLeft;
if (GUILayout.Button("~", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = ~bit;
RefreshToString();
}
}
if (GUILayout.Button("<<", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value << bit;
RefreshToString();
}
}
if (GUILayout.Button(">>", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value >> bit;
RefreshToString();
}
}
if (GUILayout.Button("|", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value | bit;
RefreshToString();
}
}
if (GUILayout.Button("&", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value & bit;
RefreshToString();
}
}
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value ^ bit;
RefreshToString();
}
}
m_bitwiseOperatorInput = GUILayout.TextField(m_bitwiseOperatorInput, new GUILayoutOption[] { GUILayout.Width(55) });
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"<color=cyan>Binary:</color>", new GUILayoutOption[] { GUILayout.Width(60) });
GUILayout.TextField(m_bitwiseToString, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
}
public void SetValueFromInput(string valueString)
{
if (MemInfo == null)
{
ExplorerCore.Log("Trying to SetValue but the MemberInfo is null!");
return;
}
if (m_isString)
{
Value = valueString;
}
else
{
try
{
Value = ParseMethod.Invoke(null, new object[] { valueString });
//if (m_inBitwiseMode)
//{
// var method = typeof(Convert).GetMethod($"To{ValueType.Name}", new Type[] { typeof(string), typeof(int) });
// Value = method.Invoke(null, new object[] { valueString, 2 });
//}
//else
//{
// Value = ParseMethod.Invoke(null, new object[] { valueString });
//}
}
catch (Exception e)
{
ExplorerCore.Log("Exception parsing value: " + e.GetType() + ", " + e.Message);
}
}
SetValue();
}
}
}

View File

@ -2,7 +2,7 @@
using System.Xml.Serialization;
using UnityEngine;
namespace Explorer
namespace Explorer.Config
{
public class ModConfig
{
@ -13,8 +13,13 @@ namespace Explorer
[XmlIgnore] public static ModConfig Instance;
// Actual configs
public KeyCode Main_Menu_Toggle = KeyCode.F7;
public Vector2 Default_Window_Size = new Vector2(550, 700);
public int Default_Page_Limit = 20;
public bool Bitwise_Support = false;
public bool Tab_View = true;
public string Default_Output_Path = @"Mods\Explorer";
public static void OnLoad()
{
@ -30,9 +35,9 @@ namespace Explorer
}
// returns true if settings successfully loaded
public static bool LoadSettings(bool checkExist = true)
public static bool LoadSettings()
{
if (checkExist && !File.Exists(SETTINGS_PATH))
if (!File.Exists(SETTINGS_PATH))
return false;
try
@ -50,9 +55,9 @@ namespace Explorer
return Instance != null;
}
public static void SaveSettings(bool checkExist = true)
public static void SaveSettings()
{
if (checkExist && File.Exists(SETTINGS_PATH))
if (File.Exists(SETTINGS_PATH))
File.Delete(SETTINGS_PATH);
using (var file = File.Create(SETTINGS_PATH))

View File

@ -2,122 +2,71 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Configuration Condition=" '$(Configuration)' == '' ">Release_ML_Cpp</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}</ProjectGuid>
<OutputType>Library</OutputType>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Explorer</RootNamespace>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
<AssemblyName>Explorer</AssemblyName>
<OutputPath>..\Release\Explorer.MelonLoader.Il2Cpp\</OutputPath>
<DefineConstants>CPP,ML</DefineConstants>
<IsCpp>true</IsCpp>
<IsMelonLoader>true</IsMelonLoader>
<IsNet35>false</IsNet35>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
<RootNamespace>Explorer</RootNamespace>
<AssemblyName>Explorer</AssemblyName>
<!-- Set this to the MelonLoader Il2Cpp Game folder, without the ending '\' character. -->
<MLCppGameFolder>D:\Steam\steamapps\common\Hellpoint</MLCppGameFolder>
<!-- Set this to the MelonLoader Mono Game folder, without the ending '\' character. -->
<MLMonoGameFolder>D:\Steam\steamapps\common\Outward</MLMonoGameFolder>
<!-- Set this to the BepInEx Il2Cpp Game folder, without the ending '\' character. -->
<BIECppGameFolder>D:\Steam\steamapps\common\Outward - Il2Cpp</BIECppGameFolder>
<BIECppGameFolder>D:\Steam\steamapps\common\Outward_Il2Cpp</BIECppGameFolder>
<!-- Set this to the BepInEx Mono Game folder, without the ending '\' character. -->
<BIEMonoGameFolder>D:\Steam\steamapps\common\Outward</BIEMonoGameFolder>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_ML_Cpp|AnyCPU' ">
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Release\ML_Cpp\</OutputPath>
<OutputPath>..\Release\Explorer.MelonLoader.Il2Cpp\</OutputPath>
<DefineConstants>CPP,ML</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
<IsCpp>true</IsCpp>
<IsMelonLoader>true</IsMelonLoader>
<IsNet35>false</IsNet35>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_ML_Mono|AnyCPU' ">
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Release\ML_Mono\</OutputPath>
<DefineConstants>MONO,ML</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
<IsCpp>false</IsCpp>
<IsMelonLoader>true</IsMelonLoader>
<IsNet35>false</IsNet35>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_ML_Mono_NET35|AnyCPU' ">
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Release\ML_Mono_NET35\</OutputPath>
<DefineConstants>MONO,ML,NET35</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\Release\Explorer.MelonLoader.Mono\</OutputPath>
<DefineConstants>MONO,ML</DefineConstants>
<Prefer32Bit>false</Prefer32Bit>
<IsCpp>false</IsCpp>
<IsMelonLoader>true</IsMelonLoader>
<IsNet35>true</IsNet35>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Cpp|AnyCPU' ">
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Release\BIE_Cpp\</OutputPath>
<OutputPath>..\Release\Explorer.BepInEx.Il2Cpp\</OutputPath>
<DefineConstants>CPP,BIE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
<IsCpp>true</IsCpp>
<IsMelonLoader>false</IsMelonLoader>
<IsNet35>false</IsNet35>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Mono|AnyCPU' ">
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Release\BIE_Mono\</OutputPath>
<DefineConstants>MONO,BIE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
<IsCpp>false</IsCpp>
<IsMelonLoader>false</IsMelonLoader>
<IsNet35>false</IsNet35>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Mono_NET35|AnyCPU' ">
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Release\BIE_Mono_NET35\</OutputPath>
<DefineConstants>MONO,BIE,NET35</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
<OutputPath>..\Release\Explorer.BepInEx.Mono\</OutputPath>
<DefineConstants>MONO,BIE</DefineConstants>
<IsCpp>false</IsCpp>
<IsMelonLoader>false</IsMelonLoader>
<IsNet35>true</IsNet35>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@ -126,176 +75,218 @@
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<!-- MCS Ref -->
<Reference Include="mcs" Condition="'$(IsNet35)'=='false'">
<!-- MCS ref -->
<Reference Include="mcs">
<HintPath>..\lib\mcs.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="mcs" Condition="'$(IsNet35)'=='true'">
<HintPath>..\lib\mcs.NET35.dll</HintPath>
<Private>True</Private>
</Reference>
<!-- MelonLoader Il2Cpp core ref -->
<Reference Include="MelonLoader.ModHandler" Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|true'">
<HintPath>$(MLCppGameFolder)\MelonLoader\MelonLoader.ModHandler.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- MelonLoader Mono core ref -->
<Reference Include="MelonLoader.ModHandler" Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|false'">
<HintPath>$(MLMonoGameFolder)\MelonLoader\MelonLoader.ModHandler.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- BepInEx Mono core ref -->
<Reference Include="BepInEx" Condition="'$(IsMelonLoader)|$(IsCpp)'=='false|false'">
<HintPath>$(BIEMonoGameFolder)\BepInEx\core\BepInEx.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="0Harmony" Condition="'$(IsMelonLoader)|$(IsCpp)'=='false|false'">
<HintPath>$(BIEMonoGameFolder)\BepInEx\core\0Harmony.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- BepInEx Il2Cpp core ref -->
<Reference Include="BepInEx" Condition="'$(IsMelonLoader)|$(IsCpp)'=='false|true'">
<HintPath>$(BIECppGameFolder)\BepInEx\core\BepInEx.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="0Harmony" Condition="'$(IsMelonLoader)|$(IsCpp)'=='false|true'">
<HintPath>$(BIECppGameFolder)\BepInEx\core\0Harmony.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="BepInEx.IL2CPP" Condition="'$(IsMelonLoader)|$(IsCpp)'=='false|true'">
<HintPath>$(BIECppGameFolder)\BepInEx\core\BepInEx.IL2CPP.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- MONO UnityEngine.dll ref -->
<Reference Include="UnityEngine" Condition="'$(IsCpp)'=='false'">
</ItemGroup>
<!-- Universal Mono UnityEngine.dll ref (v5.3) -->
<ItemGroup Condition="'$(IsCpp)'=='false'">
<Reference Include="UnityEngine">
<HintPath>..\lib\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- MelonLoader Il2Cpp UnityEngine References -->
<Reference Include="UnhollowerBaseLib" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|true'">
</ItemGroup>
<!-- MelonLoader Mono ref -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|false'">
<Reference Include="MelonLoader.ModHandler">
<HintPath>$(MLMonoGameFolder)\MelonLoader\MelonLoader.ModHandler.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- BepInEx Mono refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='false|false'">
<Reference Include="BepInEx">
<HintPath>$(BIEMonoGameFolder)\BepInEx\core\BepInEx.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="0Harmony">
<HintPath>$(BIEMonoGameFolder)\BepInEx\core\0Harmony.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- MelonLoader Il2Cpp refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|true'">
<Reference Include="MelonLoader.ModHandler">
<HintPath>$(MLCppGameFolder)\MelonLoader\MelonLoader.ModHandler.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnhollowerBaseLib">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Il2Cppmscorlib" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|true'">
<Reference Include="Il2Cppmscorlib">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Il2CppSystem.Core" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|true'">
<Reference Include="Il2CppSystem.Core">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\Il2CppSystem.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|true'">
<Reference Include="UnityEngine">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|true'">
<Reference Include="UnityEngine.CoreModule">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.IMGUIModule" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|true'">
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.PhysicsModule" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|true'">
<Reference Include="UnityEngine.PhysicsModule">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|true'">
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UI" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|true'">
<Reference Include="UnityEngine.UI">
<HintPath>$(MLCppGameFolder)\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- BepInEx Il2Cpp UnityEngine References -->
<Reference Include="UnhollowerBaseLib" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|false'">
</ItemGroup>
<!-- BepInEx Il2Cpp refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='false|true'">
<Reference Include="BepInEx">
<HintPath>$(BIECppGameFolder)\BepInEx\core\BepInEx.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="0Harmony">
<HintPath>$(BIECppGameFolder)\BepInEx\core\0Harmony.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="BepInEx.IL2CPP">
<HintPath>$(BIECppGameFolder)\BepInEx\core\BepInEx.IL2CPP.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnhollowerBaseLib">
<HintPath>$(BIECppGameFolder)\BepInEx\core\UnhollowerBaseLib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Il2Cppmscorlib" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|false'">
<Reference Include="Il2Cppmscorlib">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\Il2Cppmscorlib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Il2CppSystem.Core" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|false'">
<Reference Include="Il2CppSystem.Core">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\Il2CppSystem.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|false'">
<Reference Include="UnityEngine">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|false'">
<Reference Include="UnityEngine.CoreModule">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.IMGUIModule" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|false'">
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.PhysicsModule" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|false'">
<Reference Include="UnityEngine.PhysicsModule">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\UnityEngine.PhysicsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|false'">
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UI" Condition="'$(IsCpp)|$(IsMelonLoader)'=='true|false'">
<Reference Include="UnityEngine.UI">
<HintPath>$(BIECppGameFolder)\BepInEx\unhollowed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CachedObjects\IExpandHeight.cs" />
<Compile Include="CachedObjects\Struct\CacheColor.cs" />
<Compile Include="CachedObjects\Object\CacheDictionary.cs" />
<Compile Include="CachedObjects\Struct\CacheEnum.cs" />
<Compile Include="CachedObjects\Object\CacheGameObject.cs" />
<Compile Include="CachedObjects\Object\CacheList.cs" />
<Compile Include="CachedObjects\Struct\CacheEnumFlags.cs" />
<Compile Include="CachedObjects\Struct\CachePrimitive.cs" />
<Compile Include="CachedObjects\Other\CacheOther.cs" />
<Compile Include="CachedObjects\Other\CacheMethod.cs" />
<Compile Include="CachedObjects\Struct\CacheQuaternion.cs" />
<Compile Include="CachedObjects\Struct\CacheVector.cs" />
<Compile Include="CachedObjects\Struct\CacheRect.cs" />
<Compile Include="CacheObject\CacheEnumerated.cs" />
<Compile Include="CacheObject\CacheFactory.cs" />
<Compile Include="CacheObject\CacheField.cs" />
<Compile Include="CacheObject\CacheMember.cs" />
<Compile Include="CacheObject\CacheMethod.cs" />
<Compile Include="CacheObject\CacheProperty.cs" />
<Compile Include="CacheObject\CacheObjectBase.cs" />
<Compile Include="Helpers\Texture2DHelpers.cs" />
<Compile Include="UI\InteractiveValue\InteractiveValue.cs" />
<Compile Include="UI\InteractiveValue\Object\InteractiveDictionary.cs" />
<Compile Include="UI\InteractiveValue\Object\InteractiveEnumerable.cs" />
<Compile Include="UI\InteractiveValue\Object\InteractiveGameObject.cs" />
<Compile Include="UI\InteractiveValue\Object\InteractiveSprite.cs" />
<Compile Include="UI\InteractiveValue\Object\InteractiveTexture.cs" />
<Compile Include="UI\InteractiveValue\Object\InteractiveTexture2D.cs" />
<Compile Include="UI\InteractiveValue\Struct\InteractiveQuaternion.cs" />
<Compile Include="UI\InteractiveValue\Struct\InteractiveRect.cs" />
<Compile Include="UI\InteractiveValue\Struct\InteractiveVector.cs" />
<Compile Include="UI\InteractiveValue\Struct\InteractiveColor.cs" />
<Compile Include="UI\InteractiveValue\Struct\InteractiveEnum.cs" />
<Compile Include="UI\InteractiveValue\Struct\InteractiveFlags.cs" />
<Compile Include="UI\InteractiveValue\Struct\InteractivePrimitive.cs" />
<Compile Include="Config\ModConfig.cs" />
<Compile Include="ExplorerCore.cs" />
<Compile Include="Explorer_BepInPlugin.cs" />
<Compile Include="Explorer_MelonMod.cs" />
<Compile Include="ExplorerBepInPlugin.cs" />
<Compile Include="ExplorerMelonMod.cs" />
<Compile Include="Extensions\ReflectionExtensions.cs" />
<Compile Include="Helpers\InputHelper.cs" />
<Compile Include="Menu\CursorControl.cs" />
<Compile Include="Tests\TestClass.cs" />
<Compile Include="UnstripFixes\GUIUnstrip.cs" />
<Compile Include="UnstripFixes\LayoutUtilityUnstrip.cs" />
<Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" />
<Compile Include="Extensions\UnityExtensions.cs" />
<Compile Include="Helpers\PageHelper.cs" />
<Compile Include="Helpers\ReflectionHelpers.cs" />
<Compile Include="Menu\UIHelpers.cs" />
<Compile Include="Helpers\UnityHelpers.cs" />
<Compile Include="Menu\InspectUnderMouse.cs" />
<Compile Include="CachedObjects\CacheObjectBase.cs" />
<Compile Include="UnstripFixes\SliderHandlerUnstrip.cs" />
<Compile Include="UnstripFixes\UnstripExtensions.cs" />
<Compile Include="Menu\ResizeDrag.cs" />
<Compile Include="Menu\Windows\TabViewWindow.cs" />
<Compile Include="Menu\Windows\UIWindow.cs" />
<Compile Include="Menu\MainMenu\Pages\ConsolePage.cs" />
<Compile Include="Menu\MainMenu\Pages\Console\REPL.cs" />
<Compile Include="Menu\MainMenu\Pages\Console\REPLHelper.cs" />
<Compile Include="Menu\MainMenu\Pages\WindowPage.cs" />
<Compile Include="Menu\Windows\WindowManager.cs" />
<Compile Include="Menu\MainMenu\MainMenu.cs" />
<Compile Include="Menu\Windows\GameObjectWindow.cs" />
<Compile Include="Menu\Windows\ReflectionWindow.cs" />
<Compile Include="Menu\MainMenu\Pages\ScenePage.cs" />
<Compile Include="Menu\MainMenu\Pages\SearchPage.cs" />
<Compile Include="Menu\UIStyles.cs" />
<Compile Include="Input\IAbstractInput.cs" />
<Compile Include="Tests\TestClass.cs" />
<Compile Include="UI\ForceUnlockCursor.cs" />
<Compile Include="Input\InputManager.cs" />
<Compile Include="Input\InputSystem.cs" />
<Compile Include="Input\LegacyInput.cs" />
<Compile Include="Input\NoInput.cs" />
<Compile Include="UI\Inspectors\InspectUnderMouse.cs" />
<Compile Include="UI\Inspectors\Reflection\InstanceInspector.cs" />
<Compile Include="UI\Inspectors\ReflectionInspector.cs" />
<Compile Include="UI\Inspectors\Reflection\StaticInspector.cs" />
<Compile Include="UI\MainMenu.cs" />
<Compile Include="UI\Main\ConsolePage.cs" />
<Compile Include="UI\Main\Console\AutoComplete.cs" />
<Compile Include="UI\Main\Console\ScriptInteraction.cs" />
<Compile Include="UI\Main\Console\ScriptEvaluator.cs" />
<Compile Include="UI\Main\OptionsPage.cs" />
<Compile Include="UI\Main\ScenePage.cs" />
<Compile Include="UI\Main\SearchPage.cs" />
<Compile Include="UI\Main\BaseMainMenuPage.cs" />
<Compile Include="UI\Shared\Buttons.cs" />
<Compile Include="UI\Shared\IExpandHeight.cs" />
<Compile Include="UI\Shared\PageHelper.cs" />
<Compile Include="UI\Shared\ResizeDrag.cs" />
<Compile Include="UI\Shared\Syntax.cs" />
<Compile Include="UI\Shared\UIStyles.cs" />
<Compile Include="UI\Inspectors\GameObjectInspector.cs" />
<Compile Include="UI\TabViewWindow.cs" />
<Compile Include="UI\WindowBase.cs" />
<Compile Include="UI\WindowManager.cs" />
<Compile Include="Unstrip\ImageConversion\ImageConversionUnstrip.cs" />
<Compile Include="Unstrip\IMGUI\GUIUtilityUnstrip.cs" />
<Compile Include="Unstrip\IMGUI\TextEditorUnstrip.cs" />
<Compile Include="Helpers\ICallHelper.cs" />
<Compile Include="Unstrip\LayerMask\LayerMaskUnstrip.cs" />
<Compile Include="Unstrip\Resources\ResourcesUnstrip.cs" />
<Compile Include="Unstrip\Scene\SceneUnstrip.cs" />
<Compile Include="Unstrip\IMGUI\GUIHelper.cs" />
<Compile Include="Unstrip\IMGUI\LayoutUtilityUnstrip.cs" />
<Compile Include="Unstrip\IMGUI\ScrollViewStateUnstrip.cs" />
<Compile Include="Unstrip\IMGUI\SliderHandlerUnstrip.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Unstrip\IMGUI\GUIUnstrip.cs" />
<Compile Include="Unstrip\IMGUI\SliderStateUnstrip.cs" />
</ItemGroup>
<ItemGroup>
<None Include="ILRepack.targets" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="packages\ILRepack.Lib.MSBuild.Task.2.0.18.1\build\ILRepack.Lib.MSBuild.Task.targets" Condition="Exists('packages\ILRepack.Lib.MSBuild.Task.2.0.18.1\build\ILRepack.Lib.MSBuild.Task.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\ILRepack.Lib.MSBuild.Task.2.0.18.1\build\ILRepack.Lib.MSBuild.Task.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\ILRepack.Lib.MSBuild.Task.2.0.18.1\build\ILRepack.Lib.MSBuild.Task.targets'))" />
</Target>
</Project>

View File

@ -8,23 +8,17 @@ EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release_BIE_Cpp|Any CPU = Release_BIE_Cpp|Any CPU
Release_BIE_Mono_NET35|Any CPU = Release_BIE_Mono_NET35|Any CPU
Release_BIE_Mono|Any CPU = Release_BIE_Mono|Any CPU
Release_ML_Cpp|Any CPU = Release_ML_Cpp|Any CPU
Release_ML_Mono_NET35|Any CPU = Release_ML_Mono_NET35|Any CPU
Release_ML_Mono|Any CPU = Release_ML_Mono|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Cpp|Any CPU.ActiveCfg = Release_BIE_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Cpp|Any CPU.Build.0 = Release_BIE_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Mono_NET35|Any CPU.ActiveCfg = Release_BIE_Mono_NET35|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Mono_NET35|Any CPU.Build.0 = Release_BIE_Mono_NET35|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Mono|Any CPU.ActiveCfg = Release_BIE_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_BIE_Mono|Any CPU.Build.0 = Release_BIE_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Cpp|Any CPU.ActiveCfg = Release_ML_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Cpp|Any CPU.Build.0 = Release_ML_Cpp|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Mono_NET35|Any CPU.ActiveCfg = Release_ML_Mono_NET35|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Mono_NET35|Any CPU.Build.0 = Release_ML_Mono_NET35|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Mono|Any CPU.ActiveCfg = Release_ML_Mono|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_ML_Mono|Any CPU.Build.0 = Release_ML_Mono|Any CPU
EndGlobalSection

109
src/ExplorerBepInPlugin.cs Normal file
View File

@ -0,0 +1,109 @@
#if BIE
using System;
using System.IO;
using System.Reflection;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;
#if CPP
using UnhollowerRuntimeLib;
using BepInEx.IL2CPP;
#endif
namespace Explorer
{
[BepInPlugin(ExplorerCore.GUID, "Explorer", ExplorerCore.VERSION)]
#if CPP
public class ExplorerBepInPlugin : BasePlugin
#else
public class ExplorerBepInPlugin : BaseUnityPlugin
#endif
{
public static ExplorerBepInPlugin Instance;
public static ManualLogSource Logging =>
#if CPP
Instance?.Log;
#else
Instance?.Logger;
#endif
public static readonly Harmony HarmonyInstance = new Harmony(ExplorerCore.GUID);
#if CPP
// temporary for Il2Cpp until scene change delegate works
private static string lastSceneName;
#endif
// Init
#if CPP
public override void Load()
{
#else
internal void Awake()
{
#endif
Instance = this;
#if CPP
ClassInjector.RegisterTypeInIl2Cpp<ExplorerBehaviour>();
var obj = new GameObject(
"ExplorerBehaviour",
new Il2CppSystem.Type[]
{
Il2CppType.Of<ExplorerBehaviour>()
}
);
GameObject.DontDestroyOnLoad(obj);
#else
SceneManager.activeSceneChanged += DoSceneChange;
#endif
new ExplorerCore();
//HarmonyInstance.PatchAll();
}
internal static void DoSceneChange(Scene arg0, Scene arg1)
{
ExplorerCore.OnSceneChange();
}
#if CPP // BepInEx Il2Cpp mod class doesn't have monobehaviour methods yet, so wrap them in a dummy.
public class ExplorerBehaviour : MonoBehaviour
{
public ExplorerBehaviour(IntPtr ptr) : base(ptr) { }
internal void Awake()
{
Logging.LogMessage("ExplorerBehaviour.Awake");
}
#endif
internal void Update()
{
ExplorerCore.Update();
#if CPP
var scene = SceneManager.GetActiveScene();
if (scene.name != lastSceneName)
{
lastSceneName = scene.name;
DoSceneChange(scene, scene);
}
#endif
}
internal void OnGUI()
{
ExplorerCore.OnGUI();
}
#if CPP
}
#endif
}
}
#endif

View File

@ -1,43 +1,57 @@
using UnityEngine;
using System.Collections;
using System.Linq;
using Explorer.Config;
using Explorer.UI;
using Explorer.UI.Inspectors;
using Explorer.UI.Main;
using Explorer.UI.Shared;
using UnityEngine;
namespace Explorer
{
public class ExplorerCore
{
public const string NAME =
#if ML
#if CPP
"Explorer (Il2Cpp, MelonLoader)";
#else
"Explorer (Mono, MelonLoader)";
#endif
#else
#if CPP
"Explorer (Il2Cpp, BepInEx)";
#else
"Explorer (Mono, BepInEx)";
#endif
#endif
public const string VERSION = "1.8.0";
public const string NAME = "Explorer " + VERSION + " (" + PLATFORM + ", " + MODLOADER + ")";
public const string VERSION = "2.0.7";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.explorer";
public const string PLATFORM =
#if CPP
"Il2Cpp";
#else
"Mono";
#endif
public const string MODLOADER =
#if ML
"MelonLoader";
#else
"BepInEx";
#endif
public static ExplorerCore Instance { get; private set; }
public ExplorerCore()
{
if (Instance != null)
{
Log("An instance of Explorer is already active!");
return;
}
Instance = this;
ModConfig.OnLoad();
InputHelper.Init();
new MainMenu();
new WindowManager();
CursorControl.Init();
InputManager.Init();
ForceUnlockCursor.Init();
Log($"{NAME} {VERSION} initialized.");
ShowMenu = true;
Log($"{NAME} initialized.");
}
public static bool ShowMenu
@ -50,19 +64,19 @@ namespace Explorer
private static void SetShowMenu(bool show)
{
m_showMenu = show;
CursorControl.UpdateCursorControl();
ForceUnlockCursor.UpdateCursorControl();
}
public static void Update()
{
if (InputHelper.GetKeyDown(ModConfig.Instance.Main_Menu_Toggle))
if (InputManager.GetKeyDown(ModConfig.Instance.Main_Menu_Toggle))
{
ShowMenu = !ShowMenu;
}
if (ShowMenu)
{
CursorControl.Update();
ForceUnlockCursor.Update();
InspectUnderMouse.Update();
MainMenu.Instance.Update();
@ -81,6 +95,11 @@ namespace Explorer
WindowManager.Instance.OnGUI();
InspectUnderMouse.OnGUI();
if (!ResizeDrag.IsMouseInResizeArea && WindowManager.IsMouseInWindow)
{
InputManager.ResetInputAxes();
}
GUI.skin = origSkin;
}
@ -90,30 +109,30 @@ namespace Explorer
SearchPage.Instance?.OnSceneChange();
}
public static void Log(string message)
public static void Log(object message)
{
#if ML
MelonLoader.MelonLogger.Log(message);
MelonLoader.MelonLogger.Log(message?.ToString());
#else
Explorer_BepInPlugin.Logging?.LogMessage(message);
ExplorerBepInPlugin.Logging?.LogMessage(message?.ToString());
#endif
}
public static void LogWarning(string message)
public static void LogWarning(object message)
{
#if ML
MelonLoader.MelonLogger.LogWarning(message);
MelonLoader.MelonLogger.LogWarning(message?.ToString());
#else
Explorer_BepInPlugin.Logging?.LogWarning(message);
ExplorerBepInPlugin.Logging?.LogWarning(message?.ToString());
#endif
}
public static void LogError(string message)
public static void LogError(object message)
{
#if ML
MelonLoader.MelonLogger.LogError(message);
MelonLoader.MelonLogger.LogError(message?.ToString());
#else
Explorer_BepInPlugin.Logging?.LogError(message);
ExplorerBepInPlugin.Logging?.LogError(message?.ToString());
#endif
}
}

View File

@ -7,9 +7,9 @@ using MelonLoader;
namespace Explorer
{
public class Explorer_MelonMod : MelonMod
public class ExplorerMelonMod : MelonMod
{
public static Explorer_MelonMod Instance;
public static ExplorerMelonMod Instance;
public override void OnApplicationStart()
{

View File

@ -1,141 +0,0 @@
#if BIE
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Mono.CSharp;
using UnityEngine.SceneManagement;
using UnityEngine.Events;
using UnityEngine;
#if CPP
using UnhollowerRuntimeLib;
using BepInEx.IL2CPP;
#endif
namespace Explorer
{
[BepInPlugin(ExplorerCore.GUID, ExplorerCore.NAME, ExplorerCore.VERSION)]
#if CPP
public class Explorer_BepInPlugin : BasePlugin
#else
public class Explorer_BepInPlugin : BaseUnityPlugin
#endif
{
public static Explorer_BepInPlugin Instance;
public static ManualLogSource Logging =>
#if CPP
Instance.Log;
#else
Instance?.Logger;
#endif
public static readonly Harmony HarmonyInstance = new Harmony(ExplorerCore.GUID);
#if CPP
// temporary for BIE Il2Cpp
private static bool tempSceneChangeCheck;
private static string lastSceneName;
#endif
// Init
#if CPP
public override void Load()
{
#else
internal void Awake()
{
#endif
Instance = this;
#if CPP
tempSceneChangeCheck = true;
ClassInjector.RegisterTypeInIl2Cpp<DummyMB>();
GameObject.DontDestroyOnLoad(
new GameObject(
"Explorer_Dummy",
new Il2CppSystem.Type[]
{
Il2CppType.Of<DummyMB>()
})
);
#else
SceneManager.activeSceneChanged += DoSceneChange;
#endif
LoadMCS();
new ExplorerCore();
HarmonyInstance.PatchAll();
}
void LoadMCS()
{
#if NET35
var path = @"BepInEx\plugins\mcs.NET35.dll";
#else
var path = @"BepInEx\plugins\mcs.dll";
#endif
Assembly.Load(File.ReadAllBytes(path));
ExplorerCore.Log("Loaded mcs!");
}
internal static void DoSceneChange(Scene arg0, Scene arg1)
{
ExplorerCore.OnSceneChange();
}
internal static void DoUpdate()
{
ExplorerCore.Update();
#if CPP
if (tempSceneChangeCheck)
{
var scene = SceneManager.GetActiveScene();
if (scene.name != lastSceneName)
{
lastSceneName = scene.name;
DoSceneChange(scene, scene);
}
}
#endif
}
internal static void DoOnGUI()
{
ExplorerCore.OnGUI();
}
#if CPP
public class DummyMB : MonoBehaviour
{
public DummyMB(IntPtr ptr) : base(ptr) { }
internal void Awake()
{
Logging.LogMessage("DummyMB Awake");
}
#endif
internal void Update()
{
DoUpdate();
}
internal void OnGUI()
{
DoOnGUI();
}
#if CPP
}
#endif
}
}
#endif

View File

@ -2,25 +2,19 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Explorer.Helpers;
namespace Explorer
{
public static class ReflectionExtensions
{
#if CPP
/// <summary>
/// Extension to allow for easy, non-generic Il2Cpp casting.
/// The extension is on System.Object, but only Il2Cpp objects would be a valid target.
/// </summary>
public static object Il2CppCast(this object obj, Type castTo)
{
return ReflectionHelpers.Il2CppCast(obj, castTo);
}
#endif
/// <summary>
/// Extension to safely try to get all Types from an Assembly, with a fallback for ReflectionTypeLoadException.
/// </summary>
public static IEnumerable<Type> TryGetTypes(this Assembly asm)
{
try
@ -28,9 +22,16 @@ namespace Explorer
return asm.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
try
{
return asm.GetExportedTypes();
}
catch
{
return e.Types.Where(t => t != null);
}
}
catch
{
return Enumerable.Empty<Type>();

View File

@ -0,0 +1,41 @@
#if CPP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Reflection;
using System.Diagnostics.CodeAnalysis;
namespace Explorer.Helpers
{
[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "External methods")]
public static class ICallHelper
{
private static readonly Dictionary<string, Delegate> iCallCache = new Dictionary<string, Delegate>();
public static T GetICall<T>(string iCallName) where T : Delegate
{
if (iCallCache.ContainsKey(iCallName))
{
return (T)iCallCache[iCallName];
}
var ptr = il2cpp_resolve_icall(iCallName);
if (ptr == IntPtr.Zero)
{
throw new MissingMethodException($"Could not resolve internal call by name '{iCallName}'!");
}
var iCall = Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
iCallCache.Add(iCallName, iCall);
return (T)iCall;
}
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr il2cpp_resolve_icall([MarshalAs(UnmanagedType.LPStr)] string name);
}
}
#endif

View File

@ -1,98 +0,0 @@
using System;
using System.Reflection;
using UnityEngine;
namespace Explorer
{
/// <summary>
/// Version-agnostic UnityEngine Input module using Reflection.
/// </summary>
public static class InputHelper
{
// If Input module failed to load at all
public static bool NO_INPUT;
// Base UnityEngine.Input class
private static Type Input => _input ?? (_input = ReflectionHelpers.GetTypeByName("UnityEngine.Input"));
private static Type _input;
// Cached member infos
private static PropertyInfo _mousePosition;
private static MethodInfo _getKey;
private static MethodInfo _getKeyDown;
private static MethodInfo _getMouseButton;
private static MethodInfo _getMouseButtonDown;
public static void Init()
{
if (Input == null && !TryManuallyLoadInput())
{
NO_INPUT = true;
return;
}
// Cache reflection now that we know Input is loaded
_mousePosition = Input.GetProperty("mousePosition");
_getKey = Input.GetMethod("GetKey", new Type[] { typeof(KeyCode) });
_getKeyDown = Input.GetMethod("GetKeyDown", new Type[] { typeof(KeyCode) });
_getMouseButton = Input.GetMethod("GetMouseButton", new Type[] { typeof(int) });
_getMouseButtonDown = Input.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) });
}
#pragma warning disable IDE1006 // Camel-case property (Unity style)
public static Vector3 mousePosition
{
get
{
if (NO_INPUT) return Vector3.zero;
return (Vector3)_mousePosition.GetValue(null, null);
}
}
#pragma warning restore IDE1006
public static bool GetKeyDown(KeyCode key)
{
if (NO_INPUT) return false;
return (bool)_getKeyDown.Invoke(null, new object[] { key });
}
public static bool GetKey(KeyCode key)
{
if (NO_INPUT) return false;
return (bool)_getKey.Invoke(null, new object[] { key });
}
/// <param name="btn">1 = left, 2 = middle, 3 = right, etc</param>
public static bool GetMouseButtonDown(int btn)
{
if (NO_INPUT) return false;
return (bool)_getMouseButtonDown.Invoke(null, new object[] { btn });
}
/// <param name="btn">1 = left, 2 = middle, 3 = right, etc</param>
public static bool GetMouseButton(int btn)
{
if (NO_INPUT) return false;
return (bool)_getMouseButton.Invoke(null, new object[] { btn });
}
private static bool TryManuallyLoadInput()
{
ExplorerCore.Log("UnityEngine.Input is null, trying to load manually....");
if ((ReflectionHelpers.LoadModule("UnityEngine.InputLegacyModule.dll") || ReflectionHelpers.LoadModule("UnityEngine.CoreModule.dll"))
&& Input != null)
{
ExplorerCore.Log("Ok!");
return true;
}
else
{
ExplorerCore.Log("Could not load Input module!");
return false;
}
}
}
}

View File

@ -5,15 +5,17 @@ using System.IO;
using System.Reflection;
using UnityEngine;
using BF = System.Reflection.BindingFlags;
using System.Diagnostics.CodeAnalysis;
#if CPP
using ILType = Il2CppSystem.Type;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using System.Runtime.InteropServices;
#endif
namespace Explorer
namespace Explorer.Helpers
{
[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "External methods")]
public class ReflectionHelpers
{
public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
@ -24,21 +26,6 @@ namespace Explorer
public static ILType ObjectType => Il2CppType.Of<UnityEngine.Object>();
public static ILType ComponentType => Il2CppType.Of<Component>();
public static ILType BehaviourType => Il2CppType.Of<Behaviour>();
private static readonly MethodInfo tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast");
private static readonly Dictionary<Type, MethodInfo> cachedTryCastMethods = new Dictionary<Type, MethodInfo>();
public static object Il2CppCast(object obj, Type castTo)
{
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj;
if (!cachedTryCastMethods.ContainsKey(castTo))
{
cachedTryCastMethods.Add(castTo, tryCastMethodInfo.MakeGenericMethod(castTo));
}
return cachedTryCastMethods[castTo].Invoke(obj, null);
}
#else
public static Type GameObjectType => typeof(GameObject);
public static Type TransformType => typeof(Transform);
@ -47,6 +34,130 @@ namespace Explorer
public static Type BehaviourType => typeof(Behaviour);
#endif
#if CPP
private static readonly Dictionary<Type, IntPtr> ClassPointers = new Dictionary<Type, IntPtr>();
public static object Il2CppCast(object obj, Type castTo)
{
if (!(obj is Il2CppSystem.Object ilObj))
return obj;
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo))
return obj;
IntPtr castToPtr;
if (!ClassPointers.ContainsKey(castTo))
{
castToPtr = (IntPtr)typeof(Il2CppClassPointerStore<>)
.MakeGenericType(new Type[] { castTo })
.GetField("NativeClassPtr", BF.Public | BF.Static)
.GetValue(null);
ClassPointers.Add(castTo, castToPtr);
}
else
{
castToPtr = ClassPointers[castTo];
}
if (castToPtr == IntPtr.Zero)
return obj;
var classPtr = il2cpp_object_get_class(ilObj.Pointer);
if (!il2cpp_class_is_assignable_from(castToPtr, classPtr))
return obj;
if (RuntimeSpecificsStore.IsInjected(castToPtr))
return UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(ilObj.Pointer);
return Activator.CreateInstance(castTo, ilObj.Pointer);
}
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern bool il2cpp_class_is_assignable_from(IntPtr klass, IntPtr oklass);
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr il2cpp_object_get_class(IntPtr obj);
#endif
public static Type GetTypeByName(string fullName)
{
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in asm.TryGetTypes())
{
if (type.FullName == fullName)
{
return type;
}
}
}
return null;
}
public static Type GetActualType(object obj)
{
if (obj == null) return null;
#if CPP
// Need to use GetIl2CppType for Il2CppSystem Objects
if (obj is Il2CppSystem.Object ilObject)
{
// Prevent weird behaviour when inspecting an Il2CppSystem.Type object.
if (ilObject is ILType)
{
return typeof(ILType);
}
return Type.GetType(ilObject.GetIl2CppType().AssemblyQualifiedName) ?? obj.GetType();
}
#endif
// It's a normal object, this is fine
return obj.GetType();
}
public static Type[] GetAllBaseTypes(object obj) => GetAllBaseTypes(GetActualType(obj));
public static Type[] GetAllBaseTypes(Type type)
{
var list = new List<Type>();
while (type != null)
{
list.Add(type);
type = type.BaseType;
}
return list.ToArray();
}
public static bool LoadModule(string module)
{
#if CPP
#if ML
var path = $@"MelonLoader\Managed\{module}.dll";
#else
var path = $@"BepInEx\unhollowed\{module}.dll";
#endif
if (!File.Exists(path)) return false;
try
{
Assembly.Load(File.ReadAllBytes(path));
return true;
}
catch (Exception e)
{
ExplorerCore.Log(e.GetType() + ", " + e.Message);
}
#endif
return false;
}
public static bool IsEnumerable(Type t)
{
if (typeof(IEnumerable).IsAssignableFrom(t))
@ -93,119 +204,9 @@ namespace Explorer
#endif
}
public static Type GetTypeByName(string fullName)
{
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in asm.TryGetTypes())
{
if (type.FullName == fullName)
{
return type;
}
}
}
return null;
}
public static Type GetActualType(object obj)
{
if (obj == null) return null;
#if CPP
// Need to use GetIl2CppType for Il2CppSystem Objects
if (obj is Il2CppSystem.Object ilObject)
{
// Prevent weird behaviour when inspecting an Il2CppSystem.Type object.
if (ilObject is ILType)
{
return typeof(ILType);
}
return Type.GetType(ilObject.GetIl2CppType().AssemblyQualifiedName) ?? obj.GetType();
}
#endif
// It's a normal object, this is fine
return obj.GetType();
}
public static Type[] GetAllBaseTypes(object obj)
{
var list = new List<Type>();
var type = GetActualType(obj);
while (type != null)
{
list.Add(type);
type = type.BaseType;
}
return list.ToArray();
}
public static bool LoadModule(string module)
{
var path = $@"MelonLoader\Managed\{module}";
if (!File.Exists(path)) return false;
try
{
Assembly.Load(File.ReadAllBytes(path));
return true;
}
catch (Exception e)
{
ExplorerCore.Log(e.GetType() + ", " + e.Message);
return false;
}
}
public static string ExceptionToString(Exception e)
{
#if CPP
if (IsFailedGeneric(e))
{
return "Unable to initialize this type.";
}
else if (IsObjectCollected(e))
{
return "Garbage collected in Il2Cpp.";
}
#endif
return e.GetType() + ", " + e.Message;
}
#if CPP
public static bool IsFailedGeneric(Exception e)
{
return IsExceptionOfType(e, typeof(TargetInvocationException)) && IsExceptionOfType(e, typeof(TypeLoadException));
}
public static bool IsObjectCollected(Exception e)
{
return IsExceptionOfType(e, typeof(ObjectCollectedException));
}
public static bool IsExceptionOfType(Exception e, Type t, bool strict = true, bool checkInner = true)
{
bool isType;
if (strict)
isType = e.GetType() == t;
else
isType = t.IsAssignableFrom(e.GetType());
if (isType) return true;
if (e.InnerException != null && checkInner)
return IsExceptionOfType(e.InnerException, t, strict);
else
return false;
}
#endif
}
}

View File

@ -0,0 +1,178 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.IO;
using System.Reflection;
#if CPP
using Explorer.Unstrip.ImageConversion;
#endif
namespace Explorer.Helpers
{
public static class Texture2DHelpers
{
#if CPP // If Mono
#else
private static bool isNewEncodeMethod = false;
private static MethodInfo EncodeToPNGMethod => m_encodeToPNGMethod ?? GetEncodeToPNGMethod();
private static MethodInfo m_encodeToPNGMethod;
private static MethodInfo GetEncodeToPNGMethod()
{
if (ReflectionHelpers.GetTypeByName("UnityEngine.ImageConversion") is Type imageConversion)
{
isNewEncodeMethod = true;
return m_encodeToPNGMethod = imageConversion.GetMethod("EncodeToPNG", ReflectionHelpers.CommonFlags);
}
var method = typeof(Texture2D).GetMethod("EncodeToPNG", ReflectionHelpers.CommonFlags);
if (method != null)
{
return m_encodeToPNGMethod = method;
}
ExplorerCore.Log("ERROR: Cannot get any EncodeToPNG method!");
return null;
}
#endif
public static bool IsReadable(this Texture2D tex)
{
try
{
// This will cause an exception if it's not readable.
// Reason for doing it this way is not all Unity versions
// ship with the 'Texture.isReadable' property.
tex.GetPixel(0, 0);
return true;
}
catch
{
return false;
}
}
public static Texture2D Copy(Texture2D orig, Rect rect, bool isDTXnmNormal = false)
{
Color[] pixels;
if (!orig.IsReadable())
{
orig = ForceReadTexture(orig);
}
pixels = orig.GetPixels((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
var _newTex = new Texture2D((int)rect.width, (int)rect.height);
_newTex.SetPixels(pixels);
return _newTex;
}
public static Texture2D ForceReadTexture(Texture2D tex)
{
try
{
var origFilter = tex.filterMode;
tex.filterMode = FilterMode.Point;
var rt = RenderTexture.GetTemporary(tex.width, tex.height, 0, RenderTextureFormat.ARGB32);
rt.filterMode = FilterMode.Point;
RenderTexture.active = rt;
Graphics.Blit(tex, rt);
var _newTex = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false);
_newTex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
_newTex.Apply(false, false);
RenderTexture.active = null;
tex.filterMode = origFilter;
return _newTex;
}
catch (Exception e)
{
ExplorerCore.Log("Exception on ForceReadTexture: " + e.ToString());
return default;
}
}
public static void SaveTextureAsPNG(Texture2D tex, string dir, string name, bool isDTXnmNormal = false)
{
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
byte[] data;
var savepath = dir + @"\" + name + ".png";
// Make sure we can EncodeToPNG it.
if (tex.format != TextureFormat.ARGB32 || !tex.IsReadable())
{
tex = ForceReadTexture(tex);
}
if (isDTXnmNormal)
{
tex = DTXnmToRGBA(tex);
tex.Apply(false, false);
}
#if CPP
data = tex.EncodeToPNG();
#else
if (isNewEncodeMethod)
{
data = (byte[])EncodeToPNGMethod.Invoke(null, new object[] { tex });
}
else
{
data = (byte[])EncodeToPNGMethod.Invoke(tex, new object[0]);
}
#endif
if (data == null || data.Length < 1)
{
ExplorerCore.LogWarning("Couldn't get any data for the texture!");
}
else
{
File.WriteAllBytes(savepath, data);
}
}
// Converts DTXnm-format Normal Map to RGBA-format Normal Map.
public static Texture2D DTXnmToRGBA(Texture2D tex)
{
Color[] colors = tex.GetPixels();
for (int i = 0; i < colors.Length; i++)
{
Color c = colors[i];
c.r = c.a * 2 - 1; // red <- alpha
c.g = c.g * 2 - 1; // green is always the same
Vector2 rg = new Vector2(c.r, c.g); //this is the red-green vector
c.b = Mathf.Sqrt(1 - Mathf.Clamp01(Vector2.Dot(rg, rg))); //recalculate the blue channel
colors[i] = new Color(
(c.r * 0.5f) + 0.5f,
(c.g * 0.5f) + 0.25f,
(c.b * 0.5f) + 0.5f
);
}
var newtex = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false);
newtex.SetPixels(colors);
return newtex;
}
}
}

View File

@ -1,6 +1,6 @@
using UnityEngine;
namespace Explorer
namespace Explorer.Helpers
{
public class UnityHelpers
{

21
src/ILRepack.targets Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="ILRepacker" AfterTargets="Build">
<ItemGroup>
<InputAssemblies Include="$(OutputPath)$(AssemblyName).dll" />
<InputAssemblies Include="..\lib\mcs.dll" />
</ItemGroup>
<ILRepack
Parallel="true"
Internalize="true"
DebugInfo="false"
LibraryPath="..\lib\"
InputAssemblies="@(InputAssemblies)"
TargetKind="Dll"
OutputFile="$(OutputPath)$(AssemblyName).dll"
/>
</Target>
</Project>

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Explorer.Input
{
public interface IAbstractInput
{
void Init();
Vector2 MousePosition { get; }
bool GetKeyDown(KeyCode key);
bool GetKey(KeyCode key);
bool GetMouseButtonDown(int btn);
bool GetMouseButton(int btn);
}
}

88
src/Input/InputManager.cs Normal file
View File

@ -0,0 +1,88 @@
using System;
using System.Reflection;
using UnityEngine;
using Explorer.Input;
using Explorer.Helpers;
using System.Diagnostics.CodeAnalysis;
#if CPP
using UnhollowerBaseLib;
#endif
namespace Explorer
{
[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Unity style")]
public static class InputManager
{
private static IAbstractInput m_inputModule;
public static void Init()
{
if (InputSystem.TKeyboard != null || (ReflectionHelpers.LoadModule("Unity.InputSystem") && InputSystem.TKeyboard != null))
{
m_inputModule = new InputSystem();
}
else if (LegacyInput.TInput != null || (ReflectionHelpers.LoadModule("UnityEngine.InputLegacyModule") && LegacyInput.TInput != null))
{
m_inputModule = new LegacyInput();
}
if (m_inputModule == null)
{
ExplorerCore.LogWarning("Could not find any Input module!");
m_inputModule = new NoInput();
}
m_inputModule.Init();
}
public static Vector3 MousePosition => m_inputModule.MousePosition;
public static bool GetKeyDown(KeyCode key) => m_inputModule.GetKeyDown(key);
public static bool GetKey(KeyCode key) => m_inputModule.GetKey(key);
public static bool GetMouseButtonDown(int btn) => m_inputModule.GetMouseButtonDown(btn);
public static bool GetMouseButton(int btn) => m_inputModule.GetMouseButton(btn);
#if CPP
internal delegate void d_ResetInputAxes();
public static void ResetInputAxes() => ICallHelper.GetICall<d_ResetInputAxes>("UnityEngine.Input::ResetInputAxes").Invoke();
#else
public static void ResetInputAxes() => UnityEngine.Input.ResetInputAxes();
#endif
#if CPP
// public extern static string compositionString { get; }
internal delegate IntPtr d_get_compositionString();
public static string compositionString
{
get
{
var iCall = ICallHelper.GetICall<d_get_compositionString>("UnityEngine.Input::get_compositionString");
return IL2CPP.Il2CppStringToManaged(iCall.Invoke());
}
}
// public extern static Vector2 compositionCursorPos { get; set; }
internal delegate void d_get_compositionCursorPos(out Vector2 ret);
internal delegate void d_set_compositionCursorPos(ref Vector2 value);
public static Vector2 compositionCursorPos
{
get
{
var iCall = ICallHelper.GetICall<d_get_compositionCursorPos>("UnityEngine.Input::get_compositionCursorPos_Injected");
iCall.Invoke(out Vector2 ret);
return ret;
}
set
{
var iCall = ICallHelper.GetICall<d_set_compositionCursorPos>("UnityEngine.Input::set_compositionCursorPos_Injected");
iCall.Invoke(ref value);
}
}
#endif
}
}

110
src/Input/InputSystem.cs Normal file
View File

@ -0,0 +1,110 @@
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Explorer.Helpers;
namespace Explorer.Input
{
public class InputSystem : IAbstractInput
{
public static Type TKeyboard => m_tKeyboard ?? (m_tKeyboard = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Keyboard"));
private static Type m_tKeyboard;
public static Type TMouse => m_tMouse ?? (m_tMouse = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Mouse"));
private static Type m_tMouse;
public static Type TKey => m_tKey ?? (m_tKey = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Key"));
private static Type m_tKey;
private static PropertyInfo m_btnIsPressedProp;
private static PropertyInfo m_btnWasPressedProp;
private static object CurrentKeyboard => m_currentKeyboard ?? (m_currentKeyboard = m_kbCurrentProp.GetValue(null, null));
private static object m_currentKeyboard;
private static PropertyInfo m_kbCurrentProp;
private static PropertyInfo m_kbIndexer;
private static object CurrentMouse => m_currentMouse ?? (m_currentMouse = m_mouseCurrentProp.GetValue(null, null));
private static object m_currentMouse;
private static PropertyInfo m_mouseCurrentProp;
private static object LeftMouseButton => m_lmb ?? (m_lmb = m_leftButtonProp.GetValue(CurrentMouse, null));
private static object m_lmb;
private static PropertyInfo m_leftButtonProp;
private static object RightMouseButton => m_rmb ?? (m_rmb = m_rightButtonProp.GetValue(CurrentMouse, null));
private static object m_rmb;
private static PropertyInfo m_rightButtonProp;
private static object MousePositionInfo => m_pos ?? (m_pos = m_positionProp.GetValue(CurrentMouse, null));
private static object m_pos;
private static PropertyInfo m_positionProp;
private static MethodInfo m_readVector2InputMethod;
public Vector2 MousePosition => (Vector2)m_readVector2InputMethod.Invoke(MousePositionInfo, new object[0]);
public bool GetKeyDown(KeyCode key)
{
var parsedKey = Enum.Parse(TKey, key.ToString());
var actualKey = m_kbIndexer.GetValue(CurrentKeyboard, new object[] { parsedKey });
return (bool)m_btnWasPressedProp.GetValue(actualKey, null);
}
public bool GetKey(KeyCode key)
{
var parsed = Enum.Parse(TKey, key.ToString());
var actualKey = m_kbIndexer.GetValue(CurrentKeyboard, new object[] { parsed });
return (bool)m_btnIsPressedProp.GetValue(actualKey, null);
}
public bool GetMouseButtonDown(int btn)
{
switch (btn)
{
case 0: return (bool)m_btnWasPressedProp.GetValue(LeftMouseButton, null);
case 1: return (bool)m_btnWasPressedProp.GetValue(RightMouseButton, null);
// case 2: return (bool)_btnWasPressedProp.GetValue(MiddleMouseButton, null);
default: throw new NotImplementedException();
}
}
public bool GetMouseButton(int btn)
{
switch (btn)
{
case 0: return (bool)m_btnIsPressedProp.GetValue(LeftMouseButton, null);
case 1: return (bool)m_btnIsPressedProp.GetValue(RightMouseButton, null);
// case 2: return (bool)_btnIsPressedProp.GetValue(MiddleMouseButton, null);
default: throw new NotImplementedException();
}
}
public void Init()
{
ExplorerCore.Log("Initializing new InputSystem support...");
m_kbCurrentProp = TKeyboard.GetProperty("current");
m_kbIndexer = TKeyboard.GetProperty("Item", new Type[] { TKey });
var btnControl = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Controls.ButtonControl");
m_btnIsPressedProp = btnControl.GetProperty("isPressed");
m_btnWasPressedProp = btnControl.GetProperty("wasPressedThisFrame");
m_mouseCurrentProp = TMouse.GetProperty("current");
m_leftButtonProp = TMouse.GetProperty("leftButton");
m_rightButtonProp = TMouse.GetProperty("rightButton");
m_positionProp = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Pointer")
.GetProperty("position");
m_readVector2InputMethod = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.InputControl`1")
.MakeGenericType(typeof(Vector2))
.GetMethod("ReadValue");
}
}
}

43
src/Input/LegacyInput.cs Normal file
View File

@ -0,0 +1,43 @@
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Explorer.Helpers;
namespace Explorer.Input
{
public class LegacyInput : IAbstractInput
{
public static Type TInput => m_tInput ?? (m_tInput = ReflectionHelpers.GetTypeByName("UnityEngine.Input"));
private static Type m_tInput;
private static PropertyInfo m_mousePositionProp;
private static MethodInfo m_getKeyMethod;
private static MethodInfo m_getKeyDownMethod;
private static MethodInfo m_getMouseButtonMethod;
private static MethodInfo m_getMouseButtonDownMethod;
public Vector2 MousePosition => (Vector3)m_mousePositionProp.GetValue(null, null);
public bool GetKey(KeyCode key) => (bool)m_getKeyMethod.Invoke(null, new object[] { key });
public bool GetKeyDown(KeyCode key) => (bool)m_getKeyDownMethod.Invoke(null, new object[] { key });
public bool GetMouseButton(int btn) => (bool)m_getMouseButtonMethod.Invoke(null, new object[] { btn });
public bool GetMouseButtonDown(int btn) => (bool)m_getMouseButtonDownMethod.Invoke(null, new object[] { btn });
public void Init()
{
ExplorerCore.Log("Initializing Legacy Input support...");
m_mousePositionProp = TInput.GetProperty("mousePosition");
m_getKeyMethod = TInput.GetMethod("GetKey", new Type[] { typeof(KeyCode) });
m_getKeyDownMethod = TInput.GetMethod("GetKeyDown", new Type[] { typeof(KeyCode) });
m_getMouseButtonMethod = TInput.GetMethod("GetMouseButton", new Type[] { typeof(int) });
m_getMouseButtonDownMethod = TInput.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) });
}
}
}

23
src/Input/NoInput.cs Normal file
View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Explorer.Input
{
// Just a stub for games where no Input module was able to load at all.
public class NoInput : IAbstractInput
{
public Vector2 MousePosition => Vector2.zero;
public bool GetKey(KeyCode key) => false;
public bool GetKeyDown(KeyCode key) => false;
public bool GetMouseButton(int btn) => false;
public bool GetMouseButtonDown(int btn) => false;
public void Init() { }
}
}

View File

@ -1,126 +0,0 @@
using System;
using Mono.CSharp;
using UnityEngine;
using Attribute = System.Attribute;
using Object = UnityEngine.Object;
namespace Explorer
{
public class REPL : InteractiveBase
{
static REPL()
{
var go = new GameObject("UnityREPL");
GameObject.DontDestroyOnLoad(go);
//go.transform.parent = HPExplorer.Instance.transform;
MB = go.AddComponent<ReplHelper>();
}
[Documentation("MB - A dummy MonoBehaviour for accessing Unity.")]
public static ReplHelper MB { get; }
[Documentation("find<T>() - find a UnityEngine.Object of type T.")]
public static T find<T>() where T : Object
{
return MB.Find<T>();
}
[Documentation("findAll<T>() - find all UnityEngine.Object of type T.")]
public static T[] findAll<T>() where T : Object
{
return MB.FindAll<T>();
}
//[Documentation("runCoroutine(enumerator) - runs an IEnumerator as a Unity coroutine.")]
//public static object runCoroutine(IEnumerator i)
//{
// return MB.RunCoroutine(i);
//}
//[Documentation("endCoroutine(co) - ends a Unity coroutine.")]
//public static void endCoroutine(Coroutine c)
//{
// MB.EndCoroutine(c);
//}
////[Documentation("type<T>() - obtain type info about a type T. Provides some Reflection helpers.")]
////public static TypeHelper type<T>()
////{
//// return new TypeHelper(typeof(T));
////}
////[Documentation("type(obj) - obtain type info about object obj. Provides some Reflection helpers.")]
////public static TypeHelper type(object instance)
////{
//// return new TypeHelper(instance);
////}
//[Documentation("dir(obj) - lists all available methods and fiels of a given obj.")]
//public static string dir(object instance)
//{
// return type(instance).info();
//}
//[Documentation("dir<T>() - lists all available methods and fields of type T.")]
//public static string dir<T>()
//{
// return type<T>().info();
//}
//[Documentation("findrefs(obj) - find references to the object in currently loaded components.")]
//public static Component[] findrefs(object obj)
//{
// if (obj == null) throw new ArgumentNullException(nameof(obj));
// var results = new List<Component>();
// foreach (var component in Object.FindObjectsOfType<Component>())
// {
// var type = component.GetType();
// var nameBlacklist = new[] { "parent", "parentInternal", "root", "transform", "gameObject" };
// var typeBlacklist = new[] { typeof(bool) };
// foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
// .Where(x => x.CanRead && !nameBlacklist.Contains(x.Name) && !typeBlacklist.Contains(x.PropertyType)))
// {
// try
// {
// if (Equals(prop.GetValue(component, null), obj))
// {
// results.Add(component);
// goto finish;
// }
// }
// catch { }
// }
// foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
// .Where(x => !nameBlacklist.Contains(x.Name) && !typeBlacklist.Contains(x.FieldType)))
// {
// try
// {
// if (Equals(field.GetValue(component), obj))
// {
// results.Add(component);
// goto finish;
// }
// }
// catch { }
// }
// finish:;
// }
// return results.ToArray();
//}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
private class DocumentationAttribute : Attribute
{
public DocumentationAttribute(string doc)
{
Docs = doc;
}
public string Docs { get; }
}
}
}

View File

@ -1,33 +0,0 @@
using System;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Explorer
{
public class ReplHelper : MonoBehaviour
{
#if CPP
public ReplHelper(IntPtr intPtr) : base(intPtr) { }
#endif
public T Find<T>() where T : Object
{
return FindObjectOfType<T>();
}
public T[] FindAll<T>() where T : Object
{
return FindObjectsOfType<T>();
}
//public object RunCoroutine(IEnumerator enumerator)
//{
// return MelonCoroutines.Start(enumerator);
//}
//public void EndCoroutine(Coroutine c)
//{
// StopCoroutine(c);
//}
}
}

View File

@ -1,245 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using Mono.CSharp;
using UnityEngine;
namespace Explorer
{
public class ConsolePage : WindowPage
{
public override string Name { get => "C# Console"; }
private ScriptEvaluator _evaluator;
private readonly StringBuilder _sb = new StringBuilder();
private Vector2 inputAreaScroll;
private string MethodInput = "";
private string UsingInput = "";
public static List<string> UsingDirectives;
private static readonly string[] m_defaultUsing = new string[]
{
"System",
"UnityEngine",
"System.Linq",
"System.Collections",
"System.Collections.Generic",
"System.Reflection",
"MelonLoader"
};
public override void Init()
{
#if CPP
UnhollowerRuntimeLib.ClassInjector.RegisterTypeInIl2Cpp<ReplHelper>();
#endif
try
{
MethodInput = @"// This is a basic C# console.
// Some common using directives are added by default, you can add more below.
// If you want to return some output, Debug.Log() it.
Debug.Log(""hello world"");";
ResetConsole();
foreach (var use in m_defaultUsing)
{
AddUsing(use);
}
}
catch (Exception e)
{
ExplorerCore.Log($"Error setting up console!\r\nMessage: {e.Message}");
MainMenu.SetCurrentPage(0);
MainMenu.Pages.Remove(this);
}
}
public void ResetConsole()
{
if (_evaluator != null)
{
_evaluator.Dispose();
}
_evaluator = new ScriptEvaluator(new StringWriter(_sb)) { InteractiveBaseClass = typeof(REPL) };
UsingDirectives = new List<string>();
}
public string AsmToUsing(string asm, bool richtext = false)
{
if (richtext)
{
return $"<color=#569cd6>using</color> {asm};";
}
return $"using {asm};";
}
public void AddUsing(string asm)
{
if (!UsingDirectives.Contains(asm))
{
UsingDirectives.Add(asm);
Evaluate(AsmToUsing(asm), true);
}
}
public object Evaluate(string str, bool suppressWarning = false)
{
object ret = VoidType.Value;
_evaluator.Compile(str, out var compiled);
try
{
if (compiled == null)
{
throw new Exception("Mono.Csharp Service was unable to compile the code provided.");
}
compiled.Invoke(ref ret);
}
catch (Exception e)
{
if (!suppressWarning)
{
ExplorerCore.LogWarning(e.GetType() + ", " + e.Message);
}
}
return ret;
}
public override void DrawWindow()
{
GUILayout.Label("<b><size=15><color=cyan>C# Console</color></size></b>", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUILayout.Label("Enter code here as though it is a method body:", new GUILayoutOption[0]);
inputAreaScroll = GUIUnstrip.BeginScrollView(inputAreaScroll, new GUILayoutOption[] { GUILayout.Height(250) });
MethodInput = GUIUnstrip.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.ExpandHeight(true) });
GUIUnstrip.EndScrollView();
if (GUILayout.Button("<color=cyan><b>Execute</b></color>", new GUILayoutOption[0]))
{
try
{
MethodInput = MethodInput.Trim();
if (!string.IsNullOrEmpty(MethodInput))
{
var result = Evaluate(MethodInput);
if (result != null && !Equals(result, VoidType.Value))
{
ExplorerCore.Log("[Console Output]\r\n" + result.ToString());
}
}
}
catch (Exception e)
{
ExplorerCore.LogError("Exception compiling!\r\nMessage: " + e.Message + "\r\nStack: " + e.StackTrace);
}
}
GUILayout.Label("<b>Using directives:</b>", new GUILayoutOption[0]);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(105) });
UsingInput = GUILayout.TextField(UsingInput, new GUILayoutOption[] { GUILayout.Width(150) });
if (GUILayout.Button("<b><color=lime>Add</color></b>", new GUILayoutOption[] { GUILayout.Width(120) }))
{
AddUsing(UsingInput);
}
if (GUILayout.Button("<b><color=red>Clear All</color></b>", new GUILayoutOption[] { GUILayout.Width(120) }))
{
ResetConsole();
}
GUILayout.EndHorizontal();
foreach (var asm in UsingDirectives)
{
GUILayout.Label(AsmToUsing(asm, true), new GUILayoutOption[0]);
}
}
public override void Update() { }
private class VoidType
{
public static readonly VoidType Value = new VoidType();
private VoidType() { }
}
}
internal class ScriptEvaluator : Evaluator, IDisposable
{
private static readonly HashSet<string> StdLib =
new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) { "mscorlib", "System.Core", "System", "System.Xml" };
private readonly TextWriter _logger;
public ScriptEvaluator(TextWriter logger) : base(BuildContext(logger))
{
_logger = logger;
ImportAppdomainAssemblies(ReferenceAssembly);
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
}
public void Dispose()
{
AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
_logger.Dispose();
}
private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
string name = args.LoadedAssembly.GetName().Name;
if (StdLib.Contains(name))
return;
ReferenceAssembly(args.LoadedAssembly);
}
private static CompilerContext BuildContext(TextWriter tw)
{
var reporter = new StreamReportPrinter(tw);
var settings = new CompilerSettings
{
Version = LanguageVersion.Experimental,
GenerateDebugInfo = false,
StdLib = true,
Target = Target.Library,
WarningLevel = 0,
EnhancedWarnings = false
};
return new CompilerContext(settings, reporter);
}
private static void ImportAppdomainAssemblies(Action<Assembly> import)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
string name = assembly.GetName().Name;
if (StdLib.Contains(name))
continue;
import(assembly);
}
}
}
}

View File

@ -6,7 +6,7 @@ using Explorer;
#if ML
using MelonLoader;
[assembly: MelonInfo(typeof(Explorer_MelonMod), ExplorerCore.NAME, ExplorerCore.VERSION, ExplorerCore.AUTHOR)]
[assembly: MelonInfo(typeof(ExplorerMelonMod), "Explorer", ExplorerCore.VERSION, ExplorerCore.AUTHOR)]
[assembly: MelonGame(null, null)]
#endif

View File

@ -3,46 +3,50 @@ using System.Collections.Generic;
using System;
using UnityEngine;
using System.Reflection;
using System.Collections.Specialized;
// used to test multiple generic constraints
public class TestGeneric : IComparable<string>
{
public TestGeneric() { }
public int CompareTo(string other) => throw new NotImplementedException();
}
[Flags]
public enum TestFlags
{
Red,
Green,
Blue
}
// test non-flags weird enum
public enum WeirdEnum
{
First = 1,
Second,
Third = 2,
Fourth,
Fifth
}
using System.Runtime.InteropServices;
using Explorer.UI.Shared;
#if CPP
using UnhollowerBaseLib;
using UnityEngine.SceneManagement;
using Explorer.Unstrip.ImageConversion;
#endif
namespace Explorer.Tests
{
public static class StaticTestClass
{
public static int StaticProperty => 5;
public static int StaticField = 69;
public static List<string> StaticList = new List<string>
{
"one",
"two",
"three",
};
public static void StaticMethod() { }
}
public class TestClass
{
public static TestFlags testFlags = TestFlags.Blue | TestFlags.Green;
public static WeirdEnum testWeird = WeirdEnum.First;
public static int testBitmask;
public static TestClass Instance => m_instance ?? (m_instance = new TestClass());
private static TestClass m_instance;
public static bool ReadSetOnlyProperty => m_setOnlyProperty;
public static bool SetOnlyProperty
{
set => m_setOnlyProperty = value;
}
private static bool m_setOnlyProperty;
public Texture2D TestTexture = UIStyles.MakeTex(200, 200, Color.white);
public static Sprite TestSprite;
public static int StaticProperty => 5;
public static int StaticField = 5;
public int NonStaticField;
#if CPP
public static Il2CppSystem.Collections.Generic.HashSet<string> ILHashSetTest;
#endif
@ -50,22 +54,25 @@ namespace Explorer.Tests
public TestClass()
{
#if CPP
TestTexture.name = "TestTexture";
var r = new Rect(0, 0, TestTexture.width, TestTexture.height);
var v2 = Vector2.zero;
var v4 = Vector4.zero;
TestSprite = Sprite.CreateSprite_Injected(TestTexture, ref r, ref v2, 100f, 0u, SpriteMeshType.Tight, ref v4, false);
GameObject.DontDestroyOnLoad(TestTexture);
GameObject.DontDestroyOnLoad(TestSprite);
//// test loading a tex from file
//var dataToLoad = System.IO.File.ReadAllBytes(@"Mods\Explorer\Tex_Nemundis_Nebula.png");
//ExplorerCore.Log($"Tex load success: {TestTexture.LoadImage(dataToLoad, false)}");
ILHashSetTest = new Il2CppSystem.Collections.Generic.HashSet<string>();
ILHashSetTest.Add("1");
ILHashSetTest.Add("2");
ILHashSetTest.Add("3");
#endif
testBitmask = 1 | 2;
}
public static int StaticProperty => 5;
public static int StaticField = 5;
public int NonStaticField;
public static string TestGeneric<C, T>(string arg0) where C : Component where T : TestGeneric, IComparable<string>
{
return $"C: '{typeof(C).FullName}', T: '{typeof(T).FullName}', arg0: '{arg0}'";
}
public static string TestRefInOutGeneric<T>(ref string arg0, in int arg1, out string arg2)
@ -75,12 +82,6 @@ namespace Explorer.Tests
return $"T: '{typeof(T).FullName}', ref arg0: '{arg0}', in arg1: '{arg1}', out arg2: '{arg2}'";
}
//// this type of generic is not supported, due to requiring a non-primitive argument.
//public static T TestDifferentGeneric<T>(T obj) where T : Component
//{
// return obj;
//}
// test a non-generic dictionary
public Hashtable TestNonGenericDict()
@ -93,18 +94,6 @@ namespace Explorer.Tests
};
}
// IL2CPP HASHTABLE NOT SUPPORTED! Cannot assign Il2CppSystem.Object from primitive struct / string.
// Technically they are "supported" but if they contain System types they will not work.
//public Il2CppSystem.Collections.Hashtable TestIl2CppNonGenericDict()
//{
// var table = new Il2CppSystem.Collections.Hashtable();
// table.Add("One", 1);
// table.Add("One", 2);
// table.Add("One", 3);
// return table;
//}
// test HashSets
public static HashSet<string> HashSetTest = new HashSet<string>

View File

@ -1,47 +1,42 @@
using System;
using UnityEngine;
using Explorer.Helpers;
#if ML
using Harmony;
#else
using HarmonyLib;
#endif
namespace Explorer
namespace Explorer.UI
{
public class CursorControl
public class ForceUnlockCursor
{
public static bool ForceUnlockMouse
public static bool Unlock
{
get => m_forceUnlock;
set => SetForceUnlock(value);
}
private static bool m_forceUnlock;
public static bool ShouldForceMouse => ExplorerCore.ShowMenu && Unlock;
private static CursorLockMode m_lastLockMode;
private static bool m_lastVisibleState;
private static bool m_currentlySettingCursor = false;
public static bool ShouldForceMouse => ExplorerCore.ShowMenu && ForceUnlockMouse;
private static Type CursorType => m_cursorType ?? (m_cursorType = ReflectionHelpers.GetTypeByName("UnityEngine.Cursor"));
private static Type CursorType
=> m_cursorType
?? (m_cursorType = ReflectionHelpers.GetTypeByName("UnityEngine.Cursor"));
private static Type m_cursorType;
public static void Init()
{
try
{
// Check if Cursor class is loaded
if (CursorType == null)
{
ExplorerCore.Log("Trying to manually load Cursor module...");
if (ReflectionHelpers.LoadModule("UnityEngine.CoreModule") && CursorType != null)
{
ExplorerCore.Log("Ok!");
}
else
{
throw new Exception("Could not load UnityEngine.Cursor module!");
}
throw new Exception("Could not find Type 'UnityEngine.Cursor'!");
}
// Get current cursor state and enable cursor
@ -49,34 +44,29 @@ namespace Explorer
m_lastVisibleState = Cursor.visible;
// Setup Harmony Patches
TryPatch("lockState", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Prefix_set_lockState))), true);
TryPatch("lockState", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Postfix_get_lockState))), false);
TryPatch("lockState", new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_lockState))), true);
TryPatch("lockState", new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Postfix_get_lockState))), false);
TryPatch("visible", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Prefix_set_visible))), true);
TryPatch("visible", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Postfix_get_visible))), false);
TryPatch("visible", new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_visible))), true);
TryPatch("visible", new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Postfix_get_visible))), false);
}
catch (Exception e)
{
ExplorerCore.Log($"Exception on CursorControl.Init! {e.GetType()}, {e.Message}");
}
// Enable ShowMenu and ForceUnlockMouse
// (set m_showMenu directly to not call UpdateCursorState twice)
ExplorerCore.m_showMenu = true;
ForceUnlockMouse = true;
Unlock = true;
}
private static void TryPatch(string property, HarmonyMethod patch, bool setter)
{
try
{
// var harmony = ExplorerCore.Instance.harmonyInstance;
var harmony =
#if ML
Explorer_MelonMod.Instance.harmonyInstance;
ExplorerMelonMod.Instance.harmonyInstance;
#else
Explorer_BepInPlugin.HarmonyInstance;
ExplorerBepInPlugin.HarmonyInstance;
#endif
;
@ -85,17 +75,18 @@ namespace Explorer
if (setter)
{
// setter is prefix
harmony.Patch(prop.GetSetMethod(), patch);
harmony.Patch(prop.GetSetMethod(), prefix: patch);
}
else
{
// getter is postfix
harmony.Patch(prop.GetGetMethod(), null, patch);
harmony.Patch(prop.GetGetMethod(), postfix: patch);
}
}
catch (Exception e)
{
ExplorerCore.Log($"[NON-FATAL] Couldn't patch a method: {e.Message}");
string s = setter ? "set_" : "get_" ;
ExplorerCore.Log($"Unable to patch Cursor.{s}{property}: {e.Message}");
}
}
@ -108,9 +99,9 @@ namespace Explorer
public static void Update()
{
// Check Force-Unlock input
if (InputHelper.GetKeyDown(KeyCode.LeftAlt))
if (InputManager.GetKeyDown(KeyCode.LeftAlt))
{
ForceUnlockMouse = !ForceUnlockMouse;
Unlock = !Unlock;
}
}

View File

@ -1,14 +1,17 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.UI.Main;
using Explorer.Unstrip.LayerMasks;
using Explorer.Helpers;
#if CPP
using UnhollowerRuntimeLib;
#endif
namespace Explorer
namespace Explorer.UI.Inspectors
{
public class GameObjectWindow : UIWindow
public class GameObjectInspector : WindowBase
{
public override string Title => WindowManager.TabView
? $"<color=cyan>[G]</color> {TargetGO.name}"
@ -16,6 +19,8 @@ namespace Explorer
public GameObject TargetGO;
public bool pendingDestroy;
private static bool m_hideControls;
// gui element holders
@ -42,6 +47,8 @@ namespace Explorer
private bool m_autoUpdateTransform;
private bool m_localContext;
private int m_layer;
private readonly List<Component> m_cachedDestroyList = new List<Component>();
private string m_addComponentInput = "";
@ -49,7 +56,7 @@ namespace Explorer
public bool GetObjectAsGameObject()
{
var targetType = Target.GetType();
var targetType = Target?.GetType();
if (targetType == typeof(GameObject))
{
@ -103,25 +110,17 @@ namespace Explorer
{
try
{
if (pendingDestroy) return;
if (Target == null)
{
ExplorerCore.Log("Target is null!");
DestroyWindow();
DestroyOnException(new Exception("Target was destroyed."));
return;
}
else if (Target is UnityEngine.Object uObj)
{
if (!uObj)
{
ExplorerCore.Log("Target was destroyed!");
DestroyWindow();
return;
}
}
if (!TargetGO && !GetObjectAsGameObject())
{
throw new Exception("Object is null!");
DestroyOnException(new Exception("Target was destroyed."));
return;
}
if (m_freeze)
@ -139,6 +138,8 @@ namespace Explorer
TargetGO.transform.localScale = m_frozenScale;
}
m_layer = TargetGO.layer;
// update child objects
var childList = new List<Transform>();
for (int i = 0; i < TargetGO.transform.childCount; i++)
@ -169,7 +170,10 @@ namespace Explorer
private void DestroyOnException(Exception e)
{
if (pendingDestroy) return;
ExplorerCore.Log($"Exception drawing GameObject Window: {e.GetType()}, {e.Message}");
pendingDestroy = true;
DestroyWindow();
}
@ -211,6 +215,8 @@ namespace Explorer
public override void WindowFunction(int windowID)
{
if (pendingDestroy) return;
try
{
var rect = WindowManager.TabView ? TabViewWindow.Instance.m_rect : this.m_rect;
@ -218,12 +224,12 @@ namespace Explorer
if (!WindowManager.TabView)
{
Header();
GUIUnstrip.BeginArea(new Rect(5, 25, rect.width - 10, rect.height - 35), GUI.skin.box);
GUIHelper.BeginArea(new Rect(5, 25, rect.width - 10, rect.height - 35), GUI.skin.box);
}
scroll = GUIUnstrip.BeginScrollView(scroll);
scroll = GUIHelper.BeginScrollView(scroll);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", new GUILayoutOption[0]);
if (m_scene == UnityHelpers.ActiveSceneName)
{
@ -239,7 +245,7 @@ namespace Explorer
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) });
string pathlabel = TargetGO.transform.GetGameObjectPath();
if (TargetGO.transform.parent != null)
@ -249,22 +255,24 @@ namespace Explorer
InspectGameObject(TargetGO.transform.parent);
}
}
GUIUnstrip.TextArea(pathlabel, new GUILayoutOption[0]);
GUIHelper.TextArea(pathlabel, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Name:", new GUILayoutOption[] { GUILayout.Width(50) });
GUIUnstrip.TextArea(m_name, new GUILayoutOption[0]);
GUIHelper.TextArea(m_name, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
LayerControls();
// --- Horizontal Columns section ---
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) });
GUIHelper.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) });
TransformList(rect);
GUILayout.EndVertical();
GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) });
GUIHelper.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) });
ComponentList(rect);
GUILayout.EndVertical();
@ -272,13 +280,13 @@ namespace Explorer
GameObjectControls();
GUIUnstrip.EndScrollView();
GUIHelper.EndScrollView();
if (!WindowManager.TabView)
{
m_rect = ResizeDrag.ResizeWindow(rect, windowID);
GUIUnstrip.EndArea();
GUIHelper.EndArea();
}
}
catch (Exception e)
@ -287,14 +295,42 @@ namespace Explorer
}
}
private void LayerControls()
{
GUIHelper.BeginHorizontal();
GUILayout.Label("Layer:", new GUILayoutOption[] { GUILayout.Width(50) });
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(30) }))
{
if (m_layer > 0)
{
m_layer--;
if (TargetGO) TargetGO.layer = m_layer;
}
}
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(30) }))
{
if (m_layer < 32)
{
m_layer++;
if (TargetGO) TargetGO.layer = m_layer;
}
}
GUILayout.Label($"{m_layer} (<color=cyan>{LayerMaskUnstrip.LayerToName(m_layer)}</color>)",
new GUILayoutOption[] { GUILayout.Width(200) });
GUILayout.EndHorizontal();
}
private void TransformList(Rect m_rect)
{
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
m_transformScroll = GUIUnstrip.BeginScrollView(m_transformScroll);
GUIHelper.BeginVertical(GUIContent.none, GUI.skin.box, null);
m_transformScroll = GUIHelper.BeginScrollView(m_transformScroll);
GUILayout.Label("<b><size=15>Children</size></b>", new GUILayoutOption[0]);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
ChildPages.DrawLimitInputArea();
if (ChildPages.ItemCount > ChildPages.ItemsPerPage)
@ -302,7 +338,7 @@ namespace Explorer
ChildPages.CurrentPageLabel();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
{
@ -329,7 +365,7 @@ namespace Explorer
continue;
}
UIHelpers.GOButton(obj.gameObject, InspectGameObject, false, m_rect.width / 2 - 80);
Buttons.GameObjectButton(obj.gameObject, InspectGameObject, false, m_rect.width / 2 - 80);
}
}
else
@ -337,17 +373,17 @@ namespace Explorer
GUILayout.Label("<i>None</i>", new GUILayoutOption[0]);
}
GUIUnstrip.EndScrollView();
GUIHelper.EndScrollView();
GUILayout.EndVertical();
}
private void ComponentList(Rect m_rect)
{
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
m_compScroll = GUIUnstrip.BeginScrollView(m_compScroll);
GUIHelper.BeginVertical(GUIContent.none, GUI.skin.box, null);
m_compScroll = GUIHelper.BeginScrollView(m_compScroll);
GUILayout.Label("<b><size=15>Components</size></b>", new GUILayoutOption[0]);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
CompPages.DrawLimitInputArea();
if (CompPages.ItemCount > CompPages.ItemsPerPage)
@ -355,7 +391,7 @@ namespace Explorer
CompPages.CurrentPageLabel();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
{
@ -368,9 +404,9 @@ namespace Explorer
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
var width = m_rect.width / 2 - 115f;
m_addComponentInput = GUILayout.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(width) });
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
var width = m_rect.width / 2 - 135f;
m_addComponentInput = GUIHelper.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(width) });
if (GUILayout.Button("Add Comp", new GUILayoutOption[0]))
{
if (ReflectionHelpers.GetTypeByName(m_addComponentInput) is Type compType)
@ -417,7 +453,7 @@ namespace Explorer
#else
component.GetType();
#endif
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
if (ReflectionHelpers.BehaviourType.IsAssignableFrom(type))
{
#if CPP
@ -428,7 +464,7 @@ namespace Explorer
}
else
{
GUIUnstrip.Space(26);
GUIHelper.Space(26);
}
if (GUILayout.Button("<color=cyan>" + type.Name + "</color>", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 100) }))
{
@ -452,7 +488,7 @@ namespace Explorer
}
}
GUIUnstrip.EndScrollView();
GUIHelper.EndScrollView();
GUILayout.EndVertical();
}
@ -484,7 +520,7 @@ namespace Explorer
{
if (m_hideControls)
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b><size=15>GameObject Controls</size></b>", new GUILayoutOption[] { GUILayout.Width(200) });
if (GUILayout.Button("^ Show ^", new GUILayoutOption[] { GUILayout.Width(75) }))
{
@ -495,9 +531,9 @@ namespace Explorer
return;
}
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(520) });
GUIHelper.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(520) });
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b><size=15>GameObject Controls</size></b>", new GUILayoutOption[] { GUILayout.Width(200) });
if (GUILayout.Button("v Hide v", new GUILayoutOption[] { GUILayout.Width(75) }))
{
@ -505,13 +541,13 @@ namespace Explorer
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
bool m_active = TargetGO.activeSelf;
m_active = GUILayout.Toggle(m_active, (m_active ? "<color=lime>Enabled " : "<color=red>Disabled") + "</color>",
new GUILayoutOption[] { GUILayout.Width(80) });
if (TargetGO.activeSelf != m_active) { TargetGO.SetActive(m_active); }
UIHelpers.InstantiateButton(TargetGO, 100);
Buttons.InstantiateButton(TargetGO, 100);
if (GUILayout.Button("Set DontDestroyOnLoad", new GUILayoutOption[] { GUILayout.Width(170) }))
{
@ -530,9 +566,9 @@ namespace Explorer
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
m_setParentInput = GUILayout.TextField(m_setParentInput, new GUILayoutOption[0]);
m_setParentInput = GUIHelper.TextField(m_setParentInput, new GUILayoutOption[0]);
if (GUILayout.Button("Set Parent", new GUILayoutOption[] { GUILayout.Width(80) }))
{
if (GameObject.Find(m_setParentInput) is GameObject newparent)
@ -551,13 +587,13 @@ namespace Explorer
}
GUILayout.EndHorizontal();
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
GUIHelper.BeginVertical(GUIContent.none, GUI.skin.box, null);
m_cachedInput[0] = TranslateControl(TranslateType.Position, ref m_translateAmount, false);
m_cachedInput[1] = TranslateControl(TranslateType.Rotation, ref m_rotateAmount, true);
m_cachedInput[2] = TranslateControl(TranslateType.Scale, ref m_scaleAmount, false);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button("<color=lime>Apply to Transform</color>", new GUILayoutOption[0]) || m_autoApplyTransform)
{
if (m_localContext)
@ -583,7 +619,7 @@ namespace Explorer
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
BoolToggle(ref m_autoApplyTransform, "Auto-apply to Transform?");
BoolToggle(ref m_autoUpdateTransform, "Auto-update from transform?");
GUILayout.EndHorizontal();
@ -645,7 +681,7 @@ namespace Explorer
private Vector3 TranslateControl(TranslateType mode, ref float amount, bool multByTime)
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"<color=cyan><b>{(m_localContext ? "Local " : "")}{mode}</b></color>:",
new GUILayoutOption[] { GUILayout.Width(m_localContext ? 110 : 65) });
@ -668,7 +704,7 @@ namespace Explorer
Vector3 input = m_cachedInput[(int)mode];
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("<color=cyan>X:</color>", new GUILayoutOption[] { GUILayout.Width(20) });
@ -682,7 +718,7 @@ namespace Explorer
GUILayout.Label("+/-:", new GUILayoutOption[] { GUILayout.Width(30) });
var amountInput = amount.ToString("F3");
amountInput = GUILayout.TextField(amountInput, new GUILayoutOption[] { GUILayout.Width(60) });
amountInput = GUIHelper.TextField(amountInput, new GUILayoutOption[] { GUILayout.Width(60) });
if (float.TryParse(amountInput, out float f))
{
amount = f;
@ -697,16 +733,16 @@ namespace Explorer
private void PlusMinusFloat(ref float f, float amount, bool multByTime)
{
string s = f.ToString("F3");
s = GUILayout.TextField(s, new GUILayoutOption[] { GUILayout.Width(60) });
s = GUIHelper.TextField(s, new GUILayoutOption[] { GUILayout.Width(60) });
if (float.TryParse(s, out float f2))
{
f = f2;
}
if (GUIUnstrip.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) }))
if (GUIHelper.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) }))
{
f -= multByTime ? amount * Time.deltaTime : amount;
}
if (GUIUnstrip.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) }))
if (GUIHelper.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) }))
{
f += multByTime ? amount * Time.deltaTime : amount;
}

View File

@ -1,6 +1,7 @@
using UnityEngine;
using Explorer.Helpers;
namespace Explorer
namespace Explorer.UI.Inspectors
{
public class InspectUnderMouse
{
@ -12,7 +13,7 @@ namespace Explorer
{
if (ExplorerCore.ShowMenu)
{
if (InputHelper.GetKey(KeyCode.LeftShift) && InputHelper.GetMouseButtonDown(1))
if (InputManager.GetKey(KeyCode.LeftShift) && InputManager.GetMouseButtonDown(1))
{
EnableInspect = !EnableInspect;
}
@ -33,7 +34,7 @@ namespace Explorer
if (!UnityHelpers.MainCamera)
return;
var ray = UnityHelpers.MainCamera.ScreenPointToRay(InputHelper.mousePosition);
var ray = UnityHelpers.MainCamera.ScreenPointToRay(InputManager.MousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, 1000f))
{
@ -41,7 +42,7 @@ namespace Explorer
m_objUnderMouseName = obj.transform.GetGameObjectPath();
if (InputHelper.GetMouseButtonDown(0))
if (InputManager.GetMouseButtonDown(0))
{
EnableInspect = false;
m_objUnderMouseName = "";
@ -61,7 +62,7 @@ namespace Explorer
{
if (m_objUnderMouseName != "")
{
var pos = InputHelper.mousePosition;
var pos = InputManager.MousePosition;
var rect = new Rect(
pos.x - (Screen.width / 2), // x
Screen.height - pos.y - 50, // y

View File

@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Explorer.UI;
using Explorer.UI.Shared;
using Explorer.CacheObject;
#if CPP
using UnhollowerBaseLib;
#endif
namespace Explorer.UI.Inspectors
{
public class InstanceInspector : ReflectionInspector
{
public override bool IsStaticInspector => false;
// some extra cast-caching
public UnityEngine.Object m_uObj;
private Component m_component;
public override void Init()
{
// cache the extra cast-caching
#if CPP
if (!IsStaticInspector && Target is Il2CppSystem.Object ilObject)
{
var unityObj = ilObject.TryCast<UnityEngine.Object>();
if (unityObj)
{
m_uObj = unityObj;
var component = ilObject.TryCast<Component>();
if (component)
{
m_component = component;
}
}
}
#else
if (!IsStaticInspector)
{
m_uObj = Target as UnityEngine.Object;
m_component = Target as Component;
}
#endif
base.Init();
}
public override void Update()
{
if (Target == null)
{
ExplorerCore.Log("Target is null!");
DestroyWindow();
return;
}
if (Target is UnityEngine.Object uObj)
{
if (!uObj)
{
ExplorerCore.Log("Target was destroyed!");
DestroyWindow();
return;
}
}
base.Update();
}
public void DrawInstanceControls(Rect rect)
{
//if (m_uObj)
//{
// GUILayout.Label("Name: " + m_uObj.name, new GUILayoutOption[0]);
//}
//GUILayout.EndHorizontal();
if (m_uObj)
{
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Tools:</b>", new GUILayoutOption[] { GUILayout.Width(80) });
Buttons.InstantiateButton(m_uObj);
if (m_component && m_component.gameObject is GameObject obj)
{
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("GameObject:", new GUILayoutOption[] { GUILayout.Width(135) });
var charWidth = obj.name.Length * 15;
var maxWidth = rect.width - 350;
var btnWidth = charWidth < maxWidth ? charWidth : maxWidth;
if (GUILayout.Button("<color=#00FF00>" + obj.name + "</color>", new GUILayoutOption[] { GUILayout.Width(btnWidth) }))
{
WindowManager.InspectObject(obj, out bool _);
}
GUI.skin.label.alignment = TextAnchor.UpperLeft;
}
else
{
GUILayout.Label("Name: " + m_uObj.name, new GUILayoutOption[0]);
}
GUILayout.EndHorizontal();
}
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Explorer.CacheObject;
namespace Explorer.UI.Inspectors
{
public class StaticInspector : ReflectionInspector
{
public override bool IsStaticInspector => true;
public override void Init()
{
base.Init();
}
public override bool ShouldProcessMember(CacheMember holder)
{
return base.ShouldProcessMember(holder);
}
public override void WindowFunction(int windowID)
{
base.WindowFunction(windowID);
}
}
}

View File

@ -3,33 +3,44 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Explorer.UI;
using Explorer.UI.Shared;
using Explorer.CacheObject;
using Explorer.UI.Inspectors;
using Explorer.Helpers;
#if CPP
using UnhollowerBaseLib;
#endif
namespace Explorer
namespace Explorer.UI.Inspectors
{
public class ReflectionWindow : UIWindow
public abstract class ReflectionInspector : WindowBase
{
public override string Title => WindowManager.TabView
? $"<color=cyan>[R]</color> {TargetType.Name}"
: $"Reflection Inspector ({TargetType.Name})";
public virtual bool IsStaticInspector { get; }
public Type TargetType;
private CacheObjectBase[] m_allCachedMembers;
private CacheObjectBase[] m_cachedMembersFiltered;
private CacheMember[] m_allCachedMembers;
private CacheMember[] m_cachedMembersFiltered;
public PageHelper Pages = new PageHelper();
private bool m_autoUpdate = false;
private string m_search = "";
public MemberTypes m_filter = MemberTypes.Property;
public MemberTypes m_typeFilter = MemberTypes.Property;
private bool m_hideFailedReflection = false;
// some extra cast-caching
private UnityEngine.Object m_uObj;
private Component m_component;
private MemberScopes m_scopeFilter;
private enum MemberScopes
{
Both,
Instance,
Static
}
private static readonly HashSet<string> _typeAndMemberBlacklist = new HashSet<string>
{
@ -47,48 +58,24 @@ namespace Explorer
};
public override void Init()
{
if (!IsStaticInspector)
{
TargetType = ReflectionHelpers.GetActualType(Target);
CacheMembers(ReflectionHelpers.GetAllBaseTypes(Target));
// cache the extra cast-caching
#if CPP
if (Target is Il2CppSystem.Object ilObject)
{
var unityObj = ilObject.TryCast<UnityEngine.Object>();
if (unityObj)
{
m_uObj = unityObj;
var component = ilObject.TryCast<Component>();
if (component)
{
m_component = component;
}
else
{
CacheMembers(new Type[] { TargetType });
}
}
#else
m_uObj = Target as UnityEngine.Object;
m_component = Target as Component;
#endif
}
public override void Update()
{
if (Target == null)
if (m_allCachedMembers == null)
{
DestroyWindow();
return;
}
else if (Target is UnityEngine.Object uObj)
{
if (!uObj)
{
DestroyWindow();
return;
}
}
m_cachedMembersFiltered = m_allCachedMembers.Where(x => ShouldProcessMember(x)).ToArray();
@ -106,12 +93,22 @@ namespace Explorer
}
}
private bool ShouldProcessMember(CacheObjectBase holder)
public virtual bool ShouldProcessMember(CacheMember holder)
{
// check MemberTypes filter
if (m_filter != MemberTypes.All && m_filter != holder.MemInfo?.MemberType)
if (m_typeFilter != MemberTypes.All && m_typeFilter != holder.MemInfo?.MemberType)
return false;
// check scope filter
if (m_scopeFilter == MemberScopes.Instance)
{
return !holder.IsStatic;
}
else if (m_scopeFilter == MemberScopes.Static)
{
return holder.IsStatic;
}
// hide failed reflection
if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection)
return false;
@ -128,8 +125,8 @@ namespace Explorer
private void CacheMembers(Type[] types)
{
var list = new List<CacheObjectBase>();
var cachedSigs = new List<string>();
var list = new List<CacheMember>();
var cachedSigs = new HashSet<string>();
foreach (var declaringType in types)
{
@ -148,7 +145,7 @@ namespace Explorer
string exception = null;
#if CPP
if (target is Il2CppSystem.Object ilObject)
if (!IsStaticInspector && target is Il2CppSystem.Object ilObject)
{
try
{
@ -160,14 +157,26 @@ namespace Explorer
}
}
#endif
foreach (var member in infos)
{
// make sure member type is Field, Method of Property (4 / 8 / 16)
try
{
// make sure member type is Field, Method or Property (4 / 8 / 16)
int m = (int)member.MemberType;
if (m < 4 || m > 16)
continue;
var fi = member as FieldInfo;
var pi = member as PropertyInfo;
var mi = member as MethodInfo;
if (IsStaticInspector)
{
if (fi != null && !fi.IsStatic) continue;
else if (pi != null && !pi.GetAccessors()[0].IsStatic) continue;
else if (mi != null && !mi.IsStatic) continue;
}
// check blacklisted members
var sig = $"{member.DeclaringType.Name}.{member.Name}";
if (_typeAndMemberBlacklist.Any(it => it == sig))
@ -176,11 +185,11 @@ namespace Explorer
if (_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it)))
continue;
if (member is MethodInfo mi)
if (mi != null)
{
AppendParams(mi.GetParameters());
}
else if (member is PropertyInfo pi)
else if (pi != null)
{
AppendParams(pi.GetIndexParameters());
}
@ -200,24 +209,33 @@ namespace Explorer
continue;
}
// ExplorerCore.Log($"Trying to cache member {signature}...");
try
{
var cached = CacheObjectBase.GetCacheObject(member, target);
var cached = CacheFactory.GetCacheObject(member, target);
if (cached != null)
{
cachedSigs.Add(sig);
list.Add(cached);
if (string.IsNullOrEmpty(cached.ReflectionException))
{
cached.ReflectionException = exception;
}
}
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {sig}!");
ExplorerCore.Log(e.ToString());
}
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
ExplorerCore.Log(e.ToString());
}
}
}
m_allCachedMembers = list.ToArray();
@ -236,54 +254,49 @@ namespace Explorer
if (!WindowManager.TabView)
{
Header();
GUIUnstrip.BeginArea(new Rect(5, 25, rect.width - 10, rect.height - 35), GUI.skin.box);
GUIHelper.BeginArea(new Rect(5, 25, rect.width - 10, rect.height - 35), GUI.skin.box);
}
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Type:</b> <color=cyan>" + TargetType.FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(245f) });
if (m_uObj)
{
GUILayout.Label("Name: " + m_uObj.name, new GUILayoutOption[0]);
}
var asInstance = this as InstanceInspector;
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
var labelWidth = (asInstance != null && asInstance.m_uObj)
? new GUILayoutOption[] { GUILayout.Width(245f) }
: new GUILayoutOption[0];
GUILayout.Label("<b>Type:</b> <color=cyan>" + TargetType.FullName + "</color>", labelWidth);
GUILayout.EndHorizontal();
if (m_uObj)
if (asInstance != null)
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Tools:</b>", new GUILayoutOption[] { GUILayout.Width(80) });
UIHelpers.InstantiateButton(m_uObj);
if (m_component && m_component.gameObject is GameObject obj)
{
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("GameObject:", new GUILayoutOption[] { GUILayout.Width(135) });
var charWidth = obj.name.Length * 15;
var maxWidth = rect.width - 350;
var labelWidth = charWidth < maxWidth ? charWidth : maxWidth;
if (GUILayout.Button("<color=#00FF00>" + obj.name + "</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) }))
{
WindowManager.InspectObject(obj, out bool _);
}
GUI.skin.label.alignment = TextAnchor.UpperLeft;
}
GUILayout.EndHorizontal();
asInstance.DrawInstanceControls(rect);
}
UIStyles.HorizontalLine(Color.grey);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Search:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
m_search = GUILayout.TextField(m_search, new GUILayoutOption[0]);
m_search = GUIHelper.TextField(m_search, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Filter:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
FilterToggle(MemberTypes.All, "All");
FilterToggle(MemberTypes.Property, "Properties");
FilterToggle(MemberTypes.Field, "Fields");
FilterToggle(MemberTypes.Method, "Methods");
FilterTypeToggle(MemberTypes.All, "All");
FilterTypeToggle(MemberTypes.Property, "Properties");
FilterTypeToggle(MemberTypes.Field, "Fields");
FilterTypeToggle(MemberTypes.Method, "Methods");
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
if (this is InstanceInspector)
{
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Scope:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
FilterScopeToggle(MemberScopes.Both, "Both");
FilterScopeToggle(MemberScopes.Instance, "Instance");
FilterScopeToggle(MemberScopes.Static, "Static");
GUILayout.EndHorizontal();
}
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Values:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
if (GUILayout.Button("Update", new GUILayoutOption[] { GUILayout.Width(100) }))
{
@ -296,12 +309,12 @@ namespace Explorer
GUI.color = Color.white;
GUILayout.EndHorizontal();
GUIUnstrip.Space(10);
GUIHelper.Space(10);
Pages.ItemCount = m_cachedMembersFiltered.Length;
// prev/next page buttons
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
Pages.DrawLimitInputArea();
@ -323,13 +336,13 @@ namespace Explorer
// ====== BODY ======
scroll = GUIUnstrip.BeginScrollView(scroll);
scroll = GUIHelper.BeginScrollView(scroll);
GUIUnstrip.Space(10);
GUIHelper.Space(10);
UIStyles.HorizontalLine(Color.grey);
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
GUIHelper.BeginVertical(GUIContent.none, GUI.skin.box, null);
var members = this.m_cachedMembersFiltered;
int start = Pages.CalculateOffsetIndex();
@ -338,7 +351,7 @@ namespace Explorer
{
var holder = members[j];
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
GUIHelper.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
try
{
holder.Draw(rect, 180f);
@ -356,13 +369,13 @@ namespace Explorer
}
GUILayout.EndVertical();
GUIUnstrip.EndScrollView();
GUIHelper.EndScrollView();
if (!WindowManager.TabView)
{
m_rect = ResizeDrag.ResizeWindow(rect, windowID);
GUIUnstrip.EndArea();
GUIHelper.EndArea();
}
}
catch (Exception e) when (e.Message.Contains("in a group with only"))
@ -377,9 +390,9 @@ namespace Explorer
}
}
private void FilterToggle(MemberTypes mode, string label)
private void FilterTypeToggle(MemberTypes mode, string label)
{
if (m_filter == mode)
if (m_typeFilter == mode)
{
GUI.color = Color.green;
}
@ -389,7 +402,26 @@ namespace Explorer
}
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) }))
{
m_filter = mode;
m_typeFilter = mode;
Pages.PageOffset = 0;
scroll = Vector2.zero;
}
GUI.color = Color.white;
}
private void FilterScopeToggle(MemberScopes mode, string label)
{
if (m_scopeFilter == mode)
{
GUI.color = Color.green;
}
else
{
GUI.color = Color.white;
}
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) }))
{
m_scopeFilter = mode;
Pages.PageOffset = 0;
scroll = Vector2.zero;
}

View File

@ -0,0 +1,253 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
using Explorer.Helpers;
namespace Explorer.UI
{
public class InteractiveValue
{
public const float MAX_LABEL_WIDTH = 400f;
public const string EVALUATE_LABEL = "<color=lime>Evaluate</color>";
public CacheObjectBase OwnerCacheObject;
public object Value { get; set; }
public Type ValueType;
public string ButtonLabel => m_btnLabel ?? GetButtonLabel();
private string m_btnLabel;
public MethodInfo ToStringMethod => m_toStringMethod ?? GetToStringMethod();
private MethodInfo m_toStringMethod;
public virtual void Init()
{
UpdateValue();
}
public virtual void UpdateValue()
{
GetButtonLabel();
}
public float CalcWhitespace(Rect window)
{
if (!(this is IExpandHeight)) return 0f;
float whitespace = (this as IExpandHeight).WhiteSpace;
if (whitespace > 0)
{
ClampLabelWidth(window, ref whitespace);
}
return whitespace;
}
public static void ClampLabelWidth(Rect window, ref float labelWidth)
{
float min = window.width * 0.37f;
if (min > MAX_LABEL_WIDTH) min = MAX_LABEL_WIDTH;
labelWidth = Mathf.Clamp(labelWidth, min, MAX_LABEL_WIDTH);
}
public void Draw(Rect window, float labelWidth = 215f)
{
if (labelWidth > 0)
{
ClampLabelWidth(window, ref labelWidth);
}
var cacheMember = OwnerCacheObject as CacheMember;
if (cacheMember != null && cacheMember.MemInfo != null)
{
GUILayout.Label(cacheMember.RichTextName, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
}
else
{
GUIHelper.Space(labelWidth);
}
var cacheMethod = OwnerCacheObject as CacheMethod;
if (cacheMember != null && cacheMember.HasParameters)
{
GUIHelper.BeginVertical(new GUILayoutOption[] { GUILayout.ExpandHeight(true) } );
if (cacheMember.m_isEvaluating)
{
if (cacheMethod != null && cacheMethod.GenericArgs.Length > 0)
{
cacheMethod.DrawGenericArgsInput();
}
if (cacheMember.m_arguments.Length > 0)
{
cacheMember.DrawArgsInput();
}
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{
if (cacheMethod != null)
cacheMethod.Evaluate();
else
cacheMember.UpdateValue();
}
if (GUILayout.Button("Cancel", new GUILayoutOption[] { GUILayout.Width(70) }))
{
cacheMember.m_isEvaluating = false;
}
GUILayout.EndHorizontal();
}
else
{
var lbl = $"Evaluate (";
int len = cacheMember.m_arguments.Length;
if (cacheMethod != null) len += cacheMethod.GenericArgs.Length;
lbl += len + " params)";
if (GUILayout.Button(lbl, new GUILayoutOption[] { GUILayout.Width(150) }))
{
cacheMember.m_isEvaluating = true;
}
}
GUILayout.EndVertical();
GUILayout.EndHorizontal();
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(labelWidth);
}
else if (cacheMethod != null)
{
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{
cacheMethod.Evaluate();
}
GUILayout.EndHorizontal();
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(labelWidth);
}
string typeName = $"<color={Syntax.Class_Instance}>{ValueType.FullName}</color>";
if (cacheMember != null && !string.IsNullOrEmpty(cacheMember.ReflectionException))
{
GUILayout.Label("<color=red>Reflection failed!</color> (" + cacheMember.ReflectionException + ")", new GUILayoutOption[0]);
}
else if (cacheMember != null && (cacheMember.HasParameters || cacheMember is CacheMethod) && !cacheMember.m_evaluated)
{
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> ({typeName})", new GUILayoutOption[0]);
}
else if ((Value == null || Value is UnityEngine.Object uObj && !uObj) && !(cacheMember is CacheMethod))
{
GUILayout.Label($"<i>null ({typeName})</i>", new GUILayoutOption[0]);
}
else
{
float _width = window.width - labelWidth - 90;
if (OwnerCacheObject is CacheMethod cm)
{
cm.DrawValue(window, _width);
}
else
{
DrawValue(window, _width);
}
}
}
public virtual void DrawValue(Rect window, float width)
{
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button(ButtonLabel, new GUILayoutOption[] { GUILayout.Width(width - 15) }))
{
if (OwnerCacheObject.IsStaticClassSearchResult)
{
WindowManager.InspectStaticReflection(Value as Type);
}
else
{
WindowManager.InspectObject(Value, out bool _);
}
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
}
private MethodInfo GetToStringMethod()
{
try
{
m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0])
?? typeof(object).GetMethod("ToString", new Type[0]);
// test invoke
m_toStringMethod.Invoke(Value, null);
}
catch
{
m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]);
}
return m_toStringMethod;
}
public string GetButtonLabel()
{
if (Value == null) return null;
var valueType = ReflectionHelpers.GetActualType(Value);
string label;
if (valueType == typeof(TextAsset))
{
var textAsset = Value as TextAsset;
label = textAsset.text;
if (label.Length > 10)
{
label = $"{label.Substring(0, 10)}...";
}
label = $"\"{label}\" {textAsset.name} (<color={Syntax.Class_Instance}>UnityEngine.TextAsset</color>)";
}
else
{
label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
var classColor = valueType.IsAbstract && valueType.IsSealed
? Syntax.Class_Static
: Syntax.Class_Instance;
string typeLabel = $"<color={classColor}>{valueType.FullName}</color>";
if (Value is UnityEngine.Object)
{
label = label.Replace($"({valueType.FullName})", $"({typeLabel})");
}
else
{
if (!label.Contains(valueType.FullName))
{
label += $" ({typeLabel})";
}
else
{
label = label.Replace(valueType.FullName, typeLabel);
}
}
}
return m_btnLabel = label;
}
}
}

View File

@ -2,21 +2,27 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
using Explorer.Helpers;
#if CPP
using UnhollowerBaseLib;
#endif
namespace Explorer
namespace Explorer.UI
{
public class CacheDictionary : CacheObjectBase, IExpandHeight
// TODO: Re-work class using InteractiveEnumerable or maybe InteractiveCollection for the Keys/Value lists.
// Make the keys and values editable.
public class InteractiveDictionary : InteractiveValue, IExpandHeight
{
public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f;
public PageHelper Pages = new PageHelper();
private CacheObjectBase[] m_cachedKeys;
private CacheObjectBase[] m_cachedValues;
private CacheObjectBase[] m_cachedKeys = new CacheObjectBase[0];
private CacheObjectBase[] m_cachedValues = new CacheObjectBase[0];
public Type TypeOfKeys
{
@ -53,7 +59,7 @@ namespace Explorer
// note: "ValueType" is the Dictionary itself, TypeOfValues is the 'Dictionary.Values' type.
// get keys and values
var keys = ValueType.GetProperty("Keys") .GetValue(Value, null);
var keys = ValueType.GetProperty("Keys").GetValue(Value, null);
var values = ValueType.GetProperty("Values").GetValue(Value, null);
// create lists to hold them
@ -97,8 +103,9 @@ namespace Explorer
{
if (ValueType.IsGenericType)
{
m_keysType = ValueType.GetGenericArguments()[0];
m_valuesType = ValueType.GetGenericArguments()[1];
var generics = ValueType.GetGenericArguments();
m_keysType = generics[0];
m_valuesType = generics[1];
}
else
{
@ -113,12 +120,20 @@ namespace Explorer
// first make sure we won't run into a TypeInitializationException.
if (!EnsureDictionaryIsSupported())
{
ReflectionException = "Dictionary Type not supported with Reflection!";
if (OwnerCacheObject is CacheMember cacheMember)
{
cacheMember.ReflectionException = "Dictionary Type not supported with Reflection!";
}
return;
}
base.UpdateValue();
CacheEntries();
}
public void CacheEntries()
{
// reset
IDict = null;
@ -131,7 +146,7 @@ namespace Explorer
foreach (var key in IDict.Keys)
{
Type t = ReflectionHelpers.GetActualType(key) ?? TypeOfKeys;
var cache = GetCacheObject(key, t);
var cache = CacheFactory.GetCacheObject(key, t);
keys.Add(cache);
}
@ -139,7 +154,7 @@ namespace Explorer
foreach (var val in IDict.Values)
{
Type t = ReflectionHelpers.GetActualType(val) ?? TypeOfValues;
var cache = GetCacheObject(val, t);
var cache = CacheFactory.GetCacheObject(val, t);
values.Add(cache);
}
@ -147,7 +162,7 @@ namespace Explorer
m_cachedValues = values.ToArray();
}
private bool EnsureDictionaryIsSupported()
public bool EnsureDictionaryIsSupported()
{
if (typeof(IDictionary).IsAssignableFrom(ValueType))
{
@ -166,6 +181,11 @@ namespace Explorer
.GetField("NativeClassPtr")
.GetValue(null);
if (ptr == IntPtr.Zero)
{
return false;
}
return Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(ptr)) is Il2CppSystem.Type;
}
}
@ -190,8 +210,6 @@ namespace Explorer
var whitespace = CalcWhitespace(window);
int count = m_cachedKeys.Length;
if (!IsExpanded)
{
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
@ -209,6 +227,8 @@ namespace Explorer
var negativeWhitespace = window.width - (whitespace + 100f);
int count = m_cachedKeys.Length;
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
string btnLabel = $"[{count}] <color=#2df7b2>Dictionary<{TypeOfKeys.FullName}, {TypeOfValues.FullName}></color>";
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.Width(negativeWhitespace) }))
@ -217,7 +237,7 @@ namespace Explorer
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
GUIUnstrip.Space(5);
GUIHelper.Space(5);
if (IsExpanded)
{
@ -226,9 +246,9 @@ namespace Explorer
if (count > Pages.ItemsPerPage)
{
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.Space(whitespace);
Pages.CurrentPageLabel();
@ -244,7 +264,7 @@ namespace Explorer
Pages.DrawLimitInputArea();
GUIUnstrip.Space(5);
GUIHelper.Space(5);
}
int offset = Pages.CalculateOffsetIndex();
@ -256,24 +276,31 @@ namespace Explorer
//collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
//GUIUnstrip.Space(whitespace);
if (key == null || val == null)
if (key == null && val == null)
{
GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", new GUILayoutOption[0]);
}
else
{
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(40) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) });
key.DrawValue(window, (window.width / 2) - 80f);
if (key != null)
key.IValue.DrawValue(window, (window.width / 2) - 80f);
else
GUILayout.Label("<i>null</i>", new GUILayoutOption[0]);
GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) });
val.DrawValue(window, (window.width / 2) - 80f);
if (val != null)
val.IValue.DrawValue(window, (window.width / 2) - 80f);
else
GUILayout.Label("<i>null</i>", new GUILayoutOption[0]);
}
}

View File

@ -1,19 +1,27 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Reflection;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
using System.Linq;
using Explorer.Helpers;
#if CPP
using UnhollowerBaseLib;
#endif
namespace Explorer
namespace Explorer.UI
{
public class CacheList : CacheObjectBase, IExpandHeight
public class InteractiveEnumerable : InteractiveValue, IExpandHeight
{
public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f;
public PageHelper Pages = new PageHelper();
private CacheObjectBase[] m_cachedEntries;
private CacheEnumerated[] m_cachedEntries = new CacheEnumerated[0];
// Type of Entries in the Array
public Type EntryType
@ -170,38 +178,20 @@ namespace Explorer
private Type GetEntryType()
{
if (m_entryType == null)
if (ValueType.IsGenericType)
{
if (this.MemInfo != null)
{
Type memberType = null;
switch (this.MemInfo.MemberType)
{
case MemberTypes.Field:
memberType = (MemInfo as FieldInfo).FieldType;
break;
case MemberTypes.Property:
memberType = (MemInfo as PropertyInfo).PropertyType;
break;
}
var gArgs = ValueType.GetGenericArguments();
if (memberType != null && memberType.IsGenericType)
if (ValueType.FullName.Contains("ValueCollection"))
{
m_entryType = memberType.GetGenericArguments()[0];
m_entryType = gArgs[gArgs.Length - 1];
}
}
else if (Value != null)
else
{
var type = Value.GetType();
if (type.IsGenericType)
{
m_entryType = type.GetGenericArguments()[0];
m_entryType = gArgs[0];
}
}
}
// IList probably won't be able to get any EntryType.
if (m_entryType == null)
else
{
m_entryType = typeof(object);
}
@ -218,13 +208,19 @@ namespace Explorer
return;
}
CacheEntries();
}
public void CacheEntries()
{
var enumerator = Enumerable.GetEnumerator();
if (enumerator == null)
{
return;
}
var list = new List<CacheObjectBase>();
var list = new List<CacheEnumerated>();
int index = 0;
while (enumerator.MoveNext())
{
var obj = enumerator.Current;
@ -246,19 +242,18 @@ namespace Explorer
}
#endif
if (GetCacheObject(obj, t) is CacheObjectBase cached)
{
//ExplorerCore.Log("Caching enumeration entry " + obj.ToString() + " as " + EntryType.FullName);
var cached = new CacheEnumerated() { Index = index, RefIList = Value as IList, ParentEnumeration = this };
cached.Init(obj, EntryType);
list.Add(cached);
}
else
{
list.Add(null);
}
}
else
{
list.Add(null);
}
index++;
}
m_cachedEntries = list.ToArray();
@ -276,8 +271,6 @@ namespace Explorer
var whitespace = CalcWhitespace(window);
int count = m_cachedEntries.Length;
if (!IsExpanded)
{
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
@ -295,6 +288,8 @@ namespace Explorer
var negativeWhitespace = window.width - (whitespace + 100f);
int count = m_cachedEntries.Length;
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
string btnLabel = $"[{count}] <color=#2df7b2>{EntryType.FullName}</color>";
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.Width(negativeWhitespace) }))
@ -303,7 +298,7 @@ namespace Explorer
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
GUIUnstrip.Space(5);
GUIHelper.Space(5);
if (IsExpanded)
{
@ -312,9 +307,9 @@ namespace Explorer
if (count > Pages.ItemsPerPage)
{
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.Space(whitespace);
Pages.CurrentPageLabel();
@ -330,7 +325,7 @@ namespace Explorer
Pages.DrawLimitInputArea();
GUIUnstrip.Space(5);
GUIHelper.Space(5);
}
int offset = Pages.CalculateOffsetIndex();
@ -341,11 +336,11 @@ namespace Explorer
//collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.Space(whitespace);
if (entry == null || entry.Value == null)
if (entry == null || entry.IValue == null)
{
GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", new GUILayoutOption[0]);
}
@ -354,7 +349,8 @@ namespace Explorer
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
entry.DrawValue(window, window.width - (whitespace + 85));
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
entry.IValue.DrawValue(window, window.width - (whitespace + 85));
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
namespace Explorer.UI
{
public class InteractiveGameObject : InteractiveValue
{
public override void DrawValue(Rect window, float width)
{
Buttons.GameObjectButton(Value, null, false, width);
}
public override void UpdateValue()
{
base.UpdateValue();
}
}
}

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Explorer.Helpers;
using UnityEngine;
namespace Explorer.UI
{
public class InteractiveSprite : InteractiveTexture2D
{
private Sprite refSprite;
public override void UpdateValue()
{
#if CPP
if (Value != null && Value.Il2CppCast(typeof(Sprite)) is Sprite sprite)
{
refSprite = sprite;
}
#else
if (Value is Sprite sprite)
{
refSprite = sprite;
}
#endif
base.UpdateValue();
}
public override void GetTexture2D()
{
if (refSprite)
{
currentTex = refSprite.texture;
}
}
public override void GetGUIContent()
{
// Check if the Sprite.textureRect is just the entire texture
if (refSprite.textureRect != new Rect(0, 0, currentTex.width, currentTex.height))
{
// It's not, do a sub-copy.
currentTex = Texture2DHelpers.Copy(refSprite.texture, refSprite.textureRect);
}
base.GetGUIContent();
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Explorer.UI
{
// This class is possibly unnecessary.
// It's just for CacheMembers that have 'Texture' as the value type, but is actually a Texture2D.
public class InteractiveTexture : InteractiveTexture2D
{
public override void GetTexture2D()
{
#if CPP
if (Value != null && Value.Il2CppCast(typeof(Texture2D)) is Texture2D tex)
#else
if (Value is Texture2D tex)
#endif
{
currentTex = tex;
texContent = new GUIContent
{
image = currentTex
};
}
}
}
}

View File

@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Explorer.CacheObject;
using Explorer.Config;
using UnityEngine;
using System.IO;
using Explorer.Helpers;
#if CPP
using Explorer.Unstrip.ImageConversion;
#endif
namespace Explorer.UI
{
public class InteractiveTexture2D : InteractiveValue, IExpandHeight
{
public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f;
public Texture2D currentTex;
public GUIContent texContent;
private string saveFolder = ModConfig.Instance.Default_Output_Path;
public override void Init()
{
base.Init();
}
public override void UpdateValue()
{
base.UpdateValue();
GetTexture2D();
}
public virtual void GetTexture2D()
{
#if CPP
if (Value != null && Value.Il2CppCast(typeof(Texture2D)) is Texture2D tex)
#else
if (Value is Texture2D tex)
#endif
{
currentTex = tex;
}
}
public virtual void GetGUIContent()
{
texContent = new GUIContent
{
image = currentTex
};
}
public override void DrawValue(Rect window, float width)
{
GUIHelper.BeginVertical();
GUIHelper.BeginHorizontal();
if (currentTex && !IsExpanded)
{
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = true;
GetGUIContent();
}
}
else if (currentTex)
{
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = false;
}
}
base.DrawValue(window, width);
GUILayout.EndHorizontal();
if (currentTex && IsExpanded)
{
DrawTextureControls();
DrawTexture();
}
GUILayout.EndVertical();
}
// Temporarily disabled in BepInEx IL2CPP.
private void DrawTexture()
{
#if CPP
#if BIE
#else
GUILayout.Label(texContent, new GUILayoutOption[0]);
#endif
#else
GUILayout.Label(texContent, new GUILayoutOption[0]);
#endif
}
private void DrawTextureControls()
{
GUIHelper.BeginHorizontal();
GUILayout.Label("Save folder:", new GUILayoutOption[] { GUILayout.Width(80f) });
saveFolder = GUIHelper.TextField(saveFolder, new GUILayoutOption[0]);
GUIHelper.Space(10f);
GUILayout.EndHorizontal();
if (GUILayout.Button("Save to PNG", new GUILayoutOption[] { GUILayout.Width(100f) }))
{
var name = RemoveInvalidFilenameChars(currentTex.name ?? "");
if (string.IsNullOrEmpty(name))
{
if (OwnerCacheObject is CacheMember cacheMember)
{
name = cacheMember.MemInfo.Name;
}
else
{
name = "UNTITLED";
}
}
Texture2DHelpers.SaveTextureAsPNG(currentTex, saveFolder, name, false);
ExplorerCore.Log($@"Saved to {saveFolder}\{name}.png!");
}
}
private string RemoveInvalidFilenameChars(string s)
{
var invalid = System.IO.Path.GetInvalidFileNameChars();
foreach (var c in invalid)
{
s = s.Replace(c.ToString(), "");
}
return s;
}
}
}

View File

@ -3,10 +3,12 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
namespace Explorer
namespace Explorer.UI
{
public class CacheColor : CacheObjectBase, IExpandHeight
public class InteractiveColor : InteractiveValue, IExpandHeight
{
private string r = "0";
private string g = "0";
@ -32,7 +34,7 @@ namespace Explorer
public override void DrawValue(Rect window, float width)
{
if (CanWrite)
if (OwnerCacheObject.CanWrite)
{
if (!IsExpanded)
{
@ -55,46 +57,46 @@ namespace Explorer
GUILayout.Label($"<color=#2df7b2>Color:</color> {((Color)Value).ToString()}", new GUILayoutOption[0]);
//GUI.color = Color.white;
if (CanWrite && IsExpanded)
if (OwnerCacheObject.CanWrite && IsExpanded)
{
GUILayout.EndHorizontal();
var whitespace = CalcWhitespace(window);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("R:", new GUILayoutOption[] { GUILayout.Width(30) });
r = GUILayout.TextField(r, new GUILayoutOption[] { GUILayout.Width(120) });
r = GUIHelper.TextField(r, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("G:", new GUILayoutOption[] { GUILayout.Width(30) });
g = GUILayout.TextField(g, new GUILayoutOption[] { GUILayout.Width(120) });
g = GUIHelper.TextField(g, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("B:", new GUILayoutOption[] { GUILayout.Width(30) });
b = GUILayout.TextField(b, new GUILayoutOption[] { GUILayout.Width(120) });
b = GUIHelper.TextField(b, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("A:", new GUILayoutOption[] { GUILayout.Width(30) });
a = GUILayout.TextField(a, new GUILayoutOption[] { GUILayout.Width(120) });
a = GUIHelper.TextField(a, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
// draw set value button
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{
SetValueFromInput();
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
}
}
@ -106,7 +108,7 @@ namespace Explorer
&& float.TryParse(a, out float fA))
{
Value = new Color(fR, fG, fB, fA);
SetValue();
OwnerCacheObject.SetValue();
}
}
}

View File

@ -4,13 +4,17 @@ using System.Linq;
using System.Text;
using System.Reflection;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
namespace Explorer
namespace Explorer.UI
{
public class CacheEnum : CacheObjectBase
public class InteractiveEnum : InteractiveValue
{
internal static Dictionary<Type, string[]> EnumNamesInternalCache = new Dictionary<Type, string[]>();
// public Type EnumType;
public string[] EnumNames;
public string[] EnumNames = new string[0];
public override void Init()
{
@ -21,42 +25,55 @@ namespace Explorer
if (ValueType != null)
{
// using GetValues not GetNames, to catch instances of weird enums (eg CameraClearFlags)
var values = Enum.GetValues(ValueType);
var list = new List<string>();
foreach (var value in values)
{
var v = value.ToString();
if (list.Contains(v)) continue;
list.Add(v);
}
EnumNames = list.ToArray();
GetNames();
}
else
{
ReflectionException = "Unknown, could not get Enum names.";
if (OwnerCacheObject is CacheMember cacheMember)
{
cacheMember.ReflectionException = "Unknown, could not get Enum names.";
}
}
}
internal void GetNames()
{
if (!EnumNamesInternalCache.ContainsKey(ValueType))
{
// using GetValues not GetNames, to catch instances of weird enums (eg CameraClearFlags)
var values = Enum.GetValues(ValueType);
var set = new HashSet<string>();
foreach (var value in values)
{
var v = value.ToString();
if (set.Contains(v)) continue;
set.Add(v);
}
EnumNamesInternalCache.Add(ValueType, set.ToArray());
}
EnumNames = EnumNamesInternalCache[ValueType];
}
public override void DrawValue(Rect window, float width)
{
if (CanWrite)
if (OwnerCacheObject.CanWrite)
{
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) }))
{
SetEnum(-1);
SetValue();
OwnerCacheObject.SetValue();
}
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) }))
{
SetEnum(1);
SetValue();
OwnerCacheObject.SetValue();
}
}
GUILayout.Label(Value.ToString() + "<color=#2df7b2><i> (" + ValueType + ")</i></color>", new GUILayoutOption[0]);
GUILayout.Label(Value.ToString() + $"<color={Syntax.StructGreen}><i> ({ValueType})</i></color>", new GUILayoutOption[0]);
}
public void SetEnum(int change)

View File

@ -3,12 +3,13 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
namespace Explorer
namespace Explorer.UI
{
public class CacheEnumFlags : CacheObjectBase, IExpandHeight
public class InteractiveFlags : InteractiveEnum, IExpandHeight
{
public string[] EnumNames = new string[0];
public bool[] m_enabledFlags = new bool[0];
public bool IsExpanded { get; set; }
@ -18,48 +19,21 @@ namespace Explorer
{
base.Init();
if (ValueType == null && Value != null)
{
ValueType = Value.GetType();
}
if (ValueType != null)
{
EnumNames = Enum.GetNames(ValueType);
m_enabledFlags = new bool[EnumNames.Length];
UpdateValue();
}
else
{
ReflectionException = "Unknown, could not get Enum names.";
}
}
public void SetFlagsFromInput()
{
string val = "";
for (int i = 0; i < EnumNames.Length; i++)
{
if (m_enabledFlags[i])
{
if (val != "") val += ", ";
val += EnumNames[i];
}
}
Value = Enum.Parse(ValueType, val);
SetValue();
}
public override void UpdateValue()
{
base.UpdateValue();
if (Value == null) return;
try
{
var enabledNames = Value.ToString().Split(',').Select(it => it.Trim());
m_enabledFlags = new bool[EnumNames.Length];
for (int i = 0; i < EnumNames.Length; i++)
{
m_enabledFlags[i] = enabledNames.Contains(EnumNames[i]);
@ -71,10 +45,9 @@ namespace Explorer
}
}
public override void DrawValue(Rect window, float width)
{
if (CanWrite)
if (OwnerCacheObject.CanWrite)
{
if (!IsExpanded)
{
@ -102,24 +75,39 @@ namespace Explorer
for (int i = 0; i < EnumNames.Length; i++)
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
m_enabledFlags[i] = GUILayout.Toggle(m_enabledFlags[i], EnumNames[i], new GUILayoutOption[0]);
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{
SetFlagsFromInput();
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
}
}
public void SetFlagsFromInput()
{
string val = "";
for (int i = 0; i < EnumNames.Length; i++)
{
if (m_enabledFlags[i])
{
if (val != "") val += ", ";
val += EnumNames[i];
}
}
Value = Enum.Parse(ValueType, val);
OwnerCacheObject.SetValue();
}
}
}

View File

@ -0,0 +1,262 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
#if CPP
using UnhollowerRuntimeLib;
#endif
using Explorer.UI.Shared;
using Explorer.CacheObject;
using Explorer.Config;
namespace Explorer.UI
{
public class InteractivePrimitive : InteractiveValue
{
private string m_valueToString;
private bool m_isBool;
private bool m_isString;
public MethodInfo ParseMethod => m_parseMethod ?? (m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) }));
private MethodInfo m_parseMethod;
private bool m_canBitwiseOperate;
private bool m_inBitwiseMode;
private string m_bitwiseOperatorInput = "0";
private string m_binaryInput;
public override void Init()
{
if (ValueType == null)
{
ValueType = Value?.GetType();
// has to be a string at this point
if (ValueType == null)
{
ValueType = typeof(string);
}
}
if (ValueType == typeof(string))
{
m_isString = true;
}
else if (ValueType == typeof(bool))
{
m_isBool = true;
}
m_canBitwiseOperate = typeof(int).IsAssignableFrom(ValueType);
UpdateValue();
}
public override void UpdateValue()
{
base.UpdateValue();
RefreshToString();
}
private void RefreshToString()
{
m_valueToString = Value?.ToString();
if (m_canBitwiseOperate && Value != null)
{
var _int = (int)Value;
m_binaryInput = Convert.ToString(_int, toBase: 2);
}
}
public override void DrawValue(Rect window, float width)
{
if (m_isBool)
{
var b = (bool)Value;
var label = $"<color={(b ? "lime" : "red")}>{b}</color>";
if (OwnerCacheObject.CanWrite)
{
Value = GUILayout.Toggle(b, label, new GUILayoutOption[] { GUILayout.Width(60) });
DrawApplyButton();
//if (b != (bool)Value)
//{
// Value = b;
// OwnerCacheObject.SetValue();
//}
}
else
{
GUILayout.Label(label, new GUILayoutOption[0]);
}
return;
}
// all other non-bool values use TextField
GUIHelper.BeginVertical(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<color=#2df7b2><i>" + ValueType.Name + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
m_valueToString = GUIHelper.TextArea(m_valueToString, new GUILayoutOption[] { GUILayout.ExpandWidth(true) });
DrawApplyButton();
if (ModConfig.Instance.Bitwise_Support && m_canBitwiseOperate)
{
m_inBitwiseMode = GUILayout.Toggle(m_inBitwiseMode, "Bitwise?", new GUILayoutOption[0]);
}
GUIHelper.Space(10);
GUILayout.EndHorizontal();
if (ModConfig.Instance.Bitwise_Support && m_inBitwiseMode)
{
DrawBitwise();
}
GUILayout.EndVertical();
}
private void DrawApplyButton()
{
if (OwnerCacheObject.CanWrite)
{
if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))
{
if (m_isBool)
{
OwnerCacheObject.SetValue();
}
else
{
SetValueFromInput();
}
}
}
}
private void DrawBitwise()
{
if (OwnerCacheObject.CanWrite)
{
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("RHS:", new GUILayoutOption[] { GUILayout.Width(35) });
GUI.skin.label.alignment = TextAnchor.UpperLeft;
if (GUILayout.Button("~", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = ~bit;
RefreshToString();
}
}
if (GUILayout.Button("<<", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value << bit;
RefreshToString();
}
}
if (GUILayout.Button(">>", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value >> bit;
RefreshToString();
}
}
if (GUILayout.Button("|", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value | bit;
RefreshToString();
}
}
if (GUILayout.Button("&", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value & bit;
RefreshToString();
}
}
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value ^ bit;
RefreshToString();
}
}
m_bitwiseOperatorInput = GUIHelper.TextField(m_bitwiseOperatorInput, new GUILayoutOption[] { GUILayout.Width(55) });
GUILayout.EndHorizontal();
}
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"<color=cyan>Binary:</color>", new GUILayoutOption[] { GUILayout.Width(60) });
m_binaryInput = GUIHelper.TextField(m_binaryInput, new GUILayoutOption[0]);
if (OwnerCacheObject.CanWrite)
{
if (GUILayout.Button("Apply", new GUILayoutOption[0]))
{
SetValueFromBinaryInput();
}
}
GUILayout.EndHorizontal();
}
public void SetValueFromInput()
{
if (m_isString)
{
Value = m_valueToString;
}
else
{
try
{
Value = ParseMethod.Invoke(null, new object[] { m_valueToString });
}
catch (Exception e)
{
ExplorerCore.Log("Exception parsing value: " + e.GetType() + ", " + e.Message);
}
}
OwnerCacheObject.SetValue();
RefreshToString();
}
private void SetValueFromBinaryInput()
{
try
{
var method = typeof(Convert).GetMethod($"To{ValueType.Name}", new Type[] { typeof(string), typeof(int) });
Value = method.Invoke(null, new object[] { m_binaryInput, 2 });
OwnerCacheObject.SetValue();
RefreshToString();
}
catch (Exception e)
{
ExplorerCore.Log("Exception setting value: " + e.GetType() + ", " + e.Message);
}
}
}
}

View File

@ -3,10 +3,12 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
namespace Explorer
namespace Explorer.UI
{
public class CacheQuaternion : CacheObjectBase, IExpandHeight
public class InteractiveQuaternion : InteractiveValue, IExpandHeight
{
private string x = "0";
private string y = "0";
@ -30,7 +32,7 @@ namespace Explorer
public override void DrawValue(Rect window, float width)
{
if (CanWrite)
if (OwnerCacheObject.CanWrite)
{
if (!IsExpanded)
{
@ -50,40 +52,40 @@ namespace Explorer
GUILayout.Label($"<color=#2df7b2>Quaternion</color>: {((Quaternion)Value).eulerAngles.ToString()}", new GUILayoutOption[0]);
if (CanWrite && IsExpanded)
if (OwnerCacheObject.CanWrite && IsExpanded)
{
GUILayout.EndHorizontal();
var whitespace = CalcWhitespace(window);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) });
x = GUILayout.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) });
x = GUIHelper.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) });
y = GUILayout.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) });
y = GUIHelper.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(30) });
z = GUILayout.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) });
z = GUIHelper.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
// draw set value button
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{
SetValueFromInput();
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
}
}
@ -94,7 +96,7 @@ namespace Explorer
&& float.TryParse(z, out float fZ))
{
Value = Quaternion.Euler(new Vector3(fX, fY, fZ));
SetValue();
OwnerCacheObject.SetValue();
}
}
}

View File

@ -3,10 +3,12 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
namespace Explorer
namespace Explorer.UI
{
public class CacheRect : CacheObjectBase, IExpandHeight
public class InteractiveRect : InteractiveValue, IExpandHeight
{
private string x = "0";
private string y = "0";
@ -32,7 +34,7 @@ namespace Explorer
public override void DrawValue(Rect window, float width)
{
if (CanWrite)
if (OwnerCacheObject.CanWrite)
{
if (!IsExpanded)
{
@ -52,46 +54,46 @@ namespace Explorer
GUILayout.Label($"<color=#2df7b2>Rect</color>: {((Rect)Value).ToString()}", new GUILayoutOption[0]);
if (CanWrite && IsExpanded)
if (OwnerCacheObject.CanWrite && IsExpanded)
{
GUILayout.EndHorizontal();
var whitespace = CalcWhitespace(window);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) });
x = GUILayout.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) });
x = GUIHelper.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) });
y = GUILayout.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) });
y = GUIHelper.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("W:", new GUILayoutOption[] { GUILayout.Width(30) });
w = GUILayout.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) });
w = GUIHelper.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("H:", new GUILayoutOption[] { GUILayout.Width(30) });
h = GUILayout.TextField(h, new GUILayoutOption[] { GUILayout.Width(120) });
h = GUIHelper.TextField(h, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
// draw set value button
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{
SetValueFromInput();
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
}
}
@ -103,7 +105,7 @@ namespace Explorer
&& float.TryParse(h, out float fH))
{
Value = new Rect(fX, fY, fW, fH);
SetValue();
OwnerCacheObject.SetValue();
}
}
}

View File

@ -5,9 +5,9 @@ using System.Text;
using System.Reflection;
using UnityEngine;
namespace Explorer
namespace Explorer.UI
{
public class CacheVector : CacheObjectBase, IExpandHeight
public class InteractiveVector : InteractiveValue, IExpandHeight
{
public int VectorSize = 2;
@ -16,7 +16,7 @@ namespace Explorer
private string z = "0";
private string w = "0";
private MethodInfo m_toStringMethod;
//private MethodInfo m_toStringMethod;
public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f;
@ -31,18 +31,20 @@ namespace Explorer
if (ValueType == typeof(Vector2))
{
VectorSize = 2;
m_toStringMethod = typeof(Vector2).GetMethod("ToString", new Type[0]);
//m_toStringMethod = typeof(Vector2).GetMethod("ToString", new Type[0]);
}
else if (ValueType == typeof(Vector3))
{
VectorSize = 3;
m_toStringMethod = typeof(Vector3).GetMethod("ToString", new Type[0]);
//m_toStringMethod = typeof(Vector3).GetMethod("ToString", new Type[0]);
}
else
{
VectorSize = 4;
m_toStringMethod = typeof(Vector4).GetMethod("ToString", new Type[0]);
//m_toStringMethod = typeof(Vector4).GetMethod("ToString", new Type[0]);
}
base.Init();
}
public override void UpdateValue()
@ -71,7 +73,7 @@ namespace Explorer
public override void DrawValue(Rect window, float width)
{
if (CanWrite)
if (OwnerCacheObject.CanWrite)
{
if (!IsExpanded)
{
@ -89,56 +91,56 @@ namespace Explorer
}
}
GUILayout.Label($"<color=#2df7b2>Vector{VectorSize}</color>: {(string)m_toStringMethod.Invoke(Value, new object[0])}", new GUILayoutOption[0]);
GUILayout.Label($"<color=#2df7b2>Vector{VectorSize}</color>: {(string)ToStringMethod.Invoke(Value, new object[0])}", new GUILayoutOption[0]);
if (CanWrite && IsExpanded)
if (OwnerCacheObject.CanWrite && IsExpanded)
{
GUILayout.EndHorizontal();
var whitespace = CalcWhitespace(window);
// always draw x and y
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) });
x = GUILayout.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) });
x = GUIHelper.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) });
y = GUILayout.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) });
y = GUIHelper.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
if (VectorSize > 2)
{
// draw z
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(30) });
z = GUILayout.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) });
z = GUIHelper.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
}
if (VectorSize > 3)
{
// draw w
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUILayout.Label("W:", new GUILayoutOption[] { GUILayout.Width(30) });
w = GUILayout.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) });
w = GUIHelper.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal();
}
// draw set value button
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{
SetValueFromInput();
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
}
}
@ -161,7 +163,7 @@ namespace Explorer
if (vector != null)
{
Value = vector;
SetValue();
OwnerCacheObject.SetValue();
}
}
}

View File

@ -1,8 +1,8 @@
using UnityEngine;
namespace Explorer
namespace Explorer.UI.Main
{
public abstract class WindowPage
public abstract class BaseMainMenuPage
{
public virtual string Name { get; }

View File

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
// Thanks to ManlyMarco for this
namespace Explorer.UI.Main
{
public struct AutoComplete
{
public string Full => Prefix + Addition;
public readonly string Prefix;
public readonly string Addition;
public readonly Contexts Context;
public Color TextColor => Context == Contexts.Namespace
? Color.gray
: Color.white;
public AutoComplete(string addition, string prefix, Contexts type)
{
Addition = addition;
Prefix = prefix;
Context = type;
}
public enum Contexts
{
Namespace,
Other
}
}
public static class AutoCompleteHelpers
{
public static HashSet<string> Namespaces => _namespaces ?? GetNamespaces();
private static HashSet<string> _namespaces;
private static HashSet<string> GetNamespaces()
{
var set = new HashSet<string>(
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(GetTypes)
.Where(x => x.IsPublic && !string.IsNullOrEmpty(x.Namespace))
.Select(x => x.Namespace));
return _namespaces = set;
IEnumerable<Type> GetTypes(Assembly asm) => asm.TryGetTypes();
}
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Mono.CSharp;
// Thanks to ManlyMarco for this
namespace Explorer.UI.Main
{
internal class ScriptEvaluator : Evaluator, IDisposable
{
private static readonly HashSet<string> StdLib = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase)
{
"mscorlib", "System.Core", "System", "System.Xml"
};
private readonly TextWriter _logger;
public ScriptEvaluator(TextWriter logger) : base(BuildContext(logger))
{
_logger = logger;
ImportAppdomainAssemblies(ReferenceAssembly);
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
}
public void Dispose()
{
AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
_logger.Dispose();
}
private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
string name = args.LoadedAssembly.GetName().Name;
if (StdLib.Contains(name))
return;
ReferenceAssembly(args.LoadedAssembly);
}
private static CompilerContext BuildContext(TextWriter tw)
{
var reporter = new StreamReportPrinter(tw);
var settings = new CompilerSettings
{
Version = LanguageVersion.Experimental,
GenerateDebugInfo = false,
StdLib = true,
Target = Target.Library,
WarningLevel = 0,
EnhancedWarnings = false
};
return new CompilerContext(settings, reporter);
}
private static void ImportAppdomainAssemblies(Action<Assembly> import)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
string name = assembly.GetName().Name;
if (StdLib.Contains(name))
continue;
import(assembly);
}
}
}
}

View File

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Explorer.UI.Inspectors;
using Mono.CSharp;
using UnityEngine;
namespace Explorer.UI.Main
{
public class ScriptInteraction : InteractiveBase
{
public static void Log(object message)
{
ExplorerCore.Log(message);
}
public static object CurrentTarget()
{
if (!WindowManager.TabView)
{
ExplorerCore.Log("CurrentTarget() is only a valid method when in Tab View mode!");
return null;
}
return WindowManager.Windows.ElementAt(TabViewWindow.Instance.TargetTabID).Target;
}
public static object[] AllTargets()
{
var list = new List<object>();
foreach (var window in WindowManager.Windows)
{
if (window.Target != null)
{
list.Add(window.Target);
}
}
return list.ToArray();
}
public static void Inspect(object obj)
{
WindowManager.InspectObject(obj, out bool _);
}
public static void Inspect(Type type)
{
WindowManager.InspectStaticReflection(type);
}
public static void Help()
{
ExplorerCore.Log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
ExplorerCore.Log(" C# Console Help ");
ExplorerCore.Log("");
ExplorerCore.Log("The following helper methods are available:");
ExplorerCore.Log("");
ExplorerCore.Log("void Log(object message)");
ExplorerCore.Log(" prints a message to the console window and debug log");
ExplorerCore.Log(" usage: Log(\"hello world\");");
ExplorerCore.Log("");
ExplorerCore.Log("object CurrentTarget()");
ExplorerCore.Log(" returns the target object of the current tab (in tab view mode only)");
ExplorerCore.Log(" usage: var target = CurrentTarget();");
ExplorerCore.Log("");
ExplorerCore.Log("object[] AllTargets()");
ExplorerCore.Log(" returns an object[] array containing all currently inspected objects");
ExplorerCore.Log(" usage: var targets = AllTargets();");
ExplorerCore.Log("");
ExplorerCore.Log("void Inspect(object obj)");
ExplorerCore.Log(" inspects the provided object in a new window.");
ExplorerCore.Log(" usage: Inspect(Camera.main);");
ExplorerCore.Log("");
ExplorerCore.Log("void Inspect(Type type)");
ExplorerCore.Log(" attempts to inspect the provided type with static-only reflection.");
ExplorerCore.Log(" usage: Inspect(typeof(Camera));");
}
}
}

371
src/UI/Main/ConsolePage.cs Normal file
View File

@ -0,0 +1,371 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Mono.CSharp;
using System.Reflection;
using System.IO;
#if CPP
using UnhollowerRuntimeLib;
using TextEditor = Explorer.Unstrip.IMGUI.TextEditorUnstrip;
using Explorer.Unstrip.IMGUI;
#endif
namespace Explorer.UI.Main
{
public class ConsolePage : BaseMainMenuPage
{
public static ConsolePage Instance { get; private set; }
public override string Name { get => "C# Console"; }
private ScriptEvaluator m_evaluator;
public const string INPUT_CONTROL_NAME = "consoleInput";
private string m_input = "";
private string m_prevInput = "";
private string m_usingInput = "";
public static List<AutoComplete> AutoCompletes = new List<AutoComplete>();
public static List<string> UsingDirectives;
private Vector2 inputAreaScroll;
private Vector2 autocompleteScroll;
public static TextEditor textEditor;
private bool shouldRefocus;
public static GUIStyle AutocompleteStyle => autocompleteStyle ?? GetAutocompleteStyle();
private static GUIStyle autocompleteStyle;
public static readonly string[] DefaultUsing = new string[]
{
"System",
"UnityEngine",
"System.Linq",
"System.Collections",
"System.Collections.Generic",
"System.Reflection"
};
public override void Init()
{
Instance = this;
try
{
m_input = @"// For a list of helper methods, execute the 'Help();' method.
// Enable the Console Window with your Mod Loader to see log output.
Help();";
ResetConsole();
foreach (var use in DefaultUsing)
{
AddUsing(use);
}
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Error setting up console!\r\nMessage: {e.Message}");
MainMenu.SetCurrentPage(0);
MainMenu.Pages.Remove(this);
}
}
public override void Update() { }
public string AsmToUsing(string asm, bool richtext = false)
{
if (richtext)
{
return $"<color=#569cd6>using</color> {asm};";
}
return $"using {asm};";
}
public void AddUsing(string asm)
{
if (!UsingDirectives.Contains(asm))
{
UsingDirectives.Add(asm);
Evaluate(AsmToUsing(asm), true);
}
}
public object Evaluate(string str, bool suppressWarning = false)
{
object ret = VoidType.Value;
m_evaluator.Compile(str, out var compiled);
try
{
if (compiled == null)
{
throw new Exception("Mono.Csharp Service was unable to compile the code provided.");
}
compiled.Invoke(ref ret);
}
catch (Exception e)
{
if (!suppressWarning)
{
ExplorerCore.LogWarning(e.GetType() + ", " + e.Message);
}
}
return ret;
}
public void ResetConsole()
{
if (m_evaluator != null)
{
m_evaluator.Dispose();
}
m_evaluator = new ScriptEvaluator(new StringWriter(new StringBuilder())) { InteractiveBaseClass = typeof(ScriptInteraction) };
UsingDirectives = new List<string>();
}
public override void DrawWindow()
{
GUILayout.Label("<b><size=15><color=cyan>C# Console</color></size></b>", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.UpperLeft;
// SCRIPT INPUT
GUILayout.Label("Enter code here as though it is a method body:", new GUILayoutOption[0]);
inputAreaScroll = GUIHelper.BeginScrollView(
inputAreaScroll,
new GUILayoutOption[] { GUILayout.Height(250), GUIHelper.ExpandHeight(true) }
);
GUI.SetNextControlName(INPUT_CONTROL_NAME);
m_input = GUIHelper.TextArea(m_input, new GUILayoutOption[] { GUIHelper.ExpandHeight(true) });
GUIHelper.EndScrollView();
// EXECUTE BUTTON
if (GUILayout.Button("<color=cyan><b>Execute</b></color>", new GUILayoutOption[0]))
{
try
{
m_input = m_input.Trim();
if (!string.IsNullOrEmpty(m_input))
{
Evaluate(m_input);
//var result = Evaluate(m_input);
//if (result != null && !Equals(result, VoidType.Value))
//{
// ExplorerCore.Log("[Console Output]\r\n" + result.ToString());
//}
}
}
catch (Exception e)
{
ExplorerCore.LogError("Exception compiling!\r\nMessage: " + e.Message + "\r\nStack: " + e.StackTrace);
}
}
// SUGGESTIONS
if (AutoCompletes.Count > 0)
{
autocompleteScroll = GUIHelper.BeginScrollView(autocompleteScroll, new GUILayoutOption[] { GUILayout.Height(150) });
var origSkin = GUI.skin.button;
GUI.skin.button = AutocompleteStyle;
foreach (var autocomplete in AutoCompletes)
{
AutocompleteStyle.normal.textColor = autocomplete.TextColor;
if (GUILayout.Button(autocomplete.Full, new GUILayoutOption[] { GUILayout.Width(MainMenu.MainRect.width - 50) }))
{
UseAutocomplete(autocomplete.Addition);
break;
}
}
GUI.skin.button = origSkin;
GUIHelper.EndScrollView();
}
if (shouldRefocus)
{
GUI.FocusControl(INPUT_CONTROL_NAME);
shouldRefocus = false;
}
// USING DIRECTIVES
GUILayout.Label("<b>Using directives:</b>", new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(105) });
m_usingInput = GUIHelper.TextField(m_usingInput, new GUILayoutOption[] { GUILayout.Width(150) });
if (GUILayout.Button("<b><color=lime>Add</color></b>", new GUILayoutOption[] { GUILayout.Width(120) }))
{
AddUsing(m_usingInput);
}
if (GUILayout.Button("<b><color=red>Clear All</color></b>", new GUILayoutOption[] { GUILayout.Width(120) }))
{
ResetConsole();
}
GUILayout.EndHorizontal();
foreach (var asm in UsingDirectives)
{
GUILayout.Label(AsmToUsing(asm, true), new GUILayoutOption[0]);
}
CheckAutocomplete();
}
private void CheckAutocomplete()
{
// Temporary disabling this check in BepInEx Il2Cpp.
#if BIE
#if CPP
#else
if (GUI.GetNameOfFocusedControl() != INPUT_CONTROL_NAME)
return;
#endif
#else
if (GUI.GetNameOfFocusedControl() != INPUT_CONTROL_NAME)
return;
#endif
#if CPP
//textEditor = GUIUtility.GetStateObject(Il2CppType.Of<TextEditor>(), GUIUtility.keyboardControl).TryCast<TextEditor>();
textEditor = (TextEditor)GUIUtilityUnstrip.GetMonoStateObject(typeof(TextEditor), GUIUtility.keyboardControl);
#else
textEditor = (TextEditor)GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl);
#endif
var input = m_input;
if (!string.IsNullOrEmpty(input))
{
try
{
var splitChars = new[] { ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&' };
// Credit ManlyMarco
// Separate input into parts, grab only the part with cursor in it
var cursorIndex = textEditor.cursorIndex;
var start = cursorIndex <= 0 ? 0 : input.LastIndexOfAny(splitChars, cursorIndex - 1) + 1;
var end = cursorIndex <= 0 ? input.Length : input.IndexOfAny(splitChars, cursorIndex - 1);
if (end < 0 || end < start) end = input.Length;
input = input.Substring(start, end - start);
}
catch (ArgumentException) { }
if (!string.IsNullOrEmpty(input) && input != m_prevInput)
{
GetAutocompletes(input);
}
}
else
{
ClearAutocompletes();
}
m_prevInput = input;
}
private void ClearAutocompletes()
{
if (AutoCompletes.Any())
{
AutoCompletes.Clear();
shouldRefocus = true;
}
}
private void UseAutocomplete(string suggestion)
{
int cursorIndex = textEditor.cursorIndex;
m_input = m_input.Insert(cursorIndex, suggestion);
ClearAutocompletes();
shouldRefocus = true;
}
private void GetAutocompletes(string input)
{
try
{
//ExplorerCore.Log("Fetching suggestions for input " + input);
// Credit ManylMarco
AutoCompletes.Clear();
var completions = m_evaluator.GetCompletions(input, out string prefix);
if (completions != null)
{
if (prefix == null)
prefix = input;
AutoCompletes.AddRange(completions
.Where(x => !string.IsNullOrEmpty(x))
.Select(x => new AutoComplete(x, prefix, AutoComplete.Contexts.Other))
);
}
var trimmed = input.Trim();
if (trimmed.StartsWith("using"))
trimmed = trimmed.Remove(0, 5).Trim();
var namespaces = AutoCompleteHelpers.Namespaces
.Where(x => x.StartsWith(trimmed) && x.Length > trimmed.Length)
.Select(x => new AutoComplete(
x.Substring(trimmed.Length),
x.Substring(0, trimmed.Length),
AutoComplete.Contexts.Namespace));
AutoCompletes.AddRange(namespaces);
shouldRefocus = true;
}
catch (Exception ex)
{
ExplorerCore.Log("C# Console error:\r\n" + ex);
ClearAutocompletes();
}
}
// Credit ManlyMarco
private static GUIStyle GetAutocompleteStyle()
{
var style = new GUIStyle
{
border = new RectOffset(),
margin = new RectOffset(),
padding = new RectOffset(),
hover = { background = Texture2D.whiteTexture, textColor = Color.black },
normal = { background = null },
focused = { background = Texture2D.whiteTexture, textColor = Color.black },
active = { background = Texture2D.whiteTexture, textColor = Color.black },
alignment = TextAnchor.MiddleLeft
};
return autocompleteStyle = style;
}
private class VoidType
{
public static readonly VoidType Value = new VoidType();
private VoidType() { }
}
}
}

145
src/UI/Main/OptionsPage.cs Normal file
View File

@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.Config;
using Explorer.CacheObject;
namespace Explorer.UI.Main
{
public class OptionsPage : BaseMainMenuPage
{
public override string Name => "Options";
public string toggleKeyInputString = "";
public Vector2 defaultSizeInputVector;
public int defaultPageLimit;
public bool bitwiseSupport;
public bool tabView;
public string defaultOutputPath;
private CacheObjectBase toggleKeyInput;
private CacheObjectBase defaultSizeInput;
private CacheObjectBase defaultPageLimitInput;
private CacheObjectBase bitwiseSupportInput;
private CacheObjectBase tabViewInput;
private CacheObjectBase defaultOutputPathInput;
public override void Init()
{
toggleKeyInputString = ModConfig.Instance.Main_Menu_Toggle.ToString();
toggleKeyInput = CacheFactory.GetCacheObject(typeof(OptionsPage).GetField("toggleKeyInputString"), this);
defaultSizeInputVector = ModConfig.Instance.Default_Window_Size;
defaultSizeInput = CacheFactory.GetCacheObject(typeof(OptionsPage).GetField("defaultSizeInputVector"), this);
defaultPageLimit = ModConfig.Instance.Default_Page_Limit;
defaultPageLimitInput = CacheFactory.GetCacheObject(typeof(OptionsPage).GetField("defaultPageLimit"), this);
bitwiseSupport = ModConfig.Instance.Bitwise_Support;
bitwiseSupportInput = CacheFactory.GetCacheObject(typeof(OptionsPage).GetField("bitwiseSupport"), this);
tabView = ModConfig.Instance.Tab_View;
tabViewInput = CacheFactory.GetCacheObject(typeof(OptionsPage).GetField("tabView"), this);
defaultOutputPath = ModConfig.Instance.Default_Output_Path;
defaultOutputPathInput = CacheFactory.GetCacheObject(typeof(OptionsPage).GetField("defaultOutputPath"), this);
}
public override void Update() { }
public override void DrawWindow()
{
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<color=orange><size=16><b>Options</b></size></color>", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUIHelper.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Menu Toggle Key:", new GUILayoutOption[] { GUILayout.Width(215f) });
toggleKeyInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
UIStyles.HorizontalLine(Color.black, true);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Default Window Size:", new GUILayoutOption[] { GUILayout.Width(215f) });
defaultSizeInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
UIStyles.HorizontalLine(Color.black, true);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Default Items per Page:", new GUILayoutOption[] { GUILayout.Width(215f) });
defaultPageLimitInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
UIStyles.HorizontalLine(Color.black, true);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Enable Bitwise Editing:", new GUILayoutOption[] { GUILayout.Width(215f) });
bitwiseSupportInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
UIStyles.HorizontalLine(Color.black, true);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Enable Tab View:", new GUILayoutOption[] { GUILayout.Width(215f) });
tabViewInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
UIStyles.HorizontalLine(Color.black, true);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Default Output Path:", new GUILayoutOption[] { GUILayout.Width(215f) });
defaultOutputPathInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
if (GUILayout.Button("<color=lime><b>Apply and Save</b></color>", new GUILayoutOption[0]))
{
ApplyAndSave();
}
GUILayout.EndVertical();
GUIHelper.Space(10f);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<color=orange><size=16><b>Other</b></size></color>", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUIHelper.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[0]);
if (GUILayout.Button("Inspect Test Class", new GUILayoutOption[0]))
{
WindowManager.InspectObject(Tests.TestClass.Instance, out bool _);
}
GUILayout.EndVertical();
}
private void ApplyAndSave()
{
if (Enum.Parse(typeof(KeyCode), toggleKeyInputString) is KeyCode key)
{
ModConfig.Instance.Main_Menu_Toggle = key;
}
else
{
ExplorerCore.LogWarning($"Could not parse '{toggleKeyInputString}' to KeyCode!");
}
ModConfig.Instance.Default_Window_Size = defaultSizeInputVector;
ModConfig.Instance.Default_Page_Limit = defaultPageLimit;
ModConfig.Instance.Bitwise_Support = bitwiseSupport;
ModConfig.Instance.Tab_View = tabView;
WindowManager.TabView = tabView;
ModConfig.SaveSettings();
}
}
}

View File

@ -3,32 +3,34 @@ using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;
using Explorer.UI.Shared;
using Explorer.CacheObject;
using Explorer.Helpers;
using Explorer.Unstrip.Resources;
namespace Explorer
namespace Explorer.UI.Main
{
public class ScenePage : WindowPage
public class ScenePage : BaseMainMenuPage
{
public static ScenePage Instance;
public override string Name { get => "Scene Explorer"; }
public override string Name { get => "Scenes"; }
public PageHelper Pages = new PageHelper();
private float m_timeOfLastUpdate = -1f;
private const int PASSIVE_UPDATE_INTERVAL = 1;
private static bool m_getRootObjectsFailed;
private static string m_currentScene = "";
// gameobject list
private Transform m_currentTransform;
private readonly List<GameObjectCache> m_objectList = new List<GameObjectCache>();
private readonly List<CacheObjectBase> m_objectList = new List<CacheObjectBase>();
// search bar
private bool m_searching = false;
private string m_searchInput = "";
private List<GameObjectCache> m_searchResults = new List<GameObjectCache>();
private List<CacheObjectBase> m_searchResults = new List<CacheObjectBase>();
public override void Init()
{
@ -48,7 +50,7 @@ namespace Explorer
if (m_searching)
CancelSearch();
Update_Impl(true);
Update_Impl();
}
public void TraverseUp()
@ -73,18 +75,13 @@ namespace Explorer
public void CancelSearch()
{
m_searching = false;
if (m_getRootObjectsFailed && !m_currentTransform)
{
GetRootObjectsManual_Impl();
}
}
public List<GameObjectCache> SearchSceneObjects(string _search)
public List<CacheObjectBase> SearchSceneObjects(string _search)
{
var matches = new List<GameObjectCache>();
var matches = new List<CacheObjectBase>();
foreach (var obj in Resources.FindObjectsOfTypeAll(ReflectionHelpers.GameObjectType))
foreach (var obj in ResourcesUnstrip.FindObjectsOfTypeAll(ReflectionHelpers.GameObjectType))
{
#if CPP
var go = obj.TryCast<GameObject>();
@ -93,7 +90,7 @@ namespace Explorer
#endif
if (go.name.ToLower().Contains(_search.ToLower()) && go.scene.name == m_currentScene)
{
matches.Add(new GameObjectCache(go));
matches.Add(CacheFactory.GetCacheObject(go));
}
}
@ -110,7 +107,7 @@ namespace Explorer
Update_Impl();
}
private void Update_Impl(bool manual = false)
private void Update_Impl()
{
List<Transform> allTransforms = new List<Transform>();
@ -123,10 +120,6 @@ namespace Explorer
}
}
else
{
if (!m_getRootObjectsFailed)
{
try
{
for (int i = 0; i < SceneManager.sceneCount; i++)
{
@ -134,31 +127,19 @@ namespace Explorer
if (scene.name == m_currentScene)
{
allTransforms.AddRange(scene.GetRootGameObjects()
.Select(it => it.transform));
var rootObjects =
#if CPP
Unstrip.Scenes.SceneUnstrip.GetRootGameObjects(scene)
.Select(it => it.transform);
#else
scene.GetRootGameObjects().Select(it => it.transform);
#endif
allTransforms.AddRange(rootObjects);
break;
}
}
}
catch
{
ExplorerCore.Log("Exception getting root scene objects, falling back to backup method...");
m_getRootObjectsFailed = true;
allTransforms.AddRange(GetRootObjectsManual_Impl());
}
}
else
{
if (!manual)
{
return;
}
allTransforms.AddRange(GetRootObjectsManual_Impl());
}
}
Pages.ItemCount = allTransforms.Count;
@ -172,37 +153,7 @@ namespace Explorer
for (int i = offset; i < offset + Pages.ItemsPerPage && i < Pages.ItemCount; i++)
{
var child = allTransforms[i];
m_objectList.Add(new GameObjectCache(child.gameObject));
}
}
private IEnumerable<Transform> GetRootObjectsManual_Impl()
{
try
{
var array = Resources.FindObjectsOfTypeAll(ReflectionHelpers.TransformType);
var list = new List<Transform>();
foreach (var obj in array)
{
#if CPP
var transform = obj.TryCast<Transform>();
#else
var transform = obj as Transform;
#endif
if (transform.parent == null && transform.gameObject.scene.name == m_currentScene)
{
list.Add(transform);
}
}
return list;
}
catch (Exception e)
{
ExplorerCore.Log("Exception getting root scene objects (manual): "
+ e.GetType() + ", " + e.Message + "\r\n"
+ e.StackTrace);
return new Transform[0];
m_objectList.Add(CacheFactory.GetCacheObject(child));
}
}
@ -214,7 +165,7 @@ namespace Explorer
{
DrawHeaderArea();
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
GUIHelper.BeginVertical(GUIContent.none, GUI.skin.box, null);
DrawPageButtons();
@ -229,15 +180,18 @@ namespace Explorer
GUILayout.EndVertical();
}
catch
catch (Exception e)
{
// supress
if (!e.Message.Contains("in a group with only"))
{
ExplorerCore.Log(e.ToString());
}
}
}
private void DrawHeaderArea()
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
// Current Scene label
GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) });
@ -247,10 +201,10 @@ namespace Explorer
GUILayout.EndHorizontal();
// ----- GameObject Search -----
GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUIHelper.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUILayout.Label("<b>Search Scene:</b>", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[0]);
m_searchInput = GUIHelper.TextField(m_searchInput, new GUILayoutOption[0]);
if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) }))
{
@ -258,7 +212,7 @@ namespace Explorer
}
GUILayout.EndHorizontal();
GUIUnstrip.Space(5);
GUIHelper.Space(5);
}
private void SceneChangeButtons()
@ -287,22 +241,19 @@ namespace Explorer
{
int index = names.IndexOf(m_currentScene);
index += changeWanted;
if (index > scenes.Count - 1)
if (index >= 0 && index < SceneManager.sceneCount)
{
index = 0;
}
else if (index < 0)
{
index = scenes.Count - 1;
}
m_currentScene = scenes[index].name;
Update_Impl();
}
}
}
}
private void DrawPageButtons()
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
Pages.DrawLimitInputArea();
@ -312,7 +263,7 @@ namespace Explorer
{
Pages.TurnPage(Turn.Left, ref this.scroll);
Update_Impl(true);
Update_Impl();
}
Pages.CurrentPageLabel();
@ -321,7 +272,7 @@ namespace Explorer
{
Pages.TurnPage(Turn.Right, ref this.scroll);
Update_Impl(true);
Update_Impl();
}
}
@ -333,7 +284,7 @@ namespace Explorer
{
if (m_currentTransform != null)
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{
TraverseUp();
@ -344,21 +295,13 @@ namespace Explorer
new GUILayoutOption[] { GUILayout.Width(MainMenu.MainRect.width - 187f) });
}
UIHelpers.SmallInspectButton(m_currentTransform);
Buttons.InspectButton(m_currentTransform);
GUILayout.EndHorizontal();
}
else
{
GUILayout.Label("Scene Root GameObjects:", new GUILayoutOption[0]);
if (m_getRootObjectsFailed)
{
if (GUILayout.Button("Update Root Object List (auto-update failed!)", new GUILayoutOption[0]))
{
Update_Impl(true);
}
}
}
if (m_objectList.Count > 0)
@ -369,11 +312,15 @@ namespace Explorer
if (obj == null) continue;
if (!obj.RefGameObject)
try
{
var go = obj.IValue.Value as GameObject ?? (obj.IValue.Value as Transform)?.gameObject;
if (!go)
{
string label = "<color=red><i>null";
if (obj.RefGameObject != null)
if (go != null)
{
label += " (Destroyed)";
}
@ -383,15 +330,11 @@ namespace Explorer
}
else
{
UIHelpers.GOButton_Impl(obj.RefGameObject,
obj.EnabledColor,
obj.Label,
obj.RefGameObject.activeSelf,
SetTransformTarget,
true,
MainMenu.MainRect.width - 170);
Buttons.GameObjectButton(go, SetTransformTarget, true, MainMenu.MainRect.width - 170f);
}
}
catch { }
}
}
}
@ -410,17 +353,11 @@ namespace Explorer
for (int i = offset; i < offset + Pages.ItemsPerPage && i < m_searchResults.Count; i++)
{
var obj = m_searchResults[i];
var obj = m_searchResults[i].IValue.Value as GameObject;
if (obj.RefGameObject)
if (obj)
{
UIHelpers.GOButton_Impl(obj.RefGameObject,
obj.EnabledColor,
obj.Label,
obj.RefGameObject.activeSelf,
SetTransformTarget,
true,
MainMenu.MainRect.width - 170);
Buttons.GameObjectButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
}
else
{
@ -433,42 +370,5 @@ namespace Explorer
GUILayout.Label("<color=red><i>No results found!</i></color>", new GUILayoutOption[0]);
}
}
// -------- Mini GameObjectCache class ---------- //
public class GameObjectCache
{
public GameObject RefGameObject;
public string Label;
public Color EnabledColor;
public int ChildCount;
public GameObjectCache(GameObject obj)
{
RefGameObject = obj;
ChildCount = obj.transform.childCount;
Label = (ChildCount > 0) ? "[" + obj.transform.childCount + " children] " : "";
Label += obj.name;
bool enabled = obj.activeSelf;
int childCount = obj.transform.childCount;
if (enabled)
{
if (childCount > 0)
{
EnabledColor = Color.green;
}
else
{
EnabledColor = UIStyles.LightGreen;
}
}
else
{
EnabledColor = Color.red;
}
}
}
}
}

View File

@ -4,18 +4,26 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
using Explorer.Helpers;
using Explorer.Unstrip.Resources;
namespace Explorer
namespace Explorer.UI.Main
{
public class SearchPage : WindowPage
public class SearchPage : BaseMainMenuPage
{
public static SearchPage Instance;
public override string Name { get => "Object Search"; }
public override string Name { get => "Search"; }
private int MaxSearchResults = 5000;
private string m_searchInput = "";
private string m_typeInput = "";
//private bool m_cachingResults;
private Vector2 resultsScroll = Vector2.zero;
public PageHelper Pages = new PageHelper();
@ -52,12 +60,12 @@ namespace Explorer
Pages.PageOffset = 0;
}
public override void Update()
{
}
public override void Update() { }
private void CacheResults(IEnumerable results)
private void CacheResults(IEnumerable results, bool isStaticClasses = false)
{
//m_cachingResults = true;
m_searchResults = new List<CacheObjectBase>();
foreach (var obj in results)
@ -67,6 +75,9 @@ namespace Explorer
#if CPP
if (toCache is Il2CppSystem.Object ilObject)
{
var type = ReflectionHelpers.GetActualType(ilObject);
ilObject = (Il2CppSystem.Object)ilObject.Il2CppCast(type);
toCache = ilObject.TryCast<GameObject>() ?? ilObject.TryCast<Transform>()?.gameObject ?? ilObject;
}
#else
@ -76,190 +87,37 @@ namespace Explorer
}
#endif
var cache = CacheObjectBase.GetCacheObject(toCache);
if (toCache is TextAsset textAsset)
{
if (string.IsNullOrEmpty(textAsset.text))
{
continue;
}
}
var cache = CacheFactory.GetCacheObject(toCache);
cache.IsStaticClassSearchResult = isStaticClasses;
m_searchResults.Add(cache);
}
Pages.ItemCount = m_searchResults.Count;
Pages.PageOffset = 0;
results = null;
//m_cachingResults = false;
}
public override void DrawWindow()
{
try
{
// helpers
GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUILayout.Label("<b><color=orange>Helpers</color></b>", new GUILayoutOption[] { GUILayout.Width(70) });
if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) }))
{
//m_searchResults = GetInstanceClassScanner().ToList();
CacheResults(GetInstanceClassScanner());
}
GUILayout.EndHorizontal();
// search box
SearchBox();
// results
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<b><color=orange>Results </color></b>" + " (" + m_searchResults.Count + ")", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.UpperLeft;
int count = m_searchResults.Count;
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
Pages.DrawLimitInputArea();
if (count > Pages.ItemsPerPage)
{
// prev/next page buttons
if (Pages.ItemCount > Pages.ItemsPerPage)
{
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
{
Pages.TurnPage(Turn.Left, ref this.resultsScroll);
}
Pages.CurrentPageLabel();
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(80) }))
{
Pages.TurnPage(Turn.Right, ref this.resultsScroll);
}
}
}
GUILayout.EndHorizontal();
resultsScroll = GUIUnstrip.BeginScrollView(resultsScroll);
var _temprect = new Rect(MainMenu.MainRect.x, MainMenu.MainRect.y, MainMenu.MainRect.width + 160, MainMenu.MainRect.height);
if (m_searchResults.Count > 0)
{
int offset = Pages.CalculateOffsetIndex();
for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++)
{
m_searchResults[i].Draw(MainMenu.MainRect, 0f);
}
}
else
{
GUILayout.Label("<color=red><i>No results found!</i></color>", new GUILayoutOption[0]);
}
GUIUnstrip.EndScrollView();
GUILayout.EndVertical();
}
catch
{
m_searchResults.Clear();
}
}
private void SearchBox()
{
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
// ----- GameObject Search -----
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<b><color=orange>Search</color></b>", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) });
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Class Filter:", new GUILayoutOption[] { GUILayout.Width(100) });
ClassFilterToggle(TypeFilter.Object, "Object");
ClassFilterToggle(TypeFilter.GameObject, "GameObject");
ClassFilterToggle(TypeFilter.Component, "Component");
ClassFilterToggle(TypeFilter.Custom, "Custom");
GUILayout.EndHorizontal();
if (TypeMode == TypeFilter.Custom)
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("Custom Class:", new GUILayoutOption[] { GUILayout.Width(250) });
GUI.skin.label.alignment = TextAnchor.UpperLeft;
m_typeInput = GUILayout.TextField(m_typeInput, new GUILayoutOption[] { GUILayout.Width(250) });
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Scene Filter:", new GUILayoutOption[] { GUILayout.Width(100) });
SceneFilterToggle(SceneFilter.Any, "Any", 60);
SceneFilterToggle(SceneFilter.This, "This Scene", 100);
SceneFilterToggle(SceneFilter.DontDestroy, "DontDestroyOnLoad", 140);
SceneFilterToggle(SceneFilter.None, "No Scene", 80);
GUILayout.EndHorizontal();
if (GUILayout.Button("<b><color=cyan>Search</color></b>", new GUILayoutOption[0]))
{
Search();
}
GUILayout.EndVertical();
}
private void ClassFilterToggle(TypeFilter mode, string label)
{
if (TypeMode == mode)
{
GUI.color = Color.green;
}
else
{
GUI.color = Color.white;
}
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) }))
{
TypeMode = mode;
}
GUI.color = Color.white;
}
private void SceneFilterToggle(SceneFilter mode, string label, float width)
{
if (SceneMode == mode)
{
GUI.color = Color.green;
}
else
{
GUI.color = Color.white;
}
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(width) }))
{
SceneMode = mode;
}
GUI.color = Color.white;
}
// -------------- ACTUAL METHODS (not Gui draw) ----------------- //
// ======= search functions =======
private void Search()
{
Pages.PageOffset = 0;
CacheResults(FindAllObjectsOfType(m_searchInput, m_typeInput));
}
//if (m_cachingResults)
//{
// ExplorerCore.Log("Cannot search now, we are already caching results...");
// return;
//}
Pages.PageOffset = 0;
private List<object> FindAllObjectsOfType(string searchQuery, string typeName)
{
#if CPP
Il2CppSystem.Type searchType = null;
@ -268,9 +126,7 @@ namespace Explorer
#endif
if (TypeMode == TypeFilter.Custom)
{
try
{
if (ReflectionHelpers.GetTypeByName(typeName) is Type t)
if (ReflectionHelpers.GetTypeByName(m_typeInput) is Type t)
{
#if CPP
searchType = Il2CppSystem.Type.GetType(t.AssemblyQualifiedName);
@ -280,12 +136,8 @@ namespace Explorer
}
else
{
throw new Exception($"Could not find a Type by the name of '{typeName}'!");
}
}
catch (Exception e)
{
ExplorerCore.Log("Exception getting Search Type: " + e.GetType() + ", " + e.Message);
ExplorerCore.Log($"Could not find a Type by the name of '{m_typeInput}'!");
return;
}
}
else if (TypeMode == TypeFilter.Object)
@ -307,21 +159,21 @@ namespace Explorer
{
ExplorerCore.LogWarning("Your Custom Class Type must inherit from UnityEngine.Object!");
}
return new List<object>();
return;
}
var matches = new List<object>();
var allObjectsOfType = Resources.FindObjectsOfTypeAll(searchType);
var allObjectsOfType = ResourcesUnstrip.FindObjectsOfTypeAll(searchType);
//ExplorerCore.Log("Found count: " + allObjectsOfType.Length);
int i = 0;
foreach (var obj in allObjectsOfType)
{
if (i >= 2000) break;
if (i >= MaxSearchResults) break;
if (searchQuery != "" && !obj.name.ToLower().Contains(searchQuery.ToLower()))
if (m_searchInput != "" && !obj.name.ToLower().Contains(m_searchInput.ToLower()))
{
continue;
}
@ -351,7 +203,7 @@ namespace Explorer
i++;
}
return matches;
CacheResults(matches);
}
public static bool FilterScene(object obj, SceneFilter filter)
@ -390,8 +242,6 @@ namespace Explorer
return false;
}
// ====== other ========
private static bool FilterName(string name)
{
// Don't really want these instances.
@ -402,7 +252,7 @@ namespace Explorer
}
// credit: ManlyMarco (RuntimeUnityEditor)
public static IEnumerable<object> GetInstanceClassScanner()
public static IEnumerable<object> GetStaticInstances()
{
var query = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(t => t.TryGetTypes())
@ -457,5 +307,238 @@ namespace Explorer
}
}
}
private IEnumerable GetStaticClasses()
{
var list = new List<Type>();
var input = m_searchInput?.ToLower() ?? "";
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
foreach (var type in asm.TryGetTypes())
{
if (!type.IsAbstract || !type.IsSealed)
{
continue;
}
if (!string.IsNullOrEmpty(input))
{
var typename = type.FullName.ToLower();
if (!typename.Contains(input))
{
continue;
}
}
list.Add(type);
}
}
catch { }
}
return list;
}
// =========== GUI DRAW ============= //
public override void DrawWindow()
{
try
{
// helpers
GUIHelper.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUILayout.Label("<b><color=orange>Helpers</color></b>", new GUILayoutOption[] { GUILayout.Width(70) });
if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) }))
{
CacheResults(GetStaticInstances());
}
if (GUILayout.Button("Find Static Classes", new GUILayoutOption[] { GUILayout.Width(180) }))
{
CacheResults(GetStaticClasses(), true);
}
GUILayout.EndHorizontal();
// search box
SearchBox();
// results
GUIHelper.BeginVertical(GUIContent.none, GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<b><color=orange>Results </color></b>" + " (" + m_searchResults.Count + ")", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.UpperLeft;
int count = m_searchResults.Count;
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
Pages.DrawLimitInputArea();
if (count > Pages.ItemsPerPage)
{
// prev/next page buttons
if (Pages.ItemCount > Pages.ItemsPerPage)
{
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
{
Pages.TurnPage(Turn.Left, ref this.resultsScroll);
}
Pages.CurrentPageLabel();
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(80) }))
{
Pages.TurnPage(Turn.Right, ref this.resultsScroll);
}
}
}
GUILayout.EndHorizontal();
resultsScroll = GUIHelper.BeginScrollView(resultsScroll);
var _temprect = new Rect(MainMenu.MainRect.x, MainMenu.MainRect.y, MainMenu.MainRect.width + 160, MainMenu.MainRect.height);
if (m_searchResults.Count > 0)
{
int offset = Pages.CalculateOffsetIndex();
for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++)
{
if (i >= m_searchResults.Count) break;
m_searchResults[i].Draw(MainMenu.MainRect, 0f);
}
}
else
{
GUILayout.Label("<color=red><i>No results found!</i></color>", new GUILayoutOption[0]);
}
GUIHelper.EndScrollView();
GUILayout.EndVertical();
}
catch (Exception e)
{
if (!e.Message.Contains("in a group with only"))
{
ExplorerCore.Log("Exception drawing search results!");
while (e != null)
{
ExplorerCore.Log(e);
e = e.InnerException;
}
m_searchResults.Clear();
}
}
}
private void SearchBox()
{
GUIHelper.BeginVertical(GUIContent.none, GUI.skin.box, null);
// ----- GameObject Search -----
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<b><color=orange>Search</color></b>", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUIHelper.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) });
GUILayout.Label("Max Results:", new GUILayoutOption[] { GUILayout.Width(100) });
var s = MaxSearchResults.ToString();
s = GUIHelper.TextField(s, new GUILayoutOption[] { GUILayout.Width(80) });
if (int.TryParse(s, out int i))
{
MaxSearchResults = i;
}
GUILayout.EndHorizontal();
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Class Filter:", new GUILayoutOption[] { GUILayout.Width(100) });
ClassFilterToggle(TypeFilter.Object, "Object");
ClassFilterToggle(TypeFilter.GameObject, "GameObject");
ClassFilterToggle(TypeFilter.Component, "Component");
ClassFilterToggle(TypeFilter.Custom, "Custom");
GUILayout.EndHorizontal();
if (TypeMode == TypeFilter.Custom)
{
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("Custom Class:", new GUILayoutOption[] { GUILayout.Width(250) });
GUI.skin.label.alignment = TextAnchor.UpperLeft;
m_typeInput = GUIHelper.TextField(m_typeInput, new GUILayoutOption[] { GUILayout.Width(250) });
GUILayout.EndHorizontal();
}
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Scene Filter:", new GUILayoutOption[] { GUILayout.Width(100) });
SceneFilterToggle(SceneFilter.Any, "Any", 60);
SceneFilterToggle(SceneFilter.This, "This Scene", 100);
SceneFilterToggle(SceneFilter.DontDestroy, "DontDestroyOnLoad", 140);
SceneFilterToggle(SceneFilter.None, "No Scene", 80);
GUILayout.EndHorizontal();
if (GUILayout.Button("<b><color=cyan>Search</color></b>", new GUILayoutOption[0]))
{
Search();
}
//if (m_cachingResults)
//{
// GUILayout.Label("Searching...", new GUILayoutOption[0]);
//}
//else
//{
// if (GUILayout.Button("<b><color=cyan>Search</color></b>", new GUILayoutOption[0]))
// {
// Search();
// }
//}
GUILayout.EndVertical();
}
private void ClassFilterToggle(TypeFilter mode, string label)
{
if (TypeMode == mode)
{
GUI.color = Color.green;
}
else
{
GUI.color = Color.white;
}
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) }))
{
TypeMode = mode;
}
GUI.color = Color.white;
}
private void SceneFilterToggle(SceneFilter mode, string label, float width)
{
if (SceneMode == mode)
{
GUI.color = Color.green;
}
else
{
GUI.color = Color.white;
}
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(width) }))
{
SceneMode = mode;
}
GUI.color = Color.white;
}
}
}

View File

@ -3,8 +3,12 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Explorer.Config;
using Explorer.UI.Main;
using Explorer.UI.Shared;
using Explorer.UI.Inspectors;
namespace Explorer
namespace Explorer.UI
{
public class MainMenu
{
@ -17,18 +21,22 @@ namespace Explorer
Pages.Add(new ScenePage());
Pages.Add(new SearchPage());
Pages.Add(new ConsolePage());
Pages.Add(new OptionsPage());
for (int i = 0; i < Pages.Count; i++)
{
var page = Pages[i];
page.Init();
// If page failed to init, it will remove itself from the list. Lower the iterate counter.
if (!Pages.Contains(page)) i--;
}
}
public const int MainWindowID = 5000;
public static Rect MainRect = new Rect(5,5, ModConfig.Instance.Default_Window_Size.x,ModConfig.Instance.Default_Window_Size.y);
public static Rect MainRect = new Rect(5, 5, ModConfig.Instance.Default_Window_Size.x, ModConfig.Instance.Default_Window_Size.y);
public static readonly List<WindowPage> Pages = new List<WindowPage>();
public static readonly List<BaseMainMenuPage> Pages = new List<BaseMainMenuPage>();
private static int m_currentPage = 0;
public static void SetCurrentPage(int index)
@ -39,7 +47,7 @@ namespace Explorer
return;
}
m_currentPage = index;
GUIUnstrip.BringWindowToFront(MainWindowID);
GUIHelper.BringWindowToFront(MainWindowID);
GUI.FocusWindow(MainWindowID);
}
@ -50,39 +58,39 @@ namespace Explorer
public void OnGUI()
{
MainRect = GUIUnstrip.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, ExplorerCore.NAME);
MainRect = GUIHelper.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, ExplorerCore.NAME);
}
private void MainWindow(int id)
{
GUI.DragWindow(new Rect(0, 0, MainRect.width - 90, 20));
if (GUIUnstrip.Button(new Rect(MainRect.width - 90, 2, 80, 20), $"Hide ({ModConfig.Instance.Main_Menu_Toggle})"))
if (GUIHelper.Button(new Rect(MainRect.width - 90, 2, 80, 20), $"Hide ({ModConfig.Instance.Main_Menu_Toggle})"))
{
ExplorerCore.ShowMenu = false;
return;
}
GUIUnstrip.BeginArea(new Rect(5, 25, MainRect.width - 10, MainRect.height - 35), GUI.skin.box);
GUIHelper.BeginArea(new Rect(5, 25, MainRect.width - 10, MainRect.height - 35), GUI.skin.box);
MainHeader();
var page = Pages[m_currentPage];
page.scroll = GUIUnstrip.BeginScrollView(page.scroll);
page.scroll = GUIHelper.BeginScrollView(page.scroll);
page.DrawWindow();
GUIUnstrip.EndScrollView();
GUIHelper.EndScrollView();
MainRect = ResizeDrag.ResizeWindow(MainRect, MainWindowID);
GUIUnstrip.EndArea();
GUIHelper.EndArea();
}
private void MainHeader()
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
for (int i = 0; i < Pages.Count; i++)
{
if (m_currentPage == i)
@ -97,19 +105,19 @@ namespace Explorer
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUI.color = Color.white;
InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", new GUILayoutOption[0]);
bool mouseState = CursorControl.ForceUnlockMouse;
bool mouseState = ForceUnlockCursor.Unlock;
bool setMouse = GUILayout.Toggle(mouseState, "Force Unlock Mouse (Left Alt)", new GUILayoutOption[0]);
if (setMouse != mouseState) CursorControl.ForceUnlockMouse = setMouse;
if (setMouse != mouseState) ForceUnlockCursor.Unlock = setMouse;
WindowManager.TabView = GUILayout.Toggle(WindowManager.TabView, "Tab View", new GUILayoutOption[0]);
//WindowManager.TabView = GUILayout.Toggle(WindowManager.TabView, "Tab View", new GUILayoutOption[0]);
GUILayout.EndHorizontal();
//GUIUnstrip.Space(10);
GUIUnstrip.Space(10);
GUIHelper.Space(10);
GUI.color = Color.white;
}

View File

@ -1,34 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Explorer
namespace Explorer.UI.Shared
{
public class UIHelpers
public class Buttons
{
// helper for "Instantiate" button on UnityEngine.Objects
public static void InstantiateButton(Object obj, float width = 100)
{
if (GUILayout.Button("Instantiate", new GUILayoutOption[] { GUILayout.Width(width) }))
{
var newobj = Object.Instantiate(obj);
WindowManager.InspectObject(newobj, out _);
}
}
// helper for drawing a styled button for a GameObject or Transform
public static void GOButton(object _obj, Action<Transform> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
public static void InspectButton(object obj)
{
var obj = (_obj as GameObject) ?? (_obj as Transform).gameObject;
if (GUILayout.Button("Inspect", new GUILayoutOption[0]))
{
WindowManager.InspectObject(obj, out bool _);
}
}
bool hasChild = obj.transform.childCount > 0;
public static void GameObjectButton(object _obj, Action<Transform> inspectOverride = null, bool showSmallInspect = true, float width = 380)
{
var go = (_obj as GameObject) ?? (_obj as Transform).gameObject;
string label = hasChild ? $"[{obj.transform.childCount} children] " : "";
label += obj.name;
if (!go) return;
bool enabled = obj.activeSelf;
int childCount = obj.transform.childCount;
bool hasChild = go.transform.childCount > 0;
string label = hasChild ? $"[{go.transform.childCount} children] " : "";
label += go.name;
bool enabled = go.activeSelf;
int childCount = go.transform.childCount;
Color color;
if (enabled)
@ -47,39 +57,26 @@ namespace Explorer
color = Color.red;
}
GOButton_Impl(_obj, color, label, obj.activeSelf, specialInspectMethod, showSmallInspectBtn, width);
}
public static void GOButton_Impl(object _obj, Color activeColor, string label, bool enabled, Action<Transform> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
{
var obj = _obj as GameObject ?? (_obj as Transform).gameObject;
if (!obj)
{
GUILayout.Label("<i><color=red>null</color></i>", new GUILayoutOption[0]);
return;
}
// ------ toggle active button ------
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.button.alignment = TextAnchor.UpperLeft;
GUI.color = activeColor;
GUI.color = color;
enabled = GUILayout.Toggle(enabled, "", new GUILayoutOption[] { GUILayout.Width(18) });
if (obj.activeSelf != enabled)
if (go.activeSelf != enabled)
{
obj.SetActive(enabled);
go.SetActive(enabled);
}
// ------- actual button ---------
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Height(22), GUILayout.Width(width) }))
{
if (specialInspectMethod != null)
if (inspectOverride != null)
{
specialInspectMethod(obj.transform);
inspectOverride(go.transform);
}
else
{
@ -92,20 +89,12 @@ namespace Explorer
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
GUI.color = Color.white;
if (showSmallInspectBtn)
if (showSmallInspect)
{
SmallInspectButton(_obj);
InspectButton(_obj);
}
GUILayout.EndHorizontal();
}
public static void SmallInspectButton(object obj)
{
if (GUILayout.Button("Inspect", new GUILayoutOption[0]))
{
WindowManager.InspectObject(obj, out bool _);
}
}
}
}

View File

@ -1,6 +1,6 @@
using UnityEngine;
namespace Explorer
namespace Explorer.UI.Shared
{
public enum Turn
{
@ -21,7 +21,7 @@ namespace Explorer
CalculateMaxOffset();
}
}
private int m_itemsPerPage = 20;
private int m_itemsPerPage = Config.ModConfig.Instance.Default_Page_Limit;
public int ItemCount
{
@ -94,7 +94,7 @@ namespace Explorer
{
GUILayout.Label("Limit: ", new GUILayoutOption[] { GUILayout.Width(50) });
var limit = this.ItemsPerPage.ToString();
limit = GUILayout.TextField(limit, new GUILayoutOption[] { GUILayout.Width(50) });
limit = GUIHelper.TextField(limit, new GUILayoutOption[] { GUILayout.Width(50) });
if (limit != ItemsPerPage.ToString() && int.TryParse(limit, out int i))
{
ItemsPerPage = i;

View File

@ -4,14 +4,16 @@ using UnhollowerBaseLib;
#endif
using UnityEngine;
namespace Explorer
namespace Explorer.UI.Shared
{
public class ResizeDrag
{
private static bool RESIZE_FAILED = false;
public static bool IsResizing = false;
public static bool IsMouseInResizeArea = false;
private static readonly GUIContent gcDrag = new GUIContent("<-- Drag to resize -->");
private static bool isResizing = false;
private static Rect m_currentResize;
private static int m_currentWindow;
@ -23,39 +25,43 @@ namespace Explorer
try
{
GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUIHelper.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
#if CPP
#if ML
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) });
#if BIE
#if CPP // Temporary for BepInEx IL2CPP
GUILayout.Button("<-- Drag to resize -->", new GUILayoutOption[] { GUILayout.Height(15) });
#else
GUILayout.Button(gcDrag.ToString(), new GUILayoutOption[] { GUILayout.Height(15) });
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) });
#endif
#else
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) });
#endif
//var r = GUILayoutUtility.GetLastRect();
var r = LayoutUtilityUnstrip.GetLastRect();
var mousePos = InputHelper.mousePosition;
var resizeDragArea = GUIHelper.GetLastRect();
var mousePos = InputManager.MousePosition;
try
{
var mouse = GUIUnstrip.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y));
if (r.Contains(mouse) && InputHelper.GetMouseButtonDown(0))
var mouse = GUIHelper.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y));
if (resizeDragArea.Contains(mouse))
{
isResizing = true;
IsMouseInResizeArea = true;
if (InputManager.GetMouseButtonDown(0))
{
IsResizing = true;
m_currentWindow = ID;
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
}
else if (!InputHelper.GetMouseButton(0))
}
else if (!InputManager.GetMouseButton(0))
{
isResizing = false;
IsMouseInResizeArea = false;
IsResizing = false;
}
if (isResizing && ID == m_currentWindow)
if (IsResizing && ID == m_currentWindow)
{
_rect.width = Mathf.Max(100, m_currentResize.width + (mouse.x - m_currentResize.x));
_rect.height = Mathf.Max(100, m_currentResize.height + (mouse.y - m_currentResize.y));
@ -83,39 +89,38 @@ namespace Explorer
//ExplorerCore.Log(e.StackTrace);
return origRect;
}
GUI.skin.label.alignment = TextAnchor.UpperLeft;
}
else
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Resize window:", new GUILayoutOption[] { GUILayout.Width(100) });
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("<color=cyan>Width:</color>", new GUILayoutOption[] { GUILayout.Width(60) });
if (GUIUnstrip.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) }))
if (GUIHelper.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) }))
{
_rect.width -= 5f;
}
if (GUIUnstrip.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) }))
if (GUIHelper.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) }))
{
_rect.width += 5f;
}
GUILayout.Label("<color=cyan>Height:</color>", new GUILayoutOption[] { GUILayout.Width(60) });
if (GUIUnstrip.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) }))
if (GUIHelper.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) }))
{
_rect.height -= 5f;
}
if (GUIUnstrip.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) }))
if (GUIHelper.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) }))
{
_rect.height += 5f;
}
GUILayout.EndHorizontal();
GUI.skin.label.alignment = TextAnchor.UpperLeft;
}
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
return _rect;
}
}

26
src/UI/Shared/Syntax.cs Normal file
View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Explorer.UI.Shared
{
public class Syntax
{
public const string Field_Static = "#8d8dc6";
public const string Field_Instance = "#c266ff";
public const string Method_Static = "#b55b02";
public const string Method_Instance = "#ff8000";
public const string Prop_Static = "#588075";
public const string Prop_Instance = "#55a38e";
public const string Class_Static = "#3a8d71";
public const string Class_Instance = "#2df7b2";
public const string Local = "#a6e9e9";
public const string StructGreen = "#92c470";
}
}

View File

@ -1,29 +1,10 @@
using UnityEngine;
using Object = UnityEngine.Object;
namespace Explorer
namespace Explorer.UI.Shared
{
public class UIStyles
{
public class Syntax
{
public const string Field_Static = "#8d8dc6";
public const string Field_Instance = "#c266ff";
public const string Method_Static = "#b55b02";
public const string Method_Instance = "#ff8000";
public const string Prop_Static = "#588075";
public const string Prop_Instance = "#55a38e";
public const string Class_Static = "#3a8d71";
public const string Class_Instance = "#2df7b2";
public const string Local = "#a6e9e9";
public const string StructGreen = "#b8d7a3";
}
public static Color LightGreen = new Color(Color.green.r - 0.3f, Color.green.g - 0.3f, Color.green.b - 0.3f);
public static GUISkin WindowSkin

View File

@ -1,15 +1,17 @@
using UnityEngine;
using System;
using UnityEngine;
using Explorer.UI.Shared;
namespace Explorer
namespace Explorer.UI
{
public class TabViewWindow : UIWindow
public class TabViewWindow : WindowBase
{
public override string Title => $"Tabs ({WindowManager.Windows.Count})";
public static TabViewWindow Instance => m_instance ?? (m_instance = new TabViewWindow());
private static TabViewWindow m_instance;
private UIWindow m_targetWindow;
private WindowBase m_targetWindow;
public int TargetTabID = 0;
public override bool IsTabViewWindow => true;
@ -50,7 +52,7 @@ namespace Explorer
try
{
GUI.DragWindow(new Rect(0, 0, m_rect.width - 90, 20));
if (GUIUnstrip.Button(new Rect(m_rect.width - 90, 2, 80, 20), "<color=red>Close All</color>"))
if (GUIHelper.Button(new Rect(m_rect.width - 90, 2, 80, 20), "<color=red>Close All</color>"))
{
foreach (var window in WindowManager.Windows)
{
@ -59,20 +61,29 @@ namespace Explorer
return;
}
GUIUnstrip.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box);
GUIHelper.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box);
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginVertical(GUIContent.none, GUI.skin.box, null);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
int tabPerRow = Mathf.FloorToInt((float)((decimal)m_rect.width / 238));
int tabPerRow = (int)Math.Floor(m_rect.width / 238);
int rowCount = 0;
for (int i = 0; i < WindowManager.Windows.Count; i++)
{
var window = WindowManager.Windows[i];
// Prevent trying to draw destroyed UnityEngine.Objects
// before the WindowManager removes them.
if (window.Target is UnityEngine.Object uObj && !uObj)
{
continue;
}
if (rowCount >= tabPerRow)
{
rowCount = 0;
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
}
rowCount++;
@ -80,7 +91,6 @@ namespace Explorer
string color = focused ? "<color=lime>" : "<color=orange>";
GUI.color = focused ? Color.green : Color.white;
var window = WindowManager.Windows[i];
if (GUILayout.Button(color + window.Title + "</color>", new GUILayoutOption[] { GUILayout.Width(200) }))
{
TargetTabID = i;
@ -97,15 +107,18 @@ namespace Explorer
m_targetWindow.WindowFunction(m_targetWindow.windowID);
try
{
m_rect = ResizeDrag.ResizeWindow(m_rect, windowID);
}
catch { }
GUIUnstrip.EndArea();
GUIHelper.EndArea();
}
catch (Exception e)
{
if (!e.Message.Contains("in a group with only"))
{
ExplorerCore.Log("Exception drawing Tab View window: " + e.GetType() + ", " + e.Message);
ExplorerCore.Log(e.StackTrace);
}
}
catch { }
}
}
}

View File

@ -1,16 +1,18 @@
using System;
using UnityEngine;
using Explorer.Config;
using Explorer.UI.Inspectors;
namespace Explorer
namespace Explorer.UI
{
public abstract class UIWindow
public abstract class WindowBase
{
public abstract string Title { get; }
public object Target;
public int windowID;
public Rect m_rect = new Rect(0,0, ModConfig.Instance.Default_Window_Size.x,ModConfig.Instance.Default_Window_Size.y);
public Rect m_rect = new Rect(0, 0, ModConfig.Instance.Default_Window_Size.x, ModConfig.Instance.Default_Window_Size.y);
public Vector2 scroll = Vector2.zero;
@ -20,7 +22,7 @@ namespace Explorer
public abstract void WindowFunction(int windowID);
public abstract void Update();
public static UIWindow CreateWindow<T>(object target) where T : UIWindow
public static WindowBase CreateWindow<T>(object target) where T : WindowBase
{
var window = Activator.CreateInstance<T>();
@ -35,6 +37,22 @@ namespace Explorer
return window;
}
public static StaticInspector CreateWindowStatic(Type type)
{
var window = new StaticInspector
{
TargetType = type,
windowID = WindowManager.NextWindowID(),
m_rect = WindowManager.GetNewWindowRect()
};
WindowManager.Windows.Add(window);
window.Init();
return window;
}
public void DestroyWindow()
{
WindowManager.DestroyWindow(this);

View File

@ -1,90 +1,60 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using UnityEngine;
using Explorer.UI.Inspectors;
namespace Explorer
namespace Explorer.UI
{
public class WindowManager
{
public static WindowManager Instance;
public static bool TabView = true;
public static bool TabView = Config.ModConfig.Instance.Tab_View;
public static List<UIWindow> Windows = new List<UIWindow>();
public static bool IsMouseInWindow
{
get
{
if (!ExplorerCore.ShowMenu)
{
return false;
}
foreach (var window in Windows)
{
if (RectContainsMouse(window.m_rect))
{
return true;
}
}
return RectContainsMouse(MainMenu.MainRect);
}
}
public static List<WindowBase> Windows = new List<WindowBase>();
public static int CurrentWindowID { get; set; } = 500000;
private static Rect m_lastWindowRect;
private static readonly List<UIWindow> m_windowsToDestroy = new List<UIWindow>();
private static readonly List<WindowBase> m_windowsToDestroy = new List<WindowBase>();
public WindowManager()
{
Instance = this;
}
public static void DestroyWindow(UIWindow window)
public static void DestroyWindow(WindowBase window)
{
m_windowsToDestroy.Add(window);
}
public void Update()
{
if (m_windowsToDestroy.Count > 0)
{
foreach (var window in m_windowsToDestroy)
{
if (Windows.Contains(window))
{
Windows.Remove(window);
}
}
m_windowsToDestroy.Clear();
}
if (TabView)
{
TabViewWindow.Instance.Update();
}
else
{
for (int i = 0; i < Windows.Count; i++)
{
var window = Windows[i];
if (window != null)
{
window.Update();
}
}
}
}
public void OnGUI()
{
if (TabView)
{
if (Windows.Count > 0)
{
TabViewWindow.Instance.OnGUI();
}
}
else
{
foreach (var window in Windows)
{
window.OnGUI();
}
}
}
// ========= Public Helpers =========
public static UIWindow InspectObject(object obj, out bool createdNew, bool forceReflection = false)
public static WindowBase InspectObject(object obj, out bool createdNew, bool forceReflection = false)
{
createdNew = false;
if (InputHelper.GetKey(KeyCode.LeftShift))
{
forceReflection = true;
}
//if (InputManager.GetKey(KeyCode.LeftShift))
//{
// forceReflection = true;
//}
#if CPP
Il2CppSystem.Object iObj = null;
@ -136,11 +106,11 @@ namespace Explorer
}
}
private static void FocusWindow(UIWindow window)
private static void FocusWindow(WindowBase window)
{
if (!TabView)
{
GUIUnstrip.BringWindowToFront(window.windowID);
GUIHelper.BringWindowToFront(window.windowID);
GUI.FocusWindow(window.windowID);
}
else
@ -149,47 +119,33 @@ namespace Explorer
}
}
private static UIWindow InspectGameObject(GameObject obj)
private static WindowBase InspectGameObject(GameObject obj)
{
var new_window = UIWindow.CreateWindow<GameObjectWindow>(obj);
var new_window = WindowBase.CreateWindow<GameObjectInspector>(obj);
FocusWindow(new_window);
return new_window;
}
private static UIWindow InspectReflection(object obj)
private static WindowBase InspectReflection(object obj)
{
var new_window = UIWindow.CreateWindow<ReflectionWindow>(obj);
var new_window = WindowBase.CreateWindow<InstanceInspector>(obj);
FocusWindow(new_window);
return new_window;
}
// === Misc Helpers ===
public static StaticInspector InspectStaticReflection(Type type)
{
var new_window = WindowBase.CreateWindowStatic(type);
FocusWindow(new_window);
public static bool IsMouseInWindow
{
get
{
if (!ExplorerCore.ShowMenu)
{
return false;
}
foreach (var window in Windows)
{
if (RectContainsMouse(window.m_rect))
{
return true;
}
}
return RectContainsMouse(MainMenu.MainRect);
}
return new_window;
}
private static bool RectContainsMouse(Rect rect)
{
var mousePos = InputHelper.mousePosition;
var mousePos = InputManager.MousePosition;
return rect.Contains(new Vector2(mousePos.x, Screen.height - mousePos.y));
}
@ -222,5 +178,57 @@ namespace Explorer
return rect;
}
// ============= instance methods ===============
public void Update()
{
if (m_windowsToDestroy.Count > 0)
{
foreach (var window in m_windowsToDestroy)
{
if (Windows.Contains(window))
{
Windows.Remove(window);
}
}
m_windowsToDestroy.Clear();
}
if (TabView)
{
TabViewWindow.Instance.Update();
}
else
{
for (int i = 0; i < Windows.Count; i++)
{
var window = Windows[i];
if (window != null)
{
window.Update();
}
}
}
}
public void OnGUI()
{
if (TabView)
{
if (Windows.Count > 0)
{
TabViewWindow.Instance.OnGUI();
}
}
else
{
foreach (var window in Windows)
{
window.OnGUI();
}
}
}
}
}

View File

@ -0,0 +1,175 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
#if CPP
using Explorer.Unstrip.IMGUI;
#endif
namespace Explorer
{
// All the pre-processor directive stuff is in this class to keep it separate.
// This is so Mono build can use this class and not have to worry.
public class GUIHelper
{
internal static GUILayoutOption ExpandHeight(bool expand)
{
#if CPP
return GUIUnstrip.ExpandHeight(expand);
#else
return GUILayout.ExpandHeight(expand);
#endif
}
public static void BeginHorizontal(params GUILayoutOption[] options)
=> BeginHorizontal(GUIContent.none, GUIStyle.none, options);
public static void BeginHorizontal(GUIStyle style, params GUILayoutOption[] options)
=> BeginHorizontal(GUIContent.none, style, options);
public static void BeginHorizontal(GUIContent content, GUIStyle style, params GUILayoutOption[] options)
{
#if CPP
GUIUnstrip.BeginLayoutDirection(false, content, style, options);
#else
GUILayout.BeginHorizontal(content, style, options);
#endif
}
public static void BeginVertical(params GUILayoutOption[] options)
=> BeginVertical(GUIContent.none, GUIStyle.none, options);
public static void BeginVertical(GUIStyle style, params GUILayoutOption[] options)
=> BeginVertical(GUIContent.none, style, options);
public static void BeginVertical(GUIContent content, GUIStyle style, params GUILayoutOption[] options)
{
#if CPP
GUIUnstrip.BeginLayoutDirection(true, content, style, options);
#else
GUILayout.BeginVertical(content, style, options);
#endif
}
public static Rect GetLastRect()
{
#if CPP
return LayoutUtilityUnstrip.GetLastRect();
#else
return GUILayoutUtility.GetLastRect();
#endif
}
public static string TextField(string text, GUILayoutOption[] options)
{
#if CPP
return GUIUnstrip.TextField(text, options, false);
#else
return GUILayout.TextField(text, options);
#endif
}
public static string TextArea(string text, params GUILayoutOption[] options)
{
#if CPP
return GUIUnstrip.TextField(text, options, true);
#else
return GUILayout.TextArea(text, options);
#endif
}
public static Rect Window(int id, Rect rect, GUI.WindowFunction windowFunc, string title)
{
#if CPP
return GUI.Window(id, rect, windowFunc, GUIContent.Temp(title), GUI.skin.window);
#else
return GUI.Window(id, rect, windowFunc, title);
#endif
}
public static bool Button(Rect rect, string title)
{
#if CPP
return GUI.Button(rect, GUIContent.Temp(title), GUI.skin.button);
#else
return GUI.Button(rect, title);
#endif
}
public static void BringWindowToFront(int id)
{
#if CPP
GUIUnstrip.BringWindowToFront(id);
#else
GUI.BringWindowToFront(id);
#endif
}
public static Vector2 ScreenToGUIPoint(Vector2 screenPoint)
{
#if CPP
return GUIUnstrip.ScreenToGUIPoint(screenPoint);
#else
return GUIUtility.ScreenToGUIPoint(screenPoint);
#endif
}
public static void Space(float pixels)
{
#if CPP
GUIUnstrip.Space(pixels);
#else
GUILayout.Space(pixels);
#endif
}
public static bool RepeatButton(string text, params GUILayoutOption[] options)
{
#if CPP
return GUIUnstrip.DoRepeatButton(GUIContent.Temp(text), GUI.skin.button, options);
#else
return GUILayout.RepeatButton(text, options);
#endif
}
public static void BeginArea(Rect screenRect, GUIStyle style)
{
#if CPP
GUIUnstrip.BeginArea(screenRect, GUIContent.none, style);
#else
GUILayout.BeginArea(screenRect, style);
#endif
}
static public void EndArea()
{
#if CPP
GUIUnstrip.EndArea();
#else
GUILayout.EndArea();
#endif
}
public static Vector2 BeginScrollView(Vector2 scroll, params GUILayoutOption[] options)
{
#if CPP
return GUIUnstrip.BeginScrollView(scroll, options);
#else
return GUILayout.BeginScrollView(scroll, options);
#endif
}
public static void EndScrollView(bool handleScrollWheel = true)
{
#if CPP
GUIUnstrip.EndScrollView(handleScrollWheel);
#else
GUILayout.EndScrollView();
#endif
}
}
}

View File

@ -0,0 +1,786 @@
#if CPP
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngineInternal;
using UnhollowerRuntimeLib;
namespace Explorer.Unstrip.IMGUI
{
// Also contains some stuff from GUI.
// This class was meant to be temporary but who knows.
public class GUIUnstrip
{
#region Properties
public static int s_ScrollControlId;
public static bool ScrollFailed = false;
public static bool ManualUnstripFailed = false;
public static Stack<object> ScrollStack => m_scrollStack ?? GetScrollStack();
public static Stack<object> m_scrollStack;
//public static PropertyInfo m_scrollViewStatesInfo;
public static GUIStyle SpaceStyle => m_spaceStyle ?? GetSpaceStyle();
public static GUIStyle m_spaceStyle;
public static DateTime nextScrollStepTime;
public static MethodInfo ScreenToGuiPointMethod;
public static bool m_screenToGuiAttemped;
public static MethodInfo m_bringWindowToFrontMethod;
public static bool m_bringWindowFrontAttempted;
private static Stack<object> GetScrollStack()
{
m_scrollStack = new Stack<object>();
return m_scrollStack;
}
private static GUIStyle GetSpaceStyle()
{
try
{
m_spaceStyle = typeof(GUILayoutUtility)
.GetProperty("s_SpaceStyle")
.GetValue(null, null)
.Il2CppCast(typeof(GUIStyle))
as GUIStyle;
if (m_spaceStyle == null) throw new Exception();
}
catch { }
if (m_spaceStyle == null)
{
m_spaceStyle = new GUIStyle();
}
m_spaceStyle.stretchWidth = false;
return m_spaceStyle;
}
#endregion
#region GUILayout Methods
public static GUILayoutOption ExpandWidth(bool expand)
{
var ilValue = new Il2CppSystem.Int32
{
m_value = (!expand) ? 0 : 1
};
var option = new GUILayoutOption(GUILayoutOption.Type.stretchWidth, ilValue.BoxIl2CppObject());
return option;
}
public static GUILayoutOption ExpandHeight(bool expand)
{
var ilValue = new Il2CppSystem.Int32
{
m_value = (!expand) ? 0 : 1
};
var option = new GUILayoutOption(GUILayoutOption.Type.stretchHeight, ilValue.BoxIl2CppObject());
return option;
}
public static void BeginLayoutDirection(bool vertical, GUIContent content, GUIStyle style, GUILayoutOption[] options)
{
var g = GUILayoutUtility.BeginLayoutGroup(style, options, Il2CppType.Of<GUILayoutGroup>());
g.isVertical = vertical;
if (style != GUIStyle.none || content != GUIContent.none)
GUI.Box(g.rect, content, style);
}
public static string TextField(string text, GUILayoutOption[] options, bool multiLine)
{
text = text ?? string.Empty;
var skin = multiLine ? GUI.skin.textArea : GUI.skin.textField;
int controlID = GUIUtility.GetControlID(FocusType.Keyboard);
GUIContent guicontent = GUIContent.Temp(text);
bool flag = GUIUtility.keyboardControl != controlID;
if (flag)
{
guicontent = GUIContent.Temp(text);
}
else
{
guicontent = GUIContent.Temp(text);
// guicontent = GUIContent.Temp(text + GUIUtility.compositionString);
}
Rect rect = LayoutUtilityUnstrip.GetRect(guicontent, skin, options);
bool flag2 = GUIUtility.keyboardControl == controlID;
if (flag2)
{
guicontent = GUIContent.Temp(text);
}
DoTextField(rect, controlID, guicontent, multiLine, -1, skin);
return guicontent.text;
}
internal static void DoTextField(Rect position, int id, GUIContent content, bool multiline, int maxLength, GUIStyle style)
{
if (GUIUtilityUnstrip.GetMonoStateObject(typeof(TextEditorUnstrip), id) is TextEditorUnstrip textEditor)
{
if (maxLength >= 0 && content.text.Length > maxLength)
{
content.text = content.text.Substring(0, maxLength);
}
textEditor.m_Content.text = content.text;
textEditor.SaveBackup();
textEditor.position = position;
textEditor.style = style;
textEditor.multiline = multiline;
textEditor.controlID = id;
textEditor.DetectFocusChange();
HandleTextFieldEventForDesktop(position, id, content, multiline, maxLength, style, textEditor);
textEditor.UpdateScrollOffsetIfNeeded(Event.current);
}
}
private static void HandleTextFieldEventForDesktop(Rect position, int id, GUIContent content, bool multiline, int maxLength,
GUIStyle style, TextEditorUnstrip editor)
{
var evt = Event.current;
bool change = false;
switch (evt.type)
{
case EventType.MouseDown:
if (position.Contains(evt.mousePosition))
{
GUIUtility.hotControl = id;
GUIUtility.keyboardControl = id;
editor.m_HasFocus = true;
editor.MoveCursorToPosition(Event.current.mousePosition);
if (Event.current.clickCount == 2 && GUI.skin.settings.doubleClickSelectsWord)
{
editor.SelectCurrentWord();
editor.DblClickSnap(TextEditorUnstrip.DblClickSnapping.WORDS);
editor.MouseDragSelectsWholeWords(true);
}
if (Event.current.clickCount == 3 && GUI.skin.settings.tripleClickSelectsLine)
{
editor.SelectCurrentParagraph();
editor.MouseDragSelectsWholeWords(true);
editor.DblClickSnap(TextEditorUnstrip.DblClickSnapping.PARAGRAPHS);
}
evt.Use();
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == id)
{
if (evt.shift)
editor.MoveCursorToPosition(Event.current.mousePosition);
else
editor.SelectToPosition(Event.current.mousePosition);
evt.Use();
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == id)
{
editor.MouseDragSelectsWholeWords(false);
GUIUtility.hotControl = 0;
evt.Use();
}
break;
case EventType.KeyDown:
if (GUIUtility.keyboardControl != id)
return;
if (editor.HandleKeyEvent(evt))
{
evt.Use();
change = true;
content.text = editor.text;
break;
}
// Ignore tab & shift-tab in textfields
if (evt.keyCode == KeyCode.Tab || evt.character == '\t')
return;
char c = evt.character;
if (c == '\n' && !multiline && !evt.alt)
return;
// Simplest test: only allow the character if the display font supports it.
Font font = style.font;
if (!font)
font = GUI.skin.font;
if (font.HasCharacter(c) || c == '\n')
{
editor.Insert(c);
change = true;
break;
}
// On windows, keypresses also send events with keycode but no character. Eat them up here.
if (c == 0)
{
// if we have a composition string, make sure we clear the previous selection.
if (InputManager.compositionString.Length > 0)
{
editor.ReplaceSelection("");
change = true;
}
evt.Use();
}
// else {
// REALLY USEFUL:
// Debug.Log ("unhandled " +evt);
// evt.Use ();
// }
break;
case EventType.Repaint:
// If we have keyboard focus, draw the cursor
// TODO: check if this OpenGL view has keyboard focus
if (GUIUtility.keyboardControl != id)
{
style.Draw(position, content, id, false);
}
else
{
editor.DrawCursor(content.text);
}
break;
}
if (GUIUtility.keyboardControl == id)
GUIUtility.textFieldInput = true;
if (change)
{
GUI.changed = true;
content.text = editor.text;
if (maxLength >= 0 && content.text.Length > maxLength)
content.text = content.text.Substring(0, maxLength);
evt.Use();
}
}
public static bool DoRepeatButton(GUIContent content, GUIStyle style, GUILayoutOption[] options)
{
return GUI.DoRepeatButton(LayoutUtilityUnstrip.GetRect(content, style, options), content, style, FocusType.Passive);
}
public static void Space(float pixels)
{
if (GUILayoutUtility.current.topLevel.isVertical)
LayoutUtilityUnstrip.GetRect(0, pixels, SpaceStyle, new GUILayoutOption[] { GUILayout.Height(pixels) });
else
LayoutUtilityUnstrip.GetRect(pixels, 0, SpaceStyle, new GUILayoutOption[] { GUILayout.Width(pixels) });
if (Event.current.type == EventType.Layout)
{
GUILayoutUtility.current.topLevel.entries[GUILayoutUtility.current.topLevel.entries.Count - 1].consideredForMargin = false;
}
}
public static Vector2 ScreenToGUIPoint(Vector2 screenPoint)
{
if (!m_screenToGuiAttemped)
{
m_screenToGuiAttemped = true;
ScreenToGuiPointMethod = typeof(GUIUtility).GetMethod("ScreenToGUIPoint");
}
if (ScreenToGuiPointMethod == null)
{
throw new Exception("Couldn't get method 'GUIUtility.ScreenToGUIPoint'!");
}
return (Vector2)ScreenToGuiPointMethod.Invoke(null, new object[] { screenPoint });
}
public static void BringWindowToFront(int id)
{
if (!m_bringWindowFrontAttempted)
{
m_bringWindowFrontAttempted = true;
m_bringWindowToFrontMethod = typeof(GUI).GetMethod("BringWindowToFront");
}
if (m_bringWindowToFrontMethod == null)
{
throw new Exception("Couldn't get method 'GUIUtility.BringWindowToFront'!");
}
m_bringWindowToFrontMethod.Invoke(null, new object[] { id });
}
public static void BeginArea(Rect screenRect, GUIContent content, GUIStyle style)
{
var g = BeginLayoutArea(style, typeof(GUILayoutGroup));
if (Event.current.type == EventType.Layout)
{
g.resetCoords = true;
g.minWidth = g.maxWidth = screenRect.width;
g.minHeight = g.maxHeight = screenRect.height;
g.rect = Rect.MinMaxRect(screenRect.xMin, screenRect.yMin, g.rect.xMax, g.rect.yMax);
}
BeginGroup(g.rect, content, style);
}
internal static GUILayoutGroup BeginLayoutArea(GUIStyle style, Type layoutType)
{
EventType type = Event.current.type;
GUILayoutGroup guilayoutGroup;
if (type != EventType.Used && type != EventType.Layout)
{
guilayoutGroup = GUILayoutUtility.current.windows.GetNext().TryCast<GUILayoutGroup>();
guilayoutGroup.ResetCursor();
}
else
{
guilayoutGroup = (GUILayoutGroup)Activator.CreateInstance(layoutType);
guilayoutGroup.style = style;
GUILayoutUtility.current.windows.Add(guilayoutGroup);
}
GUILayoutUtility.current.layoutGroups.Push(guilayoutGroup);
GUILayoutUtility.current.topLevel = guilayoutGroup;
return guilayoutGroup;
}
public static void BeginGroup(Rect position, GUIContent content, GUIStyle style)
{
BeginGroup(position, content, style, Vector2.zero);
}
internal static void BeginGroup(Rect position, GUIContent content, GUIStyle style, Vector2 scrollOffset)
{
int id = GUIUtility.GetControlID(GUI.s_BeginGroupHash, FocusType.Passive);
if (content != GUIContent.none || style != GUIStyle.none)
{
switch (Event.current.type)
{
case EventType.Repaint:
style.Draw(position, content, id);
break;
default:
if (position.Contains(Event.current.mousePosition))
GUIUtility.mouseUsed = true;
break;
}
}
GUIClip.Push(position, scrollOffset, Vector2.zero, false);
}
public static void EndArea()
{
if (Event.current.type == EventType.Used)
return;
GUILayoutUtility.current.layoutGroups.Pop();
GUILayoutUtility.current.topLevel = GUILayoutUtility.current.layoutGroups.Peek().TryCast<GUILayoutGroup>();
GUI.EndGroup();
}
#endregion
#region Scrolling
public static Vector2 BeginScrollView(Vector2 scroll, params GUILayoutOption[] options)
{
// First, just try normal way, may not have been stripped or was unstripped successfully.
if (!ScrollFailed)
{
try
{
return GUILayout.BeginScrollView(scroll, options);
}
catch
{
ScrollFailed = true;
}
}
// Try manual implementation.
if (!ManualUnstripFailed)
{
try
{
return BeginScrollView_ImplLayout(scroll,
false,
false,
GUI.skin.horizontalScrollbar,
GUI.skin.verticalScrollbar,
GUI.skin.scrollView,
options);
}
catch (Exception e)
{
ExplorerCore.Log("Exception on manual BeginScrollView: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
ManualUnstripFailed = true;
}
}
// Sorry! No scrolling for you.
return scroll;
}
internal static void EndScrollView(bool handleScrollWheel)
{
// Only end the scroll view for the relevant BeginScrollView option, if any.
if (!ScrollFailed)
{
GUILayout.EndScrollView();
}
else if (!ManualUnstripFailed)
{
GUILayoutUtility.EndLayoutGroup();
if (ScrollStack.Count <= 0) return;
var scrollExt = ScrollStack.Peek() as ScrollViewStateUnstrip;
//var state = ScrollStack.Peek().TryCast<ScrollViewState>();
//var scrollExt = Internal_ScrollViewState.FromPointer(state.Pointer);
//if (scrollExt == null) throw new Exception("Could not get scrollExt!");
GUIClip.Pop();
ScrollStack.Pop();
var position = scrollExt.position;
if (handleScrollWheel && Event.current.type == EventType.ScrollWheel && position.Contains(Event.current.mousePosition))
{
var pos = scrollExt.scrollPosition;
pos.x = Mathf.Clamp(scrollExt.scrollPosition.x + Event.current.delta.x * 20f, 0f, scrollExt.viewRect.width - scrollExt.visibleRect.width);
pos.y = Mathf.Clamp(scrollExt.scrollPosition.y + Event.current.delta.y * 20f, 0f, scrollExt.viewRect.height - scrollExt.visibleRect.height);
if (scrollExt.scrollPosition.x < 0f)
{
pos.x = 0f;
}
if (pos.y < 0f)
{
pos.y = 0f;
}
scrollExt.apply = true;
Event.current.Use();
}
}
}
private static Vector2 BeginScrollView_ImplLayout(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical,
GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options)
{
var guiscrollGroup = GUILayoutUtility.BeginLayoutGroup(background, null, Il2CppType.Of<GUIScrollGroup>())
.TryCast<GUIScrollGroup>();
EventType type = Event.current.type;
if (type == EventType.Layout)
{
guiscrollGroup.resetCoords = true;
guiscrollGroup.isVertical = true;
guiscrollGroup.stretchWidth = 1;
guiscrollGroup.stretchHeight = 1;
guiscrollGroup.verticalScrollbar = verticalScrollbar;
guiscrollGroup.horizontalScrollbar = horizontalScrollbar;
guiscrollGroup.needsVerticalScrollbar = alwaysShowVertical;
guiscrollGroup.needsHorizontalScrollbar = alwaysShowHorizontal;
guiscrollGroup.ApplyOptions(options);
}
return BeginScrollView_Impl(guiscrollGroup.rect,
scrollPosition,
new Rect(0f, 0f, guiscrollGroup.clientWidth, guiscrollGroup.clientHeight),
alwaysShowHorizontal,
alwaysShowVertical,
horizontalScrollbar,
verticalScrollbar,
background
);
}
private static Vector2 BeginScrollView_Impl(Rect position, Vector2 scrollPosition, Rect viewRect, bool alwaysShowHorizontal,
bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background)
{
// GUIUtility.CheckOnGUI();
int controlID = GUIUtility.GetControlID(GUI.s_ScrollviewHash, FocusType.Passive);
ScrollViewStateUnstrip scrollExt;
try
{
scrollExt = (ScrollViewStateUnstrip)GUIUtilityUnstrip.GetMonoStateObject(typeof(ScrollViewStateUnstrip), controlID);
}
catch
{
return Vector2.zero;
}
bool apply = scrollExt.apply;
if (apply)
{
scrollPosition = scrollExt.scrollPosition;
scrollExt.apply = false;
}
scrollExt.position = position;
scrollExt.scrollPosition = scrollPosition;
scrollExt.visibleRect = scrollExt.viewRect = viewRect;
var rect = scrollExt.visibleRect;
rect.width = position.width;
rect.height = position.height;
ScrollStack.Push(scrollExt);
Rect screenRect = new Rect(position.x, position.y, position.width, position.height);
EventType type = Event.current.type;
if (type != EventType.Layout)
{
if (type != EventType.Used)
{
bool flag = alwaysShowVertical;
bool flag2 = alwaysShowHorizontal;
if (flag2 || viewRect.width > screenRect.width)
{
rect.height = position.height - horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
screenRect.height -= horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
flag2 = true;
}
if (flag || viewRect.height > screenRect.height)
{
rect.width = position.width - verticalScrollbar.fixedWidth + (float)verticalScrollbar.margin.left;
screenRect.width -= verticalScrollbar.fixedWidth + (float)verticalScrollbar.margin.left;
flag = true;
if (!flag2 && viewRect.width > screenRect.width)
{
rect.height = position.height - horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
screenRect.height -= horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
flag2 = true;
}
}
if (Event.current.type == EventType.Repaint && background != GUIStyle.none)
{
background.Draw(position, position.Contains(Event.current.mousePosition), false, flag2 && flag, false);
}
if (flag2 && horizontalScrollbar != GUIStyle.none)
{
scrollPosition.x = HorizontalScroll(
new Rect(
position.x,
position.yMax - horizontalScrollbar.fixedHeight,
screenRect.width,
horizontalScrollbar.fixedHeight),
scrollPosition.x,
Mathf.Min(screenRect.width, viewRect.width),
0f,
viewRect.width,
horizontalScrollbar
);
}
else
{
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
scrollPosition.x = ((horizontalScrollbar == GUIStyle.none)
? Mathf.Clamp(scrollPosition.x, 0f, Mathf.Max(viewRect.width - position.width, 0f))
: 0f);
}
if (flag && verticalScrollbar != GUIStyle.none)
{
scrollPosition.y = VerticalScroll(
new Rect(
screenRect.xMax + (float)verticalScrollbar.margin.left,
screenRect.y,
verticalScrollbar.fixedWidth,
screenRect.height),
scrollPosition.y,
Mathf.Min(screenRect.height, viewRect.height),
0f,
viewRect.height,
verticalScrollbar
);
}
else
{
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
scrollPosition.y = ((verticalScrollbar == GUIStyle.none)
? Mathf.Clamp(scrollPosition.y, 0f, Mathf.Max(viewRect.height - position.height, 0f))
: 0f);
}
}
}
else
{
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
}
GUIClip.Push(screenRect,
new Vector2(
Mathf.Round(-scrollPosition.x - viewRect.x),
Mathf.Round(-scrollPosition.y - viewRect.y)),
Vector2.zero,
false
);
return scrollPosition;
}
public static float HorizontalScroll(Rect position, float value, float size, float leftValue, float rightValue, GUIStyle style)
{
return Scroller(position, value, size, leftValue, rightValue, style,
GUI.skin.GetStyle(style.name + "thumb"),
GUI.skin.GetStyle(style.name + "leftbutton"),
GUI.skin.GetStyle(style.name + "rightbutton"),
true);
}
public static float VerticalScroll(Rect position, float value, float size, float topValue, float bottomValue, GUIStyle style)
{
return Scroller(position, value, size, topValue, bottomValue, style,
GUI.skin.GetStyle(style.name + "thumb"),
GUI.skin.GetStyle(style.name + "upbutton"),
GUI.skin.GetStyle(style.name + "downbutton"),
false);
}
private static float Scroller(Rect position, float value, float size, float leftValue, float rightValue, GUIStyle slider,
GUIStyle thumb, GUIStyle leftButton, GUIStyle rightButton, bool horiz)
{
GUIUtility.CheckOnGUI();
int controlID = GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive, position);
Rect position2;
Rect rect;
Rect rect2;
if (horiz)
{
position2 = new Rect(position.x + leftButton.fixedWidth,
position.y,
position.width - leftButton.fixedWidth - rightButton.fixedWidth,
position.height);
rect = new Rect(position.x, position.y, leftButton.fixedWidth, position.height);
rect2 = new Rect(position.xMax - rightButton.fixedWidth, position.y, rightButton.fixedWidth, position.height);
}
else
{
position2 = new Rect(position.x,
position.y + leftButton.fixedHeight,
position.width,
position.height - leftButton.fixedHeight - rightButton.fixedHeight);
rect = new Rect(position.x, position.y, position.width, leftButton.fixedHeight);
rect2 = new Rect(position.x, position.yMax - rightButton.fixedHeight, position.width, rightButton.fixedHeight);
}
value = Slider(position2, value, size, leftValue, rightValue, slider, thumb, horiz, controlID);
bool flag = Event.current.type == EventType.MouseUp;
if (ScrollerRepeatButton(controlID, rect, leftButton))
{
value -= 10f * ((leftValue >= rightValue) ? -1f : 1f);
}
if (ScrollerRepeatButton(controlID, rect2, rightButton))
{
value += 10f * ((leftValue >= rightValue) ? -1f : 1f);
}
if (flag && Event.current.type == EventType.Used)
{
s_ScrollControlId = 0;
}
if (leftValue < rightValue)
{
value = Mathf.Clamp(value, leftValue, rightValue - size);
}
else
{
value = Mathf.Clamp(value, rightValue, leftValue - size);
}
return value;
}
public static float Slider(Rect position, float value, float size, float start, float end, GUIStyle slider,
GUIStyle thumb, bool horiz, int id)
{
if (id == 0)
{
id = GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive, position);
}
var sliderHandler = new SliderHandlerUnstrip(position, value, size, start, end, slider, thumb, horiz, id);
return sliderHandler.Handle();
}
private static bool ScrollerRepeatButton(int scrollerID, Rect rect, GUIStyle style)
{
bool result = false;
if (GUI.DoRepeatButton(rect, GUIContent.none, style, FocusType.Passive))
{
bool flag = s_ScrollControlId != scrollerID;
s_ScrollControlId = scrollerID;
if (flag)
{
result = true;
nextScrollStepTime = DateTime.Now.AddMilliseconds(250.0);
}
else if (DateTime.Now >= nextScrollStepTime)
{
result = true;
nextScrollStepTime = DateTime.Now.AddMilliseconds(30.0);
}
if (Event.current.type == EventType.Repaint)
{
GUI.InternalRepaintEditorWindow();
}
}
return result;
}
#endregion
}
#region Extensions
public static class Extensions
{
public static Rect Unstripped_GetLast(this GUILayoutGroup group)
{
Rect result;
if (group.m_Cursor > 0 && group.m_Cursor <= group.entries.Count)
{
GUILayoutEntry guilayoutEntry = group.entries[group.m_Cursor - 1];
result = guilayoutEntry.rect;
}
else
{
result = GUILayoutEntry.kDummyRect;
}
return result;
}
}
#endregion
}
#endif

View File

@ -0,0 +1,67 @@
#if CPP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Explorer.Helpers;
namespace Explorer.Unstrip.IMGUI
{
public class GUIUtilityUnstrip
{
public static Dictionary<int, object> MonoStateCache = new Dictionary<int, object>();
public static object GetMonoStateObject(Type type, int controlID)
{
if (!MonoStateCache.ContainsKey(controlID))
{
MonoStateCache.Add(controlID, Activator.CreateInstance(type));
}
return MonoStateCache[controlID];
}
public static Dictionary<int, Il2CppSystem.Object> StateCache => m_stateCacheDict ?? GetStateCacheDict();
public static Dictionary<int, Il2CppSystem.Object> m_stateCacheDict;
public static Il2CppSystem.Object GetStateObject(Il2CppSystem.Type type, int controlID)
{
Il2CppSystem.Object obj;
if (StateCache.ContainsKey(controlID))
{
obj = StateCache[controlID];
}
else
{
obj = Il2CppSystem.Activator.CreateInstance(type);
StateCache.Add(controlID, obj);
}
return obj;
}
private static Dictionary<int, Il2CppSystem.Object> GetStateCacheDict()
{
if (m_stateCacheDict == null)
{
try
{
m_stateCacheDict = ReflectionHelpers.GetTypeByName("UnityEngine.GUIStateObjects")
.GetProperty("s_StateCache")
.GetValue(null, null)
as Dictionary<int, Il2CppSystem.Object>;
if (m_stateCacheDict == null) throw new Exception();
}
catch
{
m_stateCacheDict = new Dictionary<int, Il2CppSystem.Object>();
}
}
return m_stateCacheDict;
}
}
}
#endif

View File

@ -1,34 +1,16 @@
using UnityEngine;
#if CPP
using UnityEngine;
namespace Explorer
namespace Explorer.Unstrip.IMGUI
{
public class LayoutUtilityUnstrip
{
#if CPP
public static Rect GetRect(float width, float height) { return DoGetRect(width, width, height, height, GUIStyle.none, null); }
public static Rect GetRect(float width, float height, GUIStyle style) { return DoGetRect(width, width, height, height, style, null); }
public static Rect GetRect(float width, float height, params GUILayoutOption[] options) { return DoGetRect(width, width, height, height, GUIStyle.none, options); }
// Reserve layout space for a rectangle with a fixed content area.
public static Rect GetRect(float width, float height, GUIStyle style, params GUILayoutOption[] options)
{ return DoGetRect(width, width, height, height, style, options); }
public static Rect GetRect(float minWidth, float maxWidth, float minHeight, float maxHeight)
{ return DoGetRect(minWidth, maxWidth, minHeight, maxHeight, GUIStyle.none, null); }
public static Rect GetRect(float minWidth, float maxWidth, float minHeight, float maxHeight, GUIStyle style)
{ return DoGetRect(minWidth, maxWidth, minHeight, maxHeight, style, null); }
public static Rect GetRect(float minWidth, float maxWidth, float minHeight, float maxHeight, params GUILayoutOption[] options)
{ return DoGetRect(minWidth, maxWidth, minHeight, maxHeight, GUIStyle.none, options); }
// Reserve layout space for a flexible rect.
public static Rect GetRect(float minWidth, float maxWidth, float minHeight, float maxHeight, GUIStyle style, params GUILayoutOption[] options)
{ return DoGetRect(minWidth, maxWidth, minHeight, maxHeight, style, options); }
static Rect DoGetRect(float minWidth, float maxWidth, float minHeight, float maxHeight, GUIStyle style, GUILayoutOption[] options)
{
switch (Event.current.type)
{
case EventType.Layout:
GUILayoutUtility.current.topLevel.Add(new GUILayoutEntry(minWidth, maxWidth, minHeight, maxHeight, style, options));
GUILayoutUtility.current.topLevel.Add(new GUILayoutEntry(width, width, height, height, style, options));
return GUILayoutUtility.kDummyRect;
case EventType.Used:
return GUILayoutUtility.kDummyRect;
@ -36,14 +18,14 @@ namespace Explorer
return GUILayoutUtility.current.topLevel.GetNext().rect;
}
}
public static Rect GetRect(GUIContent content, GUIStyle style) { return DoGetRect(content, style, null); }
// Reserve layout space for a rectangle for displaying some contents with a specific style.
public static Rect GetRect(GUIContent content, GUIStyle style, params GUILayoutOption[] options) { return DoGetRect(content, style, options); }
public static Rect GetRect(GUIContent content, GUIStyle style, params GUILayoutOption[] options)
{
return DoGetRect(content, style, options);
}
static Rect DoGetRect(GUIContent content, GUIStyle style, GUILayoutOption[] options)
{
GUIUtility.CheckOnGUI();
switch (Event.current.type)
{
case EventType.Layout:
@ -96,7 +78,7 @@ namespace Explorer
Rect last;
if (type != EventType.Layout && type != EventType.Used)
{
last = GUILayoutUtility.current.topLevel.GetLastUnstripped();
last = GUILayoutUtility.current.topLevel.Unstripped_GetLast();
}
else
{
@ -104,11 +86,6 @@ namespace Explorer
}
return last;
}
#else
public static Rect GetLastRect()
{
return GUILayoutUtility.GetLastRect();
}
#endif
}
}
#endif

View File

@ -0,0 +1,89 @@
#if CPP
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Explorer.Unstrip.IMGUI
{
public class ScrollViewStateUnstrip
{
public Rect position;
public Rect visibleRect;
public Rect viewRect;
public Vector2 scrollPosition;
public bool apply;
public void ScrollTo(Rect pos)
{
this.ScrollTowards(pos, float.PositiveInfinity);
}
public bool ScrollTowards(Rect pos, float maxDelta)
{
Vector2 b = this.ScrollNeeded(pos);
bool result;
if (b.sqrMagnitude < 0.0001f)
{
result = false;
}
else if (maxDelta == 0f)
{
result = true;
}
else
{
if (b.magnitude > maxDelta)
{
b = b.normalized * maxDelta;
}
this.scrollPosition += b;
this.apply = true;
result = true;
}
return result;
}
private Vector2 ScrollNeeded(Rect pos)
{
Rect rect = this.visibleRect;
rect.x += this.scrollPosition.x;
rect.y += this.scrollPosition.y;
float num = pos.width - this.visibleRect.width;
if (num > 0f)
{
pos.width -= num;
pos.x += num * 0.5f;
}
num = pos.height - this.visibleRect.height;
if (num > 0f)
{
pos.height -= num;
pos.y += num * 0.5f;
}
Vector2 zero = Vector2.zero;
if (pos.xMax > rect.xMax)
{
zero.x += pos.xMax - rect.xMax;
}
else if (pos.xMin < rect.xMin)
{
zero.x -= rect.xMin - pos.xMin;
}
if (pos.yMax > rect.yMax)
{
zero.y += pos.yMax - rect.yMax;
}
else if (pos.yMin < rect.yMin)
{
zero.y -= rect.yMin - pos.yMin;
}
Rect rect2 = this.viewRect;
rect2.width = Mathf.Max(rect2.width, this.visibleRect.width);
rect2.height = Mathf.Max(rect2.height, this.visibleRect.height);
zero.x = Mathf.Clamp(zero.x, rect2.xMin - this.scrollPosition.x, rect2.xMax - this.visibleRect.width - this.scrollPosition.x);
zero.y = Mathf.Clamp(zero.y, rect2.yMin - this.scrollPosition.y, rect2.yMax - this.visibleRect.height - this.scrollPosition.y);
return zero;
}
}
}
#endif

View File

@ -0,0 +1,411 @@
#if CPP
using System;
using UnhollowerRuntimeLib;
using UnityEngine;
namespace Explorer.Unstrip.IMGUI
{
public struct SliderHandlerUnstrip
{
public static int ScrollTroughSide
{
get
{
if (!m_getScrollTroughSideFailed)
{
try
{
return GUI.scrollTroughSide;
}
catch
{
m_getScrollTroughSideFailed = true;
}
}
return m_manualScrollTrough;
}
set
{
if (!m_setScrollTroughSideFailed)
{
try
{
GUI.scrollTroughSide = value;
return;
}
catch
{
m_setScrollTroughSideFailed = true;
}
}
m_manualScrollTrough = value;
}
}
private static bool m_getScrollTroughSideFailed;
private static bool m_setScrollTroughSideFailed;
private static int m_manualScrollTrough;
private readonly Rect position;
private readonly float currentValue;
private readonly float size;
private readonly float start;
private readonly float end;
private readonly GUIStyle slider;
private readonly GUIStyle thumb;
private readonly bool horiz;
private readonly int id;
public SliderHandlerUnstrip(Rect position, float currentValue, float size, float start,
float end, GUIStyle slider, GUIStyle thumb, bool horiz, int id)
{
this.position = position;
this.currentValue = currentValue;
this.size = size;
this.start = start;
this.end = end;
this.slider = slider;
this.thumb = thumb;
this.horiz = horiz;
this.id = id;
}
public float Handle()
{
float result;
if (this.slider == null || this.thumb == null)
{
result = this.currentValue;
}
else
{
switch (this.CurrentEventType())
{
case EventType.MouseDown:
return this.OnMouseDown();
case EventType.MouseUp:
return this.OnMouseUp();
case EventType.MouseDrag:
return this.OnMouseDrag();
case EventType.Repaint:
return this.OnRepaint();
}
result = this.currentValue;
}
return result;
}
private float OnMouseDown()
{
float result;
if (!this.position.Contains(this.CurrentEvent().mousePosition) || this.IsEmptySlider())
{
result = this.currentValue;
}
else
{
ScrollTroughSide = 0;
GUIUtility.hotControl = this.id;
this.CurrentEvent().Use();
if (this.ThumbSelectionRect().Contains(this.CurrentEvent().mousePosition))
{
this.StartDraggingWithValue(this.ClampedCurrentValue());
result = this.currentValue;
}
else
{
GUI.changed = true;
if (this.SupportsPageMovements())
{
var state = GetSliderState();
state.isDragging = false;
GUIUnstrip.nextScrollStepTime = DateTime.Now.AddMilliseconds(250.0);
ScrollTroughSide = this.CurrentScrollTroughSide();
result = this.PageMovementValue();
}
else
{
float num = this.ValueForCurrentMousePosition();
this.StartDraggingWithValue(num);
result = this.Clamp(num);
}
}
}
return result;
}
private float OnMouseDrag()
{
float result;
if (GUIUtility.hotControl != this.id)
{
result = this.currentValue;
}
else
{
var state = GetSliderState();
if (!state.isDragging)
{
result = this.currentValue;
}
else
{
GUI.changed = true;
this.CurrentEvent().Use();
float num = this.MousePosition() - state.dragStartPos;
float value = state.dragStartValue + num / this.ValuesPerPixel();
result = this.Clamp(value);
}
}
return result;
}
private float OnMouseUp()
{
if (GUIUtility.hotControl == this.id)
{
this.CurrentEvent().Use();
GUIUtility.hotControl = 0;
}
return this.currentValue;
}
private float OnRepaint()
{
this.slider.Draw(this.position, GUIContent.none, this.id);
if (!this.IsEmptySlider() && this.currentValue >= this.MinValue() && this.currentValue <= this.MaxValue())
{
this.thumb.Draw(this.ThumbRect(), GUIContent.none, this.id);
}
float result;
if (GUIUtility.hotControl != this.id || !this.position.Contains(this.CurrentEvent().mousePosition) || this.IsEmptySlider())
{
result = this.currentValue;
}
else if (this.ThumbRect().Contains(this.CurrentEvent().mousePosition))
{
if (ScrollTroughSide != 0)
{
GUIUtility.hotControl = 0;
}
result = this.currentValue;
}
else
{
GUI.InternalRepaintEditorWindow();
if (DateTime.Now < GUIUnstrip.nextScrollStepTime)
{
result = this.currentValue;
}
else if (this.CurrentScrollTroughSide() != ScrollTroughSide)
{
result = this.currentValue;
}
else
{
GUIUnstrip.nextScrollStepTime = DateTime.Now.AddMilliseconds(30.0);
if (this.SupportsPageMovements())
{
GetSliderState().isDragging = false;
GUI.changed = true;
result = this.PageMovementValue();
}
else
{
result = this.ClampedCurrentValue();
}
}
}
return result;
}
private EventType CurrentEventType()
{
return this.CurrentEvent().GetTypeForControl(this.id);
}
private int CurrentScrollTroughSide()
{
float num = (!this.horiz) ? this.CurrentEvent().mousePosition.y : this.CurrentEvent().mousePosition.x;
float num2 = (!this.horiz) ? this.ThumbRect().y : this.ThumbRect().x;
return (num <= num2) ? -1 : 1;
}
private bool IsEmptySlider()
{
return this.start == this.end;
}
private bool SupportsPageMovements()
{
return this.size != 0f && GUI.usePageScrollbars;
}
private float PageMovementValue()
{
float num = this.currentValue;
int num2 = (this.start <= this.end) ? 1 : -1;
if (this.MousePosition() > this.PageUpMovementBound())
{
num += this.size * (float)num2 * 0.9f;
}
else
{
num -= this.size * (float)num2 * 0.9f;
}
return this.Clamp(num);
}
private float PageUpMovementBound()
{
float result;
if (this.horiz)
{
result = this.ThumbRect().xMax - this.position.x;
}
else
{
result = this.ThumbRect().yMax - this.position.y;
}
return result;
}
private Event CurrentEvent()
{
return Event.current;
}
private float ValueForCurrentMousePosition()
{
float result;
if (this.horiz)
{
result = (this.MousePosition() - this.ThumbRect().width * 0.5f) / this.ValuesPerPixel() + this.start - this.size * 0.5f;
}
else
{
result = (this.MousePosition() - this.ThumbRect().height * 0.5f) / this.ValuesPerPixel() + this.start - this.size * 0.5f;
}
return result;
}
private float Clamp(float value)
{
return Mathf.Clamp(value, this.MinValue(), this.MaxValue());
}
private Rect ThumbSelectionRect()
{
return this.ThumbRect();
}
private void StartDraggingWithValue(float dragStartValue)
{
var state = GetSliderState();
state.dragStartPos = this.MousePosition();
state.dragStartValue = dragStartValue;
state.isDragging = true;
}
private SliderStateUnstrip GetSliderState()
{
return (SliderStateUnstrip)GUIUtilityUnstrip.GetMonoStateObject(typeof(SliderStateUnstrip), this.id);
}
private Rect ThumbRect()
{
return (!this.horiz) ? this.VerticalThumbRect() : this.HorizontalThumbRect();
}
private Rect VerticalThumbRect()
{
float num = this.ValuesPerPixel();
Rect result;
if (this.start < this.end)
{
result = new Rect(this.position.x + (float)this.slider.padding.left, (this.ClampedCurrentValue() - this.start) * num + this.position.y + (float)this.slider.padding.top, this.position.width - (float)this.slider.padding.horizontal, this.size * num + this.ThumbSize());
}
else
{
result = new Rect(this.position.x + (float)this.slider.padding.left, (this.ClampedCurrentValue() + this.size - this.start) * num + this.position.y + (float)this.slider.padding.top, this.position.width - (float)this.slider.padding.horizontal, this.size * -num + this.ThumbSize());
}
return result;
}
private Rect HorizontalThumbRect()
{
float num = this.ValuesPerPixel();
Rect result;
if (this.start < this.end)
{
result = new Rect((this.ClampedCurrentValue() - this.start) * num + this.position.x + (float)this.slider.padding.left, this.position.y + (float)this.slider.padding.top, this.size * num + this.ThumbSize(), this.position.height - (float)this.slider.padding.vertical);
}
else
{
result = new Rect((this.ClampedCurrentValue() + this.size - this.start) * num + this.position.x + (float)this.slider.padding.left, this.position.y, this.size * -num + this.ThumbSize(), this.position.height);
}
return result;
}
private float ClampedCurrentValue()
{
return this.Clamp(this.currentValue);
}
private float MousePosition()
{
float result;
if (this.horiz)
{
result = this.CurrentEvent().mousePosition.x - this.position.x;
}
else
{
result = this.CurrentEvent().mousePosition.y - this.position.y;
}
return result;
}
private float ValuesPerPixel()
{
float result;
if (this.horiz)
{
result = (this.position.width - (float)this.slider.padding.horizontal - this.ThumbSize()) / (this.end - this.start);
}
else
{
result = (this.position.height - (float)this.slider.padding.vertical - this.ThumbSize()) / (this.end - this.start);
}
return result;
}
private float ThumbSize()
{
float result;
if (this.horiz)
{
result = ((this.thumb.fixedWidth == 0f) ? ((float)this.thumb.padding.horizontal) : this.thumb.fixedWidth);
}
else
{
result = ((this.thumb.fixedHeight == 0f) ? ((float)this.thumb.padding.vertical) : this.thumb.fixedHeight);
}
return result;
}
private float MaxValue()
{
return Mathf.Max(this.start, this.end) - this.size;
}
private float MinValue()
{
return Mathf.Min(this.start, this.end);
}
}
}
#endif

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Explorer.Unstrip.IMGUI
{
public class SliderStateUnstrip
{
public float dragStartPos;
public float dragStartValue;
public bool isDragging;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,73 @@
#if CPP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnhollowerBaseLib;
using UnityEngine;
using System.IO;
using Explorer.Helpers;
using System.Runtime.InteropServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace Explorer.Unstrip.ImageConversion
{
public static class ImageConversionUnstrip
{
// byte[] ImageConversion.EncodeToPNG(this Texture2D image);
internal delegate byte[] d_EncodeToPNG(IntPtr tex);
public static byte[] EncodeToPNG(this Texture2D tex)
{
var data = ICallHelper.GetICall<d_EncodeToPNG>("UnityEngine.ImageConversion::EncodeToPNG")
.Invoke(tex.Pointer);
// The Il2Cpp EncodeToPNG() method does return System.Byte[],
// but for some reason it is not recognized or valid.
// Simple fix is iterating into a new array manually.
byte[] safeData = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
safeData[i] = (byte)data[i];
}
return safeData;
}
// bool ImageConversion.LoadImage(this Texture2D tex, byte[] data, bool markNonReadable);
internal delegate bool d_LoadImage(IntPtr tex, IntPtr data, bool markNonReadable);
public static bool LoadImage(this Texture2D tex, byte[] data, bool markNonReadable)
{
var il2cppArray = new Il2CppStructArray<byte>(data.Length);
for (int i = 0; i < data.Length; i++)
{
il2cppArray[i] = data[i];
}
var ret = ICallHelper.GetICall<d_LoadImage>("UnityEngine.ImageConversion::LoadImage")
.Invoke(tex.Pointer, il2cppArray.Pointer, markNonReadable);
return ret;
}
// Helper for LoadImage from filepath
public static bool LoadImage(Texture2D tex, string filePath, bool markNonReadable)
{
if (!File.Exists(filePath))
{
return false;
}
var data = File.ReadAllBytes(filePath);
return tex.LoadImage(data, markNonReadable);
}
}
}
#endif

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Explorer.Helpers;
#if CPP
using UnhollowerBaseLib;
#endif
namespace Explorer.Unstrip.LayerMasks
{
public static class LayerMaskUnstrip
{
#if CPP
internal delegate IntPtr d_LayerToName(int layer);
public static string LayerToName(int layer)
{
var iCall = ICallHelper.GetICall<d_LayerToName>("UnityEngine.LayerMask::LayerToName");
return IL2CPP.Il2CppStringToManaged(iCall.Invoke(layer));
}
#else
public static string LayerToName(int layer) => LayerMask.LayerToName(layer);
#endif
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Explorer.Helpers;
using UnityEngine;
#if CPP
using UnhollowerBaseLib;
#endif
namespace Explorer.Unstrip.Resources
{
public class ResourcesUnstrip
{
#if CPP
internal delegate IntPtr d_FindObjectsOfTypeAll(IntPtr type);
public static UnityEngine.Object[] FindObjectsOfTypeAll(Il2CppSystem.Type type)
{
var arrayPtr = ICallHelper.GetICall<d_FindObjectsOfTypeAll>("UnityEngine.Resources::FindObjectsOfTypeAll")
.Invoke(type.Pointer);
var array = new Il2CppReferenceArray<UnityEngine.Object>(arrayPtr);
var ret = new UnityEngine.Object[array.Length];
for (int i = 0; i < array.Length; i++)
{
ret[i] = array[i];
}
return ret;
}
#else
public static UnityEngine.Object[] FindObjectsOfTypeAll(Type type) => UnityEngine.Resources.FindObjectsOfTypeAll(type);
#endif
}
}

View File

@ -0,0 +1,41 @@
#if CPP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Explorer.Helpers;
using UnhollowerBaseLib;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Explorer.Unstrip.Scenes
{
public class SceneUnstrip
{
//Scene.GetRootGameObjects();
internal delegate void d_GetRootGameObjects(int handle, IntPtr list);
public static GameObject[] GetRootGameObjects(Scene scene)
{
var list = new Il2CppSystem.Collections.Generic.List<GameObject>(GetRootCount(scene));
var iCall = ICallHelper.GetICall<d_GetRootGameObjects>("UnityEngine.SceneManagement.Scene::GetRootGameObjectsInternal");
iCall.Invoke(scene.handle, list.Pointer);
return list.ToArray();
}
//Scene.rootCount;
internal delegate int GetRootCountInternal_delegate(int handle);
public static int GetRootCount(Scene scene)
{
var iCall = ICallHelper.GetICall<GetRootCountInternal_delegate>("UnityEngine.SceneManagement.Scene::GetRootCountInternal");
return iCall.Invoke(scene.handle);
}
}
}
#endif

View File

@ -1,620 +0,0 @@
using System;
using System.Reflection;
using UnityEngine;
#if CPP
using UnityEngineInternal;
using UnhollowerRuntimeLib;
#endif
namespace Explorer
{
public class GUIUnstrip
{
#if CPP
public static int s_ScrollControlId;
public static bool ScrollFailed = false;
public static bool ManualUnstripFailed = false;
private static GenericStack ScrollStack => m_scrollStack ?? GetScrollStack();
private static PropertyInfo m_scrollViewStatesInfo;
private static GenericStack m_scrollStack;
public static DateTime nextScrollStepTime;
private static MethodInfo ScreenToGuiPointMethod;
private static bool m_screenToGuiAttemped;
private static MethodInfo m_bringWindowToFrontMethod;
private static bool m_bringWindowFrontAttempted;
private static GenericStack GetScrollStack()
{
if (m_scrollViewStatesInfo == null)
{
if (typeof(GUI).GetProperty("scrollViewStates", ReflectionHelpers.CommonFlags) is PropertyInfo scrollStatesInfo)
{
m_scrollViewStatesInfo = scrollStatesInfo;
}
else if (typeof(GUI).GetProperty("s_ScrollViewStates", ReflectionHelpers.CommonFlags) is PropertyInfo s_scrollStatesInfo)
{
m_scrollViewStatesInfo = s_scrollStatesInfo;
}
}
if (m_scrollViewStatesInfo?.GetValue(null, null) is GenericStack stack)
{
m_scrollStack = stack;
}
else
{
m_scrollStack = new GenericStack();
}
return m_scrollStack;
}
#endif
public static Rect Window(int id, Rect rect, GUI.WindowFunction windowFunc, string title)
{
#if CPP
return GUI.Window(id, rect, windowFunc, GUIContent.Temp(title), GUI.skin.window);
#else
return GUI.Window(id, rect, windowFunc, title);
#endif
}
public static bool Button(Rect rect, string title)
{
#if CPP
return GUI.Button(rect, GUIContent.Temp(title), GUI.skin.button);
#else
return GUI.Button(rect, title);
#endif
}
public static string TextArea(string text, params GUILayoutOption[] options)
{
#if CPP
return GUILayout.DoTextField(text, -1, true, GUI.skin.textArea, options);
#else
return GUILayout.TextArea(text, options);
#endif
}
public static void BringWindowToFront(int id)
{
#if CPP
if (!m_bringWindowFrontAttempted)
{
m_bringWindowFrontAttempted = true;
m_bringWindowToFrontMethod = typeof(GUI).GetMethod("BringWindowToFront");
}
if (m_bringWindowToFrontMethod == null)
{
throw new Exception("Couldn't get method 'GUIUtility.BringWindowToFront'!");
}
m_bringWindowToFrontMethod.Invoke(null, new object[] { id });
#else
GUI.BringWindowToFront(id);
#endif
}
public static Vector2 ScreenToGUIPoint(Vector2 screenPoint)
{
#if CPP
if (!m_screenToGuiAttemped)
{
m_screenToGuiAttemped = true;
ScreenToGuiPointMethod = typeof(GUIUtility).GetMethod("ScreenToGUIPoint");
}
if (ScreenToGuiPointMethod == null)
{
throw new Exception("Couldn't get method 'GUIUtility.ScreenToGUIPoint'!");
}
return (Vector2)ScreenToGuiPointMethod.Invoke(null, new object[] { screenPoint });
#else
return GUIUtility.ScreenToGUIPoint(screenPoint);
#endif
}
public static void Space(float pixels)
{
#if CPP
if (GUILayoutUtility.current.topLevel.isVertical)
LayoutUtilityUnstrip.GetRect(0, pixels, GUILayoutUtility.spaceStyle, new GUILayoutOption[] { GUILayout.Height(pixels) });
else
LayoutUtilityUnstrip.GetRect(pixels, 0, GUILayoutUtility.spaceStyle, new GUILayoutOption[] { GUILayout.Width(pixels) });
if (Event.current.type == EventType.Layout)
{
GUILayoutUtility.current.topLevel.entries[GUILayoutUtility.current.topLevel.entries.Count - 1].consideredForMargin = false;
}
#else
GUILayout.Space(pixels);
#endif
}
// fix for repeatbutton
#if CPP
#if ML
static public bool RepeatButton(Texture image, params GUILayoutOption[] options) { return DoRepeatButton(GUIContent.Temp(image), GUI.skin.button, options); }
static public bool RepeatButton(GUIContent content, params GUILayoutOption[] options) { return DoRepeatButton(content, GUI.skin.button, options); }
static public bool RepeatButton(Texture image, GUIStyle style, params GUILayoutOption[] options) { return DoRepeatButton(GUIContent.Temp(image), style, options); }
// Make a repeating button. The button returns true as long as the user holds down the mouse
#endif
static public bool RepeatButton(string text, params GUILayoutOption[] options) { return DoRepeatButton(GUIContent.Temp(text), GUI.skin.button, options); }
static public bool RepeatButton(GUIContent content, GUIStyle style, params GUILayoutOption[] options) { return DoRepeatButton(content, style, options); }
static bool DoRepeatButton(GUIContent content, GUIStyle style, GUILayoutOption[] options)
{
return GUI.DoRepeatButton(LayoutUtilityUnstrip.GetRect(content, style, options), content, style, FocusType.Passive);
}
#else // mono
public static bool RepeatButton(string text, params GUILayoutOption[] args)
{
return GUILayout.RepeatButton(text, args);
}
#endif
// Fix for BeginArea
#if CPP
#if ML
static public void BeginArea(Rect screenRect) { BeginArea(screenRect, GUIContent.none, GUIStyle.none); }
static public void BeginArea(Rect screenRect, string text) { BeginArea(screenRect, GUIContent.Temp(text), GUIStyle.none); }
static public void BeginArea(Rect screenRect, Texture image) { BeginArea(screenRect, GUIContent.Temp(image), GUIStyle.none); }
static public void BeginArea(Rect screenRect, GUIContent content) { BeginArea(screenRect, content, GUIStyle.none); }
static public void BeginArea(Rect screenRect, string text, GUIStyle style) { BeginArea(screenRect, GUIContent.Temp(text), style); }
static public void BeginArea(Rect screenRect, Texture image, GUIStyle style) { BeginArea(screenRect, GUIContent.Temp(image), style); }
#endif
static public void BeginArea(Rect screenRect, GUIStyle style) { BeginArea(screenRect, GUIContent.none, style); }
static public void BeginArea(Rect screenRect, GUIContent content, GUIStyle style)
{
GUILayoutGroup g = GUILayoutUtility.BeginLayoutArea(style, Il2CppType.Of<GUILayoutGroup>());
if (Event.current.type == EventType.Layout)
{
g.resetCoords = true;
g.minWidth = g.maxWidth = screenRect.width;
g.minHeight = g.maxHeight = screenRect.height;
g.rect = Rect.MinMaxRect(screenRect.xMin, screenRect.yMin, g.rect.xMax, g.rect.yMax);
}
GUI.BeginGroup(g.rect, content, style);
}
#else
public static void BeginArea(Rect rect, GUIStyle skin)
{
GUILayout.BeginArea(rect, skin);
}
#endif
// Close a GUILayout block started with BeginArea
#if CPP
static public void EndArea()
{
if (Event.current.type == EventType.Used)
return;
GUILayoutUtility.current.layoutGroups.Pop();
GUILayoutUtility.current.topLevel = GUILayoutUtility.current.layoutGroups.Peek().TryCast<GUILayoutGroup>();
GUI.EndGroup();
}
#else
public static void EndArea()
{
GUILayout.EndArea();
}
#endif
// Fix for BeginGroup
#if CPP
#if ML
public static void BeginGroup(Rect position) { BeginGroup(position, GUIContent.none, GUIStyle.none); }
public static void BeginGroup(Rect position, string text) { BeginGroup(position, GUIContent.Temp(text), GUIStyle.none); }
public static void BeginGroup(Rect position, Texture image) { BeginGroup(position, GUIContent.Temp(image), GUIStyle.none); }
public static void BeginGroup(Rect position, GUIContent content) { BeginGroup(position, content, GUIStyle.none); }
public static void BeginGroup(Rect position, GUIStyle style) { BeginGroup(position, GUIContent.none, style); }
public static void BeginGroup(Rect position, string text, GUIStyle style) { BeginGroup(position, GUIContent.Temp(text), style); }
public static void BeginGroup(Rect position, Texture image, GUIStyle style) { BeginGroup(position, GUIContent.Temp(image), style); }
#endif
public static void BeginGroup(Rect position, GUIContent content, GUIStyle style) { BeginGroup(position, content, style, Vector2.zero); }
internal static void BeginGroup(Rect position, GUIContent content, GUIStyle style, Vector2 scrollOffset)
{
int id = GUIUtility.GetControlID(GUI.s_BeginGroupHash, FocusType.Passive);
if (content != GUIContent.none || style != GUIStyle.none)
{
switch (Event.current.type)
{
case EventType.Repaint:
style.Draw(position, content, id);
break;
default:
if (position.Contains(Event.current.mousePosition))
GUIUtility.mouseUsed = true;
break;
}
}
GUIClip.Push(position, scrollOffset, Vector2.zero, false);
}
#else
public static void BeginGroup(Rect rect, GUIStyle style)
{
GUI.BeginGroup(rect, style);
}
#endif
#if CPP
public static void EndGroup()
{
GUIClip.Internal_Pop();
}
#else
public static void EndGroup()
{
GUI.EndGroup();
}
#endif
// Fix for BeginScrollView.
#if CPP
public static Vector2 BeginScrollView(Vector2 scroll, params GUILayoutOption[] options)
{
// First, just try normal way, may not have been stripped or was unstripped successfully.
if (!ScrollFailed)
{
try
{
return GUILayout.BeginScrollView(scroll, options);
}
catch
{
ScrollFailed = true;
}
}
// Try manual implementation.
if (!ManualUnstripFailed)
{
try
{
return BeginScrollView_ImplLayout(scroll, false, false, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUI.skin.scrollView, options);
}
catch (Exception e)
{
ExplorerCore.Log("Exception on manual BeginScrollView: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
ManualUnstripFailed = true;
}
}
// Sorry! No scrolling for you.
return scroll;
}
public static void EndScrollView(bool handleScrollWheel = true)
{
// Only end the scroll view for the relevant BeginScrollView option, if any.
if (!ScrollFailed)
{
GUILayout.EndScrollView();
}
else if (!ManualUnstripFailed)
{
GUILayoutUtility.EndLayoutGroup();
EndScrollView_Impl(handleScrollWheel);
}
}
private static Vector2 BeginScrollView_ImplLayout(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical,
GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options)
{
var guiscrollGroup = GUILayoutUtility.BeginLayoutGroup(background, null, Il2CppType.Of<GUIScrollGroup>())
.TryCast<GUIScrollGroup>();
EventType type = Event.current.type;
if (type == EventType.Layout)
{
guiscrollGroup.resetCoords = true;
guiscrollGroup.isVertical = true;
guiscrollGroup.stretchWidth = 1;
guiscrollGroup.stretchHeight = 1;
guiscrollGroup.verticalScrollbar = verticalScrollbar;
guiscrollGroup.horizontalScrollbar = horizontalScrollbar;
guiscrollGroup.needsVerticalScrollbar = alwaysShowVertical;
guiscrollGroup.needsHorizontalScrollbar = alwaysShowHorizontal;
guiscrollGroup.ApplyOptions(options);
}
return BeginScrollView_Impl(guiscrollGroup.rect,
scrollPosition,
new Rect(0f, 0f, guiscrollGroup.clientWidth, guiscrollGroup.clientHeight),
alwaysShowHorizontal,
alwaysShowVertical,
horizontalScrollbar,
verticalScrollbar,
background
);
}
private static Vector2 BeginScrollView_Impl(Rect position, Vector2 scrollPosition, Rect viewRect, bool alwaysShowHorizontal,
bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background)
{
// GUIUtility.CheckOnGUI();
int controlID = GUIUtility.GetControlID(GUI.s_ScrollviewHash, FocusType.Passive);
var scrollViewState = GUIUtility.GetStateObject(Il2CppType.Of<ScrollViewState>(), controlID).TryCast<ScrollViewState>();
var scrollExt = ScrollViewStateUnstrip.FromPointer(scrollViewState.Pointer);
if (scrollExt == null) throw new Exception($"Could not get scrollExt for pointer '{scrollViewState.Pointer}'!");
bool apply = scrollExt.apply;
if (apply)
{
scrollPosition = scrollExt.scrollPosition;
scrollExt.apply = false;
}
scrollExt.position = position;
scrollExt.scrollPosition = scrollPosition;
scrollExt.visibleRect = scrollExt.viewRect = viewRect;
var rect = scrollExt.visibleRect;
rect.width = position.width;
rect.height = position.height;
ScrollStack.Push(scrollViewState);
Rect screenRect = new Rect(position.x, position.y, position.width, position.height);
EventType type = Event.current.type;
if (type != EventType.Layout)
{
if (type != EventType.Used)
{
bool flag = alwaysShowVertical;
bool flag2 = alwaysShowHorizontal;
if (flag2 || viewRect.width > screenRect.width)
{
rect.height = position.height - horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
screenRect.height -= horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
flag2 = true;
}
if (flag || viewRect.height > screenRect.height)
{
rect.width = position.width - verticalScrollbar.fixedWidth + (float)verticalScrollbar.margin.left;
screenRect.width -= verticalScrollbar.fixedWidth + (float)verticalScrollbar.margin.left;
flag = true;
if (!flag2 && viewRect.width > screenRect.width)
{
rect.height = position.height - horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
screenRect.height -= horizontalScrollbar.fixedHeight + (float)horizontalScrollbar.margin.top;
flag2 = true;
}
}
if (Event.current.type == EventType.Repaint && background != GUIStyle.none)
{
background.Draw(position, position.Contains(Event.current.mousePosition), false, flag2 && flag, false);
}
if (flag2 && horizontalScrollbar != GUIStyle.none)
{
scrollPosition.x = HorizBar_Impl(
new Rect(
position.x,
position.yMax - horizontalScrollbar.fixedHeight,
screenRect.width,
horizontalScrollbar.fixedHeight),
scrollPosition.x,
Mathf.Min(screenRect.width, viewRect.width),
0f,
viewRect.width,
horizontalScrollbar
);
}
else
{
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
scrollPosition.x = ((horizontalScrollbar == GUIStyle.none) ? Mathf.Clamp(scrollPosition.x, 0f, Mathf.Max(viewRect.width - position.width, 0f)) : 0f);
}
if (flag && verticalScrollbar != GUIStyle.none)
{
scrollPosition.y = VertBar_Impl(
new Rect(
screenRect.xMax + (float)verticalScrollbar.margin.left,
screenRect.y,
verticalScrollbar.fixedWidth,
screenRect.height),
scrollPosition.y,
Mathf.Min(screenRect.height, viewRect.height),
0f,
viewRect.height,
verticalScrollbar
);
}
else
{
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
scrollPosition.y = ((verticalScrollbar == GUIStyle.none) ? Mathf.Clamp(scrollPosition.y, 0f, Mathf.Max(viewRect.height - position.height, 0f)) : 0f);
}
}
}
else
{
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
GUIUtility.GetControlID(GUI.s_RepeatButtonHash, FocusType.Passive);
}
GUIClip.Push(screenRect, new Vector2(Mathf.Round(-scrollPosition.x - viewRect.x), Mathf.Round(-scrollPosition.y - viewRect.y)), Vector2.zero, false);
return scrollPosition;
}
public static float HorizBar_Impl(Rect position, float value, float size, float leftValue, float rightValue, GUIStyle style)
{
return Scroller_Impl(position, value, size, leftValue, rightValue, style,
GUI.skin.GetStyle(style.name + "thumb"),
GUI.skin.GetStyle(style.name + "leftbutton"),
GUI.skin.GetStyle(style.name + "rightbutton"),
true);
}
public static float VertBar_Impl(Rect position, float value, float size, float topValue, float bottomValue, GUIStyle style)
{
return Scroller_Impl(position, value, size, topValue, bottomValue, style,
GUI.skin.GetStyle(style.name + "thumb"),
GUI.skin.GetStyle(style.name + "upbutton"),
GUI.skin.GetStyle(style.name + "downbutton"),
false);
}
private static void EndScrollView_Impl(bool handleScrollWheel)
{
GUIUtility.CheckOnGUI();
if (ScrollStack.Count <= 0) return;
var state = ScrollStack.Peek().TryCast<ScrollViewState>();
var scrollExt = ScrollViewStateUnstrip.FromPointer(state.Pointer);
if (scrollExt == null) throw new Exception("Could not get scrollExt!");
GUIClip.Pop();
ScrollStack.Pop();
var position = scrollExt.position;
if (handleScrollWheel && Event.current.type == EventType.ScrollWheel && position.Contains(Event.current.mousePosition))
{
var pos = scrollExt.scrollPosition;
pos.x = Mathf.Clamp(scrollExt.scrollPosition.x + Event.current.delta.x * 20f, 0f, scrollExt.viewRect.width - scrollExt.visibleRect.width);
pos.y = Mathf.Clamp(scrollExt.scrollPosition.y + Event.current.delta.y * 20f, 0f, scrollExt.viewRect.height - scrollExt.visibleRect.height);
if (scrollExt.scrollPosition.x < 0f)
{
pos.x = 0f;
}
if (pos.y < 0f)
{
pos.y = 0f;
}
scrollExt.apply = true;
Event.current.Use();
}
}
private static float Scroller_Impl(Rect position, float value, float size, float leftValue, float rightValue, GUIStyle slider, GUIStyle thumb, GUIStyle leftButton, GUIStyle rightButton, bool horiz)
{
GUIUtility.CheckOnGUI();
int controlID = GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive, position);
Rect position2;
Rect rect;
Rect rect2;
if (horiz)
{
position2 = new Rect(position.x + leftButton.fixedWidth, position.y, position.width - leftButton.fixedWidth - rightButton.fixedWidth, position.height);
rect = new Rect(position.x, position.y, leftButton.fixedWidth, position.height);
rect2 = new Rect(position.xMax - rightButton.fixedWidth, position.y, rightButton.fixedWidth, position.height);
}
else
{
position2 = new Rect(position.x, position.y + leftButton.fixedHeight, position.width, position.height - leftButton.fixedHeight - rightButton.fixedHeight);
rect = new Rect(position.x, position.y, position.width, leftButton.fixedHeight);
rect2 = new Rect(position.x, position.yMax - rightButton.fixedHeight, position.width, rightButton.fixedHeight);
}
value = Slider_Impl(position2, value, size, leftValue, rightValue, slider, thumb, horiz, controlID);
bool flag = Event.current.type == EventType.MouseUp;
if (ScrollerRepeatButton_Impl(controlID, rect, leftButton))
{
value -= 10f * ((leftValue >= rightValue) ? -1f : 1f);
}
if (ScrollerRepeatButton_Impl(controlID, rect2, rightButton))
{
value += 10f * ((leftValue >= rightValue) ? -1f : 1f);
}
if (flag && Event.current.type == EventType.Used)
{
s_ScrollControlId = 0;
}
if (leftValue < rightValue)
{
value = Mathf.Clamp(value, leftValue, rightValue - size);
}
else
{
value = Mathf.Clamp(value, rightValue, leftValue - size);
}
return value;
}
public static float Slider_Impl(Rect position, float value, float size, float start, float end, GUIStyle slider, GUIStyle thumb, bool horiz, int id)
{
if (id == 0)
{
id = GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive, position);
}
var sliderHandler = new SliderHandlerUnstrip(position, value, size, start, end, slider, thumb, horiz, id);
return sliderHandler.Handle();
}
private static bool ScrollerRepeatButton_Impl(int scrollerID, Rect rect, GUIStyle style)
{
bool result = false;
if (GUI.DoRepeatButton(rect, GUIContent.none, style, FocusType.Passive))
{
bool flag = s_ScrollControlId != scrollerID;
s_ScrollControlId = scrollerID;
if (flag)
{
result = true;
nextScrollStepTime = DateTime.Now.AddMilliseconds(250.0);
}
else if (DateTime.Now >= nextScrollStepTime)
{
result = true;
nextScrollStepTime = DateTime.Now.AddMilliseconds(30.0);
}
if (Event.current.type == EventType.Repaint)
{
GUI.InternalRepaintEditorWindow();
}
}
return result;
}
#else
public static Vector2 BeginScrollView(Vector2 scroll, params GUILayoutOption[] options)
{
return GUILayout.BeginScrollView(scroll, options);
}
public static void EndScrollView()
{
GUILayout.EndScrollView();
}
#endif
}
}

View File

@ -1,30 +0,0 @@
#if CPP
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Explorer
{
public class ScrollViewStateUnstrip
{
public Rect position;
public Rect visibleRect;
public Rect viewRect;
public Vector2 scrollPosition;
public bool apply;
public static Dictionary<IntPtr, ScrollViewStateUnstrip> Dict = new Dictionary<IntPtr, ScrollViewStateUnstrip>();
public static ScrollViewStateUnstrip FromPointer(IntPtr ptr)
{
if (!Dict.ContainsKey(ptr))
{
Dict.Add(ptr, new ScrollViewStateUnstrip());
}
return Dict[ptr];
}
}
}
#endif

View File

@ -1,369 +0,0 @@
#if CPP
using System;
using UnhollowerRuntimeLib;
using UnityEngine;
namespace Explorer
{
public struct SliderHandlerUnstrip
{
private readonly Rect position;
private readonly float currentValue;
private readonly float size;
private readonly float start;
private readonly float end;
private readonly GUIStyle slider;
private readonly GUIStyle thumb;
private readonly bool horiz;
private readonly int id;
public SliderHandlerUnstrip(Rect position, float currentValue, float size, float start, float end, GUIStyle slider, GUIStyle thumb, bool horiz, int id)
{
this.position = position;
this.currentValue = currentValue;
this.size = size;
this.start = start;
this.end = end;
this.slider = slider;
this.thumb = thumb;
this.horiz = horiz;
this.id = id;
}
public float Handle()
{
float result;
if (this.slider == null || this.thumb == null)
{
result = this.currentValue;
}
else
{
switch (this.CurrentEventType())
{
case EventType.MouseDown:
return this.OnMouseDown();
case EventType.MouseUp:
return this.OnMouseUp();
case EventType.MouseDrag:
return this.OnMouseDrag();
case EventType.Repaint:
return this.OnRepaint();
}
result = this.currentValue;
}
return result;
}
private float OnMouseDown()
{
float result;
if (!this.position.Contains(this.CurrentEvent().mousePosition) || this.IsEmptySlider())
{
result = this.currentValue;
}
else
{
GUI.scrollTroughSide = 0;
GUIUtility.hotControl = this.id;
this.CurrentEvent().Use();
if (this.ThumbSelectionRect().Contains(this.CurrentEvent().mousePosition))
{
this.StartDraggingWithValue(this.ClampedCurrentValue());
result = this.currentValue;
}
else
{
GUI.changed = true;
if (this.SupportsPageMovements())
{
this.SliderState().isDragging = false;
GUIUnstrip.nextScrollStepTime = DateTime.Now.AddMilliseconds(250.0);
GUI.scrollTroughSide = this.CurrentScrollTroughSide();
result = this.PageMovementValue();
}
else
{
float num = this.ValueForCurrentMousePosition();
this.StartDraggingWithValue(num);
result = this.Clamp(num);
}
}
}
return result;
}
private float OnMouseDrag()
{
float result;
if (GUIUtility.hotControl != this.id)
{
result = this.currentValue;
}
else
{
SliderState sliderState = this.SliderState();
if (!sliderState.isDragging)
{
result = this.currentValue;
}
else
{
GUI.changed = true;
this.CurrentEvent().Use();
float num = this.MousePosition() - sliderState.dragStartPos;
float value = sliderState.dragStartValue + num / this.ValuesPerPixel();
result = this.Clamp(value);
}
}
return result;
}
private float OnMouseUp()
{
if (GUIUtility.hotControl == this.id)
{
this.CurrentEvent().Use();
GUIUtility.hotControl = 0;
}
return this.currentValue;
}
private float OnRepaint()
{
this.slider.Draw(this.position, GUIContent.none, this.id);
if (!this.IsEmptySlider() && this.currentValue >= this.MinValue() && this.currentValue <= this.MaxValue())
{
this.thumb.Draw(this.ThumbRect(), GUIContent.none, this.id);
}
float result;
if (GUIUtility.hotControl != this.id || !this.position.Contains(this.CurrentEvent().mousePosition) || this.IsEmptySlider())
{
result = this.currentValue;
}
else if (this.ThumbRect().Contains(this.CurrentEvent().mousePosition))
{
if (GUI.scrollTroughSide != 0)
{
GUIUtility.hotControl = 0;
}
result = this.currentValue;
}
else
{
GUI.InternalRepaintEditorWindow();
if (DateTime.Now < GUIUnstrip.nextScrollStepTime)
{
result = this.currentValue;
}
else if (this.CurrentScrollTroughSide() != GUI.scrollTroughSide)
{
result = this.currentValue;
}
else
{
GUIUnstrip.nextScrollStepTime = DateTime.Now.AddMilliseconds(30.0);
if (this.SupportsPageMovements())
{
this.SliderState().isDragging = false;
GUI.changed = true;
result = this.PageMovementValue();
}
else
{
result = this.ClampedCurrentValue();
}
}
}
return result;
}
private EventType CurrentEventType()
{
return this.CurrentEvent().GetTypeForControl(this.id);
}
private int CurrentScrollTroughSide()
{
float num = (!this.horiz) ? this.CurrentEvent().mousePosition.y : this.CurrentEvent().mousePosition.x;
float num2 = (!this.horiz) ? this.ThumbRect().y : this.ThumbRect().x;
return (num <= num2) ? -1 : 1;
}
private bool IsEmptySlider()
{
return this.start == this.end;
}
private bool SupportsPageMovements()
{
return this.size != 0f && GUI.usePageScrollbars;
}
private float PageMovementValue()
{
float num = this.currentValue;
int num2 = (this.start <= this.end) ? 1 : -1;
if (this.MousePosition() > this.PageUpMovementBound())
{
num += this.size * (float)num2 * 0.9f;
}
else
{
num -= this.size * (float)num2 * 0.9f;
}
return this.Clamp(num);
}
private float PageUpMovementBound()
{
float result;
if (this.horiz)
{
result = this.ThumbRect().xMax - this.position.x;
}
else
{
result = this.ThumbRect().yMax - this.position.y;
}
return result;
}
private Event CurrentEvent()
{
return Event.current;
}
private float ValueForCurrentMousePosition()
{
float result;
if (this.horiz)
{
result = (this.MousePosition() - this.ThumbRect().width * 0.5f) / this.ValuesPerPixel() + this.start - this.size * 0.5f;
}
else
{
result = (this.MousePosition() - this.ThumbRect().height * 0.5f) / this.ValuesPerPixel() + this.start - this.size * 0.5f;
}
return result;
}
private float Clamp(float value)
{
return Mathf.Clamp(value, this.MinValue(), this.MaxValue());
}
private Rect ThumbSelectionRect()
{
return this.ThumbRect();
}
private void StartDraggingWithValue(float dragStartValue)
{
SliderState sliderState = this.SliderState();
sliderState.dragStartPos = this.MousePosition();
sliderState.dragStartValue = dragStartValue;
sliderState.isDragging = true;
}
private SliderState SliderState()
{
return (SliderState)GUIUtility.GetStateObject(Il2CppType.Of<SliderState>(), this.id).TryCast<SliderState>();
}
private Rect ThumbRect()
{
return (!this.horiz) ? this.VerticalThumbRect() : this.HorizontalThumbRect();
}
private Rect VerticalThumbRect()
{
float num = this.ValuesPerPixel();
Rect result;
if (this.start < this.end)
{
result = new Rect(this.position.x + (float)this.slider.padding.left, (this.ClampedCurrentValue() - this.start) * num + this.position.y + (float)this.slider.padding.top, this.position.width - (float)this.slider.padding.horizontal, this.size * num + this.ThumbSize());
}
else
{
result = new Rect(this.position.x + (float)this.slider.padding.left, (this.ClampedCurrentValue() + this.size - this.start) * num + this.position.y + (float)this.slider.padding.top, this.position.width - (float)this.slider.padding.horizontal, this.size * -num + this.ThumbSize());
}
return result;
}
private Rect HorizontalThumbRect()
{
float num = this.ValuesPerPixel();
Rect result;
if (this.start < this.end)
{
result = new Rect((this.ClampedCurrentValue() - this.start) * num + this.position.x + (float)this.slider.padding.left, this.position.y + (float)this.slider.padding.top, this.size * num + this.ThumbSize(), this.position.height - (float)this.slider.padding.vertical);
}
else
{
result = new Rect((this.ClampedCurrentValue() + this.size - this.start) * num + this.position.x + (float)this.slider.padding.left, this.position.y, this.size * -num + this.ThumbSize(), this.position.height);
}
return result;
}
private float ClampedCurrentValue()
{
return this.Clamp(this.currentValue);
}
private float MousePosition()
{
float result;
if (this.horiz)
{
result = this.CurrentEvent().mousePosition.x - this.position.x;
}
else
{
result = this.CurrentEvent().mousePosition.y - this.position.y;
}
return result;
}
private float ValuesPerPixel()
{
float result;
if (this.horiz)
{
result = (this.position.width - (float)this.slider.padding.horizontal - this.ThumbSize()) / (this.end - this.start);
}
else
{
result = (this.position.height - (float)this.slider.padding.vertical - this.ThumbSize()) / (this.end - this.start);
}
return result;
}
private float ThumbSize()
{
float result;
if (this.horiz)
{
result = ((this.thumb.fixedWidth == 0f) ? ((float)this.thumb.padding.horizontal) : this.thumb.fixedWidth);
}
else
{
result = ((this.thumb.fixedHeight == 0f) ? ((float)this.thumb.padding.vertical) : this.thumb.fixedHeight);
}
return result;
}
private float MaxValue()
{
return Mathf.Max(this.start, this.end) - this.size;
}
private float MinValue()
{
return Mathf.Min(this.start, this.end);
}
}
}
#endif

View File

@ -1,24 +0,0 @@
#if CPP
using UnityEngine;
namespace Explorer
{
public static class UnstripExtensions
{
public static Rect GetLastUnstripped(this GUILayoutGroup group)
{
Rect result;
if (group.m_Cursor > 0 && group.m_Cursor <= group.entries.Count)
{
GUILayoutEntry guilayoutEntry = group.entries[group.m_Cursor - 1];
result = guilayoutEntry.rect;
}
else
{
result = GUILayoutEntry.kDummyRect;
}
return result;
}
}
}
#endif

4
src/packages.config Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ILRepack.Lib.MSBuild.Task" version="2.0.18.1" targetFramework="net472" />
</packages>