Compare commits

..

38 Commits
1.6.8 ... 1.7.5

Author SHA1 Message Date
f203ae37fc 1.7.5
* Added support for Enums with [Flags] attribute (can set each flag individually)
* Added support for easier bitwise operations on ints (or any primitive assignable to int), and viewing the int as binary. This is intended for things like `Camera.cullingMask`, etc.
* Fixed an issue with Enums that contain duplicate values, for example `CameraClearFlags` (has duplicate values for 2).
2020-09-23 19:19:29 +10:00
2006a9ea76 Faster non-generic Il2Cpp casting 2020-09-21 22:45:33 +10:00
c9bc450d09 Update README.md 2020-09-21 17:48:13 +10:00
a1198f3a92 Update CacheColor.cs 2020-09-21 05:59:01 +10:00
04248a89ce Fix for cases when structs return null (due to null declaring instance) 2020-09-20 20:26:05 +10:00
3639824df3 Cleanup 2020-09-19 01:55:27 +10:00
939861b5f0 Cleanup 2020-09-19 01:44:38 +10:00
ad5fc04a3b Fix methods with multiple generic constraints 2020-09-19 01:27:33 +10:00
c39e097378 Add support for methods with ref/in/out args 2020-09-19 00:14:04 +10:00
129a7e3765 Improved interaction with generic methods and some minor UI fixes 2020-09-18 23:10:46 +10:00
643bb4519c Remove and sort usings 2020-09-18 18:38:11 +10:00
b154cbf39d Add support for generic methods, improved non-generic dictionary output 2020-09-18 18:03:17 +10:00
db91968519 Cleanup and improve syntax highlighting
* Static class members are now displayed in Italics and in a darker color, making them easier to distinguish.
* Cleaned up some issues related to syntax highlighting and refactored it into a global class.
* Methods and properties no longer display their arguments as part of the member name, they are only displayed when "Evaluate" is pressed.
2020-09-16 20:03:57 +10:00
5d58993b07 1.7.31
* Added support for Il2Cpp Hashtable (non-generic Dict)
* Dictionaries should now display CacheOther values better (smaller buttons)
* Cleaned up and improved some of CacheDictionary performance
2020-09-15 17:38:10 +10:00
eea581f8d5 Update ResizeDrag.cs 2020-09-14 20:27:49 +10:00
9bb3c77bae 1.7.3
* Reverted some unstrip fixes from 1.7.2 because it was causing more problems than it solved.
2020-09-14 20:25:38 +10:00
477a6859d7 Update README.md 2020-09-14 17:07:52 +10:00
f8f9671746 Update README.md 2020-09-14 16:56:42 +10:00
dc2759c599 1.7.2
unstrip fixes
2020-09-14 01:42:29 +10:00
653b4a2304 Fix BeginVertical and BeginHorizontal 2020-09-13 23:16:12 +10:00
065ab033c9 Fix crash when inspecting RigidBody2D objects 2020-09-13 17:39:15 +10:00
11cbd24a6a 1.7.1 2020-09-13 17:29:01 +10:00
fbf9859e0f Fix for GUILayout.BeginArea unstrip and UnityEngine.SystemClock unstrip
and remove some old comments
2020-09-13 17:11:15 +10:00
2d7dfa53eb Fix scroll not working in 2017 games 2020-09-12 16:14:42 +10:00
eff8d63c81 Update README.md 2020-09-12 04:03:53 +10:00
006cf0fc2c Create icon.png 2020-09-12 04:03:30 +10:00
e5ca3530ff Merge branch 'master' of https://github.com/sinai-dev/CppExplorer 2020-09-12 04:01:18 +10:00
4de378907b Update GameObjectWindow.cs 2020-09-12 02:52:52 +10:00
bbdfb46a1e Update README.md 2020-09-12 02:35:41 +10:00
e6e2b3cd67 Update README.md 2020-09-12 02:30:34 +10:00
1d07046a74 Update README.md, hide Console when it fails to init 2020-09-12 02:20:27 +10:00
de663f34b2 1.7.0
* Fix for GuiLayout.Space unstrip
* Cleanups
2020-09-12 00:59:59 +10:00
8d648fec43 1.6.9
* Fix for games where patching Cursor methods fails.
* Added backup attempt for loading Cursor module if not present.
* HashSet collections are now supported by CacheList
* try/catch for loading Mod Config
2020-09-11 18:53:17 +10:00
835a81765e Add support for Hashset, add try/catch for loading settings 2020-09-11 00:17:13 +10:00
51ed936e30 Revert PR changes 2020-09-10 22:40:23 +10:00
ac16587cdc Merge pull request #11 from PsymoNBond/master
Fixed Rect init
2020-09-10 22:38:16 +10:00
1e1eaa6c38 Fixed Rect init 2020-09-10 14:16:25 +03:00
af17ae82c6 Update README.md 2020-09-10 20:47:13 +10:00
49 changed files with 1581 additions and 795 deletions

View File

