Compare commits

..

34 Commits

Author SHA1 Message Date
7c85969085 Update ExplorerCore.cs 2020-10-24 15:46:11 +11:00
1fce3465c2 Fix bug in ForceUnlockCursor, fix mistake in Reflection Inspector, reduced amount casting with Reflection Inspector 2020-10-24 15:45:37 +11:00
8949e3dc7d Revert "some early steps remaking the GUI with UnityEngine.UI, working in all tested game so far"
This reverts commit 4280a071f6.
2020-10-23 01:48:18 +11:00
4280a071f6 some early steps remaking the GUI with UnityEngine.UI, working in all tested game so far 2020-10-23 01:48:00 +11:00
48ed78ec36 A few small fixes 2020-10-22 21:00:33 +11:00
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
97 changed files with 6765 additions and 3546 deletions

133
README.md
View File

@ -12,117 +12,82 @@
<img src="https://img.shields.io/github/downloads/sinai-dev/Explorer/total.svg" />
</p>
- [Releases](#releases)
- [Features](#features)
- [How to install](#how-to-install)
- [Mod Config](#mod-config)
- [Mouse Control](#mouse-control)
- [Building](#building)
- [Credits](#credits)
## Releases
| Mod Loader | Il2Cpp | Mono |
| ----------- | ------ | ---- |
| [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>
* 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>
- [Current status](#current-status)
- [How to install](#how-to-install)
- [How to use](#how-to-use)
- [Mod Config](#mod-config)
- [Features](#features)
- [Mouse Control](#mouse-control)
- [Building](#building)
- [Credits](#credits)
## Current status
| Mod Loader | Il2Cpp | Mono | Mono (.NET 3.5) |
| ----------- | ------ | ---- | ----|
| [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) | ✔️ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.MelonLoader.Mono.NET35.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) | ✔️ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.BepInEx.Mono.NET35.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).
* 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.
* <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 above.
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 above.
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` and `mcs.dll` should be directly in the `plugins\` folder.
3. Make sure it's not in a sub-folder, `Explorer.dll` should be directly in the `plugins\` folder.
## How to use
* 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.
### Mod Config
## Mod Config
There is a simple Mod Config for the Explorer. You can access the settings via the "Options" page of the main menu.
`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>`
`Default Items per Page` (Int)
`Default Items per Page` (int) | Default: `20`
* Sets the default items per page when viewing lists or search results.
* Default: `20`
## Features
`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)
@ -133,8 +98,9 @@ For example:
using Explorer;
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()
@ -147,7 +113,7 @@ public class MenuClass_CursorUpdate
## Building
If you'd like to build this yourself, everything you need (other than MelonLoader and/or BepInEx) is included with this repository.
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 or BepInEx for your game.
2. Open the `src\Explorer.csproj` file in a text editor.
@ -155,6 +121,7 @@ If you'd like to build this yourself, everything you need (other than MelonLoade
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> 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[] { GUIHelper.ExpandWidth(false) });
this.m_argumentInput[i] = GUIHelper.TextField(input, new GUILayoutOption[] { GUIHelper.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 = CacheFactory.GetTypeAndCacheObject(ret);
//m_cachedReturnValue = CacheFactory.GetTypeAndCacheObject(ret);
m_cachedReturnValue = CacheFactory.GetCacheObject(ret);
m_cachedReturnValue.UpdateValue();
}
else
@ -91,11 +101,11 @@ namespace Explorer
{
foreach (var constraint in GenericConstraints[i].Where(x => x != null))
{
if (!constraint.IsAssignableFrom(t))
{
if (!constraint.IsAssignableFrom(t))
{
ExplorerCore.LogWarning($"Generic argument #{i}, '{input}' is not assignable from the constraint '{constraint}'!");
return null;
}
}
}
list.Add(t);
@ -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,169 +0,0 @@
using System;
using System.Reflection;
using UnityEngine;
namespace Explorer
{
public static class CacheFactory
{
public static CacheObjectBase GetTypeAndCacheObject(object obj)
=> GetTypeAndCacheObject(obj, null, null);
public static CacheObjectBase GetTypeAndCacheObject(MemberInfo memberInfo, object declarer)
=> GetTypeAndCacheObject(null, memberInfo, declarer);
public static CacheObjectBase GetTypeAndCacheObject(object obj, MemberInfo memberInfo, object declarer)
{
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 GetCacheObject(obj, memberInfo, declarer, type);
}
public static CacheObjectBase GetCacheObject(object obj, Type valueType)
=> GetCacheObject(obj, null, null, valueType);
private static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
{
CacheObjectBase cached;
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)
{
cached = new CacheMethod();
}
else if (valueType == typeof(GameObject) || valueType == typeof(Transform))
{
cached = new CacheGameObject();
}
else if (valueType.IsPrimitive || valueType == typeof(string))
{
cached = new CachePrimitive();
}
else if (valueType.IsEnum)
{
if (valueType.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] attributes && attributes.Length > 0)
{
cached = new CacheEnumFlags();
}
else
{
cached = new CacheEnum();
}
}
else if (valueType == typeof(Vector2) || valueType == typeof(Vector3) || valueType == typeof(Vector4))
{
cached = new CacheVector();
}
else if (valueType == typeof(Quaternion))
{
cached = new CacheQuaternion();
}
else if (valueType == typeof(Color))
{
cached = new CacheColor();
}
else if (valueType == typeof(Rect))
{
cached = new CacheRect();
}
// must check this before IsEnumerable
else if (ReflectionHelpers.IsDictionary(valueType))
{
cached = new CacheDictionary();
}
else if (ReflectionHelpers.IsEnumerable(valueType))
{
cached = new CacheList();
}
else
{
cached = new CacheOther();
}
cached.Value = obj;
cached.ValueType = valueType;
if (memberInfo != null)
{
cached.MemInfo = memberInfo;
cached.DeclaringType = memberInfo.DeclaringType;
cached.DeclaringInstance = declaringInstance;
}
if (pi != null)
{
cached.m_arguments = pi.GetIndexParameters();
}
else if (mi != null)
{
cached.m_arguments = mi.GetParameters();
}
cached.m_argumentInput = new string[cached.m_arguments.Length];
cached.UpdateValue();
cached.Init();
return cached;
}
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

@ -1,430 +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 => m_canWrite ?? GetCanWrite();
private bool? m_canWrite;
public virtual void Init() { }
public abstract void DrawValue(Rect window, float width);
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}");
}
}
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))
{
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;
// ========= Gui Draw ==========
public const float MAX_LABEL_WIDTH = 400f;
public const string EVALUATE_LABEL = "<color=lime>Evaluate</color>";
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);
}
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] = GUIUnstrip.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] = GUIUnstrip.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();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(labelWidth);
}
else if (cm != null)
{
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{
cm.Evaluate();
}
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 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 = 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;
if (MemInfo.DeclaringType.IsValueType)
{
classColor = UIStyles.Syntax.StructGreen;
}
else if (MemInfo.DeclaringType.IsAbstract && MemInfo.DeclaringType.IsSealed)
{
classColor = UIStyles.Syntax.Class_Static;
}
else
{
classColor = 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 += ">";
}
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

@ -2,7 +2,7 @@
using System.Xml.Serialization;
using UnityEngine;
namespace Explorer
namespace Explorer.Config
{
public class ModConfig
{
@ -13,9 +13,13 @@ namespace Explorer
[XmlIgnore] public static ModConfig Instance;
public KeyCode Main_Menu_Toggle = KeyCode.F7;
// 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 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()
{
@ -43,7 +47,7 @@ namespace Explorer
Instance = (ModConfig)Serializer.Deserialize(file);
}
}
catch
catch
{
return false;
}

View File

@ -15,7 +15,6 @@
<DefineConstants>CPP,ML</DefineConstants>
<IsCpp>true</IsCpp>
<IsMelonLoader>true</IsMelonLoader>
<IsNet35>false</IsNet35>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
@ -27,14 +26,14 @@
<AssemblyName>Explorer</AssemblyName>
<!-- Set this to the MelonLoader Il2Cpp Game folder, without the ending '\' character. -->
<MLCppGameFolder>D:\Steam\steamapps\common\Hellpoint</MLCppGameFolder>
<!--<MLCppGameFolder>D:\source\Unity Projects\Test\_BUILD</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>
<!-- <BIEMonoGameFolder>D:\source\Unity Projects\! My Unity Games\NewInputSystemTest\_BUILD</BIEMonoGameFolder> -->
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_ML_Cpp|AnyCPU' ">
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
@ -42,24 +41,16 @@
<DefineConstants>CPP,ML</DefineConstants>
<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>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<OutputPath>..\Release\Explorer.MelonLoader.Mono\</OutputPath>
<DefineConstants>MONO,ML</DefineConstants>
<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>
<OutputPath>..\Release\Explorer.MelonLoader.Mono.NET35\</OutputPath>
<DefineConstants>MONO,ML,NET35</DefineConstants>
<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>
@ -67,23 +58,15 @@
<DefineConstants>CPP,BIE</DefineConstants>
<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>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<OutputPath>..\Release\Explorer.BepInEx.Mono\</OutputPath>
<DefineConstants>MONO,BIE</DefineConstants>
<IsCpp>false</IsCpp>
<IsMelonLoader>false</IsMelonLoader>
<IsNet35>false</IsNet35>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Mono_NET35|AnyCPU' ">
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<OutputPath>..\Release\Explorer.BepInEx.Mono.NET35\</OutputPath>
<DefineConstants>MONO,BIE,NET35</DefineConstants>
<IsCpp>false</IsCpp>
<IsMelonLoader>false</IsMelonLoader>
<IsNet35>true</IsNet35>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@ -93,13 +76,9 @@
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<!-- MCS ref -->
<Reference Include="mcs" Condition="'$(IsNet35)'=='false'">
<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>
<Private>False</Private>
</Reference>
</ItemGroup>
<!-- Universal Mono UnityEngine.dll ref (v5.3) -->
@ -222,60 +201,92 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CachedObjects\CacheFactory.cs" />
<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="ExplorerBepInPlugin.cs" />
<Compile Include="ExplorerMelonMod.cs" />
<Compile Include="Extensions\ReflectionExtensions.cs" />
<Compile Include="Input\AbstractInput.cs" />
<Compile Include="Extensions\UnityExtensions.cs" />
<Compile Include="Helpers\ReflectionHelpers.cs" />
<Compile Include="Helpers\UnityHelpers.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="Menu\CursorControl.cs" />
<Compile Include="Menu\MainMenu\Pages\OptionsPage.cs" />
<Compile Include="Tests\TestClass.cs" />
<Compile Include="UnstripFixes\GUIUnstrip.cs" />
<Compile Include="UnstripFixes\Internal_LayoutUtility.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\Internal_ScrollViewState.cs" />
<Compile Include="UnstripFixes\Internal_SliderHandler.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\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="UnstripFixes\Internal.cs" />
<Compile Include="UnstripFixes\Internal_SliderState.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

View File

@ -14,7 +14,7 @@ using BepInEx.IL2CPP;
namespace Explorer
{
[BepInPlugin(ExplorerCore.GUID, ExplorerCore.NAME, ExplorerCore.VERSION)]
[BepInPlugin(ExplorerCore.GUID, "Explorer", ExplorerCore.VERSION)]
#if CPP
public class ExplorerBepInPlugin : BasePlugin
#else
@ -62,24 +62,11 @@ namespace Explorer
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();

View File

@ -1,11 +1,18 @@
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 = "Explorer (" + PLATFORM + ", " + MODLOADER + ")";
public const string VERSION = "1.8.23";
public const string NAME = "Explorer " + VERSION + " (" + PLATFORM + ", " + MODLOADER + ")";
public const string VERSION = "2.0.8";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.explorer";
@ -26,6 +33,12 @@ namespace Explorer
public ExplorerCore()
{
if (Instance != null)
{
Log("An instance of Explorer is already active!");
return;
}
Instance = this;
ModConfig.OnLoad();
@ -34,9 +47,11 @@ namespace Explorer
new WindowManager();
InputManager.Init();
CursorControl.Init();
ForceUnlockCursor.Init();
Log($"{NAME} {VERSION} initialized.");
ShowMenu = true;
Log($"{NAME} initialized.");
}
public static bool ShowMenu
@ -49,7 +64,7 @@ namespace Explorer
private static void SetShowMenu(bool show)
{
m_showMenu = show;
CursorControl.UpdateCursorControl();
ForceUnlockCursor.UpdateCursorControl();
}
public static void Update()
@ -61,7 +76,7 @@ namespace Explorer
if (ShowMenu)
{
CursorControl.Update();
ForceUnlockCursor.Update();
InspectUnderMouse.Update();
MainMenu.Instance.Update();
@ -80,6 +95,11 @@ namespace Explorer
WindowManager.Instance.OnGUI();
InspectUnderMouse.OnGUI();
if (!ResizeDrag.IsMouseInResizeArea && WindowManager.IsMouseInWindow)
{
InputManager.ResetInputAxes();
}
GUI.skin = origSkin;
}
@ -89,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
ExplorerBepInPlugin.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
ExplorerBepInPlugin.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
ExplorerBepInPlugin.Logging?.LogError(message);
ExplorerBepInPlugin.Logging?.LogError(message?.ToString());
#endif
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Explorer.Helpers;
namespace Explorer
{

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

@ -5,48 +5,159 @@ 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;
#if CPP
public static ILType GameObjectType => Il2CppType.Of<GameObject>();
public static ILType TransformType => Il2CppType.Of<Transform>();
public static ILType ObjectType => Il2CppType.Of<UnityEngine.Object>();
public static ILType ComponentType => Il2CppType.Of<Component>();
public static ILType BehaviourType => Il2CppType.Of<Behaviour>();
public static ILType GameObjectType => Il2CppType.Of<GameObject>();
public static ILType TransformType => Il2CppType.Of<Transform>();
public static ILType ObjectType => Il2CppType.Of<UnityEngine.Object>();
public static ILType ComponentType => Il2CppType.Of<Component>();
public static ILType BehaviourType => Il2CppType.Of<Behaviour>();
#else
public static Type GameObjectType => typeof(GameObject);
public static Type TransformType => typeof(Transform);
public static Type ObjectType => typeof(UnityEngine.Object);
public static Type ComponentType => typeof(Component);
public static Type BehaviourType => typeof(Behaviour);
#endif
private static readonly MethodInfo tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast");
private static readonly Dictionary<Type, MethodInfo> cachedTryCastMethods = new Dictionary<Type, MethodInfo>();
#if CPP
private static readonly Dictionary<Type, IntPtr> ClassPointers = new Dictionary<Type, IntPtr>();
public static object Il2CppCast(object obj, Type castTo)
{
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj;
if (!(obj is Il2CppSystem.Object ilObj))
return obj;
if (!cachedTryCastMethods.ContainsKey(castTo))
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo))
return obj;
IntPtr castToPtr;
if (!ClassPointers.ContainsKey(castTo))
{
cachedTryCastMethods.Add(castTo, tryCastMethodInfo.MakeGenericMethod(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];
}
return cachedTryCastMethods[castTo].Invoke(obj, null);
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);
}
#else
public static Type GameObjectType => typeof(GameObject);
public static Type TransformType => typeof(Transform);
public static Type ObjectType => typeof(UnityEngine.Object);
public static Type ComponentType => typeof(Component);
public static Type BehaviourType => typeof(Behaviour);
[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,124 +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)
{
#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 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

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

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);
}
}

View File

@ -2,54 +2,87 @@
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
{
// If no Input modules loaded at all
public static bool NO_INPUT { get; private set; }
// If using new InputSystem module
public static bool USING_NEW_INPUT { get; private set; }
private static AbstractInput inputModule;
private static IAbstractInput m_inputModule;
public static void Init()
{
if (InputSystem.TKeyboard != null || TryLoadModule("Unity.InputSystem", InputSystem.TKeyboard))
if (InputSystem.TKeyboard != null || (ReflectionHelpers.LoadModule("Unity.InputSystem") && InputSystem.TKeyboard != null))
{
USING_NEW_INPUT = true;
inputModule = new InputSystem();
m_inputModule = new InputSystem();
}
else if (LegacyInput.TInput != null || TryLoadModule("UnityEngine.Input", LegacyInput.TInput))
else if (LegacyInput.TInput != null || (ReflectionHelpers.LoadModule("UnityEngine.InputLegacyModule") && LegacyInput.TInput != null))
{
inputModule = new LegacyInput();
m_inputModule = new LegacyInput();
}
if (inputModule == null)
if (m_inputModule == null)
{
ExplorerCore.LogWarning("Could not find any Input module!");
NO_INPUT = true;
}
else
{
inputModule.Init();
m_inputModule = new NoInput();
}
bool TryLoadModule(string dll, Type check) => ReflectionHelpers.LoadModule(dll) && check != null;
m_inputModule.Init();
}
public static Vector3 MousePosition => inputModule?.MousePosition ?? Vector3.zero;
public static Vector3 MousePosition => m_inputModule.MousePosition;
public static bool GetKeyDown(KeyCode key) => inputModule?.GetKeyDown(key) ?? false;
public static bool GetKeyDown(KeyCode key) => m_inputModule.GetKeyDown(key);
public static bool GetKey(KeyCode key) => m_inputModule.GetKey(key);
public static bool GetKey(KeyCode key) => inputModule?.GetKey(key) ?? false;
public static bool GetMouseButtonDown(int btn) => m_inputModule.GetMouseButtonDown(btn);
public static bool GetMouseButton(int btn) => m_inputModule.GetMouseButton(btn);
/// <param name="btn">0 = left, 1 = right, 2 = middle.</param>
public static bool GetMouseButtonDown(int btn) => inputModule?.GetMouseButtonDown(btn) ?? false;
#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
/// <param name="btn">0 = left, 1 = right, 2 = middle.</param>
public static bool GetMouseButton(int btn) => inputModule?.GetMouseButton(btn) ?? false;
#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
}
}
}

View File

@ -4,104 +4,105 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Explorer.Helpers;
namespace Explorer.Input
{
public class InputSystem : AbstractInput
public class InputSystem : IAbstractInput
{
public static Type TKeyboard => _keyboard ?? (_keyboard = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Keyboard"));
private static Type _keyboard;
public static Type TKeyboard => m_tKeyboard ?? (m_tKeyboard = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Keyboard"));
private static Type m_tKeyboard;
public static Type TMouse => _mouse ?? (_mouse = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Mouse"));
private static Type _mouse;
public static Type TMouse => m_tMouse ?? (m_tMouse = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Mouse"));
private static Type m_tMouse;
public static Type TKey => _key ?? (_key = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Key"));
private static Type _key;
public static Type TKey => m_tKey ?? (m_tKey = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Key"));
private static Type m_tKey;
private static PropertyInfo _btnIsPressedProp;
private static PropertyInfo _btnWasPressedProp;
private static PropertyInfo m_btnIsPressedProp;
private static PropertyInfo m_btnWasPressedProp;
private static object CurrentKeyboard => _currentKeyboard ?? (_currentKeyboard = _kbCurrentProp.GetValue(null, null));
private static object _currentKeyboard;
private static PropertyInfo _kbCurrentProp;
private static PropertyInfo _kbIndexer;
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 => _currentMouse ?? (_currentMouse = _mouseCurrentProp.GetValue(null, null));
private static object _currentMouse;
private static PropertyInfo _mouseCurrentProp;
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 => _lmb ?? (_lmb = _leftButtonProp.GetValue(CurrentMouse, null));
private static object _lmb;
private static PropertyInfo _leftButtonProp;
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 => _rmb ?? (_rmb = _rightButtonProp.GetValue(CurrentMouse, null));
private static object _rmb;
private static PropertyInfo _rightButtonProp;
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 => _pos ?? (_pos = _positionProp.GetValue(CurrentMouse, null));
private static object _pos;
private static PropertyInfo _positionProp;
private static MethodInfo _readVector2InputMethod;
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 override Vector2 MousePosition => (Vector2)_readVector2InputMethod.Invoke(MousePositionInfo, new object[0]);
public Vector2 MousePosition => (Vector2)m_readVector2InputMethod.Invoke(MousePositionInfo, new object[0]);
public override bool GetKeyDown(KeyCode key)
public bool GetKeyDown(KeyCode key)
{
var parsedKey = Enum.Parse(TKey, key.ToString());
var actualKey = _kbIndexer.GetValue(CurrentKeyboard, new object[] { parsedKey });
var actualKey = m_kbIndexer.GetValue(CurrentKeyboard, new object[] { parsedKey });
return (bool)_btnWasPressedProp.GetValue(actualKey, null);
return (bool)m_btnWasPressedProp.GetValue(actualKey, null);
}
public override bool GetKey(KeyCode key)
public bool GetKey(KeyCode key)
{
var parsed = Enum.Parse(TKey, key.ToString());
var actualKey = _kbIndexer.GetValue(CurrentKeyboard, new object[] { parsed });
var actualKey = m_kbIndexer.GetValue(CurrentKeyboard, new object[] { parsed });
return (bool)_btnIsPressedProp.GetValue(actualKey, null);
return (bool)m_btnIsPressedProp.GetValue(actualKey, null);
}
public override bool GetMouseButtonDown(int btn)
public bool GetMouseButtonDown(int btn)
{
switch (btn)
{
case 0: return (bool)_btnWasPressedProp.GetValue(LeftMouseButton, null);
case 1: return (bool)_btnWasPressedProp.GetValue(RightMouseButton, null);
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 override bool GetMouseButton(int btn)
public bool GetMouseButton(int btn)
{
switch (btn)
{
case 0: return (bool)_btnIsPressedProp.GetValue(LeftMouseButton, null);
case 1: return (bool)_btnIsPressedProp.GetValue(RightMouseButton, null);
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 override void Init()
public void Init()
{
ExplorerCore.Log("Initializing new InputSystem support...");
_kbCurrentProp = TKeyboard.GetProperty("current");
_kbIndexer = TKeyboard.GetProperty("Item", new Type[] { TKey });
m_kbCurrentProp = TKeyboard.GetProperty("current");
m_kbIndexer = TKeyboard.GetProperty("Item", new Type[] { TKey });
var btnControl = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Controls.ButtonControl");
_btnIsPressedProp = btnControl.GetProperty("isPressed");
_btnWasPressedProp = btnControl.GetProperty("wasPressedThisFrame");
m_btnIsPressedProp = btnControl.GetProperty("isPressed");
m_btnWasPressedProp = btnControl.GetProperty("wasPressedThisFrame");
_mouseCurrentProp = TMouse.GetProperty("current");
_leftButtonProp = TMouse.GetProperty("leftButton");
_rightButtonProp = TMouse.GetProperty("rightButton");
m_mouseCurrentProp = TMouse.GetProperty("current");
m_leftButtonProp = TMouse.GetProperty("leftButton");
m_rightButtonProp = TMouse.GetProperty("rightButton");
_positionProp = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Pointer")
m_positionProp = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.Pointer")
.GetProperty("position");
_readVector2InputMethod = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.InputControl`1")
m_readVector2InputMethod = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.InputControl`1")
.MakeGenericType(typeof(Vector2))
.GetMethod("ReadValue");
}

View File

@ -4,39 +4,40 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Explorer.Helpers;
namespace Explorer.Input
{
public class LegacyInput : AbstractInput
public class LegacyInput : IAbstractInput
{
public static Type TInput => _input ?? (_input = ReflectionHelpers.GetTypeByName("UnityEngine.Input"));
private static Type _input;
public static Type TInput => m_tInput ?? (m_tInput = ReflectionHelpers.GetTypeByName("UnityEngine.Input"));
private static Type m_tInput;
private static PropertyInfo _mousePositionProp;
private static MethodInfo _getKeyMethod;
private static MethodInfo _getKeyDownMethod;
private static MethodInfo _getMouseButtonMethod;
private static MethodInfo _getMouseButtonDownMethod;
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 override Vector2 MousePosition => (Vector3)_mousePositionProp.GetValue(null, null);
public Vector2 MousePosition => (Vector3)m_mousePositionProp.GetValue(null, null);
public override bool GetKey(KeyCode key) => (bool)_getKeyMethod.Invoke(null, new object[] { key });
public bool GetKey(KeyCode key) => (bool)m_getKeyMethod.Invoke(null, new object[] { key });
public override bool GetKeyDown(KeyCode key) => (bool)_getKeyDownMethod.Invoke(null, new object[] { key });
public bool GetKeyDown(KeyCode key) => (bool)m_getKeyDownMethod.Invoke(null, new object[] { key });
public override bool GetMouseButton(int btn) => (bool)_getMouseButtonMethod.Invoke(null, new object[] { btn });
public bool GetMouseButton(int btn) => (bool)m_getMouseButtonMethod.Invoke(null, new object[] { btn });
public override bool GetMouseButtonDown(int btn) => (bool)_getMouseButtonDownMethod.Invoke(null, new object[] { btn });
public bool GetMouseButtonDown(int btn) => (bool)m_getMouseButtonDownMethod.Invoke(null, new object[] { btn });
public override void Init()
public void Init()
{
ExplorerCore.Log("Initializing Legacy Input support...");
_mousePositionProp = TInput.GetProperty("mousePosition");
_getKeyMethod = TInput.GetMethod("GetKey", new Type[] { typeof(KeyCode) });
_getKeyDownMethod = TInput.GetMethod("GetKeyDown", new Type[] { typeof(KeyCode) });
_getMouseButtonMethod = TInput.GetMethod("GetMouseButton", new Type[] { typeof(int) });
_getMouseButtonDownMethod = TInput.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) });
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,253 +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",
#if ML
"MelonLoader"
#endif
};
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() or MelonLogger.Log() it.
"
#if ML
+ @"MelonLogger.Log(""hello world"");";
#else
+ @"Debug.Log(""hello world"");";
#endif
;
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 = GUIUnstrip.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

@ -1,99 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Explorer.Tests;
using UnityEngine;
namespace Explorer
{
public class OptionsPage : WindowPage
{
public override string Name => "Options";
public string toggleKeyInputString = "";
public Vector2 defaultSizeInputVector;
public int defaultPageLimit;
private CacheObjectBase toggleKeyInput;
private CacheObjectBase defaultSizeInput;
private CacheObjectBase defaultPageLimitInput;
public override void Init()
{
toggleKeyInputString = ModConfig.Instance.Main_Menu_Toggle.ToString();
toggleKeyInput = CacheFactory.GetTypeAndCacheObject(typeof(OptionsPage).GetField("toggleKeyInputString"), this);
defaultSizeInputVector = ModConfig.Instance.Default_Window_Size;
defaultSizeInput = CacheFactory.GetTypeAndCacheObject(typeof(OptionsPage).GetField("defaultSizeInputVector"), this);
defaultPageLimit = ModConfig.Instance.Default_Page_Limit;
defaultPageLimitInput = CacheFactory.GetTypeAndCacheObject(typeof(OptionsPage).GetField("defaultPageLimit"), this);
}
public override void Update() { }
public override void DrawWindow()
{
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<color=orange><size=16><b>Settings</b></size></color>", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[0]);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Menu Toggle Key:", new GUILayoutOption[] { GUILayout.Width(215f) });
toggleKeyInput.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Default Window Size:", new GUILayoutOption[] { GUILayout.Width(215f) });
defaultSizeInput.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Default Items per Page:", new GUILayoutOption[] { GUILayout.Width(215f) });
defaultPageLimitInput.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();
//GUIUnstrip.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;
//GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[0]);
//if (GUILayout.Button("Inspect Test Class", new GUILayoutOption[0]))
//{
// WindowManager.InspectObject(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.SaveSettings();
}
}
}

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,77 +1,77 @@
using System;
using UnityEngine;
using Explorer.Helpers;
using BF = System.Reflection.BindingFlags;
#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
m_lastLockMode = Cursor.lockState;
m_lastVisibleState = Cursor.visible;
try
{
//m_lastLockMode = Cursor.lockState;
m_lastLockMode = (CursorLockMode?)typeof(Cursor).GetProperty("lockState", BF.Public | BF.Static)?.GetValue(null, null)
?? CursorLockMode.None;
//m_lastVisibleState = Cursor.visible;
m_lastVisibleState = (bool?)typeof(Cursor).GetProperty("visible", BF.Public | BF.Static)?.GetValue(null, null)
?? false;
}
catch { }
// 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
ExplorerMelonMod.Instance.harmonyInstance;
@ -85,17 +85,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}");
}
}
@ -110,7 +111,7 @@ namespace Explorer
// Check Force-Unlock input
if (InputManager.GetKeyDown(KeyCode.LeftAlt))
{
ForceUnlockMouse = !ForceUnlockMouse;
Unlock = !Unlock;
}
}

View File

@ -1,21 +1,26 @@
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}"
: $"GameObject Inspector ({TargetGO.name})";
? $"<color=cyan>[G]</color> {TargetGO.name}"
: $"GameObject Inspector ({TargetGO.name})";
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))
{
@ -75,8 +82,8 @@ namespace Explorer
}
m_name = TargetGO.name;
m_scene = string.IsNullOrEmpty(TargetGO.scene.name)
? "None (Asset/Resource)"
m_scene = string.IsNullOrEmpty(TargetGO.scene.name)
? "None (Asset/Resource)"
: TargetGO.scene.name;
CacheTransformValues();
@ -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;
}
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,14 +170,17 @@ namespace Explorer
private void DestroyOnException(Exception e)
{
if (pendingDestroy) return;
ExplorerCore.Log($"Exception drawing GameObject Window: {e.GetType()}, {e.Message}");
pendingDestroy = true;
DestroyWindow();
}
private void InspectGameObject(Transform obj)
{
var window = WindowManager.InspectObject(obj, out bool created);
if (created)
{
window.m_rect = new Rect(this.m_rect.x, this.m_rect.y, this.m_rect.width, this.m_rect.height);
@ -189,7 +193,7 @@ namespace Explorer
#else
private void ReflectObject(object obj)
#endif
{
{
var window = WindowManager.InspectObject(obj, out bool created, true);
if (created)
@ -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]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
var width = m_rect.width / 2 - 135f;
m_addComponentInput = GUIUnstrip.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(width) });
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 = GUIUnstrip.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);
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,8 +681,8 @@ namespace Explorer
private Vector3 TranslateControl(TranslateType mode, ref float amount, bool multByTime)
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"<color=cyan><b>{(m_localContext ? "Local " : "")}{mode}</b></color>:",
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"<color=cyan><b>{(m_localContext ? "Local " : "")}{mode}</b></color>:",
new GUILayoutOption[] { GUILayout.Width(m_localContext ? 110 : 65) });
var transform = TargetGO.transform;
@ -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) });
@ -678,11 +714,11 @@ namespace Explorer
PlusMinusFloat(ref input.y, amount, multByTime);
GUILayout.Label("<color=cyan>Z:</color>", new GUILayoutOption[] { GUILayout.Width(20) });
PlusMinusFloat(ref input.z, amount, multByTime);
PlusMinusFloat(ref input.z, amount, multByTime);
GUILayout.Label("+/-:", new GUILayoutOption[] { GUILayout.Width(30) });
var amountInput = amount.ToString("F3");
amountInput = GUIUnstrip.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 = GUIUnstrip.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,12 +1,13 @@
using UnityEngine;
using Explorer.Helpers;
namespace Explorer
namespace Explorer.UI.Inspectors
{
public class InspectUnderMouse
{
public static bool EnableInspect { get; set; } = false;
private static string m_objUnderMouseName = "";
private static string m_objUnderMouseName = "";
public static void Update()
{

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}"
? $"<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>
{
@ -48,49 +59,23 @@ namespace Explorer
public override void Init()
{
TargetType = ReflectionHelpers.GetActualType(Target);
CacheMembers(ReflectionHelpers.GetAllBaseTypes(Target));
// cache the extra cast-caching
#if CPP
if (Target is Il2CppSystem.Object ilObject)
if (!IsStaticInspector)
{
var unityObj = ilObject.TryCast<UnityEngine.Object>();
if (unityObj)
{
m_uObj = unityObj;
var component = ilObject.TryCast<Component>();
if (component)
{
m_component = component;
}
}
TargetType = ReflectionHelpers.GetActualType(Target);
CacheMembers(ReflectionHelpers.GetAllBaseTypes(Target));
}
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)
{
ExplorerCore.Log("Target is null!");
DestroyWindow();
return;
}
if (Target is UnityEngine.Object uObj)
{
if (!uObj)
{
ExplorerCore.Log("Target was destroyed!");
DestroyWindow();
return;
}
}
m_cachedMembersFiltered = m_allCachedMembers.Where(x => ShouldProcessMember(x)).ToArray();
@ -108,18 +93,28 @@ 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;
// hide failed reflection
if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection)
if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection)
return false;
// check scope filter
if (m_scopeFilter == MemberScopes.Instance && holder.IsStatic)
{
return false;
}
else if (m_scopeFilter == MemberScopes.Static && !holder.IsStatic)
{
return false;
}
// see if we should do name search
if (m_search == "" || holder.MemInfo == null)
if (m_search == "" || holder.MemInfo == null)
return true;
// ok do name search
@ -130,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)
{
@ -140,83 +135,83 @@ namespace Explorer
{
infos = declaringType.GetMembers(ReflectionHelpers.CommonFlags);
}
catch
catch
{
ExplorerCore.Log($"Exception getting members for type: {declaringType.FullName}");
continue;
}
object target = Target;
string exception = null;
#if CPP
if (target is Il2CppSystem.Object ilObject)
{
try
{
target = ilObject.Il2CppCast(declaringType);
}
catch (Exception e)
{
exception = ReflectionHelpers.ExceptionToString(e);
}
}
#endif
foreach (var member in infos)
{
// make sure member type is Field, Method of Property (4 / 8 / 16)
int m = (int)member.MemberType;
if (m < 4 || m > 16)
continue;
// check blacklisted members
var sig = $"{member.DeclaringType.Name}.{member.Name}";
if (_typeAndMemberBlacklist.Any(it => it == sig))
continue;
if (_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it)))
continue;
if (member is MethodInfo mi)
{
AppendParams(mi.GetParameters());
}
else if (member is PropertyInfo pi)
{
AppendParams(pi.GetIndexParameters());
}
void AppendParams(ParameterInfo[] _args)
{
sig += " (";
foreach (var param in _args)
{
sig += $"{param.ParameterType.Name} {param.Name}, ";
}
sig += ")";
}
if (cachedSigs.Contains(sig))
{
continue;
}
// ExplorerCore.Log($"Trying to cache member {signature}...");
try
{
var cached = CacheFactory.GetTypeAndCacheObject(member, target);
if (cached != null)
// 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)
{
cachedSigs.Add(sig);
list.Add(cached);
cached.ReflectionException = exception;
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))
continue;
if (_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it)))
continue;
if (mi != null)
{
AppendParams(mi.GetParameters());
}
else if (pi != null)
{
AppendParams(pi.GetIndexParameters());
}
void AppendParams(ParameterInfo[] _args)
{
sig += " (";
foreach (var param in _args)
{
sig += $"{param.ParameterType.Name} {param.Name}, ";
}
sig += ")";
}
if (cachedSigs.Contains(sig))
{
continue;
}
try
{
var cached = CacheFactory.GetCacheObject(member, Target);
if (cached != null)
{
cachedSigs.Add(sig);
list.Add(cached);
}
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {sig}!");
ExplorerCore.Log(e.ToString());
}
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {sig}!");
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
ExplorerCore.Log(e.ToString());
}
}
@ -238,54 +233,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 = GUIUnstrip.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) }))
{
@ -298,12 +288,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();
@ -325,13 +315,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();
@ -340,12 +330,12 @@ namespace Explorer
{
var holder = members[j];
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
try
{
GUIHelper.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
try
{
holder.Draw(rect, 180f);
}
catch
}
catch
{
GUILayout.EndHorizontal();
continue;
@ -358,13 +348,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"))
@ -379,9 +369,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;
}
@ -391,7 +381,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,258 @@
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();
if (label.Length > 100)
{
label = label.Substring(0, 99);
}
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,11 +59,11 @@ 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
var keyList = new List<object>();
var keyList = new List<object>();
var valueList = new List<object>();
// store entries with reflection
@ -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;
@ -147,7 +162,7 @@ namespace Explorer
m_cachedValues = values.ToArray();
}
private bool EnsureDictionaryIsSupported()
public bool EnsureDictionaryIsSupported()
{
if (typeof(IDictionary).IsAssignableFrom(ValueType))
{
@ -166,12 +181,17 @@ 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;
}
}
catch
{
return false;
catch
{
return false;
}
#else
return false;
@ -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,25 +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,22 +1,30 @@
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
public Type EntryType
{
get => GetEntryType();
set => m_entryType = value;
@ -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)
{
m_entryType = memberType.GetGenericArguments()[0];
}
}
else if (Value != null)
if (ValueType.FullName.Contains("ValueCollection"))
{
var type = Value.GetType();
if (type.IsGenericType)
{
m_entryType = type.GetGenericArguments()[0];
}
m_entryType = gArgs[gArgs.Length - 1];
}
else
{
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 (CacheFactory.GetCacheObject(obj, t) is CacheObjectBase cached)
{
list.Add(cached);
}
else
{
list.Add(null);
}
//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);
}
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,10 +307,10 @@ namespace Explorer
if (count > Pages.ItemsPerPage)
{
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.Space(whitespace);
GUIUnstrip.Space(whitespace);
Pages.CurrentPageLabel();
// prev/next page buttons
@ -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]);
}
@ -355,9 +350,9 @@ namespace Explorer
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
entry.DrawValue(window, window.width - (whitespace + 85));
entry.IValue.DrawValue(window, window.width - (whitespace + 85));
}
}
GUI.skin.label.alignment = TextAnchor.UpperLeft;

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 = GUIUnstrip.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 = GUIUnstrip.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 = GUIUnstrip.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 = GUIUnstrip.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,11 +4,15 @@ 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 = new string[0];
@ -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={UIStyles.Syntax.StructGreen}><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,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 CacheEnumFlags : CacheEnum, IExpandHeight
public class InteractiveFlags : InteractiveEnum, IExpandHeight
{
public bool[] m_enabledFlags = new bool[0];
@ -45,7 +47,7 @@ namespace Explorer
public override void DrawValue(Rect window, float width)
{
if (CanWrite)
if (OwnerCacheObject.CanWrite)
{
if (!IsExpanded)
{
@ -73,23 +75,23 @@ 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]);
}
}
@ -105,7 +107,7 @@ namespace Explorer
}
}
Value = Enum.Parse(ValueType, val);
SetValue();
OwnerCacheObject.SetValue();
}
}
}

View File

@ -6,10 +6,13 @@ using UnityEngine;
#if CPP
using UnhollowerRuntimeLib;
#endif
using Explorer.UI.Shared;
using Explorer.CacheObject;
using Explorer.Config;
namespace Explorer
namespace Explorer.UI
{
public class CachePrimitive : CacheObjectBase
public class InteractivePrimitive : InteractiveValue
{
private string m_valueToString;
private bool m_isBool;
@ -61,7 +64,7 @@ namespace Explorer
{
m_valueToString = Value?.ToString();
if (m_canBitwiseOperate)
if (m_canBitwiseOperate && Value != null)
{
var _int = (int)Value;
m_binaryInput = Convert.ToString(_int, toBase: 2);
@ -75,14 +78,15 @@ namespace Explorer
var b = (bool)Value;
var label = $"<color={(b ? "lime" : "red")}>{b}</color>";
if (CanWrite)
if (OwnerCacheObject.CanWrite)
{
b = GUILayout.Toggle(b, label, new GUILayoutOption[0]);
if (b != (bool)Value)
{
Value = b;
SetValue();
}
Value = GUILayout.Toggle(b, label, new GUILayoutOption[] { GUILayout.Width(60) });
DrawApplyButton();
//if (b != (bool)Value)
//{
// Value = b;
// OwnerCacheObject.SetValue();
//}
}
else
{
@ -94,31 +98,26 @@ namespace Explorer
// all other non-bool values use TextField
GUILayout.BeginVertical(new GUILayoutOption[0]);
GUIHelper.BeginVertical(new GUILayoutOption[0]);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<color=#2df7b2><i>" + ValueType.Name + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
m_valueToString = GUIUnstrip.TextArea(m_valueToString, new GUILayoutOption[] { GUILayout.ExpandWidth(true) });
if (CanWrite)
{
if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))
{
SetValueFromInput();
}
}
m_valueToString = GUIHelper.TextArea(m_valueToString, new GUILayoutOption[] { GUIHelper.ExpandWidth(true) });
if (m_canBitwiseOperate)
DrawApplyButton();
if (ModConfig.Instance.Bitwise_Support && m_canBitwiseOperate)
{
m_inBitwiseMode = GUILayout.Toggle(m_inBitwiseMode, "Bitwise?", new GUILayoutOption[0]);
}
GUIUnstrip.Space(10);
GUIHelper.Space(10);
GUILayout.EndHorizontal();
if (m_inBitwiseMode)
if (ModConfig.Instance.Bitwise_Support && m_inBitwiseMode)
{
DrawBitwise();
}
@ -126,11 +125,29 @@ namespace Explorer
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 (CanWrite)
if (OwnerCacheObject.CanWrite)
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("RHS:", new GUILayoutOption[] { GUILayout.Width(35) });
@ -186,15 +203,15 @@ namespace Explorer
}
}
m_bitwiseOperatorInput = GUIUnstrip.TextField(m_bitwiseOperatorInput, new GUILayoutOption[] { GUILayout.Width(55) });
m_bitwiseOperatorInput = GUIHelper.TextField(m_bitwiseOperatorInput, new GUILayoutOption[] { GUILayout.Width(55) });
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"<color=cyan>Binary:</color>", new GUILayoutOption[] { GUILayout.Width(60) });
m_binaryInput = GUIUnstrip.TextField(m_binaryInput, new GUILayoutOption[0]);
if (CanWrite)
m_binaryInput = GUIHelper.TextField(m_binaryInput, new GUILayoutOption[0]);
if (OwnerCacheObject.CanWrite)
{
if (GUILayout.Button("Apply", new GUILayoutOption[0]))
{
@ -206,12 +223,6 @@ namespace Explorer
public void SetValueFromInput()
{
if (MemInfo == null)
{
ExplorerCore.Log("Trying to SetValue but the MemberInfo is null!");
return;
}
if (m_isString)
{
Value = m_valueToString;
@ -228,7 +239,7 @@ namespace Explorer
}
}
SetValue();
OwnerCacheObject.SetValue();
RefreshToString();
}
@ -239,7 +250,7 @@ namespace Explorer
var method = typeof(Convert).GetMethod($"To{ValueType.Name}", new Type[] { typeof(string), typeof(int) });
Value = method.Invoke(null, new object[] { m_binaryInput, 2 });
SetValue();
OwnerCacheObject.SetValue();
RefreshToString();
}
catch (Exception e)

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 = GUIUnstrip.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 = GUIUnstrip.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 = GUIUnstrip.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 = GUIUnstrip.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 = GUIUnstrip.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 = GUIUnstrip.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 = GUIUnstrip.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 = GUIUnstrip.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 = GUIUnstrip.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 = GUIUnstrip.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 = GUIUnstrip.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,10 +3,14 @@ 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;
@ -17,18 +21,16 @@ namespace Explorer
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>();
@ -124,39 +121,23 @@ namespace Explorer
}
else
{
if (!m_getRootObjectsFailed)
for (int i = 0; i < SceneManager.sceneCount; i++)
{
try
var scene = SceneManager.GetSceneAt(i);
if (scene.name == m_currentScene)
{
for (int i = 0; i < SceneManager.sceneCount; i++)
{
var scene = SceneManager.GetSceneAt(i);
var rootObjects =
#if CPP
Unstrip.Scenes.SceneUnstrip.GetRootGameObjects(scene)
.Select(it => it.transform);
#else
scene.GetRootGameObjects().Select(it => it.transform);
#endif
allTransforms.AddRange(rootObjects);
if (scene.name == m_currentScene)
{
allTransforms.AddRange(scene.GetRootGameObjects()
.Select(it => it.transform));
break;
}
}
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());
}
}
@ -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();
@ -240,7 +191,7 @@ namespace Explorer
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) });
@ -250,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 = GUIUnstrip.TextField(m_searchInput, new GUILayoutOption[0]);
m_searchInput = GUIHelper.TextField(m_searchInput, new GUILayoutOption[0]);
if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) }))
{
@ -261,7 +212,7 @@ namespace Explorer
}
GUILayout.EndHorizontal();
GUIUnstrip.Space(5);
GUIHelper.Space(5);
}
private void SceneChangeButtons()
@ -290,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;
m_currentScene = scenes[index].name;
Update_Impl();
}
else if (index < 0)
{
index = scenes.Count - 1;
}
m_currentScene = scenes[index].name;
}
}
}
private void DrawPageButtons()
{
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIHelper.BeginHorizontal(new GUILayoutOption[0]);
Pages.DrawLimitInputArea();
@ -315,7 +263,7 @@ namespace Explorer
{
Pages.TurnPage(Turn.Left, ref this.scroll);
Update_Impl(true);
Update_Impl();
}
Pages.CurrentPageLabel();
@ -324,7 +272,7 @@ namespace Explorer
{
Pages.TurnPage(Turn.Right, ref this.scroll);
Update_Impl(true);
Update_Impl();
}
}
@ -336,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();
@ -347,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)
@ -372,28 +312,28 @@ namespace Explorer
if (obj == null) continue;
if (!obj.RefGameObject)
try
{
string label = "<color=red><i>null";
var go = obj.IValue.Value as GameObject ?? (obj.IValue.Value as Transform)?.gameObject;
if (obj.RefGameObject != null)
if (!go)
{
label += " (Destroyed)";
}
string label = "<color=red><i>null";
label += "</i></color>";
GUILayout.Label(label, new GUILayoutOption[0]);
}
else
{
UIHelpers.GOButton_Impl(obj.RefGameObject,
obj.EnabledColor,
obj.Label,
obj.RefGameObject.activeSelf,
SetTransformTarget,
true,
MainMenu.MainRect.width - 170);
if (go != null)
{
label += " (Destroyed)";
}
label += "</i></color>";
GUILayout.Label(label, new GUILayoutOption[0]);
}
else
{
Buttons.GameObjectButton(go, SetTransformTarget, true, MainMenu.MainRect.width - 170f);
}
}
catch { }
}
}
}
@ -413,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
{
@ -436,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();
@ -41,7 +49,7 @@ namespace Explorer
Custom
}
public override void Init()
public override void Init()
{
Instance = this;
}
@ -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 = CacheFactory.GetTypeAndCacheObject(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.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 = GUIUnstrip.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 = GUIUnstrip.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,24 +126,18 @@ namespace Explorer
#endif
if (TypeMode == TypeFilter.Custom)
{
try
if (ReflectionHelpers.GetTypeByName(m_typeInput) is Type t)
{
if (ReflectionHelpers.GetTypeByName(typeName) is Type t)
{
#if CPP
searchType = Il2CppSystem.Type.GetType(t.AssemblyQualifiedName);
searchType = Il2CppSystem.Type.GetType(t.AssemblyQualifiedName);
#else
searchType = t;
searchType = t;
#endif
}
else
{
throw new Exception($"Could not find a Type by the name of '{typeName}'!");
}
}
catch (Exception e)
else
{
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)
@ -360,7 +212,7 @@ namespace Explorer
#if CPP
if (obj is Il2CppSystem.Object ilObject)
{
go = ilObject.TryCast<GameObject>() ?? ilObject.TryCast<Component>().gameObject;
go = ilObject.TryCast<GameObject>() ?? ilObject.TryCast<Component>().gameObject;
}
#else
if (obj is GameObject || obj is Component)
@ -390,19 +242,17 @@ namespace Explorer
return false;
}
// ====== other ========
private static bool FilterName(string name)
{
// Don't really want these instances.
return !name.StartsWith("Mono")
&& !name.StartsWith("System")
&& !name.StartsWith("Il2CppSystem")
return !name.StartsWith("Mono")
&& !name.StartsWith("System")
&& !name.StartsWith("Il2CppSystem")
&& !name.StartsWith("Iced");
}
// 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
{
@ -32,7 +36,7 @@ namespace Explorer
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 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)
@ -43,7 +47,7 @@ namespace Explorer
return;
}
m_currentPage = index;
GUIUnstrip.BringWindowToFront(MainWindowID);
GUIHelper.BringWindowToFront(MainWindowID);
GUI.FocusWindow(MainWindowID);
}
@ -54,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)
@ -101,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