@ -1,7 +1,7 @@
# CppExplorer [![Version](https://img.shields.io/badge/MelonLoader-0.2.7.1-green.svg)](https://github.com/HerpDerpinstine/MelonLoader) # CppExplorer [![Version](https://img.shields.io/badge/MelonLoader-0.2.7.1-green.svg)](https://github.com/HerpDerpinstine/MelonLoader)
<p align="center"> <p align="center">
<img align="center" src="https://sinai-dev.github.io/images/thumbs/02.png"> <img align="center" src="icon.png">
</p> </p>
<p align="center"> <p align="center">
@ -13,24 +13,40 @@
<img src="https://img.shields.io/github/downloads/sinai-dev/CppExplorer/total.svg" /> <img src="https://img.shields.io/github/downloads/sinai-dev/CppExplorer/total.svg" />
</p> </p>
<p align="center">
<a href="https://github.com/sinai-dev/MonoExplorer">Looking for a Mono version?</a>
</p>
### Known issues - [Known issues](#known-issues)
* CppExplorer may experience a `MissingMethodException` when trying to use certain UnityEngine methods. If you experience this, please open an issue and I will do my best to fix it. - [How to install](#how-to-install)
* Reflection may fail with certain types (eg. `Nullable<T>`, some Dictionary types, etc). Please see [Il2CppAssemblyUnhollower](https://github.com/knah/Il2CppAssemblyUnhollower#known-issues) for more details. - [How to use](#how-to-use)
- [Mod Config](#mod-config)
- [Features](#features)
- [Mouse Control](#mouse-control)
- [Building](#building)
- [Credits](#credits)
## Known issues
As of version 1.7+, CppExplorer has reached a fairly stable state for most Il2Cpp games.
* .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 CppExplorer menu may not work on all games at the moment. * Scrolling with mouse wheel in the CppExplorer menu may not work on all games at the moment.
## How to install ## How to install
Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be installed for your game. Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be installed for your game.
1. Download <b>CppExplorer.zip</b> from [Releases](https://github.com/sinaioutlander/CppExplorer/releases). 1. Download <b>CppExplorer.zip</b> from [Releases](https://github.com/sinai-dev/CppExplorer/releases).
2. Unzip the file into the `Mods` folder in your game's installation directory, created by MelonLoader. 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, `CppExplorer.dll` and `mcs.dll` should be directly in the `Mods\` folder. 3. Make sure it's not in a sub-folder, `CppExplorer.dll` and `mcs.dll` should be directly in the `Mods\` folder.
## How to use ## How to use
* Press F7 to show or hide the menu. * Press F7 to show or hide the menu.
* Simply browse through the scene, search for objects, etc, most of it is pretty self-explanatory. * 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
@ -48,18 +64,11 @@ This config is generated to `Mods\CppExplorer\config.xml`. Edit the config while
* `x` is width, `y` is height. * `x` is width, `y` is height.
* Default: `<x>550</x> <y>700</y>` * Default: `<x>550</x> <y>700</y>`
### Features ## Features
[![](overview.png)](overview.png) [![](overview.png)](https://raw.githubusercontent.com/sinai-dev/CppExplorer/master/overview.png)
<i>An overview of the different CppExplorer menus.</i> <i>An overview of the different CppExplorer menus.</i>
* Scene hierarchy explorer
* Search loaded assets with filters
* Traverse and manipulate GameObjects
* Generic Reflection inspector
* C# REPL Console
* Inspect-under-mouse
### Scene Explorer ### Scene Explorer
* A simple menu which allows you to traverse the Transform heirarchy of the scene. * A simple menu which allows you to traverse the Transform heirarchy of the scene.
@ -90,9 +99,9 @@ CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Re
* Filter by name, type, etc. * Filter by name, type, etc.
* For GameObjects and Transforms you can filter which scene they are found in too. * For GameObjects and Transforms you can filter which scene they are found in too.
### C# REPL console ### C# console
* A simple C# REPL console, allows you to execute a method body on the fly. * A simple C# console, allows you to execute a method body on the fly.
### Inspect-under-mouse ### Inspect-under-mouse
@ -108,6 +117,23 @@ CppExplorer can force the mouse to be visible and unlocked when the menu is open
* For Hellpoint, use [HPExplorerMouseControl](https://github.com/sinai-dev/Hellpoint-Mods/tree/master/HPExplorerMouseControl/HPExplorerMouseControl) * For Hellpoint, use [HPExplorerMouseControl](https://github.com/sinai-dev/Hellpoint-Mods/tree/master/HPExplorerMouseControl/HPExplorerMouseControl)
* You can create your own plugin using one of the two plugins above as an example. Usually only a few simple Harmony patches are needed to fix the problem. * You can create your own plugin using one of the two plugins above as an example. Usually only a few simple Harmony patches are needed to fix the problem.
For example:
```csharp
using Explorer;
using Harmony;
// ...
[HarmonyPatch(typeof(MyGame.MenuClass), nameof(MyGame.MenuClass.CursorUpdate)]
public class MenuClass_CursorUpdate
{
[HarmonyPrefix]
public static bool Prefix()
{
// prevent method running if menu open, let it run if not.
return !CppExplorer.ShowMenu;
}
}
```
## Building ## Building
If you'd like to build this yourself, everything you need (other than MelonLoader) is included with this repository, there is no need for recursive cloning etc. If you'd like to build this yourself, everything you need (other than MelonLoader) is included with this repository, there is no need for recursive cloning etc.

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
lib/mcs.NET35.dll Normal file

Binary file not shown.

View File

@ -2,10 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using MelonLoader; using MelonLoader;
using UnhollowerBaseLib;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer
@ -13,14 +10,14 @@ namespace Explorer
public abstract class CacheObjectBase public abstract class CacheObjectBase
{ {
public object Value; public object Value;
public string ValueTypeName;
public Type ValueType; public Type ValueType;
public MemberInfo MemInfo { get; set; } public MemberInfo MemInfo { get; set; }
public Type DeclaringType { get; set; } public Type DeclaringType { get; set; }
public object DeclaringInstance { get; set; } public object DeclaringInstance { get; set; }
public bool HasParameters => m_arguments != null && m_arguments.Length > 0; public virtual bool HasParameters => m_arguments != null && m_arguments.Length > 0;
public bool m_evaluated = false; public bool m_evaluated = false;
public bool m_isEvaluating; public bool m_isEvaluating;
public ParameterInfo[] m_arguments = new ParameterInfo[0]; public ParameterInfo[] m_arguments = new ParameterInfo[0];
@ -117,28 +114,17 @@ namespace Explorer
var pi = memberInfo as PropertyInfo; var pi = memberInfo as PropertyInfo;
var mi = memberInfo as MethodInfo; var mi = memberInfo as MethodInfo;
// if PropertyInfo, check if can process args // Check if can process args
if (pi != null && !CanProcessArgs(pi.GetIndexParameters())) if ((pi != null && !CanProcessArgs(pi.GetIndexParameters()))
|| (mi != null && !CanProcessArgs(mi.GetParameters())))
{ {
return null; return null;
} }
// This is pretty ugly, could probably make a cleaner implementation.
// However, the only cleaner ways I can think of are slower and probably not worth it.
// Note: the order is somewhat important.
if (mi != null) if (mi != null)
{
if (CacheMethod.CanEvaluate(mi))
{ {
holder = new CacheMethod(); holder = new CacheMethod();
} }
else
{
return null;
}
}
else if (valueType == typeof(GameObject) || valueType == typeof(Transform)) else if (valueType == typeof(GameObject) || valueType == typeof(Transform))
{ {
holder = new CacheGameObject(); holder = new CacheGameObject();
@ -148,9 +134,16 @@ namespace Explorer
holder = new CachePrimitive(); holder = new CachePrimitive();
} }
else if (valueType.IsEnum) else if (valueType.IsEnum)
{
if (valueType.GetCustomAttributes(typeof(FlagsAttribute), false) is object[] attributes && attributes.Length > 0)
{
holder = new CacheEnumFlags();
}
else
{ {
holder = new CacheEnum(); holder = new CacheEnum();
} }
}
else if (valueType == typeof(Vector2) || valueType == typeof(Vector3) || valueType == typeof(Vector4)) else if (valueType == typeof(Vector2) || valueType == typeof(Vector3) || valueType == typeof(Vector4))
{ {
holder = new CacheVector(); holder = new CacheVector();
@ -172,7 +165,7 @@ namespace Explorer
{ {
holder = new CacheDictionary(); holder = new CacheDictionary();
} }
else if (ReflectionHelpers.IsEnumerable(valueType) || ReflectionHelpers.IsCppList(valueType)) else if (ReflectionHelpers.IsEnumerable(valueType))
{ {
holder = new CacheList(); holder = new CacheList();
} }
@ -183,7 +176,6 @@ namespace Explorer
holder.Value = obj; holder.Value = obj;
holder.ValueType = valueType; holder.ValueType = valueType;
holder.ValueTypeName = valueType.FullName;
if (memberInfo != null) if (memberInfo != null)
{ {
@ -214,7 +206,18 @@ namespace Explorer
{ {
foreach (var param in parameters) foreach (var param in parameters)
{ {
if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string)) var pType = param.ParameterType;
if (pType.IsByRef && pType.HasElementType)
{
pType = pType.GetElementType();
}
if (pType.IsPrimitive || pType == typeof(string))
{
continue;
}
else
{ {
return false; return false;
} }
@ -244,7 +247,11 @@ namespace Explorer
var input = m_argumentInput[i]; var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType; var type = m_arguments[i].ParameterType;
// First, try parse the input and use that. if (type.IsByRef)
{
type = type.GetElementType();
}
if (!string.IsNullOrEmpty(input)) if (!string.IsNullOrEmpty(input))
{ {
// strings can obviously just be used directly // strings can obviously just be used directly
@ -309,15 +316,8 @@ namespace Explorer
var pi = MemInfo as PropertyInfo; var pi = MemInfo as PropertyInfo;
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance; var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
if (HasParameters)
{
Value = pi.GetValue(target, ParseArguments()); Value = pi.GetValue(target, ParseArguments());
} }
else
{
Value = pi.GetValue(target, null);
}
}
ReflectionException = null; ReflectionException = null;
m_evaluated = true; m_evaluated = true;
@ -384,7 +384,7 @@ namespace Explorer
} }
else else
{ {
GUILayout.Space(labelWidth); GUIUnstrip.Space(labelWidth);
} }
var cm = this as CacheMethod; var cm = this as CacheMethod;
@ -395,43 +395,87 @@ namespace Explorer
if (m_isEvaluating) if (m_isEvaluating)
{ {
if (cm != null && cm.GenericArgs.Length > 0)
{
GUILayout.Label($"<b><color=orange>Generic Arguments:</color></b>", null);
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(null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label($"<color={UIStyles.Syntax.StructGreen}>{cm.GenericArgs[i].Name}</color>", new GUILayoutOption[] { GUILayout.Width(15) });
cm.GenericArgInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label(types, null);
GUILayout.EndHorizontal();
}
}
if (m_arguments.Length > 0)
{
GUILayout.Label($"<b><color=orange>Arguments:</color></b>", null);
for (int i = 0; i < m_arguments.Length; i++) for (int i = 0; i < m_arguments.Length; i++)
{ {
var name = m_arguments[i].Name; var name = m_arguments[i].Name;
var input = m_argumentInput[i]; var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType.Name; var type = m_arguments[i].ParameterType.Name;
var label = "<color=#2df7b2>" + type + "</color> <color=#a6e9e9>" + name + "</color>"; var label = $"<color={UIStyles.Syntax.Class_Instance}>{type}</color> ";
label += $"<color={UIStyles.Syntax.Local}>{name}</color>";
if (m_arguments[i].HasDefaultValue) if (m_arguments[i].HasDefaultValue)
{ {
label = $"<i>[{label} = {m_arguments[i].DefaultValue}]</i>"; label = $"<i>[{label} = {m_arguments[i].DefaultValue ?? "null"}]</i>";
} }
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(20) }); GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(15) });
m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) }); m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label(label, null); GUILayout.Label(label, null);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
}
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
if (cm != null)
{
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) })) if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{
if (cm != null)
{ {
cm.Evaluate(); cm.Evaluate();
} }
}
else else
{
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{ {
UpdateValue(); UpdateValue();
} }
} }
if (GUILayout.Button("Cancel", new GUILayoutOption[] { GUILayout.Width(70) })) if (GUILayout.Button("Cancel", new GUILayoutOption[] { GUILayout.Width(70) }))
{ {
m_isEvaluating = false; m_isEvaluating = false;
@ -440,7 +484,12 @@ namespace Explorer
} }
else else
{ {
if (GUILayout.Button($"Evaluate ({m_arguments.Length} params)", new GUILayoutOption[] { GUILayout.Width(150) })) 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; m_isEvaluating = true;
} }
@ -451,7 +500,7 @@ namespace Explorer
// new line and space // new line and space
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(labelWidth); GUIUnstrip.Space(labelWidth);
} }
else if (cm != null) else if (cm != null)
{ {
@ -465,20 +514,22 @@ namespace Explorer
// new line and space // new line and space
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(labelWidth); GUIUnstrip.Space(labelWidth);
} }
string typeName = $"<color={UIStyles.Syntax.Class_Instance}>{ValueType.FullName}</color>";
if (!string.IsNullOrEmpty(ReflectionException)) if (!string.IsNullOrEmpty(ReflectionException))
{ {
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null); GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null);
} }
else if ((HasParameters || this is CacheMethod) && !m_evaluated) else if ((HasParameters || this is CacheMethod) && !m_evaluated)
{ {
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=#2df7b2>{ValueTypeName}</color>)", null); GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> ({typeName})", null);
} }
else if (Value == null && !(this is CacheMethod)) else if (Value == null && !(this is CacheMethod))
{ {
GUILayout.Label("<i>null (" + ValueTypeName + ")</i>", null); GUILayout.Label($"<i>null ({typeName})</i>", null);
} }
else else
{ {
@ -489,31 +540,80 @@ namespace Explorer
private string GetRichTextName() private string GetRichTextName()
{ {
string memberColor = ""; string memberColor = "";
switch (MemInfo.MemberType) bool isStatic = false;
{
case MemberTypes.Field:
memberColor = "#c266ff"; break;
case MemberTypes.Property:
memberColor = "#72a6a6"; break;
case MemberTypes.Method:
memberColor = "#ff8000"; break;
};
m_richTextName = $"<color=#2df7b2>{MemInfo.DeclaringType.Name}</color>.<color={memberColor}>{MemInfo.Name}</color>"; if (MemInfo is FieldInfo fi)
if (m_arguments.Length > 0 || this is CacheMethod)
{ {
m_richTextName += "("; if (fi.IsStatic)
var _params = "";
foreach (var param in m_arguments)
{ {
if (_params != "") _params += ", "; isStatic = true;
memberColor = UIStyles.Syntax.Field_Static;
_params += $"<color=#2df7b2>{param.ParameterType.Name}</color> <color=#a6e9e9>{param.Name}</color>";
} }
m_richTextName += _params; else
m_richTextName += ")"; memberColor = UIStyles.Syntax.Field_Instance;
} }
else if (MemInfo is MethodInfo mi)
{
if (mi.IsStatic)
{
isStatic = true;
memberColor = UIStyles.Syntax.Method_Static;
}
else
memberColor = UIStyles.Syntax.Method_Instance;
}
else if (MemInfo is PropertyInfo pi)
{
if (pi.GetAccessors()[0].IsStatic)
{
isStatic = true;
memberColor = UIStyles.Syntax.Prop_Static;
}
else
memberColor = UIStyles.Syntax.Prop_Instance;
}
string classColor = MemInfo.DeclaringType.IsAbstract && MemInfo.DeclaringType.IsSealed
? UIStyles.Syntax.Class_Static
: UIStyles.Syntax.Class_Instance;
m_richTextName = $"<color={classColor}>{MemInfo.DeclaringType.Name}</color>.";
if (isStatic) m_richTextName += "<i>";
m_richTextName += $"<color={memberColor}>{MemInfo.Name}</color>";
if (isStatic) m_richTextName += "</i>";
// generic method args
if (this is CacheMethod cm && cm.GenericArgs.Length > 0)
{
m_richTextName += "<";
var args = "";
for (int i = 0; i < cm.GenericArgs.Length; i++)
{
if (args != "") args += ", ";
args += $"<color={UIStyles.Syntax.StructGreen}>{cm.GenericArgs[i].Name}</color>";
}
m_richTextName += args;
m_richTextName += ">";
}
// Method / Property arguments
//if (m_arguments.Length > 0 || this is CacheMethod)
//{
// m_richTextName += "(";
// var args = "";
// foreach (var param in m_arguments)
// {
// if (args != "") args += ", ";
// args += $"<color={classColor}>{param.ParameterType.Name}</color> ";
// args += $"<color={UIStyles.Syntax.Local}>{param.Name}</color>";
// }
// m_richTextName += args;
// m_richTextName += ")";
//}
return m_richTextName; return m_richTextName;
} }

View File

@ -1,10 +1,4 @@
using System; namespace Explorer
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Explorer
{ {
interface IExpandHeight interface IExpandHeight
{ {

View File

@ -1,13 +1,8 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine;
using System.Reflection;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -98,37 +93,16 @@ namespace Explorer
private void GetGenericArguments() private void GetGenericArguments()
{ {
if (this.MemInfo != null) if (ValueType.IsGenericType)
{ {
Type memberType = null; m_keysType = ValueType.GetGenericArguments()[0];
switch (this.MemInfo.MemberType) m_valuesType = ValueType.GetGenericArguments()[1];
{
case MemberTypes.Field:
memberType = (MemInfo as FieldInfo).FieldType;
break;
case MemberTypes.Property:
memberType = (MemInfo as PropertyInfo).PropertyType;
break;
}
if (memberType != null && memberType.IsGenericType)
{
m_keysType = memberType.GetGenericArguments()[0];
m_valuesType = memberType.GetGenericArguments()[1];
}
}
else if (Value != null)
{
var type = Value.GetType();
if (type.IsGenericType)
{
m_keysType = type.GetGenericArguments()[0];
m_valuesType = type.GetGenericArguments()[1];
} }
else else
{ {
MelonLogger.Log("TODO? Dictionary is of type: " + Value.GetType().FullName); // It's non-generic, just use System.Object to allow for anything.
} m_keysType = typeof(object);
m_valuesType = typeof(object);
} }
} }
@ -154,14 +128,16 @@ namespace Explorer
var keys = new List<CacheObjectBase>(); var keys = new List<CacheObjectBase>();
foreach (var key in IDict.Keys) foreach (var key in IDict.Keys)
{ {
var cache = GetCacheObject(key, TypeOfKeys); Type t = ReflectionHelpers.GetActualType(key) ?? TypeOfKeys;
var cache = GetCacheObject(key, t);
keys.Add(cache); keys.Add(cache);
} }
var values = new List<CacheObjectBase>(); var values = new List<CacheObjectBase>();
foreach (var val in IDict.Values) foreach (var val in IDict.Values)
{ {
var cache = GetCacheObject(val, TypeOfValues); Type t = ReflectionHelpers.GetActualType(val) ?? TypeOfValues;
var cache = GetCacheObject(val, t);
values.Add(cache); values.Add(cache);
} }
@ -228,14 +204,14 @@ namespace Explorer
var negativeWhitespace = window.width - (whitespace + 100f); var negativeWhitespace = window.width - (whitespace + 100f);
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
string btnLabel = $"<color=#2df7b2>[{count}] Dictionary<{TypeOfKeys.FullName}, {TypeOfValues.FullName}></color>"; string btnLabel = $"[{count}] <color=#2df7b2>Dictionary<{TypeOfKeys.FullName}, {TypeOfValues.FullName}></color>";
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.Width(negativeWhitespace) })) if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.Width(negativeWhitespace) }))
{ {
WindowManager.InspectObject(Value, out bool _); WindowManager.InspectObject(Value, out bool _);
} }
GUI.skin.button.alignment = TextAnchor.MiddleCenter; GUI.skin.button.alignment = TextAnchor.MiddleCenter;
GUILayout.Space(5); GUIUnstrip.Space(5);
if (IsExpanded) if (IsExpanded)
{ {
@ -246,7 +222,7 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
Pages.CurrentPageLabel(); Pages.CurrentPageLabel();
@ -262,7 +238,7 @@ namespace Explorer
Pages.DrawLimitInputArea(); Pages.DrawLimitInputArea();
GUILayout.Space(5); GUIUnstrip.Space(5);
} }
int offset = Pages.CalculateOffsetIndex(); int offset = Pages.CalculateOffsetIndex();
@ -276,7 +252,7 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
//GUILayout.Space(whitespace); //GUIUnstrip.Space(whitespace);
if (key == null || val == null) if (key == null || val == null)
{ {
@ -288,10 +264,10 @@ namespace Explorer
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) }); GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) });
key.DrawValue(window, (window.width / 2) - 30f); key.DrawValue(window, (window.width / 2) - 80f);
GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) }); GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) });
val.DrawValue(window, (window.width / 2) - 30f); val.DrawValue(window, (window.width / 2) - 80f);
} }
} }

View File

@ -1,10 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using MelonLoader; using MelonLoader;
using UnityEngine; using UnityEngine;
@ -40,7 +39,7 @@ namespace Explorer
private Type m_genericTypeDef; private Type m_genericTypeDef;
// Cached ToArray method for Lists // Cached ToArray method for Lists
public MethodInfo GenericToArrayMethod public MethodInfo CppListToArrayMethod
{ {
get => GetGenericToArrayMethod(); get => GetGenericToArrayMethod();
} }
@ -60,7 +59,7 @@ namespace Explorer
{ {
if (m_enumerable == null && Value != null) if (m_enumerable == null && Value != null)
{ {
m_enumerable = Value as IEnumerable ?? GetEnumerableFromIl2CppList(); m_enumerable = Value as IEnumerable ?? EnumerateWithReflection();
} }
return m_enumerable; return m_enumerable;
} }
@ -100,21 +99,45 @@ namespace Explorer
return m_itemProperty; return m_itemProperty;
} }
private IEnumerable GetEnumerableFromIl2CppList() private IEnumerable EnumerateWithReflection()
{ {
if (Value == null) return null; if (Value == null) return null;
if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.List<>)) if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.List<>))
{ {
return (IEnumerable)GenericToArrayMethod?.Invoke(Value, new object[0]); return (IEnumerable)CppListToArrayMethod?.Invoke(Value, new object[0]);
}
else if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.HashSet<>))
{
return CppHashSetToMono();
} }
else else
{ {
return ConvertIListToMono(); return CppIListToMono();
} }
} }
private IList ConvertIListToMono() private IEnumerable CppHashSetToMono()
{
var set = new HashSet<object>();
// invoke GetEnumerator
var enumerator = Value.GetType().GetMethod("GetEnumerator").Invoke(Value, null);
// get the type of it
var enumeratorType = enumerator.GetType();
// reflect MoveNext and Current
var moveNext = enumeratorType.GetMethod("MoveNext");
var current = enumeratorType.GetProperty("Current");
// iterate
while ((bool)moveNext.Invoke(enumerator, null))
{
set.Add(current.GetValue(enumerator));
}
return set;
}
private IList CppIListToMono()
{ {
try try
{ {
@ -273,7 +296,7 @@ namespace Explorer
} }
GUI.skin.button.alignment = TextAnchor.MiddleCenter; GUI.skin.button.alignment = TextAnchor.MiddleCenter;
GUILayout.Space(5); GUIUnstrip.Space(5);
if (IsExpanded) if (IsExpanded)
{ {
@ -284,7 +307,7 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
Pages.CurrentPageLabel(); Pages.CurrentPageLabel();
@ -300,7 +323,7 @@ namespace Explorer
Pages.DrawLimitInputArea(); Pages.DrawLimitInputArea();
GUILayout.Space(5); GUIUnstrip.Space(5);
} }
int offset = Pages.CalculateOffsetIndex(); int offset = Pages.CalculateOffsetIndex();
@ -313,7 +336,7 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
if (entry == null || entry.Value == null) if (entry == null || entry.Value == null)
{ {

View File

@ -1,11 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
using UnityEngine;
using MelonLoader; using MelonLoader;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -13,16 +11,24 @@ namespace Explorer
{ {
private CacheObjectBase m_cachedReturnValue; private CacheObjectBase m_cachedReturnValue;
public static bool CanEvaluate(MethodInfo mi) public override bool HasParameters => base.HasParameters || GenericArgs.Length > 0;
{
// TODO generic args
if (mi.GetGenericArguments().Length > 0)
{
return false;
}
// primitive and string args supported public Type[] GenericArgs { get; private set; }
return CanProcessArgs(mi.GetParameters()); public Type[][] GenericConstraints { get; private set; }
public string[] GenericArgInput = new string[0];
public override void Init()
{
var mi = (MemInfo as MethodInfo);
GenericArgs = mi.GetGenericArguments();
GenericConstraints = GenericArgs.Select(x => x.GetGenericParameterConstraints())
.ToArray();
GenericArgInput = new string[GenericArgs.Length];
ValueType = mi.ReturnType;
} }
public override void UpdateValue() public override void UpdateValue()
@ -32,27 +38,29 @@ namespace Explorer
public void Evaluate() public void Evaluate()
{ {
m_isEvaluating = false; MethodInfo mi;
if (GenericArgs.Length > 0)
var mi = MemInfo as MethodInfo;
object ret = null;
if (!HasParameters)
{ {
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, new object[0]); mi = MakeGenericMethodFromInput();
m_evaluated = true; if (mi == null) return;
} }
else else
{ {
mi = MemInfo as MethodInfo;
}
object ret = null;
try try
{ {
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, ParseArguments()); ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, ParseArguments());
m_evaluated = true; m_evaluated = true;
m_isEvaluating = false;
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log($"Exception evaluating: {e.GetType()}, {e.Message}"); MelonLogger.LogWarning($"Exception evaluating: {e.GetType()}, {e.Message}");
} ReflectionException = ReflectionHelpers.ExceptionToString(e);
} }
if (ret != null) if (ret != null)
@ -66,10 +74,54 @@ namespace Explorer
} }
} }
private MethodInfo MakeGenericMethodFromInput()
{
var mi = MemInfo as MethodInfo;
var list = new List<Type>();
for (int i = 0; i < GenericArgs.Length; i++)
{
var input = GenericArgInput[i];
if (ReflectionHelpers.GetTypeByName(input) is Type t)
{
if (GenericConstraints[i].Length == 0)
{
list.Add(t);
}
else
{
foreach (var constraint in GenericConstraints[i].Where(x => x != null))
{
if (!constraint.IsAssignableFrom(t))
{
MelonLogger.LogWarning($"Generic argument #{i}, '{input}' is not assignable from the constraint '{constraint}'!");
return null;
}
}
list.Add(t);
}
}
else
{
MelonLogger.LogWarning($"Generic argument #{i}, could not get any type by the name of '{input}'!" +
$" Make sure you use the full name, including the NameSpace.");
return null;
}
}
// make into a generic with type list
mi = mi.MakeGenericMethod(list.ToArray());
return mi;
}
// ==== GUI DRAW ==== // ==== GUI DRAW ====
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
string typeLabel = $"<color={UIStyles.Syntax.Class_Instance}>{ValueType.FullName}</color>";
if (m_evaluated) if (m_evaluated)
{ {
if (m_cachedReturnValue != null) if (m_cachedReturnValue != null)
@ -78,12 +130,12 @@ namespace Explorer
} }
else else
{ {
GUILayout.Label($"null (<color=#2df7b2>{ValueTypeName}</color>)", null); GUILayout.Label($"null ({typeLabel})", null);
} }
} }
else else
{ {
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=#2df7b2>{ValueTypeName}</color>)", null); GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> ({typeLabel})", null);
} }
} }
} }

View File

@ -1,9 +1,4 @@
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
@ -11,13 +6,30 @@ namespace Explorer
{ {
public class CacheOther : CacheObjectBase public class CacheOther : CacheObjectBase
{ {
public string ButtonLabel => m_btnLabel ?? GetButtonLabel();
private string m_btnLabel;
public MethodInfo ToStringMethod => m_toStringMethod ?? GetToStringMethod();
private MethodInfo m_toStringMethod; private MethodInfo m_toStringMethod;
public MethodInfo ToStringMethod public override void UpdateValue()
{ {
get base.UpdateValue();
GetButtonLabel();
}
public override void DrawValue(Rect window, float width)
{ {
if (m_toStringMethod == null) 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 try
{ {
@ -31,35 +43,38 @@ namespace Explorer
{ {
m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]); m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]);
} }
}
return m_toStringMethod; return m_toStringMethod;
} }
}
public override void DrawValue(Rect window, float width) private string GetButtonLabel()
{ {
if (Value == null) return null;
string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString(); string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
if (!label.Contains(ValueTypeName)) 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 += $" (<color=#2df7b2>{ValueTypeName}</color>)"; label = label.Replace($"({ValueType.FullName})", $"({typeLabel})");
} }
else else
{ {
label = label.Replace(ValueTypeName, $"<color=#2df7b2>{ValueTypeName}</color>"); if (!label.Contains(ValueType.FullName))
{
label += $" ({typeLabel})";
}
else
{
label = label.Replace(ValueType.FullName, typeLabel);
}
} }
if (Value is UnityEngine.Object unityObj && !label.Contains(unityObj.name)) return m_btnLabel = label;
{
label = unityObj.name + " | " + label;
}
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(width - 15) }))
{
WindowManager.InspectObject(Value, out bool _);
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
} }
} }
} }

View File

@ -21,6 +21,8 @@ namespace Explorer
{ {
base.UpdateValue(); base.UpdateValue();
if (Value == null) return;
var color = (Color)Value; var color = (Color)Value;
r = color.r.ToString(); r = color.r.ToString();
@ -49,10 +51,10 @@ namespace Explorer
} }
} }
var c = (Color)Value; //var c = (Color)Value;
GUI.color = c; //GUI.color = c;
GUILayout.Label($"<color=#2df7b2>Color:</color> {c.ToString()}", null); GUILayout.Label($"<color=#2df7b2>Color:</color> {((Color)Value).ToString()}", null);
GUI.color = Color.white; //GUI.color = Color.white;
if (CanWrite && IsExpanded) if (CanWrite && IsExpanded)
{ {
@ -61,32 +63,32 @@ namespace Explorer
var whitespace = CalcWhitespace(window); var whitespace = CalcWhitespace(window);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("R:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("R:", new GUILayoutOption[] { GUILayout.Width(30) });
r = GUILayout.TextField(r, new GUILayoutOption[] { GUILayout.Width(120) }); r = GUILayout.TextField(r, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("G:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("G:", new GUILayoutOption[] { GUILayout.Width(30) });
g = GUILayout.TextField(g, new GUILayoutOption[] { GUILayout.Width(120) }); g = GUILayout.TextField(g, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("B:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("B:", new GUILayoutOption[] { GUILayout.Width(30) });
b = GUILayout.TextField(b, new GUILayoutOption[] { GUILayout.Width(120) }); b = GUILayout.TextField(b, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("A:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("A:", new GUILayoutOption[] { GUILayout.Width(30) });
a = GUILayout.TextField(a, new GUILayoutOption[] { GUILayout.Width(120) }); a = GUILayout.TextField(a, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
// draw set value button // draw set value button
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) })) if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{ {
SetValueFromInput(); SetValueFromInput();
@ -104,7 +106,7 @@ namespace Explorer
&& float.TryParse(b, out float fB) && float.TryParse(b, out float fB)
&& float.TryParse(a, out float fA)) && float.TryParse(a, out float fA))
{ {
Value = new Color(fR, fB, fG, fA); Value = new Color(fR, fG, fB, fA);
SetValue(); SetValue();
} }
} }

View File

@ -11,23 +11,30 @@ namespace Explorer
{ {
public class CacheEnum : CacheObjectBase public class CacheEnum : CacheObjectBase
{ {
public Type EnumType; // public Type EnumType;
public string[] EnumNames; public string[] EnumNames;
public override void Init() public override void Init()
{ {
try if (ValueType == null && Value != null)
{ {
EnumType = Value.GetType(); ValueType = Value.GetType();
}
catch
{
EnumType = (MemInfo as FieldInfo)?.FieldType ?? (MemInfo as PropertyInfo).PropertyType;
} }
if (EnumType != null) if (ValueType != null)
{ {
EnumNames = Enum.GetNames(EnumType); // 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();
} }
else else
{ {
@ -41,12 +48,12 @@ namespace Explorer
{ {
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) })) if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) }))
{ {
SetEnum(ref Value, -1); SetEnum(-1);
SetValue(); SetValue();
} }
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) })) if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) }))
{ {
SetEnum(ref Value, 1); SetEnum(1);
SetValue(); SetValue();
} }
} }
@ -54,15 +61,15 @@ namespace Explorer
GUILayout.Label(Value.ToString() + "<color=#2df7b2><i> (" + ValueType + ")</i></color>", null); GUILayout.Label(Value.ToString() + "<color=#2df7b2><i> (" + ValueType + ")</i></color>", null);
} }
public void SetEnum(ref object value, int change) public void SetEnum(int change)
{ {
var names = EnumNames.ToList(); var names = EnumNames.ToList();
int newindex = names.IndexOf(value.ToString()) + change; int newindex = names.IndexOf(Value.ToString()) + change;
if ((change < 0 && newindex >= 0) || (change > 0 && newindex < names.Count)) if (newindex >= 0 && newindex < names.Count)
{ {
value = Enum.Parse(EnumType, names[newindex]); Value = Enum.Parse(ValueType, EnumNames[newindex]);
} }
} }
} }

View File

@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine;
namespace Explorer
{
public class CacheEnumFlags : CacheObjectBase, IExpandHeight
{
public string[] EnumNames = new string[0];
public bool[] m_enabledFlags = new bool[0];
public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f;
public override void Init()
{
base.Init();
if (ValueType == null && Value != null)
{
ValueType = Value.GetType();
}
if (ValueType != null)
{
EnumNames = Enum.GetNames(ValueType);
m_enabledFlags = new bool[EnumNames.Length];
UpdateValue();
}
else
{
ReflectionException = "Unknown, could not get Enum names.";
}
}
public void SetFlagsFromInput()
{
string val = "";
for (int i = 0; i < EnumNames.Length; i++)
{
if (m_enabledFlags[i])
{
if (val != "") val += ", ";
val += EnumNames[i];
}
}
Value = Enum.Parse(ValueType, val);
SetValue();
}
public override void UpdateValue()
{
base.UpdateValue();
try
{
var enabledNames = Value.ToString().Split(',').Select(it => it.Trim());
for (int i = 0; i < EnumNames.Length; i++)
{
m_enabledFlags[i] = enabledNames.Contains(EnumNames[i]);
}
}
catch (Exception e)
{
MelonLogger.Log(e.ToString());
}
}
public override void DrawValue(Rect window, float width)
{
if (CanWrite)
{
if (!IsExpanded)
{
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = true;
}
}
else
{
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{
IsExpanded = false;
}
}
}
GUILayout.Label(Value.ToString() + "<color=#2df7b2><i> (" + ValueType + ")</i></color>", null);
if (IsExpanded)
{
GUILayout.EndHorizontal();
var whitespace = CalcWhitespace(window);
for (int i = 0; i < EnumNames.Length; i++)
{
GUILayout.BeginHorizontal(null);
GUIUnstrip.Space(whitespace);
m_enabledFlags[i] = GUILayout.Toggle(m_enabledFlags[i], EnumNames[i], null);
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(null);
GUIUnstrip.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{
SetFlagsFromInput();
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
}
}
}
}

View File

@ -1,7 +1,9 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using MelonLoader; using MelonLoader;
using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer
@ -16,6 +18,12 @@ namespace Explorer
public MethodInfo ParseMethod => m_parseMethod ?? (m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) })); public MethodInfo ParseMethod => m_parseMethod ?? (m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) }));
private MethodInfo m_parseMethod; private MethodInfo m_parseMethod;
private bool m_canBitwiseOperate;
private bool m_inBitwiseMode;
private string m_bitwiseOperatorInput = "0";
private string m_bitwiseToString;
//private BitArray m_bitMask; // not needed I think
public override void Init() public override void Init()
{ {
if (ValueType == null) if (ValueType == null)
@ -37,17 +45,31 @@ namespace Explorer
{ {
m_isBool = true; m_isBool = true;
} }
m_canBitwiseOperate = typeof(int).IsAssignableFrom(ValueType);
} }
public override void UpdateValue() public override void UpdateValue()
{ {
base.UpdateValue(); base.UpdateValue();
RefreshToString();
}
public void RefreshToString()
{
m_valueToString = Value?.ToString(); m_valueToString = Value?.ToString();
if (m_inBitwiseMode)
{
var _int = (int)Value;
m_bitwiseToString = Convert.ToString(_int, toBase: 2);
}
} }
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
// bool uses Toggle
if (m_isBool) if (m_isBool)
{ {
var b = (bool)Value; var b = (bool)Value;
@ -65,9 +87,16 @@ namespace Explorer
{ {
GUILayout.Label(label, null); GUILayout.Label(label, null);
} }
return;
} }
else
{ // all other non-bool values use TextField
GUILayout.BeginVertical(null);
GUILayout.BeginHorizontal(null);
// using ValueType.Name instead of ValueTypeName, because we only want the short name. // using ValueType.Name instead of ValueTypeName, because we only want the short name.
GUILayout.Label("<color=#2df7b2><i>" + ValueType.Name + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) }); GUILayout.Label("<color=#2df7b2><i>" + ValueType.Name + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
@ -89,13 +118,98 @@ namespace Explorer
if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) })) if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))
{ {
SetValueFromInput(m_valueToString); SetValueFromInput(m_valueToString);
RefreshToString();
} }
} }
GUILayout.Space(10); if (m_canBitwiseOperate)
{
bool orig = m_inBitwiseMode;
m_inBitwiseMode = GUILayout.Toggle(m_inBitwiseMode, "Bitwise?", null);
if (orig != m_inBitwiseMode)
{
RefreshToString();
} }
} }
GUIUnstrip.Space(10);
GUILayout.EndHorizontal();
if (m_inBitwiseMode)
{
if (CanWrite)
{
GUILayout.BeginHorizontal(null);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("RHS:", new GUILayoutOption[] { GUILayout.Width(35) });
GUI.skin.label.alignment = TextAnchor.UpperLeft;
if (GUILayout.Button("~", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = ~bit;
RefreshToString();
}
}
if (GUILayout.Button("<<", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value << bit;
RefreshToString();
}
}
if (GUILayout.Button(">>", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value >> bit;
RefreshToString();
}
}
if (GUILayout.Button("|", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value | bit;
RefreshToString();
}
}
if (GUILayout.Button("&", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value & bit;
RefreshToString();
}
}
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{
if (int.TryParse(m_bitwiseOperatorInput, out int bit))
{
Value = (int)Value ^ bit;
RefreshToString();
}
}
m_bitwiseOperatorInput = GUILayout.TextField(m_bitwiseOperatorInput, new GUILayoutOption[] { GUILayout.Width(55) });
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(null);
GUILayout.Label($"<color=cyan>Binary:</color>", new GUILayoutOption[] { GUILayout.Width(60) });
GUILayout.TextField(m_bitwiseToString, null);
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
}
public void SetValueFromInput(string valueString) public void SetValueFromInput(string valueString)
{ {
if (MemInfo == null) if (MemInfo == null)
@ -113,6 +227,16 @@ namespace Explorer
try try
{ {
Value = ParseMethod.Invoke(null, new object[] { valueString }); Value = ParseMethod.Invoke(null, new object[] { valueString });
//if (m_inBitwiseMode)
//{
// var method = typeof(Convert).GetMethod($"To{ValueType.Name}", new Type[] { typeof(string), typeof(int) });
// Value = method.Invoke(null, new object[] { valueString, 2 });
//}
//else
//{
// Value = ParseMethod.Invoke(null, new object[] { valueString });
//}
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -20,6 +20,8 @@ namespace Explorer
{ {
base.UpdateValue(); base.UpdateValue();
if (Value == null) return;
var euler = ((Quaternion)Value).eulerAngles; var euler = ((Quaternion)Value).eulerAngles;
x = euler.x.ToString(); x = euler.x.ToString();
@ -56,26 +58,26 @@ namespace Explorer
var whitespace = CalcWhitespace(window); var whitespace = CalcWhitespace(window);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) });
x = GUILayout.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) }); x = GUILayout.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) });
y = GUILayout.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) }); y = GUILayout.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(30) });
z = GUILayout.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) }); z = GUILayout.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
// draw set value button // draw set value button
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) })) if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{ {
SetValueFromInput(); SetValueFromInput();

View File

@ -21,6 +21,8 @@ namespace Explorer
{ {
base.UpdateValue(); base.UpdateValue();
if (Value == null) return;
var rect = (Rect)Value; var rect = (Rect)Value;
x = rect.x.ToString(); x = rect.x.ToString();
@ -58,32 +60,32 @@ namespace Explorer
var whitespace = CalcWhitespace(window); var whitespace = CalcWhitespace(window);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) });
x = GUILayout.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) }); x = GUILayout.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) });
y = GUILayout.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) }); y = GUILayout.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("W:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("W:", new GUILayoutOption[] { GUILayout.Width(30) });
w = GUILayout.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) }); w = GUILayout.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("H:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("H:", new GUILayoutOption[] { GUILayout.Width(30) });
h = GUILayout.TextField(h, new GUILayoutOption[] { GUILayout.Width(120) }); h = GUILayout.TextField(h, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
// draw set value button // draw set value button
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) })) if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{ {
SetValueFromInput(); SetValueFromInput();

View File

@ -24,20 +24,26 @@ namespace Explorer
public override void Init() public override void Init()
{ {
if (Value is Vector2) if (ValueType == null && Value != null)
{
ValueType = Value.GetType();
}
if (ValueType == typeof(Vector2))
{ {
VectorSize = 2; VectorSize = 2;
m_toStringMethod = typeof(Vector2).GetMethod("ToString", new Type[0]);
} }
else if (Value is Vector3) else if (ValueType == typeof(Vector3))
{ {
VectorSize = 3; VectorSize = 3;
m_toStringMethod = typeof(Vector3).GetMethod("ToString", new Type[0]);
} }
else else
{ {
VectorSize = 4; VectorSize = 4;
m_toStringMethod = typeof(Vector4).GetMethod("ToString", new Type[0]);
} }
m_toStringMethod = Value.GetType().GetMethod("ToString", new Type[0]);
} }
public override void UpdateValue() public override void UpdateValue()
@ -94,13 +100,13 @@ namespace Explorer
// always draw x and y // always draw x and y
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("X:", new GUILayoutOption[] { GUILayout.Width(30) });
x = GUILayout.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) }); x = GUILayout.TextField(x, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("Y:", new GUILayoutOption[] { GUILayout.Width(30) });
y = GUILayout.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) }); y = GUILayout.TextField(y, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
@ -109,7 +115,7 @@ namespace Explorer
{ {
// draw z // draw z
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("Z:", new GUILayoutOption[] { GUILayout.Width(30) });
z = GUILayout.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) }); z = GUILayout.TextField(z, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
@ -118,7 +124,7 @@ namespace Explorer
{ {
// draw w // draw w
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
GUILayout.Label("W:", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label("W:", new GUILayoutOption[] { GUILayout.Width(30) });
w = GUILayout.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) }); w = GUILayout.TextField(w, new GUILayoutOption[] { GUILayout.Width(120) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
@ -126,7 +132,7 @@ namespace Explorer
// draw set value button // draw set value button
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUIUnstrip.Space(whitespace);
if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) })) if (GUILayout.Button("<color=lime>Apply</color>", new GUILayoutOption[] { GUILayout.Width(155) }))
{ {
SetValueFromInput(); SetValueFromInput();

View File

@ -1,9 +1,4 @@
using System; using System.IO;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
using UnityEngine; using UnityEngine;
@ -28,25 +23,31 @@ namespace Explorer
Directory.CreateDirectory(EXPLORER_FOLDER); Directory.CreateDirectory(EXPLORER_FOLDER);
} }
if (File.Exists(SETTINGS_PATH)) if (LoadSettings()) return;
{
LoadSettings(false);
}
else
{
Instance = new ModConfig(); Instance = new ModConfig();
SaveSettings(false); SaveSettings();
}
} }
public static void LoadSettings(bool checkExist = true) // returns true if settings successfully loaded
public static bool LoadSettings(bool checkExist = true)
{ {
if (checkExist && !File.Exists(SETTINGS_PATH)) if (checkExist && !File.Exists(SETTINGS_PATH))
return; return false;
var file = File.OpenRead(SETTINGS_PATH); try
{
using (var file = File.OpenRead(SETTINGS_PATH))
{
Instance = (ModConfig)Serializer.Deserialize(file); Instance = (ModConfig)Serializer.Deserialize(file);
file.Close(); }
}
catch
{
return false;
}
return Instance != null;
} }
public static void SaveSettings(bool checkExist = true) public static void SaveSettings(bool checkExist = true)
@ -54,9 +55,10 @@ namespace Explorer
if (checkExist && File.Exists(SETTINGS_PATH)) if (checkExist && File.Exists(SETTINGS_PATH))
File.Delete(SETTINGS_PATH); File.Delete(SETTINGS_PATH);
FileStream file = File.Create(SETTINGS_PATH); using (var file = File.Create(SETTINGS_PATH))
{
Serializer.Serialize(file, Instance); Serializer.Serialize(file, Instance);
file.Close(); }
} }
} }
} }

View File

@ -1,11 +1,4 @@
using System; using MelonLoader;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Harmony;
using MelonLoader;
using UnhollowerBaseLib;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer
@ -13,7 +6,7 @@ namespace Explorer
public class CppExplorer : MelonMod public class CppExplorer : MelonMod
{ {
public const string NAME = "CppExplorer"; public const string NAME = "CppExplorer";
public const string VERSION = "1.6.8"; public const string VERSION = "1.7.5";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.cppexplorer"; public const string GUID = "com.sinai.cppexplorer";
@ -24,30 +17,12 @@ namespace Explorer
get => m_showMenu; get => m_showMenu;
set => SetShowMenu(value); set => SetShowMenu(value);
} }
private static bool m_showMenu; public static bool m_showMenu;
public static bool ForceUnlockMouse
{
get => m_forceUnlock;
set => SetForceUnlock(value);
}
private static bool m_forceUnlock;
private static CursorLockMode m_lastLockMode;
private static bool m_lastVisibleState;
private static bool m_currentlySettingCursor = false;
public static bool ShouldForceMouse => ShowMenu && ForceUnlockMouse;
private static void SetShowMenu(bool show) private static void SetShowMenu(bool show)
{ {
m_showMenu = show; m_showMenu = show;
UpdateCursorControl(); CursorControl.UpdateCursorControl();
}
private static void SetForceUnlock(bool unlock)
{
m_forceUnlock = unlock;
UpdateCursorControl();
} }
public override void OnApplicationStart() public override void OnApplicationStart()
@ -61,14 +36,7 @@ namespace Explorer
new MainMenu(); new MainMenu();
new WindowManager(); new WindowManager();
// Get current cursor state and enable cursor CursorControl.Init();
m_lastLockMode = Cursor.lockState;
m_lastVisibleState = Cursor.visible;
// Enable ShowMenu and ForceUnlockMouse
// (set m_showMenu directly to not call UpdateCursorState twice)
m_showMenu = true;
ForceUnlockMouse = true;
MelonLogger.Log($"CppExplorer {VERSION} initialized."); MelonLogger.Log($"CppExplorer {VERSION} initialized.");
} }
@ -81,7 +49,6 @@ namespace Explorer
public override void OnUpdate() public override void OnUpdate()
{ {
// Check main toggle key input
if (InputHelper.GetKeyDown(ModConfig.Instance.Main_Menu_Toggle)) if (InputHelper.GetKeyDown(ModConfig.Instance.Main_Menu_Toggle))
{ {
ShowMenu = !ShowMenu; ShowMenu = !ShowMenu;
@ -89,15 +56,11 @@ namespace Explorer
if (ShowMenu) if (ShowMenu)
{ {
// Check Force-Unlock input CursorControl.Update();
if (InputHelper.GetKeyDown(KeyCode.LeftAlt)) InspectUnderMouse.Update();
{
ForceUnlockMouse = !ForceUnlockMouse;
}
MainMenu.Instance.Update(); MainMenu.Instance.Update();
WindowManager.Instance.Update(); WindowManager.Instance.Update();
InspectUnderMouse.Update();
} }
} }
@ -105,93 +68,14 @@ namespace Explorer
{ {
if (!ShowMenu) return; if (!ShowMenu) return;
var origSkin = GUI.skin;
GUI.skin = UIStyles.WindowSkin;
MainMenu.Instance.OnGUI(); MainMenu.Instance.OnGUI();
WindowManager.Instance.OnGUI(); WindowManager.Instance.OnGUI();
InspectUnderMouse.OnGUI(); InspectUnderMouse.OnGUI();
}
private static void UpdateCursorControl() GUI.skin = origSkin;
{
m_currentlySettingCursor = true;
if (ShouldForceMouse)
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
else
{
Cursor.lockState = m_lastLockMode;
Cursor.visible = m_lastVisibleState;
}
m_currentlySettingCursor = false;
}
// Force mouse to stay unlocked and visible while UnlockMouse and ShowMenu are true.
// Also keep track of when anything else tries to set Cursor state, this will be the
// value that we set back to when we close the menu or disable force-unlock.
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Setter)]
public class Cursor_set_lockState
{
[HarmonyPrefix]
public static void Prefix(ref CursorLockMode value)
{
if (!m_currentlySettingCursor)
{
m_lastLockMode = value;
if (ShouldForceMouse)
{
value = CursorLockMode.None;
}
}
}
}
[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Setter)]
public class Cursor_set_visible
{
[HarmonyPrefix]
public static void Prefix(ref bool value)
{
if (!m_currentlySettingCursor)
{
m_lastVisibleState = value;
if (ShouldForceMouse)
{
value = true;
}
}
}
}
// Make it appear as though UnlockMouse is disabled to the rest of the application.
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Getter)]
public class Cursor_get_lockState
{
[HarmonyPostfix]
public static void Postfix(ref CursorLockMode __result)
{
if (ShouldForceMouse)
{
__result = m_lastLockMode;
}
}
}
[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Getter)]
public class Cursor_get_visible
{
[HarmonyPostfix]
public static void Postfix(ref bool __result)
{
if (ShouldForceMouse)
{
__result = m_lastVisibleState;
}
}
} }
} }
} }

View File

@ -29,6 +29,10 @@
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath> <HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="Il2CppSystem.Core">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2CppSystem.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="mcs"> <Reference Include="mcs">
<HintPath>..\lib\mcs.dll</HintPath> <HintPath>..\lib\mcs.dll</HintPath>
<Private>True</Private> <Private>True</Private>
@ -79,6 +83,7 @@
<Compile Include="CachedObjects\Struct\CacheEnum.cs" /> <Compile Include="CachedObjects\Struct\CacheEnum.cs" />
<Compile Include="CachedObjects\Object\CacheGameObject.cs" /> <Compile Include="CachedObjects\Object\CacheGameObject.cs" />
<Compile Include="CachedObjects\Object\CacheList.cs" /> <Compile Include="CachedObjects\Object\CacheList.cs" />
<Compile Include="CachedObjects\Struct\CacheEnumFlags.cs" />
<Compile Include="CachedObjects\Struct\CachePrimitive.cs" /> <Compile Include="CachedObjects\Struct\CachePrimitive.cs" />
<Compile Include="CachedObjects\Other\CacheOther.cs" /> <Compile Include="CachedObjects\Other\CacheOther.cs" />
<Compile Include="CachedObjects\Other\CacheMethod.cs" /> <Compile Include="CachedObjects\Other\CacheMethod.cs" />
@ -89,32 +94,34 @@
<Compile Include="CppExplorer.cs" /> <Compile Include="CppExplorer.cs" />
<Compile Include="Extensions\ReflectionExtensions.cs" /> <Compile Include="Extensions\ReflectionExtensions.cs" />
<Compile Include="Helpers\InputHelper.cs" /> <Compile Include="Helpers\InputHelper.cs" />
<Compile Include="Menu\CursorControl.cs" />
<Compile Include="Tests\TestClass.cs" /> <Compile Include="Tests\TestClass.cs" />
<Compile Include="UnstripFixes\GUIUnstrip.cs" /> <Compile Include="UnstripFixes\GUIUnstrip.cs" />
<Compile Include="UnstripFixes\LayoutUtilityUnstrip.cs" />
<Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" /> <Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" />
<Compile Include="Extensions\UnityExtensions.cs" /> <Compile Include="Extensions\UnityExtensions.cs" />
<Compile Include="Helpers\PageHelper.cs" /> <Compile Include="Helpers\PageHelper.cs" />
<Compile Include="Helpers\ReflectionHelpers.cs" /> <Compile Include="Helpers\ReflectionHelpers.cs" />
<Compile Include="Helpers\UIHelpers.cs" /> <Compile Include="Menu\UIHelpers.cs" />
<Compile Include="Helpers\UnityHelpers.cs" /> <Compile Include="Helpers\UnityHelpers.cs" />
<Compile Include="MainMenu\InspectUnderMouse.cs" /> <Compile Include="Menu\InspectUnderMouse.cs" />
<Compile Include="CachedObjects\CacheObjectBase.cs" /> <Compile Include="CachedObjects\CacheObjectBase.cs" />
<Compile Include="UnstripFixes\SliderHandlerUnstrip.cs" /> <Compile Include="UnstripFixes\SliderHandlerUnstrip.cs" />
<Compile Include="UnstripFixes\UnstripExtensions.cs" /> <Compile Include="UnstripFixes\UnstripExtensions.cs" />
<Compile Include="Windows\ResizeDrag.cs" /> <Compile Include="Menu\ResizeDrag.cs" />
<Compile Include="Windows\TabViewWindow.cs" /> <Compile Include="Menu\Windows\TabViewWindow.cs" />
<Compile Include="Windows\UIWindow.cs" /> <Compile Include="Menu\Windows\UIWindow.cs" />
<Compile Include="MainMenu\Pages\ConsolePage.cs" /> <Compile Include="Menu\MainMenu\Pages\ConsolePage.cs" />
<Compile Include="MainMenu\Pages\Console\REPL.cs" /> <Compile Include="Menu\MainMenu\Pages\Console\REPL.cs" />
<Compile Include="MainMenu\Pages\Console\REPLHelper.cs" /> <Compile Include="Menu\MainMenu\Pages\Console\REPLHelper.cs" />
<Compile Include="MainMenu\Pages\WindowPage.cs" /> <Compile Include="Menu\MainMenu\Pages\WindowPage.cs" />
<Compile Include="Windows\WindowManager.cs" /> <Compile Include="Menu\Windows\WindowManager.cs" />
<Compile Include="MainMenu\MainMenu.cs" /> <Compile Include="Menu\MainMenu\MainMenu.cs" />
<Compile Include="Windows\GameObjectWindow.cs" /> <Compile Include="Menu\Windows\GameObjectWindow.cs" />
<Compile Include="Windows\ReflectionWindow.cs" /> <Compile Include="Menu\Windows\ReflectionWindow.cs" />
<Compile Include="MainMenu\Pages\ScenePage.cs" /> <Compile Include="Menu\MainMenu\Pages\ScenePage.cs" />
<Compile Include="MainMenu\Pages\SearchPage.cs" /> <Compile Include="Menu\MainMenu\Pages\SearchPage.cs" />
<Compile Include="Helpers\UIStyles.cs" /> <Compile Include="Menu\UIStyles.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View File

@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
namespace Explorer namespace Explorer

View File

@ -1,10 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnhollowerBaseLib;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,12 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; using System.Reflection;
using UnityEngine;
using MelonLoader; using MelonLoader;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -88,7 +83,8 @@ namespace Explorer
{ {
MelonLogger.Log("UnityEngine.Input is null, trying to load manually...."); MelonLogger.Log("UnityEngine.Input is null, trying to load manually....");
if ((TryLoad("UnityEngine.InputLegacyModule.dll") || TryLoad("UnityEngine.CoreModule.dll")) && Input != null) if ((ReflectionHelpers.LoadModule("UnityEngine.InputLegacyModule.dll") || ReflectionHelpers.LoadModule("UnityEngine.CoreModule.dll"))
&& Input != null)
{ {
MelonLogger.Log("Ok!"); MelonLogger.Log("Ok!");
return true; return true;
@ -98,23 +94,6 @@ namespace Explorer
MelonLogger.Log("Could not load Input module!"); MelonLogger.Log("Could not load Input module!");
return false; return false;
} }
bool TryLoad(string module)
{
var path = $@"MelonLoader\Managed\{module}";
if (!File.Exists(path)) return false;
try
{
Assembly.Load(File.ReadAllBytes(path));
return true;
}
catch (Exception e)
{
MelonLogger.Log(e.GetType() + ", " + e.Message);
return false;
}
}
} }
} }
} }

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,8 +1,9 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.IO;
using System.Reflection; using System.Reflection;
using MelonLoader;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
@ -21,29 +22,34 @@ namespace Explorer
public static ILType ComponentType => Il2CppType.Of<Component>(); public static ILType ComponentType => Il2CppType.Of<Component>();
public static ILType BehaviourType => Il2CppType.Of<Behaviour>(); public static ILType BehaviourType => Il2CppType.Of<Behaviour>();
private static readonly MethodInfo m_tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast"); private static readonly MethodInfo tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast");
private static readonly Dictionary<Type, MethodInfo> cachedTryCastMethods = new Dictionary<Type, MethodInfo>();
public static object Il2CppCast(object obj, Type castTo) public static object Il2CppCast(object obj, Type castTo)
{ {
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj; if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj;
return m_tryCastMethodInfo if (!cachedTryCastMethods.ContainsKey(castTo))
.MakeGenericMethod(castTo) {
.Invoke(obj, null); cachedTryCastMethods.Add(castTo, tryCastMethodInfo.MakeGenericMethod(castTo));
}
return cachedTryCastMethods[castTo].Invoke(obj, null);
} }
public static bool IsEnumerable(Type t) public static bool IsEnumerable(Type t)
{ {
return typeof(IEnumerable).IsAssignableFrom(t); return typeof(IEnumerable).IsAssignableFrom(t) || IsCppEnumerable(t);
} }
// Only Il2Cpp List needs this check. C# List is IEnumerable. // Checks for Il2Cpp List or HashSet.
public static bool IsCppList(Type t) public static bool IsCppEnumerable(Type t)
{ {
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g) if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
{ {
return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g) return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g)
|| typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g); || typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g)
|| typeof(Il2CppSystem.Collections.Generic.HashSet<>).IsAssignableFrom(g);
} }
else else
{ {
@ -65,7 +71,8 @@ namespace Explorer
} }
else else
{ {
return typeof(Il2CppSystem.Collections.IDictionary).IsAssignableFrom(t); return typeof(Il2CppSystem.Collections.IDictionary).IsAssignableFrom(t)
|| typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(t);
} }
} }
@ -89,18 +96,19 @@ namespace Explorer
{ {
if (obj == null) return null; if (obj == null) return null;
// Need to use GetIl2CppType for Il2CppSystem Objects
if (obj is Il2CppSystem.Object ilObject) if (obj is Il2CppSystem.Object ilObject)
{ {
var ilTypeName = ilObject.GetIl2CppType().AssemblyQualifiedName; // Prevent weird behaviour when inspecting an Il2CppSystem.Type object.
if (ilObject is ILType)
if (Type.GetType(ilTypeName) is Type t && !t.FullName.Contains("System.RuntimeType"))
{ {
return t; return typeof(ILType);
} }
return ilObject.GetType(); return Type.GetType(ilObject.GetIl2CppType().AssemblyQualifiedName) ?? obj.GetType();
} }
// It's a normal object, this is fine
return obj.GetType(); return obj.GetType();
} }
@ -109,17 +117,33 @@ namespace Explorer
var list = new List<Type>(); var list = new List<Type>();
var type = GetActualType(obj); var type = GetActualType(obj);
list.Add(type);
while (type.BaseType != null) while (type != null)
{ {
type = type.BaseType;
list.Add(type); list.Add(type);
type = type.BaseType;
} }
return list.ToArray(); return list.ToArray();
} }
public static bool LoadModule(string module)
{
var path = $@"MelonLoader\Managed\{module}";
if (!File.Exists(path)) return false;
try
{
Assembly.Load(File.ReadAllBytes(path));
return true;
}
catch (Exception e)
{
MelonLogger.Log(e.GetType() + ", " + e.Message);
return false;
}
}
public static string ExceptionToString(Exception e) public static string ExceptionToString(Exception e)
{ {
if (IsFailedGeneric(e)) if (IsFailedGeneric(e))

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

178
src/Menu/CursorControl.cs Normal file
View File

@ -0,0 +1,178 @@
using System;
using Harmony;
using MelonLoader;
using UnityEngine;
namespace Explorer
{
public class CursorControl
{
public static bool ForceUnlockMouse
{
get => m_forceUnlock;
set => SetForceUnlock(value);
}
private static bool m_forceUnlock;
private static CursorLockMode m_lastLockMode;
private static bool m_lastVisibleState;
private static bool m_currentlySettingCursor = false;
public static bool ShouldForceMouse => CppExplorer.ShowMenu && ForceUnlockMouse;
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)
{
MelonLogger.Log("Trying to manually load Cursor module...");
if (ReflectionHelpers.LoadModule("UnityEngine.CoreModule") && CursorType != null)
{
MelonLogger.Log("Ok!");
}
else
{
throw new Exception("Could not load UnityEngine.Cursor module!");
}
}
// Get current cursor state and enable cursor
m_lastLockMode = Cursor.lockState;
m_lastVisibleState = Cursor.visible;
// Setup Harmony Patches
TryPatch("lockState", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Prefix_set_lockState))), true);
TryPatch("lockState", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Postfix_get_lockState))), false);
TryPatch("visible", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Prefix_set_visible))), true);
TryPatch("visible", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Postfix_get_visible))), false);
}
catch (Exception e)
{
MelonLogger.Log($"Exception on CursorControl.Init! {e.GetType()}, {e.Message}");
}
// Enable ShowMenu and ForceUnlockMouse
// (set m_showMenu directly to not call UpdateCursorState twice)
CppExplorer.m_showMenu = true;
ForceUnlockMouse = true;
}
private static void TryPatch(string property, HarmonyMethod patch, bool setter)
{
try
{
var harmony = CppExplorer.Instance.harmonyInstance;
var prop = typeof(Cursor).GetProperty(property);
if (setter)
{
// setter is prefix
harmony.Patch(prop.GetSetMethod(), patch);
}
else
{
// getter is postfix
harmony.Patch(prop.GetGetMethod(), null, patch);
}
}
catch (Exception e)
{
MelonLogger.Log($"[NON-FATAL] Couldn't patch a method: {e.Message}");
}
}
private static void SetForceUnlock(bool unlock)
{
m_forceUnlock = unlock;
UpdateCursorControl();
}
public static void Update()
{
// Check Force-Unlock input
if (InputHelper.GetKeyDown(KeyCode.LeftAlt))
{
ForceUnlockMouse = !ForceUnlockMouse;
}
}
public static void UpdateCursorControl()
{
try
{
m_currentlySettingCursor = true;
if (ShouldForceMouse)
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
else
{
Cursor.lockState = m_lastLockMode;
Cursor.visible = m_lastVisibleState;
}
m_currentlySettingCursor = false;
}
catch (Exception e)
{
MelonLogger.Log($"Exception setting Cursor state: {e.GetType()}, {e.Message}");
}
}
// Force mouse to stay unlocked and visible while UnlockMouse and ShowMenu are true.
// Also keep track of when anything else tries to set Cursor state, this will be the
// value that we set back to when we close the menu or disable force-unlock.
[HarmonyPrefix]
public static void Prefix_set_lockState(ref CursorLockMode value)
{
if (!m_currentlySettingCursor)
{
m_lastLockMode = value;
if (ShouldForceMouse)
{
value = CursorLockMode.None;
}
}
}
[HarmonyPrefix]
public static void Prefix_set_visible(ref bool value)
{
if (!m_currentlySettingCursor)
{
m_lastVisibleState = value;
if (ShouldForceMouse)
{
value = true;
}
}
}
[HarmonyPrefix]
public static void Postfix_get_lockState(ref CursorLockMode __result)
{
if (ShouldForceMouse)
{
__result = m_lastLockMode;
}
}
[HarmonyPrefix]
public static void Postfix_get_visible(ref bool __result)
{
if (ShouldForceMouse)
{
__result = m_lastVisibleState;
}
}
}
}

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -3,8 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using MelonLoader; using MelonLoader;
namespace Explorer namespace Explorer
@ -21,16 +19,17 @@ namespace Explorer
Pages.Add(new SearchPage()); Pages.Add(new SearchPage());
Pages.Add(new ConsolePage()); Pages.Add(new ConsolePage());
foreach (var page in Pages) for (int i = 0; i < Pages.Count; i++)
{ {
var page = Pages[i];
page.Init(); page.Init();
} }
} }
public const int MainWindowID = 5000; public const int MainWindowID = 5000;
public static Rect MainRect = new Rect(new Vector2(5,5), ModConfig.Instance.Default_Window_Size); public static Rect MainRect = new Rect(5,5, ModConfig.Instance.Default_Window_Size.x,ModConfig.Instance.Default_Window_Size.y);
private static readonly List<WindowPage> Pages = new List<WindowPage>(); public static readonly List<WindowPage> Pages = new List<WindowPage>();
private static int m_currentPage = 0; private static int m_currentPage = 0;
public static void SetCurrentPage(int index) public static void SetCurrentPage(int index)
@ -52,12 +51,7 @@ namespace Explorer
public void OnGUI() public void OnGUI()
{ {
var origSkin = GUI.skin;
GUI.skin = UIStyles.WindowSkin;
MainRect = GUI.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, CppExplorer.NAME); MainRect = GUI.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, CppExplorer.NAME);
GUI.skin = origSkin;
} }
private void MainWindow(int id) private void MainWindow(int id)
@ -70,7 +64,7 @@ namespace Explorer
return; return;
} }
GUILayout.BeginArea(new Rect(5, 25, MainRect.width - 10, MainRect.height - 35), GUI.skin.box); GUIUnstrip.BeginArea(new Rect(5, 25, MainRect.width - 10, MainRect.height - 35), GUI.skin.box);
MainHeader(); MainHeader();
@ -84,7 +78,7 @@ namespace Explorer
MainRect = ResizeDrag.ResizeWindow(MainRect, MainWindowID); MainRect = ResizeDrag.ResizeWindow(MainRect, MainWindowID);
GUILayout.EndArea(); GUIUnstrip.EndArea();
} }
private void MainHeader() private void MainHeader()
@ -108,14 +102,16 @@ namespace Explorer
GUI.color = Color.white; GUI.color = Color.white;
InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", null); InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", null);
bool mouseState = CppExplorer.ForceUnlockMouse; bool mouseState = CursorControl.ForceUnlockMouse;
bool setMouse = GUILayout.Toggle(mouseState, "Force Unlock Mouse (Left Alt)", null); bool setMouse = GUILayout.Toggle(mouseState, "Force Unlock Mouse (Left Alt)", null);
if (setMouse != mouseState) CppExplorer.ForceUnlockMouse = setMouse; if (setMouse != mouseState) CursorControl.ForceUnlockMouse = setMouse;
WindowManager.TabView = GUILayout.Toggle(WindowManager.TabView, "Tab View", null); WindowManager.TabView = GUILayout.Toggle(WindowManager.TabView, "Tab View", null);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.Space(10); //GUIUnstrip.Space(10);
GUIUnstrip.Space(10);
GUI.color = Color.white; GUI.color = Color.white;
} }
} }

View File

@ -1,9 +1,4 @@
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Mono.CSharp; using Mono.CSharp;
using UnityEngine; using UnityEngine;
using Attribute = System.Attribute; using Attribute = System.Attribute;

View File

@ -1,8 +1,5 @@
using System.Collections; using System;
//using Il2CppSystem;
using MelonLoader;
using UnityEngine; using UnityEngine;
using System;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
namespace Explorer namespace Explorer

View File

@ -1,14 +1,11 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using System.Reflection;
using Mono.CSharp;
using System.IO; using System.IO;
using System.Reflection;
using System.Text;
using MelonLoader; using MelonLoader;
using Mono.CSharp;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -43,7 +40,7 @@ namespace Explorer
try try
{ {
MethodInput = @"// This is a basic C# REPL console. MethodInput = @"// This is a basic C# console.
// Some common using directives are added by default, you can add more below. // Some common using directives are added by default, you can add more below.
// If you want to return some output, MelonLogger.Log() it. // If you want to return some output, MelonLogger.Log() it.
@ -58,7 +55,9 @@ MelonLogger.Log(""hello world"");";
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log($"Error setting up console!\r\nMessage: {e.Message}\r\nStack: {e.StackTrace}"); MelonLogger.Log($"Error setting up console!\r\nMessage: {e.Message}");
MainMenu.SetCurrentPage(0);
MainMenu.Pages.Remove(this);
} }
} }
@ -121,7 +120,7 @@ MelonLogger.Log(""hello world"");";
public override void DrawWindow() public override void DrawWindow()
{ {
GUILayout.Label("<b><size=15><color=cyan>C# REPL Console</color></size></b>", null); GUILayout.Label("<b><size=15><color=cyan>C# Console</color></size></b>", null);
GUI.skin.label.alignment = TextAnchor.UpperLeft; GUI.skin.label.alignment = TextAnchor.UpperLeft;

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using MelonLoader; using MelonLoader;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
@ -244,7 +243,7 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.Space(5); GUIUnstrip.Space(5);
} }
private void SceneChangeButtons() private void SceneChangeButtons()

View File

@ -2,10 +2,9 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using UnityEngine;
using System.Reflection; using System.Reflection;
using MelonLoader; using MelonLoader;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -428,7 +427,7 @@ namespace Explorer
{ {
var t = ReflectionHelpers.GetActualType(obj); var t = ReflectionHelpers.GetActualType(obj);
if (!FilterName(t.FullName) || ReflectionHelpers.IsEnumerable(t) || ReflectionHelpers.IsCppList(t)) if (!FilterName(t.FullName) || ReflectionHelpers.IsEnumerable(t))
{ {
continue; continue;
} }

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {

View File

@ -1,11 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using MelonLoader; using MelonLoader;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -32,12 +28,13 @@ namespace Explorer
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) }); GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) });
//var r = GUILayoutUtility.GetLastRect(); //var r = GUILayoutUtility.GetLastRect();
var r = GUIUnstrip.GetLastRect(); var r = LayoutUtilityUnstrip.GetLastRect();
var mousePos = InputHelper.mousePosition; var mousePos = InputHelper.mousePosition;
Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y)); try
{
var mouse = GUIUtility.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y));
if (r.Contains(mouse) && InputHelper.GetMouseButtonDown(0)) if (r.Contains(mouse) && InputHelper.GetMouseButtonDown(0))
{ {
isResizing = true; isResizing = true;
@ -56,6 +53,12 @@ namespace Explorer
_rect.xMax = Mathf.Min(Screen.width, _rect.xMax); // modifying xMax affects width, not x _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 _rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y
} }
}
catch
{
// throw safe Managed exception
throw new Exception("");
}
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
@ -68,7 +71,7 @@ namespace Explorer
{ {
RESIZE_FAILED = true; RESIZE_FAILED = true;
MelonLogger.Log("Exception on GuiResize: " + e.GetType() + ", " + e.Message); MelonLogger.Log("Exception on GuiResize: " + e.GetType() + ", " + e.Message);
MelonLogger.Log(e.StackTrace); //MelonLogger.Log(e.StackTrace);
return origRect; return origRect;
} }
@ -82,20 +85,20 @@ namespace Explorer
GUI.skin.label.alignment = TextAnchor.MiddleRight; GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("<color=cyan>Width:</color>", new GUILayoutOption[] { GUILayout.Width(60) }); GUILayout.Label("<color=cyan>Width:</color>", new GUILayoutOption[] { GUILayout.Width(60) });
if (GUILayout.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) })) if (GUIUnstrip.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) }))
{ {
_rect.width -= 5f; _rect.width -= 5f;
} }
if (GUILayout.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) })) if (GUIUnstrip.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) }))
{ {
_rect.width += 5f; _rect.width += 5f;
} }
GUILayout.Label("<color=cyan>Height:</color>", new GUILayoutOption[] { GUILayout.Width(60) }); GUILayout.Label("<color=cyan>Height:</color>", new GUILayoutOption[] { GUILayout.Width(60) });
if (GUILayout.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) })) if (GUIUnstrip.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) }))
{ {
_rect.height -= 5f; _rect.height -= 5f;
} }
if (GUILayout.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) })) if (GUIUnstrip.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) }))
{ {
_rect.height += 5f; _rect.height += 5f;
} }

View File

@ -1,10 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;

View File

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

View File

@ -1,21 +1,20 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using MelonLoader; using MelonLoader;
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement;
namespace Explorer namespace Explorer
{ {
public class GameObjectWindow : UIWindow public class GameObjectWindow : UIWindow
{ {
public override string Title => WindowManager.TabView public override string Title => WindowManager.TabView
? $"<color=cyan>[G]</color> {m_object.name}" ? $"<color=cyan>[G]</color> {TargetGO.name}"
: $"GameObject Inspector ({m_object.name})"; : $"GameObject Inspector ({TargetGO.name})";
public GameObject m_object; public GameObject TargetGO;
private static bool m_hideControls;
// gui element holders // gui element holders
private string m_name; private string m_name;
@ -23,11 +22,11 @@ namespace Explorer
private Transform[] m_children; private Transform[] m_children;
private Vector2 m_transformScroll = Vector2.zero; private Vector2 m_transformScroll = Vector2.zero;
private PageHelper ChildPages = new PageHelper(); private readonly PageHelper ChildPages = new PageHelper();
private Component[] m_components; private Component[] m_components;
private Vector2 m_compScroll = Vector2.zero; private Vector2 m_compScroll = Vector2.zero;
private PageHelper CompPages = new PageHelper(); private readonly PageHelper CompPages = new PageHelper();
private readonly Vector3[] m_cachedInput = new Vector3[3]; private readonly Vector3[] m_cachedInput = new Vector3[3];
private float m_translateAmount = 0.3f; private float m_translateAmount = 0.3f;
@ -42,7 +41,7 @@ namespace Explorer
private bool m_localContext; private bool m_localContext;
private readonly List<Component> m_cachedDestroyList = new List<Component>(); private readonly List<Component> m_cachedDestroyList = new List<Component>();
//private string m_addComponentInput = ""; private string m_addComponentInput = "";
private string m_setParentInput = "Enter a GameObject name or path"; private string m_setParentInput = "Enter a GameObject name or path";
@ -52,12 +51,12 @@ namespace Explorer
if (targetType == typeof(GameObject)) if (targetType == typeof(GameObject))
{ {
m_object = Target as GameObject; TargetGO = Target as GameObject;
return true; return true;
} }
else if (targetType == typeof(Transform)) else if (targetType == typeof(Transform))
{ {
m_object = (Target as Transform).gameObject; TargetGO = (Target as Transform).gameObject;
return true; return true;
} }
@ -73,10 +72,10 @@ namespace Explorer
return; return;
} }
m_name = m_object.name; m_name = TargetGO.name;
m_scene = string.IsNullOrEmpty(m_object.scene.name) m_scene = string.IsNullOrEmpty(TargetGO.scene.name)
? "None (Asset/Resource)" ? "None (Asset/Resource)"
: m_object.scene.name; : TargetGO.scene.name;
CacheTransformValues(); CacheTransformValues();
@ -87,15 +86,15 @@ namespace Explorer
{ {
if (m_localContext) if (m_localContext)
{ {
m_cachedInput[0] = m_object.transform.localPosition; m_cachedInput[0] = TargetGO.transform.localPosition;
m_cachedInput[1] = m_object.transform.localEulerAngles; m_cachedInput[1] = TargetGO.transform.localEulerAngles;
} }
else else
{ {
m_cachedInput[0] = m_object.transform.position; m_cachedInput[0] = TargetGO.transform.position;
m_cachedInput[1] = m_object.transform.eulerAngles; m_cachedInput[1] = TargetGO.transform.eulerAngles;
} }
m_cachedInput[2] = m_object.transform.localScale; m_cachedInput[2] = TargetGO.transform.localScale;
} }
public override void Update() public override void Update()
@ -118,7 +117,7 @@ namespace Explorer
} }
} }
if (!m_object && !GetObjectAsGameObject()) if (!TargetGO && !GetObjectAsGameObject())
{ {
throw new Exception("Object is null!"); throw new Exception("Object is null!");
} }
@ -127,22 +126,22 @@ namespace Explorer
{ {
if (m_localContext) if (m_localContext)
{ {
m_object.transform.localPosition = m_frozenPosition; TargetGO.transform.localPosition = m_frozenPosition;
m_object.transform.localRotation = m_frozenRotation; TargetGO.transform.localRotation = m_frozenRotation;
} }
else else
{ {
m_object.transform.position = m_frozenPosition; TargetGO.transform.position = m_frozenPosition;
m_object.transform.rotation = m_frozenRotation; TargetGO.transform.rotation = m_frozenRotation;
} }
m_object.transform.localScale = m_frozenScale; TargetGO.transform.localScale = m_frozenScale;
} }
// update child objects // update child objects
var childList = new List<Transform>(); var childList = new List<Transform>();
for (int i = 0; i < m_object.transform.childCount; i++) for (int i = 0; i < TargetGO.transform.childCount; i++)
{ {
childList.Add(m_object.transform.GetChild(i)); childList.Add(TargetGO.transform.GetChild(i));
} }
childList.Sort((a, b) => b.childCount.CompareTo(a.childCount)); childList.Sort((a, b) => b.childCount.CompareTo(a.childCount));
m_children = childList.ToArray(); m_children = childList.ToArray();
@ -151,7 +150,7 @@ namespace Explorer
// update components // update components
var compList = new Il2CppSystem.Collections.Generic.List<Component>(); var compList = new Il2CppSystem.Collections.Generic.List<Component>();
m_object.GetComponentsInternal(ReflectionHelpers.ComponentType, true, false, true, false, compList); TargetGO.GetComponentsInternal(ReflectionHelpers.ComponentType, true, false, true, false, compList);
m_components = compList.ToArray(); m_components = compList.ToArray();
@ -182,7 +181,7 @@ namespace Explorer
private void ReflectObject(Il2CppSystem.Object obj) private void ReflectObject(Il2CppSystem.Object obj)
{ {
var window = WindowManager.InspectObject(obj, out bool created); var window = WindowManager.InspectObject(obj, out bool created, true);
if (created) if (created)
{ {
@ -210,7 +209,7 @@ namespace Explorer
if (!WindowManager.TabView) if (!WindowManager.TabView)
{ {
Header(); Header();
GUILayout.BeginArea(new Rect(5, 25, rect.width - 10, rect.height - 35), GUI.skin.box); GUIUnstrip.BeginArea(new Rect(5, 25, rect.width - 10, rect.height - 35), GUI.skin.box);
} }
scroll = GUIUnstrip.BeginScrollView(scroll); scroll = GUIUnstrip.BeginScrollView(scroll);
@ -221,7 +220,7 @@ namespace Explorer
{ {
if (GUILayout.Button("<color=#00FF00>Send to Scene View</color>", new GUILayoutOption[] { GUILayout.Width(150) })) if (GUILayout.Button("<color=#00FF00>Send to Scene View</color>", new GUILayoutOption[] { GUILayout.Width(150) }))
{ {
ScenePage.Instance.SetTransformTarget(m_object.transform); ScenePage.Instance.SetTransformTarget(TargetGO.transform);
MainMenu.SetCurrentPage(0); MainMenu.SetCurrentPage(0);
} }
} }
@ -233,12 +232,12 @@ namespace Explorer
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) }); GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) });
string pathlabel = m_object.transform.GetGameObjectPath(); string pathlabel = TargetGO.transform.GetGameObjectPath();
if (m_object.transform.parent != null) if (TargetGO.transform.parent != null)
{ {
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) })) if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{ {
InspectGameObject(m_object.transform.parent); InspectGameObject(TargetGO.transform.parent);
} }
} }
GUILayout.TextArea(pathlabel, null); GUILayout.TextArea(pathlabel, null);
@ -270,7 +269,7 @@ namespace Explorer
{ {
m_rect = ResizeDrag.ResizeWindow(rect, windowID); m_rect = ResizeDrag.ResizeWindow(rect, windowID);
GUILayout.EndArea(); GUIUnstrip.EndArea();
} }
} }
catch (Exception e) catch (Exception e)
@ -360,6 +359,29 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
var width = m_rect.width / 2 - 115f;
m_addComponentInput = GUILayout.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(width) });
if (GUILayout.Button("Add Comp", null))
{
if (ReflectionHelpers.GetTypeByName(m_addComponentInput) is Type compType)
{
if (typeof(Component).IsAssignableFrom(compType))
{
TargetGO.AddComponent(Il2CppType.From(compType));
}
else
{
MelonLogger.LogWarning($"Type '{compType.Name}' is not assignable from Component!");
}
}
else
{
MelonLogger.LogWarning($"Could not find a type by the name of '{m_addComponentInput}'!");
}
}
GUILayout.EndHorizontal();
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (m_cachedDestroyList.Count > 0) if (m_cachedDestroyList.Count > 0)
{ {
@ -385,7 +407,7 @@ namespace Explorer
} }
else else
{ {
GUILayout.Space(26); GUIUnstrip.Space(26);
} }
if (GUILayout.Button("<color=cyan>" + ilType.Name + "</color>", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 100) })) if (GUILayout.Button("<color=cyan>" + ilType.Name + "</color>", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 100) }))
{ {
@ -439,21 +461,41 @@ namespace Explorer
private void GameObjectControls() private void GameObjectControls()
{ {
if (m_hideControls)
{
GUILayout.BeginHorizontal(null);
GUILayout.Label("<b><size=15>GameObject Controls</size></b>", new GUILayoutOption[] { GUILayout.Width(200) });
if (GUILayout.Button("^ Show ^", new GUILayoutOption[] { GUILayout.Width(75) }))
{
m_hideControls = false;
}
GUILayout.EndHorizontal();
return;
}
GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(520) }); GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(520) });
GUILayout.Label("<b><size=15>GameObject Controls</size></b>", null);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
bool m_active = m_object.activeSelf; 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) }))
{
m_hideControls = true;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
bool m_active = TargetGO.activeSelf;
m_active = GUILayout.Toggle(m_active, (m_active ? "<color=lime>Enabled " : "<color=red>Disabled") + "</color>", m_active = GUILayout.Toggle(m_active, (m_active ? "<color=lime>Enabled " : "<color=red>Disabled") + "</color>",
new GUILayoutOption[] { GUILayout.Width(80) }); new GUILayoutOption[] { GUILayout.Width(80) });
if (m_object.activeSelf != m_active) { m_object.SetActive(m_active); } if (TargetGO.activeSelf != m_active) { TargetGO.SetActive(m_active); }
UIHelpers.InstantiateButton(m_object, 100); UIHelpers.InstantiateButton(TargetGO, 100);
if (GUILayout.Button("Set DontDestroyOnLoad", new GUILayoutOption[] { GUILayout.Width(170) })) if (GUILayout.Button("Set DontDestroyOnLoad", new GUILayoutOption[] { GUILayout.Width(170) }))
{ {
GameObject.DontDestroyOnLoad(m_object); GameObject.DontDestroyOnLoad(TargetGO);
m_object.hideFlags |= HideFlags.DontUnloadUnusedAsset; TargetGO.hideFlags |= HideFlags.DontUnloadUnusedAsset;
} }
var lbl = m_freeze ? "<color=lime>Unfreeze</color>" : "<color=orange>Freeze Pos/Rot</color>"; var lbl = m_freeze ? "<color=lime>Unfreeze</color>" : "<color=orange>Freeze Pos/Rot</color>";
@ -474,7 +516,7 @@ namespace Explorer
{ {
if (GameObject.Find(m_setParentInput) is GameObject newparent) if (GameObject.Find(m_setParentInput) is GameObject newparent)
{ {
m_object.transform.parent = newparent.transform; TargetGO.transform.parent = newparent.transform;
} }
else else
{ {
@ -484,7 +526,7 @@ namespace Explorer
if (GUILayout.Button("Detach from parent", new GUILayoutOption[] { GUILayout.Width(160) })) if (GUILayout.Button("Detach from parent", new GUILayoutOption[] { GUILayout.Width(160) }))
{ {
m_object.transform.parent = null; TargetGO.transform.parent = null;
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
@ -499,15 +541,15 @@ namespace Explorer
{ {
if (m_localContext) if (m_localContext)
{ {
m_object.transform.localPosition = m_cachedInput[0]; TargetGO.transform.localPosition = m_cachedInput[0];
m_object.transform.localEulerAngles = m_cachedInput[1]; TargetGO.transform.localEulerAngles = m_cachedInput[1];
} }
else else
{ {
m_object.transform.position = m_cachedInput[0]; TargetGO.transform.position = m_cachedInput[0];
m_object.transform.eulerAngles = m_cachedInput[1]; TargetGO.transform.eulerAngles = m_cachedInput[1];
} }
m_object.transform.localScale = m_cachedInput[2]; TargetGO.transform.localScale = m_cachedInput[2];
if (m_freeze) if (m_freeze)
{ {
@ -526,7 +568,7 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
bool b = m_localContext; bool b = m_localContext;
b = GUILayout.Toggle(b, "<color=" + (b ? "lime" : "red") + ">Use local transform values?</color>", null); b = GUILayout.Toggle(b, "<color=" + (b ? "lime" : "orange") + ">Use local transform values?</color>", null);
if (b != m_localContext) if (b != m_localContext)
{ {
m_localContext = b; m_localContext = b;
@ -541,7 +583,7 @@ namespace Explorer
if (GUILayout.Button("<color=red><b>Destroy</b></color>", new GUILayoutOption[] { GUILayout.Width(120) })) if (GUILayout.Button("<color=red><b>Destroy</b></color>", new GUILayoutOption[] { GUILayout.Width(120) }))
{ {
GameObject.Destroy(m_object); GameObject.Destroy(TargetGO);
DestroyWindow(); DestroyWindow();
return; return;
} }
@ -553,21 +595,21 @@ namespace Explorer
{ {
if (m_localContext) if (m_localContext)
{ {
m_frozenPosition = m_object.transform.localPosition; m_frozenPosition = TargetGO.transform.localPosition;
m_frozenRotation = m_object.transform.localRotation; m_frozenRotation = TargetGO.transform.localRotation;
} }
else else
{ {
m_frozenPosition = m_object.transform.position; m_frozenPosition = TargetGO.transform.position;
m_frozenRotation = m_object.transform.rotation; m_frozenRotation = TargetGO.transform.rotation;
} }
m_frozenScale = m_object.transform.localScale; m_frozenScale = TargetGO.transform.localScale;
} }
private void BoolToggle(ref bool value, string message) private void BoolToggle(ref bool value, string message)
{ {
string lbl = "<color="; string lbl = "<color=";
lbl += value ? "lime" : "red"; lbl += value ? "lime" : "orange";
lbl += $">{message}</color>"; lbl += $">{message}</color>";
value = GUILayout.Toggle(value, lbl, null); value = GUILayout.Toggle(value, lbl, null);
@ -586,7 +628,7 @@ namespace Explorer
GUILayout.Label($"<color=cyan><b>{(m_localContext ? "Local " : "")}{mode}</b></color>:", GUILayout.Label($"<color=cyan><b>{(m_localContext ? "Local " : "")}{mode}</b></color>:",
new GUILayoutOption[] { GUILayout.Width(m_localContext ? 110 : 65) }); new GUILayoutOption[] { GUILayout.Width(m_localContext ? 110 : 65) });
var transform = m_object.transform; var transform = TargetGO.transform;
switch (mode) switch (mode)
{ {
case TranslateType.Position: case TranslateType.Position:
@ -639,11 +681,11 @@ namespace Explorer
{ {
f = f2; f = f2;
} }
if (GUILayout.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) })) if (GUIUnstrip.RepeatButton("-", new GUILayoutOption[] { GUILayout.Width(20) }))
{ {
f -= multByTime ? amount * Time.deltaTime : amount; f -= multByTime ? amount * Time.deltaTime : amount;
} }
if (GUILayout.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) })) if (GUIUnstrip.RepeatButton("+", new GUILayoutOption[] { GUILayout.Width(20) }))
{ {
f += multByTime ? amount * Time.deltaTime : amount; f += multByTime ? amount * Time.deltaTime : amount;
} }

View File

@ -1,6 +1,4 @@
using System; using System;
using System.CodeDom;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -32,13 +30,19 @@ namespace Explorer
private UnityEngine.Object m_uObj; private UnityEngine.Object m_uObj;
private Component m_component; private Component m_component;
private static readonly HashSet<string> _memberBlacklist = new HashSet<string> private static readonly HashSet<string> _typeAndMemberBlacklist = new HashSet<string>
{ {
// Causes a crash // Causes a crash
"Type.DeclaringMethod", "Type.DeclaringMethod",
// Causes a crash
"Rigidbody2D.Cast",
};
private static readonly HashSet<string> _methodStartsWithBlacklist = new HashSet<string>
{
// Pointless (handled by Properties) // Pointless (handled by Properties)
"get_", "get_",
"set_" "set_",
}; };
public override void Init() public override void Init()
@ -157,11 +161,13 @@ namespace Explorer
continue; continue;
// check blacklisted members // check blacklisted members
if (_memberBlacklist.Any(it => member.Name.StartsWith(it))) var sig = $"{member.DeclaringType.Name}.{member.Name}";
if (_typeAndMemberBlacklist.Any(it => it == sig))
continue;
if (_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it)))
continue; continue;
// compare signature to already cached members
var signature = $"{member.DeclaringType.Name}.{member.Name}";
if (member is MethodInfo mi) if (member is MethodInfo mi)
{ {
AppendParams(mi.GetParameters()); AppendParams(mi.GetParameters());
@ -173,32 +179,34 @@ namespace Explorer
void AppendParams(ParameterInfo[] _args) void AppendParams(ParameterInfo[] _args)
{ {
signature += " ("; sig += " (";
foreach (var param in _args) foreach (var param in _args)
{ {
signature += $"{param.ParameterType.Name} {param.Name}, "; sig += $"{param.ParameterType.Name} {param.Name}, ";
} }
signature += ")"; sig += ")";
} }
if (cachedSigs.Contains(signature)) if (cachedSigs.Contains(sig))
{ {
continue; continue;
} }
// MelonLogger.Log($"Trying to cache member {signature}...");
try try
{ {
var cached = CacheObjectBase.GetCacheObject(member, target); var cached = CacheObjectBase.GetCacheObject(member, target);
if (cached != null) if (cached != null)
{ {
cachedSigs.Add(signature); cachedSigs.Add(sig);
list.Add(cached); list.Add(cached);
cached.ReflectionException = exception; cached.ReflectionException = exception;
} }
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.LogWarning($"Exception caching member {signature}!"); MelonLogger.LogWarning($"Exception caching member {sig}!");
MelonLogger.Log(e.ToString()); MelonLogger.Log(e.ToString());
} }
} }
@ -220,7 +228,7 @@ namespace Explorer
if (!WindowManager.TabView) if (!WindowManager.TabView)
{ {
Header(); Header();
GUILayout.BeginArea(new Rect(5, 25, rect.width - 10, rect.height - 35), GUI.skin.box); GUIUnstrip.BeginArea(new Rect(5, 25, rect.width - 10, rect.height - 35), GUI.skin.box);
} }
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
@ -280,7 +288,7 @@ namespace Explorer
GUI.color = Color.white; GUI.color = Color.white;
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.Space(10); GUIUnstrip.Space(10);
Pages.ItemCount = m_cachedMembersFiltered.Length; Pages.ItemCount = m_cachedMembersFiltered.Length;
@ -309,7 +317,7 @@ namespace Explorer
scroll = GUIUnstrip.BeginScrollView(scroll); scroll = GUIUnstrip.BeginScrollView(scroll);
GUILayout.Space(10); GUIUnstrip.Space(10);
UIStyles.HorizontalLine(Color.grey); UIStyles.HorizontalLine(Color.grey);
@ -346,7 +354,7 @@ namespace Explorer
{ {
m_rect = ResizeDrag.ResizeWindow(rect, windowID); m_rect = ResizeDrag.ResizeWindow(rect, windowID);
GUILayout.EndArea(); GUIUnstrip.EndArea();
} }
} }
catch (Il2CppException e) catch (Il2CppException e)

View File

@ -1,10 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -65,7 +59,7 @@ namespace Explorer
return; return;
} }
GUILayout.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box); GUIUnstrip.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box);
GUILayout.BeginVertical(GUI.skin.box, null); GUILayout.BeginVertical(GUI.skin.box, null);
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
@ -109,7 +103,7 @@ namespace Explorer
} }
catch { } catch { }
GUILayout.EndArea(); GUIUnstrip.EndArea();
} }
catch { } catch { }
} }

View File

@ -1,11 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Harmony;
using MelonLoader;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer
@ -17,7 +10,7 @@ namespace Explorer
public object Target; public object Target;
public int windowID; public int windowID;
public Rect m_rect = new Rect(Vector2.zero, ModConfig.Instance.Default_Window_Size); 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; public Vector2 scroll = Vector2.zero;
@ -49,15 +42,7 @@ namespace Explorer
public void OnGUI() public void OnGUI()
{ {
if (CppExplorer.ShowMenu)
{
var origSkin = GUI.skin;
GUI.skin = UIStyles.WindowSkin;
m_rect = GUI.Window(windowID, m_rect, (GUI.WindowFunction)WindowFunction, Title); m_rect = GUI.Window(windowID, m_rect, (GUI.WindowFunction)WindowFunction, Title);
GUI.skin = origSkin;
}
} }
public void Header() public void Header()

View File

@ -1,14 +1,5 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Harmony;
using MelonLoader;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
namespace Explorer namespace Explorer
{ {

View File

@ -1,19 +1,116 @@
using System; using System.Collections;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine; using UnityEngine;
using System.Reflection;
using System.Collections.Specialized;
using MelonLoader;
// 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
}
namespace Explorer.Tests namespace Explorer.Tests
{ {
public class TestClass 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()); public static TestClass Instance => m_instance ?? (m_instance = new TestClass());
private static TestClass m_instance; private static TestClass m_instance;
public TestClass()
{
ILHashSetTest = new Il2CppSystem.Collections.Generic.HashSet<string>();
ILHashSetTest.Add("1");
ILHashSetTest.Add("2");
ILHashSetTest.Add("3");
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)
{
arg2 = "this is arg2";
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()
{
return new Hashtable
{
{ "One", 1 },
{ "Two", 2 },
{ "Three", 3 },
};
}
// 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>
{
"One",
"Two",
"Three"
};
public static Il2CppSystem.Collections.Generic.HashSet<string> ILHashSetTest;
// Test indexed parameter // Test indexed parameter
public string this[int arg0, string arg1] public string this[int arg0, string arg1]

View File

@ -1,15 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using MelonLoader;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using System.Reflection; using System.Reflection;
using MelonLoader;
using UnhollowerRuntimeLib;
using UnityEngine;
using UnityEngineInternal; using UnityEngineInternal;
using Harmony;
namespace Explorer namespace Explorer
{ {
@ -20,42 +14,135 @@ namespace Explorer
public static bool ScrollFailed = false; public static bool ScrollFailed = false;
public static bool ManualUnstripFailed = false; public static bool ManualUnstripFailed = false;
private static GenericStack ScrollStack private static GenericStack ScrollStack => m_scrollStack ?? GetScrollStack();
{ private static PropertyInfo m_scrollViewStatesInfo;
get private static GenericStack m_scrollStack;
public static DateTime nextScrollStepTime;
private static GenericStack GetScrollStack()
{ {
if (m_scrollViewStatesInfo == null) if (m_scrollViewStatesInfo == null)
{ {
try if (typeof(GUI).GetProperty("scrollViewStates", ReflectionHelpers.CommonFlags) is PropertyInfo scrollStatesInfo)
{ {
m_scrollViewStatesInfo = typeof(GUI).GetProperty("scrollViewStates"); m_scrollViewStatesInfo = scrollStatesInfo;
if (m_scrollViewStatesInfo == null) throw new Exception();
} }
catch else if (typeof(GUI).GetProperty("s_ScrollViewStates", ReflectionHelpers.CommonFlags) is PropertyInfo s_scrollStatesInfo)
{ {
m_scrollViewStatesInfo = typeof(GUI).GetProperty("s_scrollViewStates"); m_scrollViewStatesInfo = s_scrollStatesInfo;
} }
} }
return (GenericStack)m_scrollViewStatesInfo?.GetValue(null, null); if (m_scrollViewStatesInfo?.GetValue(null, null) is GenericStack stack)
}
}
private static PropertyInfo m_scrollViewStatesInfo;
public static Rect GetLastRect()
{ {
EventType type = Event.current.type; m_scrollStack = stack;
Rect last;
if (type != EventType.Layout && type != EventType.Used)
{
last = GUILayoutUtility.current.topLevel.GetLastUnstripped();
} }
else else
{ {
last = GUILayoutUtility.kDummyRect; m_scrollStack = new GenericStack();
} }
return last;
return m_scrollStack;
}
public static void Space(float pixels)
{
GUIUtility.CheckOnGUI();
if (GUILayoutUtility.current.topLevel.isVertical)
LayoutUtilityUnstrip.GetRect(0, pixels, GUILayoutUtility.spaceStyle, new GUILayoutOption[] { GUILayout.Height(pixels) });
else
LayoutUtilityUnstrip.GetRect(pixels, 0, GUILayoutUtility.spaceStyle, new GUILayoutOption[] { GUILayout.Width(pixels) });
if (Event.current.type == EventType.Layout)
{
GUILayoutUtility.current.topLevel.entries[GUILayoutUtility.current.topLevel.entries.Count - 1].consideredForMargin = false;
}
}
// fix for repeatbutton
static public bool RepeatButton(Texture image, params GUILayoutOption[] options) { return DoRepeatButton(GUIContent.Temp(image), GUI.skin.button, options); }
static public bool RepeatButton(string text, params GUILayoutOption[] options) { return DoRepeatButton(GUIContent.Temp(text), GUI.skin.button, options); }
static public bool RepeatButton(GUIContent content, params GUILayoutOption[] options) { return DoRepeatButton(content, GUI.skin.button, options); }
static public bool RepeatButton(Texture image, GUIStyle style, params GUILayoutOption[] options) { return DoRepeatButton(GUIContent.Temp(image), style, options); }
static public bool RepeatButton(string text, GUIStyle style, params GUILayoutOption[] options) { return DoRepeatButton(GUIContent.Temp(text), style, options); }
// Make a repeating button. The button returns true as long as the user holds down the mouse
static public bool RepeatButton(GUIContent content, GUIStyle style, params GUILayoutOption[] options) { return DoRepeatButton(content, style, options); }
static bool DoRepeatButton(GUIContent content, GUIStyle style, GUILayoutOption[] options)
{ return GUI.RepeatButton(LayoutUtilityUnstrip.GetRect(content, style, options), content, style); }
// Fix for BeginArea
static public void BeginArea(Rect screenRect) { BeginArea(screenRect, GUIContent.none, GUIStyle.none); }
static public void BeginArea(Rect screenRect, string text) { BeginArea(screenRect, GUIContent.Temp(text), GUIStyle.none); }
static public void BeginArea(Rect screenRect, Texture image) { BeginArea(screenRect, GUIContent.Temp(image), GUIStyle.none); }
static public void BeginArea(Rect screenRect, GUIContent content) { BeginArea(screenRect, content, GUIStyle.none); }
static public void BeginArea(Rect screenRect, GUIStyle style) { BeginArea(screenRect, GUIContent.none, style); }
static public void BeginArea(Rect screenRect, string text, GUIStyle style) { BeginArea(screenRect, GUIContent.Temp(text), style); }
static public void BeginArea(Rect screenRect, Texture image, GUIStyle style) { BeginArea(screenRect, GUIContent.Temp(image), style); }
// Begin a GUILayout block of GUI controls in a fixed screen area.
static public void BeginArea(Rect screenRect, GUIContent content, GUIStyle style)
{
GUILayoutGroup g = GUILayoutUtility.BeginLayoutArea(style, Il2CppType.Of<GUILayoutGroup>());
if (Event.current.type == EventType.Layout)
{
g.resetCoords = true;
g.minWidth = g.maxWidth = screenRect.width;
g.minHeight = g.maxHeight = screenRect.height;
g.rect = Rect.MinMaxRect(screenRect.xMin, screenRect.yMin, g.rect.xMax, g.rect.yMax);
}
GUI.BeginGroup(g.rect, content, style);
}
// Close a GUILayout block started with BeginArea
static public void EndArea()
{
if (Event.current.type == EventType.Used)
return;
GUILayoutUtility.current.layoutGroups.Pop();
GUILayoutUtility.current.topLevel = GUILayoutUtility.current.layoutGroups.Peek().TryCast<GUILayoutGroup>();
GUI.EndGroup();
}
// Fix for BeginGroup
public static void BeginGroup(Rect position) { BeginGroup(position, GUIContent.none, GUIStyle.none); }
public static void BeginGroup(Rect position, string text) { BeginGroup(position, GUIContent.Temp(text), GUIStyle.none); }
public static void BeginGroup(Rect position, Texture image) { BeginGroup(position, GUIContent.Temp(image), GUIStyle.none); }
public static void BeginGroup(Rect position, GUIContent content) { BeginGroup(position, content, GUIStyle.none); }
public static void BeginGroup(Rect position, GUIStyle style) { BeginGroup(position, GUIContent.none, style); }
public static void BeginGroup(Rect position, string text, GUIStyle style) { BeginGroup(position, GUIContent.Temp(text), style); }
public static void BeginGroup(Rect position, Texture image, GUIStyle style) { BeginGroup(position, GUIContent.Temp(image), style); }
public static void BeginGroup(Rect position, GUIContent content, GUIStyle style) { BeginGroup(position, content, style, Vector2.zero); }
internal static void BeginGroup(Rect position, GUIContent content, GUIStyle style, Vector2 scrollOffset)
{
int id = GUIUtility.GetControlID(GUI.s_BeginGroupHash, FocusType.Passive);
if (content != GUIContent.none || style != GUIStyle.none)
{
switch (Event.current.type)
{
case EventType.Repaint:
style.Draw(position, content, id);
break;
default:
if (position.Contains(Event.current.mousePosition))
GUIUtility.mouseUsed = true;
break;
}
}
GUIClip.Push(position, scrollOffset, Vector2.zero, false);
}
public static void EndGroup()
{
GUIClip.Internal_Pop();
} }
// Fix for BeginScrollView. // Fix for BeginScrollView.
@ -112,8 +199,6 @@ namespace Explorer
private static Vector2 BeginScrollView_ImplLayout(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, private static Vector2 BeginScrollView_ImplLayout(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical,
GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options) GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options)
{ {
GUIUtility.CheckOnGUI();
var guiscrollGroup = GUILayoutUtility.BeginLayoutGroup(background, null, Il2CppType.Of<GUIScrollGroup>()) var guiscrollGroup = GUILayoutUtility.BeginLayoutGroup(background, null, Il2CppType.Of<GUIScrollGroup>())
.TryCast<GUIScrollGroup>(); .TryCast<GUIScrollGroup>();
@ -145,7 +230,7 @@ namespace Explorer
private static Vector2 BeginScrollView_Impl(Rect position, Vector2 scrollPosition, Rect viewRect, bool alwaysShowHorizontal, private static Vector2 BeginScrollView_Impl(Rect position, Vector2 scrollPosition, Rect viewRect, bool alwaysShowHorizontal,
bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background) bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background)
{ {
GUIUtility.CheckOnGUI(); // GUIUtility.CheckOnGUI();
int controlID = GUIUtility.GetControlID(GUI.s_ScrollviewHash, FocusType.Passive); int controlID = GUIUtility.GetControlID(GUI.s_ScrollviewHash, FocusType.Passive);
@ -388,12 +473,12 @@ namespace Explorer
if (flag) if (flag)
{ {
result = true; result = true;
GUI.nextScrollStepTime = Il2CppSystem.DateTime.Now.AddMilliseconds(250.0); nextScrollStepTime = DateTime.Now.AddMilliseconds(250.0);
} }
else if (Il2CppSystem.DateTime.Now >= GUI.nextScrollStepTime) else if (DateTime.Now >= nextScrollStepTime)
{ {
result = true; result = true;
GUI.nextScrollStepTime = Il2CppSystem.DateTime.Now.AddMilliseconds(30.0); nextScrollStepTime = DateTime.Now.AddMilliseconds(30.0);
} }
if (Event.current.type == EventType.Repaint) if (Event.current.type == EventType.Repaint)
{ {

View File

@ -0,0 +1,107 @@
using UnityEngine;
namespace Explorer
{
public class LayoutUtilityUnstrip
{
public static Rect GetRect(float width, float height) { return DoGetRect(width, width, height, height, GUIStyle.none, null); }
public static Rect GetRect(float width, float height, GUIStyle style) { return DoGetRect(width, width, height, height, style, null); }
public static Rect GetRect(float width, float height, params GUILayoutOption[] options) { return DoGetRect(width, width, height, height, GUIStyle.none, options); }
// Reserve layout space for a rectangle with a fixed content area.
public static Rect GetRect(float width, float height, GUIStyle style, params GUILayoutOption[] options)
{ return DoGetRect(width, width, height, height, style, options); }
public static Rect GetRect(float minWidth, float maxWidth, float minHeight, float maxHeight)
{ return DoGetRect(minWidth, maxWidth, minHeight, maxHeight, GUIStyle.none, null); }
public static Rect GetRect(float minWidth, float maxWidth, float minHeight, float maxHeight, GUIStyle style)
{ return DoGetRect(minWidth, maxWidth, minHeight, maxHeight, style, null); }
public static Rect GetRect(float minWidth, float maxWidth, float minHeight, float maxHeight, params GUILayoutOption[] options)
{ return DoGetRect(minWidth, maxWidth, minHeight, maxHeight, GUIStyle.none, options); }
// Reserve layout space for a flexible rect.
public static Rect GetRect(float minWidth, float maxWidth, float minHeight, float maxHeight, GUIStyle style, params GUILayoutOption[] options)
{ return DoGetRect(minWidth, maxWidth, minHeight, maxHeight, style, options); }
static Rect DoGetRect(float minWidth, float maxWidth, float minHeight, float maxHeight, GUIStyle style, GUILayoutOption[] options)
{
switch (Event.current.type)
{
case EventType.Layout:
GUILayoutUtility.current.topLevel.Add(new GUILayoutEntry(minWidth, maxWidth, minHeight, maxHeight, style, options));
return GUILayoutUtility.kDummyRect;
case EventType.Used:
return GUILayoutUtility.kDummyRect;
default:
return GUILayoutUtility.current.topLevel.GetNext().rect;
}
}
public static Rect GetRect(GUIContent content, GUIStyle style) { return DoGetRect(content, style, null); }
// Reserve layout space for a rectangle for displaying some contents with a specific style.
public static Rect GetRect(GUIContent content, GUIStyle style, params GUILayoutOption[] options) { return DoGetRect(content, style, options); }
static Rect DoGetRect(GUIContent content, GUIStyle style, GUILayoutOption[] options)
{
GUIUtility.CheckOnGUI();
switch (Event.current.type)
{
case EventType.Layout:
if (style.isHeightDependantOnWidth)
{
GUILayoutUtility.current.topLevel.Add(new GUIWordWrapSizer(style, content, options));
}
else
{
Vector2 sizeConstraints = new Vector2(0, 0);
if (options != null)
{
foreach (var option in options)
{
if (float.TryParse(option.value.ToString(), out float f))
{
switch (option.type)
{
case GUILayoutOption.Type.maxHeight:
sizeConstraints.y = f;
break;
case GUILayoutOption.Type.maxWidth:
sizeConstraints.x = f;
break;
}
}
}
}
Vector2 size = style.CalcSizeWithConstraints(content, sizeConstraints);
// This is needed on non-integer scale ratios to avoid errors to accumulate in further layout calculations
size.x = Mathf.Ceil(size.x);
size.y = Mathf.Ceil(size.y);
GUILayoutUtility.current.topLevel.Add(new GUILayoutEntry(size.x, size.x, size.y, size.y, style, options));
}
return GUILayoutUtility.kDummyRect;
case EventType.Used:
return GUILayoutUtility.kDummyRect;
default:
var entry = GUILayoutUtility.current.topLevel.GetNext();
//GUIDebugger.LogLayoutEntry(entry.rect, entry.marginLeft, entry.marginRight, entry.marginTop, entry.marginBottom, entry.style);
return entry.rect;
}
}
public static Rect GetLastRect()
{
EventType type = Event.current.type;
Rect last;
if (type != EventType.Layout && type != EventType.Used)
{
last = GUILayoutUtility.current.topLevel.GetLastUnstripped();
}
else
{
last = GUILayoutUtility.kDummyRect;
}
return last;
}
}
}

View File

@ -1,10 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Harmony;
using MelonLoader;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer

View File

@ -1,10 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
@ -81,7 +77,7 @@ namespace Explorer
if (this.SupportsPageMovements()) if (this.SupportsPageMovements())
{ {
this.SliderState().isDragging = false; this.SliderState().isDragging = false;
GUI.nextScrollStepTime = SystemClock.now.AddMilliseconds(250.0); GUIUnstrip.nextScrollStepTime = DateTime.Now.AddMilliseconds(250.0);
GUI.scrollTroughSide = this.CurrentScrollTroughSide(); GUI.scrollTroughSide = this.CurrentScrollTroughSide();
result = this.PageMovementValue(); result = this.PageMovementValue();
} }
@ -155,7 +151,7 @@ namespace Explorer
else else
{ {
GUI.InternalRepaintEditorWindow(); GUI.InternalRepaintEditorWindow();
if (SystemClock.now < GUI.nextScrollStepTime) if (DateTime.Now < GUIUnstrip.nextScrollStepTime)
{ {
result = this.currentValue; result = this.currentValue;
} }
@ -165,7 +161,7 @@ namespace Explorer
} }
else else
{ {
GUI.nextScrollStepTime = SystemClock.now.AddMilliseconds(30.0); GUIUnstrip.nextScrollStepTime = DateTime.Now.AddMilliseconds(30.0);
if (this.SupportsPageMovements()) if (this.SupportsPageMovements())
{ {
this.SliderState().isDragging = false; this.SliderState().isDragging = false;

View File

@ -1,9 +1,4 @@
using System; using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Explorer namespace Explorer
{ {