@ -3,6 +3,6 @@
interface IExpandHeight
{
bool IsExpanded { get; set; }
float WhiteSpace { get; set; }
float WhiteSpace { get; set; }
}
}

View File

@ -1,6 +1,7 @@
using UnityEngine;
using System;
using UnityEngine;
namespace Explorer
namespace Explorer.UI.Shared
{
public enum Turn
{
@ -21,9 +22,9 @@ namespace Explorer
CalculateMaxOffset();
}
}
private int m_itemsPerPage = ModConfig.Instance.Default_Page_Limit;
private int m_itemsPerPage = Config.ModConfig.Instance.Default_Page_Limit;
public int ItemCount
public int ItemCount
{
get => m_count;
set
@ -38,7 +39,7 @@ namespace Explorer
private int CalculateMaxOffset()
{
return MaxPageOffset = (int)Mathf.Ceil((float)(ItemCount / (decimal)ItemsPerPage)) - 1;
return MaxPageOffset = (int)Math.Ceiling((float)(ItemCount / (decimal)ItemsPerPage)) - 1;
}
public void CurrentPageLabel()
@ -61,7 +62,7 @@ namespace Explorer
{
if (direction == Turn.Left)
{
if (PageOffset > 0)
if (PageOffset > 0)
{
PageOffset--;
scroll = Vector2.zero;
@ -94,7 +95,7 @@ namespace Explorer
{
GUILayout.Label("Limit: ", new GUILayoutOption[] { GUILayout.Width(50) });
var limit = this.ItemsPerPage.ToString();
limit = GUIUnstrip.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,57 +4,64 @@ using UnhollowerBaseLib;
#endif
using UnityEngine;
namespace Explorer
namespace Explorer.UI.Shared
{
public class ResizeDrag
{
#if CPP
private static bool RESIZE_FAILED = false;
#endif
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;
public static Rect ResizeWindow(Rect _rect, int ID)
{
#if CPP
if (!RESIZE_FAILED)
{
var origRect = _rect;
try
{
GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUIHelper.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
#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 = Internal_LayoutUtility.GetLastRect();
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) && InputManager.GetMouseButtonDown(0))
var mouse = GUIHelper.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y));
if (resizeDragArea.Contains(mouse))
{
isResizing = true;
m_currentWindow = ID;
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
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 (!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));
@ -62,7 +69,7 @@ namespace Explorer
_rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y
}
}
catch
catch
{
// throw safe Managed exception
throw new Exception("");
@ -82,74 +89,36 @@ 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;
}
#else // mono
GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) });
//var r = GUILayoutUtility.GetLastRect();
var r = GUILayoutUtility.GetLastRect();
var mousePos = InputManager.MousePosition;
var mouse = GUIUnstrip.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y));
if (r.Contains(mouse) && InputManager.GetMouseButtonDown(0))
{
isResizing = true;
m_currentWindow = ID;
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
}
else if (!InputManager.GetMouseButton(0))
{
isResizing = false;
}
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));
_rect.xMax = Mathf.Min(Screen.width, _rect.xMax); // modifying xMax affects width, not x
_rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y
}
GUILayout.EndHorizontal();
#endif
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 = "#92c470";
}
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;
@ -21,7 +23,7 @@ namespace Explorer
public override void Init() { }
public override void Update()
public override void Update()
{
while (TargetTabID >= WindowManager.Windows.Count)
{
@ -35,7 +37,7 @@ namespace Explorer
if (TargetTabID >= 0)
{
m_targetWindow = WindowManager.Windows[TargetTabID];
m_targetWindow = WindowManager.Windows[TargetTabID];
}
else
{
@ -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 { }
m_rect = ResizeDrag.ResizeWindow(m_rect, windowID);
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,19 @@
using System;
using UnityEngine;
using Explorer.Config;
using Explorer.UI.Inspectors;
using Explorer.Helpers;
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,11 +23,19 @@ 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>();
#if CPP
if (target is Il2CppSystem.Object ilObject)
{
target = ilObject.Il2CppCast(ReflectionHelpers.GetActualType(ilObject));
}
#endif
window.Target = target;
window.windowID = WindowManager.NextWindowID();
window.m_rect = WindowManager.GetNewWindowRect();
@ -35,6 +46,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 (InputManager.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,42 +119,28 @@ 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 bool IsMouseInWindow
public static StaticInspector InspectStaticReflection(Type type)
{
get
{
if (!ExplorerCore.ShowMenu)
{
return false;
}
var new_window = WindowBase.CreateWindowStatic(type);
FocusWindow(new_window);
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)
@ -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,183 @@
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 ExpandWidth(bool expand)
{
#if CPP
return GUIUnstrip.ExpandWidth(expand);
#else
return GUIHelper.ExpandWidth(expand);
#endif
}
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

@ -6,22 +6,19 @@ using UnityEngine;
using UnityEngineInternal;
using UnhollowerRuntimeLib;
namespace Explorer.UnstripInternals
namespace Explorer.Unstrip.IMGUI
{
public class Internal
public class GUIUnstrip
{
#region Properties
public static int s_ScrollControlId;
public static bool ScrollFailed = false;
public static bool ManualUnstripFailed = false;
public static bool ManualUnstripFailed = false;
public static GenericStack ScrollStack => m_scrollStack ?? GetScrollStack();
public static PropertyInfo m_scrollViewStatesInfo;
public static GenericStack m_scrollStack;
public static Dictionary<int, Il2CppSystem.Object> StateCache => m_stateCacheDict ?? GetStateCacheDict();
public static Dictionary<int, Il2CppSystem.Object> m_stateCacheDict;
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;
@ -34,53 +31,13 @@ namespace Explorer.UnstripInternals
public static MethodInfo m_bringWindowToFrontMethod;
public static bool m_bringWindowFrontAttempted;
private static GenericStack GetScrollStack()
private static Stack<object> 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();
}
m_scrollStack = new Stack<object>();
return m_scrollStack;
}
private static Dictionary<int, Il2CppSystem.Object> GetStateCacheDict()
{
if (m_stateCacheDict == null)
{
try
{
var type = ReflectionHelpers.GetTypeByName("UnityEngine.GUIStateObjects");
m_stateCacheDict = type.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;
}
private static GUIStyle GetSpaceStyle()
{
try
@ -107,9 +64,72 @@ namespace Explorer.UnstripInternals
#region GUILayout Methods
public static string TextField(string text, GUILayoutOption[] options)
public static GUILayoutOption ExpandWidth(bool expand)
{
text = text ?? "";
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 = BeginLayoutGroup(style, options, Il2CppType.Of<GUILayoutGroup>());
g.isVertical = vertical;
if (style != GUIStyle.none || content != GUIContent.none)
GUI.Box(g.rect, content, style);
}
public static GUILayoutGroup BeginLayoutGroup(GUIStyle style, GUILayoutOption[] options, Il2CppSystem.Type layoutType)
{
EventType type = Event.current.type;
GUILayoutGroup guilayoutGroup;
if (type != EventType.Used && type != EventType.Layout)
{
guilayoutGroup = GUILayoutUtility.current.topLevel.GetNext().TryCast<GUILayoutGroup>();
if (guilayoutGroup == null)
{
throw new ArgumentException("GUILayout: Mismatched LayoutGroup." + Event.current.type);
}
guilayoutGroup.ResetCursor();
}
else
{
guilayoutGroup = GUILayoutUtility.CreateGUILayoutGroupInstanceOfType(layoutType);
guilayoutGroup.style = style;
if (options != null)
{
guilayoutGroup.ApplyOptions(options);
}
GUILayoutUtility.current.topLevel.entries.Add(guilayoutGroup);
}
GUILayoutUtility.current.layoutGroups.Push(guilayoutGroup);
GUILayoutUtility.current.topLevel = guilayoutGroup;
return guilayoutGroup;
}
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);
@ -123,19 +143,19 @@ namespace Explorer.UnstripInternals
guicontent = GUIContent.Temp(text);
// guicontent = GUIContent.Temp(text + GUIUtility.compositionString);
}
Rect rect = Internal_LayoutUtility.GetRect(guicontent, GUI.skin.textField, options);
Rect rect = LayoutUtilityUnstrip.GetRect(guicontent, skin, options);
bool flag2 = GUIUtility.keyboardControl == controlID;
if (flag2)
{
guicontent = GUIContent.Temp(text);
}
DoTextField(rect, controlID, guicontent, false, -1, GUI.skin.textField);
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 (GetStateObject(Il2CppType.Of<TextEditor>(), id).TryCast<TextEditor>() is TextEditor textEditor)
if (GUIUtilityUnstrip.GetMonoStateObject(typeof(TextEditorUnstrip), id) is TextEditorUnstrip textEditor)
{
if (maxLength >= 0 && content.text.Length > maxLength)
{
@ -148,22 +168,149 @@ namespace Explorer.UnstripInternals
textEditor.multiline = multiline;
textEditor.controlID = id;
textEditor.DetectFocusChange();
GUI.HandleTextFieldEventForDesktop(position, id, content, multiline, maxLength, style, textEditor);
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(Internal_LayoutUtility.GetRect(content, style, options), content, style, FocusType.Passive);
return GUI.DoRepeatButton(LayoutUtilityUnstrip.GetRect(content, style, options), content, style, FocusType.Passive);
}
public static void Space(float pixels)
{
if (GUILayoutUtility.current.topLevel.isVertical)
Internal_LayoutUtility.GetRect(0, pixels, SpaceStyle, new GUILayoutOption[] { GUILayout.Height(pixels) });
LayoutUtilityUnstrip.GetRect(0, pixels, SpaceStyle, new GUILayoutOption[] { GUILayout.Height(pixels) });
else
Internal_LayoutUtility.GetRect(pixels, 0, SpaceStyle, new GUILayoutOption[] { GUILayout.Width(pixels) });
LayoutUtilityUnstrip.GetRect(pixels, 0, SpaceStyle, new GUILayoutOption[] { GUILayout.Width(pixels) });
if (Event.current.type == EventType.Layout)
{
@ -226,7 +373,7 @@ namespace Explorer.UnstripInternals
{
guilayoutGroup = (GUILayoutGroup)Activator.CreateInstance(layoutType);
guilayoutGroup.style = style;
GUILayoutUtility.current.windows.Add(guilayoutGroup);
GUILayoutUtility.current.windows.entries.Add(guilayoutGroup);
}
GUILayoutUtility.current.layoutGroups.Push(guilayoutGroup);
GUILayoutUtility.current.topLevel = guilayoutGroup;
@ -267,26 +414,10 @@ namespace Explorer.UnstripInternals
GUI.EndGroup();
}
#endregion
#endregion
#region Scrolling
private 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;
}
public static Vector2 BeginScrollView(Vector2 scroll, params GUILayoutOption[] options)
{
// First, just try normal way, may not have been stripped or was unstripped successfully.
@ -340,10 +471,12 @@ namespace Explorer.UnstripInternals
if (ScrollStack.Count <= 0) return;
var state = ScrollStack.Peek().TryCast<ScrollViewState>();
var scrollExt = Internal_ScrollViewState.FromPointer(state.Pointer);
var scrollExt = ScrollStack.Peek() as ScrollViewStateUnstrip;
if (scrollExt == null) throw new Exception("Could not get scrollExt!");
//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();
@ -411,16 +544,15 @@ namespace Explorer.UnstripInternals
int controlID = GUIUtility.GetControlID(GUI.s_ScrollviewHash, FocusType.Passive);
var scrollViewState = GetStateObject(Il2CppType.Of<ScrollViewState>(), controlID)
.TryCast<ScrollViewState>();
if (scrollViewState == null)
return scrollPosition;
var scrollExt = Internal_ScrollViewState.FromPointer(scrollViewState.Pointer);
if (scrollExt == null)
return scrollPosition;
ScrollViewStateUnstrip scrollExt;
try
{
scrollExt = (ScrollViewStateUnstrip)GUIUtilityUnstrip.GetMonoStateObject(typeof(ScrollViewStateUnstrip), controlID);
}
catch
{
return Vector2.zero;
}
bool apply = scrollExt.apply;
if (apply)
@ -438,7 +570,7 @@ namespace Explorer.UnstripInternals
rect.width = position.width;
rect.height = position.height;
ScrollStack.Push(scrollViewState);
ScrollStack.Push(scrollExt);
Rect screenRect = new Rect(position.x, position.y, position.width, position.height);
EventType type = Event.current.type;
@ -622,7 +754,7 @@ namespace Explorer.UnstripInternals
{
id = GUIUtility.GetControlID(GUI.s_SliderHash, FocusType.Passive, position);
}
var sliderHandler = new Internal_SliderHandler(position, value, size, start, end, slider, thumb, horiz, id);
var sliderHandler = new SliderHandlerUnstrip(position, value, size, start, end, slider, thumb, horiz, id);
return sliderHandler.Handle();
}
@ -652,7 +784,7 @@ namespace Explorer.UnstripInternals
return result;
}
#endregion
#endregion
}
#region Extensions

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,10 +1,9 @@
#if CPP
using UnityEngine;
using Explorer.UnstripInternals;
namespace Explorer
namespace Explorer.Unstrip.IMGUI
{
public class Internal_LayoutUtility
public class LayoutUtilityUnstrip
{
public static Rect GetRect(float width, float height, GUIStyle style, params GUILayoutOption[] options)
{
@ -86,7 +85,7 @@ namespace Explorer
last = GUILayoutUtility.kDummyRect;
}
return last;
}
}
}
}
#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,124 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
#if CPP
using Explorer.UnstripInternals;
using UnityEngineInternal;
using UnhollowerRuntimeLib;
#endif
namespace Explorer
{
public class GUIUnstrip
{
public static string TextField(string text, GUILayoutOption[] options)
{
#if CPP
return Internal.TextField(text, options);
#else
return GUILayout.TextField(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 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
Internal.BringWindowToFront(id);
#else
GUI.BringWindowToFront(id);
#endif
}
public static Vector2 ScreenToGUIPoint(Vector2 screenPoint)
{
#if CPP
return Internal.ScreenToGUIPoint(screenPoint);
#else
return GUIUtility.ScreenToGUIPoint(screenPoint);
#endif
}
public static void Space(float pixels)
{
#if CPP
Internal.Space(pixels);
#else
GUILayout.Space(pixels);
#endif
}
public static bool RepeatButton(string text, params GUILayoutOption[] options)
{
#if CPP
return Internal.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
Internal.BeginArea(screenRect, GUIContent.none, style);
#else
GUILayout.BeginArea(screenRect, style);
#endif
}
static public void EndArea()
{
#if CPP
Internal.EndArea();
#else
GUILayout.EndArea();
#endif
}
public static Vector2 BeginScrollView(Vector2 scroll, params GUILayoutOption[] options)
{
#if CPP
return Internal.BeginScrollView(scroll, options);
#else
return GUILayout.BeginScrollView(scroll, options);
#endif
}
public static void EndScrollView(bool handleScrollWheel = true)
{
#if CPP
Internal.EndScrollView(handleScrollWheel);
#else
GUILayout.EndScrollView();
#endif
}
}
}

View File

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

View File

@ -1,413 +0,0 @@
#if CPP
using System;
using UnhollowerRuntimeLib;
using UnityEngine;
using Explorer.UnstripInternals;
using Il2CppSystem.Reflection;
namespace Explorer
{
public struct Internal_SliderHandler
{
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 Internal_SliderHandler(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 ext = Internal_SliderState.FromPointer(GetSliderState().Pointer);
ext.isDragging = false;
Internal.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 ext = Internal_SliderState.FromPointer(GetSliderState().Pointer);
if (!ext.isDragging)
{
result = this.currentValue;
}
else
{
GUI.changed = true;
this.CurrentEvent().Use();
float num = this.MousePosition() - ext.dragStartPos;
float value = ext.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 < Internal.nextScrollStepTime)
{
result = this.currentValue;
}
else if (this.CurrentScrollTroughSide() != ScrollTroughSide)
{
result = this.currentValue;
}
else
{
Internal.nextScrollStepTime = DateTime.Now.AddMilliseconds(30.0);
if (this.SupportsPageMovements())
{
Internal_SliderState.FromPointer(GetSliderState().Pointer).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 ext = Internal_SliderState.FromPointer(GetSliderState().Pointer);
ext.dragStartPos = this.MousePosition();
ext.dragStartValue = dragStartValue;
ext.isDragging = true;
}
private SliderState GetSliderState()
{
return 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,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Explorer
{
public class Internal_SliderState
{
public float dragStartPos;
public float dragStartValue;
public bool isDragging;
public static Dictionary<IntPtr, Internal_SliderState> Dict = new Dictionary<IntPtr, Internal_SliderState>();
public static Internal_SliderState FromPointer(IntPtr ptr)
{
if (!Dict.ContainsKey(ptr))
{
Dict.Add(ptr, new Internal_SliderState());
}
return Dict[ptr];
}
}
}

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>